diff --git a/.DEPS.git b/.DEPS.git index bc74de7f427a6..4e1f8f31aa40e 100644 --- a/.DEPS.git +++ b/.DEPS.git @@ -4,33 +4,149 @@ # SEE http://code.google.com/p/chromium/wiki/UsingGit # FOR HOW TO ROLL DEPS vars = { + 'eyes-free': + 'http://eyes-free.googlecode.com/svn', + 'webkit_rev': + '@43aff830b25c0894823834d6ac417112fefb1fb2', + 'blink': + 'http://src.chromium.org/blink', + 'skia': + 'http://skia.googlecode.com/svn', + 'google-breakpad': + 'http://google-breakpad.googlecode.com/svn', + 'sawbuck': + 'http://sawbuck.googlecode.com/svn', + 'mozc': + 'http://mozc.googlecode.com/svn', + 'git.chromium.org': + 'https://chromium.googlesource.com', + 'v8-i18n': + 'http://v8-i18n.googlecode.com/svn', + 'selenium': + 'http://selenium.googlecode.com/svn', + 'buildspec_platforms': + 'all', 'webkit_url': 'https://chromium.googlesource.com/chromium/blink.git', + 'snappy': + 'http://snappy.googlecode.com/svn', + 'ppapi': + 'http://ppapi.googlecode.com/svn', + 'webrtc': + 'http://webrtc.googlecode.com/svn', + 'libaddressinput': + 'http://libaddressinput.googlecode.com/svn', + 'google-cache-invalidation-api': + 'http://google-cache-invalidation-api.googlecode.com/svn', + 'google-url': + 'http://google-url.googlecode.com/svn', + 'googletest': + 'http://googletest.googlecode.com/svn', + 'gyp': + 'http://gyp.googlecode.com/svn', + 'seccompsandbox': + 'http://seccompsandbox.googlecode.com/svn', + 'ots': + 'http://ots.googlecode.com/svn', + 'angleproject': + 'http://angleproject.googlecode.com/svn', + 'pefile': + 'http://pefile.googlecode.com/svn', + 'open-vcdiff': + 'http://open-vcdiff.googlecode.com/svn', + 'linux-syscall-support': + 'http://linux-syscall-support.googlecode.com/svn', + 'jsoncpp': + 'http://svn.code.sf.net/p/jsoncpp/code', + 'pywebsocket': + 'http://pywebsocket.googlecode.com/svn', + 'web-page-replay': + 'http://web-page-replay.googlecode.com/svn', + 'libjingle': + 'http://libjingle.googlecode.com/svn', + 'cld2': + 'https://cld2.googlecode.com/svn', + 'jsr-305': + 'http://jsr-305.googlecode.com/svn', + 'angle_revision': + 'bc75f36b04c8bab99bcecf8cdca16ecee3ac58ee', + 'bidichecker': + 'http://bidichecker.googlecode.com/svn', 'git_url': 'https://chromium.googlesource.com', - 'webkit_rev': - '@08c40136a44dd5b3f5bf976d5c75e7623afb8e29', - 'angle_revision': - 'd9ba4f7318979e927c7a3320c5746c25381eae90' + 'native_client': + 'http://src.chromium.org/native_client', + 'trace-viewer': + 'http://trace-viewer.googlecode.com/svn', + 'leveldb': + 'http://leveldb.googlecode.com/svn', + 'webkit_trunk': + 'http://src.chromium.org/blink/trunk', + 'googlemock': + 'http://googlemock.googlecode.com/svn', + 'grit-i18n': + 'http://grit-i18n.googlecode.com/svn', + 'pdfsqueeze': + 'http://pdfsqueeze.googlecode.com/svn', + 'protobuf': + 'http://protobuf.googlecode.com/svn', + 'smhasher': + 'http://smhasher.googlecode.com/svn', + 'google-toolbox-for-mac': + 'http://google-toolbox-for-mac.googlecode.com/svn', + 'libyuv': + 'http://libyuv.googlecode.com/svn', + 'rlz': + 'http://rlz.googlecode.com/svn', + 'v8': + 'http://v8.googlecode.com/svn', + 'octane-benchmark': + 'http://octane-benchmark.googlecode.com/svn', + 'sfntly': + 'http://sfntly.googlecode.com/svn', + 'sctp-refimpl': + 'https://sctp-refimpl.googlecode.com/svn', + 'libphonenumber': + 'http://libphonenumber.googlecode.com/svn', + 'pymox': + 'http://pymox.googlecode.com/svn', + 'pyftpdlib': + 'http://pyftpdlib.googlecode.com/svn', + 'google-safe-browsing': + 'http://google-safe-browsing.googlecode.com/svn' } deps = { + 'build': + Var('git_url') + '/chromium/tools/build.git@2fb2eb9358d60f05e42ca1e788c68d0579101833', + 'build/scripts/command_wrapper/bin': + Var('git_url') + '/chromium/tools/command_wrapper/bin.git@2eeebba9a512cae9e4e9312f5ec728dbdad80bd0', + 'build/scripts/gsd_generate_index': + Var('git_url') + '/chromium/tools/gsd_generate_index.git@d2f5d5a5d212d8fb337d751c0351644a6ac83ac8', + 'build/scripts/private/data/reliability': + Var('git_url') + '/chromium/src/chrome/test/data/reliability.git@90d6f258a2fbff583dcfc107ab1727bd8efe65e8', + 'build/scripts/tools/deps2git': + Var('git_url') + '/chromium/tools/deps2git.git@5687581bdb799f00060b6fa2275a011cacb7e0e3', + 'build/third_party/lighttpd': + Var('git_url') + '/chromium/deps/lighttpd.git@9dfa55d15937a688a92cbf2b7a8621b0927d06eb', + 'depot_tools': + Var('git_url') + '/chromium/tools/depot_tools.git@a5a945a18d007079395947e20a74e0e551fe4902', 'src/breakpad/src': Var('git_url') + '/external/google-breakpad/src.git@17f614cd8365f5b78577ffcc237300a970db9813', 'src/buildtools': - Var('git_url') + '/chromium/buildtools.git@59b93247766e1cdac6e482637ad493df38f7aeb7', + Var('git_url') + '/chromium/buildtools.git@48edf30c463fc41fb9fb0926f8466b473cb177fa', 'src/chrome/browser/resources/pdf/html_office': Var('git_url') + '/chromium/html-office-public.git@eeff97614f65e0578529490d44d412032c3d7359', 'src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin': - Var('git_url') + '/native_client/src/native_client/tests/prebuilt.git@3e17365176c94624f46cace174f61834b7f3c35d', + Var('git_url') + '/native_client/src/native_client/tests/prebuilt.git@e65f794ce4a809d92d5cacae55ae119946bdb9c5', 'src/chrome/test/data/perf/canvas_bench': Var('git_url') + '/chromium/canvas_bench.git@a7b40ea5ae0239517d78845a5fc9b12976bfc732', 'src/chrome/test/data/perf/frame_rate/content': Var('git_url') + '/chromium/frame_rate/content.git@c10272c88463efeef6bb19c9ec07c42bc8fe22b9', 'src/media/cdm/ppapi/api': - Var('git_url') + '/chromium/cdm.git@400ae69a79d6b8b531c6960d0558d13cde600879', + Var('git_url') + '/chromium/cdm.git@41c8183a3966a17b440dbe606cb2840e1b7ce884', 'src/native_client': - Var('git_url') + '/native_client/src/native_client.git@1a0e590f94ca071ac97574b172ecfa509a5d75df', + Var('git_url') + '/native_client/src/native_client.git@792e992d22507e6ff10e5088f7f2a1b61e3742a2', 'src/sdch/open-vcdiff': Var('git_url') + '/external/open-vcdiff.git@438f2a5be6d809bc21611a94cd37bfc8c28ceb33', 'src/testing/gmock': @@ -40,25 +156,25 @@ deps = { 'src/third_party/WebKit': Var('webkit_url') + '' + Var('webkit_rev'), 'src/third_party/WebKit/LayoutTests/w3c/csswg-test': - Var('git_url') + '/external/w3c/csswg-test.git@bacbb4a8dca702cd86646761fde96793db13d4f1', + Var('git_url') + '/external/w3c/csswg-test.git@f9b9daa33eac525923e941333d389fcb42f9b19f', 'src/third_party/WebKit/LayoutTests/w3c/web-platform-tests': - Var('git_url') + '/external/w3c/web-platform-tests.git@6bed4516fe8522d65512c76ef02e4f0ae8234395', + Var('git_url') + '/external/w3c/web-platform-tests.git@6c7bd34e408ff69687a95d75b7e562fa84148997', 'src/third_party/angle': Var('git_url') + '/angle/angle.git' + '@' + Var('angle_revision'), 'src/third_party/bidichecker': Var('git_url') + '/external/bidichecker/lib.git@97f2aa645b74c28c57eca56992235c79850fa9e0', 'src/third_party/boringssl/src': - 'https://boringssl.googlesource.com/boringssl.git@533cbee57eec77268ef9f53c4039ec753240fb37', + 'https://boringssl.googlesource.com/boringssl.git@c3d79605ab06cffa87877bcfe0792f767bde8b90', 'src/third_party/brotli/src': Var('git_url') + '/external/font-compression-reference.git@6cef49677dc4c650ef6e3f56041e0a41803afa8c', 'src/third_party/cacheinvalidation/src': Var('git_url') + '/external/google-cache-invalidation-api/src.git@50c0a8563b62f6e3b780da14f82246d192f9c1e0', 'src/third_party/cld_2/src': - Var('git_url') + '/external/cld2.git@f7a226750824b7a0256a8341fa447a181b7acf73', + Var('git_url') + '/external/cld2.git@7f791121dc058422ac6bb945b7e655e4ce24f473', 'src/third_party/colorama/src': Var('git_url') + '/external/colorama.git@799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', 'src/third_party/ffmpeg': - Var('git_url') + '/chromium/third_party/ffmpeg.git@5ab896ad9dffd4a78e3c310de8e3507ded3e3239', + Var('git_url') + '/chromium/third_party/ffmpeg.git@98ca32e50f6e38447bc81705d0689ebceb6ac649', 'src/third_party/flac': Var('git_url') + '/chromium/deps/flac.git@0635a091379d9677f1ddde5f2eec85d0f096f219', 'src/third_party/hunspell': @@ -74,7 +190,7 @@ deps = { 'src/third_party/leveldatabase/src': Var('git_url') + '/external/leveldb.git@3f77584eb3f9754bbb7079070873ece3f30a1e6b', 'src/third_party/libaddressinput/src': - Var('git_url') + '/external/libaddressinput.git@d75941244491c922456bf0fa4a987c098f082a76', + Var('git_url') + '/external/libaddressinput.git@5e0f1290b83e59c0b3b0b3d23c060f1b134a1364', 'src/third_party/libc++/trunk': Var('git_url') + '/chromium/llvm-project/libcxx.git@e785ef19637f88c5e3e9926fabd8a64cd7eac49d', 'src/third_party/libc++abi/trunk': @@ -82,9 +198,9 @@ deps = { 'src/third_party/libexif/sources': Var('git_url') + '/chromium/deps/libexif/sources.git@ed98343daabd7b4497f97fda972e132e6877c48a', 'src/third_party/libjingle/source/talk': - Var('git_url') + '/external/webrtc/trunk/talk.git@f6b28005a7872a3ab4b236a19f907d1aef904455', + Var('git_url') + '/external/webrtc/trunk/talk.git@3541181607ffe656d8db759a6095864d9f72248d', 'src/third_party/libjpeg_turbo': - Var('git_url') + '/chromium/deps/libjpeg_turbo.git@841fff8cddd73c0d6b966902f83bea7ad366bd4b', + Var('git_url') + '/chromium/deps/libjpeg_turbo.git@2ed5319ce40b0ba2cd9b962713ea0ef775781e69', 'src/third_party/libphonenumber/src/phonenumbers': Var('git_url') + '/external/libphonenumber/cpp/src/phonenumbers.git@8d8b5b3b2035197795d27573d4cf566b5d9ad689', 'src/third_party/libphonenumber/src/resources': @@ -94,11 +210,11 @@ deps = { 'src/third_party/libsrtp': Var('git_url') + '/chromium/deps/libsrtp.git@662d81de5cbf3667f1baaafe051b8b4ab0b12fe2', 'src/third_party/libvpx': - Var('git_url') + '/chromium/deps/libvpx.git@7b82466c4e2f56b62b21e95178f15568448beab4', + Var('git_url') + '/chromium/deps/libvpx.git@8110782824f708dd3630cd55375019648567c31c', 'src/third_party/libwebm/source': Var('git_url') + '/webm/libwebm.git@0d4cb404ea4195e5e21d04db2c955615535ce62e', 'src/third_party/libyuv': - Var('git_url') + '/external/libyuv.git@451a7541b7fbbcb85199dfbc6faccdb1955ea1d5', + Var('git_url') + '/external/libyuv.git@455c66b4375d72984b79249616d0a708ad568894', 'src/third_party/mesa/src': Var('git_url') + '/chromium/deps/mesa.git@457812d99a213dedf1c4cd38018ff48118d0c44f', 'src/third_party/openmax_dl': @@ -106,11 +222,11 @@ deps = { 'src/third_party/openssl': Var('git_url') + '/chromium/deps/openssl.git@c9613e3123ff035683f45163af142817b38d03b6', 'src/third_party/opus/src': - Var('git_url') + '/chromium/deps/opus.git@36fa2621472ebf5b859fd16bbdb749019c68cc69', + Var('git_url') + '/chromium/deps/opus.git@cae696156f1e60006e39821e79a1811ae1933c69', 'src/third_party/ots': Var('git_url') + '/external/ots.git@98897009f3ea8a5fa3e20a4a74977da7aaa8e61a', 'src/third_party/pdfium': - 'https://pdfium.googlesource.com/pdfium.git@df449c0eb4b1fb5583da71265faf50a9a520be3c', + 'https://pdfium.googlesource.com/pdfium.git@2fd7b0be4473f4c9877fed2c2ade403c71ded395', 'src/third_party/pyftpdlib/src': Var('git_url') + '/external/pyftpdlib.git@2be6d65e31c7ee6320d059f581f05ae8d89d7e45', 'src/third_party/pywebsocket/src': @@ -122,7 +238,7 @@ deps = { 'src/third_party/sfntly/cpp/src': Var('git_url') + '/external/sfntly/cpp/src.git@1bdaae8fc788a5ac8936d68bf24f37d977a13dac', 'src/third_party/skia': - Var('git_url') + '/skia.git@0aeea6d344f12e35e29a79f4bbc48af88f913204', + Var('git_url') + '/skia.git@773a327b12f653c3bb0630fee1b3ba9629f5717e', 'src/third_party/smhasher/src': Var('git_url') + '/external/smhasher.git@e87738e57558e0ec472b2fc3a643b838e5b6e88f', 'src/third_party/snappy/src': @@ -132,17 +248,17 @@ deps = { 'src/third_party/swig/Lib': Var('git_url') + '/chromium/deps/swig/Lib.git@f2a695d52e61e6a8d967731434f165ed400f0d69', 'src/third_party/trace-viewer': - Var('git_url') + '/external/trace-viewer.git@4137fb722560b80f18207beb562472d93abcbb92', + Var('git_url') + '/external/trace-viewer.git@5f89b7cd5732ddf7e783bb6c9fe57bb8a7cf74e2', 'src/third_party/usrsctp/usrsctplib': Var('git_url') + '/external/usrsctplib.git@e6e18333da18c5bb7de7e9189e53403e2ffa7023', 'src/third_party/webdriver/pylib': Var('git_url') + '/external/selenium/py.git@5fd78261a75fe08d27ca4835fb6c5ce4b42275bd', 'src/third_party/webgl/src': - Var('git_url') + '/external/khronosgroup/webgl.git@2cd3af21647cb0ff368106d88188046675baf52d', + Var('git_url') + '/external/khronosgroup/webgl.git@c81b6b214549bfeb475c25bf1de7bd3096c5d1c3', 'src/third_party/webpagereplay': Var('git_url') + '/external/web-page-replay.git@b62c02d3b64cf00a2f65a82cca0721aa42c3d6ad', 'src/third_party/webrtc': - Var('git_url') + '/external/webrtc/trunk/webrtc.git@a288b8cbb568cbf1735e6d5d0012524f4f8e5f74', + Var('git_url') + '/external/webrtc/trunk/webrtc.git@a6ed0dfa13f606d7f173f740510c2ed5855aa7fd', 'src/third_party/yasm/source/patched-yasm': Var('git_url') + '/chromium/deps/yasm/patched-yasm.git@c960eb11ccda80b10ed50be39df4f0663b371d1d', 'src/tools/deps2git': @@ -150,20 +266,22 @@ deps = { 'src/tools/grit': Var('git_url') + '/external/grit-i18n.git@6a46b04d35eade32b1270392317dca516c9dd56d', 'src/tools/gyp': - Var('git_url') + '/external/gyp.git@ac2684beed6e8ca04ac5f837c9db869eefec7bb5', + Var('git_url') + '/external/gyp.git@ed274e3ea5885bc28cf6249e848cf696d0e89263', 'src/tools/page_cycler/acid3': Var('git_url') + '/chromium/deps/acid3.git@6be0a66a1ebd7ebc5abc1b2f405a945f6d871521', 'src/tools/swarming_client': Var('git_url') + '/external/swarming.client.git@bbf1fcca7932d92cca9d7dab46ea271a7f6d61fb', 'src/v8': - Var('git_url') + '/external/v8.git@9d72b8dd94263d9f500f18255d67f0c7b8c3527a', + Var('git_url') + '/external/v8.git@895fe777a81b25528620c70eadc4c7463ccfd749', } deps_os = { 'android': { + 'src/pdf': + None, 'src/third_party/android_tools': - Var('git_url') + '/android_tools.git@8301b711a9ac7de56e9a9ff3dee0b2ebfc9a380f', + Var('git_url') + '/android_tools.git@31869996507de16812bb53a3d0aaa15cd6194c16', 'src/third_party/aosp': Var('git_url') + '/chromium/deps/aosp.git@bbafe5155dff86bbba1e92b42a073ffcfcfbf28c', 'src/third_party/apache-mime4j': @@ -211,8 +329,6 @@ deps_os = { None, 'src/third_party/WebKit/LayoutTests/w3c/web-platform-tests': None, - 'src/third_party/angle': - None, 'src/third_party/bidichecker': None, 'src/third_party/brotli/src': @@ -240,7 +356,7 @@ deps_os = { 'src/third_party/mesa/src': None, 'src/third_party/nss': - Var('git_url') + '/chromium/deps/nss.git@980afa3f6c54b09b45e4060ab64243a0d46c8071', + Var('git_url') + '/chromium/deps/nss.git@4a442d04bfa2659a888c6e110f3043cecdba12cf', 'src/third_party/openmax_dl': None, 'src/third_party/opus/src': @@ -287,7 +403,7 @@ deps_os = { 'src/third_party/lighttpd': Var('git_url') + '/chromium/deps/lighttpd.git@9dfa55d15937a688a92cbf2b7a8621b0927d06eb', 'src/third_party/nss': - Var('git_url') + '/chromium/deps/nss.git@980afa3f6c54b09b45e4060ab64243a0d46c8071', + Var('git_url') + '/chromium/deps/nss.git@4a442d04bfa2659a888c6e110f3043cecdba12cf', 'src/third_party/pdfsqueeze': Var('git_url') + '/external/pdfsqueeze.git@5936b871e6a087b7e50d4cbcb122378d8a07499f', 'src/third_party/swig/mac': @@ -295,10 +411,14 @@ deps_os = { }, 'unix': { + 'build/third_party/cbuildbot_chromite': + Var('git_url') + '/chromiumos/chromite.git@07ac932c5dbaaa0fc1de3db186669a5bf805ccd6', + 'build/third_party/xvfb': + Var('git_url') + '/chromium/tools/third_party/xvfb.git@aebb1aadf1422e4d81e831e13746b8f7ae322e07', 'src/chrome/tools/test/reference_build/chrome_linux': Var('git_url') + '/chromium/reference_builds/chrome_linux64.git@34e237c7cd03bac41e45a3148bc010d55cb79c95', 'src/third_party/chromite': - Var('git_url') + '/chromiumos/chromite.git@38242c6c77b77bcfb887fa97f3735c3262310370', + Var('git_url') + '/chromiumos/chromite.git@9350f2b004f2301477b76727b8c935aa8481c9f5', 'src/third_party/cros_system_api': Var('git_url') + '/chromiumos/platform/system_api.git@690c6b9702d7bcc761ae2d8976fe77e547255481', 'src/third_party/fontconfig/src': @@ -337,7 +457,7 @@ deps_os = { 'src/third_party/nacl_sdk_binaries': Var('git_url') + '/chromium/deps/nacl_sdk_binaries.git@759dfca03bdc774da7ecbf974f6e2b84f43699a5', 'src/third_party/nss': - Var('git_url') + '/chromium/deps/nss.git@980afa3f6c54b09b45e4060ab64243a0d46c8071', + Var('git_url') + '/chromium/deps/nss.git@4a442d04bfa2659a888c6e110f3043cecdba12cf', 'src/third_party/pefile': Var('git_url') + '/external/pefile.git@72c6ae42396cb913bcab63c15585dc3b5c3f92f1', 'src/third_party/perl': @@ -377,6 +497,17 @@ skip_child_includes = [ ] hooks = [ + { + 'action': + [ + 'python', + 'src/build/landmines.py' +], + 'pattern': + '.', + 'name': + 'landmines' +}, { 'action': [ diff --git a/.gitignore b/.gitignore index 301b48b785f17..a20e9c953694e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ .cproject .gdb_history .gdbinit +.landmines .metadata .project .pydevproject @@ -157,13 +158,13 @@ v8.log /media/yuv_convert_simd_x86.xml /metro_driver /mojo/hello_world_service.xml +/mojo/mojo_application_manager_unittests.xml /mojo/mojo_gles2_bindings.xml /mojo/mojo_launcher_bindings.xml /mojo/mojo_native_viewport_bindings.xml /mojo/mojo_public_bindings_unittests.xml /mojo/mojo_public_unittests.xml /mojo/mojo_sample_service.xml -/mojo/mojo_service_manager_unittests.xml /mojo/mojo_shell_bindings.xml /mojo/mojo_view_manager_bindings.xml /mojo/mojom_test.xml @@ -175,6 +176,7 @@ v8.log /net/testserver.log /out /out_* +/ozone /ppapi/native_client/nacl_irt.xml /ppapi/native_client/ppapi_lib.xml /ppapi/native_client/src/shared/ppapi_proxy/nacl_ppapi_browser.xml @@ -390,3 +392,4 @@ v8.log /win8/metro_driver/metro_driver_version_resources.xml /x86-generic_out/ /xcodebuild +/xwalk diff --git a/AUTHORS b/AUTHORS index 9bee90f80a006..cc2b1aa44c606 100644 --- a/AUTHORS +++ b/AUTHORS @@ -20,6 +20,7 @@ Aditya Bhargava Ajay Berwal Ajith Kumar V Alex Gartrell +Alex Henrie Alex Scheele Alexander Sulfrian Alexandre Abreu @@ -109,7 +110,7 @@ Dongwoo Joshua Im Douglas F. Turner Eduardo Lima (Etrunko) Edward Crossman -Eero Häkkinen +Eero Häkkinen Egor Starkov Ehsan Akhgari Elan Ruusamäe @@ -122,6 +123,7 @@ Etienne Laurin Evan Peterson Evan Wallace Evangelos Foutras +Evgeniy Dushistov Fabien Tassin Felix H. Dahlke Fernando Jiménez Moreno @@ -129,6 +131,7 @@ François Beaufort Francois Kritzinger Frédéric Wang Gaetano Mendola +Gajendra N Gajendra Singh Gao Chun Gao Chun @@ -180,6 +183,7 @@ Jay Soffian Jeado Ko Jesse Miller Jesus Sanchez-Palencia +Jiajia Qin Jie Chen Jihun Brent Kim Jin Yang @@ -364,6 +368,7 @@ Sam McDonald Sanjoy Pal Sanjoy Pal Sanne Wouda +Sarath Singapati Sathish Kuppuswamy Satoshi Matsuzaki Sayan Nayak @@ -470,3 +475,4 @@ The MathWorks, Inc. Torchmobile Inc. Venture 3 Systems LLC <*@venture3systems.com> Yandex LLC <*@yandex-team.ru> +ARM Holdings <*@arm.com> diff --git a/BUILD.gn b/BUILD.gn index 25797eb83e653..dd74ed550e9d1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -162,7 +162,6 @@ group("root") { ] deps -= [ - "//cc", "//chrome/browser", # Blocked on content. "//chrome/browser/devtools", # Blocked on content. "//chrome/browser/ui", # Blocked on content. @@ -179,13 +178,15 @@ group("root") { "//extensions/common", "//extensions/common/api", "//extensions/renderer", - "//mojo", # Blocked on GN toolchain support: https://codereview.chromium.org/428633006/ "//pdf", # Not compiled on Android in GYP yet, either. "//ppapi:ppapi_c", "//sandbox", "//third_party/libusb", "//ui/keyboard", # Blocked on content. + # Blocked on neon stuff in vp8 + "//third_party/WebKit/public:all_blink", + # Seems to not be compiled on Android. Otherwise it will need a config.h. "//third_party/libxslt", @@ -201,9 +202,6 @@ group("root") { "//third_party/flac", "//breakpad:symupload", - # TODO(brettw) make Blink work on Android. - "//third_party/WebKit/public:all_blink", - # Not tested on Android yet: "//google_apis/gcm", "//third_party/cld_2", diff --git a/DEPS b/DEPS index ee0ea34787528..c571868264908 100644 --- a/DEPS +++ b/DEPS @@ -1,813 +1,710 @@ -# This file is automatically processed to create .DEPS.git which is the file -# that gclient uses under git. -# -# See http://code.google.com/p/chromium/wiki/UsingGit -# -# To test manually, run: -# python tools/deps2git/deps2git.py -o .DEPS.git -w -# where is the absolute path to the directory containing the -# .gclient file (the parent of "src"). -# -# Then commit .DEPS.git locally (gclient doesn't like dirty trees) and run -# gclient sync -# Verify the thing happened you wanted. Then revert your .DEPS.git change -# DO NOT CHECK IN CHANGES TO .DEPS.git upstream. It will be automatically -# updated by a bot when you modify this one. -# -# When adding a new dependency, please update the top-level .gitignore file -# to list the dependency's destination directory. - -vars = { - # Use this googlecode_url variable only if there is an internal mirror for it. - # If you do not know, use the full path while defining your new deps entry. - "googlecode_url": "http://%s.googlecode.com/svn", - "sourceforge_url": "http://svn.code.sf.net/p/%(repo)s/code", - "llvm_url": "http://src.chromium.org/llvm-project", - "llvm_git": "https://llvm.googlesource.com", - "libcxx_revision": "206024", - "libcxxabi_revision": "206024", - "webkit_trunk": "http://src.chromium.org/blink/trunk", - "nacl_trunk": "http://src.chromium.org/native_client/trunk", - "webkit_revision": "179716", - "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", - "swig_revision": "230490", - "nacl_revision": "13592", - # After changing nacl_revision, run 'glient sync' and check native_client/DEPS - # to update other nacl_*_revision's. - "nacl_tools_revision": "13077", # native_client/DEPS: tools_rev - "google_toolbox_for_mac_revision": "662", - "libaddressinput_revision": "316", - "libphonenumber_revision": "621", - "libvpx_revision": "287125", - "lss_revision": "31", - "sfntly_revision": "239", - "lighttpd_revision": "33737", - "skia_revision": "0aeea6d344f12e35e29a79f4bbc48af88f913204", - # 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": "22960", - # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling WebRTC - # and V8 without interference from each other. - "webrtc_revision": "6825", - "jsoncpp_revision": "248", - "nss_revision": "287121", - # 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": "bbf1fcca7932d92cca9d7dab46ea271a7f6d61fb", - # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling openssl - # and whatever else without interference from each other. - "openssl_revision": "284247", - # 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": "d9ba4f7318979e927c7a3320c5746c25381eae90", - # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling build tools - # and whatever else without interference from each other. - "buildtools_revision": "59b93247766e1cdac6e482637ad493df38f7aeb7", - # 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": "df449c0eb4b1fb5583da71265faf50a9a520be3c", - # 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. - "openmax_dl_revision": "6777", - # Three lines of non-changing comments so that - # the commit queue can handle CLs rolling BoringSSL - # and whatever else without interference from each other. - "boringssl_revision": "533cbee57eec77268ef9f53c4039ec753240fb37", -} - -deps = { - "src/breakpad/src": - (Var("googlecode_url") % "google-breakpad") + "/trunk/src@1359", - - "src/buildtools": - Var("chromium_git") + "/chromium/buildtools.git@" + - Var("buildtools_revision"), - - "src/sdch/open-vcdiff": - (Var("googlecode_url") % "open-vcdiff") + "/trunk@42", - - "src/testing/gtest": - (Var("googlecode_url") % "googletest") + "/trunk@692", - - "src/testing/gmock": - (Var("googlecode_url") % "googlemock") + "/trunk@485", - - "src/third_party/angle": - Var("chromium_git") + "/angle/angle.git@" + Var("angle_revision"), - - "src/third_party/colorama/src": - Var("chromium_git") + "/external/colorama.git@799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8", - - "src/third_party/trace-viewer": - (Var("googlecode_url") % "trace-viewer") + "/trunk@1449", - - "src/third_party/WebKit": - Var("webkit_trunk") + "@" + Var("webkit_revision"), - - "src/third_party/WebKit/LayoutTests/w3c/web-platform-tests": - Var("chromium_git") + - "/external/w3c/web-platform-tests.git@6bed4516fe8522d65512c76ef02e4f0ae8234395", - - "src/third_party/WebKit/LayoutTests/w3c/csswg-test": - Var("chromium_git") + - "/external/w3c/csswg-test.git@bacbb4a8dca702cd86646761fde96793db13d4f1", - - "src/third_party/icu": - "/trunk/deps/third_party/icu52@287122", - - "src/third_party/libexif/sources": - "/trunk/deps/third_party/libexif/sources@265008", - - "src/third_party/hunspell": - "/trunk/deps/third_party/hunspell@287123", - - "src/third_party/hunspell_dictionaries": - "/trunk/deps/third_party/hunspell_dictionaries@255132", - - "src/third_party/safe_browsing/testing": - (Var("googlecode_url") % "google-safe-browsing") + "/trunk/testing@112", - - "src/third_party/cacheinvalidation/src": - (Var("googlecode_url") % "google-cache-invalidation-api") + - "/trunk/src@335", - - "src/third_party/leveldatabase/src": - (Var("googlecode_url") % "leveldb") + "/trunk@80", - - "src/third_party/libc++/trunk": - Var("llvm_url") + "/libcxx/trunk@" + Var("libcxx_revision"), - - "src/third_party/libc++abi/trunk": - Var("llvm_url") + "/libcxxabi/trunk@" + Var("libcxxabi_revision"), - - "src/third_party/snappy/src": - (Var("googlecode_url") % "snappy") + "/trunk@80", - - "src/tools/grit": - (Var("googlecode_url") % "grit-i18n") + "/trunk@172", - - "src/tools/gyp": - (Var("googlecode_url") % "gyp") + "/trunk@1959", - - "src/tools/swarming_client": - Var("chromium_git") + "/external/swarming.client.git@" + - Var("swarming_revision"), - - "src/v8": - (Var("googlecode_url") % "v8") + "/" + Var("v8_branch") + "@" + - Var("v8_revision"), - - "src/native_client": - Var("nacl_trunk") + "/src/native_client@" + Var("nacl_revision"), - - "src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin": - Var("nacl_trunk") + "/src/native_client/tests/prebuilt@" + - Var("nacl_revision"), - - "src/third_party/sfntly/cpp/src": - (Var("googlecode_url") % "sfntly") + "/trunk/cpp/src@" + - Var("sfntly_revision"), - - "src/third_party/skia": - Var("chromium_git") + "/skia.git@" + Var("skia_revision"), - - "src/third_party/ots": - (Var("googlecode_url") % "ots") + "/trunk@115", - - "src/third_party/brotli/src": - Var("chromium_git") + - "/external/font-compression-reference.git@6cef49677dc4c650ef6e3f56041e0a41803afa8c", - - "src/tools/page_cycler/acid3": - "/trunk/deps/page_cycler/acid3@171600", - - "src/chrome/test/data/perf/canvas_bench": - "/trunk/deps/canvas_bench@122605", - - "src/chrome/test/data/perf/frame_rate/content": - "/trunk/deps/frame_rate/content@93671", - - "src/third_party/bidichecker": - (Var("googlecode_url") % "bidichecker") + "/trunk/lib@4", - - "src/third_party/webgl/src": - Var("chromium_git") + - "/external/khronosgroup/webgl.git@2cd3af21647cb0ff368106d88188046675baf52d", - - "src/third_party/swig/Lib": - "/trunk/deps/third_party/swig/Lib@" + Var("swig_revision"), - - "src/third_party/webdriver/pylib": - (Var("googlecode_url") % "selenium") + "/trunk/py@18456", - - "src/third_party/libvpx": - "/trunk/deps/third_party/libvpx@" + - Var("libvpx_revision"), - - "src/third_party/ffmpeg": - Var("chromium_git") + - "/chromium/third_party/ffmpeg.git@5ab896ad9dffd4a78e3c310de8e3507ded3e3239", - - "src/third_party/libjingle/source/talk": - (Var("googlecode_url") % "webrtc") + "/trunk/talk@" + - Var("webrtc_revision"), - - "src/third_party/usrsctp/usrsctplib": - (Var("googlecode_url") % "sctp-refimpl") + - "/trunk/KERN/usrsctp/usrsctplib@8912", - - "src/third_party/libsrtp": - "/trunk/deps/third_party/libsrtp@283418", - - "src/third_party/speex": - "/trunk/deps/third_party/speex@272757", - - "src/third_party/yasm/source/patched-yasm": - "/trunk/deps/third_party/yasm/patched-yasm@167605", - - "src/third_party/libjpeg_turbo": - "/trunk/deps/third_party/libjpeg_turbo@272637", - - "src/third_party/flac": - "/trunk/deps/third_party/flac@287124", - - "src/third_party/pyftpdlib/src": - (Var("googlecode_url") % "pyftpdlib") + "/trunk@977", - - "src/third_party/scons-2.0.1": - Var("nacl_trunk") + "/src/third_party/scons-2.0.1@" + - Var("nacl_tools_revision"), - - "src/third_party/webrtc": - (Var("googlecode_url") % "webrtc") + "/trunk/webrtc@" + Var("webrtc_revision"), - - "src/third_party/openmax_dl": - (Var("googlecode_url") % "webrtc") + "/deps/third_party/openmax@" + Var("openmax_dl_revision"), - - "src/third_party/jsoncpp/source/include": - (Var("sourceforge_url") % {"repo": "jsoncpp"}) + - "/trunk/jsoncpp/include@" + Var("jsoncpp_revision"), - - "src/third_party/jsoncpp/source/src/lib_json": - (Var("sourceforge_url") % {"repo": "jsoncpp"}) + - "/trunk/jsoncpp/src/lib_json@" + Var("jsoncpp_revision"), - - "src/third_party/libyuv": - (Var("googlecode_url") % "libyuv") + "/trunk@1035", - - "src/third_party/smhasher/src": - (Var("googlecode_url") % "smhasher") + "/trunk@152", - - "src/third_party/libaddressinput/src/cpp": - (Var("googlecode_url") % "libaddressinput") + "/trunk/cpp@" + - Var("libaddressinput_revision"), - "src/third_party/libaddressinput/src/testdata": - (Var("googlecode_url") % "libaddressinput") + "/trunk/testdata@" + - Var("libaddressinput_revision"), - - "src/third_party/libphonenumber/src/phonenumbers": - (Var("googlecode_url") % "libphonenumber") + - "/trunk/cpp/src/phonenumbers@" + Var("libphonenumber_revision"), - "src/third_party/libphonenumber/src/test": - (Var("googlecode_url") % "libphonenumber") + "/trunk/cpp/test@" + - Var("libphonenumber_revision"), - "src/third_party/libphonenumber/src/resources": - (Var("googlecode_url") % "libphonenumber") + "/trunk/resources@" + - Var("libphonenumber_revision"), - - "src/tools/deps2git": - "/trunk/tools/deps2git@276439", - - "src/third_party/webpagereplay": - Var("chromium_git") + "/external/web-page-replay.git@" + - "b62c02d3b64cf00a2f65a82cca0721aa42c3d6ad", - - "src/third_party/pywebsocket/src": - (Var("googlecode_url") % "pywebsocket") + "/trunk/src@790", - - "src/third_party/opus/src": - "/trunk/deps/third_party/opus@256783", - - "src/media/cdm/ppapi/api": - "/trunk/deps/cdm@273356", - - "src/third_party/mesa/src": - "/trunk/deps/third_party/mesa@265279", - - "src/third_party/cld_2/src": - (Var("googlecode_url") % "cld2") + "/trunk@165", - - "src/chrome/browser/resources/pdf/html_office": - Var("chromium_git") + - "/chromium/html-office-public.git@eeff97614f65e0578529490d44d412032c3d7359", - - "src/third_party/libwebm/source": - Var("chromium_git") + - "/webm/libwebm.git@0d4cb404ea4195e5e21d04db2c955615535ce62e", - - "src/third_party/openssl": - "/trunk/deps/third_party/openssl@" + Var("openssl_revision"), - - "src/third_party/pdfium": - Var("pdfium_git") + "/pdfium.git@" + Var("pdfium_revision"), - - "src/third_party/boringssl/src": - Var("boringssl_git") + "/boringssl.git@" + Var("boringssl_revision"), -} - - -deps_os = { - "win": { - "src/chrome/tools/test/reference_build/chrome_win": - "/trunk/deps/reference_builds/chrome_win@273882", - - "src/third_party/cygwin": - "/trunk/deps/third_party/cygwin@231940", - - "src/third_party/psyco_win32": - "/trunk/deps/third_party/psyco_win32@237949", - - "src/third_party/bison": - "/trunk/deps/third_party/bison@147303", - - "src/third_party/gperf": - "/trunk/deps/third_party/gperf@147304", - - "src/third_party/perl": - "/trunk/deps/third_party/perl@147900", - - "src/third_party/lighttpd": - "/trunk/deps/third_party/lighttpd@" + Var("lighttpd_revision"), - - # Parses Windows PE/COFF executable format. - "src/third_party/pefile": - Var("chromium_git") + '/external/pefile.git@' + - '72c6ae42396cb913bcab63c15585dc3b5c3f92f1', - - # NSS, for SSLClientSocketNSS. - "src/third_party/nss": - "/trunk/deps/third_party/nss@" + Var("nss_revision"), - - "src/third_party/swig/win": - "/trunk/deps/third_party/swig/win@" + Var("swig_revision"), - - # GNU binutils assembler for x86-32. - "src/third_party/gnu_binutils": - (Var("nacl_trunk") + "/deps/third_party/gnu_binutils@" + - Var("nacl_tools_revision")), - # GNU binutils assembler for x86-64. - "src/third_party/mingw-w64/mingw/bin": - (Var("nacl_trunk") + "/deps/third_party/mingw-w64/mingw/bin@" + - Var("nacl_tools_revision")), - - # Dependencies used by libjpeg-turbo - "src/third_party/yasm/binaries": - "/trunk/deps/third_party/yasm/binaries@154708", - - # Binaries for nacl sdk. - "src/third_party/nacl_sdk_binaries": - "/trunk/deps/third_party/nacl_sdk_binaries@111576", - }, - "ios": { - "src/third_party/google_toolbox_for_mac/src": - (Var("googlecode_url") % "google-toolbox-for-mac") + "/trunk@" + - Var("google_toolbox_for_mac_revision"), - - "src/third_party/nss": - "/trunk/deps/third_party/nss@" + Var("nss_revision"), - - # class-dump utility to generate header files for undocumented SDKs - "src/testing/iossim/third_party/class-dump": - "/trunk/deps/third_party/class-dump@199203", - - # Code that's not needed due to not building everything - "src/build/util/support": None, - "src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin": None, - "src/chrome/test/data/perf/canvas_bench": None, - "src/chrome/test/data/perf/frame_rate/content": None, - "src/media/cdm/ppapi/api": None, - "src/native_client": None, - "src/native_client/src/third_party/ppapi": None, - "src/third_party/angle": None, - "src/third_party/bidichecker": None, - "src/third_party/brotli/src": None, - "src/third_party/cld_2/src": None, - "src/third_party/ffmpeg": None, - "src/third_party/hunspell_dictionaries": None, - "src/third_party/hunspell": None, - "src/third_party/libc++/trunk": None, - "src/third_party/libc++abi/trunk": None, - "src/third_party/libexif/sources": None, - "src/third_party/libjpeg_turbo": None, - "src/third_party/libsrtp": None, - "src/third_party/mesa/src": None, - "src/third_party/opus/src": None, - "src/third_party/openmax_dl": None, - "src/third_party/ots": None, - "src/third_party/pymox/src": None, - "src/third_party/safe_browsing/testing": None, - "src/third_party/scons-2.0.1": None, - "src/third_party/sfntly/cpp/src": None, - "src/third_party/speex": None, - "src/third_party/swig/Lib": None, - "src/third_party/usrsctp/usrsctplib": None, - "src/third_party/v8-i18n": None, - "src/third_party/webdriver/pylib": None, - "src/third_party/webgl": None, - "src/third_party/webpagereplay": None, - "src/third_party/WebKit/LayoutTests/w3c/web-platform-tests": None, - "src/third_party/WebKit/LayoutTests/w3c/csswg-test": None, - "src/third_party/yasm/source/patched-yasm": None, - "src/tools/page_cycler/acid3": None, - "src/v8": None, - }, - "mac": { - "src/chrome/tools/test/reference_build/chrome_mac": - "/trunk/deps/reference_builds/chrome_mac@273882", - - "src/third_party/google_toolbox_for_mac/src": - (Var("googlecode_url") % "google-toolbox-for-mac") + "/trunk@" + - Var("google_toolbox_for_mac_revision"), - - "src/third_party/pdfsqueeze": - (Var("googlecode_url") % "pdfsqueeze") + "/trunk@5", - - "src/third_party/lighttpd": - "/trunk/deps/third_party/lighttpd@" + Var("lighttpd_revision"), - - "src/third_party/swig/mac": - "/trunk/deps/third_party/swig/mac@" + Var("swig_revision"), - - # NSS, for SSLClientSocketNSS. - "src/third_party/nss": - "/trunk/deps/third_party/nss@" + Var("nss_revision"), - - "src/chrome/installer/mac/third_party/xz/xz": - "/trunk/deps/third_party/xz@233311", - }, - "unix": { - # Linux, really. - "src/chrome/tools/test/reference_build/chrome_linux": - "/trunk/deps/reference_builds/chrome_linux64@273882", - - "src/third_party/xdg-utils": - "/trunk/deps/third_party/xdg-utils@203785", - - "src/third_party/swig/linux": - "/trunk/deps/third_party/swig/linux@" + Var("swig_revision"), - - "src/third_party/lss": - ((Var("googlecode_url") % "linux-syscall-support") + "/trunk/lss@" + - Var("lss_revision")), - - # For Linux and Chromium OS. - "src/third_party/cros_system_api": - Var("chromiumos_git") + "/platform/system_api.git" + - "@690c6b9702d7bcc761ae2d8976fe77e547255481", - - # Note that this is different from Android's freetype repo. - "src/third_party/freetype2/src": - Var("chromium_git") + "/chromium/src/third_party/freetype2.git" + - "@d699c2994ecc178c4ed05ac2086061b2034c2178", - - # Build tools for Chrome OS. - "src/third_party/chromite": - Var("chromiumos_git") + "/chromite.git" + - "@38242c6c77b77bcfb887fa97f3735c3262310370", - - # Dependency of chromite.git. - "src/third_party/pyelftools": - Var("chromiumos_git") + "/third_party/pyelftools.git" + - "@bdc1d380acd88d4bfaf47265008091483b0d614e", - - "src/third_party/undoview": - "/trunk/deps/third_party/undoview@119694", - - "src/third_party/liblouis/src": - Var("chromium_git") + - "/external/liblouis.git@3c2daee56250162e5a75830871601d74328d39f5", - - # Used for embedded builds. CrOS & Linux use the system version. - "src/third_party/fontconfig/src": - Var("chromium_git") + "/external/fontconfig.git" + - "@f16c3118e25546c1b749f9823c51827a60aeb5c1", - }, - "android": { - "src/third_party/android_tools": - Var("chromium_git") + "/android_tools.git" + - "@8301b711a9ac7de56e9a9ff3dee0b2ebfc9a380f", - - "src/third_party/aosp": - "/trunk/deps/third_party/aosp@148330", - - "src/third_party/apache-mime4j": - "/trunk/deps/third_party/apache-mime4j@170888", - - "src/third_party/findbugs": - "/trunk/deps/third_party/findbugs@245039", - - "src/third_party/freetype": - Var("chromium_git") + "/chromium/src/third_party/freetype.git" + - "@a2b9955b49034a51dfbc8bf9f4e9d312149cecac", - - "src/third_party/guava/src": - Var("chromium_git") + "/external/guava-libraries.git" + - "@c523556ab7d0f05afadebd20e7768d4c16af8771", - - "src/third_party/elfutils/src": - Var("chromium_git") + "/external/elfutils.git" + - "@249673729a7e5dbd5de4f3760bdcaa3d23d154d7", - - "src/third_party/httpcomponents-client": - "/trunk/deps/third_party/httpcomponents-client@170888", - - "src/third_party/httpcomponents-core": - "/trunk/deps/third_party/httpcomponents-core@170888", - - "src/third_party/jarjar": - "/trunk/deps/third_party/jarjar@170888", - - "src/third_party/jsr-305/src": - (Var("googlecode_url") % "jsr-305") + "/trunk@51", - - "src/third_party/lss": - ((Var("googlecode_url") % "linux-syscall-support") + "/trunk/lss@" + - Var("lss_revision")), - - "src/third_party/eyesfree/src/android/java/src/com/googlecode/eyesfree/braille": - (Var("googlecode_url") % "eyes-free") + "/trunk/braille/client/src/com/googlecode/eyesfree/braille@797", - }, -} - - -include_rules = [ - # Everybody can use some things. - "+base", - "+build", - "+ipc", - - # Everybody can use headers generated by tools/generate_library_loader. - "+library_loaders", - - "+testing", - "+third_party/icu/source/common/unicode", - "+third_party/icu/source/i18n/unicode", - "+url", -] - - -# checkdeps.py shouldn't check include paths for files in these dirs: -skip_child_includes = [ - "breakpad", - "delegate_execute", - "metro_driver", - "native_client_sdk", - "o3d", - "sdch", - "skia", - "testing", - "third_party", - "v8", - "win8", -] - - -hooks = [ - { - # This downloads binaries for Native Client's newlib toolchain. - # Done in lieu of building the toolchain from scratch as it can take - # anywhere from 30 minutes to 4 hours depending on platform to build. - "name": "nacltools", - "pattern": ".", - "action": [ - "python", "src/build/download_nacl_toolchains.py", - "--exclude", "arm_trusted", - ], - }, - { - # Downloads an ARM sysroot image to src/arm-sysroot. This image updates - # at about the same rate that the chrome build deps change. - # This script is a no-op except for linux users who have - # target_arch=arm in their GYP_DEFINES. - "name": "sysroot", - "pattern": ".", - "action": ["python", "src/build/linux/install-arm-sysroot.py", - "--linux-only"], - }, - { - # Downloads the Debian Wheezy sysroot to chrome/installer/linux if needed. - # This sysroot updates at about the same rate that the chrome build deps - # change. This script is a no-op except for linux users who are doing - # official chrome builds. - "name": "sysroot", - "pattern": ".", - "action": [ - "python", - "src/chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py", - "--linux-only", - "--arch=amd64"], - }, - { - # Same as above, but for 32-bit Linux. - "name": "sysroot", - "pattern": ".", - "action": [ - "python", - "src/chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py", - "--linux-only", - "--arch=i386"], - }, - { - # Pull clang if on Mac or clang is requested via GYP_DEFINES. - "name": "clang", - "pattern": ".", - "action": ["python", "src/tools/clang/scripts/update.py", "--if-needed"], - }, - { - # Update the Windows toolchain if necessary. - "name": "win_toolchain", - "pattern": ".", - "action": ["python", "src/build/vs_toolchain.py", "update"], - }, - { - # Update LASTCHANGE. This is also run by export_tarball.py in - # src/tools/export_tarball - please keep them in sync. - "name": "lastchange", - "pattern": ".", - "action": ["python", "src/build/util/lastchange.py", - "-o", "src/build/util/LASTCHANGE"], - }, - { - # Update LASTCHANGE.blink. This is also run by export_tarball.py in - # src/tools/export_tarball - please keep them in sync. - "name": "lastchange", - "pattern": ".", - "action": ["python", "src/build/util/lastchange.py", - "-s", "src/third_party/WebKit", - "-o", "src/build/util/LASTCHANGE.blink"], - }, - # Pull GN binaries. This needs to be before running GYP below. - { - "name": "gn_win", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=win32", - "--no_auth", - "--bucket", "chromium-gn", - "-s", "src/buildtools/win/gn.exe.sha1", - ], - }, - { - "name": "gn_mac", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=darwin", - "--no_auth", - "--bucket", "chromium-gn", - "-s", "src/buildtools/mac/gn.sha1", - ], - }, - { - "name": "gn_linux32", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=linux*", - "--no_auth", - "--bucket", "chromium-gn", - "-s", "src/buildtools/linux32/gn.sha1", - ], - }, - { - "name": "gn_linux64", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=linux*", - "--no_auth", - "--bucket", "chromium-gn", - "-s", "src/buildtools/linux64/gn.sha1", - ], - }, - { - # Remove GN binaries from tools/gn/bin that aren't used anymore. - # TODO(brettw) remove after the end of July, 2014. - "name": "remove_old_gn_binaries", - "pattern": ".", - "action": ["python", "src/tools/gn/bin/rm_binaries.py"], - }, - # Pull clang-format binaries using checked-in hashes. - { - "name": "clang_format_win", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=win32", - "--no_auth", - "--bucket", "chromium-clang-format", - "-s", "src/buildtools/win/clang-format.exe.sha1", - ], - }, - { - "name": "clang_format_mac", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=darwin", - "--no_auth", - "--bucket", "chromium-clang-format", - "-s", "src/buildtools/mac/clang-format.sha1", - ], - }, - { - "name": "clang_format_linux", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=linux*", - "--no_auth", - "--bucket", "chromium-clang-format", - "-s", "src/buildtools/linux64/clang-format.sha1", - ], - }, - # Pull binutils for linux, enabled debug fission for faster linking / - # debugging when used with clang on Ubuntu Precise. - # https://code.google.com/p/chromium/issues/detail?id=352046 - { - "name": "binutils", - "pattern": "src/third_party/binutils", - "action": [ - "python", - "src/third_party/binutils/download.py", - ], - }, - # Pull eu-strip binaries using checked-in hashes. - { - "name": "eu-strip", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=linux*", - "--no_auth", - "--bucket", "chromium-eu-strip", - "-s", "src/build/linux/bin/eu-strip.sha1", - ], - }, - { - "name": "drmemory", - "pattern": ".", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=win32", - "--no_auth", - "--bucket", "chromium-drmemory", - "-s", "src/third_party/drmemory/drmemory-windows-sfx.exe.sha1", - ], - }, - # Pull the Syzygy binaries, used for optimization and instrumentation. - { - "name": "syzygy-binaries", - "pattern": ".", - "action": ["python", - "src/build/get_syzygy_binaries.py", - "--output-dir=src/third_party/syzygy/binaries", - "--revision=363bc02a09c380b6f5f397606cc0744d85d54a51", - "--overwrite", - ], - }, - { - "name": "apache_win32", - "pattern": "\\.sha1", - "action": [ "download_from_google_storage", - "--no_resume", - "--platform=win32", - "--directory", - "--recursive", - "--no_auth", - "--num_threads=16", - "--bucket", "chromium-apache-win32", - "src/third_party/apache-win32", - ], - }, - { - # A change to a .gyp, .gypi, or to GYP itself should run the generator. - "name": "gyp", - "pattern": ".", - "action": ["python", "src/build/gyp_chromium"], - }, -] +vars = { + 'eyes-free': + 'http://eyes-free.googlecode.com/svn', + 'blink': + 'http://src.chromium.org/blink', + 'skia': + 'http://skia.googlecode.com/svn', + 'google-breakpad': + 'http://google-breakpad.googlecode.com/svn', + 'sawbuck': + 'http://sawbuck.googlecode.com/svn', + 'mozc': + 'http://mozc.googlecode.com/svn', + 'git.chromium.org': + 'https://chromium.googlesource.com', + 'v8-i18n': + 'http://v8-i18n.googlecode.com/svn', + 'selenium': + 'http://selenium.googlecode.com/svn', + 'buildspec_platforms': + 'all', + 'snappy': + 'http://snappy.googlecode.com/svn', + 'ppapi': + 'http://ppapi.googlecode.com/svn', + 'pywebsocket': + 'http://pywebsocket.googlecode.com/svn', + 'libaddressinput': + 'http://libaddressinput.googlecode.com/svn', + 'pyftpdlib': + 'http://pyftpdlib.googlecode.com/svn', + 'google-url': + 'http://google-url.googlecode.com/svn', + 'googletest': + 'http://googletest.googlecode.com/svn', + 'gyp': + 'http://gyp.googlecode.com/svn', + 'seccompsandbox': + 'http://seccompsandbox.googlecode.com/svn', + 'ots': + 'http://ots.googlecode.com/svn', + 'angleproject': + 'http://angleproject.googlecode.com/svn', + 'pefile': + 'http://pefile.googlecode.com/svn', + 'open-vcdiff': + 'http://open-vcdiff.googlecode.com/svn', + 'linux-syscall-support': + 'http://linux-syscall-support.googlecode.com/svn', + 'trace-viewer': + 'http://trace-viewer.googlecode.com/svn', + 'webrtc': + 'http://webrtc.googlecode.com/svn', + 'web-page-replay': + 'http://web-page-replay.googlecode.com/svn', + 'libjingle': + 'http://libjingle.googlecode.com/svn', + 'cld2': + 'https://cld2.googlecode.com/svn', + 'google-cache-invalidation-api': + 'http://google-cache-invalidation-api.googlecode.com/svn', + 'jsr-305': + 'http://jsr-305.googlecode.com/svn', + 'bidichecker': + 'http://bidichecker.googlecode.com/svn', + 'native_client': + 'http://src.chromium.org/native_client', + 'jsoncpp': + 'http://svn.code.sf.net/p/jsoncpp/code', + 'leveldb': + 'http://leveldb.googlecode.com/svn', + 'webkit_trunk': + 'http://src.chromium.org/blink/trunk', + 'google-toolbox-for-mac': + 'http://google-toolbox-for-mac.googlecode.com/svn', + 'grit-i18n': + 'http://grit-i18n.googlecode.com/svn', + 'pdfsqueeze': + 'http://pdfsqueeze.googlecode.com/svn', + 'protobuf': + 'http://protobuf.googlecode.com/svn', + 'smhasher': + 'http://smhasher.googlecode.com/svn', + 'googlemock': + 'http://googlemock.googlecode.com/svn', + 'libyuv': + 'http://libyuv.googlecode.com/svn', + 'rlz': + 'http://rlz.googlecode.com/svn', + 'v8': + 'http://v8.googlecode.com/svn', + 'pymox': + 'http://pymox.googlecode.com/svn', + 'sfntly': + 'http://sfntly.googlecode.com/svn', + 'sctp-refimpl': + 'https://sctp-refimpl.googlecode.com/svn', + 'libphonenumber': + 'http://libphonenumber.googlecode.com/svn', + 'octane-benchmark': + 'http://octane-benchmark.googlecode.com/svn', + 'google-safe-browsing': + 'http://google-safe-browsing.googlecode.com/svn' +} + +deps_os = { + 'win': { + 'src/third_party/yasm/binaries': + '/trunk/deps/third_party/yasm/binaries@154708', + 'src/chrome/tools/test/reference_build/chrome_win': + '/trunk/deps/reference_builds/chrome_win@273882', + 'src/third_party/nacl_sdk_binaries': + '/trunk/deps/third_party/nacl_sdk_binaries@111576', + 'src/third_party/lighttpd': + '/trunk/deps/third_party/lighttpd@33737', + 'src/third_party/gnu_binutils': + (Var("native_client")) + '/trunk/deps/third_party/gnu_binutils@13077', + 'src/third_party/pefile': + (Var("git.chromium.org")) + '/external/pefile.git@72c6ae42396cb913bcab63c15585dc3b5c3f92f1', + 'src/third_party/psyco_win32': + '/trunk/deps/third_party/psyco_win32@237949', + 'src/third_party/mingw-w64/mingw/bin': + (Var("native_client")) + '/trunk/deps/third_party/mingw-w64/mingw/bin@13077', + 'src/third_party/perl': + '/trunk/deps/third_party/perl@147900', + 'src/third_party/gperf': + '/trunk/deps/third_party/gperf@147304', + 'src/third_party/cygwin': + '/trunk/deps/third_party/cygwin@231940', + 'src/third_party/swig/win': + '/trunk/deps/third_party/swig/win@230490', + 'src/third_party/bison': + '/trunk/deps/third_party/bison@147303', + 'src/third_party/nss': + '/trunk/deps/third_party/nss@289430' + }, + 'mac': { + 'src/third_party/pdfsqueeze': + (Var("pdfsqueeze")) + '/trunk@5', + 'src/chrome/installer/mac/third_party/xz/xz': + '/trunk/deps/third_party/xz@233311', + 'src/third_party/lighttpd': + '/trunk/deps/third_party/lighttpd@33737', + 'src/third_party/swig/mac': + '/trunk/deps/third_party/swig/mac@230490', + 'src/third_party/google_toolbox_for_mac/src': + (Var("google-toolbox-for-mac")) + '/trunk@662', + 'src/chrome/tools/test/reference_build/chrome_mac': + '/trunk/deps/reference_builds/chrome_mac@273882', + 'src/third_party/nss': + '/trunk/deps/third_party/nss@289430' + }, + 'ios': { + 'src/third_party/webdriver/pylib': None, + 'src/third_party/sfntly/cpp/src': None, + 'src/third_party/bidichecker': None, + 'src/third_party/libc++/trunk': None, + 'src/tools/page_cycler/acid3': None, + 'src/v8': None, + 'src/chrome/test/data/perf/canvas_bench': None, + 'src/third_party/libexif/sources': None, + 'src/third_party/libc++abi/trunk': None, + 'src/third_party/libsrtp': None, + 'src/third_party/WebKit/LayoutTests/w3c/csswg-test': None, + 'src/third_party/pymox/src': None, + 'src/media/cdm/ppapi/api': None, + 'src/third_party/v8-i18n': None, + 'src/third_party/safe_browsing/testing': None, + 'src/chrome/test/data/perf/frame_rate/content': None, + 'src/third_party/openmax_dl': None, + 'src/third_party/google_toolbox_for_mac/src': + (Var("google-toolbox-for-mac")) + '/trunk@662', + 'src/native_client/src/third_party/ppapi': None, + 'src/third_party/yasm/source/patched-yasm': None, + 'src/third_party/WebKit/LayoutTests/w3c/web-platform-tests': None, + 'src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin': None, + 'src/third_party/speex': None, + 'src/third_party/mesa/src': None, + 'src/third_party/opus/src': None, + 'src/third_party/webpagereplay': None, + 'src/third_party/hunspell_dictionaries': None, + 'src/native_client': None, + 'src/third_party/usrsctp/usrsctplib': None, + 'src/third_party/brotli/src': None, + 'src/third_party/webgl': None, + 'src/third_party/hunspell': None, + 'src/testing/iossim/third_party/class-dump': + '/trunk/deps/third_party/class-dump@199203', + 'src/build/util/support': None, + 'src/third_party/cld_2/src': None, + 'src/third_party/libjpeg_turbo': None, + 'src/third_party/ots': None, + 'src/third_party/ffmpeg': None, + 'src/third_party/nss': + '/trunk/deps/third_party/nss@289430', + 'src/third_party/swig/Lib': None, + 'src/third_party/scons-2.0.1': None + }, + 'unix': { + 'src/third_party/fontconfig/src': + (Var("git.chromium.org")) + '/external/fontconfig.git@f16c3118e25546c1b749f9823c51827a60aeb5c1', + 'build/third_party/cbuildbot_chromite': + (Var("git.chromium.org")) + '/chromiumos/chromite.git@07ac932c5dbaaa0fc1de3db186669a5bf805ccd6', + 'src/third_party/cros_system_api': + (Var("git.chromium.org")) + '/chromiumos/platform/system_api.git@690c6b9702d7bcc761ae2d8976fe77e547255481', + 'src/third_party/pyelftools': + (Var("git.chromium.org")) + '/chromiumos/third_party/pyelftools.git@bdc1d380acd88d4bfaf47265008091483b0d614e', + 'src/third_party/chromite': + (Var("git.chromium.org")) + '/chromiumos/chromite.git@9350f2b004f2301477b76727b8c935aa8481c9f5', + 'build/third_party/xvfb': + '/trunk/tools/third_party/xvfb@125214', + 'src/third_party/xdg-utils': + '/trunk/deps/third_party/xdg-utils@203785', + 'src/third_party/undoview': + '/trunk/deps/third_party/undoview@119694', + 'src/chrome/tools/test/reference_build/chrome_linux': + '/trunk/deps/reference_builds/chrome_linux64@273882', + 'src/third_party/swig/linux': + '/trunk/deps/third_party/swig/linux@230490', + 'src/third_party/liblouis/src': + (Var("git.chromium.org")) + '/external/liblouis.git@3c2daee56250162e5a75830871601d74328d39f5', + 'src/third_party/freetype2/src': + (Var("git.chromium.org")) + '/chromium/src/third_party/freetype2.git@d699c2994ecc178c4ed05ac2086061b2034c2178', + 'src/third_party/lss': + (Var("linux-syscall-support")) + '/trunk/lss@31' + }, + 'android': { + 'src/third_party/guava/src': + (Var("git.chromium.org")) + '/external/guava-libraries.git@c523556ab7d0f05afadebd20e7768d4c16af8771', + 'src/third_party/eyesfree/src/android/java/src/com/googlecode/eyesfree/braille': + (Var("eyes-free")) + '/trunk/braille/client/src/com/googlecode/eyesfree/braille@797', + 'src/pdf': None, + 'src/third_party/apache-mime4j': + '/trunk/deps/third_party/apache-mime4j@170888', + 'src/third_party/elfutils/src': + (Var("git.chromium.org")) + '/external/elfutils.git@249673729a7e5dbd5de4f3760bdcaa3d23d154d7', + 'src/third_party/freetype': + (Var("git.chromium.org")) + '/chromium/src/third_party/freetype.git@a2b9955b49034a51dfbc8bf9f4e9d312149cecac', + 'src/third_party/jarjar': + '/trunk/deps/third_party/jarjar@170888', + 'src/third_party/aosp': + '/trunk/deps/third_party/aosp@148330', + 'src/third_party/android_tools': + (Var("git.chromium.org")) + '/android_tools.git@31869996507de16812bb53a3d0aaa15cd6194c16', + 'src/third_party/httpcomponents-client': + '/trunk/deps/third_party/httpcomponents-client@170888', + 'src/third_party/findbugs': + '/trunk/deps/third_party/findbugs@245039', + 'src/third_party/lss': + (Var("linux-syscall-support")) + '/trunk/lss@31', + 'src/third_party/jsr-305/src': + (Var("jsr-305")) + '/trunk@51', + 'src/third_party/httpcomponents-core': + '/trunk/deps/third_party/httpcomponents-core@170888' + } + } + +deps = { + 'depot_tools': + '/trunk/tools/depot_tools@289992', + 'src/third_party/sfntly/cpp/src': + (Var("sfntly")) + '/trunk/cpp/src@239', + 'src/third_party/bidichecker': + (Var("bidichecker")) + '/trunk/lib@4', + 'src/third_party/libc++/trunk': + 'http://src.chromium.org/llvm-project/libcxx/trunk@206024', + 'src/third_party/libwebm/source': + (Var("git.chromium.org")) + '/webm/libwebm.git@0d4cb404ea4195e5e21d04db2c955615535ce62e', + 'src/third_party/WebKit': + (Var("blink")) + '/branches/chromium/2125@182060', + 'src/third_party/openmax_dl': + (Var("webrtc")) + '/deps/third_party/openmax@6777', + 'src/third_party/libc++abi/trunk': + 'http://src.chromium.org/llvm-project/libcxxabi/trunk@206024', + 'build/scripts/private/data/reliability': + '/trunk/src/chrome/test/data/reliability@237714', + 'src/third_party/WebKit/LayoutTests/w3c/csswg-test': + (Var("git.chromium.org")) + '/external/w3c/csswg-test.git@f9b9daa33eac525923e941333d389fcb42f9b19f', + 'src/third_party/flac': + '/trunk/deps/third_party/flac@287124', + 'src/media/cdm/ppapi/api': + '/trunk/deps/cdm@288172', + 'src/third_party/skia': + (Var("git.chromium.org")) + '/skia.git@773a327b12f653c3bb0630fee1b3ba9629f5717e', + 'src/tools/swarming_client': + (Var("git.chromium.org")) + '/external/swarming.client.git@bbf1fcca7932d92cca9d7dab46ea271a7f6d61fb', + 'src/chrome/test/data/perf/frame_rate/content': + '/trunk/deps/frame_rate/content@93671', + 'src/third_party/ots': + (Var("ots")) + '/trunk@115', + 'src/third_party/jsoncpp/source/src/lib_json': + (Var("jsoncpp")) + '/trunk/jsoncpp/src/lib_json@248', + 'src/third_party/WebKit/LayoutTests/w3c/web-platform-tests': + (Var("git.chromium.org")) + '/external/w3c/web-platform-tests.git@6c7bd34e408ff69687a95d75b7e562fa84148997', + 'src/third_party/libaddressinput/src/testdata': + (Var("libaddressinput")) + '/trunk/testdata@327', + 'src/testing/gmock': + (Var("git.chromium.org")) + '/external/googlemock.git@896ba0e03f520fb9b6ed582bde2bd00847e3c3f2', + 'src/tools/grit': + (Var("grit-i18n")) + '/trunk@172', + 'src/third_party/mesa/src': + '/trunk/deps/third_party/mesa@265279', + 'src/third_party/smhasher/src': + (Var("smhasher")) + '/trunk@152', + 'src/third_party/webrtc': + (Var("webrtc")) + '/branches/38/webrtc@7177', + 'build/scripts/tools/deps2git': + '/trunk/tools/deps2git@285662', + 'src/third_party/hunspell_dictionaries': + '/trunk/deps/third_party/hunspell_dictionaries@255132', + 'src/native_client': + (Var("native_client")) + '/branches/2125/src/native_client@13665', + 'src/third_party/brotli/src': + (Var("git.chromium.org")) + '/external/font-compression-reference.git@6cef49677dc4c650ef6e3f56041e0a41803afa8c', + 'src/tools/page_cycler/acid3': + '/trunk/deps/page_cycler/acid3@171600', + 'src/third_party/cacheinvalidation/src': + (Var("google-cache-invalidation-api")) + '/trunk/src@335', + 'src/chrome/test/data/extensions/api_test/permissions/nacl_enabled/bin': + (Var("native_client")) + '/branches/2125/src/native_client/tests/prebuilt@13665', + 'src/third_party/leveldatabase/src': + (Var("leveldb")) + '/trunk@80', + 'src/third_party/webpagereplay': + (Var("git.chromium.org")) + '/external/web-page-replay.git@b62c02d3b64cf00a2f65a82cca0721aa42c3d6ad', + 'src/third_party/cld_2/src': + (Var("cld2")) + '/trunk@166', + 'src/chrome/test/data/perf/canvas_bench': + '/trunk/deps/canvas_bench@122605', + 'src/sdch/open-vcdiff': + (Var("git.chromium.org")) + '/external/open-vcdiff.git@438f2a5be6d809bc21611a94cd37bfc8c28ceb33', + 'src/third_party/angle': + (Var("git.chromium.org")) + '/angle/angle.git@bc75f36b04c8bab99bcecf8cdca16ecee3ac58ee', + 'build/third_party/lighttpd': + '/trunk/deps/third_party/lighttpd@58968', + 'src/buildtools': + (Var("git.chromium.org")) + '/chromium/buildtools.git@48edf30c463fc41fb9fb0926f8466b473cb177fa', + 'src/third_party/scons-2.0.1': + (Var("native_client")) + '/trunk/src/third_party/scons-2.0.1@13077', + 'src/third_party/webdriver/pylib': + (Var("selenium")) + '/trunk/py@18456', + 'src/third_party/usrsctp/usrsctplib': + (Var("sctp-refimpl")) + '/trunk/KERN/usrsctp/usrsctplib@8912', + 'build/scripts/gsd_generate_index': + '/trunk/tools/gsd_generate_index@164784', + 'src/chrome/browser/resources/pdf/html_office': + (Var("git.chromium.org")) + '/chromium/html-office-public.git@eeff97614f65e0578529490d44d412032c3d7359', + 'src/third_party/webgl/src': + (Var("git.chromium.org")) + '/external/khronosgroup/webgl.git@c81b6b214549bfeb475c25bf1de7bd3096c5d1c3', + 'src/third_party/libyuv': + (Var("libyuv")) + '/trunk@1038', + 'src/third_party/libjingle/source/talk': + (Var("webrtc")) + '/trunk/talk@6905', + 'src/third_party/libexif/sources': + '/trunk/deps/third_party/libexif/sources@265008', + 'src/third_party/jsoncpp/source/include': + (Var("jsoncpp")) + '/trunk/jsoncpp/include@248', + 'src/third_party/libphonenumber/src/test': + (Var("libphonenumber")) + '/trunk/cpp/test@621', + 'src/third_party/pdfium': + 'https://pdfium.googlesource.com/pdfium.git@2fd7b0be4473f4c9877fed2c2ade403c71ded395', + 'src/third_party/libphonenumber/src/phonenumbers': + (Var("libphonenumber")) + '/trunk/cpp/src/phonenumbers@621', + 'src/third_party/openssl': + '/trunk/deps/third_party/openssl@284247', + 'src/third_party/trace-viewer': + (Var("git.chromium.org")) + '/external/trace-viewer.git@5f89b7cd5732ddf7e783bb6c9fe57bb8a7cf74e2', + 'src/third_party/yasm/source/patched-yasm': + '/trunk/deps/third_party/yasm/patched-yasm@167605', + 'src/third_party/safe_browsing/testing': + (Var("google-safe-browsing")) + '/trunk/testing@112', + 'src/third_party/ffmpeg': + (Var("git.chromium.org")) + '/chromium/third_party/ffmpeg.git@98ca32e50f6e38447bc81705d0689ebceb6ac649', + 'build': + '/trunk/tools/build@290047', + 'build/scripts/command_wrapper/bin': + '/trunk/tools/command_wrapper/bin@135178', + 'src/testing/gtest': + (Var("git.chromium.org")) + '/external/googletest.git@4650552ff637bb44ecf7784060091cbed3252211', + 'src/third_party/pyftpdlib/src': + (Var("pyftpdlib")) + '/trunk@977', + 'src/third_party/libaddressinput/src/cpp': + (Var("libaddressinput")) + '/trunk/cpp@327', + 'src/third_party/icu': + '/trunk/deps/third_party/icu52@287122', + 'src/third_party/speex': + '/trunk/deps/third_party/speex@272757', + 'src/third_party/opus/src': + '/trunk/deps/third_party/opus@289085', + 'src/third_party/libphonenumber/src/resources': + (Var("libphonenumber")) + '/trunk/resources@621', + 'src/third_party/colorama/src': + (Var("git.chromium.org")) + '/external/colorama.git@799604a1041e9b3bc5d2789ecbd7e8db2e18e6b8', + 'src/third_party/snappy/src': + (Var("snappy")) + '/trunk@80', + 'src/breakpad/src': + (Var("git.chromium.org")) + '/external/google-breakpad/src.git@17f614cd8365f5b78577ffcc237300a970db9813', + 'src/third_party/hunspell': + '/trunk/deps/third_party/hunspell@287123', + 'src/tools/deps2git': + '/trunk/tools/deps2git@276439', + 'src/third_party/libjpeg_turbo': + '/trunk/deps/third_party/libjpeg_turbo@291725', + 'src/third_party/libsrtp': + '/trunk/deps/third_party/libsrtp@283418', + 'src/v8': + (Var("v8")) + '/branches/3.28@23836', + 'src/third_party/pywebsocket/src': + (Var("pywebsocket")) + '/trunk/src@790', + 'src/third_party/libvpx': + '/branches/2125/deps/third_party/libvpx@291831', + 'src/third_party/boringssl/src': + 'https://boringssl.googlesource.com/boringssl.git@c3d79605ab06cffa87877bcfe0792f767bde8b90', + 'src/third_party/swig/Lib': + '/trunk/deps/third_party/swig/Lib@230490', + 'src/tools/gyp': + (Var("gyp")) + '/trunk@1964' +} + +skip_child_includes = [ + 'breakpad', + 'delegate_execute', + 'metro_driver', + 'native_client_sdk', + 'o3d', + 'sdch', + 'skia', + 'testing', + 'third_party', + 'v8', + 'win8' +] + +hooks = [{ + 'action': ['python', + 'src/build/landmines.py'], + 'pattern': + '.', + 'name': + 'landmines' +}, +{ + 'action': ['python', + 'src/build/download_nacl_toolchains.py', + '--exclude', + 'arm_trusted'], + 'pattern': + '.', + 'name': + 'nacltools' +}, +{ + 'action': ['python', + 'src/build/linux/install-arm-sysroot.py', + '--linux-only'], + 'pattern': + '.', + 'name': + 'sysroot' +}, +{ + 'action': ['python', + 'src/chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py', + '--linux-only', + '--arch=amd64'], + 'pattern': + '.', + 'name': + 'sysroot' +}, +{ + 'action': ['python', + 'src/chrome/installer/linux/sysroot_scripts/install-debian.wheezy.sysroot.py', + '--linux-only', + '--arch=i386'], + 'pattern': + '.', + 'name': + 'sysroot' +}, +{ + 'action': ['python', + 'src/tools/clang/scripts/update.py', + '--if-needed'], + 'pattern': + '.', + 'name': + 'clang' +}, +{ + 'action': ['python', + 'src/build/vs_toolchain.py', + 'update'], + 'pattern': + '.', + 'name': + 'win_toolchain' +}, +{ + 'action': ['python', + 'src/build/util/lastchange.py', + '-o', + 'src/build/util/LASTCHANGE'], + 'pattern': + '.', + 'name': + 'lastchange' +}, +{ + 'action': ['python', + 'src/build/util/lastchange.py', + '-s', + 'src/third_party/WebKit', + '-o', + 'src/build/util/LASTCHANGE.blink'], + 'pattern': + '.', + 'name': + 'lastchange' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', + 'chromium-gn', + '-s', + 'src/buildtools/win/gn.exe.sha1'], + 'pattern': + '.', + 'name': + 'gn_win' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', + 'chromium-gn', + '-s', + 'src/buildtools/mac/gn.sha1'], + 'pattern': + '.', + 'name': + 'gn_mac' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', + 'chromium-gn', + '-s', + 'src/buildtools/linux32/gn.sha1'], + 'pattern': + '.', + 'name': + 'gn_linux32' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', + 'chromium-gn', + '-s', + 'src/buildtools/linux64/gn.sha1'], + 'pattern': + '.', + 'name': + 'gn_linux64' +}, +{ + 'action': ['python', + 'src/tools/gn/bin/rm_binaries.py'], + 'pattern': + '.', + 'name': + 'remove_old_gn_binaries' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', + 'chromium-clang-format', + '-s', + 'src/buildtools/win/clang-format.exe.sha1'], + 'pattern': + '.', + 'name': + 'clang_format_win' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=darwin', + '--no_auth', + '--bucket', + 'chromium-clang-format', + '-s', + 'src/buildtools/mac/clang-format.sha1'], + 'pattern': + '.', + 'name': + 'clang_format_mac' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', + 'chromium-clang-format', + '-s', + 'src/buildtools/linux64/clang-format.sha1'], + 'pattern': + '.', + 'name': + 'clang_format_linux' +}, +{ + 'action': ['python', + 'src/third_party/binutils/download.py'], + 'pattern': + 'src/third_party/binutils', + 'name': + 'binutils' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=linux*', + '--no_auth', + '--bucket', + 'chromium-eu-strip', + '-s', + 'src/build/linux/bin/eu-strip.sha1'], + 'pattern': + '.', + 'name': + 'eu-strip' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=win32', + '--no_auth', + '--bucket', + 'chromium-drmemory', + '-s', + 'src/third_party/drmemory/drmemory-windows-sfx.exe.sha1'], + 'pattern': + '.', + 'name': + 'drmemory' +}, +{ + 'action': ['python', + 'src/build/get_syzygy_binaries.py', + '--output-dir=src/third_party/syzygy/binaries', + '--revision=363bc02a09c380b6f5f397606cc0744d85d54a51', + '--overwrite'], + 'pattern': + '.', + 'name': + 'syzygy-binaries' +}, +{ + 'action': ['download_from_google_storage', + '--no_resume', + '--platform=win32', + '--directory', + '--recursive', + '--no_auth', + '--num_threads=16', + '--bucket', + 'chromium-apache-win32', + 'src/third_party/apache-win32'], + 'pattern': + '\\.sha1', + 'name': + 'apache_win32' +}, +{ + 'action': [ + 'python', + 'src/build/gyp_chromium'], + 'pattern': + '.', + 'name': + 'gyp' + } +] + +include_rules = [ + '+base', + '+build', + '+ipc', + '+library_loaders', + '+testing', + '+third_party/icu/source/common/unicode', + '+third_party/icu/source/i18n/unicode', + '+url' +] + diff --git a/PRESUBMIT.py b/PRESUBMIT.py index a9d0502bcc0d8..581b700fd69ee 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -718,9 +718,12 @@ def _CheckIncludeOrder(input_api, output_api): Each region separated by #if, #elif, #else, #endif, #define and #undef follows these rules separately. """ + def FileFilterIncludeOrder(affected_file): + black_list = (_EXCLUDED_PATHS + input_api.DEFAULT_BLACK_LIST) + return input_api.FilterSourceFile(affected_file, black_list=black_list) warnings = [] - for f in input_api.AffectedFiles(): + for f in input_api.AffectedFiles(file_filter=FileFilterIncludeOrder): if f.LocalPath().endswith(('.cc', '.h')): changed_linenums = set(line_num for line_num, _ in f.ChangedContents()) warnings.extend(_CheckIncludeOrderInFile(input_api, f, changed_linenums)) @@ -920,6 +923,8 @@ def _CheckSpamLogging(input_api, output_api): r"^chrome[\\\/]browser[\\\/]ui[\\\/]startup[\\\/]" r"startup_browser_creator\.cc$", r"^chrome[\\\/]installer[\\\/]setup[\\\/].*", + r"chrome[\\\/]browser[\\\/]diagnostics[\\\/]" + + r"diagnostics_writer\.cc$", r"^chrome_elf[\\\/]dll_hash[\\\/]dll_hash_main\.cc$", r"^chromecast[\\\/]", r"^cloud_print[\\\/]", @@ -1522,7 +1527,7 @@ def GetDefaultTryConfigs(bots=None): 'mac_nacl_sdk_build': ['compile'], 'win_chromium_compile_dbg': ['defaulttests'], 'win_chromium_dbg': ['defaulttests'], - 'win_chromium_rel': ['defaulttests'], + 'win_chromium_rel_swarming': ['defaulttests'], 'win_chromium_x64_rel': ['defaulttests'], 'win_gpu': ['defaulttests'], 'win_nacl_sdk_build': ['compile'], @@ -1575,7 +1580,10 @@ def GetPreferredTryMasters(project, change): 'mac_chromium_rel_swarming', ]) if all(re.search('(^|[/_])win[/_.]', f) for f in files): - return GetDefaultTryConfigs(['win_chromium_dbg', 'win_chromium_rel']) + return GetDefaultTryConfigs([ + 'win_chromium_dbg', + 'win_chromium_rel_swarming', + ]) if all(re.search('(^|[/_])android[/_.]', f) for f in files): return GetDefaultTryConfigs([ 'android_aosp', @@ -1600,7 +1608,7 @@ def GetPreferredTryMasters(project, change): 'mac_chromium_rel_swarming', 'mac_gpu', 'win_chromium_compile_dbg', - 'win_chromium_rel', + 'win_chromium_rel_swarming', 'win_chromium_x64_rel', 'win_gpu', ] diff --git a/PRESUBMIT_test.py b/PRESUBMIT_test.py index 10d06904f4815..dd8dcbeb92a66 100755 --- a/PRESUBMIT_test.py +++ b/PRESUBMIT_test.py @@ -27,7 +27,7 @@ def __init__(self): self.files = [] self.is_committing = False - def AffectedFiles(self): + def AffectedFiles(self, file_filter=None): return self.files def PresubmitLocalPath(self): diff --git a/WATCHLISTS b/WATCHLISTS index dac9fe9aba005..3d32c4d3b5d7e 100644 --- a/WATCHLISTS +++ b/WATCHLISTS @@ -150,7 +150,6 @@ 'browser_components': { 'filepath': 'chrome/browser/autofill/' \ '|chrome/browser/bookmarks/' \ - '|chrome/browser/common/' \ '|chrome/browser/favicon/' \ '|chrome/browser/history/' \ '|chrome/browser/webdata/' \ @@ -170,7 +169,12 @@ 'chrome/browser/ui/views/bubble/', }, 'cast': { - 'filepath': 'media/cast/', + 'filepath': 'media/cast/'\ + '|chrome/browser/extensions/api/cast_streaming/'\ + '|chrome/browser/media/cast'\ + '|chrome/renderer/media/cast'\ + '|chrome/test/data/extensions/api_test/cast_'\ + '|content/public/renderer/media_stream_', }, 'chromecast': { 'filepath': 'chromecast/', @@ -359,6 +363,7 @@ 'hotword': { 'filepath': 'chrome/browser/extensions/api/hotword_private/'\ '|chrome/browser/resources/hotword_helper/'\ + '|chrome/browser/resources/hotword/'\ '|chrome/browser/search/hotword*'\ '|chrome/test/data/extensions/api_test/hotword_private/', }, @@ -596,27 +601,31 @@ 'filepath': 'sync/protocol/', }, 'tab_capture': { - 'filepath': 'chrome/browser/extensions/api/tab_capture/'\ - '|chrome/browser/media/media_stream'\ - '|content/browser/renderer_host/(backing_store|compositing_iosurface|'\ - 'render_widget_host_view)'\ - '|content/browser/renderer_host/media/(audio_|'\ - 'media_stream_manager\.cc|video_|web_contents_)'\ - '|content/browser/web_contents/web_contents_impl'\ - '|content/renderer/media/audio_(device_factory|message_filter|'\ - 'renderer_mixer_manager)'\ - '|content/renderer/p2p/ipc_socket_factory'\ - '|content/renderer/pepper/pepper_platform_audio_output'\ - '|media/audio/(audio_output|fake_audio_consumer|virtual_audio)'\ - '|media/base/video_util'\ - '|media/video/capture/video_capture'\ - '|ui/surface/accelerated_surface.*_win', + 'filepath': 'cc/output/copy_output_request'\ + '|chrome/browser/extensions/api/tab_capture/'\ + '|chrome/test/data/extensions/api_test/tab_capture/'\ + '|content/browser/compositor/delegated_frame_host'\ + '|content/browser/media/capture/'\ + '|content/browser/renderer_host/compositing_iosurface'\ + '|content/browser/renderer_host/media/(audio_'\ + '|media_stream_manager|video_)'\ + '|media/audio/(audio_output_controller|fake_audio_|virtual_audio_)'\ + '|media/base/video_frame\.h'\ + '|media/video/capture/video_capture_', }, 'tab_contents': { 'filepath': 'chrome/browser/tab_contents/|'\ 'content/browser/tab_contents/|'\ 'chrome/browser/ui/tab_contents/', }, + 'tab_media_indicators': { + 'filepath': 'chrome/browser/media/audio_stream_monitor'\ + '|chrome/browser/ui/cocoa/tabs/tab_controller\.mm'\ + '|chrome/browser/ui/tabs/tab_utils'\ + '|chrome/browser/ui/views/tabs/tab\.cc'\ + '|chrome/browser/ui/views/tabs/tab_renderer_data'\ + '|media/audio/audio_(output_controller|power_monitor)', + }, 'task_manager': { 'filepath': 'task_manager' }, @@ -856,7 +865,7 @@ 'i18n': ['jshin+watch@chromium.org'], 'imageburner': ['tbarzic+watch@chromium.org'], 'importer': ['tfarina@chromium.org'], - 'indexed_db': ['alecflett@chromium.org', 'cmumford@chromium.org', + 'indexed_db': ['cmumford@chromium.org', 'dgrogan@chromium.org', 'jsbell+idb@chromium.org'], 'installer': ['grt+watch@chromium.org', 'wfh+watch@chromium.org'], 'instant': ['dcblack@chromium.org', 'jered@chromium.org', @@ -930,7 +939,6 @@ 'kinuko+serviceworker@chromium.org', 'nhiroki@chromium.org', 'horo+watch@chromium.org', - 'alecflett+watch@chromium.org', 'jsbell+serviceworker@chromium.org', 'michaeln@chromium.org', 'serviceworker-reviews@chromium.org'], @@ -951,6 +959,7 @@ 'tab_capture': ['miu+watch@chromium.org'], 'tab_contents': ['avi@chromium.org', 'creis+watch@chromium.org', 'ajwong+watch@chromium.org'], + 'tab_media_indicators': ['miu+watch@chromium.org'], 'tcmalloc': ['dmikurube+memory@chromium.org'], 'telemetry': ['telemetry+watch@chromium.org'], 'tests': [], diff --git a/android_webview/Android.mk b/android_webview/Android.mk index 84e136479331a..d0264b4fb7202 100644 --- a/android_webview/Android.mk +++ b/android_webview/Android.mk @@ -63,317 +63,317 @@ include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_am.pak LOCAL_MODULE_STEM := am -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_am.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_am.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ar.pak LOCAL_MODULE_STEM := ar -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ar.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ar.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_bg.pak LOCAL_MODULE_STEM := bg -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_bg.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_bg.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_bn.pak LOCAL_MODULE_STEM := bn -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_bn.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_bn.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ca.pak LOCAL_MODULE_STEM := ca -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ca.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ca.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_cs.pak LOCAL_MODULE_STEM := cs -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_cs.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_cs.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_da.pak LOCAL_MODULE_STEM := da -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_da.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_da.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_de.pak LOCAL_MODULE_STEM := de -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_de.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_de.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_el.pak LOCAL_MODULE_STEM := el -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_el.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_el.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_en-GB.pak LOCAL_MODULE_STEM := en-GB -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_en-GB.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_en-GB.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_en-US.pak LOCAL_MODULE_STEM := en-US -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_en-US.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_en-US.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_es-419.pak LOCAL_MODULE_STEM := es-419 -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_es-419.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_es-419.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_es.pak LOCAL_MODULE_STEM := es -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_es.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_es.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_et.pak LOCAL_MODULE_STEM := et -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_et.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_et.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_fa.pak LOCAL_MODULE_STEM := fa -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_fa.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_fa.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_fil.pak LOCAL_MODULE_STEM := fil -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_fil.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_fil.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_fi.pak LOCAL_MODULE_STEM := fi -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_fi.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_fi.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_fr.pak LOCAL_MODULE_STEM := fr -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_fr.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_fr.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_gu.pak LOCAL_MODULE_STEM := gu -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_gu.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_gu.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_he.pak LOCAL_MODULE_STEM := he -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_he.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_he.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_hi.pak LOCAL_MODULE_STEM := hi -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_hi.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_hi.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_hr.pak LOCAL_MODULE_STEM := hr -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_hr.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_hr.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_hu.pak LOCAL_MODULE_STEM := hu -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_hu.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_hu.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_id.pak LOCAL_MODULE_STEM := id -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_id.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_id.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_it.pak LOCAL_MODULE_STEM := it -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_it.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_it.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ja.pak LOCAL_MODULE_STEM := ja -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ja.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ja.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_kn.pak LOCAL_MODULE_STEM := kn -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_kn.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_kn.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ko.pak LOCAL_MODULE_STEM := ko -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ko.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ko.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_lt.pak LOCAL_MODULE_STEM := lt -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_lt.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_lt.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_lv.pak LOCAL_MODULE_STEM := lv -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_lv.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_lv.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ml.pak LOCAL_MODULE_STEM := ml -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ml.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ml.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_mr.pak LOCAL_MODULE_STEM := mr -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_mr.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_mr.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ms.pak LOCAL_MODULE_STEM := ms -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ms.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ms.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_nb.pak LOCAL_MODULE_STEM := nb -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_nb.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_nb.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_nl.pak LOCAL_MODULE_STEM := nl -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_nl.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_nl.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_pl.pak LOCAL_MODULE_STEM := pl -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_pl.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_pl.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_pt-BR.pak LOCAL_MODULE_STEM := pt-BR -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_pt-BR.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_pt-BR.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_pt-PT.pak LOCAL_MODULE_STEM := pt-PT -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_pt-PT.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_pt-PT.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ro.pak LOCAL_MODULE_STEM := ro -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ro.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ro.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ru.pak LOCAL_MODULE_STEM := ru -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ru.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ru.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_sk.pak LOCAL_MODULE_STEM := sk -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_sk.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_sk.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_sl.pak LOCAL_MODULE_STEM := sl -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_sl.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_sl.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_sr.pak LOCAL_MODULE_STEM := sr -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_sr.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_sr.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_sv.pak LOCAL_MODULE_STEM := sv -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_sv.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_sv.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_sw.pak LOCAL_MODULE_STEM := sw -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_sw.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_sw.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_ta.pak LOCAL_MODULE_STEM := ta -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_ta.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_ta.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_te.pak LOCAL_MODULE_STEM := te -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_te.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_te.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_th.pak LOCAL_MODULE_STEM := th -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_th.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_th.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_tr.pak LOCAL_MODULE_STEM := tr -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_tr.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_tr.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_uk.pak LOCAL_MODULE_STEM := uk -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_uk.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_uk.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_vi.pak LOCAL_MODULE_STEM := vi -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_vi.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_vi.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_zh-CN.pak LOCAL_MODULE_STEM := zh-CN -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_zh-CN.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_zh-CN.pak include $(LOCAL_PATH)/webview_pak.mk include $(CLEAR_VARS) LOCAL_MODULE := webviewchromium_webkit_strings_zh-TW.pak LOCAL_MODULE_STEM := zh-TW -LOCAL_BUILT_MODULE_STEM := webkit/webkit_strings_zh-TW.pak +LOCAL_BUILT_MODULE_STEM := content/app/strings/content_strings_zh-TW.pak include $(LOCAL_PATH)/webview_pak.mk diff --git a/android_webview/android_webview.gyp b/android_webview/android_webview.gyp index 650f65335d0a0..60896e8c44df4 100644 --- a/android_webview/android_webview.gyp +++ b/android_webview/android_webview.gyp @@ -67,7 +67,7 @@ '<(DEPTH)/net/net.gyp:net_resources', '<(DEPTH)/third_party/WebKit/public/blink_resources.gyp:blink_resources', '<(DEPTH)/ui/resources/ui_resources.gyp:ui_resources', - '<(DEPTH)/webkit/webkit_resources.gyp:webkit_resources', + '<(DEPTH)/webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], 'actions': [ { @@ -87,7 +87,7 @@ { 'action_name': 'add_en_US_pak_locale', 'variables': { - 'pak_inputs': ['<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.pak'], + 'pak_inputs': ['<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak'], 'pak_output': '<(PRODUCT_DIR)/android_webview_apk/assets/en-US.pak', }, 'includes': [ '../build/repack_action.gypi' ], @@ -110,6 +110,7 @@ '../components/components.gyp:web_contents_delegate_android', '../content/content.gyp:content_app_both', '../content/content.gyp:content_browser', + '../gpu/command_buffer/command_buffer.gyp:gles2_utils', '../gpu/gpu.gyp:command_buffer_service', '../gpu/gpu.gyp:gles2_implementation', '../gpu/gpu.gyp:gl_in_process_context', @@ -117,7 +118,6 @@ '../printing/printing.gyp:printing', '../skia/skia.gyp:skia', '../third_party/WebKit/public/blink.gyp:blink', - '../v8/tools/gyp/v8.gyp:v8', '../ui/gl/gl.gyp:gl', '../ui/shell_dialogs/shell_dialogs.gyp:shell_dialogs', '../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu', @@ -239,8 +239,6 @@ 'public/browser/draw_gl.h', 'renderer/aw_content_renderer_client.cc', 'renderer/aw_content_renderer_client.h', - 'renderer/aw_execution_termination_filter.cc', - 'renderer/aw_execution_termination_filter.h', 'renderer/aw_key_systems.cc', 'renderer/aw_key_systems.h', 'renderer/aw_permission_client.cc', diff --git a/android_webview/android_webview_telemetry_shell.gyp b/android_webview/android_webview_telemetry_shell.gyp new file mode 100644 index 0000000000000..7d812ff31b473 --- /dev/null +++ b/android_webview/android_webview_telemetry_shell.gyp @@ -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. +{ + 'targets': [ + { + 'target_name': 'android_webview_telemetry_shell_apk', + 'type': 'none', + 'variables': { + 'apk_name': 'AndroidWebViewTelemetryShell', + 'java_in_dir': 'tools/WebViewTelemetryShell', + 'resource_dir': 'tools/WebViewTelemetryShell/res', + }, + 'includes': [ '../build/java_apk.gypi' ], + }, + ], +} diff --git a/android_webview/browser/aw_browser_context.cc b/android_webview/browser/aw_browser_context.cc index 614bd820bf2ec..262662439c430 100644 --- a/android_webview/browser/aw_browser_context.cc +++ b/android_webview/browser/aw_browser_context.cc @@ -11,11 +11,11 @@ #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 "base/bind.h" #include "base/prefs/pref_registry_simple.h" #include "base/prefs/pref_service.h" #include "base/prefs/pref_service_factory.h" #include "components/autofill/core/common/autofill_pref_names.h" -#include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_config_service.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_prefs.h" @@ -27,10 +27,11 @@ #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "net/cookies/cookie_store.h" +#include "net/proxy/proxy_service.h" using base::FilePath; using content::BrowserThread; -using data_reduction_proxy::DataReductionProxyAuthRequestHandler; +using data_reduction_proxy::DataReductionProxyConfigService; using data_reduction_proxy::DataReductionProxySettings; namespace android_webview { @@ -103,23 +104,31 @@ void AwBrowserContext::PreMainMessageLoopRun() { new DataReductionProxySettings( new data_reduction_proxy::DataReductionProxyParams( data_reduction_proxy::DataReductionProxyParams::kAllowed))); - data_reduction_proxy_auth_request_handler_.reset( - new DataReductionProxyAuthRequestHandler( - data_reduction_proxy::kClientAndroidWebview, - data_reduction_proxy::kAndroidWebViewProtocolVersion, - data_reduction_proxy_settings_->params())); #endif - - url_request_context_getter_ = - new AwURLRequestContextGetter(GetPath(), cookie_store_.get()); - + scoped_ptr + data_reduction_proxy_config_service( + new DataReductionProxyConfigService( + scoped_ptr( + net::ProxyService::CreateSystemProxyConfigService( + BrowserThread::GetMessageLoopProxyForThread( + BrowserThread::IO), + NULL /* Ignored on Android */)).Pass())); if (data_reduction_proxy_settings_.get()) { - scoped_ptr - configurator(new data_reduction_proxy::DataReductionProxyConfigTracker( - url_request_context_getter_->proxy_config_service(), + data_reduction_proxy_configurator_.reset( + new data_reduction_proxy::DataReductionProxyConfigTracker( + base::Bind(&DataReductionProxyConfigService::UpdateProxyConfig, + base::Unretained( + data_reduction_proxy_config_service.get())), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); - data_reduction_proxy_settings_->SetProxyConfigurator(configurator.Pass()); + data_reduction_proxy_settings_->SetProxyConfigurator( + data_reduction_proxy_configurator_.get()); } + + url_request_context_getter_ = + new AwURLRequestContextGetter(GetPath(), + cookie_store_.get(), + data_reduction_proxy_config_service.Pass()); + visitedlink_master_.reset( new visitedlink::VisitedLinkMaster(this, this, false)); visitedlink_master_->Init(); @@ -172,11 +181,6 @@ DataReductionProxySettings* AwBrowserContext::GetDataReductionProxySettings() { return data_reduction_proxy_settings_.get(); } -DataReductionProxyAuthRequestHandler* -AwBrowserContext::GetDataReductionProxyAuthRequestHandler() { - return data_reduction_proxy_auth_request_handler_.get(); -} - // Create user pref service for autofill functionality. void AwBrowserContext::CreateUserPrefServiceIfNecessary() { if (user_pref_service_) diff --git a/android_webview/browser/aw_browser_context.h b/android_webview/browser/aw_browser_context.h index 7a008bf290c99..5664da457e5d0 100644 --- a/android_webview/browser/aw_browser_context.h +++ b/android_webview/browser/aw_browser_context.h @@ -28,7 +28,7 @@ class WebContents; } namespace data_reduction_proxy { -class DataReductionProxyAuthRequestHandler; +class DataReductionProxyConfigurator; class DataReductionProxySettings; } @@ -40,9 +40,6 @@ namespace visitedlink { class VisitedLinkMaster; } -using data_reduction_proxy::DataReductionProxyAuthRequestHandler; -using data_reduction_proxy::DataReductionProxySettings; - namespace android_webview { class AwFormDatabaseService; @@ -87,10 +84,8 @@ class AwBrowserContext : public content::BrowserContext, AwFormDatabaseService* GetFormDatabaseService(); - DataReductionProxySettings* GetDataReductionProxySettings(); - - DataReductionProxyAuthRequestHandler* - GetDataReductionProxyAuthRequestHandler(); + data_reduction_proxy::DataReductionProxySettings* + GetDataReductionProxySettings(); void CreateUserPrefServiceIfNecessary(); @@ -137,9 +132,10 @@ class AwBrowserContext : public content::BrowserContext, scoped_ptr user_pref_service_; - scoped_ptr data_reduction_proxy_settings_; - scoped_ptr - data_reduction_proxy_auth_request_handler_; + scoped_ptr + data_reduction_proxy_configurator_; + scoped_ptr + data_reduction_proxy_settings_; DISALLOW_COPY_AND_ASSIGN(AwBrowserContext); }; diff --git a/android_webview/browser/aw_browser_main_parts.cc b/android_webview/browser/aw_browser_main_parts.cc index 459e1b5d87556..c5b66fee018c3 100644 --- a/android_webview/browser/aw_browser_main_parts.cc +++ b/android_webview/browser/aw_browser_main_parts.cc @@ -56,17 +56,17 @@ int AwBrowserMainParts::PreCreateThreads() { // fail) just to create the ResourceBundle instance. We should refactor // ResourceBundle/GetApplicationLocale to not require an instance to be // initialized. - ui::ResourceBundle::InitSharedInstanceLocaleOnly( - l10n_util::GetDefaultLocale(), NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + l10n_util::GetDefaultLocale(), + NULL, + ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); std::string locale = l10n_util::GetApplicationLocale(std::string()) + ".pak"; if (AwAssets::OpenAsset(locale, &pak_fd, &pak_off, &pak_len)) { VLOG(0) << "Load from apk succesful, fd=" << pak_fd << " off=" << pak_off << " len=" << pak_len; ui::ResourceBundle::CleanupSharedInstance(); ui::ResourceBundle::InitSharedInstanceWithPakFileRegion( - base::File(pak_fd), - base::MemoryMappedFile::Region(pak_off, pak_len), - /*should_load_common_resources=*/false); + base::File(pak_fd), base::MemoryMappedFile::Region(pak_off, pak_len)); } else { LOG(WARNING) << "Failed to load " << locale << ".pak from the apk too. " "Bringing up WebView without any locale"; diff --git a/android_webview/browser/aw_content_browser_client.cc b/android_webview/browser/aw_content_browser_client.cc index 22ad15ae266b6..5956111370ff1 100644 --- a/android_webview/browser/aw_content_browser_client.cc +++ b/android_webview/browser/aw_content_browser_client.cc @@ -364,6 +364,7 @@ void AwContentBrowserClient::AllowCertificateError( ResourceType resource_type, bool overridable, bool strict_enforcement, + bool expired_previous_decision, const base::Callback& callback, content::CertificateRequestResultType* result) { AwContentsClientBridgeBase* client = diff --git a/android_webview/browser/aw_content_browser_client.h b/android_webview/browser/aw_content_browser_client.h index 4b6b47d8b8b19..637b211502a51 100644 --- a/android_webview/browser/aw_content_browser_client.h +++ b/android_webview/browser/aw_content_browser_client.h @@ -100,6 +100,7 @@ class AwContentBrowserClient : public content::ContentBrowserClient { content::ResourceType resource_type, bool overridable, bool strict_enforcement, + bool expired_previous_decision, const base::Callback& callback, content::CertificateRequestResultType* result) OVERRIDE; virtual void SelectClientCertificate( diff --git a/android_webview/browser/aw_contents_client_bridge_base.cc b/android_webview/browser/aw_contents_client_bridge_base.cc index 4e31f71bb9dc5..ffd7160fe0efe 100644 --- a/android_webview/browser/aw_contents_client_bridge_base.cc +++ b/android_webview/browser/aw_contents_client_bridge_base.cc @@ -47,6 +47,11 @@ void AwContentsClientBridgeBase::Associate( new UserData(handler)); } +void AwContentsClientBridgeBase::Disassociate( + WebContents* web_contents) { + web_contents->RemoveUserData(kAwContentsClientBridgeBase); +} + // static AwContentsClientBridgeBase* AwContentsClientBridgeBase::FromWebContents( WebContents* web_contents) { diff --git a/android_webview/browser/aw_contents_client_bridge_base.h b/android_webview/browser/aw_contents_client_bridge_base.h index a24aa4bd9aaed..3bc18151990d3 100644 --- a/android_webview/browser/aw_contents_client_bridge_base.h +++ b/android_webview/browser/aw_contents_client_bridge_base.h @@ -34,6 +34,7 @@ class AwContentsClientBridgeBase { // Adds the handler to the UserData registry. static void Associate(content::WebContents* web_contents, AwContentsClientBridgeBase* handler); + static void Disassociate(content::WebContents* web_contents); static AwContentsClientBridgeBase* FromWebContents( content::WebContents* web_contents); static AwContentsClientBridgeBase* FromID(int render_process_id, diff --git a/android_webview/browser/aw_javascript_dialog_manager.cc b/android_webview/browser/aw_javascript_dialog_manager.cc index 630c7bfd7af03..476935a53c806 100644 --- a/android_webview/browser/aw_javascript_dialog_manager.cc +++ b/android_webview/browser/aw_javascript_dialog_manager.cc @@ -25,6 +25,11 @@ void AwJavaScriptDialogManager::RunJavaScriptDialog( bool* did_suppress_message) { AwContentsClientBridgeBase* bridge = AwContentsClientBridgeBase::FromWebContents(web_contents); + if (!bridge) { + callback.Run(false, base::string16()); + return; + } + bridge->RunJavaScriptDialog(message_type, origin_url, message_text, @@ -39,6 +44,11 @@ void AwJavaScriptDialogManager::RunBeforeUnloadDialog( const DialogClosedCallback& callback) { AwContentsClientBridgeBase* bridge = AwContentsClientBridgeBase::FromWebContents(web_contents); + if (!bridge) { + callback.Run(false, base::string16()); + return; + } + bridge->RunBeforeUnloadDialog(web_contents->GetURL(), message_text, callback); diff --git a/android_webview/browser/browser_view_renderer.cc b/android_webview/browser/browser_view_renderer.cc index 93ab0a8073e4d..98e2394ea540d 100644 --- a/android_webview/browser/browser_view_renderer.cc +++ b/android_webview/browser/browser_view_renderer.cc @@ -247,6 +247,9 @@ bool BrowserViewRenderer::OnDrawHardware(jobject java_canvas) { if (!compositor_) return false; + if (last_on_draw_global_visible_rect_.IsEmpty()) + return client_->RequestDrawGL(java_canvas, false); + if (!hardware_enabled_) { hardware_enabled_ = compositor_->InitializeHwDraw(); if (hardware_enabled_) { @@ -717,6 +720,9 @@ void BrowserViewRenderer::PostFallbackTick() { FROM_HERE, fallback_tick_fired_.callback(), base::TimeDelta::FromMilliseconds(kFallbackTickTimeoutInMilliseconds)); + } else { + // Pretend we just composited to unblock further invalidates. + DidComposite(); } } @@ -729,8 +735,12 @@ void BrowserViewRenderer::FallbackTickFired() { // This should only be called if OnDraw or DrawGL did not come in time, which // means block_invalidates_ must still be true. DCHECK(block_invalidates_); - if (compositor_needs_continuous_invalidate_ && compositor_) + if (compositor_needs_continuous_invalidate_ && compositor_) { ForceFakeCompositeSW(); + } else { + // Pretend we just composited to unblock further invalidates. + DidComposite(); + } } void BrowserViewRenderer::ForceFakeCompositeSW() { diff --git a/android_webview/browser/deferred_gpu_command_service.cc b/android_webview/browser/deferred_gpu_command_service.cc index 93f6c16ed2bdd..bc1eec0e6c2d4 100644 --- a/android_webview/browser/deferred_gpu_command_service.cc +++ b/android_webview/browser/deferred_gpu_command_service.cc @@ -7,6 +7,7 @@ #include "android_webview/browser/gl_view_renderer_manager.h" #include "android_webview/browser/shared_renderer_state.h" #include "base/debug/trace_event.h" +#include "base/lazy_instance.h" #include "base/synchronization/lock.h" #include "content/public/browser/android/synchronous_compositor.h" #include "gpu/command_buffer/service/shader_translator_cache.h" @@ -14,49 +15,6 @@ namespace android_webview { namespace { - -// TODO(boliu): Consider using base/atomicops.h. -class ThreadSafeBool { - public: - ThreadSafeBool(); - void Set(bool boolean); - bool Get(); - bool GetAndSet(); - - private: - base::Lock lock_; - bool boolean_; - DISALLOW_COPY_AND_ASSIGN(ThreadSafeBool); -}; - -ThreadSafeBool::ThreadSafeBool() : boolean_(false) { -} - -void ThreadSafeBool::Set(bool boolean) { - base::AutoLock lock(lock_); - boolean_ = boolean; -} - -bool ThreadSafeBool::GetAndSet() { - base::AutoLock lock(lock_); - bool rv = boolean_; - boolean_ = true; - return rv; -} - -bool ThreadSafeBool::Get() { - base::AutoLock lock(lock_); - return boolean_; -} - -base::LazyInstance g_request_pending = - LAZY_INSTANCE_INITIALIZER; - -// Because request is posted to UI thread, have to treat requests on UI thread -// specifically because UI can immediately block waiting for the request. -base::LazyInstance g_request_pending_on_ui = - LAZY_INSTANCE_INITIALIZER; - base::LazyInstance > g_service = LAZY_INSTANCE_INITIALIZER; } // namespace @@ -78,13 +36,11 @@ ScopedAllowGL::ScopedAllowGL() { ScopedAllowGL::~ScopedAllowGL() { allow_gl.Get().Set(false); - g_request_pending.Get().Set(false); - g_request_pending_on_ui.Get().Set(false); DeferredGpuCommandService* service = g_service.Get(); if (service) { service->RunTasks(); - if (service->HasIdleWork()) { + if (service->IdleQueueSize()) { service->RequestProcessGL(); } } @@ -95,10 +51,6 @@ void DeferredGpuCommandService::SetInstance() { if (!g_service.Get()) { g_service.Get() = new DeferredGpuCommandService; content::SynchronousCompositor::SetGpuService(g_service.Get()); - - // Initialize global booleans. - g_request_pending.Get().Set(false); - g_request_pending_on_ui.Get().Set(false); } } @@ -124,14 +76,7 @@ void DeferredGpuCommandService::RequestProcessGL() { LOG(ERROR) << "No hardware renderer. Deadlock likely"; return; } - - bool on_ui_thread = renderer_state->CurrentlyOnUIThread(); - bool need_request = on_ui_thread ? !g_request_pending_on_ui.Get().GetAndSet() - : !g_request_pending.Get().GetAndSet(); - if (need_request) { - g_request_pending.Get().Set(true); - renderer_state->ClientRequestDrawGL(); - } + renderer_state->ClientRequestDrawGL(); } // Called from different threads! @@ -147,9 +92,9 @@ void DeferredGpuCommandService::ScheduleTask(const base::Closure& task) { } } -bool DeferredGpuCommandService::HasIdleWork() { +size_t DeferredGpuCommandService::IdleQueueSize() { base::AutoLock lock(tasks_lock_); - return idle_tasks_.size() > 0; + return idle_tasks_.size(); } void DeferredGpuCommandService::ScheduleIdleWork( @@ -171,7 +116,8 @@ void DeferredGpuCommandService::PerformIdleWork(bool is_idle) { base::TimeDelta::FromMilliseconds(16); const base::Time now = base::Time::Now(); - while (HasIdleWork()) { + size_t queue_size = IdleQueueSize(); + while (queue_size--) { base::Closure task; { base::AutoLock lock(tasks_lock_); @@ -188,6 +134,14 @@ void DeferredGpuCommandService::PerformIdleWork(bool is_idle) { } } +void DeferredGpuCommandService::PerformAllIdleWork() { + TRACE_EVENT0("android_webview", + "DeferredGpuCommandService::PerformAllIdleWork"); + while (IdleQueueSize()) { + PerformIdleWork(true); + } +} + bool DeferredGpuCommandService::UseVirtualizedGLContexts() { return true; } scoped_refptr diff --git a/android_webview/browser/deferred_gpu_command_service.h b/android_webview/browser/deferred_gpu_command_service.h index 9a19fdd3d96de..c84df5b6aace4 100644 --- a/android_webview/browser/deferred_gpu_command_service.h +++ b/android_webview/browser/deferred_gpu_command_service.h @@ -45,7 +45,10 @@ class DeferredGpuCommandService void RunTasks(); // If |is_idle| is false, this will only run older idle tasks. void PerformIdleWork(bool is_idle); - bool HasIdleWork(); + // Flush the idle queue until it is empty. This is different from + // PerformIdleWork(is_idle = true), which does not run any newly scheduled + // idle tasks during the idle run. + void PerformAllIdleWork(); virtual void AddRef() const OVERRIDE; virtual void Release() const OVERRIDE; @@ -59,6 +62,7 @@ class DeferredGpuCommandService static void RequestProcessGL(); DeferredGpuCommandService(); + size_t IdleQueueSize(); base::Lock tasks_lock_; std::queue tasks_; diff --git a/android_webview/browser/hardware_renderer.cc b/android_webview/browser/hardware_renderer.cc index 818d3c5c99aa9..1598ac6423719 100644 --- a/android_webview/browser/hardware_renderer.cc +++ b/android_webview/browser/hardware_renderer.cc @@ -20,6 +20,7 @@ #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_settings.h" #include "gpu/command_buffer/client/gl_in_process_context.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "ui/gfx/frame_time.h" #include "ui/gfx/geometry/rect_conversions.h" #include "ui/gfx/geometry/rect_f.h" @@ -33,6 +34,7 @@ namespace android_webview { namespace { using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; +using webkit::gpu::WebGraphicsContext3DImpl; scoped_refptr CreateContext( scoped_refptr surface, @@ -46,21 +48,22 @@ scoped_refptr CreateContext( attributes.stencil = false; attributes.shareResources = true; attributes.noAutomaticFlushes = true; - gpu::GLInProcessContextAttribs in_process_attribs; - WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes( - attributes, &in_process_attribs); - in_process_attribs.lose_context_when_out_of_memory = 1; - - scoped_ptr context( - gpu::GLInProcessContext::Create(service, - surface, - surface->IsOffscreen(), - gfx::kNullAcceleratedWidget, - surface->GetSize(), - share_context, - false /* share_resources */, - in_process_attribs, - gpu_preference)); + gpu::gles2::ContextCreationAttribHelper attribs_for_gles2; + WebGraphicsContext3DImpl::ConvertAttributes( + attributes, &attribs_for_gles2); + attribs_for_gles2.lose_context_when_out_of_memory = true; + + scoped_ptr context(gpu::GLInProcessContext::Create( + service, + surface, + surface->IsOffscreen(), + gfx::kNullAcceleratedWidget, + surface->GetSize(), + share_context, + false /* share_resources */, + attribs_for_gles2, + gpu_preference, + gpu::GLInProcessContextSharedMemoryLimits())); DCHECK(context.get()); return webkit::gpu::ContextProviderInProcess::Create( @@ -93,8 +96,8 @@ HardwareRenderer::HardwareRenderer(SharedRendererState* state) // Webview does not own the surface so should not clear it. settings.should_clear_root_render_pass = false; - layer_tree_host_ = cc::LayerTreeHost::CreateSingleThreaded( - this, this, NULL, settings, NULL); + layer_tree_host_ = + cc::LayerTreeHost::CreateSingleThreaded(this, this, NULL, settings, NULL); layer_tree_host_->SetRootLayer(root_layer_); layer_tree_host_->SetLayerTreeHostClientReady(); layer_tree_host_->set_has_transparent_background(true); @@ -116,6 +119,10 @@ HardwareRenderer::~HardwareRenderer() { #endif // DCHECK_IS_ON resource_collection_->SetClient(NULL); + + // Reset draw constraints. + shared_renderer_state_->UpdateDrawConstraints( + ParentCompositorDrawConstraints()); } void HardwareRenderer::DidBeginMainFrame() { @@ -129,10 +136,9 @@ void HardwareRenderer::DidBeginMainFrame() { void HardwareRenderer::CommitFrame() { scoped_ptr input = shared_renderer_state_->PassDrawGLInput(); - if (!input.get()) { - DLOG(WARNING) << "No frame to commit"; + // Happens with empty global visible rect. + if (!input.get()) return; - } DCHECK(!input->frame.gl_frame_data); DCHECK(!input->frame.software_frame_data); @@ -182,11 +188,6 @@ void HardwareRenderer::DrawGL(bool stencil_enabled, return; } - if (!delegated_layer_.get()) { - DLOG(ERROR) << "No frame committed"; - return; - } - // TODO(boliu): Handle context loss. if (last_egl_context_ != current_context) DLOG(WARNING) << "EGLContextChanged"; @@ -206,6 +207,9 @@ void HardwareRenderer::DrawGL(bool stencil_enabled, draw_constraints); } + if (!delegated_layer_.get()) + return; + viewport_.SetSize(draw_info->width, draw_info->height); layer_tree_host_->SetViewportSize(viewport_); clip_.SetRect(draw_info->clip_left, diff --git a/android_webview/browser/net/aw_url_request_context_getter.cc b/android_webview/browser/net/aw_url_request_context_getter.cc index 94da7366edbc5..6e39cd79cce58 100644 --- a/android_webview/browser/net/aw_url_request_context_getter.cc +++ b/android_webview/browser/net/aw_url_request_context_getter.cc @@ -17,6 +17,7 @@ #include "base/strings/string_number_conversions.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/worker_pool.h" +#include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_config_service.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_settings.h" #include "content/public/browser/browser_thread.h" @@ -78,6 +79,9 @@ void ApplyCmdlineOverridesToNetworkSessionParams( switches::kTestingFixedHttpsPort), &value); params->testing_fixed_https_port = value; } + if (command_line.HasSwitch(switches::kIgnoreCertificateErrors)) { + params->ignore_certificate_errors = true; + } } void PopulateNetworkSessionParams( @@ -167,14 +171,12 @@ scoped_ptr CreateJobFactory( } // namespace AwURLRequestContextGetter::AwURLRequestContextGetter( - const base::FilePath& partition_path, net::CookieStore* cookie_store) + const base::FilePath& partition_path, net::CookieStore* cookie_store, + scoped_ptr + config_service) : partition_path_(partition_path), - cookie_store_(cookie_store), - proxy_config_service_(new DataReductionProxyConfigService( - scoped_ptr( - net::ProxyService::CreateSystemProxyConfigService( - GetNetworkTaskRunner(), - NULL /* Ignored on Android */)).Pass())) { + cookie_store_(cookie_store) { + data_reduction_proxy_config_service_ = config_service.Pass(); // CreateSystemProxyConfigService for Android must be called on main thread. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); } @@ -193,17 +195,19 @@ void AwURLRequestContextGetter::InitializeURLRequestContext() { #if !defined(DISABLE_FTP_SUPPORT) builder.set_ftp_enabled(false); // Android WebView does not support ftp yet. #endif - builder.set_proxy_config_service(proxy_config_service_.release()); + if (data_reduction_proxy_config_service_.get()) { + builder.set_proxy_config_service( + data_reduction_proxy_config_service_.release()); + } else { + builder.set_proxy_config_service( + net::ProxyService::CreateSystemProxyConfigService( + GetNetworkTaskRunner(), NULL /* Ignored on Android */ )); + } builder.set_accept_language(net::HttpUtil::GenerateAcceptLanguageHeader( AwContentBrowserClient::GetAcceptLangsImpl())); ApplyCmdlineOverridesToURLRequestContextBuilder(&builder); url_request_context_.reset(builder.Build()); - channel_id_service_.reset( - new net::ChannelIDService( - new net::DefaultChannelIDStore(NULL), - base::WorkerPool::GetTaskRunner(true))); - url_request_context_->set_channel_id_service(channel_id_service_.get()); // TODO(mnaganov): Fix URLRequestContextBuilder to use proper threads. net::HttpNetworkSession::Params network_session_params; @@ -222,14 +226,20 @@ void AwURLRequestContextGetter::InitializeURLRequestContext() { #if defined(SPDY_PROXY_AUTH_ORIGIN) AwBrowserContext* browser_context = AwBrowserContext::GetDefault(); DCHECK(browser_context); - DataReductionProxySettings* drp_settings = + DataReductionProxySettings* data_reduction_proxy_settings = browser_context->GetDataReductionProxySettings(); - if (drp_settings) { - aw_network_delegate->set_data_reduction_proxy_params( - drp_settings->params()); - aw_network_delegate->set_data_reduction_proxy_auth_request_handler( - browser_context->GetDataReductionProxyAuthRequestHandler()); - } + DCHECK(data_reduction_proxy_settings); + data_reduction_proxy_auth_request_handler_.reset( + new data_reduction_proxy::DataReductionProxyAuthRequestHandler( + data_reduction_proxy::kClientAndroidWebview, + data_reduction_proxy::kAndroidWebViewProtocolVersion, + data_reduction_proxy_settings->params(), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); + + aw_network_delegate->set_data_reduction_proxy_params( + data_reduction_proxy_settings->params()); + aw_network_delegate->set_data_reduction_proxy_auth_request_handler( + data_reduction_proxy_auth_request_handler_.get()); #endif main_http_factory_.reset(main_cache); @@ -261,10 +271,9 @@ void AwURLRequestContextGetter::SetHandlersAndInterceptors( request_interceptors_.swap(request_interceptors); } -DataReductionProxyConfigService* -AwURLRequestContextGetter::proxy_config_service() { - // TODO(bengr): return system config if data reduction proxy is disabled. - return proxy_config_service_.get(); +data_reduction_proxy::DataReductionProxyAuthRequestHandler* +AwURLRequestContextGetter::GetDataReductionProxyAuthRequestHandler() const { + return data_reduction_proxy_auth_request_handler_.get(); } } // namespace android_webview diff --git a/android_webview/browser/net/aw_url_request_context_getter.h b/android_webview/browser/net/aw_url_request_context_getter.h index 6e4f81e7327d1..1ec4123630a8b 100644 --- a/android_webview/browser/net/aw_url_request_context_getter.h +++ b/android_webview/browser/net/aw_url_request_context_getter.h @@ -11,7 +11,6 @@ #include "base/memory/scoped_ptr.h" #include "content/public/browser/content_browser_client.h" #include "net/http/http_network_session.h" -#include "net/ssl/channel_id_service.h" #include "net/url_request/url_request_context_getter.h" #include "net/url_request/url_request_job_factory.h" @@ -24,19 +23,21 @@ class URLRequestJobFactory; } namespace data_reduction_proxy { +class DataReductionProxyAuthRequestHandler; class DataReductionProxyConfigService; } -using data_reduction_proxy::DataReductionProxyConfigService; - namespace android_webview { class AwNetworkDelegate; class AwURLRequestContextGetter : public net::URLRequestContextGetter { public: - AwURLRequestContextGetter(const base::FilePath& partition_path, - net::CookieStore* cookie_store); + AwURLRequestContextGetter( + const base::FilePath& partition_path, + net::CookieStore* cookie_store, + scoped_ptr + config_service); void InitializeOnNetworkThread(); @@ -45,7 +46,8 @@ class AwURLRequestContextGetter : public net::URLRequestContextGetter { virtual scoped_refptr GetNetworkTaskRunner() const OVERRIDE; - DataReductionProxyConfigService* proxy_config_service(); + data_reduction_proxy::DataReductionProxyAuthRequestHandler* + GetDataReductionProxyAuthRequestHandler() const; private: friend class AwBrowserContext; @@ -66,10 +68,12 @@ class AwURLRequestContextGetter : public net::URLRequestContextGetter { const base::FilePath partition_path_; scoped_refptr cookie_store_; scoped_ptr url_request_context_; - scoped_ptr proxy_config_service_; + scoped_ptr + data_reduction_proxy_config_service_; + scoped_ptr + data_reduction_proxy_auth_request_handler_; scoped_ptr job_factory_; scoped_ptr main_http_factory_; - scoped_ptr channel_id_service_; // ProtocolHandlers and interceptors are stored here between // SetHandlersAndInterceptors() and the first GetURLRequestContext() call. diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc index 787a647683ddc..0428baa24f068 100644 --- a/android_webview/browser/renderer_host/aw_render_view_host_ext.cc +++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.cc @@ -74,11 +74,6 @@ void AwRenderViewHostExt::SetTextZoomFactor(float factor) { Send(new AwViewMsg_SetTextZoomFactor(web_contents()->GetRoutingID(), factor)); } -void AwRenderViewHostExt::SetFixedLayoutSize(const gfx::Size& size) { - DCHECK(CalledOnValidThread()); - Send(new AwViewMsg_SetFixedLayoutSize(web_contents()->GetRoutingID(), size)); -} - void AwRenderViewHostExt::ResetScrollAndScaleState() { DCHECK(CalledOnValidThread()); Send(new AwViewMsg_ResetScrollAndScaleState(web_contents()->GetRoutingID())); @@ -104,10 +99,6 @@ void AwRenderViewHostExt::SetJsOnlineProperty(bool network_up) { Send(new AwViewMsg_SetJsOnlineProperty(network_up)); } -void AwRenderViewHostExt::SendCheckRenderThreadResponsiveness() { - Send(new AwViewMsg_CheckRenderThreadResponsiveness()); -} - void AwRenderViewHostExt::RenderViewCreated( content::RenderViewHost* render_view_host) { Send(new AwViewMsg_SetBackgroundColor(web_contents()->GetRoutingID(), diff --git a/android_webview/browser/renderer_host/aw_render_view_host_ext.h b/android_webview/browser/renderer_host/aw_render_view_host_ext.h index f56d0a68f90c1..635c7fce38e1a 100644 --- a/android_webview/browser/renderer_host/aw_render_view_host_ext.h +++ b/android_webview/browser/renderer_host/aw_render_view_host_ext.h @@ -69,8 +69,6 @@ class AwRenderViewHostExt : public content::WebContentsObserver, // Text Autosizing. void SetTextZoomFactor(float factor); - void SetFixedLayoutSize(const gfx::Size& size); - void ResetScrollAndScaleState(); // Sets the initial page scale. This overrides initial scale set by @@ -79,8 +77,6 @@ class AwRenderViewHostExt : public content::WebContentsObserver, void SetBackgroundColor(SkColor c); void SetJsOnlineProperty(bool network_up); - void SendCheckRenderThreadResponsiveness(); - private: // content::WebContentsObserver implementation. virtual void RenderViewCreated(content::RenderViewHost* view_host) OVERRIDE; diff --git a/android_webview/browser/shared_renderer_state.cc b/android_webview/browser/shared_renderer_state.cc index 7c7238e7d2053..508fdc4ee73c8 100644 --- a/android_webview/browser/shared_renderer_state.cc +++ b/android_webview/browser/shared_renderer_state.cc @@ -6,10 +6,67 @@ #include "android_webview/browser/browser_view_renderer_client.h" #include "base/bind.h" +#include "base/lazy_instance.h" #include "base/location.h" namespace android_webview { +namespace internal { + +class RequestDrawGLTracker { + public: + RequestDrawGLTracker(); + bool ShouldRequestOnNoneUiThread(SharedRendererState* state); + bool ShouldRequestOnUiThread(SharedRendererState* state); + void DidRequestOnUiThread(); + void ResetPending(); + + private: + base::Lock lock_; + SharedRendererState* pending_ui_; + SharedRendererState* pending_non_ui_; +}; + +RequestDrawGLTracker::RequestDrawGLTracker() + : pending_ui_(NULL), pending_non_ui_(NULL) { +} + +bool RequestDrawGLTracker::ShouldRequestOnNoneUiThread( + SharedRendererState* state) { + base::AutoLock lock(lock_); + if (pending_ui_ || pending_non_ui_) + return false; + pending_non_ui_ = state; + return true; +} + +bool RequestDrawGLTracker::ShouldRequestOnUiThread(SharedRendererState* state) { + base::AutoLock lock(lock_); + if (pending_non_ui_) { + pending_non_ui_->ResetRequestDrawGLCallback(); + pending_non_ui_ = NULL; + } + if (pending_ui_) + return false; + pending_ui_ = state; + return true; +} + +void RequestDrawGLTracker::ResetPending() { + base::AutoLock lock(lock_); + pending_non_ui_ = NULL; + pending_ui_ = NULL; +} + +} // namespace internal + +namespace { + +base::LazyInstance g_request_draw_gl_tracker = + LAZY_INSTANCE_INITIALIZER; + +} + DrawGLInput::DrawGLInput() : width(0), height(0) { } @@ -27,30 +84,48 @@ SharedRendererState::SharedRendererState( share_context_(NULL) { DCHECK(ui_loop_->BelongsToCurrentThread()); DCHECK(client_on_ui_); + ResetRequestDrawGLCallback(); } SharedRendererState::~SharedRendererState() { DCHECK(ui_loop_->BelongsToCurrentThread()); } -bool SharedRendererState::CurrentlyOnUIThread() { - return ui_loop_->BelongsToCurrentThread(); -} - void SharedRendererState::ClientRequestDrawGL() { if (ui_loop_->BelongsToCurrentThread()) { + if (!g_request_draw_gl_tracker.Get().ShouldRequestOnUiThread(this)) + return; ClientRequestDrawGLOnUIThread(); } else { - ui_loop_->PostTask( - FROM_HERE, - base::Bind(&SharedRendererState::ClientRequestDrawGLOnUIThread, - ui_thread_weak_ptr_)); + if (!g_request_draw_gl_tracker.Get().ShouldRequestOnNoneUiThread(this)) + return; + base::Closure callback; + { + base::AutoLock lock(lock_); + callback = request_draw_gl_closure_; + } + ui_loop_->PostTask(FROM_HERE, callback); } } +void SharedRendererState::DidDrawGLProcess() { + g_request_draw_gl_tracker.Get().ResetPending(); +} + +void SharedRendererState::ResetRequestDrawGLCallback() { + DCHECK(ui_loop_->BelongsToCurrentThread()); + base::AutoLock lock(lock_); + request_draw_gl_cancelable_closure_.Reset( + base::Bind(&SharedRendererState::ClientRequestDrawGLOnUIThread, + base::Unretained(this))); + request_draw_gl_closure_ = request_draw_gl_cancelable_closure_.callback(); +} + void SharedRendererState::ClientRequestDrawGLOnUIThread() { DCHECK(ui_loop_->BelongsToCurrentThread()); + ResetRequestDrawGLCallback(); if (!client_on_ui_->RequestDrawGL(NULL, false)) { + g_request_draw_gl_tracker.Get().ResetPending(); LOG(ERROR) << "Failed to request GL process. Deadlock likely"; } } @@ -71,12 +146,15 @@ scoped_ptr SharedRendererState::PassDrawGLInput() { return draw_gl_input_.Pass(); } +void SharedRendererState::UpdateDrawConstraints( + const ParentCompositorDrawConstraints& parent_draw_constraints) { + base::AutoLock lock(lock_); + parent_draw_constraints_ = parent_draw_constraints; +} + void SharedRendererState::PostExternalDrawConstraintsToChildCompositor( const ParentCompositorDrawConstraints& parent_draw_constraints) { - { - base::AutoLock lock(lock_); - parent_draw_constraints_ = parent_draw_constraints; - } + UpdateDrawConstraints(parent_draw_constraints); // No need to hold the lock_ during the post task. ui_loop_->PostTask( diff --git a/android_webview/browser/shared_renderer_state.h b/android_webview/browser/shared_renderer_state.h index 218b7b21bbaa6..2245f73b21e79 100644 --- a/android_webview/browser/shared_renderer_state.h +++ b/android_webview/browser/shared_renderer_state.h @@ -6,6 +6,7 @@ #define ANDROID_WEBVIEW_BROWSER_SHARED_RENDERER_STATE_H_ #include "android_webview/browser/parent_compositor_draw_constraints.h" +#include "base/cancelable_callback.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop_proxy.h" #include "base/synchronization/lock.h" @@ -24,6 +25,10 @@ class GLInProcessContext; namespace android_webview { +namespace internal { +class RequestDrawGLTracker; +} + class BrowserViewRendererClient; class InsideHardwareReleaseReset; @@ -45,13 +50,15 @@ class SharedRendererState { BrowserViewRendererClient* client); ~SharedRendererState(); - bool CurrentlyOnUIThread(); void ClientRequestDrawGL(); + void DidDrawGLProcess(); void SetDrawGLInput(scoped_ptr input); scoped_ptr PassDrawGLInput(); bool IsInsideHardwareRelease() const; + void UpdateDrawConstraints( + const ParentCompositorDrawConstraints& parent_draw_constraints); void PostExternalDrawConstraintsToChildCompositor( const ParentCompositorDrawConstraints& parent_draw_constraints); @@ -66,7 +73,9 @@ class SharedRendererState { private: friend class InsideHardwareReleaseReset; + friend class internal::RequestDrawGLTracker; + void ResetRequestDrawGLCallback(); void ClientRequestDrawGLOnUIThread(); void UpdateParentDrawConstraintsOnUIThread(); void SetInsideHardwareRelease(bool inside); @@ -75,6 +84,7 @@ class SharedRendererState { BrowserViewRendererClient* client_on_ui_; base::WeakPtrFactory weak_factory_on_ui_thread_; base::WeakPtr ui_thread_weak_ptr_; + base::CancelableClosure request_draw_gl_cancelable_closure_; // Accessed by both UI and RT thread. mutable base::Lock lock_; @@ -83,6 +93,7 @@ class SharedRendererState { ParentCompositorDrawConstraints parent_draw_constraints_; gpu::GLInProcessContext* share_context_; cc::ReturnedResourceArray returned_resources_; + base::Closure request_draw_gl_closure_; DISALLOW_COPY_AND_ASSIGN(SharedRendererState); }; diff --git a/android_webview/common/render_view_messages.h b/android_webview/common/render_view_messages.h index 0b8f3159fe146..9354db5d8241b 100644 --- a/android_webview/common/render_view_messages.h +++ b/android_webview/common/render_view_messages.h @@ -67,11 +67,6 @@ IPC_MESSAGE_ROUTED0(AwViewMsg_ResetScrollAndScaleState) IPC_MESSAGE_ROUTED1(AwViewMsg_SetInitialPageScale, double /* page_scale_factor */) -// Makes the blink::WebView use the given size for layout regardless of what -// the size of the RenderWidget or viewport settings are. -IPC_MESSAGE_ROUTED1(AwViewMsg_SetFixedLayoutSize, - gfx::Size /* size */) - // Sets the base background color for this view. IPC_MESSAGE_ROUTED1(AwViewMsg_SetBackgroundColor, SkColor) @@ -79,10 +74,6 @@ IPC_MESSAGE_ROUTED1(AwViewMsg_SetBackgroundColor, IPC_MESSAGE_CONTROL1(AwViewMsg_SetJsOnlineProperty, bool /* network_up */) -// Sent prior to making a navigation via loadUrl to make sure that -// render thread isn't stuck in a loop induced by JavaScript code. -IPC_MESSAGE_CONTROL0(AwViewMsg_CheckRenderThreadResponsiveness) - //----------------------------------------------------------------------------- // RenderView messages // These are messages sent from the renderer to the browser process. diff --git a/android_webview/java/src/org/chromium/android_webview/AwAssets.java b/android_webview/java/src/org/chromium/android_webview/AwAssets.java index 97e292ff7a37f..55c94b6fec5d8 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwAssets.java +++ b/android_webview/java/src/org/chromium/android_webview/AwAssets.java @@ -24,15 +24,24 @@ public class AwAssets { @CalledByNative public static long[] openAsset(Context context, String fileName) { + AssetFileDescriptor afd = null; try { AssetManager manager = context.getAssets(); - AssetFileDescriptor afd = manager.openFd(fileName); + afd = manager.openFd(fileName); return new long[] { afd.getParcelFileDescriptor().detachFd(), afd.getStartOffset(), afd.getLength() }; } catch (IOException e) { - Log.e(LOGTAG, "Error while loading asset " + fileName + ": " + e.getMessage()); + Log.e(LOGTAG, "Error while loading asset " + fileName + ": " + e); return new long[] {-1, -1, -1}; + } finally { + try { + if (afd != null) { + afd.close(); + } + } catch (IOException e2) { + Log.e(LOGTAG, "Unable to close AssetFileDescriptor", e2); + } } } } 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 fc456df4a0d89..552d61481ae8b 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContents.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java @@ -52,9 +52,11 @@ import org.chromium.content.browser.LoadUrlParams; import org.chromium.content.browser.NavigationHistory; import org.chromium.content.browser.PageTransitionTypes; +import org.chromium.content.browser.WebContentsObserverAndroid; import org.chromium.content.common.CleanupReference; import org.chromium.content_public.Referrer; import org.chromium.content_public.browser.GestureStateListener; +import org.chromium.content_public.browser.JavaScriptCallback; import org.chromium.ui.base.ActivityWindowAndroid; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.gfx.DeviceDisplayInfo; @@ -180,8 +182,10 @@ public AwScrollOffsetManager createScrollOffsetManager( private final AwLayoutChangeListener mLayoutChangeListener; private final Context mContext; private ContentViewCore mContentViewCore; + private WindowAndroid mWindowAndroid; private final AwContentsClient mContentsClient; private final AwContentViewClient mContentViewClient; + private WebContentsObserverAndroid mWebContentsObserver; private final AwContentsClientBridge mContentsClientBridge; private final AwWebContentsDelegateAdapter mWebContentsDelegate; private final AwContentsIoThreadClient mIoThreadClient; @@ -418,17 +422,16 @@ public void setMeasuredDimension(int measuredWidth, int measuredHeight) { mInternalAccessAdapter.setMeasuredDimension(measuredWidth, measuredHeight); } - @Override - public void setFixedLayoutSize(int widthDip, int heightDip) { - if (mNativeAwContents == 0) return; - nativeSetFixedLayoutSize(mNativeAwContents, widthDip, heightDip); - } - @Override public boolean isLayoutParamsHeightWrapContent() { return mContainerView.getLayoutParams() != null && mContainerView.getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT; } + + @Override + public void setForceZeroLayoutHeight(boolean forceZeroHeight) { + getSettings().setForceZeroLayoutHeight(forceZeroHeight); + } } //-------------------------------------------------------------------------------------------- @@ -572,7 +575,8 @@ public AwContents(AwBrowserContext browserContext, ViewGroup containerView, Cont mDIPScale = DeviceDisplayInfo.create(mContext).getDIPScale(); mLayoutSizer.setDelegate(new AwLayoutSizerDelegate()); mLayoutSizer.setDIPScale(mDIPScale); - mWebContentsDelegate = new AwWebContentsDelegateAdapter(contentsClient, mContainerView); + mWebContentsDelegate = new AwWebContentsDelegateAdapter( + contentsClient, mContainerView, mContext); mContentsClientBridge = new AwContentsClientBridge(contentsClient, mBrowserContext.getKeyStore(), AwContentsStatics.getClientCertLookupTable()); mZoomControls = new AwZoomControls(this); @@ -612,12 +616,11 @@ private static ContentViewCore createAndInitializeContentViewCore(ViewGroup cont Context context, InternalAccessDelegate internalDispatcher, long nativeWebContents, GestureStateListener gestureStateListener, ContentViewClient contentViewClient, - ContentViewCore.ZoomControlsDelegate zoomControlsDelegate) { + ContentViewCore.ZoomControlsDelegate zoomControlsDelegate, + WindowAndroid windowAndroid) { ContentViewCore contentViewCore = new ContentViewCore(context); contentViewCore.initialize(containerView, internalDispatcher, nativeWebContents, - context instanceof Activity ? - new ActivityWindowAndroid((Activity) context) : - new WindowAndroid(context.getApplicationContext())); + windowAndroid); contentViewCore.addGestureStateListener(gestureStateListener); contentViewCore.setContentViewClient(contentViewClient); contentViewCore.setZoomControlsDelegate(zoomControlsDelegate); @@ -753,12 +756,16 @@ private void setNewAwContents(long newAwContentsPtr) { mCleanupReference = new CleanupReference(this, new DestroyRunnable(mNativeAwContents)); long nativeWebContents = nativeGetWebContents(mNativeAwContents); + + mWindowAndroid = mContext instanceof Activity ? + new ActivityWindowAndroid((Activity) mContext) : + new WindowAndroid(mContext.getApplicationContext()); mContentViewCore = createAndInitializeContentViewCore( mContainerView, mContext, mInternalAccessAdapter, nativeWebContents, - new AwGestureStateListener(), mContentViewClient, mZoomControls); + new AwGestureStateListener(), mContentViewClient, mZoomControls, mWindowAndroid); nativeSetJavaPeers(mNativeAwContents, this, mWebContentsDelegate, mContentsClientBridge, mIoThreadClient, mInterceptNavigationDelegate); - mContentsClient.installWebContentsObserver(mContentViewCore.getWebContents()); + installWebContentsObserver(); mSettings.setWebContents(nativeWebContents); nativeSetDipScale(mNativeAwContents, (float) mDIPScale); @@ -766,6 +773,14 @@ private void setNewAwContents(long newAwContentsPtr) { mContentViewCore.onShow(); } + private void installWebContentsObserver() { + if (mWebContentsObserver != null) { + mWebContentsObserver.detachFromWebContents(); + } + mWebContentsObserver = new AwWebContentsObserver(mContentViewCore.getWebContents(), + mContentsClient); + } + /** * Called on the "source" AwContents that is opening the popup window to * provide the AwContents to host the pop up content. @@ -1046,8 +1061,6 @@ public void run() { * @param params Parameters for this load. */ public void loadUrl(LoadUrlParams params) { - if (mNativeAwContents == 0) return; - if (params.getLoadUrlType() == LoadUrlParams.LOAD_TYPE_DATA && !params.isBaseUrlDataScheme()) { // This allows data URLs with a non-data base URL access to file:///android_asset/ and @@ -1086,11 +1099,12 @@ public void loadUrl(LoadUrlParams params) { } } - nativeSetExtraHeadersForUrl( - mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString()); + if (mNativeAwContents != 0) { + nativeSetExtraHeadersForUrl( + mNativeAwContents, params.getUrl(), params.getExtraHttpRequestHeadersString()); + } params.setExtraHeaders(new HashMap()); - nativeSendCheckRenderThreadResponsiveness(mNativeAwContents); mContentViewCore.loadUrl(params); // The behavior of WebViewClassic uses the populateVisitedLinks callback in WebKit. @@ -1666,12 +1680,12 @@ public void preauthorizePermission(Uri origin, long resources) { } /** - * @see ContentViewCore.evaluateJavaScript(String, ContentViewCore.JavaScriptCallback) + * @see ContentViewCore.evaluateJavaScript(String, JavaScriptCallback) */ public void evaluateJavaScript(String script, final ValueCallback callback) { - ContentViewCore.JavaScriptCallback jsCallback = null; + JavaScriptCallback jsCallback = null; if (callback != null) { - jsCallback = new ContentViewCore.JavaScriptCallback() { + jsCallback = new JavaScriptCallback() { @Override public void handleJavaScriptResult(String jsonResult) { callback.onReceiveValue(jsonResult); @@ -2029,13 +2043,20 @@ private boolean requestDrawGL(Canvas canvas, boolean waitForCompletion) { @CalledByNative private void postInvalidateOnAnimation() { - if (SUPPORTS_ON_ANIMATION) { + if (SUPPORTS_ON_ANIMATION && !mWindowAndroid.isInsideVSync()) { mContainerView.postInvalidateOnAnimation(); } else { - mContainerView.postInvalidate(); + mContainerView.invalidate(); } } + // Call postInvalidateOnAnimation for invalidations. This is only used to synchronize + // draw functor destruction. + @CalledByNative + private void invalidateOnFunctorDestroy() { + mContainerView.invalidate(); + } + @CalledByNative private int[] getLocationOnScreen() { int[] result = new int[2]; @@ -2461,8 +2482,6 @@ private native boolean nativeOnDraw(long nativeAwContents, Canvas canvas, private native void nativeOnAttachedToWindow(long nativeAwContents, int w, int h); private static native void nativeOnDetachedFromWindow(long nativeAwContents); private native void nativeSetDipScale(long nativeAwContents, float dipScale); - private native void nativeSetFixedLayoutSize(long nativeAwContents, - int widthDip, int heightDip); // Returns null if save state fails. private native byte[] nativeGetOpaqueState(long nativeAwContents); @@ -2480,7 +2499,6 @@ private native void nativeSetFixedLayoutSize(long nativeAwContents, private native void nativeClearView(long nativeAwContents); private native void nativeSetExtraHeadersForUrl(long nativeAwContents, String url, String extraHeaders); - private native void nativeSendCheckRenderThreadResponsiveness(long nativeAwContents); private native void nativeInvokeGeolocationCallback( long nativeAwContents, boolean value, String requestingFrame); diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java index bf82e8a8b2e95..d1effb7b09d50 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClient.java @@ -18,9 +18,6 @@ import android.webkit.WebChromeClient; import org.chromium.android_webview.permission.AwPermissionRequest; -import org.chromium.content.browser.WebContentsObserverAndroid; -import org.chromium.content_public.browser.WebContents; -import org.chromium.net.NetError; import java.security.Principal; import java.util.HashMap; @@ -38,8 +35,6 @@ public abstract class AwContentsClient { private final AwContentsClientCallbackHelper mCallbackHelper; - private AwWebContentsObserver mWebContentsObserver; - // Last background color reported from the renderer. Holds the sentinal value INVALID_COLOR // if not valid. private int mCachedRendererBackgroundColor = INVALID_COLOR; @@ -55,69 +50,6 @@ public AwContentsClient(Looper looper) { mCallbackHelper = new AwContentsClientCallbackHelper(looper, this); } - class AwWebContentsObserver extends WebContentsObserverAndroid { - public AwWebContentsObserver(WebContents webContents) { - super(webContents); - } - - @Override - public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) { - String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl(); - boolean isErrorUrl = - unreachableWebDataUrl != null && unreachableWebDataUrl.equals(validatedUrl); - if (isMainFrame && !isErrorUrl) { - AwContentsClient.this.onPageFinished(validatedUrl); - } - } - - @Override - public void didFailLoad(boolean isProvisionalLoad, - boolean isMainFrame, int errorCode, String description, String failingUrl) { - String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl(); - boolean isErrorUrl = - unreachableWebDataUrl != null && unreachableWebDataUrl.equals(failingUrl); - if (isMainFrame && !isErrorUrl) { - if (errorCode != NetError.ERR_ABORTED) { - // This error code is generated for the following reasons: - // - WebView.stopLoading is called, - // - the navigation is intercepted by the embedder via shouldOverrideNavigation. - // - // The Android WebView does not notify the embedder of these situations using - // this error code with the WebViewClient.onReceivedError callback. - AwContentsClient.this.onReceivedError( - ErrorCodeConversionHelper.convertErrorCode(errorCode), description, - failingUrl); - } - // Need to call onPageFinished after onReceivedError (if there is an error) for - // backwards compatibility with the classic webview. - AwContentsClient.this.onPageFinished(failingUrl); - } - } - - @Override - public void didNavigateMainFrame(String url, String baseUrl, - boolean isNavigationToDifferentPage, boolean isFragmentNavigation) { - // This is here to emulate the Classic WebView firing onPageFinished for main frame - // navigations where only the hash fragment changes. - if (isFragmentNavigation) { - AwContentsClient.this.onPageFinished(url); - } - } - - @Override - public void didNavigateAnyFrame(String url, String baseUrl, boolean isReload) { - AwContentsClient.this.doUpdateVisitedHistory(url, isReload); - } - - } - - final void installWebContentsObserver(WebContents webContents) { - if (mWebContentsObserver != null) { - mWebContentsObserver.detachFromWebContents(); - } - mWebContentsObserver = new AwWebContentsObserver(webContents); - } - final AwContentsClientCallbackHelper getCallbackHelper() { return mCallbackHelper; } diff --git a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java index d495ba98bfa1e..498dfbd436edb 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java +++ b/android_webview/java/src/org/chromium/android_webview/AwContentsClientBridge.java @@ -207,7 +207,7 @@ protected void selectClientCertificate(final int id, final String[] keyTypes, } if (cert != null) { nativeProvideClientCertificateResponse(mNativeContentsClientBridge, id, - cert.certChain, cert.privateKey); + cert.mCertChain, cert.mPrivateKey); return; } // Build the list of principals from encoded versions. diff --git a/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java b/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java index 3789dc7ba5fcd..6957a97ced78b 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java +++ b/android_webview/java/src/org/chromium/android_webview/AwLayoutSizer.java @@ -11,8 +11,6 @@ * Helper methods used to manage the layout of the View that contains AwContents. */ public class AwLayoutSizer { - public static final int FIXED_LAYOUT_HEIGHT = 0; - // These are used to prevent a re-layout if the content size changes within a dimension that is // fixed by the view system. private boolean mWidthMeasurementIsFixed; @@ -25,8 +23,6 @@ public class AwLayoutSizer { // Page scale factor. This is set to zero initially so that we don't attempt to do a layout if // we get the content size change notification first and a page scale change second. private float mPageScaleFactor = 0.0f; - // The page scale factor that was used in the most recent onMeasure call. - private float mLastMeasuredPageScaleFactor = 0.0f; // Whether to postpone layout requests. private boolean mFreezeLayoutRequests; @@ -40,22 +36,17 @@ public class AwLayoutSizer { // If mHeightMeasurementLimited is true then this contains the height limit. private int mHeightMeasurementLimit; - // The most recent width and height seen in onSizeChanged. - private int mLastWidth; - private int mLastHeight; - - // Used to prevent sending multiple setFixedLayoutSize notifications with the same values. - private int mLastSentFixedLayoutSizeWidth = -1; - private int mLastSentFixedLayoutSizeHeight = -1; - // Callback object for interacting with the View. private Delegate mDelegate; + /** + * Delegate interface through which the AwLayoutSizer communicates with the view it's sizing. + */ public interface Delegate { void requestLayout(); void setMeasuredDimension(int measuredWidth, int measuredHeight); - void setFixedLayoutSize(int widthDip, int heightDip); boolean isLayoutParamsHeightWrapContent(); + void setForceZeroLayoutHeight(boolean forceZeroHeight); } /** @@ -136,10 +127,6 @@ private void doUpdate(int widthCss, int heightCss, float pageScaleFactor) { } else { mDelegate.requestLayout(); } - } else if (pageScaleChanged && mLastWidth != 0) { - // Because the fixed layout size is directly impacted by the pageScaleFactor we must - // update it even if the physical size of the view doesn't change. - updateFixedLayoutSize(mLastWidth, mLastHeight, mPageScaleFactor); } } @@ -159,8 +146,6 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredHeight = contentHeightPix; int measuredWidth = contentWidthPix; - mLastMeasuredPageScaleFactor = mPageScaleFactor; - // Always use the given size unless unspecified. This matches WebViewClassic behavior. mWidthMeasurementIsFixed = (widthMode != MeasureSpec.UNSPECIFIED); mHeightMeasurementIsFixed = (heightMode == MeasureSpec.EXACTLY); @@ -193,9 +178,7 @@ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { * changed. */ public void onSizeChanged(int w, int h, int ow, int oh) { - mLastWidth = w; - mLastHeight = h; - updateFixedLayoutSize(mLastWidth, mLastHeight, mLastMeasuredPageScaleFactor); + updateLayoutSettings(); } /** @@ -204,72 +187,12 @@ public void onSizeChanged(int w, int h, int ow, int oh) { * This should be called after onSizeChanged regardless of whether the size has changed or not. */ public void onLayoutChange() { - updateFixedLayoutSize(mLastWidth, mLastHeight, mLastMeasuredPageScaleFactor); + updateLayoutSettings(); } - private void setFixedLayoutSize(int widthDip, int heightDip) { - if (widthDip == mLastSentFixedLayoutSizeWidth && - heightDip == mLastSentFixedLayoutSizeHeight) - return; - mLastSentFixedLayoutSizeWidth = widthDip; - mLastSentFixedLayoutSizeHeight = heightDip; - - mDelegate.setFixedLayoutSize(widthDip, heightDip); - } - - // This needs to be called every time either the physical size of the view is changed or the - // pageScale is changed. Since we need to ensure that this is called immediately after - // onSizeChanged we can't just wait for onLayoutChange. At the same time we can't only make this - // call from onSizeChanged, since onSizeChanged won't fire if the view's physical size doesn't - // change. - private void updateFixedLayoutSize(int w, int h, float pageScaleFactor) { - boolean wrapContentForHeight = mDelegate.isLayoutParamsHeightWrapContent(); - // If the WebView's size in the Android view system depends on the size of its contents then - // the viewport size cannot be directly calculated from the WebView's physical size as that - // can result in the layout being unstable (for example loading the following contents - //
a - // would cause the WebView to indefinitely attempt to increase its height by 50%). - // If both the width and height are fixed (specified by the parent View) then content size - // changes will not cause subsequent layout passes and so we don't need to do anything - // special. - // We assume the width is 'fixed' if the parent View specified an EXACT or an AT_MOST - // measureSpec for the width (in which case the AT_MOST upper bound is the width). - // That means that the WebView will ignore LayoutParams.width set to WRAP_CONTENT and will - // instead try to take up as much width as possible. This is necessary because it's not - // practical to do web layout without a set width. - // For height the behavior is different because for a given width it is possible to - // calculate the minimum height required to display all of the content. As such the WebView - // can size itself vertically to match the content height. Because certain container views - // (LinearLayout with a WRAP_CONTENT height, for example) can result in onMeasure calls with - // both EXACTLY and AT_MOST height measureSpecs it is not possible to infer the sizing - // policy for the whole subtree based on the parameters passed to the onMeasure call. - // For that reason the LayoutParams.height property of the WebView is used. This behaves - // more predictably and means that toggling the fixedLayoutSize mode (which can have - // significant impact on how the web contents is laid out) is a direct consequence of the - // developer's choice. The downside is that it could result in the Android layout being - // unstable if a parent of the WebView has a wrap_content height while the WebView itself - // has height set to match_parent. Unfortunately addressing this edge case is costly so it - // will have to stay as is (this is compatible with Classic behavior). - if ((mWidthMeasurementIsFixed && !wrapContentForHeight) || pageScaleFactor == 0) { - setFixedLayoutSize(0, 0); - return; - } - - final double dipAndPageScale = pageScaleFactor * mDIPScale; - final int contentWidthPix = (int) (mContentWidthCss * dipAndPageScale); - - int widthDip = (int) Math.ceil(w / dipAndPageScale); - - // Make sure that we don't introduce rounding errors if the viewport is to be exactly as - // wide as the contents. - if (w == contentWidthPix) { - widthDip = mContentWidthCss; - } - - // This is workaround due to the fact that in wrap content mode we need to use a fixed - // layout size independent of view height, otherwise things like
- // cause the webview to grow indefinitely. We need to use a height independent of the - // webview's height. 0 is the value used in WebViewClassic. - setFixedLayoutSize(widthDip, FIXED_LAYOUT_HEIGHT); + // This needs to be called every time either the physical size of the view is changed or layout + // params are updated. + private void updateLayoutSettings() { + mDelegate.setForceZeroLayoutHeight(mDelegate.isLayoutParamsHeightWrapContent()); } } diff --git a/android_webview/java/src/org/chromium/android_webview/AwResource.java b/android_webview/java/src/org/chromium/android_webview/AwResource.java index 3b3b3265f491e..b5c1a5468a48d 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwResource.java +++ b/android_webview/java/src/org/chromium/android_webview/AwResource.java @@ -26,17 +26,17 @@ public class AwResource { // Raw resource ID for an HTML page to be displayed in the case of // a specific load error. - private static int RAW_LOAD_ERROR; + private static int sRawLoadError; // Raw resource ID for an HTML page to be displayed in the case of // a generic load error. (It's called NO_DOMAIN for legacy reasons). - private static int RAW_NO_DOMAIN; + private static int sRawNoDomain; // String resource ID for the default text encoding to use. - private static int STRING_DEFAULT_TEXT_ENCODING; + private static int sStringDefaultTextEncoding; // Array resource ID for the configuration of platform specific key-systems. - private static int STRING_ARRAY_CONFIG_KEY_SYSTEM_UUID_MAPPING; + private static int sStringArrayConfigKeySystemUUIDMapping; // The embedder should inject a Resources object that will be used // to resolve Resource IDs into the actual resources. @@ -54,36 +54,36 @@ public static void setResources(Resources resources) { } public static void setErrorPageResources(int loaderror, int nodomain) { - RAW_LOAD_ERROR = loaderror; - RAW_NO_DOMAIN = nodomain; + sRawLoadError = loaderror; + sRawNoDomain = nodomain; } public static void setDefaultTextEncoding(int encoding) { - STRING_DEFAULT_TEXT_ENCODING = encoding; + sStringDefaultTextEncoding = encoding; } public static void setConfigKeySystemUuidMapping(int config) { - STRING_ARRAY_CONFIG_KEY_SYSTEM_UUID_MAPPING = config; + sStringArrayConfigKeySystemUUIDMapping = config; } @CalledByNative public static String getDefaultTextEncoding() { - return getResource(STRING_DEFAULT_TEXT_ENCODING, TYPE_STRING); + return getResource(sStringDefaultTextEncoding, TYPE_STRING); } @CalledByNative public static String getNoDomainPageContent() { - return getResource(RAW_NO_DOMAIN, TYPE_RAW); + return getResource(sRawNoDomain, TYPE_RAW); } @CalledByNative public static String getLoadErrorPageContent() { - return getResource(RAW_LOAD_ERROR, TYPE_RAW); + return getResource(sRawLoadError, TYPE_RAW); } public static String[] getConfigKeySystemUuidMapping() { // No need to cache, since this should be called only once. - return sResources.getStringArray(STRING_ARRAY_CONFIG_KEY_SYSTEM_UUID_MAPPING); + return sResources.getStringArray(sStringArrayConfigKeySystemUUIDMapping); } private static String getResource(int resid, int type) { diff --git a/android_webview/java/src/org/chromium/android_webview/AwSettings.java b/android_webview/java/src/org/chromium/android_webview/AwSettings.java index 73ac54ab7c02c..d87d57b4b4b94 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwSettings.java +++ b/android_webview/java/src/org/chromium/android_webview/AwSettings.java @@ -87,6 +87,8 @@ public enum LayoutAlgorithm { private boolean mDomStorageEnabled = false; private boolean mDatabaseEnabled = false; private boolean mUseWideViewport = false; + private boolean mZeroLayoutHeightDisablesViewportQuirk = false; + private boolean mForceZeroLayoutHeight = false; private boolean mLoadWithOverviewMode = false; private boolean mMediaPlaybackRequiresUserGesture = true; private String mDefaultVideoPosterURL; @@ -111,6 +113,7 @@ public enum LayoutAlgorithm { private boolean mShouldFocusFirstNode = true; private boolean mGeolocationEnabled = true; private boolean mAutoCompleteEnabled = true; + private boolean mFullscreenSupported = false; private boolean mSupportZoom = true; private boolean mBuiltInZoomControls = false; private boolean mDisplayZoomControls = true; @@ -461,6 +464,21 @@ private boolean getEnableSupportedHardwareAcceleratedFeaturesLocked() { return mEnableSupportedHardwareAcceleratedFeatures; } + public void setFullscreenSupported(boolean supported) { + synchronized (mAwSettingsLock) { + if (mFullscreenSupported != supported) { + mFullscreenSupported = supported; + mEventHandler.updateWebkitPreferencesLocked(); + } + } + } + + @CalledByNative + private boolean getFullscreenSupportedLocked() { + assert Thread.holdsLock(mAwSettingsLock); + return mFullscreenSupported; + } + /** * See {@link android.webkit.WebSettings#setNeedInitialFocus}. */ @@ -1211,6 +1229,48 @@ private boolean getUseWideViewportLocked() { return mUseWideViewport; } + public void setZeroLayoutHeightDisablesViewportQuirk(boolean enabled) { + synchronized (mAwSettingsLock) { + if (mZeroLayoutHeightDisablesViewportQuirk != enabled) { + mZeroLayoutHeightDisablesViewportQuirk = enabled; + mEventHandler.updateWebkitPreferencesLocked(); + } + } + } + + public boolean getZeroLayoutHeightDisablesViewportQuirk() { + synchronized (mAwSettingsLock) { + return getZeroLayoutHeightDisablesViewportQuirkLocked(); + } + } + + @CalledByNative + private boolean getZeroLayoutHeightDisablesViewportQuirkLocked() { + assert Thread.holdsLock(mAwSettingsLock); + return mZeroLayoutHeightDisablesViewportQuirk; + } + + public void setForceZeroLayoutHeight(boolean enabled) { + synchronized (mAwSettingsLock) { + if (mForceZeroLayoutHeight != enabled) { + mForceZeroLayoutHeight = enabled; + mEventHandler.updateWebkitPreferencesLocked(); + } + } + } + + public boolean getForceZeroLayoutHeight() { + synchronized (mAwSettingsLock) { + return getForceZeroLayoutHeightLocked(); + } + } + + @CalledByNative + private boolean getForceZeroLayoutHeightLocked() { + assert Thread.holdsLock(mAwSettingsLock); + return mForceZeroLayoutHeight; + } + @CalledByNative private boolean getPasswordEchoEnabledLocked() { assert Thread.holdsLock(mAwSettingsLock); diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegate.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegate.java index dbcf330120dcd..a595d3f9b667c 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegate.java +++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegate.java @@ -41,5 +41,5 @@ public abstract void runFileChooser(int processId, int renderId, int mode_flags, // Call in response to a prior runFileChooser call. protected static native void nativeFilesSelectedInChooser(int processId, int renderId, - int mode_flags, String[] filePath); + int mode_flags, String[] filePath, String[] displayName); } diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java index 70a148b57f904..2caf6a98d8f43 100644 --- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java +++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java @@ -4,14 +4,20 @@ package org.chromium.android_webview; +import android.content.ContentResolver; +import android.content.Context; +import android.net.Uri; +import android.os.AsyncTask; import android.os.Handler; import android.os.Message; +import android.provider.MediaStore; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.ValueCallback; +import org.chromium.base.ContentUriUtils; import org.chromium.base.ThreadUtils; import org.chromium.content.browser.ContentVideoView; import org.chromium.content.browser.ContentViewCore; @@ -26,11 +32,13 @@ class AwWebContentsDelegateAdapter extends AwWebContentsDelegate { final AwContentsClient mContentsClient; View mContainerView; + final Context mContext; public AwWebContentsDelegateAdapter(AwContentsClient contentsClient, - View containerView) { + View containerView, Context context) { mContentsClient = contentsClient; setContainerView(containerView); + mContext = context; } public void setContainerView(View containerView) { @@ -161,24 +169,31 @@ public void handleMessage(Message msg) { } @Override - public void runFileChooser(final int processId, final int renderId, final int mode_flags, + public void runFileChooser(final int processId, final int renderId, final int modeFlags, String acceptTypes, String title, String defaultFilename, boolean capture) { AwContentsClient.FileChooserParams params = new AwContentsClient.FileChooserParams(); - params.mode = mode_flags; + params.mode = modeFlags; params.acceptTypes = acceptTypes; params.title = title; params.defaultFilename = defaultFilename; params.capture = capture; mContentsClient.showFileChooser(new ValueCallback() { - boolean completed = false; + boolean mCompleted = false; @Override public void onReceiveValue(String[] results) { - if (completed) { + if (mCompleted) { throw new IllegalStateException("Duplicate showFileChooser result"); } - completed = true; - nativeFilesSelectedInChooser(processId, renderId, mode_flags, results); + mCompleted = true; + if (results == null) { + nativeFilesSelectedInChooser( + processId, renderId, modeFlags, null, null); + return; + } + GetDisplayNameTask task = new GetDisplayNameTask( + mContext.getContentResolver(), processId, renderId, modeFlags, results); + task.execute(); } }, params); } @@ -200,4 +215,46 @@ public void toggleFullscreenModeForTab(boolean enterFullscreen) { if (videoView != null) videoView.exitFullscreen(false); } } + + private static class GetDisplayNameTask extends AsyncTask { + final int mProcessId; + final int mRenderId; + final int mModeFlags; + final String[] mFilePaths; + final ContentResolver mContentResolver; + + public GetDisplayNameTask(ContentResolver contentResolver, int processId, int renderId, + int modeFlags, String[] filePaths) { + mProcessId = processId; + mRenderId = renderId; + mModeFlags = modeFlags; + mFilePaths = filePaths; + mContentResolver = contentResolver; + } + + @Override + protected String[] doInBackground(Void...voids) { + String[] displayNames = new String[mFilePaths.length]; + for (int i = 0; i < mFilePaths.length; i++) { + displayNames[i] = resolveFileName(mFilePaths[i]); + } + return displayNames; + } + + @Override + protected void onPostExecute(String[] result) { + nativeFilesSelectedInChooser(mProcessId, mRenderId, mModeFlags, mFilePaths, result); + } + + /** + * @return the display name of a path if it is a content URI and is present in the database + * or an empty string otherwise. + */ + private String resolveFileName(String filePath) { + if (mContentResolver == null || filePath == null) return ""; + Uri uri = Uri.parse(filePath); + return ContentUriUtils.getDisplayName( + uri, mContentResolver, MediaStore.MediaColumns.DISPLAY_NAME); + } + } } diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java new file mode 100644 index 0000000000000..d7703dc20a47f --- /dev/null +++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsObserver.java @@ -0,0 +1,70 @@ +// 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.android_webview; + +import org.chromium.content.browser.WebContentsObserverAndroid; +import org.chromium.content_public.browser.WebContents; +import org.chromium.net.NetError; + +/** + * Routes notifications from WebContents to AwContentsClient and other listeners. + */ +public class AwWebContentsObserver extends WebContentsObserverAndroid { + private final AwContentsClient mAwContentsClient; + + public AwWebContentsObserver(WebContents webContents, AwContentsClient awContentsClient) { + super(webContents); + mAwContentsClient = awContentsClient; + } + + @Override + public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) { + String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl(); + boolean isErrorUrl = + unreachableWebDataUrl != null && unreachableWebDataUrl.equals(validatedUrl); + if (isMainFrame && !isErrorUrl) { + mAwContentsClient.onPageFinished(validatedUrl); + } + } + + @Override + public void didFailLoad(boolean isProvisionalLoad, + boolean isMainFrame, int errorCode, String description, String failingUrl) { + String unreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl(); + boolean isErrorUrl = + unreachableWebDataUrl != null && unreachableWebDataUrl.equals(failingUrl); + if (isMainFrame && !isErrorUrl) { + if (errorCode != NetError.ERR_ABORTED) { + // This error code is generated for the following reasons: + // - WebView.stopLoading is called, + // - the navigation is intercepted by the embedder via shouldOverrideNavigation. + // + // The Android WebView does not notify the embedder of these situations using + // this error code with the WebViewClient.onReceivedError callback. + mAwContentsClient.onReceivedError( + ErrorCodeConversionHelper.convertErrorCode(errorCode), description, + failingUrl); + } + // Need to call onPageFinished after onReceivedError (if there is an error) for + // backwards compatibility with the classic webview. + mAwContentsClient.onPageFinished(failingUrl); + } + } + + @Override + public void didNavigateMainFrame(String url, String baseUrl, + boolean isNavigationToDifferentPage, boolean isFragmentNavigation) { + // This is here to emulate the Classic WebView firing onPageFinished for main frame + // navigations where only the hash fragment changes. + if (isFragmentNavigation) { + mAwContentsClient.onPageFinished(url); + } + } + + @Override + public void didNavigateAnyFrame(String url, String baseUrl, boolean isReload) { + mAwContentsClient.doUpdateVisitedHistory(url, isReload); + } +} diff --git a/android_webview/java/src/org/chromium/android_webview/ClientCertLookupTable.java b/android_webview/java/src/org/chromium/android_webview/ClientCertLookupTable.java index 65ef44aced532..88e6ed1408ef1 100644 --- a/android_webview/java/src/org/chromium/android_webview/ClientCertLookupTable.java +++ b/android_webview/java/src/org/chromium/android_webview/ClientCertLookupTable.java @@ -22,17 +22,17 @@ public class ClientCertLookupTable { * A container for the certificate data. */ public static class Cert { - AndroidPrivateKey privateKey; - byte[][] certChain; + AndroidPrivateKey mPrivateKey; + byte[][] mCertChain; public Cert(AndroidPrivateKey privateKey, byte[][] certChain) { - this.privateKey = privateKey; + this.mPrivateKey = privateKey; byte[][] newChain = new byte[certChain.length][]; for (int i = 0; i < certChain.length; i++) { newChain[i] = Arrays.copyOf(certChain[i], certChain[i].length); } - this.certChain = newChain; + this.mCertChain = newChain; } - }; + } private final Map mCerts; private final Set mDenieds; diff --git a/android_webview/java/src/org/chromium/android_webview/permission/AwPermissionRequest.java b/android_webview/java/src/org/chromium/android_webview/permission/AwPermissionRequest.java index c622b958ab2b2..d0860f4fd4d65 100644 --- a/android_webview/java/src/org/chromium/android_webview/permission/AwPermissionRequest.java +++ b/android_webview/java/src/org/chromium/android_webview/permission/AwPermissionRequest.java @@ -17,7 +17,7 @@ */ @JNINamespace("android_webview") public class AwPermissionRequest { - private static String TAG = "AwPermissionRequest"; + private static final String TAG = "AwPermissionRequest"; private Uri mOrigin; private long mResources; diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java index 3ada155c7f510..84278da95cafb 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AndroidViewIntegrationTest.java @@ -23,7 +23,7 @@ * Tests for certain edge cases related to integrating with the Android view system. */ public class AndroidViewIntegrationTest extends AwTestBase { - final int CONTENT_SIZE_CHANGE_STABILITY_TIMEOUT_MS = 1000; + private static final int CONTENT_SIZE_CHANGE_STABILITY_TIMEOUT_MS = 1000; private static class OnContentSizeChangedHelper extends CallbackHelper { private int mWidth; @@ -76,7 +76,7 @@ public AwLayoutSizer createLayoutSizer() { }; } - final LinearLayout.LayoutParams wrapContentLayoutParams = + final LinearLayout.LayoutParams mWrapContentLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); private AwTestContainerView createCustomTestContainerViewOnMainSync( @@ -87,7 +87,7 @@ private AwTestContainerView createCustomTestContainerViewOnMainSync( @Override public void run() { testContainerView.set(createAwTestContainerView(awContentsClient)); - testContainerView.get().setLayoutParams(wrapContentLayoutParams); + testContainerView.get().setLayoutParams(mWrapContentLayoutParams); testContainerView.get().setVisibility(visibility); } }); @@ -343,10 +343,7 @@ public void testViewSizedCorrectlyInWrapContentModeWithDynamicContents() throws final int expectedWidthCss = (int) Math.ceil(getRootLayoutWidthOnMainThread() / deviceDIPScale); - final int expectedHeightCss = contentHeightCss + - // The second div in the contents is styled to have 150% of the viewport height, hence - // the 1.5. - (int) (AwLayoutSizer.FIXED_LAYOUT_HEIGHT * 1.5); + final int expectedHeightCss = contentHeightCss; loadPageOfSizeAndWaitForSizeChange(testContainerView.getAwContents(), mOnContentSizeChangedHelper, expectedWidthCss, contentHeightCss, true); diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java index fb29ea5ce6382..897e7105d5336 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientFullScreenVideoTest.java @@ -10,6 +10,7 @@ import junit.framework.Assert; +import org.chromium.android_webview.test.util.JavascriptEventObserver; import org.chromium.android_webview.test.util.VideoTestWebServer; import org.chromium.base.CommandLine; import org.chromium.base.test.util.Feature; @@ -24,25 +25,26 @@ public class AwContentsClientFullScreenVideoTest extends AwTestBase { private FullScreenVideoTestAwContentsClient mContentsClient; private ContentViewCore mContentViewCore; - private VideoTestWebServer webServer; - private AwTestContainerView testContainerView; + private VideoTestWebServer mWebServer; + private AwTestContainerView mTestContainerView; @Override protected void setUp() throws Exception { super.setUp(); mContentsClient = new FullScreenVideoTestAwContentsClient(getActivity()); - testContainerView = + mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient); - mContentViewCore = testContainerView.getContentViewCore(); - enableJavaScriptOnUiThread(testContainerView.getAwContents()); - webServer = new VideoTestWebServer( + mContentViewCore = mTestContainerView.getContentViewCore(); + enableJavaScriptOnUiThread(mTestContainerView.getAwContents()); + mTestContainerView.getAwContents().getSettings().setFullscreenSupported(true); + mWebServer = new VideoTestWebServer( getInstrumentation().getTargetContext()); } @Override protected void tearDown() throws Exception { super.tearDown(); - if (webServer != null) webServer.getTestWebServer().shutdown(); + if (mWebServer != null) mWebServer.getTestWebServer().shutdown(); } @MediumTest @@ -109,6 +111,25 @@ public void testOnShowCustomViewAndPlayWithHtmlControl() throws Throwable { mContentViewCore, VideoTestWebServer.VIDEO_ID)); } + @MediumTest + @Feature({"AndroidWebView"}) + public void testFullscreenNotSupported() throws Throwable { + mTestContainerView.getAwContents().getSettings().setFullscreenSupported(false); + + final JavascriptEventObserver fullScreenErrorObserver = new JavascriptEventObserver(); + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + fullScreenErrorObserver.register(mContentViewCore, "javaFullScreenErrorObserver"); + } + }); + + loadTestPageAndClickFullscreen(); + + Assert.assertTrue(fullScreenErrorObserver.waitForEvent(500)); + Assert.assertFalse(mContentsClient.wasCustomViewShownCalled()); + } + private static boolean areHtmlControlsEnabled() { return !CommandLine.getInstance().hasSwitch( ContentSwitches.DISABLE_OVERLAY_FULLSCREEN_VIDEO_SUBTITLE); @@ -122,13 +143,17 @@ private void doOnShowAndHideCustomViewTest(final Runnable existFullscreen) } private void doOnShowCustomViewTest() throws Exception { - loadUrlSync(testContainerView.getAwContents(), + loadTestPageAndClickFullscreen(); + mContentsClient.waitForCustomViewShown(); + } + + private void loadTestPageAndClickFullscreen() throws Exception { + loadUrlSync(mTestContainerView.getAwContents(), mContentsClient.getOnPageFinishedHelper(), - webServer.getFullScreenVideoTestURL()); + mWebServer.getFullScreenVideoTestURL()); // Click the button in full_screen_video_test.html to enter fullscreen. TouchCommon touchCommon = new TouchCommon(this); - touchCommon.singleClickView(testContainerView); - mContentsClient.waitForCustomViewShown(); + touchCommon.singleClickView(mTestContainerView); } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetVideoLoadingProgressViewTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetVideoLoadingProgressViewTest.java index 0d424f5a7e813..25b1b03373c05 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetVideoLoadingProgressViewTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientGetVideoLoadingProgressViewTest.java @@ -62,6 +62,7 @@ protected View getVideoLoadingProgressView() { final AwTestContainerView testContainerView = createAwTestContainerViewOnMainSync(contentsClient); final AwContents awContents = testContainerView.getAwContents(); + awContents.getSettings().setFullscreenSupported(true); enableJavaScriptOnUiThread(awContents); VideoTestWebServer webServer = new VideoTestWebServer( getInstrumentation().getTargetContext()); diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java index de8e625172ba8..d83c96a161ad5 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientShouldInterceptRequestTest.java @@ -117,8 +117,8 @@ public OnLoadResourceHelper getOnLoadResourceHelper() { } } - final int teapotStatusCode = 418; - final String teapotResponsePhrase = "I'm a teapot"; + private static final int TEAPOT_STATUS_CODE = 418; + private static final String TEAPOT_RESPONSE_PHRASE = "I'm a teapot"; private String addPageToTestServer(TestWebServer webServer, String httpPath, String html) { List> headers = new ArrayList>(); @@ -492,8 +492,8 @@ public void testHttpStatusCodeAndText() throws Throwable { mShouldInterceptRequestHelper.setReturnValue( new AwWebResourceResponse("text/html", "UTF-8", new EmptyInputStream(), - teapotStatusCode, teapotResponsePhrase, new HashMap())); - assertEquals("\"[" + teapotStatusCode + "][" + teapotResponsePhrase + "]\"", + TEAPOT_STATUS_CODE, TEAPOT_RESPONSE_PHRASE, new HashMap())); + assertEquals("\"[" + TEAPOT_STATUS_CODE + "][" + TEAPOT_RESPONSE_PHRASE + "]\"", executeJavaScriptAndWaitForResult(mAwContents, mContentsClient, syncGetJs)); } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java index 9eb93b9423c37..42a3b708e3332 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwContentsClientVisitedHistoryTest.java @@ -8,6 +8,7 @@ import android.webkit.ValueCallback; import org.chromium.android_webview.AwContents; +import org.chromium.android_webview.test.TestAwContentsClient.DoUpdateVisitedHistoryHelper; import org.chromium.base.test.util.Feature; import org.chromium.content.browser.test.util.CallbackHelper; import org.chromium.net.test.util.TestWebServer; @@ -16,7 +17,7 @@ * Tests for AwContentsClient.getVisitedHistory and AwContents.doUpdateVisitedHistory callbacks. */ public class AwContentsClientVisitedHistoryTest extends AwTestBase { - public static class GetVisitedHistoryHelper extends CallbackHelper { + private static class GetVisitedHistoryHelper extends CallbackHelper { private ValueCallback mCallback; private boolean mSaveCallback = false; @@ -37,58 +38,27 @@ public void notifyCalled(ValueCallback callback) { } } - public static class DoUpdateVisitedHistoryHelper extends CallbackHelper { - String mUrl; - boolean mIsReload; - - public String getUrl() { - assert getCallCount() > 0; - return mUrl; - } - - public boolean getIsReload() { - assert getCallCount() > 0; - return mIsReload; - } - - public void notifyCalled(String url, boolean isReload) { - mUrl = url; - mIsReload = isReload; - notifyCalled(); - } - } - - private static class TestAwContentsClient - extends org.chromium.android_webview.test.TestAwContentsClient { + private static class VisitedHistoryTestAwContentsClient extends TestAwContentsClient { private GetVisitedHistoryHelper mGetVisitedHistoryHelper; - private DoUpdateVisitedHistoryHelper mDoUpdateVisitedHistoryHelper; - public TestAwContentsClient() { + public VisitedHistoryTestAwContentsClient() { mGetVisitedHistoryHelper = new GetVisitedHistoryHelper(); - mDoUpdateVisitedHistoryHelper = new DoUpdateVisitedHistoryHelper(); } public GetVisitedHistoryHelper getGetVisitedHistoryHelper() { return mGetVisitedHistoryHelper; } - public DoUpdateVisitedHistoryHelper getDoUpdateVisitedHistoryHelper() { - return mDoUpdateVisitedHistoryHelper; - } - @Override public void getVisitedHistory(ValueCallback callback) { getGetVisitedHistoryHelper().notifyCalled(callback); } - @Override - public void doUpdateVisitedHistory(String url, boolean isReload) { - getDoUpdateVisitedHistoryHelper().notifyCalled(url, isReload); - } } - private TestAwContentsClient mContentsClient = new TestAwContentsClient(); + private VisitedHistoryTestAwContentsClient mContentsClient = + new VisitedHistoryTestAwContentsClient(); @Feature({"AndroidWebView"}) @SmallTest diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java new file mode 100644 index 0000000000000..6a15244bb79db --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwJavaBridgeTest.java @@ -0,0 +1,74 @@ +// 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.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.android_webview.AwContents; +import org.chromium.base.test.util.Feature; +import org.chromium.content.browser.JavascriptInterface; + +/** + * Test suite for the WebView specific JavaBridge features. + */ +public class AwJavaBridgeTest extends AwTestBase { + + private TestAwContentsClient mContentsClient = new TestAwContentsClient(); + private AwTestContainerView mTestContainerView; + + @Override + protected void setUp() throws Exception { + super.setUp(); + mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient); + } + + @SmallTest + @Feature({"AndroidWebView", "Android-JavaBridge"}) + public void testDestroyFromJavaObject() throws Throwable { + final String HTML = "Hello World"; + final TestAwContentsClient client2 = new TestAwContentsClient(); + final AwTestContainerView view2 = createAwTestContainerViewOnMainSync(client2); + final AwContents awContents = mTestContainerView.getAwContents(); + + class Test { + @JavascriptInterface + public void destroy() { + try { + runTestOnUiThread(new Runnable() { + @Override + public void run() { + awContents.destroy(); + } + }); + // Destroying one AwContents from within the JS callback should still + // leave others functioning. + loadDataSync(view2.getAwContents(), client2.getOnPageFinishedHelper(), + HTML, "text/html", false); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + } + + enableJavaScriptOnUiThread(awContents); + runTestOnUiThread(new Runnable() { + @Override + public void run() { + awContents.addPossiblyUnsafeJavascriptInterface(new Test(), "test", null); + } + }); + + loadDataSync(awContents, mContentsClient.getOnPageFinishedHelper(), HTML, + "text/html", false); + + // Ensure the JS interface object is there, and invoke the test method. + assertEquals("\"function\"", executeJavaScriptAndWaitForResult( + awContents, mContentsClient, "typeof test.destroy")); + awContents.evaluateJavaScript("test.destroy()", null); + + client2.getOnPageFinishedHelper().waitForCallback( + client2.getOnPageFinishedHelper().getCallCount()); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java index 4ef786bfbb93e..18e06ebb9116e 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwLayoutSizerTest.java @@ -12,14 +12,16 @@ import org.chromium.android_webview.AwLayoutSizer; import org.chromium.base.test.util.Feature; +/** + * Unittests for the AwLayoutSizer class. + */ public class AwLayoutSizerTest extends InstrumentationTestCase { static class LayoutSizerDelegate implements AwLayoutSizer.Delegate { public int requestLayoutCallCount; public boolean setMeasuredDimensionCalled; public int measuredWidth; public int measuredHeight; - public int fixedLayoutWidth; - public int fixedLayoutHeight; + public boolean forceZeroHeight; public boolean heightWrapContent; @Override @@ -35,9 +37,8 @@ public void setMeasuredDimension(int measuredWidth, int measuredHeight) { } @Override - public void setFixedLayoutSize(int widthDip, int heightDip) { - fixedLayoutWidth = widthDip; - fixedLayoutHeight = heightDip; + public void setForceZeroLayoutHeight(boolean forceZeroHeight) { + this.forceZeroHeight = forceZeroHeight; } @Override @@ -347,16 +348,6 @@ public void testViewportWithExactMeasureSpec() { MeasureSpec.makeMeasureSpec(measuredHeight, MeasureSpec.EXACTLY)); assertEquals(measuredWidth, delegate.measuredWidth & View.MEASURED_SIZE_MASK); assertEquals(measuredHeight, delegate.measuredHeight & View.MEASURED_SIZE_MASK); - - layoutSizer.onSizeChanged(measuredWidth, measuredHeight, 0, 0); - - assertEquals(0, delegate.fixedLayoutWidth); - assertEquals(0, delegate.fixedLayoutHeight); - - layoutSizer.onPageScaleChanged(2.0f); - - assertEquals(0, delegate.fixedLayoutWidth); - assertEquals(0, delegate.fixedLayoutHeight); } @SmallTest @@ -386,54 +377,6 @@ public void testViewportDipSizeOverrideRounding() { layoutSizer.onSizeChanged(measuredWidth, measuredHeight, 0, 0); } - @SmallTest - @Feature({"AndroidWebView"}) - public void testViewportWithUnspecifiedMeasureSpec() { - AwLayoutSizer layoutSizer = new AwLayoutSizer(); - LayoutSizerDelegate delegate = new LayoutSizerDelegate(); - layoutSizer.setDelegate(delegate); - - final float dipScale = 1.5f; - final int pageScale = 2; - final int dipAndPageScale = (int) (dipScale * pageScale); - - int contentWidth = 800; - int contentHeight = 400; - int atMostWidth = contentWidth * dipAndPageScale; - int atMostHeight = contentHeight * dipAndPageScale; - - layoutSizer.setDIPScale(dipScale); - layoutSizer.onContentSizeChanged(contentWidth, contentHeight); - layoutSizer.onPageScaleChanged(pageScale); - layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - - assertTrue(delegate.setMeasuredDimensionCalled); - int measuredWidth = delegate.measuredWidth & View.MEASURED_SIZE_MASK; - int measuredHeight = delegate.measuredHeight & View.MEASURED_SIZE_MASK; - - int sizeWidth = measuredWidth; - int sizeHeight = measuredHeight; - layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0); - - assertEquals(contentWidth, delegate.fixedLayoutWidth); - assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight); - - sizeWidth = measuredWidth * 2; - sizeHeight = measuredHeight * 2; - layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0); - - assertEquals(sizeWidth / dipAndPageScale, delegate.fixedLayoutWidth); - assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight); - - sizeWidth = measuredWidth / 2; - sizeHeight = measuredHeight / 2; - layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0); - - assertEquals(sizeWidth / dipAndPageScale, delegate.fixedLayoutWidth); - assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight); - } - @SmallTest @Feature({"AndroidWebView"}) public void testViewportWithAtMostMeasureSpec() { @@ -451,6 +394,8 @@ public void testViewportWithAtMostMeasureSpec() { int contentWidthPix = contentWidth * dipAndPageScale; int contentHeightPix = contentHeight * dipAndPageScale; + assertFalse(delegate.forceZeroHeight); + layoutSizer.setDIPScale(dipScale); layoutSizer.onContentSizeChanged(contentWidth, contentHeight); layoutSizer.onPageScaleChanged(pageScale); @@ -458,54 +403,21 @@ public void testViewportWithAtMostMeasureSpec() { MeasureSpec.makeMeasureSpec(contentHeightPix * 2, MeasureSpec.AT_MOST)); assertTrue(delegate.setMeasuredDimensionCalled); + assertFalse(delegate.forceZeroHeight); + int measuredWidth = delegate.measuredWidth & View.MEASURED_SIZE_MASK; int measuredHeight = delegate.measuredHeight & View.MEASURED_SIZE_MASK; + layoutSizer.onSizeChanged(measuredWidth, measuredHeight, 0, 0); - int sizeWidth = measuredWidth; - int sizeHeight = measuredHeight; - layoutSizer.onSizeChanged(sizeWidth, sizeHeight, 0, 0); - - assertEquals(contentWidth, delegate.fixedLayoutWidth); - assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight); - } - - @SmallTest - @Feature({"AndroidWebView"}) - public void testFixedLayoutViewportGoesBackToZeroWithWrapContentMeasureSpec() { - AwLayoutSizer layoutSizer = new AwLayoutSizer(); - LayoutSizerDelegate delegate = new LayoutSizerDelegate(); - layoutSizer.setDelegate(delegate); - layoutSizer.setDIPScale(DIP_SCALE); - - layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, FIRST_CONTENT_HEIGHT); - layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE); - layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE), - (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0); - - assertTrue(delegate.fixedLayoutWidth != 0); - assertEquals(AwLayoutSizer.FIXED_LAYOUT_HEIGHT, delegate.fixedLayoutHeight); - - layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, AwLayoutSizer.FIXED_LAYOUT_HEIGHT); - layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE), - (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0); - assertTrue(delegate.fixedLayoutWidth != 0); - assertEquals(0, delegate.fixedLayoutHeight); - - layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, 0); - layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE), - (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0); - assertTrue(delegate.fixedLayoutWidth != 0); - assertEquals(0, delegate.fixedLayoutHeight); + assertTrue(delegate.forceZeroHeight); } @SmallTest @Feature({"AndroidWebView"}) - public void testFixedLayoutSizeUpdatedOnPageScaleChangeItNoLayoutRequest() { + public void testFixedLayoutSizeDependsOnHeightWrapContent() { AwLayoutSizer layoutSizer = new AwLayoutSizer(); LayoutSizerDelegate delegate = new LayoutSizerDelegate(); - delegate.heightWrapContent = true; + delegate.heightWrapContent = false; layoutSizer.setDelegate(delegate); layoutSizer.setDIPScale(DIP_SCALE); @@ -516,44 +428,12 @@ public void testFixedLayoutSizeUpdatedOnPageScaleChangeItNoLayoutRequest() { MeasureSpec.makeMeasureSpec(AT_MOST_MEASURE_SIZE, MeasureSpec.AT_MOST)); layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0); - assertTrue(delegate.fixedLayoutWidth != 0); - final int fixedLayoutWidth = delegate.fixedLayoutWidth; - final int requestLayoutCallCount = delegate.requestLayoutCallCount; - layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE * 2f); - assertEquals(requestLayoutCallCount, delegate.requestLayoutCallCount); - assertEquals(fixedLayoutWidth / 2, delegate.fixedLayoutWidth); - } - - @SmallTest - @Feature({"AndroidWebView"}) - public void testFixedLayoutSizeUpdatedIfNoSizeChangeAfterLayoutRequested() { - AwLayoutSizer layoutSizer = new AwLayoutSizer(); - LayoutSizerDelegate delegate = new LayoutSizerDelegate(); - layoutSizer.setDelegate(delegate); - layoutSizer.setDIPScale(DIP_SCALE); - - layoutSizer.onContentSizeChanged(FIRST_CONTENT_WIDTH, FIRST_CONTENT_HEIGHT); - layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE); - layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - - layoutSizer.onSizeChanged((int) (FIRST_CONTENT_WIDTH * DIP_SCALE), - (int) (FIRST_CONTENT_HEIGHT * DIP_SCALE), 0, 0); - - assertTrue(delegate.fixedLayoutWidth != 0); - final int fixedLayoutWidth = delegate.fixedLayoutWidth; - final int requestLayoutCallCount = delegate.requestLayoutCallCount; - layoutSizer.onPageScaleChanged(INITIAL_PAGE_SCALE * 0.5f); - assertEquals(requestLayoutCallCount + 1, delegate.requestLayoutCallCount); - assertEquals(fixedLayoutWidth, delegate.fixedLayoutWidth); + assertFalse(delegate.forceZeroHeight); - // onMeasure and onLayoutChange should always be called as a result of the AwLayoutSizer - // calling Delegate.requestLayout. - layoutSizer.onMeasure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - layoutSizer.onLayoutChange(); + delegate.heightWrapContent = true; + layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0); - assertEquals(fixedLayoutWidth * 2, delegate.fixedLayoutWidth); + assertTrue(delegate.forceZeroHeight); } @SmallTest @@ -572,7 +452,12 @@ public void testFixedLayoutSizeDoesNotDependOnMeasureSpec() { MeasureSpec.makeMeasureSpec(AT_MOST_MEASURE_SIZE, MeasureSpec.AT_MOST)); layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0); - assertEquals(0, delegate.fixedLayoutWidth); - assertEquals(0, delegate.fixedLayoutHeight); + assertFalse(delegate.forceZeroHeight); + + layoutSizer.onMeasure( + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + layoutSizer.onSizeChanged(AT_MOST_MEASURE_SIZE, AT_MOST_MEASURE_SIZE, 0, 0); + assertFalse(delegate.forceZeroHeight); } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java index ed6b10b1c3656..ea0f68a74bb3e 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwSettingsTest.java @@ -1355,6 +1355,127 @@ private String getData() { private int mOnScaleChangedCallCount; } + class AwSettingsForceZeroLayoutHeightTestHelper extends AwSettingsTestHelper { + + AwSettingsForceZeroLayoutHeightTestHelper( + AwTestContainerView containerView, + TestAwContentsClient contentViewClient, + boolean withViewPortTag) throws Throwable { + super(containerView, contentViewClient, true); + mWithViewPortTag = withViewPortTag; + mAwSettings.setUseWideViewPort(true); + } + + @Override + protected Boolean getAlteredValue() { + return ENABLED; + } + + @Override + protected Boolean getInitialValue() { + return DISABLED; + } + + @Override + protected Boolean getCurrentValue() { + return mAwSettings.getForceZeroLayoutHeight(); + } + + @Override + protected void setCurrentValue(Boolean value) { + mAwSettings.setForceZeroLayoutHeight(value); + } + + @Override + protected void doEnsureSettingHasValue(Boolean value) throws Throwable { + loadDataSync(getData()); + int height = Integer.parseInt(getTitleOnUiThread()); + if (value) { + assertEquals(0, height); + } else { + assertTrue("Div should be at least 50px high, was: " + height, height >= 50); + } + } + + private String getData() { + return "" + + (mWithViewPortTag ? "" : "") + + " " + + "" + + " " + + "
test
" + + "
" + + ""; + } + + private final boolean mWithViewPortTag; + } + + class AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper extends + AwSettingsTestHelper { + + AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper( + AwTestContainerView containerView, + TestAwContentsClient contentViewClient) throws Throwable { + super(containerView, contentViewClient, true); + mAwSettings.setUseWideViewPort(true); + mAwSettings.setForceZeroLayoutHeight(true); + } + + @Override + protected Boolean getAlteredValue() { + return ENABLED; + } + + @Override + protected Boolean getInitialValue() { + return DISABLED; + } + + @Override + protected Boolean getCurrentValue() { + return mAwSettings.getZeroLayoutHeightDisablesViewportQuirk(); + } + + @Override + protected void setCurrentValue(Boolean value) { + mAwSettings.setZeroLayoutHeightDisablesViewportQuirk(value); + } + + @Override + protected void doEnsureSettingHasValue(Boolean value) throws Throwable { + DeviceDisplayInfo deviceInfo = DeviceDisplayInfo.create(mContext); + int displayWidth = (int) (deviceInfo.getDisplayWidth() / deviceInfo.getDIPScale()); + + loadDataSync(getData()); + int width = Integer.parseInt(getTitleOnUiThread()); + if (value) { + assertEquals(displayWidth, width); + } else { + assertEquals(3000, width); + } + } + + private String getData() { + return "" + + "" + + " " + + "" + + " " + + "
test
" + + "
" + + ""; + } + } + // The test verifies that JavaScript is disabled upon WebView // creation without accessing AwSettings. If the test passes, // it means that WebView-specific web preferences configuration @@ -2482,6 +2603,39 @@ public void testUseWideViewportControlsDoubleTabToZoom() throws Throwable { zoomedOutScale < initialScale); } + @SmallTest + @Feature({"AndroidWebView", "Preferences"}) + public void testForceZeroLayoutHeightWithTwoViews() throws Throwable { + ViewPair views = createViews(); + runPerViewSettingsTest( + new AwSettingsForceZeroLayoutHeightTestHelper( + views.getContainer0(), views.getClient0(), false), + new AwSettingsForceZeroLayoutHeightTestHelper( + views.getContainer1(), views.getClient1(), false)); + } + + @SmallTest + @Feature({"AndroidWebView", "Preferences"}) + public void testForceZeroLayoutHeightViewportTagWithTwoViews() throws Throwable { + ViewPair views = createViews(); + runPerViewSettingsTest( + new AwSettingsForceZeroLayoutHeightTestHelper( + views.getContainer0(), views.getClient0(), true), + new AwSettingsForceZeroLayoutHeightTestHelper( + views.getContainer1(), views.getClient1(), true)); + } + + @SmallTest + @Feature({"AndroidWebView", "Preferences"}) + public void testZeroLayoutHeightDisablesViewportQuirkWithTwoViews() throws Throwable { + ViewPair views = createViews(); + runPerViewSettingsTest( + new AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper( + views.getContainer0(), views.getClient0()), + new AwSettingsZeroLayoutHeightDisablesViewportQuirkTestHelper( + views.getContainer1(), views.getClient1())); + } + @SmallTest @Feature({"AndroidWebView", "Preferences"}) public void testLoadWithOverviewModeWithTwoViews() throws Throwable { diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java new file mode 100644 index 0000000000000..fc4cc68b62a1e --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwWebContentsObserverTest.java @@ -0,0 +1,151 @@ +// 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.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.android_webview.AwContentsStatics; +import org.chromium.android_webview.AwWebContentsObserver; +import org.chromium.base.test.util.Feature; +import org.chromium.net.NetError; + +/** + * Tests for the AwWebContentsObserver class. + */ +public class AwWebContentsObserverTest extends AwTestBase { + private TestAwContentsClient mContentsClient; + private AwTestContainerView mTestContainerView; + private AwWebContentsObserver mWebContentsObserver; + + private static final String EXAMPLE_URL = "http://www.example.com/"; + private static final String ERROR_DESCRIPTION = "description"; + private String mUnreachableWebDataUrl; + + @Override + public void setUp() throws Exception { + super.setUp(); + mContentsClient = new TestAwContentsClient(); + mTestContainerView = createAwTestContainerViewOnMainSync(mContentsClient); + mUnreachableWebDataUrl = AwContentsStatics.getUnreachableWebDataUrl(); + // AwWebContentsObserver constructor must be run on the UI thread. + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + mWebContentsObserver = new AwWebContentsObserver( + mTestContainerView.getContentViewCore().getWebContents(), mContentsClient); + } + }); + } + + @SmallTest + @Feature({"AndroidWebView"}) + public void testOnPageFinished() { + int frameId = 0; + boolean mainFrame = true; + boolean subFrame = false; + + int callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didFinishLoad(frameId, EXAMPLE_URL, subFrame); + assertEquals("onPageFinished should only be called for the main frame.", callCount, + mContentsClient.getOnPageFinishedHelper().getCallCount()); + + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didFinishLoad(frameId, mUnreachableWebDataUrl, mainFrame); + assertEquals("onPageFinished should not be called for the error url.", callCount, + mContentsClient.getOnPageFinishedHelper().getCallCount()); + + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didFinishLoad(frameId, EXAMPLE_URL, mainFrame); + assertEquals("onPageFinished should be called for main frame navigations.", callCount + 1, + mContentsClient.getOnPageFinishedHelper().getCallCount()); + + boolean provisionalLoad = true; + + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didFailLoad(!provisionalLoad, mainFrame, + NetError.ERR_ABORTED, ERROR_DESCRIPTION, EXAMPLE_URL); + assertEquals("onPageFinished should be called for main frame errors.", callCount + 1, + mContentsClient.getOnPageFinishedHelper().getCallCount()); + + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didFailLoad(!provisionalLoad, subFrame, + NetError.ERR_ABORTED, ERROR_DESCRIPTION, EXAMPLE_URL); + assertEquals("onPageFinished should only be called for main frame errors.", callCount, + mContentsClient.getOnPageFinishedHelper().getCallCount()); + + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didFailLoad(!provisionalLoad, mainFrame, + NetError.ERR_ABORTED, ERROR_DESCRIPTION, mUnreachableWebDataUrl); + assertEquals("onPageFinished should not be called on unrechable url errors.", callCount, + mContentsClient.getOnPageFinishedHelper().getCallCount()); + + String baseUrl = null; + boolean navigationToDifferentPage = true; + boolean fragmentNavigation = true; + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didNavigateMainFrame(EXAMPLE_URL, baseUrl, + !navigationToDifferentPage, fragmentNavigation); + assertEquals("onPageFinished should be called for main frame fragment navigations.", + callCount + 1, mContentsClient.getOnPageFinishedHelper().getCallCount()); + + callCount = mContentsClient.getOnPageFinishedHelper().getCallCount(); + mWebContentsObserver.didNavigateMainFrame(EXAMPLE_URL, baseUrl, + !navigationToDifferentPage, !fragmentNavigation); + assertEquals("onPageFinished should be called only for main frame fragment navigations.", + callCount, mContentsClient.getOnPageFinishedHelper().getCallCount()); + } + + @SmallTest + @Feature({"AndroidWebView"}) + public void testOnReceivedError() { + boolean provisionalLoad = true; + boolean mainFrame = true; + boolean subFrame = false; + + int callCount = mContentsClient.getOnReceivedErrorHelper().getCallCount(); + mWebContentsObserver.didFailLoad(!provisionalLoad, subFrame, + NetError.ERR_TIMED_OUT, ERROR_DESCRIPTION, EXAMPLE_URL); + assertEquals("onReceivedError should only be called for the main frame", callCount, + mContentsClient.getOnReceivedErrorHelper().getCallCount()); + + callCount = mContentsClient.getOnReceivedErrorHelper().getCallCount(); + mWebContentsObserver.didFailLoad(!provisionalLoad, mainFrame, + NetError.ERR_TIMED_OUT, ERROR_DESCRIPTION, EXAMPLE_URL); + assertEquals("onReceivedError should be called for the main frame", callCount + 1, + mContentsClient.getOnReceivedErrorHelper().getCallCount()); + + callCount = mContentsClient.getOnReceivedErrorHelper().getCallCount(); + mWebContentsObserver.didFailLoad(!provisionalLoad, mainFrame, + NetError.ERR_ABORTED, ERROR_DESCRIPTION, EXAMPLE_URL); + assertEquals("onReceivedError should not be called for aborted navigations", callCount, + mContentsClient.getOnReceivedErrorHelper().getCallCount()); + } + + @SmallTest + @Feature({"AndroidWebView"}) + public void testDidNavigateMainFrame() { + String nullUrl = null; + String baseUrl = null; + boolean reload = true; + + int callCount = mContentsClient.getDoUpdateVisitedHistoryHelper().getCallCount(); + mWebContentsObserver.didNavigateAnyFrame(nullUrl, baseUrl, !reload); + assertEquals("doUpdateVisitedHistory should only be called for any url.", callCount + 1, + mContentsClient.getDoUpdateVisitedHistoryHelper().getCallCount()); + assertEquals(!reload, mContentsClient.getDoUpdateVisitedHistoryHelper().getIsReload()); + + callCount = mContentsClient.getDoUpdateVisitedHistoryHelper().getCallCount(); + mWebContentsObserver.didNavigateAnyFrame(EXAMPLE_URL, baseUrl, !reload); + assertEquals("doUpdateVisitedHistory should only be called for any url.", callCount + 1, + mContentsClient.getDoUpdateVisitedHistoryHelper().getCallCount()); + assertEquals(!reload, mContentsClient.getDoUpdateVisitedHistoryHelper().getIsReload()); + + callCount = mContentsClient.getDoUpdateVisitedHistoryHelper().getCallCount(); + mWebContentsObserver.didNavigateAnyFrame(EXAMPLE_URL, baseUrl, reload); + assertEquals("doUpdateVisitedHistory should be called for reloads.", callCount + 1, + mContentsClient.getDoUpdateVisitedHistoryHelper().getCallCount()); + assertEquals(reload, mContentsClient.getDoUpdateVisitedHistoryHelper().getIsReload()); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/AwZoomTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/AwZoomTest.java index b561a137a3ccf..0dbd4976dea4c 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/AwZoomTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/AwZoomTest.java @@ -24,7 +24,7 @@ public class AwZoomTest extends AwTestBase { private TestAwContentsClient mContentsClient; private AwContents mAwContents; - private float MAXIMUM_SCALE = 2.0f; + private static final float MAXIMUM_SCALE = 2.0f; @Override public void setUp() throws Exception { diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java index 15c0bcd78eb77..26a0fda611470 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageFinishedTest.java @@ -52,36 +52,36 @@ public void testOnPageFinishedPassesCorrectUrl() throws Throwable { @Feature({"AndroidWebView"}) public void testOnPageFinishedCalledAfterError() throws Throwable { class LocalTestClient extends TestAwContentsClient { - private boolean isOnReceivedErrorCalled = false; - private boolean isOnPageFinishedCalled = false; - private boolean allowAboutBlank = false; + private boolean mIsOnReceivedErrorCalled = false; + private boolean mIsOnPageFinishedCalled = false; + private boolean mAllowAboutBlank = false; @Override public void onReceivedError(int errorCode, String description, String failingUrl) { assertEquals("onReceivedError called twice for " + failingUrl, - false, isOnReceivedErrorCalled); - isOnReceivedErrorCalled = true; + false, mIsOnReceivedErrorCalled); + mIsOnReceivedErrorCalled = true; assertEquals("onPageFinished called before onReceivedError for " + failingUrl, - false, isOnPageFinishedCalled); + false, mIsOnPageFinishedCalled); super.onReceivedError(errorCode, description, failingUrl); } @Override public void onPageFinished(String url) { - if (allowAboutBlank && "about:blank".equals(url)) { + if (mAllowAboutBlank && "about:blank".equals(url)) { super.onPageFinished(url); return; } assertEquals("onPageFinished called twice for " + url, - false, isOnPageFinishedCalled); - isOnPageFinishedCalled = true; + false, mIsOnPageFinishedCalled); + mIsOnPageFinishedCalled = true; assertEquals("onReceivedError not called before onPageFinished for " + url, - true, isOnReceivedErrorCalled); + true, mIsOnReceivedErrorCalled); super.onPageFinished(url); } void setAllowAboutBlank() { - allowAboutBlank = true; + mAllowAboutBlank = true; } }; LocalTestClient testContentsClient = new LocalTestClient(); diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java index cc3f133ff39e7..8f440117428bf 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/ClientOnPageStartedTest.java @@ -49,38 +49,39 @@ public void testOnPageStartedPassesCorrectUrl() throws Throwable { @Feature({"AndroidWebView"}) public void testOnPageStartedCalledOnceOnError() throws Throwable { class LocalTestClient extends TestAwContentsClient { - private boolean isOnReceivedErrorCalled = false; - private boolean isOnPageStartedCalled = false; - private boolean allowAboutBlank = false; + private boolean mIsOnReceivedErrorCalled = false; + private boolean mIsOnPageStartedCalled = false; + private boolean mAllowAboutBlank = false; @Override public void onReceivedError(int errorCode, String description, String failingUrl) { assertEquals("onReceivedError called twice for " + failingUrl, - false, isOnReceivedErrorCalled); - isOnReceivedErrorCalled = true; + false, mIsOnReceivedErrorCalled); + mIsOnReceivedErrorCalled = true; assertEquals("onPageStarted not called before onReceivedError for " + failingUrl, - true, isOnPageStartedCalled); + true, mIsOnPageStartedCalled); super.onReceivedError(errorCode, description, failingUrl); } @Override public void onPageStarted(String url) { - if (allowAboutBlank && "about:blank".equals(url)) { + if (mAllowAboutBlank && "about:blank".equals(url)) { super.onPageStarted(url); return; } assertEquals("onPageStarted called twice for " + url, - false, isOnPageStartedCalled); - isOnPageStartedCalled = true; + false, mIsOnPageStartedCalled); + mIsOnPageStartedCalled = true; assertEquals("onReceivedError called before onPageStarted for " + url, - false, isOnReceivedErrorCalled); + false, mIsOnReceivedErrorCalled); super.onPageStarted(url); } void setAllowAboutBlank() { - allowAboutBlank = true; + mAllowAboutBlank = true; } - }; + } + LocalTestClient testContentsClient = new LocalTestClient(); setTestAwContentsClient(testContentsClient); diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CommandLineTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CommandLineTest.java new file mode 100644 index 0000000000000..4582f643da4d8 --- /dev/null +++ b/android_webview/javatests/src/org/chromium/android_webview/test/CommandLineTest.java @@ -0,0 +1,52 @@ +// 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.android_webview.test; + +import android.test.suitebuilder.annotation.SmallTest; + +import org.chromium.android_webview.AwBrowserProcess; +import org.chromium.base.CommandLine; +import org.chromium.base.test.util.Feature; + +/** + * Test suite for setting by the command line. + */ +public class CommandLineTest extends AwTestBase { + @Override + protected boolean needsBrowserProcessStarted() { + return false; + } + + @SmallTest + @Feature({"AndroidWebView"}) + public void testSetupCommandLine() throws Exception { + // The commandline starts off in Java: + CommandLine cl = CommandLine.getInstance(); + assertFalse(cl.isNativeImplementation()); + + // We can add a switch. + assertFalse(cl.hasSwitch("magic-switch")); + cl.appendSwitchWithValue("magic-switch", "magic"); + assertTrue(cl.hasSwitch("magic-switch")); + assertEquals("magic", cl.getSwitchValue("magic-switch")); + + // Setup Chrome. + AwBrowserProcess.loadLibrary(); + + // Now we should have switched to a native backed command line: + cl = CommandLine.getInstance(); + assertTrue(cl.isNativeImplementation()); + + // Our first switch is still there. + assertTrue(cl.hasSwitch("magic-switch")); + assertEquals("magic", cl.getSwitchValue("magic-switch")); + + // And we can add another one. + assertFalse(cl.hasSwitch("more-magic-switch")); + cl.appendSwitchWithValue("more-magic-switch", "more-magic"); + assertTrue(cl.hasSwitch("more-magic-switch")); + assertEquals("more-magic", cl.getSwitchValue("more-magic-switch")); + } +} diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java b/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java index 37edab55ad849..c1a82b8f554d7 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/FullScreenVideoTestAwContentsClient.java @@ -65,6 +65,10 @@ public View getCustomView() { return mCustomView; } + public boolean wasCustomViewShownCalled() { + return mOnShowCustomViewCallbackHelper.getCallCount() > 0; + } + public void waitForCustomViewShown() throws TimeoutException, InterruptedException { mOnShowCustomViewCallbackHelper.waitForCallback(0, 1, WAITING_SECONDS, TimeUnit.SECONDS); } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java index 77fb3704fd3c4..4a6bab55c7560 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/LoadUrlTest.java @@ -4,12 +4,9 @@ package org.chromium.android_webview.test; -import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.SmallTest; import android.util.Pair; -import junit.framework.Assert; - import org.apache.http.Header; import org.apache.http.HttpRequest; import org.chromium.android_webview.AwContents; @@ -334,71 +331,4 @@ public void testRendererNavigationAndGoBackWithExtraHeaders() throws Throwable { if (webServer != null) webServer.shutdown(); } } - - private static class TestController { - private final Object mLock = new Object(); - private boolean mIsReady = false; - public void notifyPageIsReady() { - synchronized (mLock) { - mIsReady = true; - mLock.notify(); - } - } - public void waitUntilIsReady() { - synchronized (mLock) { - while (!mIsReady) { - try { - mLock.wait(WAIT_TIMEOUT_MS); - } catch (Exception e) { - continue; - } - if (!mIsReady) { - Assert.fail("Wait timed out"); - } - } - mIsReady = false; - } - } - } - - // Verify that it is possible to interrupt JS scripts stuck in an infinite loop - // by calling loadUrl on a WebView. - @LargeTest - @Feature({"AndroidWebView"}) - public void testLoadUrlInterruptsLoopedScripts() throws Throwable { - final String infiniteLoopPage = - "" + - " " + - "" + - ""; - final String simplePage = ""; - final String expectedTitle = "PASS"; - final String pageWithTitle = ""; - - final AwTestContainerView testContainerView = - createAwTestContainerViewOnMainSync(new TestAwContentsClient()); - final AwContents awContents = testContainerView.getAwContents(); - getAwSettingsOnUiThread(awContents).setJavaScriptEnabled(true); - final TestController testController = new TestController(); - getInstrumentation().runOnMainSync(new Runnable() { - @Override - public void run() { - awContents.addPossiblyUnsafeJavascriptInterface(testController, "test", null); - } - }); - loadDataAsync(awContents, infiniteLoopPage, "text/html", false); - testController.waitUntilIsReady(); - loadDataAsync(awContents, simplePage, "text/html", false); - testController.waitUntilIsReady(); - // Load another page that runs JS to make sure that the WebView is still functional. - loadDataAsync(awContents, pageWithTitle, "text/html", false); - testController.waitUntilIsReady(); - assertEquals(expectedTitle, getTitleOnUiThread(awContents)); - } } diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/MediaAccessPermissionRequestTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/MediaAccessPermissionRequestTest.java index 5fc6770c069c6..05c528d599b6f 100644 --- a/android_webview/javatests/src/org/chromium/android_webview/test/MediaAccessPermissionRequestTest.java +++ b/android_webview/javatests/src/org/chromium/android_webview/test/MediaAccessPermissionRequestTest.java @@ -32,7 +32,7 @@ public boolean canceled() { } } - private static final String mData = " diff --git a/android_webview/tools/WebViewTelemetryShell/Android.mk b/android_webview/tools/WebViewTelemetryShell/Android.mk new file mode 100644 index 0000000000000..278710bb61678 --- /dev/null +++ b/android_webview/tools/WebViewTelemetryShell/Android.mk @@ -0,0 +1,14 @@ +# 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. + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := UpstreamWebViewShell + +LOCAL_SDK_VERSION := 17 + +include $(BUILD_PACKAGE) diff --git a/android_webview/tools/WebViewTelemetryShell/AndroidManifest.xml b/android_webview/tools/WebViewTelemetryShell/AndroidManifest.xml new file mode 100644 index 0000000000000..dbdda35c7c637 --- /dev/null +++ b/android_webview/tools/WebViewTelemetryShell/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android_webview/tools/WebViewTelemetryShell/res/drawable/ic_launcher.png b/android_webview/tools/WebViewTelemetryShell/res/drawable/ic_launcher.png new file mode 100644 index 0000000000000..96a442e5b8e93 Binary files /dev/null and b/android_webview/tools/WebViewTelemetryShell/res/drawable/ic_launcher.png differ diff --git a/android_webview/tools/WebViewTelemetryShell/res/layout/activity_telemetry.xml b/android_webview/tools/WebViewTelemetryShell/res/layout/activity_telemetry.xml new file mode 100644 index 0000000000000..0fd1ab5ec8d93 --- /dev/null +++ b/android_webview/tools/WebViewTelemetryShell/res/layout/activity_telemetry.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/android_webview/tools/WebViewTelemetryShell/res/values/strings.xml b/android_webview/tools/WebViewTelemetryShell/res/values/strings.xml new file mode 100644 index 0000000000000..420ab8f6719dc --- /dev/null +++ b/android_webview/tools/WebViewTelemetryShell/res/values/strings.xml @@ -0,0 +1,11 @@ + + + + + WebView Telemetry + WebView Telemetry + WebView Jank Tester + diff --git a/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/JankActivity.java b/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/JankActivity.java new file mode 100644 index 0000000000000..bb836672787a9 --- /dev/null +++ b/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/JankActivity.java @@ -0,0 +1,46 @@ +// 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.telemetry_shell; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.webkit.CookieManager; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +/** + * This activity is designed for Android Jank testing of WebView. + * It takes a URL as an argument, and displays the page ready for the Jank tester to test + * scrolling etc. + */ +public class JankActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setTitle( + getResources().getString(R.string.title_activity_jank)); + setContentView(R.layout.activity_telemetry); + + WebView webView = (WebView) findViewById(R.id.webview); + CookieManager.setAcceptFileSchemeCookies(true); + + webView.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView webView, String url) { + return false; + } + }); + + String url = getUrlFromIntent(getIntent()); + webView.loadUrl(url); + } + + private static String getUrlFromIntent(Intent intent) { + return intent != null ? intent.getDataString() : null; + } + +} diff --git a/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/TelemetryActivity.java b/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/TelemetryActivity.java new file mode 100644 index 0000000000000..005a27e8e2b8f --- /dev/null +++ b/android_webview/tools/WebViewTelemetryShell/src/org/chromium/telemetry_shell/TelemetryActivity.java @@ -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. + +package org.chromium.telemetry_shell; + +import android.app.Activity; +import android.os.Bundle; +import android.webkit.CookieManager; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +/** + * This activity is designed for Telemetry testing of WebView. + */ +public class TelemetryActivity extends Activity { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getWindow().setTitle( + getResources().getString(R.string.title_activity_telemetry)); + setContentView(R.layout.activity_telemetry); + + WebView webView = (WebView) findViewById(R.id.webview); + CookieManager.setAcceptFileSchemeCookies(true); + webView.getSettings().setJavaScriptEnabled(true); + + webView.setWebViewClient(new WebViewClient() { + @Override + public boolean shouldOverrideUrlLoading(WebView webView, String url) { + return false; + } + }); + + webView.loadUrl("about:blank"); + } +} diff --git a/android_webview/unittestjava/src/org/chromium/android_webview/unittest/InputStreamUnittest.java b/android_webview/unittestjava/src/org/chromium/android_webview/unittest/InputStreamUnittest.java index f6bbaa1bcfaea..42f6346549b80 100644 --- a/android_webview/unittestjava/src/org/chromium/android_webview/unittest/InputStreamUnittest.java +++ b/android_webview/unittestjava/src/org/chromium/android_webview/unittest/InputStreamUnittest.java @@ -51,12 +51,12 @@ public int read() throws IOException { @CalledByNative static InputStream getCountingStream(final int size) { return new InputStream() { - private int count = 0; + private int mCount = 0; @Override public int read() { - if (count < size) - return count++ % 256; + if (mCount < size) + return mCount++ % 256; else return -1; } diff --git a/apps/DEPS b/apps/DEPS index cfc372425743a..97f9cb0a67fb8 100644 --- a/apps/DEPS +++ b/apps/DEPS @@ -6,6 +6,7 @@ include_rules = [ "+content/public/test", "+components/keyed_service", "+components/pref_registry/pref_registry_syncable.h", + "+components/user_manager", "+components/web_modal", "+extensions", "+net/base", diff --git a/apps/app_shim/chrome_main_app_mode_mac.mm b/apps/app_shim/chrome_main_app_mode_mac.mm index 18b8f9e1cfdbc..8b91322a957c3 100644 --- a/apps/app_shim/chrome_main_app_mode_mac.mm +++ b/apps/app_shim/chrome_main_app_mode_mac.mm @@ -582,7 +582,8 @@ int ChromeAppModeStart(const app_mode::ChromeAppModeInfo* info) { l10n_util::GetApplicationLocale(preferred_localization)); // Load localized strings. - ResourceBundle::InitSharedInstanceLocaleOnly(locale, NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + locale, NULL, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); // Launch the IO thread. base::Thread::Options io_thread_options; diff --git a/apps/app_window.cc b/apps/app_window.cc index e13e1b01f777e..eb4f81a2fd7e4 100644 --- a/apps/app_window.cc +++ b/apps/app_window.cc @@ -160,13 +160,14 @@ AppWindow::CreateParams::CreateParams() has_frame_color(false), active_frame_color(SK_ColorBLACK), inactive_frame_color(SK_ColorBLACK), - transparent_background(false), + alpha_enabled(false), creator_process_id(0), state(ui::SHOW_STATE_DEFAULT), hidden(false), resizable(true), focused(true), - always_on_top(false) {} + always_on_top(false) { +} AppWindow::CreateParams::~CreateParams() {} @@ -243,7 +244,7 @@ AppWindow::AppWindow(BrowserContext* context, can_send_events_(false), is_hidden_(false), cached_always_on_top_(false), - requested_transparent_background_(false) { + requested_alpha_enabled_(false) { extensions::ExtensionsBrowserClient* client = extensions::ExtensionsBrowserClient::Get(); CHECK(!client->IsGuestSession(context) || context->IsOffTheRecord()) @@ -283,7 +284,7 @@ void AppWindow::Init(const GURL& url, if (new_params.state == ui::SHOW_STATE_FULLSCREEN) new_params.always_on_top = false; - requested_transparent_background_ = new_params.transparent_background; + requested_alpha_enabled_ = new_params.alpha_enabled; AppsClient* apps_client = AppsClient::Get(); native_app_window_.reset( @@ -731,9 +732,9 @@ void AppWindow::GetSerializedState(base::DictionaryValue* properties) const { properties->SetBoolean("maximized", native_app_window_->IsMaximized()); properties->SetBoolean("alwaysOnTop", IsAlwaysOnTop()); properties->SetBoolean("hasFrameColor", native_app_window_->HasFrameColor()); - properties->SetBoolean("alphaEnabled", - requested_transparent_background_ && - native_app_window_->CanHaveAlphaEnabled()); + properties->SetBoolean( + "alphaEnabled", + requested_alpha_enabled_ && native_app_window_->CanHaveAlphaEnabled()); // These properties are undocumented and are to enable testing. Alpha is // removed to @@ -923,7 +924,7 @@ void AppWindow::MoveContents(WebContents* source, const gfx::Rect& pos) { } void AppWindow::NavigationStateChanged(const content::WebContents* source, - unsigned changed_flags) { + content::InvalidateTypes changed_flags) { if (changed_flags & content::INVALIDATE_TYPE_TITLE) native_app_window_->UpdateWindowTitle(); else if (changed_flags & content::INVALIDATE_TYPE_TAB) diff --git a/apps/app_window.h b/apps/app_window.h index ba3b44861e478..7e35b81bd2945 100644 --- a/apps/app_window.h +++ b/apps/app_window.h @@ -152,7 +152,7 @@ class AppWindow : public content::NotificationObserver, bool has_frame_color; SkColor active_frame_color; SkColor inactive_frame_color; - bool transparent_background; // Only supported on ash. + bool alpha_enabled; // The initial content/inner bounds specification (excluding any window // decorations). @@ -344,10 +344,8 @@ class AppWindow : public content::NotificationObserver, // app. void WindowEventsReady(); - // Whether the app window wants a transparent background. - bool requested_transparent_background() const { - return requested_transparent_background_; - } + // Whether the app window wants to be alpha enabled. + bool requested_alpha_enabled() const { return requested_alpha_enabled_; } protected: virtual ~AppWindow(); @@ -370,8 +368,9 @@ class AppWindow : public content::NotificationObserver, const OVERRIDE; virtual void MoveContents(content::WebContents* source, const gfx::Rect& pos) OVERRIDE; - virtual void NavigationStateChanged(const content::WebContents* source, - unsigned changed_flags) OVERRIDE; + virtual void NavigationStateChanged( + const content::WebContents* source, + content::InvalidateTypes changed_flags) OVERRIDE; virtual void ToggleFullscreenModeForTab(content::WebContents* source, bool enter_fullscreen) OVERRIDE; virtual bool IsFullscreenForTabOrPending(const content::WebContents* source) @@ -546,8 +545,8 @@ class AppWindow : public content::NotificationObserver, // taskbar. bool cached_always_on_top_; - // Whether |transparent_background| was set in the CreateParams. - bool requested_transparent_background_; + // Whether |alpha_enabled| was set in the CreateParams. + bool requested_alpha_enabled_; DISALLOW_COPY_AND_ASSIGN(AppWindow); }; diff --git a/apps/app_window_geometry_cache.cc b/apps/app_window_geometry_cache.cc index ce6290ececc22..667595f9ac2e7 100644 --- a/apps/app_window_geometry_cache.cc +++ b/apps/app_window_geometry_cache.cc @@ -7,8 +7,6 @@ #include "base/bind.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" -#include "chrome/browser/profiles/incognito_helpers.h" -#include "chrome/browser/profiles/profile.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs_factory.h" @@ -27,12 +25,12 @@ const int kSyncTimeoutMilliseconds = 1000; namespace apps { AppWindowGeometryCache::AppWindowGeometryCache( - Profile* profile, + content::BrowserContext* context, extensions::ExtensionPrefs* prefs) : prefs_(prefs), sync_delay_(base::TimeDelta::FromMilliseconds(kSyncTimeoutMilliseconds)), extension_registry_observer_(this) { - extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(profile)); + extension_registry_observer_.Add(extensions::ExtensionRegistry::Get(context)); } AppWindowGeometryCache::~AppWindowGeometryCache() {} @@ -284,9 +282,8 @@ AppWindowGeometryCache::Factory::~Factory() {} KeyedService* AppWindowGeometryCache::Factory::BuildServiceInstanceFor( content::BrowserContext* context) const { - Profile* profile = Profile::FromBrowserContext(context); - return new AppWindowGeometryCache(profile, - extensions::ExtensionPrefs::Get(profile)); + return new AppWindowGeometryCache(context, + extensions::ExtensionPrefs::Get(context)); } bool AppWindowGeometryCache::Factory::ServiceIsNULLWhileTesting() const { diff --git a/apps/app_window_geometry_cache.h b/apps/app_window_geometry_cache.h index 8813eea2b0815..30f3ad06c4d21 100644 --- a/apps/app_window_geometry_cache.h +++ b/apps/app_window_geometry_cache.h @@ -22,8 +22,6 @@ #include "ui/base/ui_base_types.h" #include "ui/gfx/rect.h" -class Profile; - namespace extensions { class ExtensionPrefs; class ExtensionRegistry; @@ -69,7 +67,8 @@ class AppWindowGeometryCache : public KeyedService, virtual ~Observer() {} }; - AppWindowGeometryCache(Profile* profile, extensions::ExtensionPrefs* prefs); + AppWindowGeometryCache(content::BrowserContext* context, + extensions::ExtensionPrefs* prefs); virtual ~AppWindowGeometryCache(); diff --git a/apps/app_window_registry.cc b/apps/app_window_registry.cc index c6482470e3cba..67c836ab35a07 100644 --- a/apps/app_window_registry.cc +++ b/apps/app_window_registry.cc @@ -280,13 +280,13 @@ void AppWindowRegistry::CloseAllAppWindows() { void AppWindowRegistry::OnDevToolsStateChanged( content::DevToolsAgentHost* agent_host, bool attached) { - content::RenderViewHost* rvh = agent_host->GetRenderViewHost(); + content::WebContents* web_contents = agent_host->GetWebContents(); // Ignore unrelated notifications. - if (!rvh || - rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() != context_) + if (!web_contents || web_contents->GetBrowserContext() != context_) return; - std::string key = GetWindowKeyForRenderViewHost(this, rvh); + std::string key = + GetWindowKeyForRenderViewHost(this, web_contents->GetRenderViewHost()); if (key.empty()) return; diff --git a/apps/apps.gypi b/apps/apps.gypi index 63dbc48d8884c..357dcc5f1af5c 100644 --- a/apps/apps.gypi +++ b/apps/apps.gypi @@ -93,6 +93,9 @@ ], ['enable_extensions==0', { + 'dependencies!': [ + 'browser_extensions', + ], 'sources/': [ ['exclude', '.*'], ['include', 'ui/web_contents_sizer\.cc$'], diff --git a/apps/custom_launcher_page_contents.cc b/apps/custom_launcher_page_contents.cc index 2af3d3a96359f..1c5eeae74146c 100644 --- a/apps/custom_launcher_page_contents.cc +++ b/apps/custom_launcher_page_contents.cc @@ -14,6 +14,7 @@ #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" #include "content/public/common/renderer_preferences.h" +#include "extensions/browser/view_type_utils.h" #include "extensions/common/extension_messages.h" namespace apps { @@ -45,6 +46,8 @@ void CustomLauncherPageContents::Initialize(content::BrowserContext* context, context, extension_id_, web_contents_.get(), app_delegate_.get())); web_contents_->SetDelegate(this); + extensions::SetViewType(web_contents(), extensions::VIEW_TYPE_LAUNCHER_PAGE); + // This observer will activate the extension when it is navigated to, which // allows Dispatcher to give it the proper context and makes it behave like an // extension. diff --git a/apps/launcher.cc b/apps/launcher.cc index ca463de5f1e43..aabbc6489241f 100644 --- a/apps/launcher.cc +++ b/apps/launcher.cc @@ -4,8 +4,8 @@ #include "apps/launcher.h" -#include #include +#include #include "base/command_line.h" #include "base/file_util.h" @@ -40,7 +40,7 @@ #include "url/gurl.h" #if defined(OS_CHROMEOS) -#include "chrome/browser/chromeos/login/users/user_manager.h" +#include "components/user_manager/user_manager.h" #endif namespace app_runtime = extensions::core_api::app_runtime; @@ -318,7 +318,7 @@ void LaunchPlatformAppWithCommandLine(Profile* profile, if (extensions::KioskModeInfo::IsKioskOnly(extension)) { bool in_kiosk_mode = false; #if defined(OS_CHROMEOS) - chromeos::UserManager* user_manager = chromeos::UserManager::Get(); + user_manager::UserManager* user_manager = user_manager::UserManager::Get(); in_kiosk_mode = user_manager && user_manager->IsLoggedInAsKioskApp(); #endif if (!in_kiosk_mode) { diff --git a/apps/ui/views/native_app_window_views.cc b/apps/ui/views/native_app_window_views.cc index da84d8a1822e7..b2135fca7e891 100644 --- a/apps/ui/views/native_app_window_views.cc +++ b/apps/ui/views/native_app_window_views.cc @@ -262,8 +262,7 @@ void NativeAppWindowViews::OnWidgetActivationChanged(views::Widget* widget, void NativeAppWindowViews::RenderViewCreated( content::RenderViewHost* render_view_host) { - if (app_window_->requested_transparent_background() && - CanHaveAlphaEnabled()) { + if (app_window_->requested_alpha_enabled() && CanHaveAlphaEnabled()) { content::RenderWidgetHostView* view = render_view_host->GetView(); DCHECK(view); view->SetBackgroundOpaque(false); diff --git a/ash/BUILD.gn b/ash/BUILD.gn index defed0d316f2b..b49942e8cada9 100644 --- a/ash/BUILD.gn +++ b/ash/BUILD.gn @@ -169,11 +169,16 @@ static_library("test_support") { deps = [ ":ash", "//ash/resources", + "//content/test:test_support", "//skia", "//testing/gtest", "//ui/accessibility", "//ui/app_list", + "//ui/app_list:test_support", + "//ui/events:events_base", + "//ui/gl", "//ui/views", + "//ui/views:test_support", ] if (is_win) { @@ -205,6 +210,7 @@ static_library("ash_shell_lib") { #"//chrome:packed_resources", TODO(GYP) "//content", "//content/shell:content_shell_lib", + "//net", "//skia", "//third_party/icu", "//ui/app_list", @@ -212,6 +218,7 @@ static_library("ash_shell_lib") { "//ui/base", "//ui/compositor", "//ui/events", + "//ui/events:events_base", "//ui/gfx", "//ui/gfx/geometry", "//ui/keyboard", diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc index 7fd904931a123..3d63595638e41 100644 --- a/ash/accelerators/accelerator_table.cc +++ b/ash/accelerators/accelerator_table.cc @@ -146,14 +146,6 @@ const AcceleratorData kAcceleratorData[] = { // Window management shortcuts. { true, ui::VKEY_OEM_4, ui::EF_ALT_DOWN, WINDOW_SNAP_LEFT }, { true, ui::VKEY_OEM_6, ui::EF_ALT_DOWN, WINDOW_SNAP_RIGHT }, - // The same accelerator is defined in - // c/b/ui/views/accelerator_table.cc in order for the web page to - // intercept and process this shortcut. This accelerator is used if the - // focused window isn't browser window nor web content. - // TODO(stevet/jamescook): Remove this in M33, as well as the copy - // referenced above. We want to move away from shortcuts bound to Ctrl - // because web content often uses them. - { true, ui::VKEY_M, ui::EF_CONTROL_DOWN, WINDOW_MINIMIZE }, { true, ui::VKEY_OEM_MINUS, ui::EF_ALT_DOWN, WINDOW_MINIMIZE }, { true, ui::VKEY_OEM_PLUS, ui::EF_ALT_DOWN, TOGGLE_MAXIMIZED }, { true, ui::VKEY_OEM_PLUS, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, diff --git a/ash/accessibility_delegate.h b/ash/accessibility_delegate.h index 7dc2826e35279..410ea7a259077 100644 --- a/ash/accessibility_delegate.h +++ b/ash/accessibility_delegate.h @@ -92,6 +92,11 @@ class ASH_EXPORT AccessibilityDelegate { // Gets the last accessibility alert that was triggered. virtual AccessibilityAlert GetLastAccessibilityAlert() = 0; + // Plays an earcon. Earcons are brief and distinctive sounds that indicate + // when their mapped event has occurred. The sound key enums can be found in + // chromeos/audio/chromeos_sounds.h. + virtual void PlayEarcon(int sound_key) = 0; + // Initiates play of shutdown sound and returns it's duration. virtual base::TimeDelta PlayShutdownSound() const = 0; }; diff --git a/ash/ash.gyp b/ash/ash.gyp index 7141ae56314be..6c6b8a3478d39 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -189,6 +189,7 @@ 'screen_util.cc', 'screen_util.h', 'screenshot_delegate.h', + 'session/session_state_delegate.cc', 'session/session_state_delegate.h', 'session/session_state_observer.cc', 'session/session_state_observer.h', @@ -688,6 +689,8 @@ 'test/test_activation_delegate.h', 'test/test_lock_state_controller_delegate.cc', 'test/test_lock_state_controller_delegate.h', + 'test/test_overlay_delegate.cc', + 'test/test_overlay_delegate.h', 'test/test_screenshot_delegate.cc', 'test/test_screenshot_delegate.h', 'test/test_session_state_delegate.cc', @@ -859,6 +862,7 @@ 'wm/window_util_unittest.cc', 'wm/maximize_mode/workspace_backdrop_delegate.cc', 'wm/maximize_mode/workspace_backdrop_delegate.h', + 'wm/overlay_event_filter_unittest.cc', 'wm/workspace/magnetism_matcher_unittest.cc', 'wm/workspace/multi_window_resize_controller_unittest.cc', 'wm/workspace/workspace_event_handler_test_helper.cc', diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index 11c36468e7cee..074cdb1a1611e 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -250,6 +250,9 @@ Press Ctrl+Alt+Z to disable. You can only have up to three accounts in multiple sign-in. + + The administrator for this account has disallowed multiple sign-in. + Bluetooth diff --git a/ash/ash_touch_exploration_manager_chromeos.cc b/ash/ash_touch_exploration_manager_chromeos.cc index f5e212b9525c6..e10d2e7e79585 100644 --- a/ash/ash_touch_exploration_manager_chromeos.cc +++ b/ash/ash_touch_exploration_manager_chromeos.cc @@ -37,14 +37,6 @@ void AshTouchExplorationManager::OnAccessibilityModeChanged( UpdateTouchExplorationState(); } -void AshTouchExplorationManager::PlayVolumeAdjustSound() { - if (!VolumeAdjustSoundEnabled()) - return; - if ((!audio_handler_->IsOutputMuted()) || - !(audio_handler_->GetOutputVolumePercent() == 100)) - PlaySystemSoundIfSpokenFeedback(chromeos::SOUND_VOLUME_ADJUST); -} - void AshTouchExplorationManager::SetOutputLevel(int volume) { if (volume > 0) { if (audio_handler_->IsOutputMuted()) { @@ -57,6 +49,37 @@ void AshTouchExplorationManager::SetOutputLevel(int volume) { audio_handler_->SetOutputMute(true); } +void AshTouchExplorationManager::SilenceSpokenFeedback() { + AccessibilityDelegate* delegate = + Shell::GetInstance()->accessibility_delegate(); + if (!delegate->IsSpokenFeedbackEnabled()) + return; + delegate->SilenceSpokenFeedback(); +} + +void AshTouchExplorationManager::PlayVolumeAdjustEarcon() { + if (!VolumeAdjustSoundEnabled()) + return; + if (!audio_handler_->IsOutputMuted() && + audio_handler_->GetOutputVolumePercent() != 100) + PlaySystemSoundIfSpokenFeedback(chromeos::SOUND_VOLUME_ADJUST); +} + +void AshTouchExplorationManager::PlayPassthroughEarcon() { + Shell::GetInstance()->accessibility_delegate()->PlayEarcon( + chromeos::SOUND_PASSTHROUGH); +} + +void AshTouchExplorationManager::PlayExitScreenEarcon() { + Shell::GetInstance()->accessibility_delegate()->PlayEarcon( + chromeos::SOUND_EXIT_SCREEN); +} + +void AshTouchExplorationManager::PlayEnterScreenEarcon() { + Shell::GetInstance()->accessibility_delegate()->PlayEarcon( + chromeos::SOUND_ENTER_SCREEN); +} + void AshTouchExplorationManager::UpdateTouchExplorationState() { AccessibilityDelegate* delegate = Shell::GetInstance()->accessibility_delegate(); diff --git a/ash/ash_touch_exploration_manager_chromeos.h b/ash/ash_touch_exploration_manager_chromeos.h index fc9cbc176ab3a..bce2a52991acb 100644 --- a/ash/ash_touch_exploration_manager_chromeos.h +++ b/ash/ash_touch_exploration_manager_chromeos.h @@ -33,8 +33,12 @@ class ASH_EXPORT AshTouchExplorationManager AccessibilityNotificationVisibility notify) OVERRIDE; // TouchExplorationControllerDelegate overrides: - virtual void PlayVolumeAdjustSound() OVERRIDE; virtual void SetOutputLevel(int volume) OVERRIDE; + virtual void SilenceSpokenFeedback() OVERRIDE; + virtual void PlayVolumeAdjustEarcon() OVERRIDE; + virtual void PlayPassthroughEarcon() OVERRIDE; + virtual void PlayExitScreenEarcon() OVERRIDE; + virtual void PlayEnterScreenEarcon() OVERRIDE; private: void UpdateTouchExplorationState(); diff --git a/ash/autoclick/autoclick_controller.cc b/ash/autoclick/autoclick_controller.cc index e356602874167..95ab0127d60ab 100644 --- a/ash/autoclick/autoclick_controller.cc +++ b/ash/autoclick/autoclick_controller.cc @@ -15,6 +15,7 @@ #include "ui/events/event_processor.h" #include "ui/gfx/point.h" #include "ui/gfx/vector2d.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -128,9 +129,8 @@ void AutoclickControllerImpl::OnMouseEvent(ui::MouseEvent* event) { mouse_event_flags_ = event->flags(); gfx::Point mouse_location = event->root_location(); - ash::wm::ConvertPointToScreen( - wm::GetRootWindowAt(mouse_location), - &mouse_location); + ::wm::ConvertPointToScreen(wm::GetRootWindowAt(mouse_location), + &mouse_location); // The distance between the mouse location and the anchor location // must exceed a certain threshold to initiate a new autoclick countdown. @@ -185,7 +185,7 @@ void AutoclickControllerImpl::DoAutoclick() { gfx::Point click_location(screen_location); anchor_location_ = click_location; - wm::ConvertPointFromScreen(root_window, &click_location); + ::wm::ConvertPointFromScreen(root_window, &click_location); aura::WindowTreeHost* host = root_window->GetHost(); host->ConvertPointToHost(&click_location); diff --git a/ash/default_accessibility_delegate.cc b/ash/default_accessibility_delegate.cc index ee4f27b772cbf..b9ecfa2ee419d 100644 --- a/ash/default_accessibility_delegate.cc +++ b/ash/default_accessibility_delegate.cc @@ -110,6 +110,9 @@ AccessibilityAlert DefaultAccessibilityDelegate::GetLastAccessibilityAlert() { return accessibility_alert_; } +void DefaultAccessibilityDelegate::PlayEarcon(int sound_key) { +} + base::TimeDelta DefaultAccessibilityDelegate::PlayShutdownSound() const { return base::TimeDelta(); } diff --git a/ash/default_accessibility_delegate.h b/ash/default_accessibility_delegate.h index 52f8aafd522d4..70d24e8f409e6 100644 --- a/ash/default_accessibility_delegate.h +++ b/ash/default_accessibility_delegate.h @@ -39,6 +39,7 @@ class ASH_EXPORT DefaultAccessibilityDelegate : public AccessibilityDelegate { virtual double GetSavedScreenMagnifierScale() OVERRIDE; virtual void TriggerAccessibilityAlert(AccessibilityAlert alert) OVERRIDE; virtual AccessibilityAlert GetLastAccessibilityAlert() OVERRIDE; + virtual void PlayEarcon(int sound_key) OVERRIDE; virtual base::TimeDelta PlayShutdownSound() const OVERRIDE; private: diff --git a/ash/desktop_background/desktop_background_view.cc b/ash/desktop_background/desktop_background_view.cc index ed3733b767203..1c6f6db7574f9 100644 --- a/ash/desktop_background/desktop_background_view.cc +++ b/ash/desktop_background/desktop_background_view.cc @@ -20,7 +20,6 @@ #include "base/message_loop/message_loop.h" #include "base/strings/utf_string_conversions.h" #include "ui/aura/window_event_dispatcher.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" diff --git a/ash/desktop_background/wallpaper_resizer.cc b/ash/desktop_background/wallpaper_resizer.cc index 9cf22030c76be..166d17219ec4b 100644 --- a/ash/desktop_background/wallpaper_resizer.cc +++ b/ash/desktop_background/wallpaper_resizer.cc @@ -11,7 +11,6 @@ #include "base/threading/worker_pool.h" #include "content/public/browser/browser_thread.h" #include "third_party/skia/include/core/SkImage.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image_skia_rep.h" #include "ui/gfx/skia_util.h" diff --git a/ash/display/display_change_observer_chromeos.cc b/ash/display/display_change_observer_chromeos.cc index 63cc33a7152ee..1df63bde4077d 100644 --- a/ash/display/display_change_observer_chromeos.cc +++ b/ash/display/display_change_observer_chromeos.cc @@ -191,10 +191,18 @@ void DisplayChangeObserver::OnDisplayModeChanged( continue; float device_scale_factor = 1.0f; - if (!ui::IsDisplaySizeBlackListed(state.display->physical_size())) { - device_scale_factor = - FindDeviceScaleFactor((kInchInMm * mode_info->size().width() / - state.display->physical_size().width())); + if (state.display->type() == ui::DISPLAY_CONNECTION_TYPE_INTERNAL) { + if (!ui::IsDisplaySizeBlackListed(state.display->physical_size())) { + device_scale_factor = + FindDeviceScaleFactor((kInchInMm * mode_info->size().width() / + state.display->physical_size().width())); + } + } else { + DisplayMode mode; + if (Shell::GetInstance()->display_manager()->GetSelectedModeForDisplayId( + state.display->display_id(), &mode)) { + device_scale_factor = mode.device_scale_factor; + } } gfx::Rect display_bounds(state.display->origin(), mode_info->size()); diff --git a/ash/display/display_controller.cc b/ash/display/display_controller.cc index 4fb77b8f7cf31..173b2b7f9cf76 100644 --- a/ash/display/display_controller.cc +++ b/ash/display/display_controller.cc @@ -25,6 +25,7 @@ #include "ash/shell_delegate.h" #include "ash/wm/coordinate_conversion.h" #include "base/command_line.h" +#include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "ui/aura/client/capture_client.h" @@ -266,17 +267,27 @@ void DisplayController::Shutdown() { virtual_keyboard_window_controller_.reset(); Shell::GetScreen()->RemoveObserver(this); - // Delete all root window controllers, which deletes root window - // from the last so that the primary root window gets deleted last. - for (WindowTreeHostMap::const_reverse_iterator it = - window_tree_hosts_.rbegin(); - it != window_tree_hosts_.rend(); - ++it) { - RootWindowController* controller = - GetRootWindowController(GetWindow(it->second)); - DCHECK(controller); - delete controller; + + int64 primary_id = Shell::GetScreen()->GetPrimaryDisplay().id(); + + // Delete non primary root window controllers first, then + // delete the primary root window controller. + aura::Window::Windows root_windows = DisplayController::GetAllRootWindows(); + std::vector to_delete; + RootWindowController* primary_rwc = NULL; + for (aura::Window::Windows::iterator iter = root_windows.begin(); + iter != root_windows.end(); + ++iter) { + RootWindowController* rwc = GetRootWindowController(*iter); + if (GetRootWindowSettings(*iter)->display_id == primary_id) + primary_rwc = rwc; + else + to_delete.push_back(rwc); } + CHECK(primary_rwc); + + STLDeleteElements(&to_delete); + delete primary_rwc; } void DisplayController::CreatePrimaryHost( @@ -325,7 +336,7 @@ aura::Window* DisplayController::GetPrimaryRootWindow() { } aura::Window* DisplayController::GetRootWindowForDisplayId(int64 id) { - DCHECK_EQ(1u, window_tree_hosts_.count(id)); + CHECK_EQ(1u, window_tree_hosts_.count(id)); AshWindowTreeHost* host = window_tree_hosts_[id]; CHECK(host); return GetWindow(host); diff --git a/ash/display/display_info.cc b/ash/display/display_info.cc index bffaf3e3516f4..dc37650f3c81c 100644 --- a/ash/display/display_info.cc +++ b/ash/display/display_info.cc @@ -26,6 +26,24 @@ namespace { // TODO(oshima): This feature is obsolete. Remove this after m38. bool allow_upgrade_to_high_dpi = false; +// Check the content of |spec| and fill |bounds| and |device_scale_factor|. +// Returns true when |bounds| is found. +bool GetDisplayBounds( + const std::string& spec, gfx::Rect* bounds, float* device_scale_factor) { + int width = 0; + int height = 0; + int x = 0; + int y = 0; + if (sscanf(spec.c_str(), "%dx%d*%f", + &width, &height, device_scale_factor) >= 2 || + sscanf(spec.c_str(), "%d+%d-%dx%d*%f", &x, &y, &width, &height, + device_scale_factor) >= 4) { + bounds->SetRect(x, y, width, height); + return true; + } + return false; +} + } DisplayMode::DisplayMode() @@ -126,14 +144,8 @@ DisplayInfo DisplayInfo::CreateFromSpecWithID(const std::string& spec, } } - int x = 0, y = 0, width, height; float device_scale_factor = 1.0f; - if (sscanf(main_spec.c_str(), "%dx%d*%f", - &width, &height, &device_scale_factor) >= 2 || - sscanf(main_spec.c_str(), "%d+%d-%dx%d*%f", &x, &y, &width, &height, - &device_scale_factor) >= 4) { - bounds_in_native.SetRect(x, y, width, height); - } else { + if (!GetDisplayBounds(main_spec, &bounds_in_native, &device_scale_factor)) { #if defined(OS_WIN) if (gfx::IsHighDPIEnabled()) { device_scale_factor = gfx::GetDPIScale(); @@ -150,23 +162,23 @@ DisplayInfo DisplayInfo::CreateFromSpecWithID(const std::string& spec, std::string resolution_list = parts[1]; count = Tokenize(resolution_list, "|", &parts); for (size_t i = 0; i < count; ++i) { - std::string resolution = parts[i]; - int width, height; - float refresh_rate = 0.0f; - if (sscanf(resolution.c_str(), - "%dx%d%%%f", - &width, - &height, - &refresh_rate) >= 2) { - if (width * height >= largest_area && - refresh_rate > highest_refresh_rate) { + DisplayMode mode; + gfx::Rect mode_bounds; + std::vector resolution; + Tokenize(parts[i], "%", &resolution); + if (GetDisplayBounds( + resolution[0], &mode_bounds, &mode.device_scale_factor)) { + mode.size = mode_bounds.size(); + if (resolution.size() > 1) + sscanf(resolution[1].c_str(), "%f", &mode.refresh_rate); + if (mode.size.GetArea() >= largest_area && + mode.refresh_rate > highest_refresh_rate) { // Use mode with largest area and highest refresh rate as native. - largest_area = width * height; - highest_refresh_rate = refresh_rate; + largest_area = mode.size.GetArea(); + highest_refresh_rate = mode.refresh_rate; native_mode = i; } - display_modes.push_back( - DisplayMode(gfx::Size(width, height), refresh_rate, false, false)); + display_modes.push_back(mode); } } display_modes[native_mode].native = true; diff --git a/ash/display/display_info_unittest.cc b/ash/display/display_info_unittest.cc index 507dfa748586c..61f3664028451 100644 --- a/ash/display/display_info_unittest.cc +++ b/ash/display/display_info_unittest.cc @@ -44,17 +44,27 @@ TEST_F(DisplayInfoTest, CreateFromSpec) { EXPECT_EQ(1.5f, info.configured_ui_scale()); info = DisplayInfo::CreateFromSpecWithID( - "200x200#300x200|200x200%59.9|100x100%60", 10); + "200x200#300x200|200x200%59.9|100x100%60|150x100*2|150x150*1.25%30", 10); EXPECT_EQ("0,0 200x200", info.bounds_in_native().ToString()); - EXPECT_EQ(3u, info.display_modes().size()); + EXPECT_EQ(5u, info.display_modes().size()); EXPECT_EQ("300x200", info.display_modes()[0].size.ToString()); EXPECT_EQ("200x200", info.display_modes()[1].size.ToString()); EXPECT_EQ("100x100", info.display_modes()[2].size.ToString()); + EXPECT_EQ("150x100", info.display_modes()[3].size.ToString()); + EXPECT_EQ("150x150", info.display_modes()[4].size.ToString()); EXPECT_EQ(59.9f, info.display_modes()[1].refresh_rate); EXPECT_EQ(60.0f, info.display_modes()[2].refresh_rate); + EXPECT_EQ(30.0f, info.display_modes()[4].refresh_rate); + EXPECT_EQ(1.0f, info.display_modes()[0].device_scale_factor); + EXPECT_EQ(1.0f, info.display_modes()[1].device_scale_factor); + EXPECT_EQ(1.0f, info.display_modes()[2].device_scale_factor); + EXPECT_EQ(2.0f, info.display_modes()[3].device_scale_factor); + EXPECT_EQ(1.25f, info.display_modes()[4].device_scale_factor); EXPECT_TRUE(info.display_modes()[0].native); EXPECT_FALSE(info.display_modes()[1].native); EXPECT_FALSE(info.display_modes()[2].native); + EXPECT_FALSE(info.display_modes()[3].native); + EXPECT_FALSE(info.display_modes()[4].native); } } // namespace ash diff --git a/ash/display/display_manager.cc b/ash/display/display_manager.cc index 2232b8a422d5f..f3b7a20a2dc0f 100644 --- a/ash/display/display_manager.cc +++ b/ash/display/display_manager.cc @@ -29,6 +29,7 @@ #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/display.h" #include "ui/gfx/display_observer.h" +#include "ui/gfx/font_render_params.h" #include "ui/gfx/rect.h" #include "ui/gfx/screen.h" #include "ui/gfx/size_conversions.h" @@ -183,6 +184,10 @@ DisplayManager::DisplayManager() } DisplayManager::~DisplayManager() { +#if defined(OS_CHROMEOS) + // Reset the font params. + gfx::SetFontRenderParamsDeviceScaleFactor(1.0f); +#endif } // static @@ -262,6 +267,17 @@ void DisplayManager::InitDefaultDisplay() { OnNativeDisplaysChanged(info_list); } +void DisplayManager::InitFontParams() { +#if defined(OS_CHROMEOS) + if (!HasInternalDisplay()) + return; + const DisplayInfo& display_info = + GetDisplayInfo(gfx::Display::InternalDisplayId()); + gfx::SetFontRenderParamsDeviceScaleFactor( + display_info.device_scale_factor()); +#endif // OS_CHROMEOS +} + // static void DisplayManager::UpdateDisplayBoundsForLayoutById( const DisplayLayout& layout, @@ -530,6 +546,7 @@ void DisplayManager::RegisterDisplayProperty( float ui_scale, const gfx::Insets* overscan_insets, const gfx::Size& resolution_in_pixels, + float device_scale_factor, ui::ColorCalibrationProfile color_profile) { if (display_info_.find(display_id) == display_info_.end()) display_info_[display_id] = DisplayInfo(display_id, std::string(), false); @@ -544,10 +561,12 @@ void DisplayManager::RegisterDisplayProperty( if (overscan_insets) display_info_[display_id].SetOverscanInsets(*overscan_insets); if (!resolution_in_pixels.IsEmpty()) { + DCHECK(!IsInternalDisplayId(display_id)); // Default refresh rate, until OnNativeDisplaysChanged() updates us with the // actual display info, is 60 Hz. - display_modes_[display_id] = - DisplayMode(resolution_in_pixels, 60.0f, false, false); + DisplayMode mode(resolution_in_pixels, 60.0f, false, false); + mode.device_scale_factor = device_scale_factor; + display_modes_[display_id] = mode; } } diff --git a/ash/display/display_manager.h b/ash/display/display_manager.h index bc3c7dae453e6..4be4bbd3977df 100644 --- a/ash/display/display_manager.h +++ b/ash/display/display_manager.h @@ -122,6 +122,10 @@ class ASH_EXPORT DisplayManager // Initialize default display. void InitDefaultDisplay(); + // Initializes font related params that depends on display + // configuration. + void InitFontParams(); + // True if the given |display| is currently connected. bool IsActiveDisplay(const gfx::Display& display) const; @@ -182,6 +186,7 @@ class ASH_EXPORT DisplayManager float ui_scale, const gfx::Insets* overscan_insets, const gfx::Size& resolution_in_pixels, + float device_scale_factor, ui::ColorCalibrationProfile color_profile); // Returns the display mode of |display_id| which is currently used. diff --git a/ash/display/display_manager_unittest.cc b/ash/display/display_manager_unittest.cc index 2d3fc3e185920..ff260761e6cb7 100644 --- a/ash/display/display_manager_unittest.cc +++ b/ash/display/display_manager_unittest.cc @@ -4,6 +4,7 @@ #include "ash/display/display_manager.h" +#include "ash/ash_switches.h" #include "ash/display/display_controller.h" #include "ash/display/display_layout_store.h" #include "ash/screen_util.h" @@ -11,6 +12,7 @@ #include "ash/test/ash_test_base.h" #include "ash/test/display_manager_test_api.h" #include "ash/test/mirror_window_test_api.h" +#include "base/command_line.h" #include "base/format_macros.h" #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" @@ -20,6 +22,7 @@ #include "ui/events/test/event_generator.h" #include "ui/gfx/display.h" #include "ui/gfx/display_observer.h" +#include "ui/gfx/font_render_params.h" #include "ui/gfx/screen.h" #include "ui/gfx/screen_type_delegate.h" @@ -1381,4 +1384,96 @@ TEST_F(ScreenShutdownTest, ScreenAfterShutdown) { UpdateDisplay("500x300,800x400"); } + +#if defined(OS_CHROMEOS) +namespace { + +// A helper class that sets the display configuration and starts ash. +// This is to make sure the font configuration happens during ash +// initialization process. +class FontTestHelper : public test::AshTestBase { + public: + enum DisplayType { + INTERNAL, + EXTERNAL + }; + + FontTestHelper(float scale, DisplayType display_type) { + gfx::ClearFontRenderParamsCacheForTest(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (display_type == INTERNAL) + command_line->AppendSwitch(switches::kAshUseFirstDisplayAsInternal); + command_line->AppendSwitchASCII(switches::kAshHostWindowBounds, + StringPrintf("1000x800*%f", scale)); + SetUp(); + } + + virtual ~FontTestHelper() { + TearDown(); + } + + // test::AshTestBase: + virtual void TestBody() OVERRIDE { + NOTREACHED(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(FontTestHelper); +}; + + +bool IsTextSubpixelPositioningEnabled() { + gfx::FontRenderParams params = + gfx::GetFontRenderParams(gfx::FontRenderParamsQuery(false), NULL); + return params.subpixel_positioning; +} + +} // namespace + +typedef testing::Test DisplayManagerFontTest; + +TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf100Internal) { + FontTestHelper helper(1.0f, FontTestHelper::INTERNAL); + ASSERT_DOUBLE_EQ( + 1.0f, Shell::GetScreen()->GetPrimaryDisplay().device_scale_factor()); + EXPECT_FALSE(IsTextSubpixelPositioningEnabled()); +} + +TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf125Internal) { + FontTestHelper helper(1.25f, FontTestHelper::INTERNAL); + ASSERT_DOUBLE_EQ( + 1.25f, Shell::GetScreen()->GetPrimaryDisplay().device_scale_factor()); + EXPECT_TRUE(IsTextSubpixelPositioningEnabled()); +} + +TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf200Internal) { + FontTestHelper helper(2.0f, FontTestHelper::INTERNAL); + ASSERT_DOUBLE_EQ( + 2.0f, Shell::GetScreen()->GetPrimaryDisplay().device_scale_factor()); + EXPECT_TRUE(IsTextSubpixelPositioningEnabled()); +} + +TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf100External) { + FontTestHelper helper(1.0f, FontTestHelper::EXTERNAL); + ASSERT_DOUBLE_EQ( + 1.0f, Shell::GetScreen()->GetPrimaryDisplay().device_scale_factor()); + EXPECT_FALSE(IsTextSubpixelPositioningEnabled()); +} + +TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf125External) { + FontTestHelper helper(1.25f, FontTestHelper::EXTERNAL); + ASSERT_DOUBLE_EQ( + 1.25f, Shell::GetScreen()->GetPrimaryDisplay().device_scale_factor()); + EXPECT_FALSE(IsTextSubpixelPositioningEnabled()); +} + +TEST_F(DisplayManagerFontTest, TextSubpixelPositioningWithDsf200External) { + FontTestHelper helper(2.0f, FontTestHelper::EXTERNAL); + ASSERT_DOUBLE_EQ( + 2.0f, Shell::GetScreen()->GetPrimaryDisplay().device_scale_factor()); + EXPECT_FALSE(IsTextSubpixelPositioningEnabled()); +} + +#endif // OS_CHROMEOS + } // namespace ash diff --git a/ash/display/event_transformation_handler.cc b/ash/display/event_transformation_handler.cc index 62a6fa15cf2d3..e1af21fca07e7 100644 --- a/ash/display/event_transformation_handler.cc +++ b/ash/display/event_transformation_handler.cc @@ -9,7 +9,6 @@ #include "ash/display/display_info.h" #include "ash/display/display_manager.h" #include "ash/shell.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/window_util.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" @@ -17,6 +16,7 @@ #include "ui/events/event.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { namespace { @@ -41,7 +41,7 @@ void EventTransformationHandler::OnScrollEvent(ui::ScrollEvent* event) { // the event locations etc. are already in DIP. gfx::Point point_in_screen(event->location()); aura::Window* target = static_cast(event->target()); - wm::ConvertPointToScreen(target, &point_in_screen); + ::wm::ConvertPointToScreen(target, &point_in_screen); const gfx::Display& display = Shell::GetScreen()->GetDisplayNearestPoint(point_in_screen); diff --git a/ash/display/mouse_cursor_event_filter.cc b/ash/display/mouse_cursor_event_filter.cc index 8da1918ce32e1..14a6913e925a7 100644 --- a/ash/display/mouse_cursor_event_filter.cc +++ b/ash/display/mouse_cursor_event_filter.cc @@ -14,7 +14,6 @@ #include "ash/root_window_controller.h" #include "ash/screen_util.h" #include "ash/shell.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/window_util.h" #include "ui/aura/env.h" #include "ui/aura/window.h" @@ -25,6 +24,7 @@ #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { namespace { @@ -42,7 +42,7 @@ const int kIndicatorThickness = 1; void ConvertPointFromScreenToNative(const aura::Window* root_window, gfx::Point* point) { - wm::ConvertPointFromScreen(root_window, point); + ::wm::ConvertPointFromScreen(root_window, point); root_window->GetHost()->ConvertPointToNativeScreen(point); } @@ -204,7 +204,7 @@ void MouseCursorEventFilter::OnMouseEvent(ui::MouseEvent* event) { void MouseCursorEventFilter::MoveCursorTo(aura::Window* root, const gfx::Point& point_in_screen) { gfx::Point point_in_native = point_in_screen; - wm::ConvertPointFromScreen(root, &point_in_native); + ::wm::ConvertPointFromScreen(root, &point_in_native); root->GetHost()->ConvertPointToNativeScreen(&point_in_native); // now fit the point inside the native bounds. @@ -234,7 +234,7 @@ bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(ui::MouseEvent* event) { gfx::Point point_in_screen = event->location(); aura::Window* target = static_cast(event->target()); - wm::ConvertPointToScreen(target, &point_in_screen); + ::wm::ConvertPointToScreen(target, &point_in_screen); return WarpMouseCursorInNativeCoords(point_in_native, point_in_screen); } @@ -379,7 +379,7 @@ bool MouseCursorEventFilter::WarpMouseCursorIfNecessaryForTest( aura::Window* target_root, const gfx::Point& point_in_screen) { gfx::Point native = point_in_screen; - wm::ConvertPointFromScreen(target_root, &native); + ::wm::ConvertPointFromScreen(target_root, &native); target_root->GetHost()->ConvertPointToNativeScreen(&native); return WarpMouseCursorInNativeCoords(native, point_in_screen); } diff --git a/ash/display/mouse_cursor_event_filter_ozone.cc b/ash/display/mouse_cursor_event_filter_ozone.cc index c342dbac644f2..8c0487156bc2f 100644 --- a/ash/display/mouse_cursor_event_filter_ozone.cc +++ b/ash/display/mouse_cursor_event_filter_ozone.cc @@ -7,6 +7,7 @@ #include "ash/shell.h" #include "ash/wm/coordinate_conversion.h" #include "ash/wm/window_util.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -21,7 +22,7 @@ bool MouseCursorEventFilter::WarpMouseCursorIfNecessary(ui::MouseEvent* event) { gfx::Point point_in_screen(event->location()); aura::Window* target = static_cast(event->target()); - wm::ConvertPointToScreen(target, &point_in_screen); + ::wm::ConvertPointToScreen(target, &point_in_screen); return WarpMouseCursorInScreenCoords(target->GetRootWindow(), point_in_screen); } @@ -45,7 +46,7 @@ bool MouseCursorEventFilter::WarpMouseCursorInScreenCoords( aura::Window* root_at_point = wm::GetRootWindowAt(point_in_screen); gfx::Point point_in_root = point_in_screen; - wm::ConvertPointFromScreen(root_at_point, &point_in_root); + ::wm::ConvertPointFromScreen(root_at_point, &point_in_root); gfx::Rect root_bounds = root_at_point->bounds(); int offset_x = 0; int offset_y = 0; @@ -86,7 +87,7 @@ bool MouseCursorEventFilter::WarpMouseCursorInScreenCoords( return false; } - wm::ConvertPointFromScreen(dst_root, &point_in_dst_screen); + ::wm::ConvertPointFromScreen(dst_root, &point_in_dst_screen); if (dst_root->bounds().Contains(point_in_dst_screen)) { DCHECK_NE(dst_root, root_at_point); diff --git a/ash/drag_drop/drag_drop_controller.cc b/ash/drag_drop/drag_drop_controller.cc index 2ff526fe7681f..004298c7ee878 100644 --- a/ash/drag_drop/drag_drop_controller.cc +++ b/ash/drag_drop/drag_drop_controller.cc @@ -7,7 +7,6 @@ #include "ash/drag_drop/drag_drop_tracker.h" #include "ash/drag_drop/drag_image_view.h" #include "ash/shell.h" -#include "ash/wm/coordinate_conversion.h" #include "base/bind.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" @@ -28,6 +27,7 @@ #include "ui/gfx/rect_conversions.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/native_widget_aura.h" +#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/public/drag_drop_delegate.h" namespace ash { @@ -198,7 +198,7 @@ int DragDropController::StartDragAndDrop( drag_image_vertical_offset = kTouchDragImageVerticalOffset; } gfx::Point start_location = root_location; - ash::wm::ConvertPointToScreen(root_window, &start_location); + ::wm::ConvertPointToScreen(root_window, &start_location); drag_image_final_bounds_for_cancel_animation_ = gfx::Rect( start_location - provider->GetDragImageOffset(), provider->GetDragImage().size()); @@ -292,8 +292,8 @@ void DragDropController::DragUpdate(aura::Window* target, DCHECK(drag_image_.get()); if (drag_image_->visible()) { gfx::Point root_location_in_screen = event.root_location(); - ash::wm::ConvertPointToScreen(target->GetRootWindow(), - &root_location_in_screen); + ::wm::ConvertPointToScreen(target->GetRootWindow(), + &root_location_in_screen); drag_image_->SetScreenPosition( root_location_in_screen - drag_image_offset_); drag_image_->SetTouchDragOperation(op); diff --git a/ash/drag_drop/drag_drop_interactive_uitest.cc b/ash/drag_drop/drag_drop_interactive_uitest.cc index 24920fd1b4c3d..4e99df7c64ad4 100644 --- a/ash/drag_drop/drag_drop_interactive_uitest.cc +++ b/ash/drag_drop/drag_drop_interactive_uitest.cc @@ -130,7 +130,8 @@ class DragDropTest : public test::AshTestBase { gfx::GLSurface::InitializeOneOffForTests(); ui::RegisterPathProvider(); - ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); base::FilePath resources_pack_path; PathService::Get(base::DIR_MODULE, &resources_pack_path); resources_pack_path = diff --git a/ash/drag_drop/drag_drop_tracker.cc b/ash/drag_drop/drag_drop_tracker.cc index 193dc62054a93..c1ec24441a29d 100644 --- a/ash/drag_drop/drag_drop_tracker.cc +++ b/ash/drag_drop/drag_drop_tracker.cc @@ -12,6 +12,7 @@ #include "ui/aura/window_event_dispatcher.h" #include "ui/events/event.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/public/activation_delegate.h" namespace ash { @@ -68,12 +69,11 @@ void DragDropTracker::TakeCapture() { aura::Window* DragDropTracker::GetTarget(const ui::LocatedEvent& event) { DCHECK(capture_window_.get()); gfx::Point location_in_screen = event.location(); - wm::ConvertPointToScreen(capture_window_.get(), - &location_in_screen); + ::wm::ConvertPointToScreen(capture_window_.get(), &location_in_screen); aura::Window* root_window_at_point = wm::GetRootWindowAt(location_in_screen); gfx::Point location_in_root = location_in_screen; - wm::ConvertPointFromScreen(root_window_at_point, &location_in_root); + ::wm::ConvertPointFromScreen(root_window_at_point, &location_in_root); return root_window_at_point->GetEventHandlerForPoint(location_in_root); } @@ -85,7 +85,7 @@ ui::LocatedEvent* DragDropTracker::ConvertEvent( aura::Window::ConvertPointToTarget(capture_window_.get(), target, &target_location); gfx::Point location_in_screen = event.location(); - ash::wm::ConvertPointToScreen(capture_window_.get(), &location_in_screen); + ::wm::ConvertPointToScreen(capture_window_.get(), &location_in_screen); gfx::Point target_root_location = event.root_location(); aura::Window::ConvertPointToTarget( capture_window_->GetRootWindow(), diff --git a/ash/first_run/first_run_helper_impl.cc b/ash/first_run/first_run_helper_impl.cc index 919cca91a1410..62692a5b009fc 100644 --- a/ash/first_run/first_run_helper_impl.cc +++ b/ash/first_run/first_run_helper_impl.cc @@ -40,7 +40,7 @@ FirstRunHelperImpl::FirstRunHelperImpl() } FirstRunHelperImpl::~FirstRunHelperImpl() { - Shell::GetInstance()->overlay_filter()->Deactivate(); + Shell::GetInstance()->overlay_filter()->Deactivate(this); if (IsTrayBubbleOpened()) CloseTrayBubble(); widget_->Close(); @@ -51,15 +51,11 @@ views::Widget* FirstRunHelperImpl::GetOverlayWidget() { } void FirstRunHelperImpl::OpenAppList() { - if (Shell::GetInstance()->GetAppListTargetVisibility()) - return; - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); } void FirstRunHelperImpl::CloseAppList() { - if (!Shell::GetInstance()->GetAppListTargetVisibility()) - return; - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->DismissAppList(); } gfx::Rect FirstRunHelperImpl::GetLauncherBounds() { diff --git a/ash/frame/default_header_painter.cc b/ash/frame/default_header_painter.cc index fd31d776e9191..89584d1c00bbb 100644 --- a/ash/frame/default_header_painter.cc +++ b/ash/frame/default_header_painter.cc @@ -9,12 +9,12 @@ #include "base/debug/leak_annotations.h" #include "base/logging.h" // DCHECK #include "grit/ash_resources.h" -#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkPath.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/gfx/canvas.h" +#include "ui/gfx/color_utils.h" #include "ui/gfx/font_list.h" #include "ui/gfx/image/image.h" #include "ui/gfx/rect.h" @@ -35,12 +35,15 @@ const SkColor kHeaderContentSeparatorColor = SkColorSetRGB(150, 150, 152); // Color of the inactive window header/content separator line. const SkColor kHeaderContentSeparatorInactiveColor = SkColorSetRGB(180, 180, 182); +// The color of the frame. +const SkColor kFrameColor = SkColorSetRGB(242, 242, 242); +// The alpha of the inactive frame. +const SkAlpha kInactiveFrameAlpha = 204; // Duration of crossfade animation for activating and deactivating frame. const int kActivationCrossfadeDurationMs = 200; // Tiles an image into an area, rounding the top corners. void TileRoundRect(gfx::Canvas* canvas, - const gfx::ImageSkia& image, const SkPaint& paint, const gfx::Rect& bounds, int corner_radius) { @@ -53,7 +56,7 @@ void TileRoundRect(gfx::Canvas* canvas, 0, 0}; // bottom-left SkPath path; path.addRoundRect(rect, radii, SkPath::kCW_Direction); - canvas->DrawImageInPath(image, 0, 0, path, paint); + canvas->DrawPath(path, paint); } // Returns the FontList to use for the title. @@ -166,26 +169,12 @@ void DefaultHeaderPainter::PaintHeader(gfx::Canvas* canvas, Mode mode) { int corner_radius = (frame_->IsMaximized() || frame_->IsFullscreen()) ? 0 : HeaderPainterUtil::GetTopCornerRadiusWhenRestored(); - int active_alpha = activation_animation_->CurrentValueBetween(0, 255); - int inactive_alpha = 255 - active_alpha; - SkPaint paint; - if (inactive_alpha > 0) { - if (active_alpha > 0) - paint.setXfermodeMode(SkXfermode::kPlus_Mode); - - paint.setAlpha(inactive_alpha); - gfx::ImageSkia inactive_frame = *GetInactiveFrameImage(); - TileRoundRect(canvas, inactive_frame, paint, GetLocalBounds(), - corner_radius); - } + int active_alpha = activation_animation_->CurrentValueBetween(0, 255); + paint.setColor(color_utils::AlphaBlend( + kFrameColor, GetInactiveFrameColor(), active_alpha)); - if (active_alpha > 0) { - paint.setAlpha(active_alpha); - gfx::ImageSkia active_frame = *GetActiveFrameImage(); - TileRoundRect(canvas, active_frame, paint, GetLocalBounds(), - corner_radius); - } + TileRoundRect(canvas, paint, GetLocalBounds(), corner_radius); if (!frame_->IsMaximized() && !frame_->IsFullscreen() && @@ -322,17 +311,15 @@ gfx::Rect DefaultHeaderPainter::GetTitleBounds() const { window_icon_, caption_button_container_, GetTitleFontList()); } -gfx::ImageSkia* DefaultHeaderPainter::GetActiveFrameImage() const { - return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - IDR_AURA_WINDOW_HEADER_BASE); -} - -gfx::ImageSkia* DefaultHeaderPainter::GetInactiveFrameImage() const { - int frame_image_id = (frame_->IsMaximized() || frame_->IsFullscreen()) ? - IDR_AURA_WINDOW_HEADER_BASE : - IDR_AURA_WINDOW_HEADER_BASE_RESTORED_INACTIVE; - return ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( - frame_image_id); +SkColor DefaultHeaderPainter::GetInactiveFrameColor() const { + SkColor color = kFrameColor; + if (!frame_->IsMaximized() && !frame_->IsFullscreen()) { + color = SkColorSetARGB(kInactiveFrameAlpha, + SkColorGetR(color), + SkColorGetG(color), + SkColorGetB(color)); + } + return color; } } // namespace ash diff --git a/ash/frame/default_header_painter.h b/ash/frame/default_header_painter.h index e06c31208f145..3a99f819c6186 100644 --- a/ash/frame/default_header_painter.h +++ b/ash/frame/default_header_painter.h @@ -11,6 +11,7 @@ #include "base/compiler_specific.h" // OVERRIDE #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/animation/animation_delegate.h" namespace gfx { @@ -74,11 +75,8 @@ class ASH_EXPORT DefaultHeaderPainter : public HeaderPainter, // Returns the bounds for the title. gfx::Rect GetTitleBounds() const; - // Returns the frame image to use when |frame_| is active. - gfx::ImageSkia* GetActiveFrameImage() const; - - // Returns the frame image to use when |frame_| is inactive. - gfx::ImageSkia* GetInactiveFrameImage() const; + // Returns the frame color to use when |frame_| is inactive. + SkColor GetInactiveFrameColor() const; views::Widget* frame_; views::View* view_; diff --git a/ash/keyboard_overlay/keyboard_overlay_delegate.cc b/ash/keyboard_overlay/keyboard_overlay_delegate.cc index 877fe2aea3982..391fa613387cd 100644 --- a/ash/keyboard_overlay/keyboard_overlay_delegate.cc +++ b/ash/keyboard_overlay/keyboard_overlay_delegate.cc @@ -14,7 +14,6 @@ #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_message_handler.h" #include "ui/aura/window_event_dispatcher.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/screen.h" #include "ui/views/controls/webview/web_dialog_view.h" #include "ui/views/widget/widget.h" diff --git a/ash/keyboard_overlay/keyboard_overlay_view.cc b/ash/keyboard_overlay/keyboard_overlay_view.cc index 2847653ca5763..e5a71f9a2453f 100644 --- a/ash/keyboard_overlay/keyboard_overlay_view.cc +++ b/ash/keyboard_overlay/keyboard_overlay_view.cc @@ -43,7 +43,7 @@ KeyboardOverlayView::~KeyboardOverlayView() { } void KeyboardOverlayView::Cancel() { - Shell::GetInstance()->overlay_filter()->Deactivate(); + Shell::GetInstance()->overlay_filter()->Deactivate(this); views::Widget* widget = GetWidget(); if (widget) widget->Close(); @@ -66,10 +66,14 @@ aura::Window* KeyboardOverlayView::GetWindow() { return GetWidget()->GetNativeWindow(); } +// static void KeyboardOverlayView::ShowDialog( content::BrowserContext* context, WebContentsHandler* handler, const GURL& url) { + if (Shell::GetInstance()->overlay_filter()->IsActive()) + return; + KeyboardOverlayDelegate* delegate = new KeyboardOverlayDelegate( l10n_util::GetStringUTF16(IDS_ASH_KEYBOARD_OVERLAY_TITLE), url); KeyboardOverlayView* view = diff --git a/ash/resources/ash_resources.grd b/ash/resources/ash_resources.grd index 2fa2376742f3b..61fac688900ad 100644 --- a/ash/resources/ash_resources.grd +++ b/ash/resources/ash_resources.grd @@ -198,8 +198,6 @@ - - diff --git a/ash/resources/default_100_percent/common/window_header_base_active.png b/ash/resources/default_100_percent/common/window_header_base_active.png deleted file mode 100644 index f6f3eb5274d3c..0000000000000 Binary files a/ash/resources/default_100_percent/common/window_header_base_active.png and /dev/null differ diff --git a/ash/resources/default_100_percent/common/window_header_base_inactive.png b/ash/resources/default_100_percent/common/window_header_base_inactive.png deleted file mode 100644 index ecff0804fdbaf..0000000000000 Binary files a/ash/resources/default_100_percent/common/window_header_base_inactive.png and /dev/null differ diff --git a/ash/resources/default_200_percent/common/window_header_base_active.png b/ash/resources/default_200_percent/common/window_header_base_active.png deleted file mode 100644 index d836c831fc431..0000000000000 Binary files a/ash/resources/default_200_percent/common/window_header_base_active.png and /dev/null differ diff --git a/ash/resources/default_200_percent/common/window_header_base_inactive.png b/ash/resources/default_200_percent/common/window_header_base_inactive.png deleted file mode 100644 index f4dd6041542b4..0000000000000 Binary files a/ash/resources/default_200_percent/common/window_header_base_inactive.png and /dev/null differ diff --git a/ash/session/session_state_delegate.cc b/ash/session/session_state_delegate.cc new file mode 100644 index 0000000000000..e55a5adf55e79 --- /dev/null +++ b/ash/session/session_state_delegate.cc @@ -0,0 +1,14 @@ +// 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 "ash/session/session_state_delegate.h" + +namespace ash { + +bool SessionStateDelegate::IsInSecondaryLoginScreen() const { + return GetSessionState() == + ash::SessionStateDelegate::SESSION_STATE_LOGIN_SECONDARY; +} + +} // namespace ash diff --git a/ash/session/session_state_delegate.h b/ash/session/session_state_delegate.h index 89c1ff8d4f951..c1c07137105fe 100644 --- a/ash/session/session_state_delegate.h +++ b/ash/session/session_state_delegate.h @@ -134,9 +134,14 @@ class ASH_EXPORT SessionStateDelegate { // ordering as GetLoggedInUsers. virtual void CycleActiveUser(CycleUser cycle_user) = 0; + // Returns true if primary user policy does not forbid multiple signin. + virtual bool IsMultiProfileAllowedByPrimaryUserPolicy() const = 0; + // Adds or removes sessions state observer. virtual void AddSessionStateObserver(SessionStateObserver* observer) = 0; virtual void RemoveSessionStateObserver(SessionStateObserver* observer) = 0; + + bool IsInSecondaryLoginScreen() const; }; } // namespace ash diff --git a/ash/shelf/shelf.cc b/ash/shelf/shelf.cc index 08f32e2bc6897..100fb04d8795f 100644 --- a/ash/shelf/shelf.cc +++ b/ash/shelf/shelf.cc @@ -27,7 +27,6 @@ #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_observer.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image.h" diff --git a/ash/shelf/shelf_alignment_menu.cc b/ash/shelf/shelf_alignment_menu.cc index 4fbba2ed56679..6194b3ba0f818 100644 --- a/ash/shelf/shelf_alignment_menu.cc +++ b/ash/shelf/shelf_alignment_menu.cc @@ -10,7 +10,6 @@ #include "ash/shell.h" #include "grit/ash_strings.h" #include "ui/aura/window.h" -#include "ui/base/l10n/l10n_util.h" namespace ash { diff --git a/ash/shelf/shelf_bezel_event_filter.cc b/ash/shelf/shelf_bezel_event_filter.cc index 513a94908ce38..9906602dd1030 100644 --- a/ash/shelf/shelf_bezel_event_filter.cc +++ b/ash/shelf/shelf_bezel_event_filter.cc @@ -6,8 +6,8 @@ #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" -#include "ash/wm/coordinate_conversion.h" #include "ui/aura/window.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -26,7 +26,7 @@ void ShelfBezelEventFilter::OnGestureEvent( ui::GestureEvent* event) { gfx::Point point_in_screen(event->location()); aura::Window* target = static_cast(event->target()); - wm::ConvertPointToScreen(target, &point_in_screen); + ::wm::ConvertPointToScreen(target, &point_in_screen); gfx::Rect screen = Shell::GetScreen()->GetDisplayNearestPoint(point_in_screen).bounds(); if ((!screen.Contains(point_in_screen) && diff --git a/ash/shelf/shelf_layout_manager_unittest.cc b/ash/shelf/shelf_layout_manager_unittest.cc index d0339600c6a3a..58c712adcdef7 100644 --- a/ash/shelf/shelf_layout_manager_unittest.cc +++ b/ash/shelf/shelf_layout_manager_unittest.cc @@ -1278,13 +1278,13 @@ TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfVisibleState) { EXPECT_FALSE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state()); - // Toggle app list to show, and the shelf stays visible. - shell->ToggleAppList(NULL); + // Show app list and the shelf stays visible. + shell->ShowAppList(NULL); EXPECT_TRUE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state()); - // Toggle app list to hide, and the shelf stays visible. - shell->ToggleAppList(NULL); + // Hide app list and the shelf stays visible. + shell->DismissAppList(); EXPECT_FALSE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_VISIBLE, shelf->visibility_state()); } @@ -1308,8 +1308,8 @@ TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfAutoHideState) { EXPECT_FALSE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); - // Toggle app list to show. - shell->ToggleAppList(NULL); + // Show app list. + shell->ShowAppList(NULL); // The shelf's auto hide state won't be changed until the timer fires, so // calling shell->UpdateShelfVisibility() is kind of manually helping it to // update the state. @@ -1318,8 +1318,8 @@ TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfAutoHideState) { EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state()); - // Toggle app list to hide. - shell->ToggleAppList(NULL); + // Hide app list. + shell->DismissAppList(); EXPECT_FALSE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_AUTO_HIDE, shelf->visibility_state()); } @@ -1343,13 +1343,13 @@ TEST_F(ShelfLayoutManagerTest, OpenAppListWithShelfHiddenState) { EXPECT_FALSE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); - // Toggle app list to show. - shell->ToggleAppList(NULL); + // Show app list. + shell->ShowAppList(NULL); EXPECT_TRUE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); - // Toggle app list to hide. - shell->ToggleAppList(NULL); + // Hide app list. + shell->DismissAppList(); EXPECT_FALSE(shell->GetAppListTargetVisibility()); EXPECT_EQ(SHELF_HIDDEN, shelf->visibility_state()); } diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc index cc0e1ae2836b7..5e4f00255c028 100644 --- a/ash/shelf/shelf_view.cc +++ b/ash/shelf/shelf_view.cc @@ -55,6 +55,7 @@ #include "ui/views/view_model.h" #include "ui/views/view_model_utils.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" using gfx::Animation; using views::View; @@ -617,9 +618,8 @@ bool ShelfView::StartDrag(const std::string& app_id, gfx::Point pt = drag_and_drop_view->GetBoundsInScreen().CenterPoint(); views::View::ConvertPointFromScreen(drag_and_drop_view, &pt); gfx::Point point_in_root = location_in_screen_coordinates; - ash::wm::ConvertPointFromScreen( - ash::wm::GetRootWindowAt(location_in_screen_coordinates), - &point_in_root); + ::wm::ConvertPointFromScreen( + ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root); ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root, 0, 0); PointerPressedOnButton(drag_and_drop_view, ShelfButtonHost::DRAG_AND_DROP, @@ -640,9 +640,8 @@ bool ShelfView::Drag(const gfx::Point& location_in_screen_coordinates) { model_->ItemIndexByID(drag_and_drop_shelf_id_)); ConvertPointFromScreen(drag_and_drop_view, &pt); gfx::Point point_in_root = location_in_screen_coordinates; - ash::wm::ConvertPointFromScreen( - ash::wm::GetRootWindowAt(location_in_screen_coordinates), - &point_in_root); + ::wm::ConvertPointFromScreen( + ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root); ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root, 0, 0); PointerDraggedOnButton(drag_and_drop_view, ShelfButtonHost::DRAG_AND_DROP, @@ -1010,8 +1009,8 @@ bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) { delegate_->GetAppIDForShelfID(model_->items()[current_index].id); gfx::Point screen_location = event.root_location(); - ash::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(), - &screen_location); + ::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(), + &screen_location); // To avoid ugly forwards and backwards flipping we use different constants // for ripping off / re-inserting the items. diff --git a/ash/shelf/shelf_view_unittest.cc b/ash/shelf/shelf_view_unittest.cc index 1d7a8d0f51355..c8fee6f6cd34a 100644 --- a/ash/shelf/shelf_view_unittest.cc +++ b/ash/shelf/shelf_view_unittest.cc @@ -29,7 +29,6 @@ #include "ash/test/shell_test_api.h" #include "ash/test/test_shelf_delegate.h" #include "ash/test/test_shelf_item_delegate.h" -#include "ash/wm/coordinate_conversion.h" #include "base/basictypes.h" #include "base/command_line.h" #include "base/compiler_specific.h" @@ -47,6 +46,7 @@ #include "ui/views/view_model.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { namespace test { @@ -1301,7 +1301,7 @@ TEST_F(ShelfViewTest, ShouldHideTooltipTest) { } TEST_F(ShelfViewTest, ShouldHideTooltipWithAppListWindowTest) { - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); ASSERT_TRUE(Shell::GetInstance()->GetAppListWindow()); // The tooltip shouldn't hide if the mouse is on normal buttons. @@ -1604,7 +1604,7 @@ TEST_F(ShelfViewTest, CheckRipOffFromLeftShelfAlignmentWithMultiMonitor) { // Fetch the start point of dragging. gfx::Point start_point = button->GetBoundsInScreen().CenterPoint(); - wm::ConvertPointFromScreen(second_root, &start_point); + ::wm::ConvertPointFromScreen(second_root, &start_point); ui::test::EventGenerator generator(second_root, start_point); diff --git a/ash/shell.cc b/ash/shell.cc index 2c393065aba61..fd06ea826bf3b 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -292,13 +292,28 @@ void Shell::ShowContextMenu(const gfx::Point& location_in_screen, ->ShowContextMenu(location_in_screen, source_type); } -void Shell::ToggleAppList(aura::Window* window) { +void Shell::ShowAppList(aura::Window* window) { // If the context window is not given, show it on the target root window. if (!window) window = GetTargetRootWindow(); if (!app_list_controller_) app_list_controller_.reset(new AppListController); - app_list_controller_->SetVisible(!app_list_controller_->IsVisible(), window); + app_list_controller_->SetVisible(true, window); +} + +void Shell::DismissAppList() { + if (!app_list_controller_) + return; + app_list_controller_->SetVisible(false, GetTargetRootWindow()); +} + +void Shell::ToggleAppList(aura::Window* window) { + if (app_list_controller_ && app_list_controller_->IsVisible()) { + DismissAppList(); + return; + } + + ShowAppList(window); } bool Shell::GetAppListTargetVisibility() const { @@ -837,6 +852,8 @@ void Shell::Init(const ShellInitParams& init_params) { if (!display_initialized) display_manager_->InitDefaultDisplay(); + display_manager_->InitFontParams(); + // Install the custom factory first so that views::FocusManagers for Tray, // Shelf, and WallPaper could be created by the factory. views::FocusManagerFactory::Install(new AshFocusManagerFactory); diff --git a/ash/shell.h b/ash/shell.h index 14e1f9144cdff..7d5ab3b680ae1 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -231,9 +231,15 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate, void ShowContextMenu(const gfx::Point& location_in_screen, ui::MenuSourceType source_type); - // Toggles the app list. |window| specifies in which display the app + // Shows the app list. |window| specifies in which display the app // list should be shown. If this is NULL, the active root window // will be used. + void ShowAppList(aura::Window* anchor); + + // Dismisses the app list. + void DismissAppList(); + + // Shows the app list if it's not visible. Dismisses it otherwise. void ToggleAppList(aura::Window* anchor); // Returns app list target visibility. diff --git a/ash/shell/app_list.cc b/ash/shell/app_list.cc index f8351532d384a..bf5a44b504b9e 100644 --- a/ash/shell/app_list.cc +++ b/ash/shell/app_list.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include #include "ash/session/session_state_delegate.h" #include "ash/shell.h" @@ -316,8 +317,7 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate { virtual void Dismiss() OVERRIDE { DCHECK(ash::Shell::HasInstance()); - if (Shell::GetInstance()->GetAppListTargetVisibility()) - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->DismissAppList(); } virtual void ViewClosing() OVERRIDE { @@ -353,8 +353,9 @@ class ExampleAppListViewDelegate : public app_list::AppListViewDelegate { return NULL; } - virtual views::View* CreateCustomPageWebView(const gfx::Size& size) OVERRIDE { - return NULL; + virtual std::vector CreateCustomPageWebViews( + const gfx::Size& size) OVERRIDE { + return std::vector(); } virtual bool IsSpeechRecognitionEnabled() OVERRIDE { diff --git a/ash/shell/content_client/shell_browser_main_parts.cc b/ash/shell/content_client/shell_browser_main_parts.cc index cb84b4680af66..6cdc1147a4959 100644 --- a/ash/shell/content_client/shell_browser_main_parts.cc +++ b/ash/shell/content_client/shell_browser_main_parts.cc @@ -26,7 +26,6 @@ #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" #include "ui/compositor/compositor.h" #include "ui/gfx/screen.h" diff --git a/ash/shell/content_client/shell_main_delegate.cc b/ash/shell/content_client/shell_main_delegate.cc index 014fae1a2b83c..5e6144316839d 100644 --- a/ash/shell/content_client/shell_main_delegate.cc +++ b/ash/shell/content_client/shell_main_delegate.cc @@ -38,7 +38,8 @@ content::ContentBrowserClient* ShellMainDelegate::CreateContentBrowserClient() { } void ShellMainDelegate::InitializeResourceBundle() { - ui::ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); } } // namespace shell diff --git a/ash/shell/context_menu.cc b/ash/shell/context_menu.cc index b7bffb30dbfb4..91f5abd344b4e 100644 --- a/ash/shell/context_menu.cc +++ b/ash/shell/context_menu.cc @@ -9,7 +9,6 @@ #include "ash/shelf/shelf_types.h" #include "ash/shell.h" #include "grit/ash_strings.h" -#include "ui/base/l10n/l10n_util.h" namespace ash { namespace shell { diff --git a/ash/shell/shell_delegate_impl.cc b/ash/shell/shell_delegate_impl.cc index 6261e1e98f2ad..02c6a27a2d5d7 100644 --- a/ash/shell/shell_delegate_impl.cc +++ b/ash/shell/shell_delegate_impl.cc @@ -128,6 +128,9 @@ class SessionStateDelegateImpl : public SessionStateDelegate { } virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE {} virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE {} + virtual bool IsMultiProfileAllowedByPrimaryUserPolicy() const OVERRIDE { + return true; + } virtual void AddSessionStateObserver( ash::SessionStateObserver* observer) OVERRIDE {} virtual void RemoveSessionStateObserver( diff --git a/ash/shell/shell_main_parts.cc b/ash/shell/shell_main_parts.cc index 8cad410084e27..0de3d310d8e9a 100644 --- a/ash/shell/shell_main_parts.cc +++ b/ash/shell/shell_main_parts.cc @@ -14,7 +14,8 @@ namespace shell { void PreMainMessageLoopStart() { ui::RegisterPathProvider(); base::i18n::InitializeICU(); - ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); } } // namespace ash diff --git a/ash/strings/ash_strings_am.xtb b/ash/strings/ash_strings_am.xtb index 31e289c98a065..bff21bea5d1b4 100644 --- a/ash/strings/ash_strings_am.xtb +++ b/ash/strings/ash_strings_am.xtb @@ -154,7 +154,7 @@ ወደ በማንጸባረቅ ላይ አነስተኛ ኃይል ወዳለው ባትሪ መሙያ ተሰክቷል። የባትሪ መሙላት አስተማማኝ ላይሆን ይችላል። ቆልፍ -ትግበራዎች +መተግበሪያዎች የማግበር አለመሳካት ከአውታረ መረብ «» ጋር መገናኘት አልተሳካም፦ Wi-Fi ጠፍቷል። diff --git a/ash/strings/ash_strings_ar.xtb b/ash/strings/ash_strings_ar.xtb index 1332f40ab917d..a3ed5e6a31c09 100644 --- a/ash/strings/ash_strings_ar.xtb +++ b/ash/strings/ash_strings_ar.xtb @@ -154,7 +154,7 @@ نسخ إلى تمّ توصيل شاحن منخفض الطاقة. لذا قد لا تكون عملية شحن البطارية جديرة بالثقة. قفل -التطبيقات +تطبيقات إخفاق في عملية التنشيط أخفق الاتصال بشبكة "": ‏تم إيقاف تشغيل Wi-Fi. diff --git a/ash/strings/ash_strings_da.xtb b/ash/strings/ash_strings_da.xtb index 60e7041cdaada..3aa37fc15b5af 100644 --- a/ash/strings/ash_strings_da.xtb +++ b/ash/strings/ash_strings_da.xtb @@ -177,7 +177,7 @@ Tryk på Shift+Alt for at ændre den. Ugyldig adgangskode Aktivér mobildata ChromeVox (talefeedback) -Hjælpemidler +Hjælpefunktioner Sessionen slutter om . Fejlen genkendes ikke Hovedtelefon @@ -194,7 +194,7 @@ Servermeddelelse: Det var ikke muligt at oprette forbindelse til netværket: Afslut session Wi-Fi er slået til. -Alt+Søg eller Skift +Alt+Søg eller Shift Bluetooth-enheden "" vil gerne have parringstilladelse. Inden du accepterer, skal du bekræfte, at denne adgangsnøgle er vist på den pågældende enhed: Batteriet er % opladet. Indstillinger... @@ -218,10 +218,10 @@ Servermeddelelse: Batteriet er helt opladet. Vis skærmtastatur Fortryd -Søg eller skift +Søg eller Shift Hjælp CAPS LOCK er slået til. -Tryk på Søg eller Skift for at annullere. +Tryk på Søg eller Shift for at annullere. Beregner... Hjemmenetværk mangler Bluetooth-enheden "" vil gerne have parringstilladelse. Indtast denne pinkode på den pågældende enhed: @@ -242,7 +242,7 @@ Tryk på Søg eller Skift for at annullere. Tryk på Ctrl+Alt+Z for at deaktivere dette. Google Drev-indstillinger... CAPS LOCK er slået til. -Tryk på Alt+Søg eller Skift for at annullere. +Tryk på Alt+Søg eller Shift for at annullere. Batteriniveauet er lavt ( %) Accepter : tilbage diff --git a/ash/strings/ash_strings_es-419.xtb b/ash/strings/ash_strings_es-419.xtb index 4ed0c869dfea9..d41b033d9f64f 100644 --- a/ash/strings/ash_strings_es-419.xtb +++ b/ash/strings/ash_strings_es-419.xtb @@ -154,7 +154,7 @@ Para cambiarlo, presiona Shift + Alt. Copiando en Conexión a un cargador de baja potencia. Es posible que la carga de la batería no sea confiable. Bloquear -Google Apps +Aplicaciones Fallo en la activación Error al conectar a la red "": Wi-Fi desactivada diff --git a/ash/strings/ash_strings_fil.xtb b/ash/strings/ash_strings_fil.xtb index 9a7dd4403df1b..590998657db6d 100644 --- a/ash/strings/ash_strings_fil.xtb +++ b/ash/strings/ash_strings_fil.xtb @@ -154,7 +154,7 @@ Pindutin ang Shift + Alt upang magpalit. Nagmi-mirror sa Naka-saksak sa isang low-power charger. Maaaring hindi maging tiyak ang pag-charge ng baterya. I-lock -Apps +Mga App Pagkabigo ng pag-activate Nabigong kumonekta sa network na '': Naka-off ang Wi-Fi. diff --git a/ash/strings/ash_strings_fr.xtb b/ash/strings/ash_strings_fr.xtb index 31c6a5b49be99..206d154294471 100644 --- a/ash/strings/ash_strings_fr.xtb +++ b/ash/strings/ash_strings_fr.xtb @@ -154,7 +154,7 @@ Appuyez sur Maj + Alt pour en utiliser un autre. Mise en miroir pour L'appareil est branché à un chargeur de faible puissance. Il se peut que la charge ne soit pas fiable. Verrouiller -Google Apps +Applications Échec de l'activation Échec de la connexion au réseau "" : . Le Wi-Fi est désactivé. diff --git a/ash/strings/ash_strings_gu.xtb b/ash/strings/ash_strings_gu.xtb index 400ac4ad7d1a6..31db1636e7391 100644 --- a/ash/strings/ash_strings_gu.xtb +++ b/ash/strings/ash_strings_gu.xtb @@ -154,7 +154,7 @@ પર પ્રતિબિંબિત થઈ રહ્યું છે નિમ્ન-પાવર ચાર્જરમાં પ્લગ કરેલું છે. બૅટરી ચાર્જિંગ વિશ્વસનીય હશે નહીં. લૉક -એપ્લિકેશન્સ +એપ્સ સક્રિયતા નિષ્ફળ નેટવર્ક '' થી કનેક્ટ કરવામાં નિષ્ફળ: Wi-Fi બંધ છે. diff --git a/ash/strings/ash_strings_hi.xtb b/ash/strings/ash_strings_hi.xtb index 78cad174999dc..5e00881eded9f 100644 --- a/ash/strings/ash_strings_hi.xtb +++ b/ash/strings/ash_strings_hi.xtb @@ -154,7 +154,7 @@ पर मिरर कर रहा है कम-शक्ति वाले चार्जर में प्लग इन करें. बैटरी चार्ज करना संभवत: विश्वसनीय नहीं होगा. लॉक करें -Apps +ऐप्स सक्रियण विफलता नेटवर्क से कनेक्ट करने में विफल '': वाई-फ़ाई बंद है. diff --git a/ash/strings/ash_strings_hr.xtb b/ash/strings/ash_strings_hr.xtb index 6f87dc5c697e6..0e21d331fca6c 100644 --- a/ash/strings/ash_strings_hr.xtb +++ b/ash/strings/ash_strings_hr.xtb @@ -154,7 +154,7 @@ Pritisnite Shift + Alt za promjenu. Zrcaljenje na zaslon Uređaj je priključen na punjač male snage. Punjenje baterije možda nije pouzdano. Zaključaj -Apps +Aplikacije Neuspjela aktivacija Neuspješno povezivanje s mrežom "": Wi-Fi je isključen. diff --git a/ash/strings/ash_strings_hu.xtb b/ash/strings/ash_strings_hu.xtb index be8b9832c4f14..3bbac57fdfebd 100644 --- a/ash/strings/ash_strings_hu.xtb +++ b/ash/strings/ash_strings_hu.xtb @@ -154,7 +154,7 @@ A váltáshoz nyomja meg a Shift + Alt billentyűkódot. Tükrözés: Kis teljesítményű töltőt csatlakoztatott. Az akkumulátor töltése nem megbízható. Zárolás -Google Alkalmazások +Alkalmazások Aktiválási hiba Nem sikerült csatlakozni a(z) hálózathoz: Wi-Fi kikapcsolva. diff --git a/ash/strings/ash_strings_id.xtb b/ash/strings/ash_strings_id.xtb index 01709fc4e8c6c..f12b4693fc177 100644 --- a/ash/strings/ash_strings_id.xtb +++ b/ash/strings/ash_strings_id.xtb @@ -154,7 +154,7 @@ Tekan Shift + Alt untuk beralih. Mencerminkan ke Dipasang ke pengisi daya rendah. Pengisian daya baterai mungkin tidak dapat diandalkan. Kunci -Apps +Apl Kegagalan aktivasi Gagal menyambung ke jaringan '': Wi-Fi dinonaktifkan. diff --git a/ash/strings/ash_strings_it.xtb b/ash/strings/ash_strings_it.xtb index 15f2e0429ecd4..20c13bd1ae12d 100644 --- a/ash/strings/ash_strings_it.xtb +++ b/ash/strings/ash_strings_it.xtb @@ -154,7 +154,7 @@ Premi Maiusc+Alt per cambiare metodo. Mirroring su Collegato a un caricabatterie a basso consumo. La carica della batteria potrebbe non essere affidabile. Blocca -Google Apps +App Errore di attivazione Connessione alla rete "" non riuscita: Wi-Fi non attivo. diff --git a/ash/strings/ash_strings_iw.xtb b/ash/strings/ash_strings_iw.xtb index 7e8e6e53bf198..1f9b0d4d9fe1c 100644 --- a/ash/strings/ash_strings_iw.xtb +++ b/ash/strings/ash_strings_iw.xtb @@ -154,7 +154,7 @@ משקף אל מחובר למטען בעל מתח נמוך. ייתכן שטעינת הסוללה לא תהיה אמינה. נעילה -Apps +אפליקציות כשל בהפעלה ההתחברות לרשת נכשלה '': ‏Wi-Fi כבוי. diff --git a/ash/strings/ash_strings_ja.xtb b/ash/strings/ash_strings_ja.xtb index cfc805c90459b..f6d5ae16a89ac 100644 --- a/ash/strings/ash_strings_ja.xtb +++ b/ash/strings/ash_strings_ja.xtb @@ -154,7 +154,7 @@ へミラーリング 低電力の充電器に接続しています。バッテリーが充電されない可能性があります。 ロック -Apps +アプリ 起動失敗 ネットワーク「」に接続できませんでした: Wi-Fi が無効になりました。 diff --git a/ash/strings/ash_strings_kn.xtb b/ash/strings/ash_strings_kn.xtb index 5879955994e0a..cd20d66f3a874 100644 --- a/ash/strings/ash_strings_kn.xtb +++ b/ash/strings/ash_strings_kn.xtb @@ -154,7 +154,7 @@ ಗೆ ಪ್ರತಿಬಿಂಬಿಸುತ್ತಿದೆ ಕಡಿಮೆ ವಿದ್ಯುತ್ ಚಾರ್ಜರ್‌ಗೆ ಪ್ಲಗ್ ಮಾಡಲಾಗಿದೆ. ಬ್ಯಾಟರಿ ಚಾರ್ಜಿಂಗ್ ವಿಶ್ವಾಸಾರ್ಹವಾಗಿಲ್ಲದಿರಬಹುದು. ಲಾಕ್ ಮಾಡಿ -Apps +ಆಪ್ಸ್‌‌ ಸಕ್ರಿಯಗೊಳಿಸುವಿಕೆ ವಿಫಲವಾಗಿದೆ '' ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಸಂಪರ್ಕಿಸಲು ವಿಫಲವಾಗಿದೆ: Wi-Fi ಆಫ್ ಮಾಡಲಾಗಿದೆ. diff --git a/ash/strings/ash_strings_ko.xtb b/ash/strings/ash_strings_ko.xtb index 84f8ed458ca11..2b767fd230ccd 100644 --- a/ash/strings/ash_strings_ko.xtb +++ b/ash/strings/ash_strings_ko.xtb @@ -154,7 +154,7 @@ 에 미러링 저출력 충전기에 연결되었습니다. 배터리 충전 상태가 불안정합니다. 잠금 -응용프로그램 + 활성화 실패 '' 네트워크에 연결하지 못했습니다: Wi-Fi가 꺼져 있습니다. diff --git a/ash/strings/ash_strings_ml.xtb b/ash/strings/ash_strings_ml.xtb index c3ec52fbc0835..8dd240c82188e 100644 --- a/ash/strings/ash_strings_ml.xtb +++ b/ash/strings/ash_strings_ml.xtb @@ -153,7 +153,7 @@ എന്നതിലേക്ക് മിറർചെയ്യുന്നു കുറഞ്ഞ തോതിൽ വൈദ്യുതി പ്രവഹിക്കുന്ന ചാർജ്ജറിലേക്ക് പ്ലഗ് ചെയ്‌തിരിക്കുന്നു. ബാറ്ററി ചാർജുചെയ്യൽ വിശ്വസനീയമാകണമെന്നില്ല. ലോക്കുചെയ്യുക -അപ്ലിക്കേഷന്‍സ് +ആപ്സ് സജീവമാക്കല്‍ പരാജയപ്പെട്ടു '' നെറ്റ്‌വര്‍‌ക്കിലേക്ക് ബന്ധിപ്പിക്കുന്നതിൽ പരാജയപ്പെട്ടു: Wi-Fi ഓഫുചെയ്‌തു. diff --git a/ash/strings/ash_strings_nl.xtb b/ash/strings/ash_strings_nl.xtb index 53e17daed68e5..c99a16fc409b4 100644 --- a/ash/strings/ash_strings_nl.xtb +++ b/ash/strings/ash_strings_nl.xtb @@ -154,7 +154,7 @@ Druk op Shift + Alt om te schakelen. Spiegelen naar Aangesloten op een laag-vermogen-lader. Opladen van de batterij mogelijk niet betrouwbaar. Vergrendelen -Google Apps +Apps Activering mislukt Kan geen verbinding maken met het netwerk '': Wifi is uitgeschakeld. diff --git a/ash/strings/ash_strings_sr.xtb b/ash/strings/ash_strings_sr.xtb index 211f69b95bcde..0597112247aa3 100644 --- a/ash/strings/ash_strings_sr.xtb +++ b/ash/strings/ash_strings_sr.xtb @@ -154,7 +154,7 @@ Пресликавање у Уређај је укључен у пуњач мале снаге. Пуњење батерије можда неће бити поуздано. Закључај -Apps +Апликације Активација није успела Повезивање са мрежом „“ није успело: Wi-Fi је искључен. diff --git a/ash/strings/ash_strings_sv.xtb b/ash/strings/ash_strings_sv.xtb index 7b227267de237..1d1c93a379a3e 100644 --- a/ash/strings/ash_strings_sv.xtb +++ b/ash/strings/ash_strings_sv.xtb @@ -154,7 +154,7 @@ Spegling av Ansluten till en laddare med låg effekt. Batteriet kanske inte laddas ordentligt. Lås -Apps +Appar Aktiveringsfel Det gick inte att ansluta till nätverket : Wi-Fi är inaktiverat. diff --git a/ash/strings/ash_strings_te.xtb b/ash/strings/ash_strings_te.xtb index 4132adc6a0ae4..638a55bfec668 100644 --- a/ash/strings/ash_strings_te.xtb +++ b/ash/strings/ash_strings_te.xtb @@ -154,7 +154,7 @@ కు దర్పణం చేస్తోంది తక్కువ-పవర్ గల ఛార్జర్‌కు ప్లగిన్ చేయబడింది. బ్యాటరీ ఛార్జింగ్ విశ్వసనీయంగా ఉండకపోవచ్చు. లాక్ చేయి -Apps +యాప్స్ సక్రియా విఫలం ''కు నెట్‌వర్క్‌కు కనెక్ట్ చేయడానికి విఫలమైంది: Wi-Fi నిలిపివేయబడింది. diff --git a/ash/strings/ash_strings_th.xtb b/ash/strings/ash_strings_th.xtb index 63496e81cb5c9..d64076035a9fe 100644 --- a/ash/strings/ash_strings_th.xtb +++ b/ash/strings/ash_strings_th.xtb @@ -154,7 +154,7 @@ กำลังแสดงผลไปที่ เสียบอยู่กับที่ชาร์จพลังงานต่ำ การชาร์จแบตเตอรี่อาจไม่น่าเชื่อถือ ล็อก -แอปพลิเคชัน +แอป การเปิดใช้งานล้มเหลว ไม่สามารถเชื่อมต่อเครือข่าย "": WiFi ปิดอยู่ diff --git a/ash/system/audio/tray_audio.cc b/ash/system/audio/tray_audio.cc index 194b6c65fdf34..634463e525d89 100644 --- a/ash/system/audio/tray_audio.cc +++ b/ash/system/audio/tray_audio.cc @@ -26,8 +26,6 @@ #include "third_party/skia/include/core/SkPaint.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/effects/SkGradientShader.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/canvas.h" #include "ui/gfx/font_list.h" #include "ui/gfx/image/image.h" diff --git a/ash/system/bluetooth/tray_bluetooth.cc b/ash/system/bluetooth/tray_bluetooth.cc index 6f4509f8e8224..091f24bebd122 100644 --- a/ash/system/bluetooth/tray_bluetooth.cc +++ b/ash/system/bluetooth/tray_bluetooth.cc @@ -4,6 +4,7 @@ #include "ash/system/bluetooth/tray_bluetooth.h" +#include "ash/session/session_state_delegate.h" #include "ash/shell.h" #include "ash/system/tray/fixed_sized_scroll_view.h" #include "ash/system/tray/hover_highlight_view.h" @@ -301,7 +302,12 @@ class BluetoothDetailedView : public TrayDetailsView, // Add bluetooth device requires a browser window, hide it for non logged in // user. - if (login_ == user::LOGGED_IN_NONE || login_ == user::LOGGED_IN_LOCKED) + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + + if (login_ == user::LOGGED_IN_NONE || login_ == user::LOGGED_IN_LOCKED || + userAddingRunning) return; ash::SystemTrayDelegate* delegate = diff --git a/ash/system/chromeos/network/network_connect.cc b/ash/system/chromeos/network/network_connect.cc index 8ae61f8728446..fc9232e8be613 100644 --- a/ash/system/chromeos/network/network_connect.cc +++ b/ash/system/chromeos/network/network_connect.cc @@ -12,6 +12,7 @@ #include "ash/system/tray/system_tray_notifier.h" #include "base/bind.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 "chromeos/login/login_state.h" @@ -340,12 +341,17 @@ void ConnectToNetwork(const std::string& service_path, gfx::NativeWindow parent_window) { NET_LOG_USER("ConnectToNetwork", service_path); const NetworkState* network = GetNetworkState(service_path); - if (network && !network->error().empty() && !network->security().empty()) { - NET_LOG_USER("Configure: " + network->error(), service_path); - // If the network is in an error state, show the configuration UI directly - // to avoid a spurious notification. - HandleUnconfiguredNetwork(service_path, parent_window); - return; + if (network) { + if (!network->error().empty() && !network->security().empty()) { + NET_LOG_USER("Configure: " + network->error(), service_path); + // If the network is in an error state, show the configuration UI directly + // to avoid a spurious notification. + HandleUnconfiguredNetwork(service_path, parent_window); + return; + } else if (network->RequiresActivation()) { + ActivateCellular(service_path); + return; + } } const bool check_error_state = true; CallConnectToNetwork(service_path, check_error_state, parent_window); @@ -442,7 +448,7 @@ void ShowMobileSetup(const std::string& service_path) { return; } if (cellular->activation_state() != shill::kActivationStateActivated && - cellular->activate_over_non_cellular_networks() && + cellular->activation_type() == shill::kActivationTypeNonCellular && !handler->DefaultNetwork()) { message_center::MessageCenter::Get()->AddNotification( message_center::Notification::CreateSystemNotification( diff --git a/ash/system/chromeos/network/tray_vpn.cc b/ash/system/chromeos/network/tray_vpn.cc index a128968c805c9..01547ae11a11c 100644 --- a/ash/system/chromeos/network/tray_vpn.cc +++ b/ash/system/chromeos/network/tray_vpn.cc @@ -5,6 +5,7 @@ #include "ash/system/chromeos/network/tray_vpn.h" #include "ash/metrics/user_metrics_recorder.h" +#include "ash/session/session_state_delegate.h" #include "ash/shell.h" #include "ash/system/chromeos/network/network_state_list_detailed_view.h" #include "ash/system/tray/system_tray.h" @@ -18,7 +19,6 @@ #include "grit/ui_chromeos_strings.h" #include "third_party/cros_system_api/dbus/service_constants.h" #include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/chromeos/network/network_icon.h" #include "ui/chromeos/network/network_icon_animation.h" @@ -126,7 +126,13 @@ views::View* TrayVPN::CreateDefaultView(user::LoginStatus status) { if (!tray::VpnDefaultView::ShouldShow()) return NULL; - default_ = new tray::VpnDefaultView(this, status != user::LOGGED_IN_LOCKED); + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + + default_ = new tray::VpnDefaultView( + this, status != user::LOGGED_IN_LOCKED && !userAddingRunning); + return default_; } diff --git a/ash/system/chromeos/settings/tray_settings.cc b/ash/system/chromeos/settings/tray_settings.cc index b6f4e76f663e9..d88d04ba7f398 100644 --- a/ash/system/chromeos/settings/tray_settings.cc +++ b/ash/system/chromeos/settings/tray_settings.cc @@ -4,6 +4,7 @@ #include "ash/system/chromeos/settings/tray_settings.h" +#include "ash/session/session_state_delegate.h" #include "ash/shell.h" #include "ash/system/chromeos/power/power_status.h" #include "ash/system/chromeos/power/power_status_view.h" @@ -40,8 +41,12 @@ class SettingsDefaultView : public ActionableView, ash::kTrayPopupPaddingBetweenItems)); bool power_view_right_align = false; + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + if (login_status_ != user::LOGGED_IN_NONE && - login_status_ != user::LOGGED_IN_LOCKED) { + login_status_ != user::LOGGED_IN_LOCKED && !userAddingRunning) { ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); views::ImageView* icon = new ash::FixedSizedImageView(0, ash::kTrayPopupItemHeight); @@ -72,8 +77,12 @@ class SettingsDefaultView : public ActionableView, // Overridden from ash::ActionableView. virtual bool PerformAction(const ui::Event& event) OVERRIDE { + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + if (login_status_ == user::LOGGED_IN_NONE || - login_status_ == user::LOGGED_IN_LOCKED) + login_status_ == user::LOGGED_IN_LOCKED || userAddingRunning) return false; ash::Shell::GetInstance()->system_tray_delegate()->ShowSettings(); diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc index d1fca840b3068..d8cafb9668bde 100644 --- a/ash/system/chromeos/tray_display.cc +++ b/ash/system/chromeos/tray_display.cc @@ -122,7 +122,10 @@ void OpenSettings() { case user::LOGGED_IN_PUBLIC: case user::LOGGED_IN_SUPERVISED: case user::LOGGED_IN_KIOSK_APP: - Shell::GetInstance()->system_tray_delegate()->ShowDisplaySettings(); + ash::SystemTrayDelegate* delegate = + Shell::GetInstance()->system_tray_delegate(); + if (delegate->ShouldShowSettings()) + delegate->ShowDisplaySettings(); } } diff --git a/ash/system/chromeos/tray_display_unittest.cc b/ash/system/chromeos/tray_display_unittest.cc index 5689c849ac711..1e22f9d36442a 100644 --- a/ash/system/chromeos/tray_display_unittest.cc +++ b/ash/system/chromeos/tray_display_unittest.cc @@ -12,6 +12,7 @@ #include "ash/test/ash_test_base.h" #include "ash/test/test_system_tray_delegate.h" #include "base/strings/string16.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "grit/ash_strings.h" #include "ui/accessibility/ax_view_state.h" diff --git a/ash/system/chromeos/tray_tracing.cc b/ash/system/chromeos/tray_tracing.cc index 907b51bfd7ccd..c885e079ec8ee 100644 --- a/ash/system/chromeos/tray_tracing.cc +++ b/ash/system/chromeos/tray_tracing.cc @@ -13,7 +13,6 @@ #include "ash/system/tray/tray_constants.h" #include "grit/ash_resources.h" #include "grit/ash_strings.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/image/image.h" #include "ui/views/controls/image_view.h" diff --git a/ash/system/date/date_default_view.cc b/ash/system/date/date_default_view.cc index f9ed12309b27b..bd05be2c88a22 100644 --- a/ash/system/date/date_default_view.cc +++ b/ash/system/date/date_default_view.cc @@ -42,8 +42,12 @@ DateDefaultView::DateDefaultView(ash::user::LoginStatus login) view->SetContent(date_view_); AddChildView(view); - if (login == ash::user::LOGGED_IN_LOCKED || - login == ash::user::LOGGED_IN_NONE) + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + + if (login == user::LOGGED_IN_LOCKED || + login == user::LOGGED_IN_NONE || userAddingRunning) return; date_view_->SetAction(TrayDate::SHOW_DATE_SETTINGS); diff --git a/ash/system/ime/tray_ime.cc b/ash/system/ime/tray_ime.cc index 3bd0296d06325..0ffa769498a25 100644 --- a/ash/system/ime/tray_ime.cc +++ b/ash/system/ime/tray_ime.cc @@ -8,6 +8,7 @@ #include "ash/metrics/user_metrics_recorder.h" #include "ash/root_window_controller.h" +#include "ash/session/session_state_delegate.h" #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" #include "ash/system/tray/hover_highlight_view.h" @@ -25,7 +26,6 @@ #include "grit/ash_strings.h" #include "ui/accessibility/ax_enums.h" #include "ui/accessibility/ax_view_state.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/gfx/font.h" #include "ui/gfx/image/image.h" @@ -114,7 +114,12 @@ class IMEDetailedView : public TrayDetailsView, AppendIMEList(list); if (!property_list.empty()) AppendIMEProperties(property_list); - if (login_ != user::LOGGED_IN_NONE && login_ != user::LOGGED_IN_LOCKED) + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + + if (login_ != user::LOGGED_IN_NONE && login_ != user::LOGGED_IN_LOCKED && + !userAddingRunning) AppendSettings(); AppendHeaderEntry(); diff --git a/ash/system/status_area_widget_delegate.cc b/ash/system/status_area_widget_delegate.cc index cfdae47d30df9..7d711a1ac21fa 100644 --- a/ash/system/status_area_widget_delegate.cc +++ b/ash/system/status_area_widget_delegate.cc @@ -13,7 +13,6 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "ui/aura/window_event_dispatcher.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/layer.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/animation/tween.h" diff --git a/ash/system/tray/system_tray.cc b/ash/system/tray/system_tray.cc index a28f78405fe7b..9bd30a016436b 100644 --- a/ash/system/tray/system_tray.cc +++ b/ash/system/tray/system_tray.cc @@ -8,7 +8,6 @@ #include "ash/metrics/user_metrics_recorder.h" #include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" -#include "ash/shell/panel_window.h" #include "ash/shell_window_ids.h" #include "ash/system/audio/tray_audio.h" #include "ash/system/bluetooth/tray_bluetooth.h" diff --git a/ash/system/tray_accessibility.cc b/ash/system/tray_accessibility.cc index 02c8d840b1ef6..143c94e28360a 100644 --- a/ash/system/tray_accessibility.cc +++ b/ash/system/tray_accessibility.cc @@ -6,6 +6,7 @@ #include "ash/accessibility_delegate.h" #include "ash/metrics/user_metrics_recorder.h" +#include "ash/session/session_state_delegate.h" #include "ash/shell.h" #include "ash/system/tray/hover_highlight_view.h" #include "ash/system/tray/system_tray.h" @@ -208,8 +209,12 @@ void AccessibilityDetailedView::AppendAccessibilityList() { void AccessibilityDetailedView::AppendHelpEntries() { // Currently the help page requires a browser window. // TODO(yoshiki): show this even on login/lock screen. crbug.com/158286 + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + if (login_ == user::LOGGED_IN_NONE || - login_ == user::LOGGED_IN_LOCKED) + login_ == user::LOGGED_IN_LOCKED || userAddingRunning) return; views::View* bottom_row = new View(); diff --git a/ash/system/user/accounts_detailed_view.cc b/ash/system/user/accounts_detailed_view.cc index 393f8d58fa620..d2534143a155f 100644 --- a/ash/system/user/accounts_detailed_view.cc +++ b/ash/system/user/accounts_detailed_view.cc @@ -20,6 +20,7 @@ #include "components/user_manager/user_info.h" #include "grit/ash_resources.h" #include "grit/ui_resources.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" #include "ui/views/border.h" #include "ui/views/layout/box_layout.h" diff --git a/ash/system/user/accounts_detailed_view.h b/ash/system/user/accounts_detailed_view.h index 6dd9a0bbe7239..99ee2b3551fe3 100644 --- a/ash/system/user/accounts_detailed_view.h +++ b/ash/system/user/accounts_detailed_view.h @@ -15,7 +15,6 @@ #include "ash/system/user/user_view.h" #include "base/macros.h" #include "grit/ash_strings.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/label.h" diff --git a/ash/system/user/user_view.cc b/ash/system/user/user_view.cc index f3646a7a7aa3c..689764fc5ffb6 100644 --- a/ash/system/user/user_view.cc +++ b/ash/system/user/user_view.cc @@ -493,8 +493,13 @@ void UserView::ToggleAddUserMenuOption() { const SessionStateDelegate* delegate = Shell::GetInstance()->session_state_delegate(); - add_user_disabled_ = delegate->NumberOfLoggedInUsers() >= - delegate->GetMaximumNumberOfLoggedInUsers(); + + bool multi_profile_allowed = + delegate->IsMultiProfileAllowedByPrimaryUserPolicy(); + add_user_disabled_ = (delegate->NumberOfLoggedInUsers() >= + delegate->GetMaximumNumberOfLoggedInUsers()) || + !multi_profile_allowed; + ButtonFromView* button = new ButtonFromView( add_user_view, add_user_disabled_ ? NULL : this, @@ -508,9 +513,15 @@ void UserView::ToggleAddUserMenuOption() { if (add_user_disabled_) { ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + int message_id; + if (!multi_profile_allowed) + message_id = IDS_ASH_STATUS_TRAY_MESSAGE_NOT_ALLOWED_PRIMARY_USER; + else + message_id = IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER; + popup_message_.reset(new PopupMessage( bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_CAPTION_CANNOT_ADD_USER), - bundle.GetLocalizedString(IDS_ASH_STATUS_TRAY_MESSAGE_CANNOT_ADD_USER), + bundle.GetLocalizedString(message_id), PopupMessage::ICON_WARNING, add_user_view->anchor(), views::BubbleBorder::TOP_LEFT, diff --git a/ash/system/web_notification/ash_popup_alignment_delegate.cc b/ash/system/web_notification/ash_popup_alignment_delegate.cc index 1f8cd3a7bf2d1..a81b0e176ad3e 100644 --- a/ash/system/web_notification/ash_popup_alignment_delegate.cc +++ b/ash/system/web_notification/ash_popup_alignment_delegate.cc @@ -51,6 +51,7 @@ void AshPopupAlignmentDelegate::StartObserving(gfx::Screen* screen, const gfx::Display& display) { screen_ = screen; display_id_ = display.id(); + work_area_ = display.work_area(); root_window_ = ash::Shell::GetInstance()->display_controller()-> GetRootWindowForDisplayId(display_id_); UpdateShelf(); diff --git a/ash/system/web_notification/web_notification_tray.cc b/ash/system/web_notification/web_notification_tray.cc index 554d60e6ae2b3..900dd936fcd61 100644 --- a/ash/system/web_notification/web_notification_tray.cc +++ b/ash/system/web_notification/web_notification_tray.cc @@ -91,8 +91,8 @@ class WebNotificationBubbleWrapper { } views::TrayBubbleView* bubble_view = views::TrayBubbleView::Create( tray->GetBubbleWindowContainer(), anchor, tray, &init_params); - bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); bubble_wrapper_.reset(new TrayBubbleWrapper(tray, bubble_view)); + bubble_view->SetArrowPaintType(views::BubbleBorder::PAINT_NONE); bubble->InitializeContents(bubble_view); } @@ -393,8 +393,12 @@ bool WebNotificationTray::ShowNotifierSettings() { bool WebNotificationTray::IsContextMenuEnabled() const { user::LoginStatus login_status = status_area_widget()->login_status(); + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + return login_status != user::LOGGED_IN_NONE - && login_status != user::LOGGED_IN_LOCKED; + && login_status != user::LOGGED_IN_LOCKED && !userAddingRunning; } message_center::MessageCenterTray* WebNotificationTray::GetMessageCenterTray() { @@ -457,9 +461,13 @@ void WebNotificationTray::UpdateTrayContent() { button_->SetState(views::CustomButton::STATE_PRESSED); else button_->SetState(views::CustomButton::STATE_NORMAL); + bool userAddingRunning = ash::Shell::GetInstance() + ->session_state_delegate() + ->IsInSecondaryLoginScreen(); + SetVisible((status_area_widget()->login_status() != user::LOGGED_IN_NONE) && (status_area_widget()->login_status() != user::LOGGED_IN_LOCKED) && - (message_center->NotificationCount() > 0)); + !userAddingRunning && (message_center->NotificationCount() > 0)); Layout(); SchedulePaint(); } diff --git a/ash/system/win/audio/tray_audio_delegate_win.cc b/ash/system/win/audio/tray_audio_delegate_win.cc index 57c7b56307e86..eda45b38f8bab 100644 --- a/ash/system/win/audio/tray_audio_delegate_win.cc +++ b/ash/system/win/audio/tray_audio_delegate_win.cc @@ -7,8 +7,6 @@ #include #include -#include "grit/ash_resources.h" -#include "grit/ash_strings.h" #include "media/audio/win/core_audio_util_win.h" using base::win::ScopedComPtr; diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc index 70db370c69f7d..166e2b7d007df 100644 --- a/ash/test/ash_test_base.cc +++ b/ash/test/ash_test_base.cc @@ -16,7 +16,6 @@ #include "ash/test/test_session_state_delegate.h" #include "ash/test/test_shell_delegate.h" #include "ash/test/test_system_tray_delegate.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/window_positioner.h" #include "base/command_line.h" #include "ui/aura/client/aura_constants.h" @@ -32,6 +31,7 @@ #include "ui/gfx/display.h" #include "ui/gfx/point.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" #if defined(OS_CHROMEOS) #include "ash/system/chromeos/tray_display.h" @@ -262,7 +262,7 @@ aura::Window* AshTestBase::CreateTestWindowInShellWithDelegateAndType( aura::Window* root = ash::Shell::GetInstance()->display_controller()-> GetRootWindowForDisplayId(display.id()); gfx::Point origin = bounds.origin(); - wm::ConvertPointFromScreen(root, &origin); + ::wm::ConvertPointFromScreen(root, &origin); window->SetBounds(gfx::Rect(origin, bounds.size())); aura::client::ParentWindowWithContext(window, root, bounds); } diff --git a/ash/test/ash_test_helper.cc b/ash/test/ash_test_helper.cc index ceb2910432ec4..b9f8ece934864 100644 --- a/ash/test/ash_test_helper.cc +++ b/ash/test/ash_test_helper.cc @@ -19,6 +19,7 @@ #include "ui/aura/env.h" #include "ui/aura/input_state_lookup.h" #include "ui/aura/test/env_test_helper.h" +#include "ui/aura/test/event_generator_delegate_aura.h" #include "ui/base/ime/input_method_initializer.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/compositor/test/context_factories_for_test.h" @@ -51,6 +52,7 @@ AshTestHelper::AshTestHelper(base::MessageLoopForUI* message_loop) #if defined(USE_X11) aura::test::SetUseOverrideRedirectWindowByDefault(true); #endif + aura::test::InitializeAuraEventGeneratorDelegate(); } AshTestHelper::~AshTestHelper() { diff --git a/ash/test/shell_test_api.cc b/ash/test/shell_test_api.cc index 94d93e545fe07..c24fa1794b00c 100644 --- a/ash/test/shell_test_api.cc +++ b/ash/test/shell_test_api.cc @@ -5,6 +5,7 @@ #include "ash/test/shell_test_api.h" #include "ash/root_window_controller.h" +#include "ash/session/session_state_delegate.h" #include "ash/shelf/shelf_delegate.h" #include "ash/shell.h" @@ -69,5 +70,10 @@ void ShellTestApi::SetShelfDelegate(ShelfDelegate* delegate) { shell_->shelf_delegate_.reset(delegate); } +void ShellTestApi::SetSessionStateDelegate( + SessionStateDelegate* session_state_delegate) { + shell_->session_state_delegate_.reset(session_state_delegate); +} + } // namespace test } // namespace ash diff --git a/ash/test/shell_test_api.h b/ash/test/shell_test_api.h index 73643974fd851..16cc576c4d36f 100644 --- a/ash/test/shell_test_api.h +++ b/ash/test/shell_test_api.h @@ -17,6 +17,7 @@ class AshNativeCursorManager; class DragDropController; class MaximizeModeWindowManager; class RootWindowLayoutManager; +class SessionStateDelegate; class ScreenPositionController; class ShelfDelegate; class ShelfModel; @@ -46,6 +47,9 @@ class ShellTestApi { // Set ShelfDelegate. void SetShelfDelegate(ShelfDelegate* delegate); + // Set SessionStateDelegate. + void SetSessionStateDelegate(SessionStateDelegate* session_state_delegate); + private: Shell* shell_; // not owned diff --git a/ash/test/test_overlay_delegate.cc b/ash/test/test_overlay_delegate.cc new file mode 100644 index 0000000000000..404e90988b54a --- /dev/null +++ b/ash/test/test_overlay_delegate.cc @@ -0,0 +1,33 @@ +// 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 "ash/test/test_overlay_delegate.h" + +namespace ash { +namespace test { + +TestOverlayDelegate::TestOverlayDelegate() + : cancel_count_(0) {} +TestOverlayDelegate::~TestOverlayDelegate() {} + +int TestOverlayDelegate::GetCancelCountAndReset() { + int count = cancel_count_; + cancel_count_ = 0; + return count; +} + +void TestOverlayDelegate::Cancel() { + ++cancel_count_; +} + +bool TestOverlayDelegate::IsCancelingKeyEvent(ui::KeyEvent* event) { + return false; +} + +aura::Window* TestOverlayDelegate::GetWindow() { + return NULL; +} + +} // namespace test +} // namespace ash diff --git a/ash/test/test_overlay_delegate.h b/ash/test/test_overlay_delegate.h new file mode 100644 index 0000000000000..d43903fa8427a --- /dev/null +++ b/ash/test/test_overlay_delegate.h @@ -0,0 +1,34 @@ +// 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 ASH_TEST_TEST_OVERLAY_DELEGATE_H_ +#define ASH_TEST_TEST_OVERLAY_DELEGATE_H_ + +#include "ash/wm/overlay_event_filter.h" + +namespace ash { +namespace test { + +class TestOverlayDelegate : public OverlayEventFilter::Delegate { + public: + TestOverlayDelegate(); + virtual ~TestOverlayDelegate(); + + int GetCancelCountAndReset(); + + private: + // Overridden from OverlayEventFilter::Delegate: + virtual void Cancel() OVERRIDE; + virtual bool IsCancelingKeyEvent(ui::KeyEvent* event) OVERRIDE; + virtual aura::Window* GetWindow() OVERRIDE; + + int cancel_count_; + + DISALLOW_COPY_AND_ASSIGN(TestOverlayDelegate); +}; + +} // namespace test +} // namespace ash + +#endif // ASH_TEST_TEST_OVERLAY_DELEGATE_H_ diff --git a/ash/test/test_session_state_delegate.cc b/ash/test/test_session_state_delegate.cc index 4c7614341ee32..eac5576997fe6 100644 --- a/ash/test/test_session_state_delegate.cc +++ b/ash/test/test_session_state_delegate.cc @@ -227,6 +227,11 @@ void TestSessionStateDelegate::CycleActiveUser(CycleUser cycle_user) { SwitchActiveUser("someone@tray"); } +bool TestSessionStateDelegate::IsMultiProfileAllowedByPrimaryUserPolicy() + const { + return true; +} + void TestSessionStateDelegate::AddSessionStateObserver( SessionStateObserver* observer) { } diff --git a/ash/test/test_session_state_delegate.h b/ash/test/test_session_state_delegate.h index 478c340e80b8e..c508299112ce9 100644 --- a/ash/test/test_session_state_delegate.h +++ b/ash/test/test_session_state_delegate.h @@ -48,6 +48,7 @@ class TestSessionStateDelegate : public SessionStateDelegate { virtual bool ShouldShowAvatar(aura::Window* window) const OVERRIDE; virtual void SwitchActiveUser(const std::string& user_id) OVERRIDE; virtual void CycleActiveUser(CycleUser cycle_user) OVERRIDE; + virtual bool IsMultiProfileAllowedByPrimaryUserPolicy() const OVERRIDE; virtual void AddSessionStateObserver( ash::SessionStateObserver* observer) OVERRIDE; virtual void RemoveSessionStateObserver( diff --git a/ash/test/test_suite.cc b/ash/test/test_suite.cc index 7955264789c56..57e384e6a89f2 100644 --- a/ash/test/test_suite.cc +++ b/ash/test/test_suite.cc @@ -51,7 +51,8 @@ void AuraShellTestSuite::Initialize() { // Force unittests to run using en-US so if we test against string // output, it'll pass regardless of the system language. - ui::ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); } void AuraShellTestSuite::Shutdown() { diff --git a/ash/tooltips/tooltip_controller_unittest.cc b/ash/tooltips/tooltip_controller_unittest.cc index bb017307217b7..9591c58d99df3 100644 --- a/ash/tooltips/tooltip_controller_unittest.cc +++ b/ash/tooltips/tooltip_controller_unittest.cc @@ -8,7 +8,6 @@ #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/events/test/event_generator.h" #include "ui/gfx/font.h" #include "ui/gfx/point.h" diff --git a/ash/wm/app_list_controller_unittest.cc b/ash/wm/app_list_controller_unittest.cc index 05ceae3ba6146..7691567f96bf3 100644 --- a/ash/wm/app_list_controller_unittest.cc +++ b/ash/wm/app_list_controller_unittest.cc @@ -55,7 +55,7 @@ bool AppListControllerTest::IsCentered() const { // Tests that app launcher hides when focus moves to a normal window. TEST_P(AppListControllerTest, HideOnFocusOut) { - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); scoped_ptr window(CreateTestWindowInShellWithId(0)); @@ -67,7 +67,7 @@ TEST_P(AppListControllerTest, HideOnFocusOut) { // Tests that app launcher remains visible when focus is moved to a different // window in kShellWindowId_AppListContainer. TEST_P(AppListControllerTest, RemainVisibleWhenFocusingToApplistContainer) { - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); aura::Window* applist_container = Shell::GetContainer( @@ -82,7 +82,7 @@ TEST_P(AppListControllerTest, RemainVisibleWhenFocusingToApplistContainer) { // Tests that clicking outside the app-list bubble closes it. TEST_P(AppListControllerTest, ClickOutsideBubbleClosesBubble) { Shell* shell = Shell::GetInstance(); - shell->ToggleAppList(NULL); + shell->ShowAppList(NULL); aura::Window* app_window = shell->GetAppListWindow(); ASSERT_TRUE(app_window); @@ -105,7 +105,7 @@ TEST_P(AppListControllerTest, ClickOutsideBubbleClosesBubble) { // Tests that clicking outside the app-list bubble closes it. TEST_P(AppListControllerTest, TapOutsideBubbleClosesBubble) { Shell* shell = Shell::GetInstance(); - shell->ToggleAppList(NULL); + shell->ShowAppList(NULL); aura::Window* app_window = shell->GetAppListWindow(); ASSERT_TRUE(app_window); @@ -139,7 +139,7 @@ TEST_P(AppListControllerTest, NonPrimaryDisplay) { aura::Window* secondary_window = root_windows[1]; EXPECT_EQ("800,0 800x600", secondary_window->GetBoundsInScreen().ToString()); - Shell::GetInstance()->ToggleAppList(secondary_window); + Shell::GetInstance()->ShowAppList(secondary_window); EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); // Remove the secondary display. Shouldn't crash (http://crbug.com/368990). @@ -166,7 +166,7 @@ TEST_P(AppListControllerTest, TinyDisplay) { // Set up a screen with a tiny display (height smaller than the app list). UpdateDisplay("400x300"); - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); // The top of the app list should be on-screen (even if the bottom is not). diff --git a/ash/wm/ash_native_cursor_manager.cc b/ash/wm/ash_native_cursor_manager.cc index 91f1161cf3119..ae7bcd4e60350 100644 --- a/ash/wm/ash_native_cursor_manager.cc +++ b/ash/wm/ash_native_cursor_manager.cc @@ -13,6 +13,7 @@ #include "ui/aura/window_tree_host.h" #include "ui/base/cursor/cursor.h" #include "ui/base/cursor/image_cursors.h" +#include "ui/base/layout.h" namespace ash { namespace { @@ -75,9 +76,17 @@ void AshNativeCursorManager::SetDisplay( DCHECK(display.is_valid()); // Use the platform's device scale factor instead of the display's, which // might have been adjusted for the UI scale. - const float scale_factor = Shell::GetInstance()->display_manager()-> + const float original_scale = Shell::GetInstance()->display_manager()-> GetDisplayInfo(display.id()).device_scale_factor(); - if (image_cursors_->SetDisplay(display, scale_factor)) +#if defined(OS_CHROMEOS) + // And use the nearest resource scale factor. + const float cursor_scale = ui::GetScaleForScaleFactor( + ui::GetSupportedScaleFactor(original_scale)); +#else + // TODO(oshima): crbug.com/143619 + const float cursor_scale = original_scale; +#endif + if (image_cursors_->SetDisplay(display, cursor_scale)) SetCursor(delegate->GetCursor(), delegate); #if defined(OS_CHROMEOS) Shell::GetInstance()->display_controller()->cursor_window_controller()-> diff --git a/ash/wm/ash_native_cursor_manager_interactive_uitest.cc b/ash/wm/ash_native_cursor_manager_interactive_uitest.cc index 83df158964464..eb0e20a420960 100644 --- a/ash/wm/ash_native_cursor_manager_interactive_uitest.cc +++ b/ash/wm/ash_native_cursor_manager_interactive_uitest.cc @@ -34,7 +34,8 @@ class AshNativeCursorManagerTest : public test::AshTestBase { gfx::GLSurface::InitializeOneOffForTests(); ui::RegisterPathProvider(); - ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); base::FilePath resources_pack_path; PathService::Get(base::DIR_MODULE, &resources_pack_path); resources_pack_path = diff --git a/ash/wm/ash_native_cursor_manager_unittest.cc b/ash/wm/ash_native_cursor_manager_unittest.cc index ba7de8a5d1c16..4d746f87b3a6f 100644 --- a/ash/wm/ash_native_cursor_manager_unittest.cc +++ b/ash/wm/ash_native_cursor_manager_unittest.cc @@ -146,6 +146,17 @@ TEST_F(AshNativeCursorManagerTest, SetDeviceScaleFactorAndRotation) { EXPECT_EQ(gfx::Display::ROTATE_270, test_api.GetCurrentCursorRotation()); } +#if defined(OS_CHROMEOS) +// TODO(oshima): crbug.com/143619 +TEST_F(AshNativeCursorManagerTest, FractionalScale) { + ::wm::CursorManager* cursor_manager = Shell::GetInstance()->cursor_manager(); + CursorManagerTestApi test_api(cursor_manager); + // Cursor should use the resource scale factor. + UpdateDisplay("800x100*1.25"); + EXPECT_EQ(1.0f, test_api.GetCurrentCursor().device_scale_factor()); +} +#endif + TEST_F(AshNativeCursorManagerTest, UIScaleShouldNotChangeCursor) { int64 display_id = Shell::GetScreen()->GetPrimaryDisplay().id(); gfx::Display::SetInternalDisplayId(display_id); diff --git a/ash/wm/coordinate_conversion.cc b/ash/wm/coordinate_conversion.cc index 991e7d4e1a0d4..258f97664aca5 100644 --- a/ash/wm/coordinate_conversion.cc +++ b/ash/wm/coordinate_conversion.cc @@ -31,17 +31,5 @@ aura::Window* GetRootWindowMatching(const gfx::Rect& rect) { GetRootWindowForDisplayId(display.id()); } -void ConvertPointToScreen(const aura::Window* window, gfx::Point* point) { - CHECK(aura::client::GetScreenPositionClient(window->GetRootWindow())); - aura::client::GetScreenPositionClient(window->GetRootWindow())-> - ConvertPointToScreen(window, point); -} - -void ConvertPointFromScreen(const aura::Window* window, - gfx::Point* point_in_screen) { - aura::client::GetScreenPositionClient(window->GetRootWindow())-> - ConvertPointFromScreen(window, point_in_screen); -} - } // namespace wm } // namespace ash diff --git a/ash/wm/coordinate_conversion.h b/ash/wm/coordinate_conversion.h index 539e7c036b85d..aa4a84850b190 100644 --- a/ash/wm/coordinate_conversion.h +++ b/ash/wm/coordinate_conversion.h @@ -28,16 +28,6 @@ ASH_EXPORT aura::Window* GetRootWindowAt(const gfx::Point& point); // the virtual scren coordinates. ASH_EXPORT aura::Window* GetRootWindowMatching(const gfx::Rect& rect); -// Converts the |point| from a given |window|'s coordinates into the screen -// coordinates. -ASH_EXPORT void ConvertPointToScreen(const aura::Window* window, - gfx::Point* point); - -// Converts the |point| from the screen coordinates to a given |window|'s -// coordinates. -ASH_EXPORT void ConvertPointFromScreen(const aura::Window* window, - gfx::Point* point_in_screen); - } // namespace wm } // namespace ash diff --git a/ash/wm/dock/docked_window_layout_manager_unittest.cc b/ash/wm/dock/docked_window_layout_manager_unittest.cc index 48db1547dc6ed..a8054a39b9a76 100644 --- a/ash/wm/dock/docked_window_layout_manager_unittest.cc +++ b/ash/wm/dock/docked_window_layout_manager_unittest.cc @@ -33,6 +33,7 @@ #include "ui/base/hit_test.h" #include "ui/gfx/screen.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -188,7 +189,7 @@ class DockedWindowLayoutManagerTest gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(window).work_area(); gfx::Point initial_location_in_screen = initial_location_in_parent_; - wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen); + ::wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen); // Drag the window left or right to the edge (or almost to it). if (edge == DOCKED_EDGE_LEFT) dx += work_area.x() - initial_location_in_screen.x(); diff --git a/ash/wm/dock/docked_window_resizer.cc b/ash/wm/dock/docked_window_resizer.cc index d6f04d0db0f24..14b58d408fe53 100644 --- a/ash/wm/dock/docked_window_resizer.cc +++ b/ash/wm/dock/docked_window_resizer.cc @@ -12,7 +12,6 @@ #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/dock/docked_window_layout_manager.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" @@ -30,6 +29,7 @@ #include "ui/base/ui_base_types.h" #include "ui/gfx/screen.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { namespace { @@ -61,7 +61,7 @@ DockedWindowResizer::Create(WindowResizer* next_window_resizer, void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) { last_location_ = location; - wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_); + ::wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_); if (!did_move_or_resize_) { did_move_or_resize_ = true; StartedDragging(); diff --git a/ash/wm/dock/docked_window_resizer_unittest.cc b/ash/wm/dock/docked_window_resizer_unittest.cc index 98c70b0c02613..acb54ff68a486 100644 --- a/ash/wm/dock/docked_window_resizer_unittest.cc +++ b/ash/wm/dock/docked_window_resizer_unittest.cc @@ -18,7 +18,6 @@ #include "ash/test/cursor_manager_test_api.h" #include "ash/test/shell_test_api.h" #include "ash/test/test_shelf_delegate.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/dock/docked_window_layout_manager.h" #include "ash/wm/drag_window_resizer.h" #include "ash/wm/panels/panel_layout_manager.h" @@ -34,6 +33,7 @@ #include "ui/base/ui_base_types.h" #include "ui/events/test/event_generator.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/window_util.h" namespace ash { @@ -105,7 +105,7 @@ class DockedWindowResizerTest aura::Window* root = ash::Shell::GetInstance()->display_controller()-> GetRootWindowForDisplayId(display.id()); gfx::Point origin = bounds.origin(); - wm::ConvertPointFromScreen(root, &origin); + ::wm::ConvertPointFromScreen(root, &origin); window->SetBounds(gfx::Rect(origin, bounds.size())); aura::client::ParentWindowWithContext(window, root, bounds); } @@ -216,7 +216,7 @@ class DockedWindowResizerTest gfx::Rect work_area = Shell::GetScreen()->GetDisplayNearestWindow(window).work_area(); gfx::Point initial_location_in_screen = initial_location_in_parent_; - wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen); + ::wm::ConvertPointToScreen(window->parent(), &initial_location_in_screen); // Drag the window left or right to the edge (or almost to it). if (edge == DOCKED_EDGE_LEFT) dx += work_area.x() - initial_location_in_screen.x(); diff --git a/ash/wm/drag_window_resizer.cc b/ash/wm/drag_window_resizer.cc index 15b0584675208..214dbc5c730c3 100644 --- a/ash/wm/drag_window_resizer.cc +++ b/ash/wm/drag_window_resizer.cc @@ -20,6 +20,7 @@ #include "ui/base/hit_test.h" #include "ui/base/ui_base_types.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/window_util.h" namespace ash { @@ -79,7 +80,7 @@ void DragWindowResizer::Drag(const gfx::Point& location, int event_flags) { // Show a phantom window for dragging in another root window. if (HasSecondaryRootWindow()) { gfx::Point location_in_screen = location; - wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen); + ::wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen); const bool in_original_root = wm::GetRootWindowAt(location_in_screen) == GetTarget()->GetRootWindow(); UpdateDragWindow(GetTarget()->bounds(), in_original_root); @@ -96,8 +97,8 @@ void DragWindowResizer::CompleteDrag() { // Check if the destination is another display. gfx::Point last_mouse_location_in_screen = last_mouse_location_; - wm::ConvertPointToScreen(GetTarget()->parent(), - &last_mouse_location_in_screen); + ::wm::ConvertPointToScreen(GetTarget()->parent(), + &last_mouse_location_in_screen); gfx::Screen* screen = Shell::GetScreen(); const gfx::Display dst_display = screen->GetDisplayNearestPoint(last_mouse_location_in_screen); diff --git a/ash/wm/gestures/overview_gesture_handler.cc b/ash/wm/gestures/overview_gesture_handler.cc index 642f122fab692..87c867b5eab2e 100644 --- a/ash/wm/gestures/overview_gesture_handler.cc +++ b/ash/wm/gestures/overview_gesture_handler.cc @@ -6,13 +6,13 @@ #include "ash/metrics/user_metrics_recorder.h" #include "ash/shell.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/overview/window_selector_controller.h" #include "ui/aura/window.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" #include "ui/gfx/rect.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { namespace { @@ -84,7 +84,7 @@ bool OverviewGestureHandler::ProcessGestureEvent( event.details().touch_points() == 1) { gfx::Point point_in_screen(event.location()); aura::Window* target = static_cast(event.target()); - wm::ConvertPointToScreen(target, &point_in_screen); + ::wm::ConvertPointToScreen(target, &point_in_screen); in_top_bezel_gesture_ = !Shell::GetScreen()->GetDisplayNearestPoint( point_in_screen).bounds().y() + kTopBezelExtraPixels > point_in_screen.y(); diff --git a/ash/wm/lock_layout_manager_unittest.cc b/ash/wm/lock_layout_manager_unittest.cc index 70984c49f9f19..b680ba21a9718 100644 --- a/ash/wm/lock_layout_manager_unittest.cc +++ b/ash/wm/lock_layout_manager_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ash/display/display_manager.h" #include "ash/root_window_controller.h" #include "ash/screen_util.h" #include "ash/shell.h" @@ -187,7 +188,8 @@ TEST_F(LockLayoutManagerTest, MaximizedFullscreenWindowBoundsAreEqualToScreen) { } TEST_F(LockLayoutManagerTest, KeyboardBounds) { - gfx::Rect screen_bounds = Shell::GetScreen()->GetPrimaryDisplay().bounds(); + gfx::Display primary_display = Shell::GetScreen()->GetPrimaryDisplay(); + gfx::Rect screen_bounds = primary_display.bounds(); views::Widget::InitParams widget_params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); @@ -203,8 +205,30 @@ TEST_F(LockLayoutManagerTest, KeyboardBounds) { keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED); ShowKeyboard(true); EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString()); + gfx::Rect keyboard_bounds = + keyboard::KeyboardController::GetInstance()->current_keyboard_bounds(); + EXPECT_NE(keyboard_bounds, gfx::Rect()); ShowKeyboard(false); + // When keyboard is hidden make sure that rotating the screen gives 100% of + // screen size to window. + // Repro steps for http://crbug.com/401667: + // 1. Set up login screen defaults: VK override disabled + // 2. Show/hide keyboard, make sure that no stale keyboard bounds are cached. + keyboard::SetKeyboardOverscrollOverride( + keyboard::KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED); + ShowKeyboard(true); + ShowKeyboard(false); + ash::DisplayManager* display_manager = + Shell::GetInstance()->display_manager(); + display_manager->SetDisplayRotation(primary_display.id(), + gfx::Display::ROTATE_90); + primary_display = Shell::GetScreen()->GetPrimaryDisplay(); + screen_bounds = primary_display.bounds(); + EXPECT_EQ(screen_bounds.ToString(), window->GetBoundsInScreen().ToString()); + display_manager->SetDisplayRotation(primary_display.id(), + gfx::Display::ROTATE_0); + // When virtual keyboard overscroll is disabled keyboard bounds do // affect window bounds. keyboard::SetKeyboardOverscrollOverride( @@ -212,6 +236,8 @@ TEST_F(LockLayoutManagerTest, KeyboardBounds) { ShowKeyboard(true); keyboard::KeyboardController* keyboard = keyboard::KeyboardController::GetInstance(); + primary_display = Shell::GetScreen()->GetPrimaryDisplay(); + screen_bounds = primary_display.bounds(); gfx::Rect target_bounds(screen_bounds); target_bounds.set_height(target_bounds.height() - keyboard->proxy()->GetKeyboardWindow()->bounds().height()); diff --git a/ash/wm/lock_state_controller_unittest.cc b/ash/wm/lock_state_controller_unittest.cc index eea43c8532bb9..80db34923ae14 100644 --- a/ash/wm/lock_state_controller_unittest.cc +++ b/ash/wm/lock_state_controller_unittest.cc @@ -965,7 +965,7 @@ TEST_F(LockStateControllerTest, IgnorePowerButtonIfScreenIsOff) { ReleasePowerButton(); } -#if defined(OS_CHROMEOS) && defined(USE_X11) +#if defined(OS_CHROMEOS) TEST_F(LockStateControllerTest, HonorPowerButtonInDockedMode) { ScopedVector modes; modes.push_back(new ui::DisplayMode(gfx::Size(1, 1), false, 60.0f)); diff --git a/ash/wm/lock_window_state.cc b/ash/wm/lock_window_state.cc index 5d38b59e8e51b..fa1684fda80da 100644 --- a/ash/wm/lock_window_state.cc +++ b/ash/wm/lock_window_state.cc @@ -167,12 +167,17 @@ void LockWindowState::UpdateBounds(wm::WindowState* window_state) { keyboard::KeyboardController::GetInstance(); gfx::Rect keyboard_bounds; - if (keyboard_controller && !keyboard::IsKeyboardOverscrollEnabled()) + if (keyboard_controller && + !keyboard::IsKeyboardOverscrollEnabled() && + keyboard_controller->keyboard_visible()) { keyboard_bounds = keyboard_controller->current_keyboard_bounds(); + } gfx::Rect bounds = ScreenUtil::GetDisplayBoundsInParent(window_state->window()); bounds.set_height(bounds.height() - keyboard_bounds.height()); + + VLOG(1) << "Updating window bounds to: " << bounds.ToString(); window_state->SetBoundsDirect(bounds); } diff --git a/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.cc b/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.cc index 09973337d078f..c78c406d729e4 100644 --- a/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.cc +++ b/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.cc @@ -36,6 +36,10 @@ const char kInternalTouchpadName[] = "Elan Touchpad"; // The name of the xinput device corresponding to the internal keyboard. const char kInternalKeyboardName[] = "AT Translated Set 2 keyboard"; +// Repeated key events have their source set to the core keyboard device. +// These must be disabled also until http://crbug.com/402898 is resolved. +const char kCoreKeyboardName[] = "Virtual core keyboard"; + // Device id used to indicate that a device has not been detected. const int kDeviceIdNone = -1; @@ -64,6 +68,7 @@ ScopedDisableInternalMouseAndKeyboardX11:: ScopedDisableInternalMouseAndKeyboardX11() : touchpad_device_id_(kDeviceIdNone), keyboard_device_id_(kDeviceIdNone), + core_keyboard_device_id_(kDeviceIdNone), last_mouse_location_(GetMouseLocationInScreen()) { ui::DeviceDataManagerX11* device_data_manager = @@ -81,6 +86,9 @@ ScopedDisableInternalMouseAndKeyboardX11:: } else if (device_name == kInternalKeyboardName) { keyboard_device_id_ = xi_dev_list[i].deviceid; device_data_manager->DisableDevice(keyboard_device_id_); + } else if (device_name == kCoreKeyboardName) { + core_keyboard_device_id_ = xi_dev_list[i].deviceid; + device_data_manager->DisableDevice(core_keyboard_device_id_); } } } @@ -91,7 +99,7 @@ ScopedDisableInternalMouseAndKeyboardX11:: excepted_keys->insert(ui::VKEY_VOLUME_DOWN); excepted_keys->insert(ui::VKEY_VOLUME_UP); excepted_keys->insert(ui::VKEY_POWER); - device_data_manager->DisableKeyboard(excepted_keys.Pass()); + device_data_manager->SetDisabledKeyboardAllowedKeys(excepted_keys.Pass()); ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this); } @@ -104,7 +112,10 @@ ScopedDisableInternalMouseAndKeyboardX11:: device_data_manager->EnableDevice(touchpad_device_id_); if (keyboard_device_id_ != kDeviceIdNone) device_data_manager->EnableDevice(keyboard_device_id_); - device_data_manager->EnableKeyboard(); + if (core_keyboard_device_id_ != kDeviceIdNone) + device_data_manager->EnableDevice(core_keyboard_device_id_); + device_data_manager->SetDisabledKeyboardAllowedKeys( + scoped_ptr >()); ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this); } diff --git a/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.h b/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.h index 2a1416b30677f..6f0d34f7befb3 100644 --- a/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.h +++ b/ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard_x11.h @@ -30,6 +30,7 @@ class ScopedDisableInternalMouseAndKeyboardX11 private: int touchpad_device_id_; int keyboard_device_id_; + int core_keyboard_device_id_; // Tracks the last known mouse cursor location caused before blocking the // internal touchpad or caused by an external mouse. diff --git a/ash/wm/overlay_event_filter.cc b/ash/wm/overlay_event_filter.cc index cd5a487650aa9..2e1c998370e3a 100644 --- a/ash/wm/overlay_event_filter.cc +++ b/ash/wm/overlay_event_filter.cc @@ -56,11 +56,14 @@ void OverlayEventFilter::OnLockStateChanged(bool locked) { } void OverlayEventFilter::Activate(Delegate* delegate) { + if (delegate_) + delegate_->Cancel(); delegate_ = delegate; } -void OverlayEventFilter::Deactivate() { - delegate_ = NULL; +void OverlayEventFilter::Deactivate(Delegate* delegate) { + if (delegate_ == delegate) + delegate_ = NULL; } void OverlayEventFilter::Cancel() { @@ -68,4 +71,8 @@ void OverlayEventFilter::Cancel() { delegate_->Cancel(); } +bool OverlayEventFilter::IsActive() { + return delegate_ != NULL; +} + } // namespace ash diff --git a/ash/wm/overlay_event_filter.h b/ash/wm/overlay_event_filter.h index 7787c45b1e447..c46215300426b 100644 --- a/ash/wm/overlay_event_filter.h +++ b/ash/wm/overlay_event_filter.h @@ -46,11 +46,14 @@ class ASH_EXPORT OverlayEventFilter : public ui::EventHandler, void Activate(Delegate* delegate); // Ends the filtering of events. - void Deactivate(); + void Deactivate(Delegate* delegate); // Cancels the partial screenshot UI. Do nothing if it's not activated. void Cancel(); + // Returns true if it's currently active. + bool IsActive(); + // ui::EventHandler overrides: virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE; @@ -60,6 +63,8 @@ class ASH_EXPORT OverlayEventFilter : public ui::EventHandler, virtual void OnLockStateChanged(bool locked) OVERRIDE; private: + FRIEND_TEST_ALL_PREFIXES(PartialScreenshotViewTest, DontStartOverOverlay); + Delegate* delegate_; DISALLOW_COPY_AND_ASSIGN(OverlayEventFilter); diff --git a/ash/wm/overlay_event_filter_unittest.cc b/ash/wm/overlay_event_filter_unittest.cc new file mode 100644 index 0000000000000..618e4d8d13aac --- /dev/null +++ b/ash/wm/overlay_event_filter_unittest.cc @@ -0,0 +1,37 @@ +// 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 "ash/wm/overlay_event_filter.h" + +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/test_overlay_delegate.h" + +namespace ash { +namespace test { + +typedef AshTestBase OverlayEventFilterTest; + +// Tests of the multiple overlay delegates attempt to activate, in that case +// Cancel() of the existing delegate should be called. +// See http://crbug.com/341958 +TEST_F(OverlayEventFilterTest, CancelAtActivating) { + TestOverlayDelegate d1; + TestOverlayDelegate d2; + + Shell::GetInstance()->overlay_filter()->Activate(&d1); + EXPECT_EQ(0, d1.GetCancelCountAndReset()); + EXPECT_EQ(0, d2.GetCancelCountAndReset()); + + Shell::GetInstance()->overlay_filter()->Activate(&d2); + EXPECT_EQ(1, d1.GetCancelCountAndReset()); + EXPECT_EQ(0, d2.GetCancelCountAndReset()); + + Shell::GetInstance()->overlay_filter()->Cancel(); + EXPECT_EQ(0, d1.GetCancelCountAndReset()); + EXPECT_EQ(1, d2.GetCancelCountAndReset()); +} + +} // namespace test +} // namespace ash diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc index d9f136fa54e94..e8751b8b09776 100644 --- a/ash/wm/overview/window_grid.cc +++ b/ash/wm/overview/window_grid.cc @@ -4,6 +4,7 @@ #include "ash/wm/overview/window_grid.h" +#include "ash/ash_switches.h" #include "ash/screen_util.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" @@ -13,6 +14,7 @@ #include "ash/wm/overview/window_selector_panels.h" #include "ash/wm/overview/window_selector_window.h" #include "ash/wm/window_state.h" +#include "base/command_line.h" #include "base/i18n/string_search.h" #include "base/memory/scoped_vector.h" #include "third_party/skia/include/core/SkColor.h" @@ -102,6 +104,10 @@ const int kOverviewSelectorTransitionMilliseconds = 100; const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK; const unsigned char kWindowOverviewSelectorOpacity = 128; +// The minimum amount of spacing between the bottom of the text filtering +// text field and the top of the selection widget on the first row of items. +const int kTextFilterBottomMargin = 5; + // Returns the vector for the fade in animation. gfx::Vector2d GetSlideVectorForFadeIn(WindowSelector::Direction direction, const gfx::Rect& bounds) { @@ -178,6 +184,17 @@ void WindowGrid::PositionWindows(bool animate) { ScreenUtil::GetDisplayWorkAreaBoundsInParent( Shell::GetContainer(root_window_, kShellWindowId_DefaultContainer))); + // If the text filtering feature is enabled, reserve space at the top for the + // text filtering textbox to appear. + if (!CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAshDisableTextFilteringInOverviewMode)) { + total_bounds.Inset( + 0, + WindowSelector::kTextFilterBottomEdge + kTextFilterBottomMargin, + 0, + 0); + } + // Find the minimum number of windows per row that will fit all of the // windows on screen. num_columns_ = std::max( diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc index ca6923f532faf..d1aba53162b1e 100644 --- a/ash/wm/overview/window_selector.cc +++ b/ash/wm/overview/window_selector.cc @@ -21,15 +21,21 @@ #include "base/auto_reset.h" #include "base/command_line.h" #include "base/metrics/histogram.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_observer.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event.h" +#include "ui/gfx/canvas.h" #include "ui/gfx/screen.h" +#include "ui/gfx/skia_util.h" #include "ui/views/border.h" #include "ui/views/controls/textfield/textfield.h" +#include "ui/views/layout/box_layout.h" #include "ui/wm/core/window_util.h" #include "ui/wm/public/activation_client.h" @@ -38,19 +44,27 @@ namespace ash { namespace { // The proportion of screen width that the text filter takes. -const float kTextFilterScreenProportion = 0.5; +const float kTextFilterScreenProportion = 0.25; -// The height of the text filter. -const int kTextFilterHeight = 50; +// The amount of padding surrounding the text in the text filtering textbox. +const int kTextFilterHorizontalPadding = 8; -// Solid shadow length from the text filter. -const int kVerticalShadowOffset = 1; +// The distance between the top of the screen and the top edge of the +// text filtering textbox. +const int kTextFilterDistanceFromTop = 32; -// Amount of blur applied to the text filter shadow. -const int kShadowBlur = 10; +// The height of the text filtering textbox. +const int kTextFilterHeight = 32; -// Text filter shadow color. -const SkColor kTextFilterShadow = 0xB0000000; +// The font style used for text filtering. +static const ::ui::ResourceBundle::FontStyle kTextFilterFontStyle = + ::ui::ResourceBundle::FontStyle::MediumFont; + +// The alpha value for the background of the text filtering textbox. +const unsigned char kTextFilterOpacity = 180; + +// The radius used for the rounded corners on the text filtering textbox. +const int kTextFilterCornerRadius = 1; // A comparator for locating a grid with a given root window. struct RootWindowGridComparator @@ -94,6 +108,42 @@ struct WindowSelectorItemForRoot const aura::Window* root_window; }; +// A View having rounded corners and a specified background color which is +// only painted within the bounds defined by the rounded corners. +// TODO(tdanderson): This duplicates code from RoundedImageView. Refactor these +// classes and move into ui/views. +class RoundedContainerView : public views::View { + public: + RoundedContainerView(int corner_radius, SkColor background) + : corner_radius_(corner_radius), + background_(background) { + } + + virtual ~RoundedContainerView() {} + + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + views::View::OnPaint(canvas); + + SkScalar radius = SkIntToScalar(corner_radius_); + const SkScalar kRadius[8] = {radius, radius, radius, radius, + radius, radius, radius, radius}; + SkPath path; + gfx::Rect bounds(size()); + path.addRoundRect(gfx::RectToSkRect(bounds), kRadius); + + SkPaint paint; + paint.setAntiAlias(true); + canvas->ClipPath(path, true); + canvas->DrawColor(background_); + } + + private: + int corner_radius_; + SkColor background_; + + DISALLOW_COPY_AND_ASSIGN(RoundedContainerView); +}; + // Triggers a shelf visibility update on all root window controllers. void UpdateShelfVisibility() { Shell::RootWindowControllerList root_window_controllers = @@ -118,22 +168,39 @@ views::Widget* CreateTextFilter(views::TextfieldController* controller, Shell::GetContainer(root_window, ash::kShellWindowId_OverlayContainer); params.accept_events = true; params.bounds = gfx::Rect( - root_window->bounds().width() / 2 * (1 - kTextFilterScreenProportion), 0, + root_window->bounds().width() / 2 * (1 - kTextFilterScreenProportion), + kTextFilterDistanceFromTop, root_window->bounds().width() * kTextFilterScreenProportion, kTextFilterHeight); widget->Init(params); + // Use |container| to specify the padding surrounding the text and to give + // the textfield rounded corners. + views::View* container = new RoundedContainerView( + kTextFilterCornerRadius, SkColorSetARGB(kTextFilterOpacity, 0, 0, 0)); + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + int text_height = bundle.GetFontList(kTextFilterFontStyle).GetHeight(); + DCHECK(text_height); + int vertical_padding = (kTextFilterHeight - text_height) / 2; + container->SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, + kTextFilterHorizontalPadding, + vertical_padding, + 0)); + views::Textfield* textfield = new views::Textfield; textfield->set_controller(controller); textfield->SetBackgroundColor(SK_ColorTRANSPARENT); textfield->SetBorder(views::Border::NullBorder()); textfield->SetTextColor(SK_ColorWHITE); - textfield->SetShadows(gfx::ShadowValues(1, gfx::ShadowValue( - gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kTextFilterShadow))); - widget->SetContentsView(textfield); + textfield->SetFontList(bundle.GetFontList(kTextFilterFontStyle)); + + container->AddChildView(textfield); + widget->SetContentsView(container); + // The textfield initially contains no text, so shift its position to be + // outside the visible bounds of the screen. gfx::Transform transform; - transform.Translate(0, -kTextFilterHeight); + transform.Translate(0, -WindowSelector::kTextFilterBottomEdge); widget->GetNativeWindow()->SetTransform(transform); widget->Show(); textfield->RequestFocus(); @@ -143,6 +210,9 @@ views::Widget* CreateTextFilter(views::TextfieldController* controller, } // namespace +const int WindowSelector::kTextFilterBottomEdge = + kTextFilterDistanceFromTop + kTextFilterHeight; + WindowSelector::WindowSelector(const WindowList& windows, WindowSelectorDelegate* delegate) : delegate_(delegate), @@ -420,10 +490,13 @@ void WindowSelector::ContentsChanged(views::Textfield* sender, gfx::Tween::FAST_OUT_LINEAR_IN : gfx::Tween::LINEAR_OUT_SLOW_IN); gfx::Transform transform; - if (should_show_selection_widget) + if (should_show_selection_widget) { transform.Translate(0, 0); - else - transform.Translate(0, -kTextFilterHeight); + text_filter_widget_->GetNativeWindow()->layer()->SetOpacity(1); + } else { + transform.Translate(0, -kTextFilterBottomEdge); + text_filter_widget_->GetNativeWindow()->layer()->SetOpacity(0); + } text_filter_widget_->GetNativeWindow()->SetTransform(transform); showing_selection_widget_ = should_show_selection_widget; diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h index 8eae7e0f356b4..678313c4f3a4c 100644 --- a/ash/wm/overview/window_selector.h +++ b/ash/wm/overview/window_selector.h @@ -52,6 +52,10 @@ class ASH_EXPORT WindowSelector public aura::client::ActivationChangeObserver, public views::TextfieldController { public: + // The distance between the top edge of the screen and the bottom edge of + // the text filtering textfield. + static const int kTextFilterBottomEdge; + enum Direction { LEFT, UP, diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc index 93dba4d88c0e8..adb8f1dcaaf22 100644 --- a/ash/wm/overview/window_selector_unittest.cc +++ b/ash/wm/overview/window_selector_unittest.cc @@ -439,7 +439,7 @@ TEST_F(WindowSelectorTest, SelectingHidesAppList) { gfx::Rect bounds(0, 0, 400, 400); scoped_ptr window1(CreateWindow(bounds)); scoped_ptr window2(CreateWindow(bounds)); - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); ToggleOverview(); EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility()); @@ -885,8 +885,8 @@ TEST_F(WindowSelectorTest, BasicTabKeyNavigation) { TEST_F(WindowSelectorTest, BasicArrowKeyNavigation) { if (!SupportsHostWindowResize()) return; - const size_t test_windows = 7; - UpdateDisplay("400x300"); + const size_t test_windows = 9; + UpdateDisplay("800x600"); ScopedVector windows; for (size_t i = test_windows; i > 0; i--) windows.push_back(CreateWindowWithId(gfx::Rect(0, 0, 100, 100), i)); @@ -897,22 +897,24 @@ TEST_F(WindowSelectorTest, BasicArrowKeyNavigation) { ui::VKEY_LEFT, ui::VKEY_UP }; - // Expected window layout: - // +-------+ +-------+ +-------+ - // | 1 | | 2 | | 3 | - // +-------+ +-------+ +-------+ - // +-------+ +-------+ +-------+ - // | 4 | | 5 | | 6 | - // +-------+ +-------+ +-------+ + // Expected window layout, assuming that the text filtering feature is + // enabled by default (i.e., --ash-disable-text-filtering-in-overview-mode + // is not being used). + // +-------+ +-------+ +-------+ +-------+ + // | 1 | | 2 | | 3 | | 4 | + // +-------+ +-------+ +-------+ +-------+ + // +-------+ +-------+ +-------+ +-------+ + // | 5 | | 6 | | 7 | | 8 | + // +-------+ +-------+ +-------+ +-------+ // +-------+ - // | 7 | + // | 9 | // +-------+ // Index for each window during a full loop plus wrapping around. int index_path_for_direction[][test_windows + 1] = { - {1, 2, 3, 4, 5, 6, 7, 1}, // Right - {1, 4, 7, 2, 5, 3, 6, 1}, // Down - {7, 6, 5, 4, 3, 2, 1, 7}, // Left - {6, 3, 5, 2, 7, 4, 1, 6} // Up + {1, 2, 3, 4, 5, 6, 7, 8, 9, 1}, // Right + {1, 5, 9, 2, 6, 3, 7, 4, 8, 1}, // Down + {9, 8, 7, 6, 5, 4, 3, 2, 1, 9}, // Left + {8, 4, 7, 3, 6, 2, 9, 5, 1, 8} // Up }; for (size_t key_index = 0; key_index < arraysize(arrow_keys); key_index++) { @@ -1136,4 +1138,61 @@ TEST_F(WindowSelectorTest, TextFilteringSelection) { EXPECT_EQ(GetSelectedWindow(), window2.get()); } +// Tests clicking on the desktop itself to cancel overview mode. +TEST_F(WindowSelectorTest, CancelOverviewOnMouseClick) { + // Overview disabled by default. + EXPECT_FALSE(IsSelecting()); + + // Point and bounds selected so that they don't intersect. This causes + // events located at the point to be passed to DesktopBackgroundController, + // and not the window. + gfx::Point point_in_background_page(0, 0); + gfx::Rect bounds(10, 10, 100, 100); + scoped_ptr window1(CreateWindow(bounds)); + ui::test::EventGenerator& generator = GetEventGenerator(); + // Move mouse to point in the background page. Sending an event here will pass + // it to the DesktopBackgroundController in both regular and overview mode. + generator.MoveMouseTo(point_in_background_page); + + // Clicking on the background page while not in overview should not toggle + // overview. + generator.ClickLeftButton(); + EXPECT_FALSE(IsSelecting()); + + // Switch to overview mode. + ToggleOverview(); + ASSERT_TRUE(IsSelecting()); + + // Click should now exit overview mode. + generator.ClickLeftButton(); + EXPECT_FALSE(IsSelecting()); +} + +// Tests tapping on the desktop itself to cancel overview mode. +TEST_F(WindowSelectorTest, CancelOverviewOnTap) { + // Overview disabled by default. + EXPECT_FALSE(IsSelecting()); + + // Point and bounds selected so that they don't intersect. This causes + // events located at the point to be passed to DesktopBackgroundController, + // and not the window. + gfx::Point point_in_background_page(0, 0); + gfx::Rect bounds(10, 10, 100, 100); + scoped_ptr window1(CreateWindow(bounds)); + ui::test::EventGenerator& generator = GetEventGenerator(); + + // Tapping on the background page while not in overview should not toggle + // overview. + generator.GestureTapAt(point_in_background_page); + EXPECT_FALSE(IsSelecting()); + + // Switch to overview mode. + ToggleOverview(); + ASSERT_TRUE(IsSelecting()); + + // Tap should now exit overview mode. + generator.GestureTapAt(point_in_background_page); + EXPECT_FALSE(IsSelecting()); +} + } // namespace ash diff --git a/ash/wm/overview/window_selector_window.cc b/ash/wm/overview/window_selector_window.cc index 1ece15497a3ae..ca2ecabb9ec83 100644 --- a/ash/wm/overview/window_selector_window.cc +++ b/ash/wm/overview/window_selector_window.cc @@ -8,9 +8,7 @@ #include "ash/shell.h" #include "ash/shell_window_ids.h" #include "ash/wm/overview/scoped_transform_overview_window.h" -#include "grit/ash_resources.h" #include "ui/aura/window.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/rect.h" #include "ui/gfx/transform.h" diff --git a/ash/wm/panels/panel_frame_view.cc b/ash/wm/panels/panel_frame_view.cc index a6e082270a051..24e37b5fa239f 100644 --- a/ash/wm/panels/panel_frame_view.cc +++ b/ash/wm/panels/panel_frame_view.cc @@ -7,9 +7,7 @@ #include "ash/frame/caption_buttons/frame_caption_button_container_view.h" #include "ash/frame/default_header_painter.h" #include "ash/frame/frame_border_hit_test_controller.h" -#include "grit/ash_resources.h" #include "ui/base/hit_test.h" -#include "ui/base/l10n/l10n_util.h" #include "ui/gfx/canvas.h" #include "ui/views/controls/image_view.h" #include "ui/views/widget/widget.h" diff --git a/ash/wm/panels/panel_window_resizer.cc b/ash/wm/panels/panel_window_resizer.cc index 14632b608c3d0..a57ac596ba9e5 100644 --- a/ash/wm/panels/panel_window_resizer.cc +++ b/ash/wm/panels/panel_window_resizer.cc @@ -11,7 +11,6 @@ #include "ash/shelf/shelf_widget.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/panels/panel_layout_manager.h" #include "ash/wm/window_state.h" #include "ash/wm/window_util.h" @@ -26,6 +25,7 @@ #include "ui/base/ui_base_types.h" #include "ui/gfx/screen.h" #include "ui/views/widget/widget.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -51,7 +51,7 @@ PanelWindowResizer::Create(WindowResizer* next_window_resizer, void PanelWindowResizer::Drag(const gfx::Point& location, int event_flags) { last_location_ = location; - wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_); + ::wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_); if (!did_move_or_resize_) { did_move_or_resize_ = true; StartedDragging(); diff --git a/ash/wm/partial_screenshot_view.cc b/ash/wm/partial_screenshot_view.cc index cec063db85f5e..b18c9fb530f0c 100644 --- a/ash/wm/partial_screenshot_view.cc +++ b/ash/wm/partial_screenshot_view.cc @@ -70,7 +70,7 @@ class PartialScreenshotView::OverlayDelegate private: virtual ~OverlayDelegate() { - Shell::GetInstance()->overlay_filter()->Deactivate(); + Shell::GetInstance()->overlay_filter()->Deactivate(this); } std::vector widgets_; @@ -83,6 +83,10 @@ std::vector PartialScreenshotView::StartPartialScreenshot( ScreenshotDelegate* screenshot_delegate) { std::vector views; + + if (Shell::GetInstance()->overlay_filter()->IsActive()) + return views; + OverlayDelegate* overlay_delegate = new OverlayDelegate(); aura::Window::Windows root_windows = Shell::GetAllRootWindows(); for (aura::Window::Windows::iterator it = root_windows.begin(); diff --git a/ash/wm/partial_screenshot_view_unittest.cc b/ash/wm/partial_screenshot_view_unittest.cc index 3b763b42ff504..26e27e3ca8bc1 100644 --- a/ash/wm/partial_screenshot_view_unittest.cc +++ b/ash/wm/partial_screenshot_view_unittest.cc @@ -8,33 +8,51 @@ #include "ash/shell.h" #include "ash/shell_window_ids.h" #include "ash/test/ash_test_base.h" +#include "ash/test/test_overlay_delegate.h" #include "ash/test/test_screenshot_delegate.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/events/test/event_generator.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_observer.h" namespace ash { -class PartialScreenshotViewTest : public test::AshTestBase { +class PartialScreenshotViewTest : public test::AshTestBase, + public views::WidgetObserver { public: PartialScreenshotViewTest() : view_(NULL) {} - virtual ~PartialScreenshotViewTest() {} + virtual ~PartialScreenshotViewTest() { + if (view_) + view_->GetWidget()->RemoveObserver(this); + } - virtual void SetUp() OVERRIDE { - test::AshTestBase::SetUp(); + void StartPartialScreenshot() { std::vector views = PartialScreenshotView::StartPartialScreenshot(GetScreenshotDelegate()); - ASSERT_EQ(1u, views.size()); - view_ = views[0]; + if (!views.empty()) { + view_ = views[0]; + view_->GetWidget()->AddObserver(this); + } } protected: PartialScreenshotView* view_; private: + // views::WidgetObserver: + virtual void OnWidgetDestroying(views::Widget* widget) OVERRIDE { + if (view_ && view_->GetWidget() == widget) + view_ = NULL; + widget->RemoveObserver(this); + } + DISALLOW_COPY_AND_ASSIGN(PartialScreenshotViewTest); }; TEST_F(PartialScreenshotViewTest, BasicMouse) { + StartPartialScreenshot(); + ASSERT_TRUE(view_); + ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); generator.MoveMouseTo(100, 100); @@ -53,6 +71,9 @@ TEST_F(PartialScreenshotViewTest, BasicMouse) { } TEST_F(PartialScreenshotViewTest, BasicTouch) { + StartPartialScreenshot(); + ASSERT_TRUE(view_); + ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow()); generator.set_current_location(gfx::Point(100,100)); @@ -74,4 +95,28 @@ TEST_F(PartialScreenshotViewTest, BasicTouch) { EXPECT_EQ("100,100 100x100", GetScreenshotDelegate()->last_rect().ToString()); } +// Partial screenshot session should not start when there is already another +// overlay. See: http://crbug.com/341958 +TEST_F(PartialScreenshotViewTest, DontStartOverOverlay) { + OverlayEventFilter* overlay_filter = Shell::GetInstance()->overlay_filter(); + test::TestOverlayDelegate delegate; + overlay_filter->Activate(&delegate); + EXPECT_EQ(&delegate, overlay_filter->delegate_); + + StartPartialScreenshot(); + EXPECT_TRUE(view_ == NULL); + EXPECT_EQ(&delegate, overlay_filter->delegate_); + overlay_filter->Deactivate(&delegate); + + StartPartialScreenshot(); + EXPECT_TRUE(view_ != NULL); + + // Someone else attempts to activate the overlay session, which should cancel + // the current partial screenshot session. + overlay_filter->Activate(&delegate); + RunAllPendingInMessageLoop(); + EXPECT_EQ(&delegate, overlay_filter->delegate_); + EXPECT_TRUE(view_ == NULL); +} + } // namespace ash diff --git a/ash/wm/window_cycle_controller_unittest.cc b/ash/wm/window_cycle_controller_unittest.cc index 8680472ffcc2c..06cc61ee5c92e 100644 --- a/ash/wm/window_cycle_controller_unittest.cc +++ b/ash/wm/window_cycle_controller_unittest.cc @@ -469,7 +469,7 @@ TEST_F(WindowCycleControllerTest, SelectingHidesAppList) { scoped_ptr window0(CreateTestWindowInShellWithId(0)); scoped_ptr window1(CreateTestWindowInShellWithId(1)); - Shell::GetInstance()->ToggleAppList(NULL); + Shell::GetInstance()->ShowAppList(NULL); EXPECT_TRUE(Shell::GetInstance()->GetAppListTargetVisibility()); controller->HandleCycleWindow(WindowCycleController::FORWARD); EXPECT_FALSE(Shell::GetInstance()->GetAppListTargetVisibility()); diff --git a/ash/wm/window_resizer.cc b/ash/wm/window_resizer.cc index 08ff0c42fec07..fbdc29c7e69d4 100644 --- a/ash/wm/window_resizer.cc +++ b/ash/wm/window_resizer.cc @@ -20,6 +20,7 @@ #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/display.h" #include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" namespace ash { @@ -204,7 +205,7 @@ gfx::Rect WindowResizer::CalculateBoundsForDrag( // for the current display but the window can move to a different one. aura::Window* parent = GetTarget()->parent(); gfx::Point passed_location_in_screen(passed_location); - wm::ConvertPointToScreen(parent, &passed_location_in_screen); + ::wm::ConvertPointToScreen(parent, &passed_location_in_screen); gfx::Rect near_passed_location(passed_location_in_screen, gfx::Size()); // Use a pointer location (matching the logic in DragWindowResizer) to // calculate the target display after the drag. diff --git a/ash/wm/workspace/multi_window_resize_controller.cc b/ash/wm/workspace/multi_window_resize_controller.cc index d03cfa168f2a0..34af90b4b6f44 100644 --- a/ash/wm/workspace/multi_window_resize_controller.cc +++ b/ash/wm/workspace/multi_window_resize_controller.cc @@ -7,7 +7,6 @@ #include "ash/screen_util.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/window_animations.h" #include "ash/wm/workspace/workspace_event_handler.h" #include "ash/wm/workspace/workspace_window_resizer.h" @@ -25,6 +24,7 @@ #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/wm/core/compound_event_filter.h" +#include "ui/wm/core/coordinate_conversion.h" using aura::Window; @@ -228,7 +228,7 @@ MultiWindowResizeController::DetermineWindowsFromScreenPoint( aura::Window* window) const { gfx::Point mouse_location( gfx::Screen::GetScreenFor(window)->GetCursorScreenPoint()); - wm::ConvertPointFromScreen(window, &mouse_location); + ::wm::ConvertPointFromScreen(window, &mouse_location); const int component = window->delegate()->GetNonClientComponent(mouse_location); return DetermineWindows(window, component, mouse_location); diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index c82acf2ebd344..395c2adbdc80f 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -15,7 +15,6 @@ #include "ash/screen_util.h" #include "ash/shell.h" #include "ash/shell_window_ids.h" -#include "ash/wm/coordinate_conversion.h" #include "ash/wm/default_window_resizer.h" #include "ash/wm/dock/docked_window_layout_manager.h" #include "ash/wm/dock/docked_window_resizer.h" @@ -37,6 +36,7 @@ #include "ui/compositor/layer.h" #include "ui/gfx/screen.h" #include "ui/gfx/transform.h" +#include "ui/wm/core/coordinate_conversion.h" #include "ui/wm/core/window_util.h" #include "ui/wm/public/window_types.h" @@ -371,7 +371,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent, } gfx::Point location_in_screen = location_in_parent; - wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen); + ::wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen); aura::Window* root = NULL; gfx::Display display = @@ -771,8 +771,8 @@ void WorkspaceWindowResizer::AdjustBoundsForMainWindow( int sticky_size, gfx::Rect* bounds) { gfx::Point last_mouse_location_in_screen = last_mouse_location_; - wm::ConvertPointToScreen(GetTarget()->parent(), - &last_mouse_location_in_screen); + ::wm::ConvertPointToScreen(GetTarget()->parent(), + &last_mouse_location_in_screen); gfx::Display display = Shell::GetScreen()->GetDisplayNearestPoint( last_mouse_location_in_screen); gfx::Rect work_area = diff --git a/athena/activity/DEPS b/athena/activity/DEPS index aafc6d4c6d6e9..78a43714d0cc0 100644 --- a/athena/activity/DEPS +++ b/athena/activity/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+athena/screen", "+ui/aura", + "+ui/base", "+ui/views", ] diff --git a/athena/activity/activity_frame_view.cc b/athena/activity/activity_frame_view.cc index dac849986eba6..08aeb3d12fa6d 100644 --- a/athena/activity/activity_frame_view.cc +++ b/athena/activity/activity_frame_view.cc @@ -8,7 +8,9 @@ #include #include "athena/activity/public/activity_view_model.h" +#include "ui/base/hit_test.h" #include "ui/views/background.h" +#include "ui/views/border.h" #include "ui/views/controls/label.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" @@ -26,9 +28,9 @@ ActivityFrameView::ActivityFrameView(views::Widget* frame, ActivityViewModel* view_model) : frame_(frame), view_model_(view_model), title_(new views::Label) { title_->SetHorizontalAlignment(gfx::ALIGN_CENTER); - const gfx::FontList& font_list = title_->font_list(); - title_->SetFontList(font_list.Derive(1, gfx::Font::BOLD)); - title_->SetEnabledColor(SK_ColorBLACK); + title_->SetEnabledColor(SkColorSetA(SK_ColorBLACK, 0xe5)); + title_->SetBorder(views::Border::CreateSolidSidedBorder(0, 0, 1, 0, + SkColorSetA(SK_ColorGRAY, 0x7f))); AddChildView(title_); UpdateWindowTitle(); } @@ -41,18 +43,24 @@ ActivityFrameView::~ActivityFrameView() { gfx::Rect ActivityFrameView::GetBoundsForClientView() const { gfx::Rect client_bounds = bounds(); - client_bounds.Inset(0, NonClientTopBorderHeight(), 0, 0); + if (view_model_->UsesFrame()) + client_bounds.Inset(0, NonClientTopBorderHeight(), 0, 0); return client_bounds; } gfx::Rect ActivityFrameView::GetWindowBoundsForClientBounds( const gfx::Rect& client_bounds) const { gfx::Rect window_bounds = client_bounds; - window_bounds.Inset(0, -NonClientTopBorderHeight(), 0, 0); + if (view_model_->UsesFrame()) + window_bounds.Inset(0, -NonClientTopBorderHeight(), 0, 0); return window_bounds; } int ActivityFrameView::NonClientHitTest(const gfx::Point& point) { + if (frame_->IsFullscreen()) + return 0; + if (title_->bounds().Contains(point)) + return HTCAPTION; return 0; } @@ -67,6 +75,9 @@ void ActivityFrameView::UpdateWindowIcon() { } void ActivityFrameView::UpdateWindowTitle() { + if (!view_model_->UsesFrame()) + return; + SkColor bgcolor = view_model_->GetRepresentativeColor(); title_->set_background(views::Background::CreateSolidBackground(bgcolor)); title_->SetBackgroundColor(bgcolor); @@ -96,7 +107,8 @@ void ActivityFrameView::Layout() { // ActivityFrameView, private: int ActivityFrameView::NonClientTopBorderHeight() const { - return frame_->IsFullscreen() ? 0 : title_->GetPreferredSize().height(); + const int kDefaultTitleHeight = 25; + return frame_->IsFullscreen() ? 0 : kDefaultTitleHeight; } } // namespace ash diff --git a/athena/activity/activity_view_manager_impl.cc b/athena/activity/activity_view_manager_impl.cc index 212586835c367..6974fe7dbed06 100644 --- a/athena/activity/activity_view_manager_impl.cc +++ b/athena/activity/activity_view_manager_impl.cc @@ -24,10 +24,7 @@ class ActivityWidget { : activity_(activity), widget_(NULL) { ActivityViewModel* view_model = activity->GetActivityViewModel(); widget_ = new views::Widget; - views::Widget::InitParams params( - view_model->UsesFrame() - ? views::Widget::InitParams::TYPE_WINDOW - : views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW); params.context = ScreenManager::Get()->GetContext(); params.delegate = new ActivityWidgetDelegate(view_model); params.activatable = views::Widget::InitParams::ACTIVATABLE_YES; diff --git a/athena/athena.gyp b/athena/athena.gyp index ea119bc81b2f1..da4bd4d755528 100644 --- a/athena/athena.gyp +++ b/athena/athena.gyp @@ -11,13 +11,18 @@ 'target_name': 'athena_lib', 'type': '<(component)', 'dependencies': [ + '../base/base.gyp:test_support_base', + '../extensions/shell/app_shell.gyp:app_shell_version_header', + '../ipc/ipc.gyp:ipc', '../skia/skia.gyp:skia', '../ui/accessibility/accessibility.gyp:ax_gen', '../ui/app_list/app_list.gyp:app_list', '../ui/aura/aura.gyp:aura', + '../ui/aura/aura.gyp:aura_test_support', '../ui/events/events.gyp:events_base', '../ui/strings/ui_strings.gyp:ui_strings', '../ui/views/views.gyp:views', + 'resources/athena_resources.gyp:athena_resources', ], 'defines': [ 'ATHENA_IMPLEMENTATION', @@ -45,8 +50,8 @@ 'common/switches.h', 'home/app_list_view_delegate.cc', 'home/app_list_view_delegate.h', - 'home/bottom_home_view.cc', - 'home/bottom_home_view.h', + 'home/athena_start_page_view.cc', + 'home/athena_start_page_view.h', 'home/home_card_impl.cc', 'home/minimized_home.cc', 'home/minimized_home.h', @@ -63,16 +68,27 @@ 'screen/screen_accelerator_handler.cc', 'screen/screen_accelerator_handler.h', 'screen/screen_manager_impl.cc', + 'system/device_socket_listener.cc', + 'system/device_socket_listener.h', + 'system/orientation_controller.cc', + 'system/orientation_controller.h', 'system/power_button_controller.cc', 'system/power_button_controller.h', 'system/public/system_ui.h', 'system/system_ui_impl.cc', - 'wm/public/window_manager.h', - 'wm/public/window_manager_observer.h', 'wm/bezel_controller.cc', 'wm/bezel_controller.h', + 'wm/overview_toolbar.cc', + 'wm/overview_toolbar.h', + 'wm/public/window_list_provider.h', + 'wm/public/window_manager.h', + 'wm/public/window_manager_observer.h', 'wm/split_view_controller.cc', 'wm/split_view_controller.h', + 'wm/title_drag_controller.cc', + 'wm/title_drag_controller.h', + 'wm/window_list_provider_impl.cc', + 'wm/window_list_provider_impl.h', 'wm/window_manager_impl.cc', 'wm/window_overview_mode.cc', 'wm/window_overview_mode.h', @@ -84,6 +100,8 @@ 'dependencies': [ 'athena_lib', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../components/components.gyp:renderer_context_menu', + '../components/components.gyp:web_modal', '../content/content.gyp:content_browser', '../ui/app_list/app_list.gyp:app_list', '../ui/keyboard/keyboard.gyp:keyboard', @@ -98,12 +116,16 @@ 'sources': [ 'content/public/content_activity_factory.h', 'content/public/content_app_model_builder.h', + 'content/public/web_contents_view_delegate_creator.h', 'content/content_activity_factory.cc', 'content/content_app_model_builder.cc', 'content/app_activity.h', 'content/app_activity.cc', + 'content/render_view_context_menu_impl.cc', + 'content/render_view_context_menu_impl.h', 'content/web_activity.h', 'content/web_activity.cc', + 'content/web_contents_view_delegate_factory_impl.cc', 'virtual_keyboard/public/virtual_keyboard_manager.h', 'virtual_keyboard/virtual_keyboard_manager_impl.cc', ], @@ -124,6 +146,7 @@ '../ui/views/views.gyp:views', '../ui/wm/wm.gyp:wm', '../url/url.gyp:url_lib', + 'athena_content_lib', 'athena_lib', 'resources/athena_resources.gyp:athena_resources', ], @@ -160,6 +183,8 @@ 'home/home_card_unittest.cc', 'input/accelerator_manager_unittest.cc', 'screen/screen_manager_unittest.cc', + 'wm/split_view_controller_unittest.cc', + 'wm/window_list_provider_impl_unittest.cc', 'wm/window_manager_unittest.cc', ], } diff --git a/athena/common/DEPS b/athena/common/DEPS index 716fe604be9af..87555656e91d4 100644 --- a/athena/common/DEPS +++ b/athena/common/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+athena/athena_export.h", "+ui/aura", + "+ui/compositor", ] diff --git a/athena/content/DEPS b/athena/content/DEPS index 5401a42f0efe7..0d15742f86c2a 100644 --- a/athena/content/DEPS +++ b/athena/content/DEPS @@ -2,11 +2,18 @@ include_rules = [ "+athena/activity/public", "+athena/home/public", "+athena/input/public", + "+components/renderer_context_menu", + "+components/web_modal", "+content/public", "+extensions/browser", "+extensions/common", "+extensions/shell/browser", "+ui/app_list", + "+ui/aura", "+ui/gfx", "+ui/views", + + # No inclusion of WebKit from the athena main process, other than + # strictly enum/POD, header-only types, and some selected common code. + "+third_party/WebKit/public/web/WebContextMenuData.h", ] diff --git a/athena/content/content_app_model_builder.cc b/athena/content/content_app_model_builder.cc index d8a361615bb02..a84d3c2901f44 100644 --- a/athena/content/content_app_model_builder.cc +++ b/athena/content/content_app_model_builder.cc @@ -107,6 +107,13 @@ ContentAppModelBuilder::~ContentAppModelBuilder() { } void ContentAppModelBuilder::PopulateApps(app_list::AppListModel* model) { + ShellExtensionSystem* extension_system = + GetShellExtensionSystem(browser_context_); + if (extension_system && extension_system->extension()) { + model->AddItem(scoped_ptr( + new AppItem(extension_system->extension(), browser_context_))); + } + model->AddItem(scoped_ptr(new DummyItem( "mail", GURL("http://gmail.com/"), SK_ColorRED, browser_context_))); model->AddItem(scoped_ptr(new DummyItem( @@ -120,13 +127,6 @@ void ContentAppModelBuilder::PopulateApps(app_list::AppListModel* model) { model->AddItem(scoped_ptr(new DummyItem( "contact", GURL("https://www.google.com/contacts"), SK_ColorCYAN, browser_context_))); - - ShellExtensionSystem* extension_system = - GetShellExtensionSystem(browser_context_); - if (extension_system && extension_system->extension()) { - model->AddItem(scoped_ptr( - new AppItem(extension_system->extension(), browser_context_))); - } } } // namespace athena diff --git a/athena/content/public/web_contents_view_delegate_creator.h b/athena/content/public/web_contents_view_delegate_creator.h new file mode 100644 index 0000000000000..1f56fc4ae0754 --- /dev/null +++ b/athena/content/public/web_contents_view_delegate_creator.h @@ -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. + +#ifndef ATHENA_CONTENT_PUBLIC_WEB_CONTENTS_VIEW_DELEGATE_FACTORY_H_ +#define ATHENA_CONTENT_PUBLIC_WEB_CONTENTS_VIEW_DELEGATE_FACTORY_H_ + +#include "athena/athena_export.h" + +namespace content { +class WebContents; +class WebContentsViewDelegate; +} + +namespace athena { + +ATHENA_EXPORT content::WebContentsViewDelegate* CreateWebContentsViewDelegate( + content::WebContents* web_contents); + +} // namespace athena + +#endif // ATHENA_CONTENT_PUBLIC_WEB_CONTENTS_VIEW_DELEGATE_FACTORY_H_ diff --git a/athena/content/render_view_context_menu_impl.cc b/athena/content/render_view_context_menu_impl.cc new file mode 100644 index 0000000000000..6c2f2b60a5be0 --- /dev/null +++ b/athena/content/render_view_context_menu_impl.cc @@ -0,0 +1,267 @@ +// 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 "athena/content/render_view_context_menu_impl.h" + +#include "base/strings/utf_string_conversions.h" +#include "components/renderer_context_menu/context_menu_content_type.h" +#include "components/renderer_context_menu/views/toolkit_delegate_views.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/web_contents.h" +#include "third_party/WebKit/public/web/WebContextMenuData.h" + +namespace athena { +using blink::WebContextMenuData; + +namespace { + +enum { + // Nativation + CMD_BACK = 0, + CMD_FORWARD, + CMD_RELOAD, + CMD_VIEW_SOURCE, + + // Link + CMD_OPEN_LINK_NEW_ACTIVITY, + + // Edit + CMD_UNDO, + CMD_REDO, + CMD_CUT, + CMD_COPY, + CMD_PASTE, + CMD_PASTE_AND_MATCH_STYLE, + CMD_DELETE, + CMD_SELECT_ALL, + CMD_LAST, +}; + +// Max number of custom command ids allowd. +const int kNumCustomCommandIds = 1000; + +// TODO(oshima): Move IDS for context menus to components/renderer_context_menu +// and replace hardcoded strings below. +void AppendPageItems(ui::SimpleMenuModel* menu_model) { + menu_model->AddItem(CMD_BACK, base::ASCIIToUTF16("Back")); + menu_model->AddItem(CMD_FORWARD, base::ASCIIToUTF16("Forward")); + menu_model->AddItem(CMD_RELOAD, base::ASCIIToUTF16("Reload")); + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + menu_model->AddItem(CMD_VIEW_SOURCE, base::ASCIIToUTF16("View Source")); +} + +void AppendLinkItems(const content::ContextMenuParams& params, + ui::SimpleMenuModel* menu_model) { + if (!params.link_url.is_empty()) + menu_model->AddItem(CMD_OPEN_LINK_NEW_ACTIVITY, + base::ASCIIToUTF16("Open Link In New Activity")); +} + +void AppendEditableItems(ui::SimpleMenuModel* menu_model) { + menu_model->AddItem(CMD_UNDO, base::ASCIIToUTF16("Undo")); + menu_model->AddItem(CMD_REDO, base::ASCIIToUTF16("Redo")); + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + menu_model->AddItem(CMD_CUT, base::ASCIIToUTF16("Cut")); + menu_model->AddItem(CMD_COPY, base::ASCIIToUTF16("Copy")); + menu_model->AddItem(CMD_PASTE, base::ASCIIToUTF16("Paste")); + menu_model->AddItem(CMD_PASTE_AND_MATCH_STYLE, + base::ASCIIToUTF16("Paste as plain text")); + menu_model->AddItem(CMD_DELETE, base::ASCIIToUTF16("Delete")); + menu_model->AddSeparator(ui::NORMAL_SEPARATOR); + menu_model->AddItem(CMD_SELECT_ALL, base::ASCIIToUTF16("Select All")); +} + +} // namespace + +RenderViewContextMenuImpl::RenderViewContextMenuImpl( + content::RenderFrameHost* render_frame_host, + const content::ContextMenuParams& params) + : RenderViewContextMenuBase(render_frame_host, params) { + SetContentCustomCommandIdRange(CMD_LAST, CMD_LAST + kNumCustomCommandIds); + // TODO(oshima): Support other types + set_content_type( + new ContextMenuContentType(source_web_contents_, params, true)); + set_toolkit_delegate(scoped_ptr(new ToolkitDelegateViews)); +} + +RenderViewContextMenuImpl::~RenderViewContextMenuImpl() { +} + +void RenderViewContextMenuImpl::RunMenuAt(views::Widget* parent, + const gfx::Point& point, + ui::MenuSourceType type) { + static_cast(toolkit_delegate()) + ->RunMenuAt(parent, point, type); +} + +void RenderViewContextMenuImpl::InitMenu() { + RenderViewContextMenuBase::InitMenu(); + bool needs_separator = false; + if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_PAGE)) { + AppendPageItems(&menu_model_); + needs_separator = true; + } + + if (content_type_->SupportsGroup(ContextMenuContentType::ITEM_GROUP_LINK)) { + if (needs_separator) + AddSeparator(); + AppendLinkItems(params_, &menu_model_); + needs_separator = true; + } + + if (content_type_->SupportsGroup( + ContextMenuContentType::ITEM_GROUP_EDITABLE)) { + if (needs_separator) + AddSeparator(); + AppendEditableItems(&menu_model_); + } +} + +void RenderViewContextMenuImpl::RecordShownItem(int id) { + // TODO(oshima): Imelement UMA stats. crbug.com/401673 + NOTIMPLEMENTED(); +} + +void RenderViewContextMenuImpl::RecordUsedItem(int id) { + // TODO(oshima): Imelement UMA stats. crbug.com/401673 + NOTIMPLEMENTED(); +} + +#if defined(ENABLE_PLUGINS) +void RenderViewContextMenuImpl::HandleAuthorizeAllPlugins() { +} +#endif + +void RenderViewContextMenuImpl::NotifyMenuShown() { +} + +void RenderViewContextMenuImpl::NotifyURLOpened( + const GURL& url, + content::WebContents* new_contents) { +} + +bool RenderViewContextMenuImpl::GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) { + NOTIMPLEMENTED(); + return false; +} + +bool RenderViewContextMenuImpl::IsCommandIdChecked(int command_id) const { + return false; +} + +bool RenderViewContextMenuImpl::IsCommandIdEnabled(int command_id) const { + { + bool enabled = false; + if (RenderViewContextMenuBase::IsCommandIdKnown(command_id, &enabled)) + return enabled; + } + switch (command_id) { + // Navigation + case CMD_BACK: + return source_web_contents_->GetController().CanGoBack(); + case CMD_FORWARD: + return source_web_contents_->GetController().CanGoForward(); + case CMD_RELOAD: + return true; + case CMD_VIEW_SOURCE: + return source_web_contents_->GetController().CanViewSource(); + + // Link + case CMD_OPEN_LINK_NEW_ACTIVITY: + return params_.link_url.is_valid(); + + // Editable + case CMD_UNDO: + return !!(params_.edit_flags & WebContextMenuData::CanUndo); + + case CMD_REDO: + return !!(params_.edit_flags & WebContextMenuData::CanRedo); + + case CMD_CUT: + return !!(params_.edit_flags & WebContextMenuData::CanCut); + + case CMD_COPY: + return !!(params_.edit_flags & WebContextMenuData::CanCopy); + + case CMD_PASTE: + case CMD_PASTE_AND_MATCH_STYLE: + return !!(params_.edit_flags & WebContextMenuData::CanPaste); + + case CMD_DELETE: + return !!(params_.edit_flags & WebContextMenuData::CanDelete); + + case CMD_SELECT_ALL: + return !!(params_.edit_flags & WebContextMenuData::CanSelectAll); + } + return false; +} + +void RenderViewContextMenuImpl::ExecuteCommand(int command_id, + int event_flags) { + RenderViewContextMenuBase::ExecuteCommand(command_id, event_flags); + if (command_executed_) + return; + command_executed_ = true; + switch (command_id) { + // Navigation + case CMD_BACK: + source_web_contents_->GetController().GoBack(); + break; + case CMD_FORWARD: + source_web_contents_->GetController().GoForward(); + break; + case CMD_RELOAD: + source_web_contents_->GetController().Reload(true); + break; + case CMD_VIEW_SOURCE: + source_web_contents_->ViewSource(); + break; + + // Link + case CMD_OPEN_LINK_NEW_ACTIVITY: + OpenURL( + params_.link_url, + params_.frame_url.is_empty() ? params_.page_url : params_.frame_url, + NEW_FOREGROUND_TAB, + content::PAGE_TRANSITION_LINK); + break; + + // Editable + case CMD_UNDO: + source_web_contents_->Undo(); + break; + + case CMD_REDO: + source_web_contents_->Redo(); + break; + + case CMD_CUT: + source_web_contents_->Cut(); + break; + + case CMD_COPY: + source_web_contents_->Copy(); + break; + + case CMD_PASTE: + source_web_contents_->Paste(); + break; + + case CMD_PASTE_AND_MATCH_STYLE: + source_web_contents_->PasteAndMatchStyle(); + break; + + case CMD_DELETE: + source_web_contents_->Delete(); + break; + + case CMD_SELECT_ALL: + source_web_contents_->SelectAll(); + break; + } +} + +} // namespace athena diff --git a/athena/content/render_view_context_menu_impl.h b/athena/content/render_view_context_menu_impl.h new file mode 100644 index 0000000000000..c12c65f72606b --- /dev/null +++ b/athena/content/render_view_context_menu_impl.h @@ -0,0 +1,55 @@ +// 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 ATHENA_CONTENT_RENDER_VIEW_CONTEXT_MENU_IMPL_H_ +#define ATHENA_CONTENT_RENDER_VIEW_CONTEXT_MENU_IMPL_H_ + +#include "components/renderer_context_menu/render_view_context_menu_base.h" + +namespace views { +class Widget; +} + +namespace gfx { +class Point; +} + +namespace athena { + +class RenderViewContextMenuImpl : public RenderViewContextMenuBase { + public: + RenderViewContextMenuImpl(content::RenderFrameHost* render_frame_host, + const content::ContextMenuParams& params); + virtual ~RenderViewContextMenuImpl(); + + void RunMenuAt(views::Widget* parent, + const gfx::Point& point, + ui::MenuSourceType type); + + private: + // RenderViewContextMenuBase: + virtual void InitMenu() OVERRIDE; + virtual void RecordShownItem(int id) OVERRIDE; + virtual void RecordUsedItem(int id) OVERRIDE; +#if defined(ENABLE_PLUGINS) + virtual void HandleAuthorizeAllPlugins() OVERRIDE; +#endif + virtual void NotifyMenuShown() OVERRIDE; + virtual void NotifyURLOpened(const GURL& url, + content::WebContents* new_contents) OVERRIDE; + + // ui::SimpleMenuModel: + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE; + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(RenderViewContextMenuImpl); +}; + +} // namespace athena + +#endif // ATHENA_CONTENT_RENDER_VIEW_CONTEXT_MENU_IMPL_H_ diff --git a/athena/content/web_activity.cc b/athena/content/web_activity.cc index 0a5e0b5a1140e..35e0d71553d05 100644 --- a/athena/content/web_activity.cc +++ b/athena/content/web_activity.cc @@ -7,6 +7,7 @@ #include "athena/activity/public/activity_factory.h" #include "athena/activity/public/activity_manager.h" #include "athena/input/public/accelerator_manager.h" +#include "base/strings/utf_string_conversions.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/web_contents.h" @@ -120,6 +121,9 @@ class WebActivityController : public AcceleratorHandler { DISALLOW_COPY_AND_ASSIGN(WebActivityController); }; +const SkColor kDefaultTitleColor = SkColorSetRGB(0xf2, 0xf2, 0xf2); +const SkColor kDefaultUnavailableColor = SkColorSetRGB(0xbb, 0x77, 0x77); + } // namespace // A web view for athena's web activity. Note that AthenaWebView will create its @@ -261,6 +265,7 @@ WebActivity::WebActivity(content::BrowserContext* browser_context, : browser_context_(browser_context), url_(url), web_view_(NULL), + title_color_(kDefaultTitleColor), current_state_(ACTIVITY_UNLOADED) { } @@ -349,11 +354,13 @@ void WebActivity::Init() { SkColor WebActivity::GetRepresentativeColor() const { // TODO(sad): Compute the color from the favicon. - return web_view_ ? SK_ColorGRAY : SkColorSetRGB(0xbb, 0x77, 0x77); + return web_view_ ? title_color_ : kDefaultUnavailableColor; } base::string16 WebActivity::GetTitle() const { - return web_view_ ? web_view_->GetWebContents()->GetTitle() : base::string16(); + return web_view_ ? base::UTF8ToUTF16( + web_view_->GetWebContents()->GetVisibleURL().host()) + : base::string16(); } bool WebActivity::UsesFrame() const { @@ -389,4 +396,8 @@ void WebActivity::DidUpdateFaviconURL( ActivityManager::Get()->UpdateActivity(this); } +void WebActivity::DidChangeThemeColor(SkColor theme_color) { + title_color_ = theme_color; +} + } // namespace athena diff --git a/athena/content/web_activity.h b/athena/content/web_activity.h index 45407a487f8d7..6e3f41ba0e0eb 100644 --- a/athena/content/web_activity.h +++ b/athena/content/web_activity.h @@ -54,11 +54,13 @@ class WebActivity : public Activity, bool explicit_set) OVERRIDE; virtual void DidUpdateFaviconURL( const std::vector& candidates) OVERRIDE; + virtual void DidChangeThemeColor(SkColor theme_color) OVERRIDE; private: content::BrowserContext* browser_context_; const GURL url_; AthenaWebView* web_view_; + SkColor title_color_; // The current state for this activity. ActivityState current_state_; diff --git a/athena/content/web_contents_view_delegate_factory_impl.cc b/athena/content/web_contents_view_delegate_factory_impl.cc new file mode 100644 index 0000000000000..d378722917413 --- /dev/null +++ b/athena/content/web_contents_view_delegate_factory_impl.cc @@ -0,0 +1,159 @@ +// 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 "athena/content/public/web_contents_view_delegate_creator.h" + +#include "athena/content/render_view_context_menu_impl.h" +#include "components/web_modal/popup_manager.h" +#include "components/web_modal/single_web_contents_dialog_manager.h" +#include "components/web_modal/web_contents_modal_dialog_host.h" +#include "components/web_modal/web_contents_modal_dialog_manager.h" +#include "content/public/browser/render_widget_host_view.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" +#include "content/public/browser/web_contents_view_delegate.h" +#include "ui/aura/client/screen_position_client.h" +#include "ui/aura/window.h" +#include "ui/views/widget/widget.h" + +namespace athena { +namespace { + +class WebContentsViewDelegateImpl : public content::WebContentsViewDelegate { + public: + explicit WebContentsViewDelegateImpl(content::WebContents* web_contents) + : web_contents_(web_contents) {} + virtual ~WebContentsViewDelegateImpl() {} + + virtual content::WebDragDestDelegate* GetDragDestDelegate() OVERRIDE { + // TODO(oshima): crbug.com/401610 + return NULL; + } + + virtual bool Focus() OVERRIDE { + web_modal::PopupManager* popup_manager = + web_modal::PopupManager::FromWebContents(web_contents_); + if (popup_manager) + popup_manager->WasFocused(web_contents_); + return false; + } + + virtual void TakeFocus(bool reverse) OVERRIDE {} + virtual void StoreFocus() OVERRIDE {} + virtual void RestoreFocus() OVERRIDE {} + + virtual void ShowContextMenu( + content::RenderFrameHost* render_frame_host, + const content::ContextMenuParams& params) OVERRIDE { + ShowMenu(BuildMenu( + content::WebContents::FromRenderFrameHost(render_frame_host), params)); + } + + virtual void SizeChanged(const gfx::Size& size) OVERRIDE { + // TODO(oshima|sadrul): Implement this when sad_tab is componentized. + // See c/b/ui/views/tab_contents/chrome_web_contents_view_delegate_views.cc + } + + scoped_ptr BuildMenu( + content::WebContents* web_contents, + const content::ContextMenuParams& params) { + scoped_ptr menu; + content::RenderFrameHost* focused_frame = web_contents->GetFocusedFrame(); + // If the frame tree does not have a focused frame at this point, do not + // bother creating RenderViewContextMenuViews. + // This happens if the frame has navigated to a different page before + // ContextMenu message was received by the current RenderFrameHost. + if (focused_frame) { + menu.reset(new RenderViewContextMenuImpl(focused_frame, params)); + menu->Init(); + } + return menu.Pass(); + } + + void ShowMenu(scoped_ptr menu) { + context_menu_.reset(menu.release()); + + if (!context_menu_.get()) + return; + + // Menus need a Widget to work. If we're not the active tab we won't + // necessarily be in a widget. + views::Widget* top_level_widget = GetTopLevelWidget(); + if (!top_level_widget) + return; + + const content::ContextMenuParams& params = context_menu_->params(); + // Don't show empty menus. + if (context_menu_->menu_model().GetItemCount() == 0) + return; + + gfx::Point screen_point(params.x, params.y); + + // Convert from target window coordinates to root window coordinates. + aura::Window* target_window = GetActiveNativeView(); + aura::Window* root_window = target_window->GetRootWindow(); + aura::client::ScreenPositionClient* screen_position_client = + aura::client::GetScreenPositionClient(root_window); + if (screen_position_client) { + screen_position_client->ConvertPointToScreen(target_window, + &screen_point); + } + // Enable recursive tasks on the message loop so we can get updates while + // the context menu is being displayed. + base::MessageLoop::ScopedNestableTaskAllower allow( + base::MessageLoop::current()); + context_menu_->RunMenuAt( + top_level_widget, screen_point, params.source_type); + } + + aura::Window* GetActiveNativeView() { + return web_contents_->GetFullscreenRenderWidgetHostView() + ? web_contents_->GetFullscreenRenderWidgetHostView() + ->GetNativeView() + : web_contents_->GetNativeView(); + } + + views::Widget* GetTopLevelWidget() { + return views::Widget::GetTopLevelWidgetForNativeView(GetActiveNativeView()); + } + + views::FocusManager* GetFocusManager() { + views::Widget* toplevel_widget = GetTopLevelWidget(); + return toplevel_widget ? toplevel_widget->GetFocusManager() : NULL; + } + + void SetInitialFocus() { + if (web_contents_->FocusLocationBarByDefault()) { + if (web_contents_->GetDelegate()) + web_contents_->GetDelegate()->SetFocusToLocationBar(false); + } else { + web_contents_->Focus(); + } + } + scoped_ptr context_menu_; + content::WebContents* web_contents_; + DISALLOW_COPY_AND_ASSIGN(WebContentsViewDelegateImpl); +}; + +} // namespace + +content::WebContentsViewDelegate* CreateWebContentsViewDelegate( + content::WebContents* web_contents) { + return new WebContentsViewDelegateImpl(web_contents); +} + +} // namespace athena + +namespace web_modal { + +SingleWebContentsDialogManager* +WebContentsModalDialogManager::CreateNativeWebModalManager( + NativeWebContentsModalDialog dialog, + SingleWebContentsDialogManagerDelegate* native_delegate) { + // TODO(oshima): Investigate if we need to implement this. + NOTREACHED(); + return NULL; +} + +} // namespace web_modal diff --git a/athena/home/DEPS b/athena/home/DEPS index 9074623351822..93860d064239f 100644 --- a/athena/home/DEPS +++ b/athena/home/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+third_party/skia/include", "+ui/aura", "+ui/app_list", + "+ui/compositor", "+ui/events", "+ui/gfx", "+ui/views", diff --git a/athena/home/app_list_view_delegate.cc b/athena/home/app_list_view_delegate.cc index bd85ecaaead5c..c86736461bcb1 100644 --- a/athena/home/app_list_view_delegate.cc +++ b/athena/home/app_list_view_delegate.cc @@ -5,6 +5,7 @@ #include "athena/home/app_list_view_delegate.h" #include +#include #include "athena/home/public/app_model_builder.h" #include "base/basictypes.h" @@ -19,9 +20,35 @@ #include "ui/app_list/search_result.h" #include "ui/app_list/speech_ui_model.h" #include "ui/gfx/image/image_skia.h" +#include "ui/views/background.h" +#include "ui/views/view.h" namespace athena { +namespace { + +// A view to draw the logo area of app-list centered view. +// TODO(mukai): replace this by the actual start page webview. +class DummyLogoView : public views::View { + public: + explicit DummyLogoView(const gfx::Size& size) + : size_(size) { + set_background(views::Background::CreateSolidBackground( + SK_ColorLTGRAY)); + } + + private: + virtual gfx::Size GetPreferredSize() const OVERRIDE { + return size_; + } + + const gfx::Size size_; + + DISALLOW_COPY_AND_ASSIGN(DummyLogoView); +}; + +} + AppListViewDelegate::AppListViewDelegate(AppModelBuilder* model_builder) : model_(new app_list::AppListModel), speech_ui_(new app_list::SpeechUIModel( @@ -155,12 +182,12 @@ void AppListViewDelegate::ShowForProfileByPath( views::View* AppListViewDelegate::CreateStartPageWebView( const gfx::Size& size) { - return NULL; + return new DummyLogoView(size); } -views::View* AppListViewDelegate::CreateCustomPageWebView( +std::vector AppListViewDelegate::CreateCustomPageWebViews( const gfx::Size& size) { - return NULL; + return std::vector(); } bool AppListViewDelegate::IsSpeechRecognitionEnabled() { diff --git a/athena/home/app_list_view_delegate.h b/athena/home/app_list_view_delegate.h index 76c06aaabdbaf..7ea9240de3c3e 100644 --- a/athena/home/app_list_view_delegate.h +++ b/athena/home/app_list_view_delegate.h @@ -55,7 +55,8 @@ class AppListViewDelegate : public app_list::AppListViewDelegate { virtual void ShowForProfileByPath( const base::FilePath& profile_path) OVERRIDE; virtual views::View* CreateStartPageWebView(const gfx::Size& size) OVERRIDE; - virtual views::View* CreateCustomPageWebView(const gfx::Size& size) OVERRIDE; + virtual std::vector CreateCustomPageWebViews( + const gfx::Size& size) OVERRIDE; virtual bool IsSpeechRecognitionEnabled() OVERRIDE; virtual const Users& GetUsers() const OVERRIDE; virtual bool ShouldCenterWindow() const OVERRIDE; diff --git a/athena/home/athena_start_page_view.cc b/athena/home/athena_start_page_view.cc new file mode 100644 index 0000000000000..a1b9e5ce12d8d --- /dev/null +++ b/athena/home/athena_start_page_view.cc @@ -0,0 +1,187 @@ +// 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 "athena/home/athena_start_page_view.h" + +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" +#include "ui/app_list/app_list_item.h" +#include "ui/app_list/app_list_item_list.h" +#include "ui/app_list/app_list_model.h" +#include "ui/app_list/app_list_view_delegate.h" +#include "ui/app_list/views/search_box_view.h" +#include "ui/gfx/canvas.h" +#include "ui/views/background.h" +#include "ui/views/border.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/round_rect_painter.h" + +namespace { + +const size_t kMaxIconNum = 3; +const int kIconSize = 50; +const int kIconMargin = 25; + +// Copied from ui/app_list/views/start_page_view.cc +const int kSearchBoxBorderWidth = 1; +const int kSearchBoxCornerRadius = 2; +const int kSearchBoxWidth = 490; +const int kSearchBoxHeight = 40; + +// The preferred height for VISIBLE_BOTTOM state. +const int kPreferredHeightBottom = 100; + +class PlaceHolderButton : public views::ImageButton, + public views::ButtonListener { + public: + PlaceHolderButton() + : ImageButton(this) { + gfx::Canvas canvas(gfx::Size(kIconSize, kIconSize), 1.0f, true); + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(SkColorSetRGB(86, 119, 252)); + paint.setFlags(SkPaint::kAntiAlias_Flag); + canvas.DrawCircle( + gfx::Point(kIconSize / 2, kIconSize / 2), kIconSize / 2, paint); + + scoped_ptr image( + new gfx::ImageSkia(canvas.ExtractImageRep())); + SetImage(STATE_NORMAL, image.get()); + } + + private: + // views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE { + // Do nothing: remove these place holders. + } + + DISALLOW_COPY_AND_ASSIGN(PlaceHolderButton); +}; + +class AppIconButton : public views::ImageButton, + public views::ButtonListener { + public: + explicit AppIconButton(app_list::AppListItem* item) + : ImageButton(this), + item_(item) { + // TODO(mukai): icon should be resized. + SetImage(STATE_NORMAL, &item->icon()); + } + + private: + // views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const ui::Event& event) OVERRIDE { + DCHECK_EQ(sender, this); + item_->Activate(event.flags()); + } + + app_list::AppListItem* item_; + + DISALLOW_COPY_AND_ASSIGN(AppIconButton); +}; + +// The background to paint the round rectangle of the view area. +class RoundRectBackground : public views::Background { + public: + RoundRectBackground(SkColor color, int corner_radius) + : color_(color), + corner_radius_(corner_radius) {} + virtual ~RoundRectBackground() {} + + private: + // views::Background: + virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE { + SkPaint paint; + paint.setStyle(SkPaint::kFill_Style); + paint.setColor(color_); + canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, paint); + } + + SkColor color_; + int corner_radius_; + + DISALLOW_COPY_AND_ASSIGN(RoundRectBackground); +}; + +class SearchBoxContainer : public views::View { + public: + explicit SearchBoxContainer(app_list::SearchBoxView* search_box) + : search_box_(search_box) { + search_box->set_background( + new RoundRectBackground(SK_ColorWHITE, kSearchBoxCornerRadius)); + search_box->SetBorder(views::Border::CreateBorderPainter( + new views::RoundRectPainter(SK_ColorGRAY, kSearchBoxCornerRadius), + gfx::Insets(kSearchBoxBorderWidth, kSearchBoxBorderWidth, + kSearchBoxBorderWidth, kSearchBoxBorderWidth))); + AddChildView(search_box_); + } + virtual ~SearchBoxContainer() {} + + private: + // views::View: + virtual void Layout() OVERRIDE { + gfx::Rect search_box_bounds = GetContentsBounds(); + search_box_bounds.ClampToCenteredSize(GetPreferredSize()); + search_box_->SetBoundsRect(search_box_bounds); + } + virtual gfx::Size GetPreferredSize() const OVERRIDE { + return gfx::Size(kSearchBoxWidth, kSearchBoxHeight); + } + + // Owned by the views hierarchy. + app_list::SearchBoxView* search_box_; + + DISALLOW_COPY_AND_ASSIGN(SearchBoxContainer); +}; + +} // namespace + +namespace athena { + +AthenaStartPageView::AthenaStartPageView( + app_list::AppListViewDelegate* view_delegate) { + app_list::AppListItemList* top_level = + view_delegate->GetModel()->top_level_item_list(); + + container_ = new views::View(); + AddChildView(container_); + + views::BoxLayout* box_layout = new views::BoxLayout( + views::BoxLayout::kHorizontal, kIconMargin, kIconMargin, kIconMargin); + box_layout->set_main_axis_alignment( + views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); + box_layout->set_cross_axis_alignment( + views::BoxLayout::CROSS_AXIS_ALIGNMENT_CENTER); + container_->SetLayoutManager(box_layout); + for (size_t i = 0; i < std::min(top_level->item_count(), kMaxIconNum); ++i) + container_->AddChildView(new AppIconButton(top_level->item_at(i))); + + views::View* search_box_container = new SearchBoxContainer( + new app_list::SearchBoxView(this, view_delegate)); + container_->AddChildView(search_box_container); + box_layout->SetFlexForView(search_box_container, 1); + + for (size_t i = 0; i < kMaxIconNum; ++i) + container_->AddChildView(new PlaceHolderButton()); + + set_background(views::Background::CreateSolidBackground( + 255, 255, 255, 255 * 0.9)); +} + +AthenaStartPageView::~AthenaStartPageView() {} + +void AthenaStartPageView::Layout() { + gfx::Rect container_bounds = bounds(); + container_bounds.set_height(kPreferredHeightBottom); + container_->SetBoundsRect(container_bounds); +} + +void AthenaStartPageView::QueryChanged(app_list::SearchBoxView* sender) { + // Nothing needs to be done. +} + +} // namespace athena diff --git a/athena/home/athena_start_page_view.h b/athena/home/athena_start_page_view.h new file mode 100644 index 0000000000000..11991712c35de --- /dev/null +++ b/athena/home/athena_start_page_view.h @@ -0,0 +1,40 @@ +// 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 ATHENA_HOME_ATHENA_START_PAGE_VIEW_H_ +#define ATHENA_HOME_ATHENA_START_PAGE_VIEW_H_ + +#include "athena/home/public/home_card.h" +#include "ui/app_list/views/search_box_view_delegate.h" +#include "ui/views/view.h" + +namespace app_list { +class AppListViewDelegate; +} + +namespace athena { + +// It will replace app_list::StartPageView in Athena UI in the future. +// Right now it's simply used for VISIBLE_BOTTOM state. +class AthenaStartPageView : public views::View, + public app_list::SearchBoxViewDelegate { + public: + explicit AthenaStartPageView(app_list::AppListViewDelegate* delegate); + virtual ~AthenaStartPageView(); + + private: + // views::View: + virtual void Layout() OVERRIDE; + + // app_list::SearchBoxViewDelegate: + virtual void QueryChanged(app_list::SearchBoxView* sender) OVERRIDE; + + views::View* container_; + + DISALLOW_COPY_AND_ASSIGN(AthenaStartPageView); +}; + +} // namespace athena + +#endif // ATHENA_HOME_ATHENA_START_PAGE_VIEW_H_ diff --git a/athena/home/bottom_home_view.cc b/athena/home/bottom_home_view.cc deleted file mode 100644 index 8170761c88569..0000000000000 --- a/athena/home/bottom_home_view.cc +++ /dev/null @@ -1,90 +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 "athena/home/bottom_home_view.h" - -#include "ui/app_list/app_list_item_list.h" -#include "ui/app_list/app_list_model.h" -#include "ui/app_list/app_list_view_delegate.h" -#include "ui/app_list/views/search_box_view.h" -#include "ui/app_list/views/tile_item_view.h" -#include "ui/gfx/canvas.h" -#include "ui/views/background.h" -#include "ui/views/border.h" -#include "ui/views/layout/box_layout.h" -#include "ui/views/painter.h" -#include "ui/views/shadow_border.h" - -namespace { - -class BottomHomeBackground : public views::Background { - public: - explicit BottomHomeBackground(views::View* search_box) - : search_box_(search_box), - painter_(views::Painter::CreateVerticalGradient( - SkColorSetA(SK_ColorWHITE, 0x7f), - SK_ColorWHITE)) {} - virtual ~BottomHomeBackground() {} - - private: - // views::Background: - virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE { - CHECK_EQ(view, search_box_->parent()); - views::Painter::PaintPainterAt( - canvas, - painter_.get(), - gfx::Rect(0, 0, view->width(), search_box_->y())); - canvas->FillRect(gfx::Rect(0, - search_box_->y(), - view->width(), - view->height() - search_box_->y()), - SK_ColorWHITE); - } - - views::View* search_box_; - scoped_ptr painter_; - DISALLOW_COPY_AND_ASSIGN(BottomHomeBackground); -}; - -} // namespace - -namespace athena { - -BottomHomeView::BottomHomeView(app_list::AppListViewDelegate* view_delegate) - : view_delegate_(view_delegate) { - SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); - - app_list::AppListModel* model = view_delegate->GetModel(); - app_list::AppListItemList* top_level = model->top_level_item_list(); - - views::View* items_container = new views::View(); - AddChildView(items_container); - - views::BoxLayout* items_layout = new views::BoxLayout( - views::BoxLayout::kHorizontal, 0, 0, 0); - items_layout->SetDefaultFlex(1); - items_container->SetLayoutManager(items_layout); - for (size_t i = 0; i < top_level->item_count(); ++i) { - app_list::TileItemView* tile_item_view = new app_list::TileItemView(); - tile_item_view->SetAppListItem(top_level->item_at(i)); - items_container->AddChildView(tile_item_view); - tile_item_view->set_background(NULL); - } - - app_list::SearchBoxView* search_box = new app_list::SearchBoxView( - this, view_delegate); - AddChildView(search_box); - search_box->SetBorder( - views::Border::CreateSolidSidedBorder(1, 0, 0, 0, SK_ColorGRAY)); - - set_background(new BottomHomeBackground(search_box)); -} - -BottomHomeView::~BottomHomeView() {} - -void BottomHomeView::QueryChanged(app_list::SearchBoxView* sender) { - // Nothing needs to be done. -} - -} // namespace athena diff --git a/athena/home/bottom_home_view.h b/athena/home/bottom_home_view.h deleted file mode 100644 index b88b04aab1ea2..0000000000000 --- a/athena/home/bottom_home_view.h +++ /dev/null @@ -1,36 +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 ATHENA_HOME_BOTTOM_HOME_VIEW_H_ -#define ATHENA_HOME_BOTTOM_HOME_VIEW_H_ - -#include "ui/app_list/views/search_box_view_delegate.h" -#include "ui/views/view.h" - -namespace app_list { -class AppListViewDelegate; -} - -namespace athena { - -// The view of 'VISIBLE_BOTTOM' state of home card, which occupies -// smaller area and provides limited functionalities. -class BottomHomeView : public views::View, - public app_list::SearchBoxViewDelegate { - public: - explicit BottomHomeView(app_list::AppListViewDelegate* delegate); - virtual ~BottomHomeView(); - - private: - // Overridden from app_list::SearchBoxViewDelegate: - virtual void QueryChanged(app_list::SearchBoxView* sender) OVERRIDE; - - app_list::AppListViewDelegate* view_delegate_; - - DISALLOW_COPY_AND_ASSIGN(BottomHomeView); -}; - -} // namespace athena - -#endif // ATHENA_HOME_BOTTOM_HOME_VIEW_H_ diff --git a/athena/home/home_card_impl.cc b/athena/home/home_card_impl.cc index 07cf5e0217bb4..e2f5936a51ac1 100644 --- a/athena/home/home_card_impl.cc +++ b/athena/home/home_card_impl.cc @@ -4,11 +4,12 @@ #include "athena/home/public/home_card.h" +#include #include #include "athena/common/container_priorities.h" #include "athena/home/app_list_view_delegate.h" -#include "athena/home/bottom_home_view.h" +#include "athena/home/athena_start_page_view.h" #include "athena/home/minimized_home.h" #include "athena/home/public/app_model_builder.h" #include "athena/input/public/accelerator_manager.h" @@ -16,16 +17,19 @@ #include "athena/wm/public/window_manager.h" #include "athena/wm/public/window_manager_observer.h" #include "base/bind.h" +#include "base/memory/weak_ptr.h" #include "ui/app_list/search_provider.h" #include "ui/app_list/views/app_list_main_view.h" #include "ui/app_list/views/contents_view.h" #include "ui/aura/layout_manager.h" #include "ui/aura/window.h" +#include "ui/compositor/closure_animation_observer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/views/background.h" #include "ui/views/layout/box_layout.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" -#include "ui/wm/core/shadow.h" +#include "ui/wm/core/shadow_types.h" #include "ui/wm/core/visibility_controller.h" #include "ui/wm/core/window_animations.h" #include "ui/wm/public/activation_change_observer.h" @@ -35,6 +39,33 @@ namespace athena { namespace { HomeCard* instance = NULL; +const int kHomeCardHeight = 100; +const int kHomeCardMinimizedHeight = 6; + +gfx::Rect GetBoundsForState(const gfx::Rect& screen_bounds, + HomeCard::State state) { + switch (state) { + case HomeCard::HIDDEN: + break; + + case HomeCard::VISIBLE_CENTERED: + return screen_bounds; + + case HomeCard::VISIBLE_BOTTOM: + return gfx::Rect(0, + screen_bounds.bottom() - kHomeCardHeight, + screen_bounds.width(), + kHomeCardHeight); + case HomeCard::VISIBLE_MINIMIZED: + return gfx::Rect(0, + screen_bounds.bottom() - kHomeCardMinimizedHeight, + screen_bounds.width(), + kHomeCardMinimizedHeight); + } + + NOTREACHED(); + return gfx::Rect(); +} // Makes sure the homecard is center-aligned horizontally and bottom-aligned // vertically. @@ -44,10 +75,7 @@ class HomeCardLayoutManager : public aura::LayoutManager { public: virtual ~Delegate() {} - virtual int GetHomeCardHeight() const = 0; - - virtual int GetHorizontalMargin() const = 0; - + virtual HomeCard::State GetState() = 0; virtual aura::Window* GetNativeWindow() = 0; }; @@ -63,15 +91,13 @@ class HomeCardLayoutManager : public aura::LayoutManager { if (!home_card || !home_card->GetRootWindow()) return; - int height = delegate_->GetHomeCardHeight(); - int horiz_margin = delegate_->GetHorizontalMargin(); - gfx::Rect screen_bounds = home_card->GetRootWindow()->bounds(); - height = std::min(height, screen_bounds.height()); - gfx::Rect card_bounds = screen_bounds; - card_bounds.Inset(horiz_margin, screen_bounds.height() - height, - horiz_margin, 0); - - SetChildBoundsDirect(home_card, card_bounds); + { + ui::ScopedLayerAnimationSettings settings( + home_card->layer()->GetAnimator()); + settings.SetTweenType(gfx::Tween::EASE_IN_OUT); + SetChildBoundsDirect(home_card, GetBoundsForState( + home_card->GetRootWindow()->bounds(), delegate_->GetState())); + } } private: @@ -88,7 +114,7 @@ class HomeCardLayoutManager : public aura::LayoutManager { } virtual void SetChildBounds(aura::Window* child, const gfx::Rect& requested_bounds) OVERRIDE { - SetChildBoundsDirect(child, gfx::Rect(requested_bounds.size())); + SetChildBoundsDirect(child, requested_bounds); } Delegate* delegate_; @@ -96,40 +122,243 @@ class HomeCardLayoutManager : public aura::LayoutManager { DISALLOW_COPY_AND_ASSIGN(HomeCardLayoutManager); }; +class HomeCardGestureManager { + public: + class Delegate { + public: + // Called when the gesture has ended. The state of the home card will + // end up with |final_state|. + virtual void OnGestureEnded(HomeCard::State final_state) = 0; + + // Called when the gesture position is updated so that |delegate| should + // update the visual. The arguments represent the state of the current + // gesture position is switching from |from_state| to |to_state|, and + // the level of the progress is at |progress|, which is 0 to 1. + // |from_state| and |to_state| could be same. For example, if the user moves + // the finger down to the bottom of the screen, both states are MINIMIZED. + // In that case |progress| is 0. + virtual void OnGestureProgressed( + HomeCard::State from_state, + HomeCard::State to_state, + float progress) = 0; + }; + + HomeCardGestureManager(Delegate* delegate, + const gfx::Rect& screen_bounds) + : delegate_(delegate), + last_state_(HomeCard::Get()->GetState()), + y_offset_(0), + last_estimated_top_(0), + screen_bounds_(screen_bounds) {} + + void ProcessGestureEvent(ui::GestureEvent* event) { + switch (event->type()) { + case ui::ET_GESTURE_SCROLL_BEGIN: + y_offset_ = event->location().y(); + event->SetHandled(); + break; + case ui::ET_GESTURE_SCROLL_END: + event->SetHandled(); + delegate_->OnGestureEnded(GetClosestState()); + break; + case ui::ET_GESTURE_SCROLL_UPDATE: + UpdateScrollState(*event); + break; + case ui::ET_SCROLL_FLING_START: { + const ui::GestureEventDetails& details = event->details(); + const float kFlingCompletionVelocity = 100.0f; + if (::fabs(details.velocity_y()) > kFlingCompletionVelocity) { + int step = (details.velocity_y() > 0) ? 1 : -1; + int new_state = static_cast(last_state_) + step; + if (new_state >= HomeCard::VISIBLE_CENTERED && + new_state <= HomeCard::VISIBLE_MINIMIZED) { + last_state_ = static_cast(new_state); + } + delegate_->OnGestureEnded(last_state_); + } + break; + } + default: + // do nothing. + break; + } + } + + private: + HomeCard::State GetClosestState() { + // The top position of the bounds for one smaller state than the current + // one. + int smaller_top = -1; + for (int i = HomeCard::VISIBLE_MINIMIZED; + i >= HomeCard::VISIBLE_CENTERED; --i) { + HomeCard::State state = static_cast(i); + int top = GetBoundsForState(screen_bounds_, state).y(); + if (last_estimated_top_ == top) { + return state; + } else if (last_estimated_top_ > top) { + if (smaller_top < 0) + return state; + + if (smaller_top - last_estimated_top_ > (smaller_top - top) / 5) { + return state; + } else { + return static_cast(i + 1); + } + } + smaller_top = top; + } + + NOTREACHED(); + return last_state_; + } + + void UpdateScrollState(const ui::GestureEvent& event) { + last_estimated_top_ = event.root_location().y() - y_offset_; + + // The bounds which is at one smaller state than the current one. + gfx::Rect smaller_bounds; + + for (int i = HomeCard::VISIBLE_MINIMIZED; + i >= HomeCard::VISIBLE_CENTERED; --i) { + HomeCard::State state = static_cast(i); + const gfx::Rect bounds = GetBoundsForState(screen_bounds_, state); + if (last_estimated_top_ == bounds.y()) { + delegate_->OnGestureProgressed(last_state_, state, 1.0f); + last_state_ = state; + return; + } else if (last_estimated_top_ > bounds.y()) { + if (smaller_bounds.IsEmpty()) { + // Smaller than minimized -- returning the minimized bounds. + delegate_->OnGestureProgressed(last_state_, state, 1.0f); + } else { + // The finger is between two states. + float progress = + static_cast((smaller_bounds.y() - last_estimated_top_)) / + (smaller_bounds.y() - bounds.y()); + if (last_state_ == state) { + if (event.details().scroll_y() > 0) { + state = static_cast(state + 1); + progress = 1.0f - progress; + } else { + last_state_ = static_cast(last_state_ + 1); + } + } + delegate_->OnGestureProgressed(last_state_, state, progress); + } + last_state_ = state; + return; + } + smaller_bounds = bounds; + } + NOTREACHED(); + } + + Delegate* delegate_; + HomeCard::State last_state_; + + // The offset from the top edge of the home card and the initial position of + // gesture. + int y_offset_; + + // The estimated top edge of the home card after the last touch event. + int last_estimated_top_; + + // The bounds of the screen to compute the home card bounds. + gfx::Rect screen_bounds_; + + DISALLOW_COPY_AND_ASSIGN(HomeCardGestureManager); +}; + // The container view of home card contents of each state. class HomeCardView : public views::WidgetDelegateView { public: HomeCardView(app_list::AppListViewDelegate* view_delegate, aura::Window* container, - MinimizedHomeDragDelegate* minimized_delegate) { - bottom_view_ = new BottomHomeView(view_delegate); + HomeCardGestureManager::Delegate* gesture_delegate) + : gesture_delegate_(gesture_delegate), + weak_factory_(this) { + bottom_view_ = new AthenaStartPageView(view_delegate); AddChildView(bottom_view_); + bottom_view_->SetPaintToLayer(true); + bottom_view_->layer()->SetFillsBoundsOpaquely(false); main_view_ = new app_list::AppListMainView( view_delegate, 0 /* initial_apps_page */, container); AddChildView(main_view_); main_view_->set_background( views::Background::CreateSolidBackground(SK_ColorWHITE)); + main_view_->SetPaintToLayer(true); - minimized_view_ = CreateMinimizedHome(minimized_delegate); + minimized_view_ = CreateMinimizedHome(); + minimized_view_->SetPaintToLayer(true); AddChildView(minimized_view_); } + void SetStateProgress(HomeCard::State from_state, + HomeCard::State to_state, + float progress) { + if (from_state == HomeCard::VISIBLE_BOTTOM && + to_state == HomeCard::VISIBLE_MINIMIZED) { + SetStateProgress(to_state, from_state, 1.0 - progress); + return; + } + + // View from minimized to bottom. + if (from_state == HomeCard::VISIBLE_MINIMIZED && + to_state == HomeCard::VISIBLE_BOTTOM) { + bottom_view_->SetVisible(true); + minimized_view_->SetVisible(true); + minimized_view_->layer()->SetOpacity(1.0f - progress); + return; + } + + SetState(to_state); + } + void SetState(HomeCard::State state) { bottom_view_->SetVisible(state == HomeCard::VISIBLE_BOTTOM); main_view_->SetVisible(state == HomeCard::VISIBLE_CENTERED); minimized_view_->SetVisible(state == HomeCard::VISIBLE_MINIMIZED); + if (minimized_view_->visible()) + minimized_view_->layer()->SetOpacity(1.0f); if (state == HomeCard::VISIBLE_CENTERED) { app_list::ContentsView* contents_view = main_view_->contents_view(); contents_view->SetActivePage(contents_view->GetPageIndexForNamedPage( app_list::ContentsView::NAMED_PAGE_START)); } + wm::SetShadowType(GetWidget()->GetNativeView(), + state == HomeCard::VISIBLE_MINIMIZED ? + wm::SHADOW_TYPE_NONE : + wm::SHADOW_TYPE_RECTANGULAR); + } + + void SetStateWithAnimation(HomeCard::State from_state, + HomeCard::State to_state) { + if ((from_state == HomeCard::VISIBLE_MINIMIZED && + to_state == HomeCard::VISIBLE_BOTTOM) || + (from_state == HomeCard::VISIBLE_BOTTOM && + to_state == HomeCard::VISIBLE_MINIMIZED)) { + minimized_view_->SetVisible(true); + bottom_view_->SetVisible(true); + { + ui::ScopedLayerAnimationSettings settings( + minimized_view_->layer()->GetAnimator()); + settings.SetTweenType(gfx::Tween::EASE_IN_OUT); + settings.AddObserver(new ui::ClosureAnimationObserver( + base::Bind(&HomeCardView::SetState, + weak_factory_.GetWeakPtr(), + to_state))); + minimized_view_->layer()->SetOpacity( + (to_state == HomeCard::VISIBLE_MINIMIZED) ? 1.0f : 0.0f); + } + } else { + // TODO(mukai): Take care of other transition. + SetState(to_state); + } + } - if (state != HomeCard::VISIBLE_BOTTOM) - shadow_.reset(); - // Do not create the shadow yet. Instead, create it in OnWidgetMove(), to - // make sure that widget has been resized correctly (because the size of the - // shadow depends on the size of the widget). + void ClearGesture() { + gesture_manager_.reset(); } // views::View: @@ -137,38 +366,42 @@ class HomeCardView : public views::WidgetDelegateView { for (int i = 0; i < child_count(); ++i) { views::View* child = child_at(i); if (child->visible()) { - child->SetBoundsRect(bounds()); - return; + if (child == minimized_view_) { + gfx::Rect minimized_bounds = bounds(); + minimized_bounds.set_y( + minimized_bounds.bottom() - kHomeCardMinimizedHeight); + minimized_bounds.set_height(kHomeCardMinimizedHeight); + child->SetBoundsRect(minimized_bounds); + } else { + child->SetBoundsRect(bounds()); + } } } + } + virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { + if (!gesture_manager_ && + event->type() == ui::ET_GESTURE_SCROLL_BEGIN) { + gesture_manager_.reset(new HomeCardGestureManager( + gesture_delegate_, + GetWidget()->GetNativeWindow()->GetRootWindow()->bounds())); + } - // One of the child views has to be visible. - NOTREACHED(); + if (gesture_manager_) + gesture_manager_->ProcessGestureEvent(event); } private: // views::WidgetDelegate: - virtual void OnWidgetMove() OVERRIDE { - if (bottom_view_->visible() && !shadow_) { - aura::Window* window = GetWidget()->GetNativeWindow(); - shadow_.reset(new wm::Shadow()); - shadow_->Init(wm::Shadow::STYLE_ACTIVE); - shadow_->SetContentBounds(gfx::Rect(window->bounds().size())); - shadow_->layer()->SetVisible(true); - - ui::Layer* layer = window->layer(); - layer->Add(shadow_->layer()); - } - } - virtual views::View* GetContentsView() OVERRIDE { return this; } app_list::AppListMainView* main_view_; - BottomHomeView* bottom_view_; + views::View* bottom_view_; views::View* minimized_view_; - scoped_ptr shadow_; + scoped_ptr gesture_manager_; + HomeCardGestureManager::Delegate* gesture_delegate_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(HomeCardView); }; @@ -176,7 +409,7 @@ class HomeCardView : public views::WidgetDelegateView { class HomeCardImpl : public HomeCard, public AcceleratorHandler, public HomeCardLayoutManager::Delegate, - public MinimizedHomeDragDelegate, + public HomeCardGestureManager::Delegate, public WindowManagerObserver, public aura::client::ActivationChangeObserver { public: @@ -202,71 +435,23 @@ class HomeCardImpl : public HomeCard, // AcceleratorHandler: virtual bool IsCommandEnabled(int command_id) const OVERRIDE { return true; } virtual bool OnAcceleratorFired(int command_id, - const ui::Accelerator& accelerator) OVERRIDE { - DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id); - - if (state_ == VISIBLE_CENTERED && original_state_ != VISIBLE_BOTTOM) - SetState(VISIBLE_MINIMIZED); - else if (state_ == VISIBLE_MINIMIZED) - SetState(VISIBLE_CENTERED); - return true; - } + const ui::Accelerator& accelerator) OVERRIDE; // HomeCardLayoutManager::Delegate: - virtual int GetHomeCardHeight() const OVERRIDE { - const int kHomeCardHeight = 150; - const int kHomeCardMinimizedHeight = 8; - - switch (state_) { - case VISIBLE_CENTERED: - // Span the screen fully. - return std::numeric_limits::max(); - case VISIBLE_BOTTOM: - return kHomeCardHeight; - case VISIBLE_MINIMIZED: - return kHomeCardMinimizedHeight; - case HIDDEN: - break; - } - NOTREACHED(); - return -1; - } + virtual aura::Window* GetNativeWindow() OVERRIDE; - virtual int GetHorizontalMargin() const OVERRIDE { - CHECK_NE(HIDDEN, state_); - const int kHomeCardHorizontalMargin = 100; - return state_ == VISIBLE_BOTTOM ? kHomeCardHorizontalMargin : 0; - } - - virtual aura::Window* GetNativeWindow() OVERRIDE { - if (state_ == HIDDEN) - return NULL; - - return home_card_widget_ ? home_card_widget_->GetNativeWindow() : NULL; - } - - // MinimizedHomeDragDelegate: - virtual void OnDragUpCompleted() OVERRIDE { - WindowManager::GetInstance()->ToggleOverview(); - } + // HomeCardGestureManager::Delegate: + virtual void OnGestureEnded(State final_state) OVERRIDE; + virtual void OnGestureProgressed( + State from_state, State to_state, float progress) OVERRIDE; // WindowManagerObserver: - virtual void OnOverviewModeEnter() OVERRIDE { - SetState(VISIBLE_BOTTOM); - } - - virtual void OnOverviewModeExit() OVERRIDE { - SetState(VISIBLE_MINIMIZED); - } + virtual void OnOverviewModeEnter() OVERRIDE; + virtual void OnOverviewModeExit() OVERRIDE; // aura::client::ActivationChangeObserver: virtual void OnWindowActivated(aura::Window* gained_active, - aura::Window* lost_active) OVERRIDE { - if (state_ != HIDDEN && - gained_active != home_card_widget_->GetNativeWindow()) { - SetState(VISIBLE_MINIMIZED); - } - } + aura::Window* lost_active) OVERRIDE; scoped_ptr model_builder_; @@ -311,12 +496,54 @@ HomeCardImpl::~HomeCardImpl() { instance = NULL; } +void HomeCardImpl::Init() { + InstallAccelerators(); + ScreenManager::ContainerParams params("HomeCardContainer", CP_HOME_CARD); + params.can_activate_children = true; + aura::Window* container = ScreenManager::Get()->CreateContainer(params); + layout_manager_ = new HomeCardLayoutManager(this); + + container->SetLayoutManager(layout_manager_); + wm::SetChildWindowVisibilityChangesAnimated(container); + + view_delegate_.reset(new AppListViewDelegate(model_builder_.get())); + if (search_provider_) + view_delegate_->RegisterSearchProvider(search_provider_.get()); + + home_card_view_ = new HomeCardView(view_delegate_.get(), container, this); + home_card_widget_ = new views::Widget(); + views::Widget::InitParams widget_params( + views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); + widget_params.parent = container; + widget_params.delegate = home_card_view_; + widget_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; + home_card_widget_->Init(widget_params); + + SetState(VISIBLE_MINIMIZED); + home_card_view_->Layout(); + + activation_client_ = + aura::client::GetActivationClient(container->GetRootWindow()); + if (activation_client_) + activation_client_->AddObserver(this); +} + +void HomeCardImpl::InstallAccelerators() { + const AcceleratorData accelerator_data[] = { + {TRIGGER_ON_PRESS, ui::VKEY_L, ui::EF_CONTROL_DOWN, + COMMAND_SHOW_HOME_CARD, AF_NONE}, + }; + AcceleratorManager::Get()->RegisterAccelerators( + accelerator_data, arraysize(accelerator_data), this); +} + void HomeCardImpl::SetState(HomeCard::State state) { if (state_ == state) return; // Update |state_| before changing the visibility of the widgets, so that // LayoutManager callbacks get the correct state. + HomeCard::State old_state = state_; state_ = state; original_state_ = state; if (state_ == HIDDEN) { @@ -326,7 +553,7 @@ void HomeCardImpl::SetState(HomeCard::State state) { home_card_widget_->Show(); else home_card_widget_->ShowInactive(); - home_card_view_->SetState(state); + home_card_view_->SetStateWithAnimation(old_state, state); layout_manager_->Layout(); } } @@ -355,45 +582,66 @@ void HomeCardImpl::UpdateVirtualKeyboardBounds( } } -void HomeCardImpl::Init() { - InstallAccelerators(); - ScreenManager::ContainerParams params("HomeCardContainer", CP_HOME_CARD); - params.can_activate_children = true; - aura::Window* container = ScreenManager::Get()->CreateContainer(params); - layout_manager_ = new HomeCardLayoutManager(this); +bool HomeCardImpl::OnAcceleratorFired(int command_id, + const ui::Accelerator& accelerator) { + DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id); - container->SetLayoutManager(layout_manager_); - wm::SetChildWindowVisibilityChangesAnimated(container); + if (state_ == VISIBLE_CENTERED && original_state_ != VISIBLE_BOTTOM) + SetState(VISIBLE_MINIMIZED); + else if (state_ == VISIBLE_MINIMIZED) + SetState(VISIBLE_CENTERED); + return true; +} - view_delegate_.reset(new AppListViewDelegate(model_builder_.get())); - if (search_provider_) - view_delegate_->RegisterSearchProvider(search_provider_.get()); +aura::Window* HomeCardImpl::GetNativeWindow() { + if (state_ == HIDDEN) + return NULL; - home_card_view_ = new HomeCardView(view_delegate_.get(), container, this); - home_card_widget_ = new views::Widget(); - views::Widget::InitParams widget_params( - views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - widget_params.parent = container; - widget_params.delegate = home_card_view_; - widget_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; - home_card_widget_->Init(widget_params); + return home_card_widget_ ? home_card_widget_->GetNativeWindow() : NULL; +} - SetState(VISIBLE_MINIMIZED); - home_card_view_->Layout(); +void HomeCardImpl::OnGestureEnded(State final_state) { + home_card_view_->ClearGesture(); + if (state_ != final_state && + (state_ == VISIBLE_MINIMIZED || final_state == VISIBLE_MINIMIZED)) { + WindowManager::GetInstance()->ToggleOverview(); + } else { + HomeCard::State old_state = state_; + state_ = final_state; + home_card_view_->SetStateWithAnimation(old_state, final_state); + layout_manager_->Layout(); + } +} - activation_client_ = - aura::client::GetActivationClient(container->GetRootWindow()); - if (activation_client_) - activation_client_->AddObserver(this); +void HomeCardImpl::OnGestureProgressed( + State from_state, State to_state, float progress) { + home_card_view_->SetStateProgress(from_state, to_state, progress); + + gfx::Rect screen_bounds = + home_card_widget_->GetNativeWindow()->GetRootWindow()->bounds(); + home_card_widget_->SetBounds(gfx::Tween::RectValueBetween( + progress, + GetBoundsForState(screen_bounds, from_state), + GetBoundsForState(screen_bounds, to_state))); + + // TODO(mukai): signals the update to the window manager so that it shows the + // intermediate visual state of overview mode. } -void HomeCardImpl::InstallAccelerators() { - const AcceleratorData accelerator_data[] = { - {TRIGGER_ON_PRESS, ui::VKEY_L, ui::EF_CONTROL_DOWN, - COMMAND_SHOW_HOME_CARD, AF_NONE}, - }; - AcceleratorManager::Get()->RegisterAccelerators( - accelerator_data, arraysize(accelerator_data), this); +void HomeCardImpl::OnOverviewModeEnter() { + SetState(VISIBLE_BOTTOM); +} + +void HomeCardImpl::OnOverviewModeExit() { + SetState(VISIBLE_MINIMIZED); +} + +void HomeCardImpl::OnWindowActivated(aura::Window* gained_active, + aura::Window* lost_active) { + if (state_ != HIDDEN && + gained_active != home_card_widget_->GetNativeWindow()) { + SetState(VISIBLE_MINIMIZED); + } } } // namespace diff --git a/athena/home/minimized_home.cc b/athena/home/minimized_home.cc index 3854fb2ade5b0..3ffd65b1cf84d 100644 --- a/athena/home/minimized_home.cc +++ b/athena/home/minimized_home.cc @@ -4,66 +4,41 @@ #include "athena/home/minimized_home.h" +#include "athena/wm/public/window_manager.h" +#include "ui/gfx/canvas.h" #include "ui/views/background.h" -#include "ui/views/layout/box_layout.h" #include "ui/views/view.h" -#include "ui/views/widget/widget.h" namespace { -const SkColor kDragHandleColorNormal = SK_ColorGRAY; -const SkColor kDragHandleColorHot = SK_ColorWHITE; +const int kDragHandleWidth = 112; +const int kDragHandleHeight = 2; -// The small white bar in the middle of the minimized view. Does not reach to -// events. -class SmallBarView : public views::View { +class MinimizedHomeBackground : public views::Background { public: - SmallBarView() : color_(SK_ColorTRANSPARENT) { - SetColor(kDragHandleColorNormal); - } - - virtual ~SmallBarView() {} - - void SetActive(bool active) { - SetColor(active ? kDragHandleColorHot : kDragHandleColorNormal); - } + MinimizedHomeBackground() {} + virtual ~MinimizedHomeBackground() {} private: - void SetColor(SkColor color) { - if (color_ == color) - return; - color_ = color; - set_background(views::Background::CreateSolidBackground(color_)); - SchedulePaint(); + virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE { + gfx::Rect bounds = view->GetLocalBounds(); + canvas->FillRect(bounds, SK_ColorBLACK); + canvas->FillRect(gfx::Rect((bounds.width() - kDragHandleWidth) / 2, + bounds.bottom() - kDragHandleHeight, + kDragHandleWidth, + kDragHandleHeight), + SK_ColorWHITE); } - // views::View - virtual gfx::Size GetPreferredSize() const OVERRIDE { - const int kDragHandleWidth = 80; - const int kDragHandleHeight = 4; - return gfx::Size(kDragHandleWidth, kDragHandleHeight); - } - - SkColor color_; - - DISALLOW_COPY_AND_ASSIGN(SmallBarView); + DISALLOW_COPY_AND_ASSIGN(MinimizedHomeBackground); }; // This View shows an instance of SmallBarView in the middle, and reacts to // mouse and touch-gesture events. class MinimizedHomeView : public views::View { public: - explicit MinimizedHomeView(athena::MinimizedHomeDragDelegate* delegate) - : delegate_(delegate), - bar_(new SmallBarView) { - set_background(views::Background::CreateSolidBackground(SK_ColorBLACK)); - views::BoxLayout* layout = - new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 2, 0); - layout->set_main_axis_alignment( - views::BoxLayout::MAIN_AXIS_ALIGNMENT_CENTER); - SetLayoutManager(layout); - - AddChildView(bar_); + MinimizedHomeView() { + set_background(new MinimizedHomeBackground()); } virtual ~MinimizedHomeView() {} @@ -71,48 +46,12 @@ class MinimizedHomeView : public views::View { // views::View: virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE { if (event.IsLeftMouseButton() && event.GetClickCount() == 1) { - delegate_->OnDragUpCompleted(); - bar_->SetActive(false); + athena::WindowManager::GetInstance()->ToggleOverview(); return true; } return false; } - virtual void OnMouseEntered(const ui::MouseEvent& event) OVERRIDE { - bar_->SetActive(true); - } - - virtual void OnMouseExited(const ui::MouseEvent& event) OVERRIDE { - bar_->SetActive(false); - } - - virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE { - if (event->type() == ui::ET_GESTURE_BEGIN && - event->details().touch_points() == 1) { - bar_->SetActive(true); - event->SetHandled(); - return; - } else if (event->type() == ui::ET_GESTURE_END && - event->details().touch_points() == 1) { - bar_->SetActive(false); - event->SetHandled(); - return; - } - - if (event->type() == ui::ET_GESTURE_SCROLL_BEGIN) { - event->SetHandled(); - } else if (event->type() == ui::ET_SCROLL_FLING_START) { - const ui::GestureEventDetails& details = event->details(); - const float kFlingCompletionVelocity = -100.f; - if (details.velocity_y() < kFlingCompletionVelocity) - delegate_->OnDragUpCompleted(); - bar_->SetActive(false); - } - } - - athena::MinimizedHomeDragDelegate* delegate_; - SmallBarView* bar_; - DISALLOW_COPY_AND_ASSIGN(MinimizedHomeView); }; @@ -120,8 +59,8 @@ class MinimizedHomeView : public views::View { namespace athena { -views::View* CreateMinimizedHome(MinimizedHomeDragDelegate* delegate) { - return new MinimizedHomeView(delegate); +views::View* CreateMinimizedHome() { + return new MinimizedHomeView(); } } // namespace athena diff --git a/athena/home/minimized_home.h b/athena/home/minimized_home.h index 9276e379e2a1d..4427f45732d4b 100644 --- a/athena/home/minimized_home.h +++ b/athena/home/minimized_home.h @@ -11,16 +11,7 @@ class View; namespace athena { -class MinimizedHomeDragDelegate { - public: - virtual ~MinimizedHomeDragDelegate() {} - - virtual void OnDragUpCompleted() = 0; -}; - -// Note that |delegate| is guaranteed to be alive as long as the returned view -// is alive. -views::View* CreateMinimizedHome(MinimizedHomeDragDelegate* delegate); +views::View* CreateMinimizedHome(); } // namespace athena diff --git a/athena/main/athena_launcher.cc b/athena/main/athena_launcher.cc index 04b55a77f2981..fdfbfe9f0de27 100644 --- a/athena/main/athena_launcher.cc +++ b/athena/main/athena_launcher.cc @@ -13,6 +13,7 @@ #include "athena/system/public/system_ui.h" #include "athena/wm/public/window_manager.h" #include "base/memory/scoped_ptr.h" +#include "content/public/browser/browser_thread.h" #include "ui/aura/window_property.h" #include "ui/views/views_delegate.h" #include "ui/wm/core/visibility_controller.h" @@ -69,7 +70,9 @@ void StartAthena(aura::Window* root_window, aura::client::SetVisibilityClient(root_window, root_window_state->visibility_client.get()); - athena::SystemUI::Create(); + athena::SystemUI::Create( + content::BrowserThread::GetMessageLoopProxyForThread( + content::BrowserThread::FILE)); athena::InputManager::Create()->OnRootWindowCreated(root_window); athena::ScreenManager::Create(root_window); athena::WindowManager::Create(); diff --git a/athena/main/athena_main.cc b/athena/main/athena_main.cc index 911776a5ec031..8ce723694d27a 100644 --- a/athena/main/athena_main.cc +++ b/athena/main/athena_main.cc @@ -4,6 +4,7 @@ #include "athena/content/public/content_activity_factory.h" #include "athena/content/public/content_app_model_builder.h" +#include "athena/content/public/web_contents_view_delegate_creator.h" #include "athena/home/public/home_card.h" #include "athena/main/athena_app_window_controller.h" #include "athena/main/athena_launcher.h" @@ -145,8 +146,7 @@ class AthenaContentBrowserClient // content::ContentBrowserClient: virtual content::WebContentsViewDelegate* GetWebContentsViewDelegate( content::WebContents* web_contents) OVERRIDE { - // TODO(oshima): Implement athena's WebContentsViewDelegate. - return NULL; + return athena::CreateWebContentsViewDelegate(web_contents); } private: diff --git a/athena/resources/athena_resources.grd b/athena/resources/athena_resources.grd index ee1665a962631..12e2ff4d275b5 100644 --- a/athena/resources/athena_resources.grd +++ b/athena/resources/athena_resources.grd @@ -11,6 +11,8 @@ + + diff --git a/athena/resources/athena_resources.gyp b/athena/resources/athena_resources.gyp index 88aa5a7010bb4..8d9769c0226f3 100644 --- a/athena/resources/athena_resources.gyp +++ b/athena/resources/athena_resources.gyp @@ -29,11 +29,11 @@ 'type': 'none', 'dependencies': [ '../../ash/ash_resources.gyp:ash_resources', + '../../content/app/strings/content_strings.gyp:content_strings', '../../extensions/extensions.gyp:extensions_shell_and_test_pak', '../../ui/chromeos/ui_chromeos.gyp:ui_chromeos_resources', '../../ui/chromeos/ui_chromeos.gyp:ui_chromeos_strings', - '../../webkit/webkit_resources.gyp:webkit_resources', - '../../webkit/webkit_resources.gyp:webkit_strings', + '../../webkit/glue/resources/webkit_resources.gyp:webkit_resources', 'athena_resources', ], 'actions': [{ @@ -43,10 +43,10 @@ '<(PRODUCT_DIR)/extensions_shell_and_test.pak', '<(SHARED_INTERMEDIATE_DIR)/ash/resources/ash_resources_100_percent.pak', '<(SHARED_INTERMEDIATE_DIR)/athena/resources/athena_resources_100_percent.pak', + '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/ui/chromeos/resources/ui_chromeos_resources_100_percent.pak', '<(SHARED_INTERMEDIATE_DIR)/ui/chromeos/strings/ui_chromeos_strings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources_100_percent.pak', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.pak', ], 'pak_output': '<(PRODUCT_DIR)/athena_resources.pak', }, diff --git a/athena/resources/default_100_percent/overview_split.png b/athena/resources/default_100_percent/overview_split.png new file mode 100644 index 0000000000000..3874397b94dc3 Binary files /dev/null and b/athena/resources/default_100_percent/overview_split.png differ diff --git a/athena/resources/default_100_percent/overview_trash.png b/athena/resources/default_100_percent/overview_trash.png new file mode 100644 index 0000000000000..c8ffde1f7dbd6 Binary files /dev/null and b/athena/resources/default_100_percent/overview_trash.png differ diff --git a/athena/screen/DEPS b/athena/screen/DEPS index 2497d27ab1603..df60728e1a481 100644 --- a/athena/screen/DEPS +++ b/athena/screen/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+athena/input/public", + "+third_party/skia/include/core/SkColor.h", "+ui/aura", "+ui/compositor", "+ui/events", @@ -7,3 +8,9 @@ include_rules = [ "+ui/views", "+ui/wm", ] + +specific_include_rules = { + "background_controller.cc": [ + "+extensions/shell/common/version.h", + ], +} diff --git a/athena/screen/background_controller.cc b/athena/screen/background_controller.cc index 66686d0ca8317..bcdf40fa15d46 100644 --- a/athena/screen/background_controller.cc +++ b/athena/screen/background_controller.cc @@ -4,18 +4,54 @@ #include "athena/screen/background_controller.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "extensions/shell/common/version.h" +#include "third_party/skia/include/core/SkColor.h" #include "ui/aura/window.h" #include "ui/compositor/layer.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia.h" +#include "ui/views/controls/label.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" namespace athena { +namespace { + +const SkColor kVersionColor = SK_ColorWHITE; +const SkColor kVersionBackground = SK_ColorTRANSPARENT; +const SkColor kVersionShadow = 0xB0000000; +const int kVersionShadowBlur = 10; + +class VersionView : public views::Label { + public: + VersionView() { + SetEnabledColor(kVersionColor); + SetBackgroundColor(kVersionBackground); + SetShadows(gfx::ShadowValues(1, gfx::ShadowValue(gfx::Point(0, 1), + kVersionShadowBlur, + kVersionShadow))); + SetText(base::UTF8ToUTF16(base::StringPrintf("%s (Build %s)", + PRODUCT_VERSION, + LAST_CHANGE))); + SetBoundsRect(gfx::Rect(gfx::Point(), GetPreferredSize())); + } + virtual ~VersionView() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(VersionView); +}; + +} // namespace + class BackgroundView : public views::View { public: - BackgroundView() {} + BackgroundView() { + AddChildView(new VersionView); + } virtual ~BackgroundView() {} void SetImage(const gfx::ImageSkia& image) { diff --git a/athena/screen/public/screen_manager.h b/athena/screen/public/screen_manager.h index 8a82f6c617272..64f90357dee42 100644 --- a/athena/screen/public/screen_manager.h +++ b/athena/screen/public/screen_manager.h @@ -8,6 +8,7 @@ #include #include "athena/athena_export.h" +#include "ui/gfx/display.h" namespace aura { class Window; @@ -17,6 +18,10 @@ namespace gfx { class ImageSkia; } +namespace ui { +class LayerAnimator; +} + namespace wm { class FocusRules; } @@ -64,6 +69,15 @@ class ATHENA_EXPORT ScreenManager { // Sets the background image. virtual void SetBackgroundImage(const gfx::ImageSkia& image) = 0; + // Set screen rotation. + // TODO(flackr): Extract and use ash DisplayManager to set rotation + // instead: http://crbug.com/401044. + virtual void SetRotation(gfx::Display::Rotation rotation) = 0; + + // Returns the LayerAnimator to use to animate the entire screen (e.g. fade + // screen to white). + virtual ui::LayerAnimator* GetScreenAnimator() = 0; + // Create a focus rules. // TODO(oshima): Make this virtual function. static wm::FocusRules* CreateFocusRules(); diff --git a/athena/screen/screen_accelerator_handler.cc b/athena/screen/screen_accelerator_handler.cc index 031a039dc68a2..97c08ec4aa38e 100644 --- a/athena/screen/screen_accelerator_handler.cc +++ b/athena/screen/screen_accelerator_handler.cc @@ -5,10 +5,13 @@ #include "athena/screen/screen_accelerator_handler.h" #include "athena/input/public/accelerator_manager.h" +#include "athena/screen/public/screen_manager.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_tree_host.h" #include "ui/compositor/debug_utils.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" #include "ui/wm/public/activation_client.h" namespace athena { @@ -17,6 +20,7 @@ namespace { enum Command { CMD_PRINT_LAYER_HIERARCHY, CMD_PRINT_WINDOW_HIERARCHY, + CMD_ROTATE_SCREEN, }; const int EF_ALL_DOWN = @@ -27,6 +31,9 @@ const AcceleratorData accelerator_data[] = { AF_DEBUG}, {TRIGGER_ON_PRESS, ui::VKEY_W, EF_ALL_DOWN, CMD_PRINT_WINDOW_HIERARCHY, AF_DEBUG}, + {TRIGGER_ON_PRESS, ui::VKEY_F3, + ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN, + CMD_ROTATE_SCREEN, AF_NONE}, }; void PrintLayerHierarchy(aura::Window* root_window) { @@ -63,6 +70,20 @@ void HandlePrintWindowHierarchy(aura::Window* root_window) { LOG(ERROR) << out.str(); } +void HandleRotateScreen() { + ScreenManager* screen_manager = ScreenManager::Get(); + gfx::Display::Rotation current_rotation = + gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().rotation(); + if (current_rotation == gfx::Display::ROTATE_0) + screen_manager->SetRotation(gfx::Display::ROTATE_90); + else if (current_rotation == gfx::Display::ROTATE_90) + screen_manager->SetRotation(gfx::Display::ROTATE_180); + else if (current_rotation == gfx::Display::ROTATE_180) + screen_manager->SetRotation(gfx::Display::ROTATE_270); + else if (current_rotation == gfx::Display::ROTATE_270) + screen_manager->SetRotation(gfx::Display::ROTATE_0); +} + } // namespace // static @@ -89,6 +110,9 @@ bool ScreenAcceleratorHandler::OnAcceleratorFired( case CMD_PRINT_WINDOW_HIERARCHY: HandlePrintWindowHierarchy(root_window_); return true; + case CMD_ROTATE_SCREEN: + HandleRotateScreen(); + return true; } return false; } diff --git a/athena/screen/screen_manager_impl.cc b/athena/screen/screen_manager_impl.cc index 8522acfc7acd3..3877e413f5d4b 100644 --- a/athena/screen/screen_manager_impl.cc +++ b/athena/screen/screen_manager_impl.cc @@ -14,10 +14,14 @@ #include "ui/aura/client/screen_position_client.h" #include "ui/aura/client/window_tree_client.h" #include "ui/aura/layout_manager.h" +#include "ui/aura/test/test_screen.h" #include "ui/aura/window.h" #include "ui/aura/window_property.h" #include "ui/aura/window_targeter.h" #include "ui/aura/window_tree_host.h" +#include "ui/compositor/layer.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" #include "ui/wm/core/base_focus_rules.h" #include "ui/wm/core/capture_controller.h" @@ -199,6 +203,8 @@ class ScreenManagerImpl : public ScreenManager { virtual aura::Window* CreateContainer(const ContainerParams& params) OVERRIDE; virtual aura::Window* GetContext() OVERRIDE { return root_window_; } virtual void SetBackgroundImage(const gfx::ImageSkia& image) OVERRIDE; + virtual void SetRotation(gfx::Display::Rotation rotation) OVERRIDE; + virtual ui::LayerAnimator* GetScreenAnimator() OVERRIDE; aura::Window* root_window_; aura::Window* background_window_; @@ -322,6 +328,22 @@ void ScreenManagerImpl::SetBackgroundImage(const gfx::ImageSkia& image) { background_controller_->SetImage(image); } +void ScreenManagerImpl::SetRotation(gfx::Display::Rotation rotation) { + if (rotation == + gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().rotation()) { + return; + } + + // TODO(flackr): Use display manager to update display rotation: + // http://crbug.com/401044. + static_cast(gfx::Screen::GetNativeScreen())-> + SetDisplayRotation(rotation); +} + +ui::LayerAnimator* ScreenManagerImpl::GetScreenAnimator() { + return root_window_->layer()->GetAnimator(); +} + } // namespace ScreenManager::ContainerParams::ContainerParams(const std::string& n, diff --git a/athena/system/DEPS b/athena/system/DEPS index ae97819654c9a..c3952d8e04106 100644 --- a/athena/system/DEPS +++ b/athena/system/DEPS @@ -1,5 +1,7 @@ include_rules = [ + "+athena/screen/public", "+athena/system/public", "+chromeos/dbus", + "+ui", ] diff --git a/athena/system/device_socket_listener.cc b/athena/system/device_socket_listener.cc new file mode 100644 index 0000000000000..e041e44c5413d --- /dev/null +++ b/athena/system/device_socket_listener.cc @@ -0,0 +1,287 @@ +// 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 "athena/system/device_socket_listener.h" + +#include +#include +#include +#include +#include + +#include "base/bind.h" +#include "base/files/file_path.h" +#include "base/memory/singleton.h" +#include "base/message_loop/message_loop.h" +#include "base/observer_list.h" +#include "base/stl_util.h" +#include "ipc/unix_domain_socket_util.h" + +namespace athena { + +namespace { + +typedef ObserverList DeviceSocketListeners; + +// Reads from a device socket blocks of a particular size. When that amount of +// data is read DeviceSocketManager::OnDataAvailable is called on the singleton +// instance which then informs all of the listeners on that socket. +class DeviceSocketReader : public base::MessagePumpLibevent::Watcher { + public: + DeviceSocketReader(const std::string& socket_path, + size_t data_size) + : socket_path_(socket_path), + data_size_(data_size), + data_(new char[data_size]) { + } + virtual ~DeviceSocketReader() {} + + private: + // Overidden from base::MessagePumpLibevent::Watcher. + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; + virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; + + std::string socket_path_; + size_t data_size_; + scoped_ptr data_; + + DISALLOW_COPY_AND_ASSIGN(DeviceSocketReader); +}; + +class DeviceSocketManager; +DeviceSocketManager* device_socket_manager_instance_ = NULL; + +// A singleton instance for managing all connections to sockets. +class DeviceSocketManager { + public: + static void Create(scoped_refptr io_task_runner) { + device_socket_manager_instance_ = + new DeviceSocketManager(io_task_runner); + } + + static void Shutdown() { + CHECK(device_socket_manager_instance_); + delete device_socket_manager_instance_; + device_socket_manager_instance_ = NULL; + } + + static DeviceSocketManager* GetInstance() { + CHECK(device_socket_manager_instance_); + return device_socket_manager_instance_; + } + + // If there isn't an existing connection to |socket_path|, then opens a + // connection to |socket_path| and starts listening for data. All listeners + // for |socket_path| receives data when data is available on the socket. + void StartListening(const std::string& socket_path, + size_t data_size, + DeviceSocketListener* listener); + + // Removes |listener| from the list of listeners that receive data from + // |socket_path|. If this is the last listener, then this closes the + // connection to the socket. + void StopListening(const std::string& socket_path, + DeviceSocketListener* listener); + + // Sends data to all the listeners registered to receive data from + // |socket_path|. + void OnDataAvailable(const std::string& socket_path, + const void* data); + + // Notifies listeners of errors reading from the socket and closes it. + void OnError(const std::string& socket_path, int err); + void OnEOF(const std::string& socket_path); + + private: + friend struct DefaultSingletonTraits; + + struct SocketData { + SocketData() + : fd(-1) { + } + + int fd; + DeviceSocketListeners observers; + scoped_ptr controller; + scoped_ptr watcher; + }; + + DeviceSocketManager(scoped_refptr io_task_runner) + : io_task_runner_(io_task_runner) { + } + + ~DeviceSocketManager() { + STLDeleteContainerPairSecondPointers(socket_data_.begin(), + socket_data_.end()); + } + + void StartListeningOnIO(const std::string& socket_path, + size_t data_size, + DeviceSocketListener* listener); + + void StopListeningOnIO(const std::string& socket_path, + DeviceSocketListener* listener); + + void CloseSocket(const std::string& socket_path); + + std::map socket_data_; + scoped_refptr io_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(DeviceSocketManager); +}; + +//////////////////////////////////////////////////////////////////////////////// +// DeviceSocketReader + +void DeviceSocketReader::OnFileCanReadWithoutBlocking(int fd) { + ssize_t read_size = recv(fd, data_.get(), data_size_, 0); + if (read_size < 0) { + if (errno == EINTR) + return; + DeviceSocketManager::GetInstance()->OnError(socket_path_, errno); + return; + } + if (read_size == 0) { + DeviceSocketManager::GetInstance()->OnEOF(socket_path_); + return; + } + if (read_size != static_cast(data_size_)) + return; + DeviceSocketManager::GetInstance()->OnDataAvailable(socket_path_, + data_.get()); +} + +void DeviceSocketReader::OnFileCanWriteWithoutBlocking(int fd) { + NOTREACHED(); +} + +//////////////////////////////////////////////////////////////////////////////// +// DeviceSocketManager + +void DeviceSocketManager::StartListening(const std::string& socket_path, + size_t data_size, + DeviceSocketListener* listener) { + io_task_runner_->PostTask(FROM_HERE, + base::Bind(&DeviceSocketManager::StartListeningOnIO, + base::Unretained(this), socket_path, data_size, listener)); +} + +void DeviceSocketManager::StopListening(const std::string& socket_path, + DeviceSocketListener* listener) { + io_task_runner_->PostTask(FROM_HERE, + base::Bind(&DeviceSocketManager::StopListeningOnIO, + base::Unretained(this), socket_path, listener)); +} + +void DeviceSocketManager::OnDataAvailable(const std::string& socket_path, + const void* data) { + CHECK_GT(socket_data_.count(socket_path), 0UL); + DeviceSocketListeners& listeners = socket_data_[socket_path]->observers; + FOR_EACH_OBSERVER(DeviceSocketListener, listeners, OnDataAvailableOnIO(data)); +} + +void DeviceSocketManager::CloseSocket(const std::string& socket_path) { + if (!socket_data_.count(socket_path)) + return; + SocketData* socket_data = socket_data_[socket_path]; + close(socket_data->fd); + delete socket_data; + socket_data_.erase(socket_path); +} + +void DeviceSocketManager::OnError(const std::string& socket_path, int err) { + LOG(ERROR) << "Error reading from socket: " << socket_path << ": " + << strerror(err); + CloseSocket(socket_path); + // TODO(flackr): Notify listeners that the socket was closed unexpectedly. +} + +void DeviceSocketManager::OnEOF(const std::string& socket_path) { + LOG(ERROR) << "EOF reading from socket: " << socket_path; + CloseSocket(socket_path); +} + +void DeviceSocketManager::StartListeningOnIO(const std::string& socket_path, + size_t data_size, + DeviceSocketListener* listener) { + CHECK(io_task_runner_->RunsTasksOnCurrentThread()); + SocketData* socket_data = NULL; + if (!socket_data_.count(socket_path)) { + int socket_fd = -1; + if (!IPC::CreateClientUnixDomainSocket(base::FilePath(socket_path), + &socket_fd)) { + LOG(ERROR) << "Error connecting to socket: " << socket_path; + return; + } + + socket_data = new SocketData; + socket_data_[socket_path] = socket_data; + + socket_data->fd = socket_fd; + + socket_data->controller.reset( + new base::MessagePumpLibevent::FileDescriptorWatcher()); + socket_data->watcher.reset( + new DeviceSocketReader(socket_path, data_size)); + + base::MessageLoopForIO::current()->WatchFileDescriptor( + socket_fd, + true, + base::MessageLoopForIO::WATCH_READ, + socket_data->controller.get(), + socket_data->watcher.get()); + } else { + socket_data = socket_data_[socket_path]; + } + socket_data->observers.AddObserver(listener); +} + +void DeviceSocketManager::StopListeningOnIO(const std::string& socket_path, + DeviceSocketListener* listener) { + if (!socket_data_.count(socket_path)) + return; // Happens if unable to create a socket. + + CHECK(io_task_runner_->RunsTasksOnCurrentThread()); + DeviceSocketListeners& listeners = socket_data_[socket_path]->observers; + listeners.RemoveObserver(listener); + if (!listeners.might_have_observers()) { + // All listeners for this socket has been removed. Close the socket. + CloseSocket(socket_path); + } +} + +} // namespace + +DeviceSocketListener::DeviceSocketListener(const std::string& socket_path, + size_t data_size) + : socket_path_(socket_path), + data_size_(data_size) { +} + +DeviceSocketListener::~DeviceSocketListener() { + StopListening(); +} + +// static +void DeviceSocketListener::CreateSocketManager( + scoped_refptr io_task_runner) { + DeviceSocketManager::Create(io_task_runner); +} + +// static +void DeviceSocketListener::ShutdownSocketManager() { + DeviceSocketManager::Shutdown(); +} + +void DeviceSocketListener::StartListening() { + DeviceSocketManager::GetInstance()->StartListening(socket_path_, + data_size_, + this); +} + +void DeviceSocketListener::StopListening() { + DeviceSocketManager::GetInstance()->StopListening(socket_path_, this); +} + +} // namespace athena diff --git a/athena/system/device_socket_listener.h b/athena/system/device_socket_listener.h new file mode 100644 index 0000000000000..aa37701e75512 --- /dev/null +++ b/athena/system/device_socket_listener.h @@ -0,0 +1,48 @@ +// 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 ATHENA_SYSTEM_DEVICE_SOCKET_LISTENER_H_ +#define ATHENA_SYSTEM_DEVICE_SOCKET_LISTENER_H_ + +#include + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" + +namespace base { +class TaskRunner; +} + +namespace athena { + +// This class reads device-data from a socket. +class DeviceSocketListener { + public: + DeviceSocketListener(const std::string& socket_parth, + size_t data_size); + virtual ~DeviceSocketListener(); + + static void CreateSocketManager( + scoped_refptr io_task_runner); + static void ShutdownSocketManager(); + + // This is called on the IO thread when data is available on the socket. + // |data| is guaranteed to be of exactly |data_size| length. Note that the + // implementation must not mutate or free the data. + virtual void OnDataAvailableOnIO(const void* data) = 0; + + protected: + void StartListening(); + void StopListening(); + + private: + const std::string socket_path_; + size_t data_size_; + + DISALLOW_COPY_AND_ASSIGN(DeviceSocketListener); +}; + +} // namespace athena + +#endif // ATHENA_SYSTEM_DEVICE_SOCKET_LISTENER_H_ diff --git a/athena/system/orientation_controller.cc b/athena/system/orientation_controller.cc new file mode 100644 index 0000000000000..671215bf8b9ec --- /dev/null +++ b/athena/system/orientation_controller.cc @@ -0,0 +1,143 @@ +// 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 "athena/screen/public/screen_manager.h" +#include "athena/system/orientation_controller.h" +#include "base/bind.h" +#include "base/file_util.h" +#include "base/files/file_path_watcher.h" +#include "base/message_loop/message_loop.h" +#include "base/task_runner.h" + +namespace athena { + +namespace { + +// Path of the socket which the sensor daemon creates. +const char kSocketPath[] = "/dev/sensors/orientation"; + +// Threshold after which to rotate in a given direction. +const int kGravityThreshold = 6.0f; + +// Minimum delay before triggering another orientation change. +const int kOrientationChangeDelayNS = 500000000; + +enum SensorType { + SENSOR_ACCELEROMETER, + SENSOR_LIGHT, + SENSOR_PROXIMITY +}; + +// A sensor event from the device. +struct DeviceSensorEvent { + // The type of event from the SensorType enum above. + int32_t type; + + // The time in nanoseconds at which the event happened. + int64_t timestamp; + + union { + // Accelerometer X,Y,Z values in SI units (m/s^2) including gravity. + // The orientation is described at + // http://www.html5rocks.com/en/tutorials/device/orientation/. + float data[3]; + + // Ambient (room) temperature in degrees Celcius. + float temperature; + + // Proximity sensor distance in centimeters. + float distance; + + // Ambient light level in SI lux units. + float light; + }; +}; + +} // namespace + +OrientationController::OrientationController( + scoped_refptr io_task_runner) + : DeviceSocketListener(kSocketPath, sizeof(DeviceSensorEvent)), + last_orientation_change_time_(0), + weak_factory_(this) { + CHECK(base::MessageLoopForUI::current()); + ui_task_runner_ = base::MessageLoopForUI::current()->task_runner(); + io_task_runner->PostTask(FROM_HERE, base::Bind( + &OrientationController::WatchForSocketPathOnIO, + make_scoped_refptr(this))); +} + +OrientationController::~OrientationController() { +} + +void OrientationController::WatchForSocketPathOnIO() { + CHECK(base::MessageLoopForIO::current()); + if (base::PathExists(base::FilePath(kSocketPath))) { + ui_task_runner_->PostTask(FROM_HERE, + base::Bind(&OrientationController::StartListening, + make_scoped_refptr(this))); + } else { + watcher_.reset(new base::FilePathWatcher); + watcher_->Watch( + base::FilePath(kSocketPath), false, + base::Bind(&OrientationController::OnFilePathChangedOnIO, + make_scoped_refptr(this))); + } +} + +void OrientationController::OnFilePathChangedOnIO(const base::FilePath& path, + bool error) { + watcher_.reset(); + if (error) + return; + + ui_task_runner_->PostTask(FROM_HERE, + base::Bind(&OrientationController::StartListening, + make_scoped_refptr(this))); +} + +void OrientationController::OnDataAvailableOnIO(const void* data) { + const DeviceSensorEvent* event = + static_cast(data); + if (event->type != SENSOR_ACCELEROMETER) + return; + + float gravity_x = event->data[0]; + float gravity_y = event->data[1]; + gfx::Display::Rotation rotation; + if (gravity_x < -kGravityThreshold) { + rotation = gfx::Display::ROTATE_270; + } else if (gravity_x > kGravityThreshold) { + rotation = gfx::Display::ROTATE_90; + } else if (gravity_y < -kGravityThreshold) { + rotation = gfx::Display::ROTATE_180; + } else if (gravity_y > kGravityThreshold) { + rotation = gfx::Display::ROTATE_0; + } else { + // No rotation as gravity threshold was not hit. + return; + } + + if (rotation == current_rotation_ || + event->timestamp - last_orientation_change_time_ < + kOrientationChangeDelayNS) { + return; + } + + last_orientation_change_time_ = event->timestamp; + current_rotation_ = rotation; + ui_task_runner_->PostTask(FROM_HERE, + base::Bind(&OrientationController::RotateOnUI, + make_scoped_refptr(this), rotation)); +} + +void OrientationController::RotateOnUI(gfx::Display::Rotation rotation) { + ScreenManager* screen_manager = ScreenManager::Get(); + // Since this is called from the IO thread, the screen manager may no longer + // exist. + if (screen_manager) + screen_manager->SetRotation(rotation); +} + +} // namespace athena diff --git a/athena/system/orientation_controller.h b/athena/system/orientation_controller.h new file mode 100644 index 0000000000000..f6dc6a8336c22 --- /dev/null +++ b/athena/system/orientation_controller.h @@ -0,0 +1,65 @@ +// 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 ATHENA_SYSTEM_ORIENTATION_CONTROLLER_H_ +#define ATHENA_SYSTEM_ORIENTATION_CONTROLLER_H_ + +#include "athena/system/device_socket_listener.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "ui/gfx/display.h" + +namespace base { +class FilePath; +class FilePathWatcher; +class TaskRunner; +} + +namespace athena { + +// Monitors accelerometers, detecting orientation changes. When a change is +// detected rotates the root window to match. +class OrientationController + : public DeviceSocketListener, + public base::RefCountedThreadSafe { + public: + OrientationController(scoped_refptr io_task_runner); + + private: + friend class base::RefCountedThreadSafe; + + virtual ~OrientationController(); + + // Watch for the socket path to be created, called on the IO thread. + void WatchForSocketPathOnIO(); + void OnFilePathChangedOnIO(const base::FilePath& path, bool error); + + // Overridden from device::DeviceSocketListener: + virtual void OnDataAvailableOnIO(const void* data) OVERRIDE; + + // Rotates the display to |rotation|, called on the UI thread. + void RotateOnUI(gfx::Display::Rotation rotation); + + // The last configured rotation. + gfx::Display::Rotation current_rotation_; + + // The timestamp of the last applied orientation change. + int64_t last_orientation_change_time_; + + // A task runner for the UI thread. + scoped_refptr ui_task_runner_; + + // File path watcher used to detect when sensors are present. + scoped_ptr watcher_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(OrientationController); +}; + +} // namespace athena + +#endif // ATHENA_SYSTEM_ORIENTATION_CONTROLLER_H_ diff --git a/athena/system/power_button_controller.cc b/athena/system/power_button_controller.cc index c54ca46419af9..b4195a3061a08 100644 --- a/athena/system/power_button_controller.cc +++ b/athena/system/power_button_controller.cc @@ -4,13 +4,25 @@ #include "athena/system/power_button_controller.h" +#include "athena/screen/public/screen_manager.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "ui/compositor/layer_animator.h" +#include "ui/compositor/scoped_layer_animation_settings.h" namespace athena { +namespace { + +// Duration of the shutdown animation. +const int kShutdownDurationMs = 1000; + +// Duration of the cancel shutdown animation. +const int kCancelShutdownDurationMs = 500; + +} // namespace PowerButtonController::PowerButtonController() : brightness_is_zero_(false), - shutdown_requested_(false) { + state_(STATE_OTHER) { chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( this); } @@ -20,6 +32,23 @@ PowerButtonController::~PowerButtonController() { this); } +void PowerButtonController::StartGrayscaleAndBrightnessAnimation( + float target, + int duration_ms, + gfx::Tween::Type tween_type) { + ui::LayerAnimator* animator = ScreenManager::Get()->GetScreenAnimator(); + ui::ScopedLayerAnimationSettings settings(animator); + settings.SetTransitionDuration( + base::TimeDelta::FromMilliseconds(duration_ms)); + settings.SetTweenType(tween_type); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + settings.AddObserver(this); + + animator->SetBrightness(target); + animator->SetGrayscale(target); +} + void PowerButtonController::BrightnessChanged(int level, bool user_initiated) { if (brightness_is_zero_) zero_brightness_end_time_ = base::TimeTicks::Now(); @@ -28,7 +57,7 @@ void PowerButtonController::BrightnessChanged(int level, bool user_initiated) { void PowerButtonController::PowerButtonEventReceived( bool down, - const base::TimeTicks& details) { + const base::TimeTicks& timestamp) { // Avoid requesting a shutdown if the power button is pressed while the screen // is off (http://crbug.com/128451) base::TimeDelta time_since_zero_brightness = brightness_is_zero_ ? @@ -37,8 +66,24 @@ void PowerButtonController::PowerButtonEventReceived( if (time_since_zero_brightness.InMilliseconds() <= kShortTimeMs) return; - if (down && !shutdown_requested_) { - shutdown_requested_ = true; + if (state_ == STATE_SHUTDOWN_REQUESTED) + return; + + StopObservingImplicitAnimations(); + if (down) { + state_ = STATE_PRE_SHUTDOWN_ANIMATION; + StartGrayscaleAndBrightnessAnimation( + 1.0f, kShutdownDurationMs, gfx::Tween::EASE_IN); + } else { + state_ = STATE_OTHER; + StartGrayscaleAndBrightnessAnimation( + 0.0f, kCancelShutdownDurationMs, gfx::Tween::EASE_IN_OUT); + } +} + +void PowerButtonController::OnImplicitAnimationsCompleted() { + if (state_ == STATE_PRE_SHUTDOWN_ANIMATION) { + state_ = STATE_SHUTDOWN_REQUESTED; chromeos::DBusThreadManager::Get() ->GetPowerManagerClient() ->RequestShutdown(); diff --git a/athena/system/power_button_controller.h b/athena/system/power_button_controller.h index 69fa9a8ac7153..0d4bd0e50b609 100644 --- a/athena/system/power_button_controller.h +++ b/athena/system/power_button_controller.h @@ -5,31 +5,52 @@ #ifndef ATHENA_SYSTEM_POWER_BUTTON_CONTROLLER_H_ #define ATHENA_SYSTEM_POWER_BUTTON_CONTROLLER_H_ +#include "base/time/time.h" #include "chromeos/dbus/power_manager_client.h" +#include "ui/compositor/layer_animation_observer.h" +#include "ui/gfx/animation/tween.h" namespace athena { // Shuts down in response to the power button being pressed. -class PowerButtonController : public chromeos::PowerManagerClient::Observer { +class PowerButtonController : public chromeos::PowerManagerClient::Observer, + public ui::ImplicitAnimationObserver { public: PowerButtonController(); virtual ~PowerButtonController(); + private: + enum State { + // The screen is animating prior to shutdown. Shutdown can be canceled. + STATE_PRE_SHUTDOWN_ANIMATION, + + // A D-Bus shutdown request has been sent. Shutdown cannot be canceled. + STATE_SHUTDOWN_REQUESTED, + + STATE_OTHER + }; + + // Animates the screen's grayscale and brightness to |target|. + void StartGrayscaleAndBrightnessAnimation(float target, + int duration_ms, + gfx::Tween::Type tween_type); + // chromeos::PowerManagerClient::Observer: virtual void BrightnessChanged(int level, bool user_initiated) OVERRIDE; virtual void PowerButtonEventReceived( bool down, const base::TimeTicks& timestamp) OVERRIDE; - private: + // ui::ImplicitAnimationObserver: + virtual void OnImplicitAnimationsCompleted() OVERRIDE; + // Whether the screen brightness was reduced to 0%. bool brightness_is_zero_; // The last time at which the screen brightness was 0%. base::TimeTicks zero_brightness_end_time_; - // Whether shutdown was requested. - bool shutdown_requested_; + State state_; DISALLOW_COPY_AND_ASSIGN(PowerButtonController); }; diff --git a/athena/system/public/system_ui.h b/athena/system/public/system_ui.h index c1e21d69c69cd..0032c5483cd17 100644 --- a/athena/system/public/system_ui.h +++ b/athena/system/public/system_ui.h @@ -6,13 +6,18 @@ #define ATHENA_SYSTEM_PUBLIC_SYSTEM_UI_H_ #include "athena/athena_export.h" +#include "base/memory/ref_counted.h" + +namespace base { +class TaskRunner; +} namespace athena { class ATHENA_EXPORT SystemUI { public: // Creates and deletes the singleton object of the SystemUI implementation. - static SystemUI* Create(); + static SystemUI* Create(scoped_refptr io_task_runner); static void Shutdown(); virtual ~SystemUI() {} diff --git a/athena/system/system_ui_impl.cc b/athena/system/system_ui_impl.cc index 4b94d03a32c47..5bd0e529c7abe 100644 --- a/athena/system/system_ui_impl.cc +++ b/athena/system/system_ui_impl.cc @@ -4,8 +4,11 @@ #include "athena/system/public/system_ui.h" +#include "athena/system/device_socket_listener.h" +#include "athena/system/orientation_controller.h" #include "athena/system/power_button_controller.h" #include "base/logging.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" namespace athena { @@ -15,13 +18,16 @@ SystemUI* instance = NULL; class SystemUIImpl : public SystemUI { public: - SystemUIImpl() : power_button_controller_(new PowerButtonController) { + SystemUIImpl(scoped_refptr io_task_runner) + : orientation_controller_(new OrientationController(io_task_runner)), + power_button_controller_(new PowerButtonController) { } virtual ~SystemUIImpl() { } private: + scoped_refptr orientation_controller_; scoped_ptr power_button_controller_; DISALLOW_COPY_AND_ASSIGN(SystemUIImpl); @@ -30,8 +36,10 @@ class SystemUIImpl : public SystemUI { } // namespace // static -SystemUI* SystemUI::Create() { - instance = new SystemUIImpl; +SystemUI* SystemUI::Create( + scoped_refptr io_task_runner) { + DeviceSocketListener::CreateSocketManager(io_task_runner); + instance = new SystemUIImpl(io_task_runner); return instance; } @@ -40,6 +48,7 @@ void SystemUI::Shutdown() { CHECK(instance); delete instance; instance = NULL; + DeviceSocketListener::ShutdownSocketManager(); } } // namespace athena diff --git a/athena/test/athena_test_base.cc b/athena/test/athena_test_base.cc index a8ee6d3e2c788..8ddc437e7ae54 100644 --- a/athena/test/athena_test_base.cc +++ b/athena/test/athena_test_base.cc @@ -5,6 +5,7 @@ #include "athena/test/athena_test_base.h" #include "athena/test/athena_test_helper.h" +#include "ui/aura/test/event_generator_delegate_aura.h" #include "ui/compositor/test/context_factories_for_test.h" #if defined(USE_X11) @@ -38,6 +39,7 @@ void AthenaTestBase::SetUp() { #if defined(USE_X11) aura::test::SetUseOverrideRedirectWindowByDefault(true); #endif + aura::test::InitializeAuraEventGeneratorDelegate(); helper_->SetUp(context_factory); } diff --git a/athena/wm/DEPS b/athena/wm/DEPS index f24508c9b3b1f..aa13df16bc0ac 100644 --- a/athena/wm/DEPS +++ b/athena/wm/DEPS @@ -1,7 +1,11 @@ include_rules = [ + "+athena/athena_export.h", + "+athena/common", "+athena/input/public", + "+athena/resources", "+athena/screen/public", "+ui/aura", + "+ui/base", "+ui/compositor", "+ui/events", "+ui/gfx", diff --git a/athena/wm/bezel_controller.cc b/athena/wm/bezel_controller.cc index fb6215533b87e..a4e815602e7b3 100644 --- a/athena/wm/bezel_controller.cc +++ b/athena/wm/bezel_controller.cc @@ -6,6 +6,10 @@ #include "ui/aura/window.h" #include "ui/events/event_handler.h" +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/screen.h" +#include "ui/wm/core/coordinate_conversion.h" namespace athena { namespace { @@ -15,7 +19,7 @@ namespace { // So setting this width fairly high for now. const float kBezelWidth = 20.0f; -const float kScrollPositionNone = -100; +const float kScrollDeltaNone = 0; bool ShouldProcessGesture(ui::EventType event_type) { return event_type == ui::ET_GESTURE_SCROLL_UPDATE || @@ -24,6 +28,24 @@ bool ShouldProcessGesture(ui::EventType event_type) { event_type == ui::ET_GESTURE_END; } +gfx::Display GetDisplay(aura::Window* window) { + gfx::Screen* screen = gfx::Screen::GetScreenFor(window); + return screen->GetDisplayNearestWindow(window); +} + +float GetDistance(const gfx::PointF& location, + aura::Window* window, + BezelController::Bezel bezel) { + DCHECK(bezel == BezelController::BEZEL_LEFT || + bezel == BezelController::BEZEL_RIGHT); + // Convert location from window coordinates to screen coordinates. + gfx::Point point_in_screen(gfx::ToRoundedPoint(location)); + wm::ConvertPointToScreen(window, &point_in_screen); + return bezel == BezelController::BEZEL_LEFT + ? point_in_screen.x() + : point_in_screen.x() - GetDisplay(window).bounds().width(); +} + } // namespace BezelController::BezelController(aura::Window* container) @@ -34,25 +56,22 @@ BezelController::BezelController(aura::Window* container) left_right_delegate_(NULL) { } -float BezelController::GetDistance(const gfx::PointF& position, - BezelController::Bezel bezel) { - DCHECK(bezel == BEZEL_LEFT || bezel == BEZEL_RIGHT); - return bezel == BEZEL_LEFT - ? position.x() - : position.x() - container_->GetBoundsInScreen().width(); +void BezelController::SetState(BezelController::State state) { + // Use SetState(State, float) if |state| is one of the BEZEL_SCROLLING states. + DCHECK_NE(state, BEZEL_SCROLLING_TWO_FINGERS); + DCHECK_NE(state, BEZEL_SCROLLING_ONE_FINGER); + SetState(state, kScrollDeltaNone); } void BezelController::SetState(BezelController::State state, - const gfx::PointF& scroll_position) { + float scroll_delta) { if (!left_right_delegate_ || state == state_) return; - if (state == BEZEL_SCROLLING_TWO_FINGERS) { - float delta = GetDistance(scroll_position, scroll_bezel_); - left_right_delegate_->ScrollBegin(scroll_bezel_, delta); - } else if (state_ == BEZEL_SCROLLING_TWO_FINGERS) { + if (state == BEZEL_SCROLLING_TWO_FINGERS) + left_right_delegate_->ScrollBegin(scroll_bezel_, scroll_delta); + else if (state_ == BEZEL_SCROLLING_TWO_FINGERS) left_right_delegate_->ScrollEnd(); - } state_ = state; if (state == NONE) { scroll_bezel_ = BEZEL_NONE; @@ -62,10 +81,10 @@ void BezelController::SetState(BezelController::State state, // Only implemented for LEFT and RIGHT bezels ATM. BezelController::Bezel BezelController::GetBezel(const gfx::PointF& location) { + int screen_width = GetDisplay(container_).bounds().width(); if (location.x() < kBezelWidth) { return BEZEL_LEFT; - } else if (location.x() > - container_->GetBoundsInScreen().width() - kBezelWidth) { + } else if (location.x() > screen_width - kBezelWidth) { return BEZEL_RIGHT; } else { return BEZEL_NONE; @@ -73,7 +92,7 @@ BezelController::Bezel BezelController::GetBezel(const gfx::PointF& location) { } void BezelController::OnGestureEvent(ui::GestureEvent* event) { - // TODO (mfomitchev): Currently we aren't retargetting or consuming any of the + // TODO(mfomitchev): Currently we aren't retargetting or consuming any of the // touch events. This means that content can prevent the generation of gesture // events and two-finger scroll won't work. Possible solution to this problem // is hosting our own gesture recognizer or retargetting touch events at the @@ -92,11 +111,15 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { const gfx::PointF& event_location = event->location_f(); const ui::GestureEventDetails& event_details = event->details(); int num_touch_points = event_details.touch_points(); + float scroll_delta = kScrollDeltaNone; + if (scroll_bezel_ != BEZEL_NONE) { + aura::Window* target_window = static_cast(event->target()); + scroll_delta = GetDistance(event_location, target_window, scroll_bezel_); + } if (type == ui::ET_GESTURE_BEGIN) { if (num_touch_points > 2) { - SetState(IGNORE_CURRENT_SCROLL, - gfx::Point(kScrollPositionNone, kScrollPositionNone)); + SetState(IGNORE_CURRENT_SCROLL); return; } BezelController::Bezel event_bezel = GetBezel(event->location_f()); @@ -104,12 +127,10 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { case NONE: scroll_bezel_ = event_bezel; scroll_target_ = event->target(); - if (event_bezel != BEZEL_LEFT && event_bezel != BEZEL_RIGHT) { - SetState(IGNORE_CURRENT_SCROLL, - gfx::Point(kScrollPositionNone, kScrollPositionNone)); - } else { - SetState(BEZEL_GESTURE_STARTED, event_location); - } + if (event_bezel != BEZEL_LEFT && event_bezel != BEZEL_RIGHT) + SetState(IGNORE_CURRENT_SCROLL); + else + SetState(BEZEL_GESTURE_STARTED); break; case IGNORE_CURRENT_SCROLL: break; @@ -120,12 +141,11 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { DCHECK_NE(scroll_bezel_, BEZEL_NONE); if (event_bezel != scroll_bezel_) { - SetState(IGNORE_CURRENT_SCROLL, - gfx::Point(kScrollPositionNone, kScrollPositionNone)); + SetState(IGNORE_CURRENT_SCROLL); return; } if (state_ == BEZEL_SCROLLING_ONE_FINGER) - SetState(BEZEL_SCROLLING_TWO_FINGERS, event_location); + SetState(BEZEL_SCROLLING_TWO_FINGERS); break; case BEZEL_SCROLLING_TWO_FINGERS: // Should've exited above @@ -138,10 +158,9 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { CHECK(scroll_target_); if (num_touch_points == 1) { - SetState(NONE, gfx::Point(kScrollPositionNone, kScrollPositionNone)); + SetState(NONE); } else { - SetState(IGNORE_CURRENT_SCROLL, - gfx::Point(kScrollPositionNone, kScrollPositionNone)); + SetState(IGNORE_CURRENT_SCROLL); } } else if (type == ui::ET_GESTURE_SCROLL_BEGIN) { DCHECK(state_ == IGNORE_CURRENT_SCROLL || state_ == BEZEL_GESTURE_STARTED); @@ -149,19 +168,18 @@ void BezelController::OnGestureEvent(ui::GestureEvent* event) { return; if (num_touch_points == 1) { - SetState(BEZEL_SCROLLING_ONE_FINGER, event_location); + SetState(BEZEL_SCROLLING_ONE_FINGER, scroll_delta); return; } DCHECK_EQ(num_touch_points, 2); - SetState(BEZEL_SCROLLING_TWO_FINGERS, event_location); + SetState(BEZEL_SCROLLING_TWO_FINGERS, scroll_delta); if (left_right_delegate_->CanScroll()) event->SetHandled(); } else if (type == ui::ET_GESTURE_SCROLL_UPDATE) { if (state_ != BEZEL_SCROLLING_TWO_FINGERS) return; - float scroll_delta = GetDistance(event_location, scroll_bezel_); left_right_delegate_->ScrollUpdate(scroll_delta); if (left_right_delegate_->CanScroll()) event->SetHandled(); diff --git a/athena/wm/bezel_controller.h b/athena/wm/bezel_controller.h index 30fe974f6cbb7..7bec71ad343c1 100644 --- a/athena/wm/bezel_controller.h +++ b/athena/wm/bezel_controller.h @@ -35,12 +35,15 @@ class BezelController : public ui::EventHandler { virtual ~ScrollDelegate() {} // Beginning of a bezel scroll gesture started from the |bezel|. + // |delta| is the difference between the x-coordinate of the current scroll + // position and the bezel. It will be zero or negative for the right bezel. virtual void ScrollBegin(Bezel bezel, float delta) = 0; // End of the current bezel scroll virtual void ScrollEnd() = 0; // Update of the scroll position for the currently active bezel scroll. + // |delta| has the same meaning as in ScrollBegin(). virtual void ScrollUpdate(float delta) = 0; // Should return false if the delegate isn't going to react to the scroll @@ -67,11 +70,10 @@ class BezelController : public ui::EventHandler { BEZEL_SCROLLING_TWO_FINGERS, }; - // Calculates the distance from |position| to the |bezel|. - float GetDistance(const gfx::PointF& position, Bezel bezel); - - // |scroll_position| only needs to be passed in the scrolling state - void SetState(State state, const gfx::PointF& scroll_position); + void SetState(State state); + // |scroll_delta| only needs to be passed when |state| is one of the + // BEZEL_SROLLING states. + void SetState(State state, float scroll_delta); // Returns the bezel corresponding to the |location| or BEZEL_NONE if the // location is outside of the bezel area. diff --git a/athena/wm/overview_toolbar.cc b/athena/wm/overview_toolbar.cc new file mode 100644 index 0000000000000..4537a04c43ba6 --- /dev/null +++ b/athena/wm/overview_toolbar.cc @@ -0,0 +1,196 @@ +// 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 "athena/wm/overview_toolbar.h" + +#include "athena/resources/grit/athena_resources.h" +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" +#include "ui/aura/window.h" +#include "ui/base/resource/resource_bundle.h" +#include "ui/compositor/closure_animation_observer.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/layer_delegate.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/events/event.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/transform.h" + +namespace { + +const int kActionButtonImageSize = 54; +const int kActionButtonTextSize = 20; +const int kActionButtonPaddingFromRight = 32; +} + +namespace athena { + +class ActionButton : public ui::LayerDelegate { + public: + ActionButton(int resource_id, const std::string& label) + : resource_id_(resource_id), label_(base::UTF8ToUTF16(label)) { + layer_.reset(new ui::Layer(ui::LAYER_TEXTURED)); + layer_->set_delegate(this); + layer_->SetFillsBoundsOpaquely(false); + layer_->SetVisible(true); + layer_->SetOpacity(0); + } + + virtual ~ActionButton() {} + + static void DestroyAfterFadeout(scoped_ptr button) { + ui::Layer* layer = button->layer(); + ui::ScopedLayerAnimationSettings settings(layer->GetAnimator()); + settings.AddObserver(new ui::ClosureAnimationObserver( + base::Bind(&ActionButton::DestroyImmediately, base::Passed(&button)))); + layer->SetOpacity(0); + } + + void SetPosition(const gfx::Point& position) { + layer_->SetBounds( + gfx::Rect(position, + gfx::Size(kActionButtonImageSize, + kActionButtonImageSize + kActionButtonTextSize))); + } + + ui::Layer* layer() { return layer_.get(); } + + private: + static void DestroyImmediately(scoped_ptr button) { + button.reset(); + } + + // ui::LayerDelegate: + virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + canvas->DrawImageInt(*bundle.GetImageSkiaNamed(resource_id_), 0, 0); + gfx::ShadowValues shadow; + shadow.push_back(gfx::ShadowValue(gfx::Point(0, 1), 2, SK_ColorBLACK)); + shadow.push_back(gfx::ShadowValue(gfx::Point(0, -1), 2, SK_ColorBLACK)); + canvas->DrawStringRectWithShadows(label_, + gfx::FontList(), + SK_ColorWHITE, + gfx::Rect(0, + kActionButtonImageSize, + kActionButtonImageSize, + kActionButtonTextSize), + 0, + gfx::Canvas::TEXT_ALIGN_CENTER, + shadow); + } + + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {} + virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE { + return base::Closure(); + } + + int resource_id_; + base::string16 label_; + scoped_ptr layer_; + + DISALLOW_COPY_AND_ASSIGN(ActionButton); +}; + +OverviewToolbar::OverviewToolbar(aura::Window* container) + : shown_(false), + close_(new ActionButton(IDR_ATHENA_OVERVIEW_TRASH, "Close")), + split_(new ActionButton(IDR_ATHENA_OVERVIEW_SPLIT, "Split")), + current_action_(ACTION_TYPE_NONE), + container_bounds_(container->bounds()) { + const int kPaddingFromBottom = 200; + const int kPaddingBetweenButtons = 200; + + int x = container_bounds_.right() - + (kActionButtonPaddingFromRight + kActionButtonImageSize); + int y = container_bounds_.bottom() - + (kPaddingFromBottom + kActionButtonImageSize); + split_->SetPosition(gfx::Point(x, y)); + y -= kPaddingBetweenButtons; + close_->SetPosition(gfx::Point(x, y)); + + container->layer()->Add(split_->layer()); + container->layer()->Add(close_->layer()); +} + +OverviewToolbar::~OverviewToolbar() { + // If the buttons are visible, then fade them out, instead of destroying them + // immediately. + if (shown_) { + ActionButton::DestroyAfterFadeout(split_.Pass()); + ActionButton::DestroyAfterFadeout(close_.Pass()); + } +} + +OverviewToolbar::ActionType OverviewToolbar::GetHighlightAction( + const ui::GestureEvent& event) const { + if (IsEventOverButton(split_.get(), event)) + return ACTION_TYPE_SPLIT; + if (IsEventOverButton(close_.get(), event)) + return ACTION_TYPE_CLOSE; + return ACTION_TYPE_NONE; +} + +void OverviewToolbar::SetHighlightAction(ActionType action) { + if (current_action_ == action) + return; + current_action_ = action; + if (!shown_) { + ShowActionButtons(); + } else { + TransformButton(close_.get()); + TransformButton(split_.get()); + } +} + +void OverviewToolbar::ShowActionButtons() { + if (!shown_) + ToggleActionButtonsVisibility(); +} + +void OverviewToolbar::HideActionButtons() { + if (shown_) + ToggleActionButtonsVisibility(); +} + +void OverviewToolbar::ToggleActionButtonsVisibility() { + shown_ = !shown_; + TransformButton(close_.get()); + TransformButton(split_.get()); +} + +bool OverviewToolbar::IsEventOverButton(ActionButton* button, + const ui::GestureEvent& event) const { + const int kBoundsInsetForTarget = 30; + gfx::RectF bounds = button->layer()->bounds(); + bounds.Inset(-kBoundsInsetForTarget, -kBoundsInsetForTarget); + return bounds.Contains(event.location()); +} + +gfx::Transform OverviewToolbar::ComputeTransformFor( + ActionButton* button) const { + if (!shown_) + return gfx::Transform(); + + const float kHighlightScale = 1.5; + bool button_is_highlighted = + (current_action_ == ACTION_TYPE_CLOSE && button == close_.get()) || + (current_action_ == ACTION_TYPE_SPLIT && button == split_.get()); + gfx::Transform transform; + if (button_is_highlighted) { + transform.Translate(-kActionButtonImageSize * (kHighlightScale - 1) / 2, 0); + transform.Scale(kHighlightScale, kHighlightScale); + } + return transform; +} + +void OverviewToolbar::TransformButton(ActionButton* button) { + ui::ScopedLayerAnimationSettings split_settings( + button->layer()->GetAnimator()); + split_settings.SetTweenType(gfx::Tween::SMOOTH_IN_OUT); + button->layer()->SetTransform(ComputeTransformFor(button)); + button->layer()->SetOpacity(shown_ ? 1 : 0); +} + +} // namespace athena diff --git a/athena/wm/overview_toolbar.h b/athena/wm/overview_toolbar.h new file mode 100644 index 0000000000000..a6fdb3b8415e1 --- /dev/null +++ b/athena/wm/overview_toolbar.h @@ -0,0 +1,68 @@ +// 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 ATHENA_WM_OVERVIEW_TOOLBAR_H_ +#define ATHENA_WM_OVERVIEW_TOOLBAR_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/geometry/rect.h" + +namespace aura { +class Window; +} + +namespace gfx { +class Transform; +} + +namespace ui { +class GestureEvent; +} + +namespace athena { + +class ActionButton; + +// Responsible for showing action-buttons at the right edge of the screen during +// overview mode. +class OverviewToolbar { + public: + enum ActionType { + ACTION_TYPE_NONE, + ACTION_TYPE_CLOSE, + ACTION_TYPE_SPLIT, + }; + + explicit OverviewToolbar(aura::Window* container); + virtual ~OverviewToolbar(); + + ActionType current_action() const { return current_action_; } + + // Returns the action the gesture-event is targeting. + ActionType GetHighlightAction(const ui::GestureEvent& event) const; + + void SetHighlightAction(ActionType action); + void ShowActionButtons(); + void HideActionButtons(); + + private: + void ToggleActionButtonsVisibility(); + bool IsEventOverButton(ActionButton* button, + const ui::GestureEvent& event) const; + gfx::Transform ComputeTransformFor(ActionButton* button) const; + void TransformButton(ActionButton* button); + + bool shown_; + scoped_ptr close_; + scoped_ptr split_; + ActionType current_action_; + const gfx::Rect container_bounds_; + + DISALLOW_COPY_AND_ASSIGN(OverviewToolbar); +}; + +} // namespace athena + +#endif // ATHENA_WM_OVERVIEW_TOOLBAR_H_ diff --git a/athena/wm/public/window_list_provider.h b/athena/wm/public/window_list_provider.h new file mode 100644 index 0000000000000..d22aa3da37606 --- /dev/null +++ b/athena/wm/public/window_list_provider.h @@ -0,0 +1,24 @@ +// 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 ATHENA_WM_PUBLIC_WINDOW_LIST_PROVIDER_H_ +#define ATHENA_WM_PUBLIC_WINDOW_LIST_PROVIDER_H_ + +#include "athena/athena_export.h" +#include "ui/aura/window.h" + +namespace athena { + +// Interface for an ordered list of aura::Window objects. +class ATHENA_EXPORT WindowListProvider { + public: + virtual ~WindowListProvider() {} + + // Returns an ordered list of windows. + virtual aura::Window::Windows GetWindowList() const = 0; +}; + +} // namespace athena + +#endif // ATHENA_WM_PUBLIC_WINDOW_LIST_PROVIDER_H_ diff --git a/athena/wm/public/window_manager.h b/athena/wm/public/window_manager.h index 23aab7f4a4967..04e2cce6ca8a4 100644 --- a/athena/wm/public/window_manager.h +++ b/athena/wm/public/window_manager.h @@ -24,6 +24,8 @@ class ATHENA_EXPORT WindowManager { virtual void ToggleOverview() = 0; + virtual bool IsOverviewModeActive() = 0; + virtual void AddObserver(WindowManagerObserver* observer) = 0; virtual void RemoveObserver(WindowManagerObserver* observer) = 0; }; diff --git a/athena/wm/public/window_manager_observer.h b/athena/wm/public/window_manager_observer.h index e7f6bcfa610f8..216b58ceeee3e 100644 --- a/athena/wm/public/window_manager_observer.h +++ b/athena/wm/public/window_manager_observer.h @@ -13,10 +13,10 @@ class ATHENA_EXPORT WindowManagerObserver { public: virtual ~WindowManagerObserver() {} - // Called when the overview mode is displayed. + // Called immediately before the overview mode is displayed. virtual void OnOverviewModeEnter() = 0; - // Called when going out of overview mode. + // Called immediately after going out of the overview mode. virtual void OnOverviewModeExit() = 0; }; diff --git a/athena/wm/split_view_controller.cc b/athena/wm/split_view_controller.cc index 7da36755f7e76..232908994061e 100644 --- a/athena/wm/split_view_controller.cc +++ b/athena/wm/split_view_controller.cc @@ -4,29 +4,270 @@ #include "athena/wm/split_view_controller.h" +#include + +#include "athena/wm/public/window_list_provider.h" +#include "athena/wm/public/window_manager.h" +#include "base/bind.h" #include "ui/aura/window.h" +#include "ui/compositor/closure_animation_observer.h" +#include "ui/compositor/layer_animation_observer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event_handler.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" +#include "ui/wm/core/window_util.h" namespace athena { -SplitViewController::SplitViewController() { +SplitViewController::SplitViewController( + aura::Window* container, + WindowListProvider* window_list_provider) + : state_(INACTIVE), + container_(container), + window_list_provider_(window_list_provider), + left_window_(NULL), + right_window_(NULL), + separator_position_(0), + weak_factory_(this) { } SplitViewController::~SplitViewController() { } +bool SplitViewController::IsSplitViewModeActive() const { + return state_ == ACTIVE; +} + +void SplitViewController::ActivateSplitMode(aura::Window* left, + aura::Window* right) { + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + aura::Window::Windows::reverse_iterator iter = windows.rbegin(); + if (state_ == ACTIVE) { + if (left_window_ == right) + left_window_ = left; + if (right_window_ == left) + right_window_ = right; + + if (!left) + left = left_window_; + if (!right) + right = right_window_; + } + + if (!left && iter != windows.rend()) { + left = *iter; + iter++; + if (left == right && iter != windows.rend()) { + left = *iter; + iter++; + } + } + + if (!right && iter != windows.rend()) { + right = *iter; + iter++; + if (right == left && iter != windows.rend()) { + right = *iter; + iter++; + } + } + + state_ = ACTIVE; + if (right_window_ != right) { + right_window_ = right; + container_->StackChildAtTop(right_window_); + } + if (left_window_ != left) { + left_window_ = left; + container_->StackChildAtTop(left_window_); + } + UpdateLayout(true); +} + +void SplitViewController::ReplaceWindow(aura::Window* window, + aura::Window* replace_with) { + CHECK(IsSplitViewModeActive()); + CHECK(replace_with); + CHECK(window == left_window_ || window == right_window_); + CHECK(replace_with != left_window_ && replace_with != right_window_); +#if !defined(NDEBUG) + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + DCHECK(std::find(windows.begin(), windows.end(), replace_with) != + windows.end()); +#endif + + replace_with->SetBounds(window->bounds()); + replace_with->SetTransform(gfx::Transform()); + if (window == left_window_) + left_window_ = replace_with; + else + right_window_ = replace_with; + wm::ActivateWindow(replace_with); + window->SetTransform(gfx::Transform()); +} + +void SplitViewController::DeactivateSplitMode() { + CHECK_NE(SCROLLING, state_); + state_ = INACTIVE; + left_window_ = right_window_ = NULL; +} + +void SplitViewController::UpdateLayout(bool animate) { + if (!left_window_) + return; + CHECK(right_window_); + gfx::Transform left_transform; + gfx::Transform right_transform; + int container_width = container_->GetBoundsInScreen().width(); + if (state_ == ACTIVE) { + // This method should only be called once in ACTIVE state when + // the left and rightwindows are still full screen and need to be resized. + CHECK_EQ(left_window_->bounds().width(), container_width); + CHECK_EQ(right_window_->bounds().width(), container_width); + // Windows should be resized via an animation when entering the ACTIVE + // state. + CHECK(animate); + // We scale the windows here, but when the animation finishes, we reset + // the scaling and update the window bounds to the proper size - see + // OnAnimationCompleted(). + left_transform.Scale(.5, 1); + right_transform.Scale(.5, 1); + right_transform.Translate(container_width, 0); + } else { + left_transform.Translate(separator_position_ - container_width, 0); + right_transform.Translate(separator_position_, 0); + } + left_window_->Show(); + right_window_->Show(); + SetWindowTransform(left_window_, left_transform, animate); + SetWindowTransform(right_window_, right_transform, animate); +} + +void SplitViewController::SetWindowTransform(aura::Window* window, + const gfx::Transform& transform, + bool animate) { + if (animate) { + scoped_refptr animator = window->layer()->GetAnimator(); + ui::ScopedLayerAnimationSettings settings(animator); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + settings.AddObserver(new ui::ClosureAnimationObserver( + base::Bind(&SplitViewController::OnAnimationCompleted, + weak_factory_.GetWeakPtr(), + window))); + window->SetTransform(transform); + } else { + window->SetTransform(transform); + } +} + +void SplitViewController::OnAnimationCompleted(aura::Window* window) { + DCHECK(window == left_window_ || window == right_window_); + if (state_ == ACTIVE) { + gfx::Rect window_bounds = gfx::Rect(container_->bounds().size()); + int container_width = window_bounds.width(); + window_bounds.set_width(container_width / 2); + window->SetTransform(gfx::Transform()); + if (window == left_window_) { + left_window_->SetBounds(window_bounds); + } else { + window_bounds.set_x(container_width / 2); + right_window_->SetBounds(window_bounds); + } + } else { + int container_width = container_->bounds().width(); + window->SetTransform(gfx::Transform()); + if (window == left_window_) { + if (separator_position_ == 0) + left_window_->Hide(); + if (state_ == INACTIVE) + left_window_ = NULL; + } else { + if (separator_position_ == container_width) + right_window_->Hide(); + if (state_ == INACTIVE) + right_window_ = NULL; + } + } +} + +void SplitViewController::UpdateSeparatorPositionFromScrollDelta(float delta) { + gfx::Screen* screen = gfx::Screen::GetScreenFor(container_); + const gfx::Rect& display_bounds = + screen->GetDisplayNearestWindow(container_).bounds(); + gfx::Rect container_bounds = container_->GetBoundsInScreen(); + separator_position_ = + delta > 0 ? ((int)delta) + display_bounds.x() - container_bounds.x() + : display_bounds.right() - container_bounds.x() + delta; +} + +/////////////////////////////////////////////////////////////////////////////// +// BezelController::ScrollDelegate: + void SplitViewController::ScrollBegin(BezelController::Bezel bezel, float delta) { + if (!CanScroll()) + return; + state_ = SCROLLING; + + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + CHECK(windows.size() >= 2); + aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); + aura::Window* current_window = *(iter); + CHECK(wm::IsActiveWindow(current_window)); + + if (delta > 0) { + right_window_ = current_window; + left_window_ = *(iter + 1); + } else { + left_window_ = current_window; + right_window_ = *(iter + 1); + } + + CHECK(left_window_); + CHECK(right_window_); + + UpdateSeparatorPositionFromScrollDelta(delta); + UpdateLayout(false); } void SplitViewController::ScrollEnd() { + if (state_ != SCROLLING) + return; + + // Max distance from the scroll end position to the middle of the screen where + // we would go into the split view mode. + const int kMaxDistanceFromMiddle = 120; + int container_width = container_->GetBoundsInScreen().width(); + if (std::abs(container_width / 2 - separator_position_) <= + kMaxDistanceFromMiddle) { + state_ = ACTIVE; + separator_position_ = container_width / 2; + } else if (separator_position_ < container_width / 2) { + separator_position_ = 0; + state_ = INACTIVE; + wm::ActivateWindow(right_window_); + } else { + separator_position_ = container_width; + state_ = INACTIVE; + wm::ActivateWindow(left_window_); + } + UpdateLayout(true); } void SplitViewController::ScrollUpdate(float delta) { + if (state_ != SCROLLING) + return; + UpdateSeparatorPositionFromScrollDelta(delta); + UpdateLayout(false); } bool SplitViewController::CanScroll() { - return false; + // TODO(mfomitchev): return false in vertical orientation, in full screen. + bool result = (!IsSplitViewModeActive() && + window_list_provider_->GetWindowList().size() >= 2); + return result; } } // namespace athena diff --git a/athena/wm/split_view_controller.h b/athena/wm/split_view_controller.h index e0652def1b190..8d8db77aca4bc 100644 --- a/athena/wm/split_view_controller.h +++ b/athena/wm/split_view_controller.h @@ -5,25 +5,94 @@ #ifndef ATHENA_WM_SPLIT_VIEW_CONTROLLER_H_ #define ATHENA_WM_SPLIT_VIEW_CONTROLLER_H_ +#include "athena/athena_export.h" #include "athena/wm/bezel_controller.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" + +namespace gfx { +class Transform; +} namespace athena { +class WindowListProvider; // Responsible for entering split view mode, exiting from split view mode, and // laying out the windows in split view mode. -class SplitViewController : public BezelController::ScrollDelegate { +class ATHENA_EXPORT SplitViewController + : public BezelController::ScrollDelegate { public: - SplitViewController(); + SplitViewController(aura::Window* container, + WindowListProvider* window_list_provider); + virtual ~SplitViewController(); + bool IsSplitViewModeActive() const; + + // Activates split-view mode with |left| and |right| windows. If |left| and/or + // |right| is NULL, then the first window in the window-list (which is neither + // |left| nor |right|) is selected instead. + void ActivateSplitMode(aura::Window* left, aura::Window* right); + + // Resets the internal state to an inactive state. Calling this does not + // change the window bounds/transforms etc. The caller must take care of + // making any necessary changes. + void DeactivateSplitMode(); + + void ReplaceWindow(aura::Window* window, + aura::Window* replace_with); + + aura::Window* left_window() { return left_window_; } + aura::Window* right_window() { return right_window_; } + private: - // BezelController::ScrollDelegate overrides. + enum State { + // Split View mode is not active. |left_window_| and |right_window| are + // NULL. + INACTIVE, + // Two windows |left_window_| and |right_window| are shown side by side and + // there is a horizontal scroll in progress which is dragging the separator + // between the two windows. + SCROLLING, + // Split View mode is active with |left_window_| and |right_window| showing + // side by side each occupying half the screen. No scroll in progress. + ACTIVE + }; + + void UpdateLayout(bool animate); + + void SetWindowTransform(aura::Window* left_window, + const gfx::Transform& transform, + bool animate); + + void OnAnimationCompleted(aura::Window* window); + + void UpdateSeparatorPositionFromScrollDelta(float delta); + + // BezelController::ScrollDelegate: virtual void ScrollBegin(BezelController::Bezel bezel, float delta) OVERRIDE; virtual void ScrollEnd() OVERRIDE; virtual void ScrollUpdate(float delta) OVERRIDE; virtual bool CanScroll() OVERRIDE; + State state_; + + aura::Window* container_; + + // Provider of the list of windows to cycle through. Not owned. + WindowListProvider* window_list_provider_; + + // Windows for the left and right activities shown in SCROLLING and ACTIVE + // states. In INACTIVE state these are NULL. + aura::Window* left_window_; + aura::Window* right_window_; + + // Position of the separator between left_window_ and right_window_ in + // container_ coordinates (along the x axis). + int separator_position_; + + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(SplitViewController); }; diff --git a/athena/wm/split_view_controller_unittest.cc b/athena/wm/split_view_controller_unittest.cc new file mode 100644 index 0000000000000..268a3acf1d24e --- /dev/null +++ b/athena/wm/split_view_controller_unittest.cc @@ -0,0 +1,72 @@ +// 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 "athena/wm/split_view_controller.h" + +#include "athena/common/fill_layout_manager.h" +#include "athena/test/athena_test_base.h" +#include "athena/wm/window_list_provider_impl.h" +#include "base/memory/scoped_vector.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window.h" + +namespace athena { + +typedef test::AthenaTestBase SplitViewControllerTest; + +// Tests that when split mode is activated, the windows on the left and right +// are selected correctly. +TEST_F(SplitViewControllerTest, SplitModeActivation) { + aura::test::TestWindowDelegate delegate; + ScopedVector windows; + const int kNumWindows = 6; + for (size_t i = 0; i < kNumWindows; ++i) { + aura::Window* window = new aura::Window(&delegate); + window->SetType(ui::wm::WINDOW_TYPE_NORMAL); + window->Init(aura::WINDOW_LAYER_SOLID_COLOR); + root_window()->AddChild(window); + windows.push_back(window); + } + + scoped_ptr list_provider( + new WindowListProviderImpl(root_window())); + SplitViewController controller(root_window(), list_provider.get()); + ASSERT_FALSE(controller.IsSplitViewModeActive()); + + controller.ActivateSplitMode(NULL, NULL); + ASSERT_TRUE(controller.IsSplitViewModeActive()); + // The last two windows should be on the left and right, respectively. + EXPECT_EQ(windows[kNumWindows - 1], controller.left_window()); + EXPECT_EQ(windows[kNumWindows - 2], controller.right_window()); + + // Select the window that is currently on the left for the right panel. The + // windows should switch. + controller.ActivateSplitMode(NULL, windows[kNumWindows - 1]); + EXPECT_EQ(windows[kNumWindows - 2], controller.left_window()); + EXPECT_EQ(windows[kNumWindows - 1], controller.right_window()); + + controller.ActivateSplitMode(windows[kNumWindows - 1], NULL); + EXPECT_EQ(windows[kNumWindows - 1], controller.left_window()); + EXPECT_EQ(windows[kNumWindows - 2], controller.right_window()); + + // Select one of the windows behind the stacks for the right panel. The window + // on the left should remain unchanged. + controller.ActivateSplitMode(NULL, windows[0]); + EXPECT_EQ(windows[kNumWindows - 1], controller.left_window()); + EXPECT_EQ(windows[0], controller.right_window()); + EXPECT_EQ(windows[0], *list_provider->GetWindowList().rbegin()); + + controller.ActivateSplitMode(windows[1], NULL); + EXPECT_EQ(windows[1], controller.left_window()); + EXPECT_EQ(windows[0], controller.right_window()); + EXPECT_EQ(windows[1], *list_provider->GetWindowList().rbegin()); + + controller.ActivateSplitMode(windows[4], windows[5]); + EXPECT_EQ(windows[4], controller.left_window()); + EXPECT_EQ(windows[5], controller.right_window()); + EXPECT_EQ(windows[4], *list_provider->GetWindowList().rbegin()); + EXPECT_EQ(windows[5], *(list_provider->GetWindowList().rbegin() + 1)); +} + +} // namespace athena diff --git a/athena/wm/title_drag_controller.cc b/athena/wm/title_drag_controller.cc new file mode 100644 index 0000000000000..b41b7fd07f74d --- /dev/null +++ b/athena/wm/title_drag_controller.cc @@ -0,0 +1,144 @@ +// 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 "athena/wm/title_drag_controller.h" + +#include "base/bind.h" +#include "ui/aura/window.h" +#include "ui/aura/window_delegate.h" +#include "ui/base/hit_test.h" +#include "ui/compositor/closure_animation_observer.h" +#include "ui/compositor/layer.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/wm/core/shadow.h" +#include "ui/wm/core/window_util.h" + +namespace { + +// The minimum amount to drag to confirm a window switch at the end of the +// non-fling gesture. +const int kMinDragDistanceForSwitch = 300; +// The minimum velocity to confirm a window switch for a fling (only applicable +// if the amount dragged was not sufficient, i.e. smaller than +// kMinDragDistanceForSwitch). +const int kMinDragVelocityForSwitch = 5000; + +} + +namespace athena { + +TitleDragController::TitleDragController(aura::Window* container, + TitleDragControllerDelegate* delegate) + : container_(container), + delegate_(delegate), + weak_ptr_(this) { + CHECK(container_); + CHECK(delegate_); + container_->AddPreTargetHandler(this); +} + +TitleDragController::~TitleDragController() { + container_->RemovePreTargetHandler(this); +} + +void TitleDragController::EndTransition(aura::Window* window, bool complete) { + ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + settings.AddObserver(new ui::ClosureAnimationObserver( + base::Bind(&TitleDragController::OnTransitionEnd, + weak_ptr_.GetWeakPtr(), + window, + complete))); + gfx::Transform transform; + transform.Translate(0, complete ? window->bounds().height() : 0); + window->SetTransform(transform); +} + +void TitleDragController::OnTransitionEnd(aura::Window* window, bool complete) { + weak_ptr_.InvalidateWeakPtrs(); + if (!tracker_.Contains(window)) + window = NULL; + shadow_.reset(); + if (window) { + window->SetTransform(gfx::Transform()); + tracker_.Remove(window); + } + if (complete && window && wm::IsActiveWindow(window)) + delegate_->OnTitleDragCompleted(window); + else + delegate_->OnTitleDragCanceled(window); +} + +void TitleDragController::OnGestureEvent(ui::GestureEvent* gesture) { + // Do not process any gesture events if an animation is still in progress from + // a previous drag. + if (weak_ptr_.HasWeakPtrs()) + return; + + if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) { + // It is possible to start a gesture sequence on a second window while a + // drag is already in progress (e.g. the user starts interacting with the + // window that is being revealed by the title-drag). Ignore these gesture + // sequences. + if (!tracker_.windows().empty()) + return; + aura::Window* window = static_cast(gesture->target()); + if (!window || !window->delegate()) + return; + int component = + window->delegate()->GetNonClientComponent(gesture->location()); + if (component != HTCAPTION) + return; + if (!delegate_->GetWindowBehind(window)) + return; + tracker_.Add(window); + drag_start_location_ = gesture->root_location(); + return; + } + + // If this gesture is for a different window, then ignore. + aura::Window* window = static_cast(gesture->target()); + if (!tracker_.Contains(window)) + return; + + if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) { + delegate_->OnTitleDragStarted(window); + shadow_.reset(new wm::Shadow()); + shadow_->Init(wm::Shadow::STYLE_ACTIVE); + shadow_->SetContentBounds(gfx::Rect(window->bounds().size())); + window->layer()->Add(shadow_->layer()); + gesture->SetHandled(); + return; + } + + if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { + gfx::Vector2dF distance = gesture->root_location() - drag_start_location_; + gfx::Transform transform; + transform.Translate(0, std::max(0.f, distance.y())); + window->SetTransform(transform); + gesture->SetHandled(); + return; + } + + if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { + gfx::Vector2dF distance = gesture->root_location() - drag_start_location_; + EndTransition(window, distance.y() >= kMinDragDistanceForSwitch); + gesture->SetHandled(); + return; + } + + if (gesture->type() == ui::ET_SCROLL_FLING_START) { + gfx::Vector2dF distance = gesture->root_location() - drag_start_location_; + bool swipe_downwards = gesture->details().velocity_y() > 0; + EndTransition( + window, + swipe_downwards && + (distance.y() >= kMinDragDistanceForSwitch || + gesture->details().velocity_y() >= kMinDragVelocityForSwitch)); + gesture->SetHandled(); + } +} + +} // namespace athena diff --git a/athena/wm/title_drag_controller.h b/athena/wm/title_drag_controller.h new file mode 100644 index 0000000000000..1b4b2e58defa9 --- /dev/null +++ b/athena/wm/title_drag_controller.h @@ -0,0 +1,66 @@ +// 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 ATHENA_WM_TITLE_DRAG_CONTROLLER_H_ +#define ATHENA_WM_TITLE_DRAG_CONTROLLER_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "ui/aura/window_tracker.h" +#include "ui/events/event_handler.h" +#include "ui/gfx/geometry/point.h" + +namespace aura { +class Window; +} + +namespace wm { +class Shadow; +} + +namespace athena { + +class TitleDragControllerDelegate { + public: + virtual ~TitleDragControllerDelegate() {} + + // Returns the window behind |window|. The returned window is the one that + // would be revealed while |window|'s title is dragged. + virtual aura::Window* GetWindowBehind(aura::Window* window) = 0; + + // Notifies the delegate during various stages of the drag. + virtual void OnTitleDragStarted(aura::Window* window) = 0; + virtual void OnTitleDragCompleted(aura::Window* window) = 0; + virtual void OnTitleDragCanceled(aura::Window* window) = 0; +}; + +// Responsible for allowing dragging a window by its title bar. +class TitleDragController : public ui::EventHandler { + public: + TitleDragController(aura::Window* container, + TitleDragControllerDelegate* delegate); + virtual ~TitleDragController(); + + private: + void EndTransition(aura::Window* window, bool complete); + void OnTransitionEnd(aura::Window* window, bool complete); + + // ui::EventHandler: + virtual void OnGestureEvent(ui::GestureEvent* gesture) OVERRIDE; + + aura::Window* container_; + TitleDragControllerDelegate* delegate_; + gfx::Point drag_start_location_; + scoped_ptr shadow_; + aura::WindowTracker tracker_; + + base::WeakPtrFactory weak_ptr_; + + DISALLOW_COPY_AND_ASSIGN(TitleDragController); +}; + +} // namespace athena + +#endif // ATHENA_WM_TITLE_DRAG_CONTROLLER_H_ diff --git a/athena/wm/window_list_provider_impl.cc b/athena/wm/window_list_provider_impl.cc new file mode 100644 index 0000000000000..8b4cba5abdc28 --- /dev/null +++ b/athena/wm/window_list_provider_impl.cc @@ -0,0 +1,31 @@ +// 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 "athena/wm/window_list_provider_impl.h" + +#include "ui/aura/window.h" + +namespace athena { + +WindowListProviderImpl::WindowListProviderImpl(aura::Window* container) + : container_(container) { + CHECK(container_); +} + +WindowListProviderImpl::~WindowListProviderImpl() { +} + +aura::Window::Windows WindowListProviderImpl::GetWindowList() const { + aura::Window::Windows list; + const aura::Window::Windows& container_children = container_->children(); + for (aura::Window::Windows::const_iterator iter = container_children.begin(); + iter != container_children.end(); + ++iter) { + if ((*iter)->type() == ui::wm::WINDOW_TYPE_NORMAL) + list.push_back(*iter); + } + return list; +} + +} // namespace athena diff --git a/athena/wm/window_list_provider_impl.h b/athena/wm/window_list_provider_impl.h new file mode 100644 index 0000000000000..a62507ee90760 --- /dev/null +++ b/athena/wm/window_list_provider_impl.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ATHENA_WM_WINDOW_LIST_PROVIDER_IMPL_H_ +#define ATHENA_WM_WINDOW_LIST_PROVIDER_IMPL_H_ + +#include "athena/wm/public/window_list_provider.h" + +namespace athena { + +// This implementation of the WindowListProviderImpl uses the same order as in +// the container window's stacking order. +class ATHENA_EXPORT WindowListProviderImpl : public WindowListProvider { + public: + explicit WindowListProviderImpl(aura::Window* container); + virtual ~WindowListProviderImpl(); + + private: + // WindowListProvider: + virtual aura::Window::Windows GetWindowList() const OVERRIDE; + + aura::Window* container_; + + DISALLOW_COPY_AND_ASSIGN(WindowListProviderImpl); +}; + +} // namespace athena + +#endif // ATHENA_WM_WINDOW_LIST_PROVIDER_IMPL_H_ diff --git a/athena/wm/window_list_provider_impl_unittest.cc b/athena/wm/window_list_provider_impl_unittest.cc new file mode 100644 index 0000000000000..eba6b7a9ca58f --- /dev/null +++ b/athena/wm/window_list_provider_impl_unittest.cc @@ -0,0 +1,86 @@ +// 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 "athena/wm/window_list_provider_impl.h" + +#include + +#include "athena/test/athena_test_base.h" +#include "ui/aura/test/test_window_delegate.h" +#include "ui/aura/window.h" + +namespace athena { + +namespace { + +bool AreWindowListsEqual(const aura::Window::Windows& one, + const aura::Window::Windows& two) { + return one.size() == two.size() && + std::equal(one.begin(), one.end(), two.begin()); +} + +scoped_ptr CreateWindow(aura::WindowDelegate* delegate, + ui::wm::WindowType window_type) { + scoped_ptr window(new aura::Window(delegate)); + window->SetType(window_type); + window->Init(aura::WINDOW_LAYER_SOLID_COLOR); + return window.Pass(); +} + +} // namespace + +typedef test::AthenaTestBase WindowListProviderImplTest; + +// Tests that the order of windows match the stacking order of the windows in +// the container, even after the order is changed through the aura Window API. +TEST_F(WindowListProviderImplTest, StackingOrder) { + aura::test::TestWindowDelegate delegate; + scoped_ptr container(new aura::Window(&delegate)); + scoped_ptr first = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_NORMAL); + scoped_ptr second = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_NORMAL); + scoped_ptr third = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_NORMAL); + container->AddChild(first.get()); + container->AddChild(second.get()); + container->AddChild(third.get()); + + scoped_ptr list_provider( + new WindowListProviderImpl(container.get())); + EXPECT_TRUE(AreWindowListsEqual(container->children(), + list_provider->GetWindowList())); + + container->StackChildAtTop(first.get()); + EXPECT_TRUE(AreWindowListsEqual(container->children(), + list_provider->GetWindowList())); + EXPECT_EQ(first.get(), container->children().back()); +} + +TEST_F(WindowListProviderImplTest, ListContainsOnlyNormalWindows) { + aura::test::TestWindowDelegate delegate; + scoped_ptr container(new aura::Window(&delegate)); + scoped_ptr first = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_NORMAL); + scoped_ptr second = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_POPUP); + scoped_ptr third = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_NORMAL); + scoped_ptr fourth = + CreateWindow(&delegate, ui::wm::WINDOW_TYPE_MENU); + container->AddChild(first.get()); + container->AddChild(second.get()); + container->AddChild(third.get()); + container->AddChild(fourth.get()); + + scoped_ptr list_provider( + new WindowListProviderImpl(container.get())); + const aura::Window::Windows list = list_provider->GetWindowList(); + EXPECT_EQ(list.end(), std::find(list.begin(), list.end(), second.get())); + EXPECT_EQ(list.end(), std::find(list.begin(), list.end(), fourth.get())); + EXPECT_NE(list.end(), std::find(list.begin(), list.end(), first.get())); + EXPECT_NE(list.end(), std::find(list.begin(), list.end(), third.get())); +} + +} // namespace athena diff --git a/athena/wm/window_manager_impl.cc b/athena/wm/window_manager_impl.cc index fb06188a4ec86..e3bb2b281d521 100644 --- a/athena/wm/window_manager_impl.cc +++ b/athena/wm/window_manager_impl.cc @@ -4,19 +4,25 @@ #include "athena/wm/public/window_manager.h" +#include + #include "athena/common/container_priorities.h" #include "athena/input/public/accelerator_manager.h" #include "athena/screen/public/screen_manager.h" #include "athena/wm/bezel_controller.h" #include "athena/wm/public/window_manager_observer.h" #include "athena/wm/split_view_controller.h" +#include "athena/wm/title_drag_controller.h" +#include "athena/wm/window_list_provider_impl.h" #include "athena/wm/window_overview_mode.h" #include "base/logging.h" #include "base/observer_list.h" #include "ui/aura/layout_manager.h" #include "ui/aura/window.h" +#include "ui/wm/core/shadow_controller.h" #include "ui/wm/core/window_util.h" #include "ui/wm/core/wm_state.h" +#include "ui/wm/public/activation_client.h" #include "ui/wm/public/window_types.h" namespace athena { @@ -25,7 +31,8 @@ namespace { class WindowManagerImpl : public WindowManager, public WindowOverviewModeDelegate, public aura::WindowObserver, - public AcceleratorHandler { + public AcceleratorHandler, + public TitleDragControllerDelegate { public: WindowManagerImpl(); virtual ~WindowManagerImpl(); @@ -35,9 +42,11 @@ class WindowManagerImpl : public WindowManager, // WindowManager: virtual void ToggleOverview() OVERRIDE; + virtual bool IsOverviewModeActive() OVERRIDE; + private: enum Command { - COMMAND_TOGGLE_OVERVIEW, + CMD_TOGGLE_OVERVIEW, }; // Sets whether overview mode is active. @@ -51,6 +60,8 @@ class WindowManagerImpl : public WindowManager, // WindowOverviewModeDelegate: virtual void OnSelectWindow(aura::Window* window) OVERRIDE; + virtual void OnSplitViewMode(aura::Window* left, + aura::Window* right) OVERRIDE; // aura::WindowObserver virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE; @@ -61,11 +72,20 @@ class WindowManagerImpl : public WindowManager, virtual bool OnAcceleratorFired(int command_id, const ui::Accelerator& accelerator) OVERRIDE; + // TitleDragControllerDelegate: + virtual aura::Window* GetWindowBehind(aura::Window* window) OVERRIDE; + virtual void OnTitleDragStarted(aura::Window* window) OVERRIDE; + virtual void OnTitleDragCompleted(aura::Window* window) OVERRIDE; + virtual void OnTitleDragCanceled(aura::Window* window) OVERRIDE; + scoped_ptr container_; + scoped_ptr window_list_provider_; scoped_ptr overview_; scoped_ptr bezel_controller_; scoped_ptr split_view_controller_; scoped_ptr wm_state_; + scoped_ptr title_drag_controller_; + scoped_ptr shadow_controller_; ObserverList observers_; DISALLOW_COPY_AND_ASSIGN(WindowManagerImpl); @@ -98,21 +118,31 @@ WindowManagerImpl::WindowManagerImpl() { container_.reset(ScreenManager::Get()->CreateDefaultContainer(params)); container_->SetLayoutManager(new AthenaContainerLayoutManager); container_->AddObserver(this); + window_list_provider_.reset(new WindowListProviderImpl(container_.get())); bezel_controller_.reset(new BezelController(container_.get())); - split_view_controller_.reset(new SplitViewController()); + split_view_controller_.reset( + new SplitViewController(container_.get(), window_list_provider_.get())); bezel_controller_->set_left_right_delegate(split_view_controller_.get()); container_->AddPreTargetHandler(bezel_controller_.get()); + title_drag_controller_.reset(new TitleDragController(container_.get(), this)); wm_state_.reset(new wm::WMState()); + aura::client::ActivationClient* activation_client = + aura::client::GetActivationClient(container_->GetRootWindow()); + shadow_controller_.reset(new wm::ShadowController(activation_client)); instance = this; InstallAccelerators(); } WindowManagerImpl::~WindowManagerImpl() { overview_.reset(); + split_view_controller_.reset(); + window_list_provider_.reset(); if (container_) { container_->RemoveObserver(this); container_->RemovePreTargetHandler(bezel_controller_.get()); } + // |title_drag_controller_| needs to be reset before |container_|. + title_drag_controller_.reset(); container_.reset(); instance = NULL; } @@ -134,16 +164,30 @@ void WindowManagerImpl::ToggleOverview() { SetInOverview(overview_.get() == NULL); } +bool WindowManagerImpl::IsOverviewModeActive() { + return overview_; +} + void WindowManagerImpl::SetInOverview(bool active) { bool in_overview = !!overview_; if (active == in_overview) return; + bezel_controller_->set_left_right_delegate( + active ? NULL : split_view_controller_.get()); if (active) { - overview_ = WindowOverviewMode::Create(container_.get(), this); + split_view_controller_->DeactivateSplitMode(); FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeEnter()); + // Re-stack all windows in the order defined by window_list_provider_. + aura::Window::Windows window_list = window_list_provider_->GetWindowList(); + aura::Window::Windows::iterator it; + for (it = window_list.begin(); it != window_list.end(); ++it) + container_->StackChildAtTop(*it); + overview_ = WindowOverviewMode::Create( + container_.get(), window_list_provider_.get(), this); } else { + CHECK(!split_view_controller_->IsSplitViewModeActive()); overview_.reset(); FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeExit()); @@ -152,7 +196,7 @@ void WindowManagerImpl::SetInOverview(bool active) { void WindowManagerImpl::InstallAccelerators() { const AcceleratorData accelerator_data[] = { - {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_NONE, COMMAND_TOGGLE_OVERVIEW, + {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_NONE, CMD_TOGGLE_OVERVIEW, AF_NONE}, }; AcceleratorManager::Get()->RegisterAccelerators( @@ -168,12 +212,16 @@ void WindowManagerImpl::RemoveObserver(WindowManagerObserver* observer) { } void WindowManagerImpl::OnSelectWindow(aura::Window* window) { - CHECK_EQ(container_.get(), window->parent()); - container_->StackChildAtTop(window); wm::ActivateWindow(window); SetInOverview(false); } +void WindowManagerImpl::OnSplitViewMode(aura::Window* left, + aura::Window* right) { + SetInOverview(false); + split_view_controller_->ActivateSplitMode(left, right); +} + void WindowManagerImpl::OnWindowAdded(aura::Window* new_window) { if (new_window->type() == ui::wm::WINDOW_TYPE_NORMAL) SetInOverview(false); @@ -191,13 +239,68 @@ bool WindowManagerImpl::IsCommandEnabled(int command_id) const { bool WindowManagerImpl::OnAcceleratorFired(int command_id, const ui::Accelerator& accelerator) { switch (command_id) { - case COMMAND_TOGGLE_OVERVIEW: + case CMD_TOGGLE_OVERVIEW: ToggleOverview(); break; } return true; } +aura::Window* WindowManagerImpl::GetWindowBehind(aura::Window* window) { + const aura::Window::Windows& windows = window_list_provider_->GetWindowList(); + aura::Window::Windows::const_reverse_iterator iter = + std::find(windows.rbegin(), windows.rend(), window); + CHECK(iter != windows.rend()); + ++iter; + aura::Window* behind = NULL; + if (iter != windows.rend()) + behind = *iter++; + + if (split_view_controller_->IsSplitViewModeActive()) { + aura::Window* left = split_view_controller_->left_window(); + aura::Window* right = split_view_controller_->right_window(); + CHECK(window == left || window == right); + if (behind == left || behind == right) + behind = (iter == windows.rend()) ? NULL : *iter; + } + + return behind; +} + +void WindowManagerImpl::OnTitleDragStarted(aura::Window* window) { + aura::Window* next_window = GetWindowBehind(window); + if (!next_window) + return; + // Make sure |window| is active. Also make sure that |next_window| is visible, + // and positioned to match the top-left edge of |window|. + wm::ActivateWindow(window); + next_window->Show(); + int dx = window->bounds().x() - next_window->bounds().x(); + if (dx) { + gfx::Transform transform; + transform.Translate(dx, 0); + next_window->SetTransform(transform); + } +} + +void WindowManagerImpl::OnTitleDragCompleted(aura::Window* window) { + aura::Window* next_window = GetWindowBehind(window); + if (!next_window) + return; + if (split_view_controller_->IsSplitViewModeActive()) + split_view_controller_->ReplaceWindow(window, next_window); + else + OnSelectWindow(next_window); + wm::ActivateWindow(next_window); +} + +void WindowManagerImpl::OnTitleDragCanceled(aura::Window* window) { + aura::Window* next_window = GetWindowBehind(window); + if (!next_window) + return; + next_window->SetTransform(gfx::Transform()); +} + AthenaContainerLayoutManager::AthenaContainerLayoutManager() { } diff --git a/athena/wm/window_overview_mode.cc b/athena/wm/window_overview_mode.cc index a294cdec6e66d..d8e0597f29c50 100644 --- a/athena/wm/window_overview_mode.cc +++ b/athena/wm/window_overview_mode.cc @@ -8,6 +8,9 @@ #include #include +#include "athena/wm/overview_toolbar.h" +#include "athena/wm/public/window_list_provider.h" +#include "base/bind.h" #include "base/macros.h" #include "ui/aura/scoped_window_targeter.h" #include "ui/aura/window.h" @@ -15,6 +18,7 @@ #include "ui/aura/window_property.h" #include "ui/aura/window_targeter.h" #include "ui/aura/window_tree_host.h" +#include "ui/compositor/closure_animation_observer.h" #include "ui/compositor/compositor.h" #include "ui/compositor/compositor_animation_observer.h" #include "ui/compositor/scoped_layer_animation_settings.h" @@ -22,7 +26,7 @@ #include "ui/events/gestures/fling_curve.h" #include "ui/gfx/frame_time.h" #include "ui/gfx/transform.h" -#include "ui/wm/core/shadow.h" +#include "ui/wm/core/shadow_types.h" namespace { @@ -36,8 +40,6 @@ struct WindowOverviewState { // The current overview state of the window. 0.f means the window is at the // topmost position. 1.f means the window is at the bottom-most position. float progress; - - scoped_ptr shadow; }; } // namespace @@ -50,17 +52,18 @@ namespace athena { namespace { -bool ShouldShowWindowInOverviewMode(aura::Window* window) { - return window->type() == ui::wm::WINDOW_TYPE_NORMAL; +// Gets the transform for the window in its current state. +gfx::Transform GetTransformForState(WindowOverviewState* state) { + return gfx::Tween::TransformValueBetween(state->progress, + state->top, + state->bottom); } // Sets the progress-state for the window in the overview mode. void SetWindowProgress(aura::Window* window, float progress) { WindowOverviewState* state = window->GetProperty(kWindowOverviewState); - gfx::Transform transform = - gfx::Tween::TransformValueBetween(progress, state->top, state->bottom); - window->SetTransform(transform); state->progress = progress; + window->SetTransform(GetTransformForState(state)); } // Resets the overview-related state for |window|. @@ -72,6 +75,7 @@ void RestoreWindowState(aura::Window* window) { ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250)); window->SetTransform(gfx::Transform()); + wm::SetShadowType(window, wm::SHADOW_TYPE_NONE); } // Always returns the same target. @@ -102,13 +106,16 @@ class WindowOverviewModeImpl : public WindowOverviewMode, public ui::CompositorAnimationObserver { public: WindowOverviewModeImpl(aura::Window* container, + const WindowListProvider* window_list_provider, WindowOverviewModeDelegate* delegate) : container_(container), + window_list_provider_(window_list_provider), delegate_(delegate), scoped_targeter_(new aura::ScopedWindowTargeter( container, scoped_ptr( - new StaticWindowTargeter(container)))) { + new StaticWindowTargeter(container)))), + dragged_window_(NULL) { container_->set_target_handler(this); // Prepare the desired transforms for all the windows, and set the initial @@ -120,13 +127,10 @@ class WindowOverviewModeImpl : public WindowOverviewMode, virtual ~WindowOverviewModeImpl() { container_->set_target_handler(container_->delegate()); RemoveAnimationObserver(); - const aura::Window::Windows& windows = container_->children(); - for (aura::Window::Windows::const_iterator iter = windows.begin(); - iter != windows.end(); - ++iter) { - if ((*iter)->GetProperty(kWindowOverviewState)) - RestoreWindowState(*iter); - } + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + if (windows.empty()) + return; + std::for_each(windows.begin(), windows.end(), &RestoreWindowState); } private: @@ -134,24 +138,18 @@ class WindowOverviewModeImpl : public WindowOverviewMode, // positions. The transforms are set in the |kWindowOverviewState| property of // the windows. void ComputeTerminalStatesForAllWindows() { - const aura::Window::Windows& windows = container_->children(); - size_t window_count = std::count_if(windows.begin(), windows.end(), - ShouldShowWindowInOverviewMode); - + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + size_t window_count = windows.size(); size_t index = 0; const gfx::Size container_size = container_->bounds().size(); const int kGapBetweenWindowsBottom = 10; const int kGapBetweenWindowsTop = 5; - const float kMinScale = 0.6f; - const float kMaxScale = 0.95f; for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); iter != windows.rend(); - ++iter) { + ++iter, ++index) { aura::Window* window = (*iter); - if (!ShouldShowWindowInOverviewMode(window)) - continue; gfx::Transform top_transform; int top = (window_count - index - 1) * kGapBetweenWindowsTop; @@ -169,29 +167,22 @@ class WindowOverviewModeImpl : public WindowOverviewMode, state->top = top_transform; state->bottom = bottom_transform; state->progress = 0.f; - state->shadow = CreateShadowForWindow(window); window->SetProperty(kWindowOverviewState, state); - - index++; + wm::SetShadowType(window, wm::SHADOW_TYPE_RECTANGULAR_ALWAYS_ACTIVE); } } // Sets the initial position for the windows for the overview mode. void SetInitialWindowStates() { + aura::Window::Windows windows = window_list_provider_->GetWindowList(); + size_t window_count = windows.size(); // The initial overview state of the topmost three windows. const float kInitialProgress[] = { 0.5f, 0.05f, 0.01f }; - size_t index = 0; - const aura::Window::Windows& windows = container_->children(); - for (aura::Window::Windows::const_reverse_iterator iter = windows.rbegin(); - iter != windows.rend(); - ++iter) { - aura::Window* window = (*iter); - if (!window->GetProperty(kWindowOverviewState)) - continue; - + for (size_t i = 0; i < window_count; ++i) { float progress = 0.f; - if (index < arraysize(kInitialProgress)) - progress = kInitialProgress[index]; + aura::Window* window = windows[window_count - 1 - i]; + if (i < arraysize(kInitialProgress)) + progress = kInitialProgress[i]; scoped_refptr animator = window->layer()->GetAnimator(); @@ -212,19 +203,9 @@ class WindowOverviewModeImpl : public WindowOverviewMode, settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(250)); SetWindowProgress(window, progress); } - index++; } } - scoped_ptr CreateShadowForWindow(aura::Window* window) { - scoped_ptr shadow(new wm::Shadow()); - shadow->Init(wm::Shadow::STYLE_ACTIVE); - shadow->SetContentBounds(gfx::Rect(window->bounds().size())); - shadow->layer()->SetVisible(true); - window->layer()->Add(shadow->layer()); - return shadow.Pass(); - } - aura::Window* SelectWindowAt(ui::LocatedEvent* event) { CHECK_EQ(container_, event->target()); // Find the old targeter to find the target of the event. @@ -248,7 +229,7 @@ class WindowOverviewModeImpl : public WindowOverviewMode, void DoScroll(float delta_y) { const float kEpsilon = 1e-3f; float delta_y_p = std::abs(delta_y) / GetScrollableHeight(); - const aura::Window::Windows& windows = container_->children(); + aura::Window::Windows windows = window_list_provider_->GetWindowList(); if (delta_y < 0) { // Scroll up. Start with the top-most (i.e. behind-most in terms of // z-index) window, and try to scroll them up. @@ -257,8 +238,6 @@ class WindowOverviewModeImpl : public WindowOverviewMode, ++iter) { aura::Window* window = (*iter); WindowOverviewState* state = window->GetProperty(kWindowOverviewState); - if (!state) - continue; if (state->progress > kEpsilon) { // It is possible to scroll |window| up. Scroll it up, and update // |delta_y_p| for the next window. @@ -270,14 +249,12 @@ class WindowOverviewModeImpl : public WindowOverviewMode, } else { // Scroll down. Start with the bottom-most (i.e. front-most in terms of // z-index) window, and try to scroll them down. - for (aura::Window::Windows::const_reverse_iterator iter = - windows.rbegin(); + aura::Window::Windows::const_reverse_iterator iter; + for (iter = windows.rbegin(); delta_y_p > kEpsilon && iter != windows.rend(); ++iter) { aura::Window* window = (*iter); WindowOverviewState* state = window->GetProperty(kWindowOverviewState); - if (!state) - continue; if (1.f - state->progress > kEpsilon) { // It is possible to scroll |window| down. Scroll it down, and update // |delta_y_p| for the next window. @@ -311,6 +288,154 @@ class WindowOverviewModeImpl : public WindowOverviewMode, compositor->RemoveAnimationObserver(this); } + void DragWindow(const ui::GestureEvent& event) { + CHECK(dragged_window_); + CHECK_EQ(ui::ET_GESTURE_SCROLL_UPDATE, event.type()); + CHECK(overview_toolbar_); + gfx::Vector2dF dragged_distance = + dragged_start_location_ - event.location(); + WindowOverviewState* dragged_state = + dragged_window_->GetProperty(kWindowOverviewState); + CHECK(dragged_state); + gfx::Transform transform = GetTransformForState(dragged_state); + transform.Translate(-dragged_distance.x(), 0); + dragged_window_->SetTransform(transform); + + // Update the toolbar. + const int kMinDistanceForActionButtons = 20; + if (fabs(dragged_distance.x()) > kMinDistanceForActionButtons) + overview_toolbar_->ShowActionButtons(); + else + overview_toolbar_->HideActionButtons(); + + // See if the touch-point is above one of the action-buttons. + OverviewToolbar::ActionType new_action = + overview_toolbar_->GetHighlightAction(event); + + // If the touch-point is not above any of the action buttons, then highlight + // the close-button by default, if the user has dragged enough to close the + // window. + if (new_action == OverviewToolbar::ACTION_TYPE_NONE) { + if (fabs(dragged_distance.x()) > kMinDistanceForDismissal) + new_action = OverviewToolbar::ACTION_TYPE_CLOSE; + else + new_action = OverviewToolbar::ACTION_TYPE_NONE; + } + OverviewToolbar::ActionType previous_action = + overview_toolbar_->current_action(); + overview_toolbar_->SetHighlightAction(new_action); + + // If the user has selected to get into split-view mode, then show the + // window with full opacity. Otherwise, fade it out as it closes. Animate + // the opacity if transitioning to/from the split-view button. + bool animate_opacity = + (new_action != previous_action) && + ((new_action == OverviewToolbar::ACTION_TYPE_SPLIT) || + (previous_action == OverviewToolbar::ACTION_TYPE_SPLIT)); + float ratio = std::min( + 1.f, std::abs(dragged_distance.x()) / kMinDistanceForDismissal); + float opacity = + (new_action == OverviewToolbar::ACTION_TYPE_SPLIT) + ? 1 + : gfx::Tween::FloatValueBetween(ratio, kMaxOpacity, kMinOpacity); + if (animate_opacity) { + ui::ScopedLayerAnimationSettings settings( + dragged_window_->layer()->GetAnimator()); + dragged_window_->layer()->SetOpacity(opacity); + } else { + dragged_window_->layer()->SetOpacity(opacity); + } + } + + bool ShouldCloseDragWindow(const ui::GestureEvent& event) const { + gfx::Vector2dF dragged_distance = + dragged_start_location_ - event.location(); + if (event.type() == ui::ET_GESTURE_SCROLL_END) + return std::abs(dragged_distance.x()) >= kMinDistanceForDismissal; + CHECK_EQ(ui::ET_SCROLL_FLING_START, event.type()); + const bool dragging_towards_right = dragged_distance.x() < 0; + const bool swipe_towards_right = event.details().velocity_x() > 0; + if (dragging_towards_right != swipe_towards_right) + return false; + const float kMinVelocityForDismissal = 500.f; + return std::abs(event.details().velocity_x()) > kMinVelocityForDismissal; + } + + void CloseDragWindow(const ui::GestureEvent& gesture) { + // Animate |dragged_window_| offscreen first, then destroy it. + ui::ScopedLayerAnimationSettings settings( + dragged_window_->layer()->GetAnimator()); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + settings.AddObserver(new ui::ClosureAnimationObserver( + base::Bind(&base::DeletePointer, dragged_window_))); + + WindowOverviewState* dragged_state = + dragged_window_->GetProperty(kWindowOverviewState); + CHECK(dragged_state); + gfx::Transform transform = dragged_window_->layer()->transform(); + gfx::RectF transformed_bounds = dragged_window_->bounds(); + transform.TransformRect(&transformed_bounds); + float transform_x = 0.f; + if (gesture.location().x() > dragged_start_location_.x()) + transform_x = container_->bounds().right() - transformed_bounds.x(); + else + transform_x = -(transformed_bounds.x() + transformed_bounds.width()); + float scale = gfx::Tween::FloatValueBetween( + dragged_state->progress, kMinScale, kMaxScale); + transform.Translate(transform_x / scale, 0); + dragged_window_->SetTransform(transform); + dragged_window_->layer()->SetOpacity(kMinOpacity); + + // Move the windows behind |dragged_window_| in the stack forward one step. + const aura::Window::Windows& list = container_->children(); + for (aura::Window::Windows::const_iterator iter = list.begin(); + iter != list.end() && *iter != dragged_window_; + ++iter) { + aura::Window* window = *iter; + ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + WindowOverviewState* state = window->GetProperty(kWindowOverviewState); + + aura::Window* next = *(iter + 1); + WindowOverviewState* next_state = next->GetProperty(kWindowOverviewState); + state->top = next_state->top; + state->bottom = next_state->bottom; + SetWindowProgress(window, next_state->progress); + } + + dragged_window_ = NULL; + } + + void RestoreDragWindow() { + CHECK(dragged_window_); + WindowOverviewState* dragged_state = + dragged_window_->GetProperty(kWindowOverviewState); + CHECK(dragged_state); + + ui::ScopedLayerAnimationSettings settings( + dragged_window_->layer()->GetAnimator()); + settings.SetPreemptionStrategy( + ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); + dragged_window_->SetTransform(GetTransformForState(dragged_state)); + dragged_window_->layer()->SetOpacity(1.f); + dragged_window_ = NULL; + } + + void EndDragWindow(const ui::GestureEvent& gesture) { + CHECK(dragged_window_); + CHECK(overview_toolbar_); + OverviewToolbar::ActionType action = overview_toolbar_->current_action(); + overview_toolbar_.reset(); + if (action == OverviewToolbar::ACTION_TYPE_SPLIT) + delegate_->OnSplitViewMode(NULL, dragged_window_); + else if (ShouldCloseDragWindow(gesture)) + CloseDragWindow(gesture); + else + RestoreDragWindow(); + } + // ui::EventHandler: virtual void OnMouseEvent(ui::MouseEvent* mouse) OVERRIDE { if (mouse->type() == ui::ET_MOUSE_PRESSED) { @@ -336,17 +461,39 @@ class WindowOverviewModeImpl : public WindowOverviewMode, gesture->SetHandled(); delegate_->OnSelectWindow(select); } + } else if (gesture->type() == ui::ET_GESTURE_SCROLL_BEGIN) { + if (std::abs(gesture->details().scroll_x_hint()) > + std::abs(gesture->details().scroll_y_hint()) * 2) { + dragged_start_location_ = gesture->location(); + dragged_window_ = SelectWindowAt(gesture); + if (dragged_window_) + overview_toolbar_.reset(new OverviewToolbar(container_)); + } } else if (gesture->type() == ui::ET_GESTURE_SCROLL_UPDATE) { - DoScroll(gesture->details().scroll_y()); + if (dragged_window_) + DragWindow(*gesture); + else + DoScroll(gesture->details().scroll_y()); gesture->SetHandled(); - } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { - CreateFlingerFor(*gesture); - AddAnimationObserver(); + } else if (gesture->type() == ui::ET_GESTURE_SCROLL_END) { + if (dragged_window_) + EndDragWindow(*gesture); gesture->SetHandled(); - } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN && fling_) { - fling_.reset(); - RemoveAnimationObserver(); + } else if (gesture->type() == ui::ET_SCROLL_FLING_START) { + if (dragged_window_) { + EndDragWindow(*gesture); + } else { + CreateFlingerFor(*gesture); + AddAnimationObserver(); + } gesture->SetHandled(); + } else if (gesture->type() == ui::ET_GESTURE_TAP_DOWN) { + if (fling_) { + fling_.reset(); + RemoveAnimationObserver(); + gesture->SetHandled(); + } + dragged_window_ = NULL; } } @@ -364,11 +511,23 @@ class WindowOverviewModeImpl : public WindowOverviewMode, } } + const int kMinDistanceForDismissal = 300; + const float kMinScale = 0.6f; + const float kMaxScale = 0.95f; + const float kMaxOpacity = 1.0f; + const float kMinOpacity = 0.2f; + aura::Window* container_; + // Provider of the stack of windows to show in the overview mode. Not owned. + const WindowListProvider* window_list_provider_; WindowOverviewModeDelegate* delegate_; scoped_ptr scoped_targeter_; scoped_ptr fling_; + aura::Window* dragged_window_; + gfx::Point dragged_start_location_; + scoped_ptr overview_toolbar_; + DISALLOW_COPY_AND_ASSIGN(WindowOverviewModeImpl); }; @@ -377,9 +536,10 @@ class WindowOverviewModeImpl : public WindowOverviewMode, // static scoped_ptr WindowOverviewMode::Create( aura::Window* container, + const WindowListProvider* window_list_provider, WindowOverviewModeDelegate* delegate) { return scoped_ptr( - new WindowOverviewModeImpl(container, delegate)); + new WindowOverviewModeImpl(container, window_list_provider, delegate)); } } // namespace athena diff --git a/athena/wm/window_overview_mode.h b/athena/wm/window_overview_mode.h index 46eb249031eec..157b525aa6e27 100644 --- a/athena/wm/window_overview_mode.h +++ b/athena/wm/window_overview_mode.h @@ -6,18 +6,22 @@ #define ATHENA_WM_WINDOW_OVERVIEW_MODE_H_ #include "base/memory/scoped_ptr.h" - -namespace aura { -class Window; -} +#include "ui/aura/window.h" namespace athena { +class WindowListProvider; class WindowOverviewModeDelegate { public: virtual ~WindowOverviewModeDelegate() {} virtual void OnSelectWindow(aura::Window* window) = 0; + + // Gets into split-view mode with |left| on the left-side of the screen, and + // |right| on the right-side. If |left| or |right| is NULL, then the delegate + // selects the best option in its place. + virtual void OnSplitViewMode(aura::Window* left, + aura::Window* right) = 0; }; class WindowOverviewMode { @@ -26,6 +30,7 @@ class WindowOverviewMode { static scoped_ptr Create( aura::Window* container, + const WindowListProvider* window_list_provider, WindowOverviewModeDelegate* delegate); }; diff --git a/base/BUILD.gn b/base/BUILD.gn index afa96e9a604c4..e8a27b946db09 100644 --- a/base/BUILD.gn +++ b/base/BUILD.gn @@ -49,6 +49,8 @@ component("base") { "android/jni_registrar.h", "android/jni_string.cc", "android/jni_string.h", + "android/jni_utils.cc", + "android/jni_utils.h", "android/jni_weak_ref.cc", "android/jni_weak_ref.h", "android/library_loader/library_loader_hooks.cc", @@ -152,6 +154,8 @@ component("base") { "debug/stack_trace_android.cc", "debug/stack_trace_posix.cc", "debug/stack_trace_win.cc", + "debug/task_annotator.cc", + "debug/task_annotator.h", "debug/trace_event.h", "debug/trace_event_android.cc", "debug/trace_event_argument.cc", @@ -171,15 +175,9 @@ component("base") { "environment.cc", "environment.h", "event_recorder.h", + "event_recorder_stubs.cc", "event_recorder_win.cc", "file_descriptor_posix.h", - "file_util.cc", - "file_util.h", - "file_util_android.cc", - "file_util_linux.cc", - "file_util_mac.mm", - "file_util_posix.cc", - "file_util_win.cc", "file_version_info.h", "file_version_info_mac.h", "file_version_info_mac.mm", @@ -209,8 +207,15 @@ component("base") { "files/file_path_watcher_win.cc", "files/file_proxy.cc", "files/file_proxy.h", + "files/file_util.cc", + "files/file_util.h", + "files/file_util_android.cc", + "files/file_util_linux.cc", + "files/file_util_mac.mm", + "files/file_util_posix.cc", "files/file_util_proxy.cc", "files/file_util_proxy.h", + "files/file_util_win.cc", "files/important_file_writer.cc", "files/important_file_writer.h", "files/memory_mapped_file.cc", @@ -231,8 +236,6 @@ component("base") { "hash.cc", "hash.h", "id_map.h", - "ini_parser.cc", - "ini_parser.h", "ios/device_util.h", "ios/device_util.mm", "ios/ios_util.h", @@ -302,6 +305,8 @@ component("base") { "mac/scoped_nsexception_enabler.h", "mac/scoped_nsexception_enabler.mm", "mac/scoped_nsobject.h", + "mac/scoped_objc_class_swizzler.h", + "mac/scoped_objc_class_swizzler.mm", "mac/scoped_sending_event.h", "mac/scoped_sending_event.mm", "mac/sdk_forward_declarations.h", @@ -367,7 +372,6 @@ component("base") { "message_loop/message_pump_libevent.h", "message_loop/message_pump_mac.h", "message_loop/message_pump_mac.mm", - "message_loop/message_pump_observer.h", "message_loop/message_pump_win.cc", "message_loop/message_pump_win.h", "metrics/field_trial.cc", @@ -800,7 +804,7 @@ component("base") { sources -= [ "debug/stack_trace_posix.cc", "files/file_enumerator_posix.cc", - "file_util_posix.cc", + "files/file_util_posix.cc", "message_loop/message_pump_libevent.cc", "process/kill_posix.cc", "process/launch_posix.cc", @@ -1076,6 +1080,7 @@ test("base_unittests") { "debug/leak_tracker_unittest.cc", "debug/proc_maps_linux_unittest.cc", "debug/stack_trace_unittest.cc", + "debug/task_annotator_unittest.cc", "debug/trace_event_argument_unittest.cc", "debug/trace_event_memory_unittest.cc", "debug/trace_event_synthetic_delay_unittest.cc", @@ -1085,13 +1090,13 @@ test("base_unittests") { "debug/trace_event_win_unittest.cc", "deferred_sequenced_task_runner_unittest.cc", "environment_unittest.cc", - "file_util_unittest.cc", "file_version_info_unittest.cc", "files/dir_reader_posix_unittest.cc", "files/file_path_unittest.cc", "files/file_proxy_unittest.cc", "files/file_unittest.cc", "files/file_util_proxy_unittest.cc", + "files/file_util_unittest.cc", "files/important_file_writer_unittest.cc", "files/scoped_temp_dir_unittest.cc", "gmock_unittest.cc", @@ -1109,7 +1114,6 @@ test("base_unittests") { "i18n/string_search_unittest.cc", "i18n/time_formatting_unittest.cc", "i18n/timezone_unittest.cc", - "ini_parser_unittest.cc", "ios/device_util_unittest.mm", "json/json_parser_unittest.cc", "json/json_reader_unittest.cc", @@ -1125,6 +1129,7 @@ test("base_unittests") { "mac/mac_util_unittest.mm", "mac/objc_property_releaser_unittest.mm", "mac/scoped_nsobject_unittest.mm", + "mac/scoped_objc_class_swizzler_unittest.mm", "mac/scoped_sending_event_unittest.mm", "md5_unittest.cc", "memory/aligned_memory_unittest.cc", @@ -1340,6 +1345,7 @@ if (is_android) { "android/java/src/org/chromium/base/EventLog.java", "android/java/src/org/chromium/base/FieldTrialList.java", "android/java/src/org/chromium/base/ImportantFileWriterAndroid.java", + "android/java/src/org/chromium/base/JNIUtils.java", "android/java/src/org/chromium/base/library_loader/LibraryLoader.java", "android/java/src/org/chromium/base/MemoryPressureListener.java", "android/java/src/org/chromium/base/JavaHandlerThread.java", diff --git a/base/PRESUBMIT.py b/base/PRESUBMIT.py index 913aa4f8648a9..732ac27e2a2ad 100644 --- a/base/PRESUBMIT.py +++ b/base/PRESUBMIT.py @@ -57,6 +57,6 @@ def GetPreferredTryMasters(project, change): 'mac_chromium_rel_swarming': set(['defaulttests']), }, 'tryserver.chromium.win': { - 'win_chromium_rel': set(['defaulttests']), + 'win_chromium_rel_swarming': set(['defaulttests']), } } diff --git a/base/android/base_jni_registrar.cc b/base/android/base_jni_registrar.cc index a7a5fd2415eed..b6d8a28328760 100644 --- a/base/android/base_jni_registrar.cc +++ b/base/android/base_jni_registrar.cc @@ -15,6 +15,7 @@ #include "base/android/java_handler_thread.h" #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" +#include "base/android/jni_utils.h" #include "base/android/memory_pressure_listener_android.h" #include "base/android/path_service_android.h" #include "base/android/path_utils.h" @@ -40,6 +41,7 @@ static RegistrationMethod kBaseRegisteredMethods[] = { { "FieldTrialList", base::android::RegisterFieldTrialList }, { "ImportantFileWriterAndroid", base::android::RegisterImportantFileWriterAndroid }, + { "JNIUtils", base::android::RegisterJNIUtils }, { "MemoryPressureListenerAndroid", base::android::MemoryPressureListenerAndroid::Register }, { "JavaHandlerThread", base::android::JavaHandlerThread::RegisterBindings }, diff --git a/base/android/java/src/org/chromium/base/ApplicationStatus.java b/base/android/java/src/org/chromium/base/ApplicationStatus.java index d68160010767b..b4b79f104b57b 100644 --- a/base/android/java/src/org/chromium/base/ApplicationStatus.java +++ b/base/android/java/src/org/chromium/base/ApplicationStatus.java @@ -108,11 +108,11 @@ private ApplicationStatus() {} * * @param application The application whose status you wish to monitor. */ - public static void initialize(BaseChromiumApplication application) { - sApplication = application; + public static void initialize(Application app) { + sApplication = app; - application.registerWindowFocusChangedListener( - new BaseChromiumApplication.WindowFocusChangedListener() { + ApplicationStatusManager.registerWindowFocusChangedListener( + new ApplicationStatusManager.WindowFocusChangedListener() { @Override public void onWindowFocusChanged(Activity activity, boolean hasFocus) { if (!hasFocus || activity == sActivity) return; @@ -127,7 +127,7 @@ public void onWindowFocusChanged(Activity activity, boolean hasFocus) { } }); - application.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { + sApplication.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { onStateChange(activity, ActivityState.CREATED); @@ -190,6 +190,9 @@ private static void onStateChange(Activity activity, int newState) { sCachedApplicationState = null; ActivityInfo info = sActivityInfo.get(activity); + // Ignore status from none tracked activitys. + if (info == null) return; + info.setStatus(newState); // Notify all state observers that are specifically listening to this activity. @@ -381,6 +384,17 @@ public static void unregisterApplicationStateListener(ApplicationStateListener l sApplicationStateListeners.removeObserver(listener); } + /** + * When ApplicationStatus initialized after application started, the onActivityCreated(), + * onActivityStarted() and onActivityResumed() callbacks will be missed. + * This function will give the chance to simulate these three callbacks. + */ + public static void informActivityStarted(Activity activity) { + onStateChange(activity, ActivityState.CREATED); + onStateChange(activity, ActivityState.STARTED); + onStateChange(activity, ActivityState.RESUMED); + } + /** * Registers the single thread-safe native activity status listener. * This handles the case where the caller is not on the main thread. diff --git a/base/android/java/src/org/chromium/base/ApplicationStatusManager.java b/base/android/java/src/org/chromium/base/ApplicationStatusManager.java new file mode 100644 index 0000000000000..aeae40f2b53e3 --- /dev/null +++ b/base/android/java/src/org/chromium/base/ApplicationStatusManager.java @@ -0,0 +1,111 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.base; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; +import android.view.Window; + +/** + * Basic application functionality that should be shared among all browser applications. + */ +public class ApplicationStatusManager { + /** + * Interface to be implemented by listeners for window focus events. + */ + public interface WindowFocusChangedListener { + /** + * Called when the window focus changes for {@code activity}. + * @param activity The {@link Activity} that has a window focus changed event. + * @param hasFocus Whether or not {@code activity} gained or lost focus. + */ + public void onWindowFocusChanged(Activity activity, boolean hasFocus); + } + + private static ObserverList sWindowFocusListeners = + new ObserverList(); + + public static void init(Application app) { + ApplicationStatus.initialize(app); + + app.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() { + @Override + public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { + setWindowFocusChangedCallback(activity); + } + + @Override + public void onActivityDestroyed(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityPaused(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityResumed(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityStarted(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + + @Override + public void onActivityStopped(Activity activity) { + assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; + } + }); + } + + /** + * Registers a listener to receive window focus updates on activities in this application. + * @param listener Listener to receive window focus events. + */ + public static void registerWindowFocusChangedListener(WindowFocusChangedListener listener) { + sWindowFocusListeners.addObserver(listener); + } + + /** + * Unregisters a listener from receiving window focus updates on activities in this application. + * @param listener Listener that doesn't want to receive window focus events. + */ + public static void unregisterWindowFocusChangedListener(WindowFocusChangedListener listener) { + sWindowFocusListeners.removeObserver(listener); + } + + /** + * When ApplicationStatus initialized after application started, the onActivityCreated(), + * onActivityStarted() and onActivityResumed() callbacks will be missed. + * This function will give the chance to simulate these three callbacks. + */ + public static void informActivityStarted(final Activity activity) { + setWindowFocusChangedCallback(activity); + ApplicationStatus.informActivityStarted(activity); + } + + private static void setWindowFocusChangedCallback(final Activity activity) { + Window.Callback callback = activity.getWindow().getCallback(); + activity.getWindow().setCallback(new WindowCallbackWrapper(callback) { + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + + for (WindowFocusChangedListener listener : sWindowFocusListeners) { + listener.onWindowFocusChanged(activity, hasFocus); + } + } + }); + } +} diff --git a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java index a9dc2f783ead8..055fa3e86c24f 100644 --- a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java +++ b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java @@ -4,96 +4,16 @@ package org.chromium.base; -import android.app.Activity; import android.app.Application; -import android.os.Bundle; -import android.view.Window; /** * Basic application functionality that should be shared among all browser applications. */ public class BaseChromiumApplication extends Application { - /** - * Interface to be implemented by listeners for window focus events. - */ - public interface WindowFocusChangedListener { - /** - * Called when the window focus changes for {@code activity}. - * @param activity The {@link Activity} that has a window focus changed event. - * @param hasFocus Whether or not {@code activity} gained or lost focus. - */ - public void onWindowFocusChanged(Activity activity, boolean hasFocus); - } - - private ObserverList mWindowFocusListeners = - new ObserverList(); @Override public void onCreate() { super.onCreate(); - ApplicationStatus.initialize(this); - - registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { - @Override - public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { - Window.Callback callback = activity.getWindow().getCallback(); - activity.getWindow().setCallback(new WindowCallbackWrapper(callback) { - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - - for (WindowFocusChangedListener listener : mWindowFocusListeners) { - listener.onWindowFocusChanged(activity, hasFocus); - } - } - }); - } - - @Override - public void onActivityDestroyed(Activity activity) { - assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; - } - - @Override - public void onActivityPaused(Activity activity) { - assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; - } - - @Override - public void onActivityResumed(Activity activity) { - assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; - } - - @Override - public void onActivityStarted(Activity activity) { - assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; - } - - @Override - public void onActivityStopped(Activity activity) { - assert activity.getWindow().getCallback() instanceof WindowCallbackWrapper; - } - }); - } - - /** - * Registers a listener to receive window focus updates on activities in this application. - * @param listener Listener to receive window focus events. - */ - public void registerWindowFocusChangedListener(WindowFocusChangedListener listener) { - mWindowFocusListeners.addObserver(listener); - } - - /** - * Unregisters a listener from receiving window focus updates on activities in this application. - * @param listener Listener that doesn't want to receive window focus events. - */ - public void unregisterWindowFocusChangedListener(WindowFocusChangedListener listener) { - mWindowFocusListeners.removeObserver(listener); + ApplicationStatusManager.init(this); } } diff --git a/base/android/java/src/org/chromium/base/ContentUriUtils.java b/base/android/java/src/org/chromium/base/ContentUriUtils.java index 74c74f4aa6d80..70c27ed14167f 100644 --- a/base/android/java/src/org/chromium/base/ContentUriUtils.java +++ b/base/android/java/src/org/chromium/base/ContentUriUtils.java @@ -6,19 +6,48 @@ import android.content.ContentResolver; import android.content.Context; +import android.database.Cursor; import android.net.Uri; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.File; + /** * This class provides methods to access content URI schemes. */ -abstract class ContentUriUtils { +public abstract class ContentUriUtils { private static final String TAG = "ContentUriUtils"; + private static FileProviderUtil sFileProviderUtil; + + /** + * Provides functionality to translate a file into a content URI for use + * with a content provider. + */ + public interface FileProviderUtil { + /** + * Generate a content uri from the given file. + * @param context Application context. + * @param file The file to be translated. + */ + public Uri getContentUriFromFile(Context context, File file); + } // Prevent instantiation. private ContentUriUtils() {} + public static void setFileProviderUtil(FileProviderUtil util) { + sFileProviderUtil = util; + } + + public static Uri getContentUriFromFile(Context context, File file) { + ThreadUtils.assertOnUiThread(); + if (sFileProviderUtil != null) { + return sFileProviderUtil.getContentUriFromFile(context, file); + } + return null; + } + /** * Opens the content URI for reading, and returns the file descriptor to * the caller. The caller is responsible for closing the file desciptor. @@ -71,4 +100,35 @@ private static ParcelFileDescriptor getParcelFileDescriptor(Context context, Str } return pfd; } + + /** + * Method to resolve the display name of a content URI. + * + * @param uri the content URI to be resolved. + * @param contentResolver the content resolver to query. + * @param columnField the column field to query. + * @returns the display name of the @code uri if present in the database + * or an empty string otherwise. + */ + public static String getDisplayName( + Uri uri, ContentResolver contentResolver, String columnField) { + if (contentResolver == null || uri == null) return ""; + Cursor cursor = null; + try { + cursor = contentResolver.query(uri, null, null, null, null); + + if (cursor != null && cursor.getCount() >= 1) { + cursor.moveToFirst(); + int index = cursor.getColumnIndex(columnField); + if (index > -1) return cursor.getString(index); + } + } catch (NullPointerException e) { + // Some android models don't handle the provider call correctly. + // see crbug.com/345393 + return ""; + } finally { + if (cursor != null) cursor.close(); + } + return ""; + } } diff --git a/base/android/java/src/org/chromium/base/JNIUtils.java b/base/android/java/src/org/chromium/base/JNIUtils.java new file mode 100644 index 0000000000000..6f6cd54fb85ff --- /dev/null +++ b/base/android/java/src/org/chromium/base/JNIUtils.java @@ -0,0 +1,20 @@ +// 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.base; + +/** + * This class provides JNI-related methods to the native library. + */ +public class JNIUtils { + /** + * This returns a ClassLoader that is capable of loading Chromium Java code. Such a ClassLoader + * is needed for the few cases where the JNI mechanism is unable to automatically determine the + * appropriate ClassLoader instance. + */ + @CalledByNative + public static Object getClassLoader() { + return JNIUtils.class.getClassLoader(); + } +} diff --git a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java index 278dcfe39d1e1..d3cbf35adafe0 100644 --- a/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java +++ b/base/android/java/src/org/chromium/base/library_loader/LibraryLoader.java @@ -83,7 +83,7 @@ public static void ensureInitialized( return; } loadAlreadyLocked(context, shouldDeleteOldWorkaroundLibraries); - initializeAlreadyLocked(CommandLine.getJavaSwitchesOrNull()); + initializeAlreadyLocked(); } } @@ -130,12 +130,10 @@ public static void loadNow(Context context, boolean shouldDeleteOldWorkaroundLib * initializes the library here and now: must be called on the thread that the * native will call its "main" thread. The library must have previously been * loaded with loadNow. - * @param initCommandLine The command line arguments that native command line will - * be initialized with. */ - public static void initialize(String[] initCommandLine) throws ProcessInitException { + public static void initialize() throws ProcessInitException { synchronized (sLock) { - initializeAlreadyLocked(initCommandLine); + initializeAlreadyLocked(); } } @@ -190,6 +188,10 @@ private static void loadAlreadyLocked( stopTime - startTime, startTime % 10000, stopTime % 10000)); + + nativeInitCommandLine(CommandLine.getJavaSwitchesOrNull()); + CommandLine.enableNativeProxy(); + sLoaded = true; } } catch (UnsatisfiedLinkError e) { @@ -207,12 +209,12 @@ private static void loadAlreadyLocked( } // Invoke base::android::LibraryLoaded in library_loader_hooks.cc - private static void initializeAlreadyLocked(String[] initCommandLine) - throws ProcessInitException { + private static void initializeAlreadyLocked() throws ProcessInitException { if (sInitialized) { return; } - if (!nativeLibraryLoaded(initCommandLine)) { + + if (!nativeLibraryLoaded()) { Log.e(TAG, "error calling nativeLibraryLoaded"); throw new ProcessInitException(LoaderErrors.LOADER_ERROR_FAILED_TO_REGISTER_JNI); } @@ -220,11 +222,13 @@ private static void initializeAlreadyLocked(String[] initCommandLine) // shouldn't complain from now on (and in fact, it's used by the // following calls). sInitialized = true; - CommandLine.enableNativeProxy(); // From now on, keep tracing in sync with native. TraceEvent.registerNativeEnabledObserver(); + } + // Called after all native initializations are complete. + public static void onNativeInitializationComplete() { // Record histogram for the Chromium linker. if (Linker.isUsed()) { nativeRecordChromiumAndroidLinkerHistogram(Linker.loadAtFixedAddressFailed(), @@ -234,13 +238,15 @@ private static void initializeAlreadyLocked(String[] initCommandLine) nativeRecordNativeLibraryHack(sNativeLibraryHackWasUsed); } + private static native void nativeInitCommandLine(String[] initCommandLine); + // Only methods needed before or during normal JNI registration are during System.OnLoad. // nativeLibraryLoaded is then called to register everything else. This process is called // "initialization". This method will be mapped (by generated code) to the LibraryLoaded // definition in base/android/library_loader/library_loader_hooks.cc. // // Return true on success and false on failure. - private static native boolean nativeLibraryLoaded(String[] initCommandLine); + private static native boolean nativeLibraryLoaded(); // Method called to record statistics about the Chromium linker operation, // i.e. whether the library failed to be loaded at a fixed address, and diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java index 49a76b6a66d2a..7771cbfd10032 100644 --- a/base/android/java/src/org/chromium/base/library_loader/Linker.java +++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java @@ -1013,7 +1013,8 @@ public LibInfo(Parcel in) { mRelroStart = in.readLong(); mRelroSize = in.readLong(); ParcelFileDescriptor fd = in.readFileDescriptor(); - mRelroFd = fd.detachFd(); + // If CreateSharedRelro fails, the OS file descriptor will be -1 and |fd| will be null. + mRelroFd = (fd == null) ? -1 : fd.detachFd(); } // from Parcelable diff --git a/base/android/jni_android.cc b/base/android/jni_android.cc index 6f080fb6e3b11..e09c2d5d15058 100644 --- a/base/android/jni_android.cc +++ b/base/android/jni_android.cc @@ -8,6 +8,7 @@ #include "base/android/build_info.h" #include "base/android/jni_string.h" +#include "base/android/jni_utils.h" #include "base/lazy_instance.h" #include "base/logging.h" @@ -21,6 +22,9 @@ JavaVM* g_jvm = NULL; // that may still be running at shutdown. There is no harm in doing this. base::LazyInstance >::Leaky g_application_context = LAZY_INSTANCE_INITIALIZER; +base::LazyInstance >::Leaky + g_class_loader = LAZY_INSTANCE_INITIALIZER; +jmethodID g_class_loader_load_class_method_id = 0; std::string GetJavaExceptionInfo(JNIEnv* env, jthrowable java_throwable) { ScopedJavaLocalRef throwable_clazz = @@ -118,17 +122,68 @@ void InitApplicationContext(JNIEnv* env, const JavaRef& context) { g_application_context.Get().Reset(context); } +void InitReplacementClassLoader(JNIEnv* env, + const JavaRef& class_loader) { + DCHECK(g_class_loader.Get().is_null()); + DCHECK(!class_loader.is_null()); + + ScopedJavaLocalRef class_loader_clazz = + GetClass(env, "java/lang/ClassLoader"); + CHECK(!ClearException(env)); + g_class_loader_load_class_method_id = + env->GetMethodID(class_loader_clazz.obj(), + "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + CHECK(!ClearException(env)); + + DCHECK(env->IsInstanceOf(class_loader.obj(), class_loader_clazz.obj())); + g_class_loader.Get().Reset(class_loader); +} + const jobject GetApplicationContext() { DCHECK(!g_application_context.Get().is_null()); return g_application_context.Get().obj(); } ScopedJavaLocalRef GetClass(JNIEnv* env, const char* class_name) { - jclass clazz = env->FindClass(class_name); + jclass clazz; + if (!g_class_loader.Get().is_null()) { + clazz = static_cast( + env->CallObjectMethod(g_class_loader.Get().obj(), + g_class_loader_load_class_method_id, + ConvertUTF8ToJavaString(env, class_name).obj())); + } else { + clazz = env->FindClass(class_name); + } CHECK(!ClearException(env) && clazz) << "Failed to find class " << class_name; return ScopedJavaLocalRef(env, clazz); } +jclass LazyGetClass( + JNIEnv* env, + const char* class_name, + base::subtle::AtomicWord* atomic_class_id) { + COMPILE_ASSERT(sizeof(subtle::AtomicWord) >= sizeof(jclass), + AtomicWord_SmallerThan_jMethodID); + subtle::AtomicWord value = base::subtle::Acquire_Load(atomic_class_id); + if (value) + return reinterpret_cast(value); + ScopedJavaGlobalRef clazz; + clazz.Reset(GetClass(env, class_name)); + subtle::AtomicWord null_aw = reinterpret_cast(NULL); + subtle::AtomicWord cas_result = base::subtle::Release_CompareAndSwap( + atomic_class_id, + null_aw, + reinterpret_cast(clazz.obj())); + if (cas_result == null_aw) { + // We intentionally leak the global ref since we now storing it as a raw + // pointer in |atomic_class_id|. + return clazz.Release(); + } else { + return reinterpret_cast(cas_result); + } +} + template jmethodID MethodID::Get(JNIEnv* env, jclass clazz, diff --git a/base/android/jni_android.h b/base/android/jni_android.h index faf53b7400bbd..b5e55263e0e6e 100644 --- a/base/android/jni_android.h +++ b/base/android/jni_android.h @@ -53,6 +53,15 @@ BASE_EXPORT bool IsVMInitialized(); BASE_EXPORT void InitApplicationContext(JNIEnv* env, const JavaRef& context); +// Initializes the global ClassLoader used by the GetClass and LazyGetClass +// methods. This is needed because JNI will use the base ClassLoader when there +// is no Java code on the stack. The base ClassLoader doesn't know about any of +// the application classes and will fail to lookup anything other than system +// classes. +BASE_EXPORT void InitReplacementClassLoader( + JNIEnv* env, + const JavaRef& class_loader); + // Gets a global ref to the application context set with // InitApplicationContext(). Ownership is retained by the function - the caller // must NOT release it. @@ -66,6 +75,17 @@ const BASE_EXPORT jobject GetApplicationContext(); BASE_EXPORT ScopedJavaLocalRef GetClass(JNIEnv* env, const char* class_name); +// The method will initialize |atomic_class_id| to contain a global ref to the +// class. And will return that ref on subsequent calls. It's the caller's +// responsibility to release the ref when it is no longer needed. +// The caller is responsible to zero-initialize |atomic_method_id|. +// It's fine to simultaneously call this on multiple threads referencing the +// same |atomic_method_id|. +BASE_EXPORT jclass LazyGetClass( + JNIEnv* env, + const char* class_name, + base::subtle::AtomicWord* atomic_class_id); + // This class is a wrapper for JNIEnv Get(Static)MethodID. class BASE_EXPORT MethodID { public: diff --git a/base/android/jni_generator/golden_sample_for_tests_jni.h b/base/android/jni_generator/golden_sample_for_tests_jni.h index 7dbf71eb99719..0fcdc699e2df8 100644 --- a/base/android/jni_generator/golden_sample_for_tests_jni.h +++ b/base/android/jni_generator/golden_sample_for_tests_jni.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -26,10 +26,13 @@ const char kInnerStructBClassPath[] = "org/chromium/example/jni_generator/SampleForTests$InnerStructB"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_InnerStructA_clazz = NULL; +#define InnerStructA_clazz(env) g_InnerStructA_clazz // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_SampleForTests_clazz = NULL; +#define SampleForTests_clazz(env) g_SampleForTests_clazz // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_InnerStructB_clazz = NULL; +#define InnerStructB_clazz(env) g_InnerStructB_clazz } // namespace @@ -99,11 +102,11 @@ static jint Java_SampleForTests_javaMethod(JNIEnv* env, jobject obj, JniIntWrapper bar) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_SampleForTests_clazz, 0); + SampleForTests_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_SampleForTests_clazz, + env, SampleForTests_clazz(env), "javaMethod", "(" @@ -123,12 +126,12 @@ static jint Java_SampleForTests_javaMethod(JNIEnv* env, jobject obj, static base::subtle::AtomicWord g_SampleForTests_staticJavaMethod = 0; static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_SampleForTests_clazz, - g_SampleForTests_clazz, false); + CHECK_CLAZZ(env, SampleForTests_clazz(env), + SampleForTests_clazz(env), false); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_SampleForTests_clazz, + env, SampleForTests_clazz(env), "staticJavaMethod", "(" @@ -137,7 +140,7 @@ static jboolean Java_SampleForTests_staticJavaMethod(JNIEnv* env) { &g_SampleForTests_staticJavaMethod); jboolean ret = - env->CallStaticBooleanMethod(g_SampleForTests_clazz, + env->CallStaticBooleanMethod(SampleForTests_clazz(env), method_id); jni_generator::CheckException(env); return ret; @@ -148,11 +151,11 @@ static void Java_SampleForTests_packagePrivateJavaMethod(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_SampleForTests_clazz); + SampleForTests_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_SampleForTests_clazz, + env, SampleForTests_clazz(env), "packagePrivateJavaMethod", "(" @@ -171,11 +174,11 @@ static void Java_SampleForTests_methodThatThrowsException(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_SampleForTests_clazz); + SampleForTests_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_SampleForTests_clazz, + env, SampleForTests_clazz(env), "methodThatThrowsException", "(" @@ -194,12 +197,12 @@ static base::android::ScopedJavaLocalRef JniIntWrapper i, jstring s) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_InnerStructA_clazz, - g_InnerStructA_clazz, NULL); + CHECK_CLAZZ(env, InnerStructA_clazz(env), + InnerStructA_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_InnerStructA_clazz, + env, InnerStructA_clazz(env), "create", "(" @@ -211,7 +214,7 @@ static base::android::ScopedJavaLocalRef &g_InnerStructA_create); jobject ret = - env->CallStaticObjectMethod(g_InnerStructA_clazz, + env->CallStaticObjectMethod(InnerStructA_clazz(env), method_id, l, as_jint(i), s); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -222,11 +225,11 @@ static void Java_SampleForTests_addStructA(JNIEnv* env, jobject obj, jobject a) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_SampleForTests_clazz); + SampleForTests_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_SampleForTests_clazz, + env, SampleForTests_clazz(env), "addStructA", "(" @@ -246,11 +249,11 @@ static void Java_SampleForTests_iterateAndDoSomething(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_SampleForTests_clazz); + SampleForTests_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_SampleForTests_clazz, + env, SampleForTests_clazz(env), "iterateAndDoSomething", "(" @@ -268,11 +271,11 @@ static base::subtle::AtomicWord g_InnerStructB_getKey = 0; static jlong Java_InnerStructB_getKey(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InnerStructB_clazz, 0); + InnerStructB_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InnerStructB_clazz, + env, InnerStructB_clazz(env), "getKey", "(" @@ -292,11 +295,11 @@ static base::android::ScopedJavaLocalRef Java_InnerStructB_getValue(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InnerStructB_clazz, NULL); + InnerStructB_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InnerStructB_clazz, + env, InnerStructB_clazz(env), "getValue", "(" @@ -379,11 +382,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsSampleForTestsSize = arraysize(kMethodsSampleForTests); - if (env->RegisterNatives(g_SampleForTests_clazz, + if (env->RegisterNatives(SampleForTests_clazz(env), kMethodsSampleForTests, kMethodsSampleForTestsSize) < 0) { jni_generator::HandleRegistrationError( - env, g_SampleForTests_clazz, __FILE__); + env, SampleForTests_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/jni_generator.py b/base/android/jni_generator/jni_generator.py index 56db13cf9182e..4342fed69a47d 100755 --- a/base/android/jni_generator/jni_generator.py +++ b/base/android/jni_generator/jni_generator.py @@ -731,7 +731,7 @@ def ExtractInitNative(self, options): def GetContent(self): """Returns the content of the JNI binding file.""" template = Template("""\ -// Copyright (c) 2012 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. @@ -814,6 +814,8 @@ def GetForwardDeclarationsString(self): for native in self.natives: if native.type != 'method': ret += [self.GetForwardDeclaration(native)] + if self.options.native_exports and ret: + return '\nextern "C" {\n' + "\n".join(ret) + '\n}; // extern "C"' return '\n'.join(ret) def GetConstantFieldsString(self): @@ -835,6 +837,9 @@ def GetMethodStubsString(self): ret += self.GetEagerCalledByNativeMethodStubs() else: ret += self.GetLazyCalledByNativeMethodStubs() + + if self.options.native_exports and ret: + return '\nextern "C" {\n' + "\n".join(ret) + '\n}; // extern "C"' return '\n'.join(ret) def GetLazyCalledByNativeMethodStubs(self): @@ -880,6 +885,8 @@ def SubstituteNativeMethods(self, template): def GetJNINativeMethodsString(self): """Returns the implementation of the array of native methods.""" + if self.options.native_exports: + return '' template = Template("""\ static const JNINativeMethod kMethods${JAVA_CLASS}[] = { ${KMETHODS} @@ -934,14 +941,17 @@ def GetRegisterNativesString(self): def GetRegisterNativesImplString(self): """Returns the shared implementation for RegisterNatives.""" + if self.options.native_exports: + return '' + template = Template("""\ const int kMethods${JAVA_CLASS}Size = arraysize(kMethods${JAVA_CLASS}); - if (env->RegisterNatives(g_${JAVA_CLASS}_clazz, + if (env->RegisterNatives(${JAVA_CLASS}_clazz(env), kMethods${JAVA_CLASS}, kMethods${JAVA_CLASS}Size) < 0) { jni_generator::HandleRegistrationError( - env, g_${JAVA_CLASS}_clazz, __FILE__); + env, ${JAVA_CLASS}_clazz(env), __FILE__); return false; } """) @@ -958,11 +968,17 @@ def GetJNIRegisterNativesString(self): return ${NAMESPACE}RegisterNativesImpl(env, clazz); } """) - fully_qualified_class = self.fully_qualified_class.replace('/', '_') + + if self.options.native_exports: + java_name = JniParams.RemapClassName(self.fully_qualified_class) + java_name = java_name.replace('_', '_1').replace('/', '_') + else: + java_name = self.fully_qualified_class.replace('/', '_') + namespace = '' if self.namespace: namespace = self.namespace + '::' - values = {'FULLY_QUALIFIED_CLASS': fully_qualified_class, + values = {'FULLY_QUALIFIED_CLASS': java_name, 'INIT_NATIVE_NAME': 'native' + self.init_native.name, 'NAMESPACE': namespace, 'REGISTER_NATIVES_IMPL': self.GetRegisterNativesImplString() @@ -1016,23 +1032,52 @@ def GetCalledByNativeParamsInDeclaration(self, called_by_native): for param in called_by_native.params]) def GetForwardDeclaration(self, native): - template = Template(""" + template_str = """ static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS}); -""") +""" + if self.options.native_exports: + template_str += """ +__attribute__((visibility("default"))) +${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env, ${PARAMS}) { + return ${NAME}(${PARAMS_IN_CALL}); +} +""" + template = Template(template_str) + params_in_call = [] + if not self.options.pure_native_methods: + params_in_call = ['env', 'jcaller'] + params_in_call = ', '.join(params_in_call + [p.name for p in native.params]) + + java_name = JniParams.RemapClassName(self.fully_qualified_class) + java_name = java_name.replace('_', '_1').replace('/', '_') + if native.java_class_name: + java_name += '_00024' + native.java_class_name + values = {'RETURN': JavaDataTypeToC(native.return_type), 'NAME': native.name, - 'PARAMS': self.GetParamsInDeclaration(native)} + 'JAVA_NAME': java_name, + 'PARAMS': self.GetParamsInDeclaration(native), + 'PARAMS_IN_CALL': params_in_call} return template.substitute(values) def GetNativeMethodStubString(self, native): """Returns stubs for native methods.""" - template = Template("""\ -static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) { + if self.options.native_exports: + template_str = """\ +__attribute__((visibility("default"))) +${RETURN} Java_${JAVA_NAME}_native${NAME}(JNIEnv* env, + ${PARAMS_IN_DECLARATION}) {""" + else: + template_str = """\ +static ${RETURN} ${NAME}(JNIEnv* env, ${PARAMS_IN_DECLARATION}) {""" + template_str += """ ${P0_TYPE}* native = reinterpret_cast<${P0_TYPE}*>(${PARAM0_NAME}); CHECK_NATIVE_PTR(env, jcaller, native, "${NAME}"${OPTIONAL_ERROR_RETURN}); return native->${NAME}(${PARAMS_IN_CALL})${POST_CALL}; } -""") +""" + + template = Template(template_str) params = [] if not self.options.pure_native_methods: params = ['env', 'jcaller'] @@ -1045,9 +1090,19 @@ def GetNativeMethodStubString(self, native): post_call = '' if re.match(RE_SCOPED_JNI_RETURN_TYPES, return_type): post_call = '.Release()' + + if self.options.native_exports: + java_name = JniParams.RemapClassName(self.fully_qualified_class) + java_name = java_name.replace('_', '_1').replace('/', '_') + if native.java_class_name: + java_name += '_00024' + native.java_class_name + else: + java_name = '' + values = { 'RETURN': return_type, 'OPTIONAL_ERROR_RETURN': optional_error_return, + 'JAVA_NAME': java_name, 'NAME': native.name, 'PARAMS_IN_DECLARATION': self.GetParamsInDeclaration(native), 'PARAM0_NAME': native.params[0].name, @@ -1067,11 +1122,10 @@ def GetArgumentsInCall(self, params): def GetCalledByNativeValues(self, called_by_native): """Fills in necessary values for the CalledByNative methods.""" + java_class = called_by_native.java_class_name or self.class_name if called_by_native.static or called_by_native.is_constructor: first_param_in_declaration = '' - first_param_in_call = ('g_%s_clazz' % - (called_by_native.java_class_name or - self.class_name)) + first_param_in_call = ('%s_clazz(env)' % java_class) else: first_param_in_declaration = ', jobject obj' first_param_in_call = 'obj' @@ -1105,7 +1159,7 @@ def GetCalledByNativeValues(self, called_by_native): else: return_clause = 'return ret;' return { - 'JAVA_CLASS': called_by_native.java_class_name or self.class_name, + 'JAVA_CLASS': java_class, 'RETURN_TYPE': return_type, 'OPTIONAL_ERROR_RETURN': optional_error_return, 'RETURN_DECLARATION': return_declaration, @@ -1149,7 +1203,7 @@ def GetLazyCalledByNativeMethodStub(self, called_by_native): ${FUNCTION_HEADER} /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, ${FIRST_PARAM_IN_CALL}, - g_${JAVA_CLASS}_clazz${OPTIONAL_ERROR_RETURN}); + ${JAVA_CLASS}_clazz(env)${OPTIONAL_ERROR_RETURN}); jmethodID method_id = ${GET_METHOD_ID_IMPL} ${RETURN_DECLARATION} @@ -1195,8 +1249,12 @@ def GetClassPathDefinitions(self): const char k${JAVA_CLASS}ClassPath[] = "${JNI_CLASS_PATH}";""") native_classes = self.GetUniqueClasses(self.natives) called_by_native_classes = self.GetUniqueClasses(self.called_by_natives) - all_classes = native_classes - all_classes.update(called_by_native_classes) + if self.options.native_exports: + all_classes = called_by_native_classes + else: + all_classes = native_classes + all_classes.update(called_by_native_classes) + for clazz in all_classes: values = { 'JAVA_CLASS': clazz, @@ -1204,22 +1262,42 @@ def GetClassPathDefinitions(self): } ret += [template.substitute(values)] ret += '' - for clazz in called_by_native_classes: + + class_getter_methods = [] + if self.options.native_exports: template = Template("""\ // Leaking this jclass as we cannot use LazyInstance from some threads. -jclass g_${JAVA_CLASS}_clazz = NULL;""") +base::subtle::AtomicWord g_${JAVA_CLASS}_clazz = 0; +#define ${JAVA_CLASS}_clazz(env) \ +base::android::LazyGetClass(env, k${JAVA_CLASS}ClassPath, \ +&g_${JAVA_CLASS}_clazz)""") + else: + template = Template("""\ +// Leaking this jclass as we cannot use LazyInstance from some threads. +jclass g_${JAVA_CLASS}_clazz = NULL; +#define ${JAVA_CLASS}_clazz(env) g_${JAVA_CLASS}_clazz""") + + for clazz in called_by_native_classes: values = { 'JAVA_CLASS': clazz, } ret += [template.substitute(values)] + return '\n'.join(ret) def GetFindClasses(self): """Returns the imlementation of FindClass for all known classes.""" if self.init_native: - template = Template("""\ + if self.options.native_exports: + template = Template("""\ + base::subtle::Release_Store(&g_${JAVA_CLASS}_clazz, + static_cast(env->NewWeakGlobalRef(clazz));""") + else: + template = Template("""\ g_${JAVA_CLASS}_clazz = static_cast(env->NewWeakGlobalRef(clazz));""") else: + if self.options.native_exports: + return '\n' template = Template("""\ g_${JAVA_CLASS}_clazz = reinterpret_cast(env->NewGlobalRef( base::android::GetClass(env, k${JAVA_CLASS}ClassPath).obj()));""") @@ -1234,13 +1312,13 @@ def GetMethodIDImpl(self, called_by_native): if self.options.eager_called_by_natives: template = Template("""\ env->Get${STATIC_METHOD_PART}MethodID( - g_${JAVA_CLASS}_clazz, + ${JAVA_CLASS}_clazz(env), "${JNI_NAME}", ${JNI_SIGNATURE});""") else: template = Template("""\ base::android::MethodID::LazyGet< base::android::MethodID::TYPE_${STATIC}>( - env, g_${JAVA_CLASS}_clazz, + env, ${JAVA_CLASS}_clazz(env), "${JNI_NAME}", ${JNI_SIGNATURE}, &g_${JAVA_CLASS}_${METHOD_ID_VAR_NAME}); @@ -1417,6 +1495,9 @@ def main(argv): help='The path to cpp command.') option_parser.add_option('--javap', default='javap', help='The path to javap command.') + option_parser.add_option('--native_exports', action='store_true', + help='Native method registration through .so ' + 'exports.') options, args = option_parser.parse_args(argv) if options.jar_file: input_file = ExtractJarInputFile(options.jar_file, options.input_file, diff --git a/base/android/jni_generator/jni_generator_tests.py b/base/android/jni_generator/jni_generator_tests.py index 7db9a10391b67..a7ce537a6ccba 100755 --- a/base/android/jni_generator/jni_generator_tests.py +++ b/base/android/jni_generator/jni_generator_tests.py @@ -42,7 +42,7 @@ def __init__(self): self.eager_called_by_natives = False self.cpp = 'cpp' self.javap = 'javap' - + self.native_exports = False class TestGenerator(unittest.TestCase): def assertObjEquals(self, first, second): @@ -1005,6 +1005,45 @@ class Test { test_data, 'org/chromium/example/jni_generator/Test', options) self.assertGoldenTextEquals(jni_from_java.GetContent()) + def testNativeExportsOption(self): + test_data = """ + package org.chromium.example.jni_generator; + + /** The pointer to the native Test. */ + long nativeTest; + + class Test { + private static native boolean nativeInitNativeClass(); + private static native int nativeStaticMethod(long nativeTest, int arg1); + private native int nativeMethod(long nativeTest, int arg1); + @CalledByNative + private void testMethodWithParam(int iParam); + @CalledByNative + private String testMethodWithParamAndReturn(int iParam); + @CalledByNative + private static int testStaticMethodWithParam(int iParam); + @CalledByNative + private static double testMethodWithNoParam(); + @CalledByNative + private static String testStaticMethodWithNoParam(); + + class MyInnerClass { + @NativeCall("MyInnerClass") + private native int nativeInit(); + } + class MyOtherInnerClass { + @NativeCall("MyOtherInnerClass") + private native int nativeInit(); + } + } + """ + options = TestOptions() + options.jni_init_native_name = 'nativeInitNativeClass' + options.native_exports = True + jni_from_java = jni_generator.JNIFromJavaSource( + test_data, 'org/chromium/example/jni_generator/SampleForTests', options) + self.assertGoldenTextEquals(jni_from_java.GetContent()) + def testOuterInnerRaises(self): test_data = """ package org.chromium.media; diff --git a/base/android/jni_generator/testCalledByNatives.golden b/base/android/jni_generator/testCalledByNatives.golden index abdc50740b5d3..22aa45d39a1b8 100644 --- a/base/android/jni_generator/testCalledByNatives.golden +++ b/base/android/jni_generator/testCalledByNatives.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -22,8 +22,10 @@ const char kTestJniClassPath[] = "org/chromium/TestJni"; const char kInfoBarClassPath[] = "org/chromium/TestJni$InfoBar"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_TestJni_clazz = NULL; +#define TestJni_clazz(env) g_TestJni_clazz // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_InfoBar_clazz = NULL; +#define InfoBar_clazz(env) g_InfoBar_clazz } // namespace @@ -39,11 +41,11 @@ static base::android::ScopedJavaLocalRef jobject icon) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "showConfirmInfoBar", "(" @@ -73,11 +75,11 @@ static base::android::ScopedJavaLocalRef jstring args) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "showAutoLoginInfoBar", "(" @@ -100,11 +102,11 @@ static base::subtle::AtomicWord g_InfoBar_dismiss = 0; static void Java_InfoBar_dismiss(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InfoBar_clazz); + InfoBar_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InfoBar_clazz, + env, InfoBar_clazz(env), "dismiss", "(" @@ -124,12 +126,12 @@ static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view, jstring account, jstring args) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_TestJni_clazz, - g_TestJni_clazz, false); + CHECK_CLAZZ(env, TestJni_clazz(env), + TestJni_clazz(env), false); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "shouldShowAutoLogin", "(" @@ -142,7 +144,7 @@ static jboolean Java_TestJni_shouldShowAutoLogin(JNIEnv* env, jobject view, &g_TestJni_shouldShowAutoLogin); jboolean ret = - env->CallStaticBooleanMethod(g_TestJni_clazz, + env->CallStaticBooleanMethod(TestJni_clazz(env), method_id, view, realm, account, args); jni_generator::CheckException(env); return ret; @@ -152,12 +154,12 @@ static base::subtle::AtomicWord g_TestJni_openUrl = 0; static base::android::ScopedJavaLocalRef Java_TestJni_openUrl(JNIEnv* env, jstring url) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_TestJni_clazz, - g_TestJni_clazz, NULL); + CHECK_CLAZZ(env, TestJni_clazz(env), + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "openUrl", "(" @@ -167,7 +169,7 @@ static base::android::ScopedJavaLocalRef Java_TestJni_openUrl(JNIEnv* &g_TestJni_openUrl); jobject ret = - env->CallStaticObjectMethod(g_TestJni_clazz, + env->CallStaticObjectMethod(TestJni_clazz(env), method_id, url); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -182,11 +184,11 @@ static void Java_TestJni_activateHardwareAcceleration(JNIEnv* env, jobject obj, JniIntWrapper iSecondaryID) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz); + TestJni_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "activateHardwareAcceleration", "(" @@ -211,11 +213,11 @@ static void Java_TestJni_uncheckedCall(JNIEnv* env, jobject obj, JniIntWrapper iParam) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz); + TestJni_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "uncheckedCall", "(" @@ -234,11 +236,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnByteArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnByteArray", "(" @@ -258,11 +260,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnBooleanArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnBooleanArray", "(" @@ -282,11 +284,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnCharArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnCharArray", "(" @@ -306,11 +308,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnShortArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnShortArray", "(" @@ -330,11 +332,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnIntArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnIntArray", "(" @@ -354,11 +356,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnLongArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnLongArray", "(" @@ -378,11 +380,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnDoubleArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnDoubleArray", "(" @@ -402,11 +404,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnObjectArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnObjectArray", "(" @@ -426,11 +428,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_returnArrayOfByteArray(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "returnArrayOfByteArray", "(" @@ -450,11 +452,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_getCompressFormat(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "getCompressFormat", "(" @@ -474,11 +476,11 @@ static base::android::ScopedJavaLocalRef Java_TestJni_getCompressFormatList(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_TestJni_clazz, NULL); + TestJni_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_TestJni_clazz, + env, TestJni_clazz(env), "getCompressFormatList", "(" diff --git a/base/android/jni_generator/testConstantsFromJavaP.golden b/base/android/jni_generator/testConstantsFromJavaP.golden index 795bd547f2b00..f122529ddba4c 100644 --- a/base/android/jni_generator/testConstantsFromJavaP.golden +++ b/base/android/jni_generator/testConstantsFromJavaP.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kMotionEventClassPath[] = "android/view/MotionEvent"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_MotionEvent_clazz = NULL; +#define MotionEvent_clazz(env) g_MotionEvent_clazz } // namespace @@ -117,11 +118,11 @@ static void Java_MotionEvent_finalize(JNIEnv* env, jobject obj) __attribute__ static void Java_MotionEvent_finalize(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "finalize", "()V", &g_MotionEvent_finalize); @@ -167,18 +168,18 @@ static base::android::ScopedJavaLocalRef JniIntWrapper p12, JniIntWrapper p13) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtain", "(JJII[Landroid/view/MotionEvent$PointerProperties;[Landroid/view/MotionEvent$PointerCoords;IIFFIIII)Landroid/view/MotionEvent;", &g_MotionEvent_obtainAVME_J_J_I_I_LAVMEPP_LAVMEPC_I_I_F_F_I_I_I_I); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, as_jint(p6), as_jint(p7), p8, p9, as_jint(p10), as_jint(p11), as_jint(p12), as_jint(p13)); @@ -219,18 +220,18 @@ static base::android::ScopedJavaLocalRef JniIntWrapper p11, JniIntWrapper p12) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtain", "(JJII[I[Landroid/view/MotionEvent$PointerCoords;IFFIIII)Landroid/view/MotionEvent;", &g_MotionEvent_obtainAVME_J_J_I_I_AI_LAVMEPC_I_F_F_I_I_I_I); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, as_jint(p6), p7, p8, as_jint(p9), as_jint(p10), as_jint(p11), as_jint(p12)); jni_generator::CheckException(env); @@ -266,18 +267,18 @@ static base::android::ScopedJavaLocalRef JniIntWrapper p10, JniIntWrapper p11) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtain", "(JJIFFFFIFFII)Landroid/view/MotionEvent;", &g_MotionEvent_obtainAVME_J_J_I_F_F_F_F_I_F_F_I_I); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0, p1, as_jint(p2), p3, p4, p5, p6, as_jint(p7), p8, p9, as_jint(p10), as_jint(p11)); jni_generator::CheckException(env); @@ -315,18 +316,18 @@ static base::android::ScopedJavaLocalRef JniIntWrapper p11, JniIntWrapper p12) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtain", "(JJIIFFFFIFFII)Landroid/view/MotionEvent;", &g_MotionEvent_obtainAVME_J_J_I_I_F_F_F_F_I_F_F_I_I); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0, p1, as_jint(p2), as_jint(p3), p4, p5, p6, p7, as_jint(p8), p9, p10, as_jint(p11), as_jint(p12)); jni_generator::CheckException(env); @@ -349,18 +350,18 @@ static base::android::ScopedJavaLocalRef jfloat p4, JniIntWrapper p5) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtain", "(JJIFFI)Landroid/view/MotionEvent;", &g_MotionEvent_obtainAVME_J_J_I_F_F_I); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0, p1, as_jint(p2), p3, p4, as_jint(p5)); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -373,18 +374,18 @@ static base::android::ScopedJavaLocalRef static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainAVME_AVME(JNIEnv* env, jobject p0) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtain", "(Landroid/view/MotionEvent;)Landroid/view/MotionEvent;", &g_MotionEvent_obtainAVME_AVME); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -397,18 +398,18 @@ static base::android::ScopedJavaLocalRef static base::android::ScopedJavaLocalRef Java_MotionEvent_obtainNoHistory(JNIEnv* env, jobject p0) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "obtainNoHistory", "(Landroid/view/MotionEvent;)Landroid/view/MotionEvent;", &g_MotionEvent_obtainNoHistory); jobject ret = - env->CallStaticObjectMethod(g_MotionEvent_clazz, + env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, p0); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -420,11 +421,11 @@ static void Java_MotionEvent_recycle(JNIEnv* env, jobject obj) __attribute__ static void Java_MotionEvent_recycle(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "recycle", "()V", &g_MotionEvent_recycle); @@ -441,11 +442,11 @@ static jint Java_MotionEvent_getDeviceId(JNIEnv* env, jobject obj) __attribute__ static jint Java_MotionEvent_getDeviceId(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getDeviceId", "()I", &g_MotionEvent_getDeviceId); @@ -463,11 +464,11 @@ static jint Java_MotionEvent_getSource(JNIEnv* env, jobject obj) __attribute__ static jint Java_MotionEvent_getSource(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getSource", "()I", &g_MotionEvent_getSource); @@ -486,11 +487,11 @@ static void Java_MotionEvent_setSource(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "setSource", "(I)V", &g_MotionEvent_setSource); @@ -507,11 +508,11 @@ static jint Java_MotionEvent_getAction(JNIEnv* env, jobject obj) __attribute__ static jint Java_MotionEvent_getAction(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getAction", "()I", &g_MotionEvent_getAction); @@ -529,11 +530,11 @@ static jint Java_MotionEvent_getActionMasked(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getActionMasked(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getActionMasked", "()I", &g_MotionEvent_getActionMasked); @@ -551,11 +552,11 @@ static jint Java_MotionEvent_getActionIndex(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getActionIndex(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getActionIndex", "()I", &g_MotionEvent_getActionIndex); @@ -573,11 +574,11 @@ static jint Java_MotionEvent_getFlags(JNIEnv* env, jobject obj) __attribute__ static jint Java_MotionEvent_getFlags(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getFlags", "()I", &g_MotionEvent_getFlags); @@ -595,11 +596,11 @@ static jlong Java_MotionEvent_getDownTime(JNIEnv* env, jobject obj) static jlong Java_MotionEvent_getDownTime(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getDownTime", "()J", &g_MotionEvent_getDownTime); @@ -617,11 +618,11 @@ static jlong Java_MotionEvent_getEventTime(JNIEnv* env, jobject obj) static jlong Java_MotionEvent_getEventTime(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getEventTime", "()J", &g_MotionEvent_getEventTime); @@ -639,11 +640,11 @@ static jfloat Java_MotionEvent_getXF(JNIEnv* env, jobject obj) __attribute__ static jfloat Java_MotionEvent_getXF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getX", "()F", &g_MotionEvent_getXF); @@ -661,11 +662,11 @@ static jfloat Java_MotionEvent_getYF(JNIEnv* env, jobject obj) __attribute__ static jfloat Java_MotionEvent_getYF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getY", "()F", &g_MotionEvent_getYF); @@ -683,11 +684,11 @@ static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getPressureF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getPressure", "()F", &g_MotionEvent_getPressureF); @@ -705,11 +706,11 @@ static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, jobject obj) __attribute__ static jfloat Java_MotionEvent_getSizeF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getSize", "()F", &g_MotionEvent_getSizeF); @@ -727,11 +728,11 @@ static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getTouchMajorF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getTouchMajor", "()F", &g_MotionEvent_getTouchMajorF); @@ -749,11 +750,11 @@ static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getTouchMinorF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getTouchMinor", "()F", &g_MotionEvent_getTouchMinorF); @@ -771,11 +772,11 @@ static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getToolMajorF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getToolMajor", "()F", &g_MotionEvent_getToolMajorF); @@ -793,11 +794,11 @@ static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getToolMinorF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getToolMinor", "()F", &g_MotionEvent_getToolMinorF); @@ -815,11 +816,11 @@ static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getOrientationF(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getOrientation", "()F", &g_MotionEvent_getOrientationF); @@ -838,11 +839,11 @@ static jfloat Java_MotionEvent_getAxisValueF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getAxisValue", "(I)F", &g_MotionEvent_getAxisValueF_I); @@ -860,11 +861,11 @@ static jint Java_MotionEvent_getPointerCount(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getPointerCount(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getPointerCount", "()I", &g_MotionEvent_getPointerCount); @@ -883,11 +884,11 @@ static jint Java_MotionEvent_getPointerId(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getPointerId", "(I)I", &g_MotionEvent_getPointerId); @@ -906,11 +907,11 @@ static jint Java_MotionEvent_getToolType(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getToolType", "(I)I", &g_MotionEvent_getToolType); @@ -929,11 +930,11 @@ static jint Java_MotionEvent_findPointerIndex(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "findPointerIndex", "(I)I", &g_MotionEvent_findPointerIndex); @@ -952,11 +953,11 @@ static jfloat Java_MotionEvent_getXF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getX", "(I)F", &g_MotionEvent_getXF_I); @@ -975,11 +976,11 @@ static jfloat Java_MotionEvent_getYF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getY", "(I)F", &g_MotionEvent_getYF_I); @@ -998,11 +999,11 @@ static jfloat Java_MotionEvent_getPressureF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getPressure", "(I)F", &g_MotionEvent_getPressureF_I); @@ -1021,11 +1022,11 @@ static jfloat Java_MotionEvent_getSizeF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getSize", "(I)F", &g_MotionEvent_getSizeF_I); @@ -1044,11 +1045,11 @@ static jfloat Java_MotionEvent_getTouchMajorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getTouchMajor", "(I)F", &g_MotionEvent_getTouchMajorF_I); @@ -1067,11 +1068,11 @@ static jfloat Java_MotionEvent_getTouchMinorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getTouchMinor", "(I)F", &g_MotionEvent_getTouchMinorF_I); @@ -1090,11 +1091,11 @@ static jfloat Java_MotionEvent_getToolMajorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getToolMajor", "(I)F", &g_MotionEvent_getToolMajorF_I); @@ -1113,11 +1114,11 @@ static jfloat Java_MotionEvent_getToolMinorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getToolMinor", "(I)F", &g_MotionEvent_getToolMinorF_I); @@ -1136,11 +1137,11 @@ static jfloat Java_MotionEvent_getOrientationF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getOrientation", "(I)F", &g_MotionEvent_getOrientationF_I); @@ -1161,11 +1162,11 @@ static jfloat Java_MotionEvent_getAxisValueF_I_I(JNIEnv* env, jobject obj, JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getAxisValue", "(II)F", &g_MotionEvent_getAxisValueF_I_I); @@ -1186,11 +1187,11 @@ static void Java_MotionEvent_getPointerCoords(JNIEnv* env, jobject obj, jobject p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getPointerCoords", "(ILandroid/view/MotionEvent$PointerCoords;)V", &g_MotionEvent_getPointerCoords); @@ -1210,11 +1211,11 @@ static void Java_MotionEvent_getPointerProperties(JNIEnv* env, jobject obj, jobject p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getPointerProperties", "(ILandroid/view/MotionEvent$PointerProperties;)V", &g_MotionEvent_getPointerProperties); @@ -1231,11 +1232,11 @@ static jint Java_MotionEvent_getMetaState(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getMetaState(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getMetaState", "()I", &g_MotionEvent_getMetaState); @@ -1253,11 +1254,11 @@ static jint Java_MotionEvent_getButtonState(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getButtonState(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getButtonState", "()I", &g_MotionEvent_getButtonState); @@ -1275,11 +1276,11 @@ static jfloat Java_MotionEvent_getRawX(JNIEnv* env, jobject obj) __attribute__ static jfloat Java_MotionEvent_getRawX(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getRawX", "()F", &g_MotionEvent_getRawX); @@ -1297,11 +1298,11 @@ static jfloat Java_MotionEvent_getRawY(JNIEnv* env, jobject obj) __attribute__ static jfloat Java_MotionEvent_getRawY(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getRawY", "()F", &g_MotionEvent_getRawY); @@ -1319,11 +1320,11 @@ static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getXPrecision(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getXPrecision", "()F", &g_MotionEvent_getXPrecision); @@ -1341,11 +1342,11 @@ static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, jobject obj) static jfloat Java_MotionEvent_getYPrecision(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getYPrecision", "()F", &g_MotionEvent_getYPrecision); @@ -1363,11 +1364,11 @@ static jint Java_MotionEvent_getHistorySize(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getHistorySize(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistorySize", "()I", &g_MotionEvent_getHistorySize); @@ -1386,11 +1387,11 @@ static jlong Java_MotionEvent_getHistoricalEventTime(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalEventTime", "(I)J", &g_MotionEvent_getHistoricalEventTime); @@ -1409,11 +1410,11 @@ static jfloat Java_MotionEvent_getHistoricalXF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalX", "(I)F", &g_MotionEvent_getHistoricalXF_I); @@ -1432,11 +1433,11 @@ static jfloat Java_MotionEvent_getHistoricalYF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalY", "(I)F", &g_MotionEvent_getHistoricalYF_I); @@ -1455,11 +1456,11 @@ static jfloat Java_MotionEvent_getHistoricalPressureF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalPressure", "(I)F", &g_MotionEvent_getHistoricalPressureF_I); @@ -1478,11 +1479,11 @@ static jfloat Java_MotionEvent_getHistoricalSizeF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalSize", "(I)F", &g_MotionEvent_getHistoricalSizeF_I); @@ -1501,11 +1502,11 @@ static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalTouchMajor", "(I)F", &g_MotionEvent_getHistoricalTouchMajorF_I); @@ -1524,11 +1525,11 @@ static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalTouchMinor", "(I)F", &g_MotionEvent_getHistoricalTouchMinorF_I); @@ -1547,11 +1548,11 @@ static jfloat Java_MotionEvent_getHistoricalToolMajorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalToolMajor", "(I)F", &g_MotionEvent_getHistoricalToolMajorF_I); @@ -1570,11 +1571,11 @@ static jfloat Java_MotionEvent_getHistoricalToolMinorF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalToolMinor", "(I)F", &g_MotionEvent_getHistoricalToolMinorF_I); @@ -1593,11 +1594,11 @@ static jfloat Java_MotionEvent_getHistoricalOrientationF_I(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalOrientation", "(I)F", &g_MotionEvent_getHistoricalOrientationF_I); @@ -1618,11 +1619,11 @@ static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I(JNIEnv* env, jobject JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalAxisValue", "(II)F", &g_MotionEvent_getHistoricalAxisValueF_I_I); @@ -1643,11 +1644,11 @@ static jfloat Java_MotionEvent_getHistoricalXF_I_I(JNIEnv* env, jobject obj, JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalX", "(II)F", &g_MotionEvent_getHistoricalXF_I_I); @@ -1668,11 +1669,11 @@ static jfloat Java_MotionEvent_getHistoricalYF_I_I(JNIEnv* env, jobject obj, JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalY", "(II)F", &g_MotionEvent_getHistoricalYF_I_I); @@ -1693,11 +1694,11 @@ static jfloat Java_MotionEvent_getHistoricalPressureF_I_I(JNIEnv* env, jobject JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalPressure", "(II)F", &g_MotionEvent_getHistoricalPressureF_I_I); @@ -1718,11 +1719,11 @@ static jfloat Java_MotionEvent_getHistoricalSizeF_I_I(JNIEnv* env, jobject obj, JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalSize", "(II)F", &g_MotionEvent_getHistoricalSizeF_I_I); @@ -1743,11 +1744,11 @@ static jfloat Java_MotionEvent_getHistoricalTouchMajorF_I_I(JNIEnv* env, jobject JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalTouchMajor", "(II)F", &g_MotionEvent_getHistoricalTouchMajorF_I_I); @@ -1768,11 +1769,11 @@ static jfloat Java_MotionEvent_getHistoricalTouchMinorF_I_I(JNIEnv* env, jobject JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalTouchMinor", "(II)F", &g_MotionEvent_getHistoricalTouchMinorF_I_I); @@ -1793,11 +1794,11 @@ static jfloat Java_MotionEvent_getHistoricalToolMajorF_I_I(JNIEnv* env, jobject JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalToolMajor", "(II)F", &g_MotionEvent_getHistoricalToolMajorF_I_I); @@ -1818,11 +1819,11 @@ static jfloat Java_MotionEvent_getHistoricalToolMinorF_I_I(JNIEnv* env, jobject JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalToolMinor", "(II)F", &g_MotionEvent_getHistoricalToolMinorF_I_I); @@ -1843,11 +1844,11 @@ static jfloat Java_MotionEvent_getHistoricalOrientationF_I_I(JNIEnv* env, JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalOrientation", "(II)F", &g_MotionEvent_getHistoricalOrientationF_I_I); @@ -1870,11 +1871,11 @@ static jfloat Java_MotionEvent_getHistoricalAxisValueF_I_I_I(JNIEnv* env, JniIntWrapper p2) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalAxisValue", "(III)F", &g_MotionEvent_getHistoricalAxisValueF_I_I_I); @@ -1897,11 +1898,11 @@ static void Java_MotionEvent_getHistoricalPointerCoords(JNIEnv* env, jobject jobject p2) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getHistoricalPointerCoords", "(IILandroid/view/MotionEvent$PointerCoords;)V", &g_MotionEvent_getHistoricalPointerCoords); @@ -1918,11 +1919,11 @@ static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, jobject obj) static jint Java_MotionEvent_getEdgeFlags(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, 0); + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "getEdgeFlags", "()I", &g_MotionEvent_getEdgeFlags); @@ -1941,11 +1942,11 @@ static void Java_MotionEvent_setEdgeFlags(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "setEdgeFlags", "(I)V", &g_MotionEvent_setEdgeFlags); @@ -1963,11 +1964,11 @@ static void Java_MotionEvent_setAction(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "setAction", "(I)V", &g_MotionEvent_setAction); @@ -1985,11 +1986,11 @@ static void Java_MotionEvent_offsetLocation(JNIEnv* env, jobject obj, jfloat p0, jfloat p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "offsetLocation", "(FF)V", &g_MotionEvent_offsetLocation); @@ -2007,11 +2008,11 @@ static void Java_MotionEvent_setLocation(JNIEnv* env, jobject obj, jfloat p0, jfloat p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "setLocation", "(FF)V", &g_MotionEvent_setLocation); @@ -2028,11 +2029,11 @@ static void Java_MotionEvent_transform(JNIEnv* env, jobject obj, jobject p0) static void Java_MotionEvent_transform(JNIEnv* env, jobject obj, jobject p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "transform", "(Landroid/graphics/Matrix;)V", &g_MotionEvent_transform); @@ -2060,11 +2061,11 @@ static void Java_MotionEvent_addBatchV_J_F_F_F_F_I(JNIEnv* env, jobject obj, JniIntWrapper p5) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "addBatch", "(JFFFFI)V", &g_MotionEvent_addBatchV_J_F_F_F_F_I); @@ -2086,11 +2087,11 @@ static void Java_MotionEvent_addBatchV_J_LAVMEPC_I(JNIEnv* env, jobject obj, JniIntWrapper p2) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "addBatch", "(J[Landroid/view/MotionEvent$PointerCoords;I)V", &g_MotionEvent_addBatchV_J_LAVMEPC_I); @@ -2109,11 +2110,11 @@ static base::android::ScopedJavaLocalRef Java_MotionEvent_toString(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz, NULL); + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "toString", "()Ljava/lang/String;", &g_MotionEvent_toString); @@ -2132,18 +2133,18 @@ static base::android::ScopedJavaLocalRef static base::android::ScopedJavaLocalRef Java_MotionEvent_actionToString(JNIEnv* env, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "actionToString", "(I)Ljava/lang/String;", &g_MotionEvent_actionToString); jstring ret = - static_cast(env->CallStaticObjectMethod(g_MotionEvent_clazz, + static_cast(env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, as_jint(p0))); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -2156,18 +2157,18 @@ static base::android::ScopedJavaLocalRef static base::android::ScopedJavaLocalRef Java_MotionEvent_axisToString(JNIEnv* env, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, NULL); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "axisToString", "(I)Ljava/lang/String;", &g_MotionEvent_axisToString); jstring ret = - static_cast(env->CallStaticObjectMethod(g_MotionEvent_clazz, + static_cast(env->CallStaticObjectMethod(MotionEvent_clazz(env), method_id, as_jint(p0))); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); @@ -2178,18 +2179,18 @@ static jint Java_MotionEvent_axisFromString(JNIEnv* env, jstring p0) __attribute__ ((unused)); static jint Java_MotionEvent_axisFromString(JNIEnv* env, jstring p0) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_MotionEvent_clazz, - g_MotionEvent_clazz, 0); + CHECK_CLAZZ(env, MotionEvent_clazz(env), + MotionEvent_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "axisFromString", "(Ljava/lang/String;)I", &g_MotionEvent_axisFromString); jint ret = - env->CallStaticIntMethod(g_MotionEvent_clazz, + env->CallStaticIntMethod(MotionEvent_clazz(env), method_id, p0); jni_generator::CheckException(env); return ret; @@ -2202,11 +2203,11 @@ static void Java_MotionEvent_writeToParcel(JNIEnv* env, jobject obj, jobject p0, JniIntWrapper p1) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_MotionEvent_clazz); + MotionEvent_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_MotionEvent_clazz, + env, MotionEvent_clazz(env), "writeToParcel", "(Landroid/os/Parcel;I)V", &g_MotionEvent_writeToParcel); diff --git a/base/android/jni_generator/testEagerCalledByNativesOption.golden b/base/android/jni_generator/testEagerCalledByNativesOption.golden index 4ff81ac516434..6c1323ed99c0c 100644 --- a/base/android/jni_generator/testEagerCalledByNativesOption.golden +++ b/base/android/jni_generator/testEagerCalledByNativesOption.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kTestClassPath[] = "org/chromium/example/jni_generator/Test"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_Test_clazz = NULL; +#define Test_clazz(env) g_Test_clazz jmethodID g_Test_testMethodWithParam = NULL; jmethodID g_Test_testStaticMethodWithParam = NULL; jmethodID g_Test_testMethodWithNoParam = NULL; @@ -46,20 +47,21 @@ static void testMethodWithParam(JNIEnv* env, jobject obj, JniIntWrapper iParam) } static jint testStaticMethodWithParam(JNIEnv* env, JniIntWrapper iParam) { - jint ret = env->CallStaticIntMethod(g_Test_clazz, + jint ret = env->CallStaticIntMethod(Test_clazz(env), g_Test_testStaticMethodWithParam, as_jint(iParam)); return ret; } static jdouble testMethodWithNoParam(JNIEnv* env) { - jdouble ret = env->CallStaticDoubleMethod(g_Test_clazz, + jdouble ret = env->CallStaticDoubleMethod(Test_clazz(env), g_Test_testMethodWithNoParam); return ret; } static base::android::ScopedJavaLocalRef testStaticMethodWithNoParam(JNIEnv* env) { - jstring ret = static_cast(env->CallStaticObjectMethod(g_Test_clazz, + jstring ret = + static_cast(env->CallStaticObjectMethod(Test_clazz(env), g_Test_testStaticMethodWithNoParam)); return base::android::ScopedJavaLocalRef(env, ret); } @@ -81,16 +83,16 @@ static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) { const int kMethodsTestSize = arraysize(kMethodsTest); - if (env->RegisterNatives(g_Test_clazz, + if (env->RegisterNatives(Test_clazz(env), kMethodsTest, kMethodsTestSize) < 0) { jni_generator::HandleRegistrationError( - env, g_Test_clazz, __FILE__); + env, Test_clazz(env), __FILE__); return false; } g_Test_testMethodWithParam = env->GetMethodID( - g_Test_clazz, + Test_clazz(env), "testMethodWithParam", "(" "I" @@ -101,7 +103,7 @@ static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) { } g_Test_testStaticMethodWithParam = env->GetStaticMethodID( - g_Test_clazz, + Test_clazz(env), "testStaticMethodWithParam", "(" "I" @@ -112,7 +114,7 @@ static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) { } g_Test_testMethodWithNoParam = env->GetStaticMethodID( - g_Test_clazz, + Test_clazz(env), "testMethodWithNoParam", "(" ")" @@ -122,7 +124,7 @@ static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) { } g_Test_testStaticMethodWithNoParam = env->GetStaticMethodID( - g_Test_clazz, + Test_clazz(env), "testStaticMethodWithNoParam", "(" ")" diff --git a/base/android/jni_generator/testFromJavaP.golden b/base/android/jni_generator/testFromJavaP.golden index 34e21184316c5..5827410c7414a 100644 --- a/base/android/jni_generator/testFromJavaP.golden +++ b/base/android/jni_generator/testFromJavaP.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kInputStreamClassPath[] = "java/io/InputStream"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_InputStream_clazz = NULL; +#define InputStream_clazz(env) g_InputStream_clazz } // namespace @@ -34,11 +35,11 @@ static jint Java_InputStream_available(JNIEnv* env, jobject obj) __attribute__ static jint Java_InputStream_available(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz, 0); + InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "available", "()I", &g_InputStream_available); @@ -56,11 +57,11 @@ static void Java_InputStream_close(JNIEnv* env, jobject obj) __attribute__ static void Java_InputStream_close(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz); + InputStream_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "close", "()V", &g_InputStream_close); @@ -77,11 +78,11 @@ static void Java_InputStream_mark(JNIEnv* env, jobject obj, JniIntWrapper p0) static void Java_InputStream_mark(JNIEnv* env, jobject obj, JniIntWrapper p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz); + InputStream_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "mark", "(I)V", &g_InputStream_mark); @@ -98,11 +99,11 @@ static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) static jboolean Java_InputStream_markSupported(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz, false); + InputStream_clazz(env), false); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "markSupported", "()Z", &g_InputStream_markSupported); @@ -120,11 +121,11 @@ static jint Java_InputStream_readI(JNIEnv* env, jobject obj) __attribute__ static jint Java_InputStream_readI(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz, 0); + InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "read", "()I", &g_InputStream_readI); @@ -142,11 +143,11 @@ static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) static jint Java_InputStream_readI_AB(JNIEnv* env, jobject obj, jbyteArray p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz, 0); + InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "read", "([B)I", &g_InputStream_readI_AB); @@ -169,11 +170,11 @@ static jint Java_InputStream_readI_AB_I_I(JNIEnv* env, jobject obj, jbyteArray JniIntWrapper p2) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz, 0); + InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "read", "([BII)I", &g_InputStream_readI_AB_I_I); @@ -191,11 +192,11 @@ static void Java_InputStream_reset(JNIEnv* env, jobject obj) __attribute__ static void Java_InputStream_reset(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz); + InputStream_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "reset", "()V", &g_InputStream_reset); @@ -212,11 +213,11 @@ static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) static jlong Java_InputStream_skip(JNIEnv* env, jobject obj, jlong p0) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_InputStream_clazz, 0); + InputStream_clazz(env), 0); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "skip", "(J)J", &g_InputStream_skip); @@ -234,18 +235,18 @@ static base::android::ScopedJavaLocalRef static base::android::ScopedJavaLocalRef Java_InputStream_Constructor(JNIEnv* env) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_InputStream_clazz, - g_InputStream_clazz, NULL); + CHECK_CLAZZ(env, InputStream_clazz(env), + InputStream_clazz(env), NULL); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_InputStream_clazz, + env, InputStream_clazz(env), "", "()V", &g_InputStream_Constructor); jobject ret = - env->NewObject(g_InputStream_clazz, + env->NewObject(InputStream_clazz(env), method_id); jni_generator::CheckException(env); return base::android::ScopedJavaLocalRef(env, ret); diff --git a/base/android/jni_generator/testFromJavaPGenerics.golden b/base/android/jni_generator/testFromJavaPGenerics.golden index 48582fdb21d4d..5d783909473f5 100644 --- a/base/android/jni_generator/testFromJavaPGenerics.golden +++ b/base/android/jni_generator/testFromJavaPGenerics.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kHashSetClassPath[] = "java/util/HashSet"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_HashSet_clazz = NULL; +#define HashSet_clazz(env) g_HashSet_clazz } // namespace @@ -34,11 +35,11 @@ static void Java_HashSet_dummy(JNIEnv* env, jobject obj) __attribute__ static void Java_HashSet_dummy(JNIEnv* env, jobject obj) { /* Must call RegisterNativesImpl() */ CHECK_CLAZZ(env, obj, - g_HashSet_clazz); + HashSet_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_INSTANCE>( - env, g_HashSet_clazz, + env, HashSet_clazz(env), "dummy", "()V", &g_HashSet_dummy); diff --git a/base/android/jni_generator/testInnerClassNatives.golden b/base/android/jni_generator/testInnerClassNatives.golden index 56a2e9b2ba228..2dee84eda43a7 100644 --- a/base/android/jni_generator/testInnerClassNatives.golden +++ b/base/android/jni_generator/testInnerClassNatives.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -22,6 +22,7 @@ const char kTestJniClassPath[] = "org/chromium/TestJni"; const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_TestJni_clazz = NULL; +#define TestJni_clazz(env) g_TestJni_clazz } // namespace @@ -44,11 +45,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass); - if (env->RegisterNatives(g_MyInnerClass_clazz, + if (env->RegisterNatives(MyInnerClass_clazz(env), kMethodsMyInnerClass, kMethodsMyInnerClassSize) < 0) { jni_generator::HandleRegistrationError( - env, g_MyInnerClass_clazz, __FILE__); + env, MyInnerClass_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden index 07b857f9a3ac9..6ffbaac28b600 100644 --- a/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden +++ b/base/android/jni_generator/testInnerClassNativesBothInnerAndOuter.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -23,6 +23,7 @@ const char kMyOtherInnerClassClassPath[] = const char kTestJniClassPath[] = "org/chromium/TestJni"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_TestJni_clazz = NULL; +#define TestJni_clazz(env) g_TestJni_clazz } // namespace @@ -55,21 +56,21 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsMyOtherInnerClassSize = arraysize(kMethodsMyOtherInnerClass); - if (env->RegisterNatives(g_MyOtherInnerClass_clazz, + if (env->RegisterNatives(MyOtherInnerClass_clazz(env), kMethodsMyOtherInnerClass, kMethodsMyOtherInnerClassSize) < 0) { jni_generator::HandleRegistrationError( - env, g_MyOtherInnerClass_clazz, __FILE__); + env, MyOtherInnerClass_clazz(env), __FILE__); return false; } const int kMethodsTestJniSize = arraysize(kMethodsTestJni); - if (env->RegisterNatives(g_TestJni_clazz, + if (env->RegisterNatives(TestJni_clazz(env), kMethodsTestJni, kMethodsTestJniSize) < 0) { jni_generator::HandleRegistrationError( - env, g_TestJni_clazz, __FILE__); + env, TestJni_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testInnerClassNativesMultiple.golden b/base/android/jni_generator/testInnerClassNativesMultiple.golden index 6a7f04d8bc981..b74e65fa13d3c 100644 --- a/base/android/jni_generator/testInnerClassNativesMultiple.golden +++ b/base/android/jni_generator/testInnerClassNativesMultiple.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -24,6 +24,7 @@ const char kTestJniClassPath[] = "org/chromium/TestJni"; const char kMyInnerClassClassPath[] = "org/chromium/TestJni$MyInnerClass"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_TestJni_clazz = NULL; +#define TestJni_clazz(env) g_TestJni_clazz } // namespace @@ -56,21 +57,21 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsMyOtherInnerClassSize = arraysize(kMethodsMyOtherInnerClass); - if (env->RegisterNatives(g_MyOtherInnerClass_clazz, + if (env->RegisterNatives(MyOtherInnerClass_clazz(env), kMethodsMyOtherInnerClass, kMethodsMyOtherInnerClassSize) < 0) { jni_generator::HandleRegistrationError( - env, g_MyOtherInnerClass_clazz, __FILE__); + env, MyOtherInnerClass_clazz(env), __FILE__); return false; } const int kMethodsMyInnerClassSize = arraysize(kMethodsMyInnerClass); - if (env->RegisterNatives(g_MyInnerClass_clazz, + if (env->RegisterNatives(MyInnerClass_clazz(env), kMethodsMyInnerClass, kMethodsMyInnerClassSize) < 0) { jni_generator::HandleRegistrationError( - env, g_MyInnerClass_clazz, __FILE__); + env, MyInnerClass_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testJNIInitNativeNameOption.golden b/base/android/jni_generator/testJNIInitNativeNameOption.golden index 0d5d3c6137092..53b5f17886058 100644 --- a/base/android/jni_generator/testJNIInitNativeNameOption.golden +++ b/base/android/jni_generator/testJNIInitNativeNameOption.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kTestClassPath[] = "org/chromium/example/jni_generator/Test"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_Test_clazz = NULL; +#define Test_clazz(env) g_Test_clazz } // namespace @@ -49,11 +50,11 @@ static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) { const int kMethodsTestSize = arraysize(kMethodsTest); - if (env->RegisterNatives(g_Test_clazz, + if (env->RegisterNatives(Test_clazz(env), kMethodsTest, kMethodsTestSize) < 0) { jni_generator::HandleRegistrationError( - env, g_Test_clazz, __FILE__); + env, Test_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testJarJarRemapping.golden b/base/android/jni_generator/testJarJarRemapping.golden index 9b2c0b3fc67e3..75a35c5fa2aa4 100644 --- a/base/android/jni_generator/testJarJarRemapping.golden +++ b/base/android/jni_generator/testJarJarRemapping.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kExampleClassPath[] = "com/test/jni_generator/Example"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_Example_clazz = NULL; +#define Example_clazz(env) g_Example_clazz } // namespace @@ -69,11 +70,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsExampleSize = arraysize(kMethodsExample); - if (env->RegisterNatives(g_Example_clazz, + if (env->RegisterNatives(Example_clazz(env), kMethodsExample, kMethodsExampleSize) < 0) { jni_generator::HandleRegistrationError( - env, g_Example_clazz, __FILE__); + env, Example_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden index 9cc1256cbfe2b..df5b1c86568af 100644 --- a/base/android/jni_generator/testMultipleJNIAdditionalImport.golden +++ b/base/android/jni_generator/testMultipleJNIAdditionalImport.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kFooClassPath[] = "org/chromium/foo/Foo"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_Foo_clazz = NULL; +#define Foo_clazz(env) g_Foo_clazz } // namespace @@ -34,12 +35,12 @@ static base::subtle::AtomicWord g_Foo_calledByNative = 0; static void Java_Foo_calledByNative(JNIEnv* env, jobject callback1, jobject callback2) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_Foo_clazz, - g_Foo_clazz); + CHECK_CLAZZ(env, Foo_clazz(env), + Foo_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_Foo_clazz, + env, Foo_clazz(env), "calledByNative", "(" @@ -49,7 +50,7 @@ static void Java_Foo_calledByNative(JNIEnv* env, jobject callback1, "V", &g_Foo_calledByNative); - env->CallStaticVoidMethod(g_Foo_clazz, + env->CallStaticVoidMethod(Foo_clazz(env), method_id, callback1, callback2); jni_generator::CheckException(env); @@ -72,11 +73,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsFooSize = arraysize(kMethodsFoo); - if (env->RegisterNatives(g_Foo_clazz, + if (env->RegisterNatives(Foo_clazz(env), kMethodsFoo, kMethodsFooSize) < 0) { jni_generator::HandleRegistrationError( - env, g_Foo_clazz, __FILE__); + env, Foo_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testNativeExportsOption.golden b/base/android/jni_generator/testNativeExportsOption.golden new file mode 100644 index 0000000000000..5beaa82706bfb --- /dev/null +++ b/base/android/jni_generator/testNativeExportsOption.golden @@ -0,0 +1,218 @@ +// 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. + +// This file is autogenerated by +// base/android/jni_generator/jni_generator.py +// For +// org/chromium/example/jni_generator/SampleForTests + +#ifndef org_chromium_example_jni_generator_SampleForTests_JNI +#define org_chromium_example_jni_generator_SampleForTests_JNI + +#include + +#include "base/android/jni_generator/jni_generator_helper.h" + +#include "base/android/jni_int_wrapper.h" + +// Step 1: forward declarations. +namespace { +const char kSampleForTestsClassPath[] = + "org/chromium/example/jni_generator/SampleForTests"; +// Leaking this jclass as we cannot use LazyInstance from some threads. +base::subtle::AtomicWord g_SampleForTests_clazz = 0; +#define SampleForTests_clazz(env) base::android::LazyGetClass(env, kSampleForTestsClassPath, &g_SampleForTests_clazz) + +} // namespace + +extern "C" { + +static jint Init(JNIEnv* env, jobject jcaller); + +__attribute__((visibility("default"))) +jint + Java_org_chromium_example_jni_1generator_SampleForTests_00024MyInnerClass_nativeInit(JNIEnv* + env, jobject jcaller) { + return Init(env, jcaller); +} + +static jint Init(JNIEnv* env, jobject jcaller); + +__attribute__((visibility("default"))) +jint + Java_org_chromium_example_jni_1generator_SampleForTests_00024MyOtherInnerClass_nativeInit(JNIEnv* + env, jobject jcaller) { + return Init(env, jcaller); +} + +}; // extern "C" + +// Step 2: method stubs. + +extern "C" { +__attribute__((visibility("default"))) +jint + Java_org_chromium_example_jni_1generator_SampleForTests_nativeStaticMethod(JNIEnv* + env, + jobject jcaller, + jlong nativeTest, + jint arg1) { + Test* native = reinterpret_cast(nativeTest); + CHECK_NATIVE_PTR(env, jcaller, native, "StaticMethod", 0); + return native->StaticMethod(env, jcaller, arg1); +} + +__attribute__((visibility("default"))) +jint + Java_org_chromium_example_jni_1generator_SampleForTests_nativeMethod(JNIEnv* + env, + jobject jcaller, + jlong nativeTest, + jint arg1) { + Test* native = reinterpret_cast(nativeTest); + CHECK_NATIVE_PTR(env, jcaller, native, "Method", 0); + return native->Method(env, jcaller, arg1); +} + +static base::subtle::AtomicWord g_SampleForTests_testMethodWithParam = 0; +static void Java_SampleForTests_testMethodWithParam(JNIEnv* env, jobject obj, + JniIntWrapper iParam) { + /* Must call RegisterNativesImpl() */ + CHECK_CLAZZ(env, obj, + SampleForTests_clazz(env)); + jmethodID method_id = + base::android::MethodID::LazyGet< + base::android::MethodID::TYPE_INSTANCE>( + env, SampleForTests_clazz(env), + "testMethodWithParam", + +"(" +"I" +")" +"V", + &g_SampleForTests_testMethodWithParam); + + env->CallVoidMethod(obj, + method_id, as_jint(iParam)); + jni_generator::CheckException(env); + +} + +static base::subtle::AtomicWord g_SampleForTests_testMethodWithParamAndReturn = + 0; +static base::android::ScopedJavaLocalRef + Java_SampleForTests_testMethodWithParamAndReturn(JNIEnv* env, jobject obj, + JniIntWrapper iParam) { + /* Must call RegisterNativesImpl() */ + CHECK_CLAZZ(env, obj, + SampleForTests_clazz(env), NULL); + jmethodID method_id = + base::android::MethodID::LazyGet< + base::android::MethodID::TYPE_INSTANCE>( + env, SampleForTests_clazz(env), + "testMethodWithParamAndReturn", + +"(" +"I" +")" +"Ljava/lang/String;", + &g_SampleForTests_testMethodWithParamAndReturn); + + jstring ret = + static_cast(env->CallObjectMethod(obj, + method_id, as_jint(iParam))); + jni_generator::CheckException(env); + return base::android::ScopedJavaLocalRef(env, ret); +} + +static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithParam = 0; +static jint Java_SampleForTests_testStaticMethodWithParam(JNIEnv* env, + JniIntWrapper iParam) { + /* Must call RegisterNativesImpl() */ + CHECK_CLAZZ(env, SampleForTests_clazz(env), + SampleForTests_clazz(env), 0); + jmethodID method_id = + base::android::MethodID::LazyGet< + base::android::MethodID::TYPE_STATIC>( + env, SampleForTests_clazz(env), + "testStaticMethodWithParam", + +"(" +"I" +")" +"I", + &g_SampleForTests_testStaticMethodWithParam); + + jint ret = + env->CallStaticIntMethod(SampleForTests_clazz(env), + method_id, as_jint(iParam)); + jni_generator::CheckException(env); + return ret; +} + +static base::subtle::AtomicWord g_SampleForTests_testMethodWithNoParam = 0; +static jdouble Java_SampleForTests_testMethodWithNoParam(JNIEnv* env) { + /* Must call RegisterNativesImpl() */ + CHECK_CLAZZ(env, SampleForTests_clazz(env), + SampleForTests_clazz(env), 0); + jmethodID method_id = + base::android::MethodID::LazyGet< + base::android::MethodID::TYPE_STATIC>( + env, SampleForTests_clazz(env), + "testMethodWithNoParam", + +"(" +")" +"D", + &g_SampleForTests_testMethodWithNoParam); + + jdouble ret = + env->CallStaticDoubleMethod(SampleForTests_clazz(env), + method_id); + jni_generator::CheckException(env); + return ret; +} + +static base::subtle::AtomicWord g_SampleForTests_testStaticMethodWithNoParam = + 0; +static base::android::ScopedJavaLocalRef + Java_SampleForTests_testStaticMethodWithNoParam(JNIEnv* env) { + /* Must call RegisterNativesImpl() */ + CHECK_CLAZZ(env, SampleForTests_clazz(env), + SampleForTests_clazz(env), NULL); + jmethodID method_id = + base::android::MethodID::LazyGet< + base::android::MethodID::TYPE_STATIC>( + env, SampleForTests_clazz(env), + "testStaticMethodWithNoParam", + +"(" +")" +"Ljava/lang/String;", + &g_SampleForTests_testStaticMethodWithNoParam); + + jstring ret = +static_cast(env->CallStaticObjectMethod(SampleForTests_clazz(env), + method_id)); + jni_generator::CheckException(env); + return base::android::ScopedJavaLocalRef(env, ret); +} +}; // extern "C" + +// Step 3: RegisterNatives. + +static bool RegisterNativesImpl(JNIEnv* env, jclass clazz) { + base::subtle::Release_Store(&g_SampleForTests_clazz, + static_cast(env->NewWeakGlobalRef(clazz)); + + return true; +} + +extern "C" JNIEXPORT bool JNICALL +Java_org_chromium_example_jni_1generator_SampleForTests_nativeInitNativeClass(JNIEnv* + env, jclass clazz) { + return RegisterNativesImpl(env, clazz); +} + +#endif // org_chromium_example_jni_generator_SampleForTests_JNI diff --git a/base/android/jni_generator/testNatives.golden b/base/android/jni_generator/testNatives.golden index db69a5aaa6d9d..8708fa2dca2ea 100644 --- a/base/android/jni_generator/testNatives.golden +++ b/base/android/jni_generator/testNatives.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kTestJniClassPath[] = "org/chromium/TestJni"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_TestJni_clazz = NULL; +#define TestJni_clazz(env) g_TestJni_clazz } // namespace @@ -202,11 +203,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsTestJniSize = arraysize(kMethodsTestJni); - if (env->RegisterNatives(g_TestJni_clazz, + if (env->RegisterNatives(TestJni_clazz(env), kMethodsTestJni, kMethodsTestJniSize) < 0) { jni_generator::HandleRegistrationError( - env, g_TestJni_clazz, __FILE__); + env, TestJni_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testNativesLong.golden b/base/android/jni_generator/testNativesLong.golden index 25b5fadf0d98b..11e7c49cfa6e5 100644 --- a/base/android/jni_generator/testNativesLong.golden +++ b/base/android/jni_generator/testNativesLong.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kTestJniClassPath[] = "org/chromium/TestJni"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_TestJni_clazz = NULL; +#define TestJni_clazz(env) g_TestJni_clazz } // namespace @@ -49,11 +50,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsTestJniSize = arraysize(kMethodsTestJni); - if (env->RegisterNatives(g_TestJni_clazz, + if (env->RegisterNatives(TestJni_clazz(env), kMethodsTestJni, kMethodsTestJniSize) < 0) { jni_generator::HandleRegistrationError( - env, g_TestJni_clazz, __FILE__); + env, TestJni_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testPureNativeMethodsOption.golden b/base/android/jni_generator/testPureNativeMethodsOption.golden index 8d9ee9e26f06a..a45a3864d6cc7 100644 --- a/base/android/jni_generator/testPureNativeMethodsOption.golden +++ b/base/android/jni_generator/testPureNativeMethodsOption.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kTestClassPath[] = "org/chromium/example/jni_generator/Test"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_Test_clazz = NULL; +#define Test_clazz(env) g_Test_clazz } // namespace @@ -50,11 +51,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsTestSize = arraysize(kMethodsTest); - if (env->RegisterNatives(g_Test_clazz, + if (env->RegisterNatives(Test_clazz(env), kMethodsTest, kMethodsTestSize) < 0) { jni_generator::HandleRegistrationError( - env, g_Test_clazz, __FILE__); + env, Test_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_generator/testSingleJNIAdditionalImport.golden b/base/android/jni_generator/testSingleJNIAdditionalImport.golden index e395657cf79bf..787f7f5fd8058 100644 --- a/base/android/jni_generator/testSingleJNIAdditionalImport.golden +++ b/base/android/jni_generator/testSingleJNIAdditionalImport.golden @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. @@ -21,6 +21,7 @@ namespace { const char kFooClassPath[] = "org/chromium/foo/Foo"; // Leaking this jclass as we cannot use LazyInstance from some threads. jclass g_Foo_clazz = NULL; +#define Foo_clazz(env) g_Foo_clazz } // namespace @@ -32,12 +33,12 @@ static void DoSomething(JNIEnv* env, jclass jcaller, static base::subtle::AtomicWord g_Foo_calledByNative = 0; static void Java_Foo_calledByNative(JNIEnv* env, jobject callback) { /* Must call RegisterNativesImpl() */ - CHECK_CLAZZ(env, g_Foo_clazz, - g_Foo_clazz); + CHECK_CLAZZ(env, Foo_clazz(env), + Foo_clazz(env)); jmethodID method_id = base::android::MethodID::LazyGet< base::android::MethodID::TYPE_STATIC>( - env, g_Foo_clazz, + env, Foo_clazz(env), "calledByNative", "(" @@ -46,7 +47,7 @@ static void Java_Foo_calledByNative(JNIEnv* env, jobject callback) { "V", &g_Foo_calledByNative); - env->CallStaticVoidMethod(g_Foo_clazz, + env->CallStaticVoidMethod(Foo_clazz(env), method_id, callback); jni_generator::CheckException(env); @@ -68,11 +69,11 @@ static bool RegisterNativesImpl(JNIEnv* env) { const int kMethodsFooSize = arraysize(kMethodsFoo); - if (env->RegisterNatives(g_Foo_clazz, + if (env->RegisterNatives(Foo_clazz(env), kMethodsFoo, kMethodsFooSize) < 0) { jni_generator::HandleRegistrationError( - env, g_Foo_clazz, __FILE__); + env, Foo_clazz(env), __FILE__); return false; } diff --git a/base/android/jni_utils.cc b/base/android/jni_utils.cc new file mode 100644 index 0000000000000..b4d682bc191f9 --- /dev/null +++ b/base/android/jni_utils.cc @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/jni_utils.h" + +#include "base/android/jni_android.h" +#include "base/android/scoped_java_ref.h" + +#include "jni/JNIUtils_jni.h" + +namespace base { +namespace android { + +ScopedJavaLocalRef GetClassLoader(JNIEnv* env) { + return Java_JNIUtils_getClassLoader(env); +} + +bool RegisterJNIUtils(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace base + diff --git a/base/android/jni_utils.h b/base/android/jni_utils.h new file mode 100644 index 0000000000000..b793aed2be6f9 --- /dev/null +++ b/base/android/jni_utils.h @@ -0,0 +1,27 @@ +// 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 BASE_ANDROID_JNI_UTILS_H_ +#define BASE_ANDROID_JNI_UTILS_H_ + +#include + +#include "base/android/scoped_java_ref.h" + +namespace base { + +namespace android { + +// Gets a ClassLoader instance capable of loading Chromium java classes. +// This should be called either from JNI_OnLoad or from within a method called +// via JNI from Java. +BASE_EXPORT ScopedJavaLocalRef GetClassLoader(JNIEnv* env); + +bool RegisterJNIUtils(JNIEnv* env); + +} // namespace android +} // namespace base + +#endif // BASE_ANDROID_JNI_UTILS_H_ + diff --git a/base/android/library_loader/library_loader_hooks.cc b/base/android/library_loader/library_loader_hooks.cc index 79470108eb33b..87f791090b0a4 100644 --- a/base/android/library_loader/library_loader_hooks.cc +++ b/base/android/library_loader/library_loader_hooks.cc @@ -4,6 +4,7 @@ #include "base/android/library_loader/library_loader_hooks.h" +#include "base/android/command_line_android.h" #include "base/android/jni_string.h" #include "base/at_exit.h" #include "base/metrics/histogram.h" @@ -24,12 +25,16 @@ void SetLibraryLoadedHook(LibraryLoadedHook* func) { g_registration_callback = func; } -static jboolean LibraryLoaded(JNIEnv* env, jclass clazz, - jobjectArray init_command_line) { +static void InitCommandLine(JNIEnv* env, jclass clazz, + jobjectArray init_command_line) { + InitNativeCommandLineFromJavaArray(env, init_command_line); +} + +static jboolean LibraryLoaded(JNIEnv* env, jclass clazz) { if(g_registration_callback == NULL) { return true; } - return g_registration_callback(env, clazz, init_command_line); + return g_registration_callback(env, clazz); } static void RecordChromiumAndroidLinkerHistogram( diff --git a/base/android/library_loader/library_loader_hooks.h b/base/android/library_loader/library_loader_hooks.h index 06709f6ba9e49..72935cf816208 100644 --- a/base/android/library_loader/library_loader_hooks.h +++ b/base/android/library_loader/library_loader_hooks.h @@ -26,8 +26,7 @@ BASE_EXPORT bool RegisterLibraryLoaderEntryHook(JNIEnv* env); // Note: this can't use base::Callback because there is no way of initializing // the default callback without using static objects, which we forbid. typedef bool LibraryLoadedHook(JNIEnv* env, - jclass clazz, - jobjectArray init_command_line); + jclass clazz); // Set the hook function to be called (from Java) once the libraries are loaded. // SetLibraryLoadedHook may only be called from JNI_OnLoad. The hook function diff --git a/base/android/linker/linker_jni.cc b/base/android/linker/linker_jni.cc index be4a5ed30df2c..d33e6faeaccdc 100644 --- a/base/android/linker/linker_jni.cc +++ b/base/android/linker/linker_jni.cc @@ -297,7 +297,7 @@ bool FileLibraryOpener::Open( // Used for opening the library in a zip file. class ZipLibraryOpener { public: - ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) {} + explicit ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) {} bool Open( crazy_library_t** library, const char* library_name, @@ -310,14 +310,14 @@ bool ZipLibraryOpener::Open( crazy_library_t** library, const char* library_name, crazy_context_t* context) const { - if (!crazy_library_open_in_zip_file( - library, zip_file_, library_name, context)) { - LOG_ERROR("%s: Could not open %s in zip file %s: %s", - __FUNCTION__, library_name, zip_file_, - crazy_context_get_error(context)); - return false; - } - return true; + if (!crazy_library_open_in_zip_file( + library, zip_file_, library_name, context)) { + LOG_ERROR("%s: Could not open %s in zip file %s: %s", + __FUNCTION__, library_name, zip_file_, + crazy_context_get_error(context)); + return false; + } + return true; } } // unnamed namespace @@ -451,7 +451,7 @@ static bool PostForLaterExecution(crazy_callback_t* callback_request, LOG_INFO("%s: Calling back to java with handler %p, opaque %p", __FUNCTION__, callback->handler, callback->opaque); - jlong arg = static_cast(reinterpret_cast(callback)); + jlong arg = static_cast(reinterpret_cast(callback)); env->CallStaticVoidMethod( s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg); diff --git a/base/android/path_utils_unittest.cc b/base/android/path_utils_unittest.cc index b6410754eedd9..c678ce21ec808 100644 --- a/base/android/path_utils_unittest.cc +++ b/base/android/path_utils_unittest.cc @@ -3,8 +3,8 @@ // found in the LICENSE file. #include "base/android/path_utils.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/base/atomicops.h b/base/atomicops.h index cb737cd37047a..84be8c04bc00e 100644 --- a/base/atomicops.h +++ b/base/atomicops.h @@ -152,7 +152,8 @@ Atomic64 Release_Load(volatile const Atomic64* ptr); #include "base/atomicops_internals_arm64_gcc.h" #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) #include "base/atomicops_internals_x86_gcc.h" -#elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS_FAMILY) +#elif defined(COMPILER_GCC) && \ + (defined(ARCH_CPU_MIPS_FAMILY) || defined(ARCH_CPU_MIPS64_FAMILY)) #include "base/atomicops_internals_mips_gcc.h" #else #error "Atomic operations are not supported on your platform" diff --git a/base/atomicops_internals_mips_gcc.h b/base/atomicops_internals_mips_gcc.h index 29947b37afa61..9111a535b26f8 100644 --- a/base/atomicops_internals_mips_gcc.h +++ b/base/atomicops_internals_mips_gcc.h @@ -51,7 +51,7 @@ inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, __asm__ __volatile__(".set push\n" ".set noreorder\n" "1:\n" - "ll %1, %2\n" // old = *ptr + "ll %1, %4\n" // old = *ptr "move %0, %3\n" // temp = new_value "sc %0, %2\n" // *ptr = temp (with atomic check) "beqz %0, 1b\n" // start again on atomic error @@ -73,7 +73,7 @@ inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, __asm__ __volatile__(".set push\n" ".set noreorder\n" "1:\n" - "ll %0, %2\n" // temp = *ptr + "ll %0, %4\n" // temp = *ptr "addu %1, %0, %3\n" // temp2 = temp + increment "sc %1, %2\n" // *ptr = temp2 (with atomic check) "beqz %1, 1b\n" // start again on atomic error @@ -148,6 +148,132 @@ inline Atomic32 Release_Load(volatile const Atomic32* ptr) { return *ptr; } +#if defined(__LP64__) +// 64-bit versions of the atomic ops. + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev, tmp; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "lld %0, %5\n" // prev = *ptr + "bne %0, %3, 2f\n" // if (prev != old_value) goto 2 + "move %2, %4\n" // tmp = new_value + "scd %2, %1\n" // *ptr = tmp (with atomic check) + "beqz %2, 1b\n" // start again on atomic error + "nop\n" // delay slot nop + "2:\n" + ".set pop\n" + : "=&r" (prev), "=m" (*ptr), "=&r" (tmp) + : "Ir" (old_value), "r" (new_value), "m" (*ptr) + : "memory"); + return prev; +} + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + Atomic64 temp, old; + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "lld %1, %4\n" // old = *ptr + "move %0, %3\n" // temp = new_value + "scd %0, %2\n" // *ptr = temp (with atomic check) + "beqz %0, 1b\n" // start again on atomic error + "nop\n" // delay slot nop + ".set pop\n" + : "=&r" (temp), "=&r" (old), "=m" (*ptr) + : "r" (new_value), "m" (*ptr) + : "memory"); + + return old; +} + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 temp, temp2; + + __asm__ __volatile__(".set push\n" + ".set noreorder\n" + "1:\n" + "lld %0, %4\n" // temp = *ptr + "daddu %1, %0, %3\n" // temp2 = temp + increment + "scd %1, %2\n" // *ptr = temp2 (with atomic check) + "beqz %1, 1b\n" // start again on atomic error + "daddu %1, %0, %3\n" // temp2 = temp + increment + ".set pop\n" + : "=&r" (temp), "=&r" (temp2), "=m" (*ptr) + : "Ir" (increment), "m" (*ptr) + : "memory"); + // temp2 now holds the final value. + return temp2; +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + MemoryBarrier(); + Atomic64 res = NoBarrier_AtomicIncrement(ptr, increment); + MemoryBarrier(); + return res; +} + +// "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + MemoryBarrier(); + return res; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + MemoryBarrier(); + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} +#endif + } // namespace base::subtle } // namespace base diff --git a/base/base.gyp b/base/base.gyp index f934b23ab45e3..9f55a7ddbc226 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -54,7 +54,7 @@ ['exclude', '_nss\\.cc$'], ], }], - ['use_glib==1', { + ['use_glib==1 or <(use_ozone)==1', { 'dependencies': [ '../build/linux/system.gyp:glib', ], @@ -239,7 +239,6 @@ 'message_loop/message_pump_glib.h', 'message_loop/message_pump_io_ios.cc', 'message_loop/message_pump_io_ios.h', - 'message_loop/message_pump_observer.h', 'message_loop/message_pump_libevent.cc', 'message_loop/message_pump_libevent.h', 'message_loop/message_pump_mac.h', @@ -448,6 +447,7 @@ 'debug/leak_tracker_unittest.cc', 'debug/proc_maps_linux_unittest.cc', 'debug/stack_trace_unittest.cc', + 'debug/task_annotator_unittest.cc', 'debug/trace_event_argument_unittest.cc', 'debug/trace_event_memory_unittest.cc', 'debug/trace_event_synthetic_delay_unittest.cc', @@ -457,13 +457,13 @@ 'debug/trace_event_win_unittest.cc', 'deferred_sequenced_task_runner_unittest.cc', 'environment_unittest.cc', - 'file_util_unittest.cc', 'file_version_info_unittest.cc', 'files/dir_reader_posix_unittest.cc', 'files/file_path_unittest.cc', 'files/file_proxy_unittest.cc', 'files/file_unittest.cc', 'files/file_util_proxy_unittest.cc', + 'files/file_util_unittest.cc', 'files/important_file_writer_unittest.cc', 'files/memory_mapped_file_unittest.cc', 'files/scoped_temp_dir_unittest.cc', @@ -482,7 +482,6 @@ 'i18n/string_search_unittest.cc', 'i18n/time_formatting_unittest.cc', 'i18n/timezone_unittest.cc', - 'ini_parser_unittest.cc', 'ios/device_util_unittest.mm', 'json/json_parser_unittest.cc', 'json/json_reader_unittest.cc', @@ -498,6 +497,7 @@ 'mac/mac_util_unittest.mm', 'mac/objc_property_releaser_unittest.mm', 'mac/scoped_nsobject_unittest.mm', + 'mac/scoped_objc_class_swizzler_unittest.mm', 'mac/scoped_sending_event_unittest.mm', 'md5_unittest.cc', 'memory/aligned_memory_unittest.cc', @@ -1127,7 +1127,6 @@ 'linux_util.h', 'md5.cc', 'md5.h', - 'message_loop/message_pump_observer.h', 'message_loop/message_pump_libevent.cc', 'message_loop/message_pump_libevent.h', 'metrics/field_trial.cc', @@ -1283,6 +1282,7 @@ 'android/java/src/org/chromium/base/EventLog.java', 'android/java/src/org/chromium/base/FieldTrialList.java', 'android/java/src/org/chromium/base/ImportantFileWriterAndroid.java', + 'android/java/src/org/chromium/base/JNIUtils.java', 'android/java/src/org/chromium/base/library_loader/LibraryLoader.java', 'android/java/src/org/chromium/base/MemoryPressureListener.java', 'android/java/src/org/chromium/base/JavaHandlerThread.java', @@ -1412,10 +1412,10 @@ 'target_name': 'chromium_android_linker', 'type': 'shared_library', 'conditions': [ - ['android_webview_build == 0 and target_arch != "x64"', { - # Avoid breaking the webview build because it - # does not have <(android_ndk_root)/crazy_linker.gyp. - # Note that webview never uses the linker anyway. + # Avoid breaking the webview build because it + # does not have <(android_ndk_root)/crazy_linker.gyp. + # Note that webview never uses the linker anyway. + ['android_webview_build == 0', { 'sources': [ 'android/linker/linker_jni.cc', ], diff --git a/base/base.gypi b/base/base.gypi index d5eb0ca286d0e..7f25947738142 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -55,6 +55,8 @@ 'android/jni_registrar.h', 'android/jni_string.cc', 'android/jni_string.h', + 'android/jni_utils.cc', + 'android/jni_utils.h', 'android/jni_weak_ref.cc', 'android/jni_weak_ref.h', 'android/library_loader/library_loader_hooks.cc', @@ -158,6 +160,8 @@ 'debug/stack_trace_android.cc', 'debug/stack_trace_posix.cc', 'debug/stack_trace_win.cc', + 'debug/task_annotator.cc', + 'debug/task_annotator.h', 'debug/trace_event.h', 'debug/trace_event_android.cc', 'debug/trace_event_argument.cc', @@ -176,13 +180,6 @@ 'environment.cc', 'environment.h', 'file_descriptor_posix.h', - 'file_util.cc', - 'file_util.h', - 'file_util_android.cc', - 'file_util_linux.cc', - 'file_util_mac.mm', - 'file_util_posix.cc', - 'file_util_win.cc', 'file_version_info.h', 'file_version_info_mac.h', 'file_version_info_mac.mm', @@ -213,8 +210,15 @@ 'files/file_posix.cc', 'files/file_proxy.cc', 'files/file_proxy.h', + 'files/file_util.cc', + 'files/file_util.h', + 'files/file_util_android.cc', + 'files/file_util_linux.cc', + 'files/file_util_mac.mm', + 'files/file_util_posix.cc', 'files/file_util_proxy.cc', 'files/file_util_proxy.h', + 'files/file_util_win.cc', 'files/file_win.cc', 'files/important_file_writer.h', 'files/important_file_writer.cc', @@ -236,8 +240,6 @@ 'hash.cc', 'hash.h', 'id_map.h', - 'ini_parser.cc', - 'ini_parser.h', 'ios/device_util.h', 'ios/device_util.mm', 'ios/ios_util.h', @@ -306,6 +308,8 @@ 'mac/scoped_nsexception_enabler.h', 'mac/scoped_nsexception_enabler.mm', 'mac/scoped_nsobject.h', + 'mac/scoped_objc_class_swizzler.h', + 'mac/scoped_objc_class_swizzler.mm', 'mac/scoped_sending_event.h', 'mac/scoped_sending_event.mm', 'mac/scoped_typeref.h', @@ -737,7 +741,7 @@ 'atomicops_internals_x86_gcc.cc', ], }], - ['<(use_glib)==0 or >(nacl_untrusted_build)==1', { + ['(<(use_glib)==0 and <(use_ozone)==0) or >(nacl_untrusted_build)==1', { 'sources!': [ 'message_loop/message_pump_glib.cc', ], @@ -756,14 +760,14 @@ 'base_paths.cc', 'cpu.cc', 'debug/stack_trace_posix.cc', - 'file_util.cc', - 'file_util_posix.cc', 'files/file_enumerator_posix.cc', 'files/file_path_watcher_fsevents.cc', 'files/file_path_watcher_fsevents.h', 'files/file_path_watcher_kqueue.cc', 'files/file_path_watcher_kqueue.h', 'files/file_proxy.cc', + 'files/file_util.cc', + 'files/file_util_posix.cc', 'files/file_util_proxy.cc', 'memory/shared_memory_posix.cc', 'native_library_posix.cc', @@ -839,7 +843,7 @@ # by file name rules). ['include', '^atomicops_internals_mac\\.'], ['include', '^base_paths_mac\\.'], - ['include', '^file_util_mac\\.'], + ['include', '^files/file_util_mac\\.'], ['include', '^file_version_info_mac\\.'], ['include', '^mac/bundle_locations\\.'], ['include', '^mac/foundation_util\\.'], @@ -914,11 +918,6 @@ 'strings/string16.cc', ], },], - ['<(use_ozone) == 1', { - 'sources!': [ - 'message_loop/message_pump_glib.cc', - ] - }], ['OS == "linux" and >(nacl_untrusted_build)==0', { 'sources!': [ 'files/file_path_watcher_fsevents.cc', @@ -943,7 +942,7 @@ 'sources/': [ ['exclude', '^files/file_path_watcher_linux\\.cc$'], ['exclude', '^files/file_path_watcher_stub\\.cc$'], - ['exclude', '^file_util_linux\\.cc$'], + ['exclude', '^files/file_util_linux\\.cc$'], ['exclude', '^process/process_linux\\.cc$'], ['exclude', '^sys_info_linux\\.cc$'], ], diff --git a/base/base_paths.cc b/base/base_paths.cc index 1b99e85075889..7076ec3be15fb 100644 --- a/base/base_paths.cc +++ b/base/base_paths.cc @@ -4,8 +4,8 @@ #include "base/base_paths.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/path_service.h" namespace base { diff --git a/base/base_paths_android.cc b/base/base_paths_android.cc index 27be24ea982cd..56c6cc70f474b 100644 --- a/base/base_paths_android.cc +++ b/base/base_paths_android.cc @@ -10,8 +10,8 @@ #include "base/android/jni_android.h" #include "base/android/path_utils.h" #include "base/base_paths.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/process/process_metrics.h" diff --git a/base/base_paths_mac.mm b/base/base_paths_mac.mm index 3930c44c3b721..9864eb3dce9d4 100644 --- a/base/base_paths_mac.mm +++ b/base/base_paths_mac.mm @@ -11,8 +11,8 @@ #include "base/base_paths.h" #include "base/compiler_specific.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/mac/foundation_util.h" #include "base/path_service.h" diff --git a/base/base_paths_posix.cc b/base/base_paths_posix.cc index cf136d47fa3df..048434f0ef75d 100644 --- a/base/base_paths_posix.cc +++ b/base/base_paths_posix.cc @@ -11,8 +11,8 @@ #include "base/base_paths.h" #include "base/environment.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/nix/xdg_util.h" diff --git a/base/base_paths_win.cc b/base/base_paths_win.cc index 509d5fd7ef173..a9b31c7b20852 100644 --- a/base/base_paths_win.cc +++ b/base/base_paths_win.cc @@ -16,38 +16,6 @@ extern "C" IMAGE_DOS_HEADER __ImageBase; using base::FilePath; -namespace { - -bool GetQuickLaunchPath(bool default_user, FilePath* result) { - if (default_user) { - wchar_t system_buffer[MAX_PATH]; - system_buffer[0] = 0; - // As per MSDN, passing -1 for |hToken| indicates the Default user: - // http://msdn.microsoft.com/library/windows/desktop/bb762181.aspx - if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, - reinterpret_cast(-1), SHGFP_TYPE_CURRENT, - system_buffer))) { - return false; - } - *result = FilePath(system_buffer); - } else if (!PathService::Get(base::DIR_APP_DATA, result)) { - // For the current user, grab the APPDATA directory directly from the - // PathService cache. - return false; - } - // According to various sources, appending - // "Microsoft\Internet Explorer\Quick Launch" to %appdata% is the only - // reliable way to get the quick launch folder across all versions of Windows. - // http://stackoverflow.com/questions/76080/how-do-you-reliably-get-the-quick- - // http://www.microsoft.com/technet/scriptcenter/resources/qanda/sept05/hey0901.mspx - *result = result->AppendASCII("Microsoft"); - *result = result->AppendASCII("Internet Explorer"); - *result = result->AppendASCII("Quick Launch"); - return true; -} - -} // namespace - namespace base { bool PathProviderWin(int key, FilePath* result) { @@ -178,12 +146,17 @@ bool PathProviderWin(int key, FilePath* result) { cur = FilePath(system_buffer); break; case base::DIR_USER_QUICK_LAUNCH: - if (!GetQuickLaunchPath(false, &cur)) - return false; - break; - case base::DIR_DEFAULT_USER_QUICK_LAUNCH: - if (!GetQuickLaunchPath(true, &cur)) - return false; + if (!PathService::Get(base::DIR_APP_DATA, &cur)) + return false; + // According to various sources, appending + // "Microsoft\Internet Explorer\Quick Launch" to %appdata% is the only + // reliable way to get the quick launch folder across all versions of + // Windows. + // http://stackoverflow.com/questions/76080/how-do-you-reliably-get-the-quick- + // http://www.microsoft.com/technet/scriptcenter/resources/qanda/sept05/hey0901.mspx + cur = cur.AppendASCII("Microsoft") + .AppendASCII("Internet Explorer") + .AppendASCII("Quick Launch"); break; case base::DIR_TASKBAR_PINS: if (!PathService::Get(base::DIR_USER_QUICK_LAUNCH, &cur)) diff --git a/base/base_paths_win.h b/base/base_paths_win.h index b042d08737989..46201710ff8d9 100644 --- a/base/base_paths_win.h +++ b/base/base_paths_win.h @@ -37,8 +37,6 @@ enum { DIR_COMMON_DESKTOP, // Directory for the common desktop (visible // on all user's Desktop). DIR_USER_QUICK_LAUNCH, // Directory for the quick launch shortcuts. - DIR_DEFAULT_USER_QUICK_LAUNCH, // Directory for the quick launch shortcuts - // of the Default user. DIR_TASKBAR_PINS, // Directory for the shortcuts pinned to taskbar via // base::win::TaskbarPinShortcutLink(). DIR_WINDOWS_FONTS, // Usually C:\Windows\Fonts. diff --git a/base/cpu.cc b/base/cpu.cc index dcba3b6e73e5a..ef3309dad180b 100644 --- a/base/cpu.cc +++ b/base/cpu.cc @@ -4,15 +4,17 @@ #include "base/cpu.h" +#include #include #include #include "base/basictypes.h" +#include "base/strings/string_piece.h" #include "build/build_config.h" #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #endif @@ -44,6 +46,7 @@ CPU::CPU() has_avx_hardware_(false), has_aesni_(false), has_non_stop_time_stamp_counter_(false), + has_broken_neon_(false), cpu_vendor_("unknown") { Initialize(); } @@ -90,52 +93,99 @@ uint64 _xgetbv(uint32 xcr) { #endif // ARCH_CPU_X86_FAMILY #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) +class LazyCpuInfoValue { + public: + LazyCpuInfoValue() : has_broken_neon_(false) { + // This function finds the value from /proc/cpuinfo under the key "model + // name" or "Processor". "model name" is used in Linux 3.8 and later (3.7 + // and later for arm64) and is shown once per CPU. "Processor" is used in + // earler versions and is shown only once at the top of /proc/cpuinfo + // regardless of the number CPUs. + const char kModelNamePrefix[] = "model name\t: "; + const char kProcessorPrefix[] = "Processor\t: "; + + // This function also calculates whether we believe that this CPU has a + // broken NEON unit based on these fields from cpuinfo: + unsigned implementer = 0, architecture = 0, variant = 0, part = 0, + revision = 0; + const struct { + const char key[17]; + unsigned *result; + } kUnsignedValues[] = { + {"CPU implementer", &implementer}, + {"CPU architecture", &architecture}, + {"CPU variant", &variant}, + {"CPU part", &part}, + {"CPU revision", &revision}, + }; + + std::string contents; + ReadFileToString(FilePath("/proc/cpuinfo"), &contents); + DCHECK(!contents.empty()); + if (contents.empty()) { + return; + } -// Returns the string found in /proc/cpuinfo under the key "model name" or -// "Processor". "model name" is used in Linux 3.8 and later (3.7 and later for -// arm64) and is shown once per CPU. "Processor" is used in earler versions and -// is shown only once at the top of /proc/cpuinfo regardless of the number CPUs. -std::string ParseCpuInfo() { - const char kModelNamePrefix[] = "model name\t: "; - const char kProcessorPrefix[] = "Processor\t: "; - std::string contents; - ReadFileToString(FilePath("/proc/cpuinfo"), &contents); - DCHECK(!contents.empty()); - std::string cpu_brand; - if (!contents.empty()) { std::istringstream iss(contents); std::string line; while (std::getline(iss, line)) { - if (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0) { - cpu_brand.assign(line.substr(strlen(kModelNamePrefix))); - break; + if (brand_.empty() && + (line.compare(0, strlen(kModelNamePrefix), kModelNamePrefix) == 0 || + line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0)) { + brand_.assign(line.substr(strlen(kModelNamePrefix))); } - if (line.compare(0, strlen(kProcessorPrefix), kProcessorPrefix) == 0) { - cpu_brand.assign(line.substr(strlen(kProcessorPrefix))); - break; + + for (size_t i = 0; i < arraysize(kUnsignedValues); i++) { + const char *key = kUnsignedValues[i].key; + const size_t len = strlen(key); + + if (line.compare(0, len, key) == 0 && + line.size() >= len + 1 && + (line[len] == '\t' || line[len] == ' ' || line[len] == ':')) { + size_t colon_pos = line.find(':', len); + if (colon_pos == std::string::npos) { + continue; + } + + const StringPiece line_sp(line); + StringPiece value_sp = line_sp.substr(colon_pos + 1); + while (!value_sp.empty() && + (value_sp[0] == ' ' || value_sp[0] == '\t')) { + value_sp = value_sp.substr(1); + } + + // The string may have leading "0x" or not, so we use strtoul to + // handle that. + char *endptr; + std::string value(value_sp.as_string()); + unsigned long int result = strtoul(value.c_str(), &endptr, 0); + if (*endptr == 0 && result <= UINT_MAX) { + *kUnsignedValues[i].result = result; + } + } } } + + has_broken_neon_ = + implementer == 0x51 && + architecture == 7 && + variant == 1 && + part == 0x4d && + revision == 0; } - return cpu_brand; -} -class LazyCpuInfoValue { - public: - LazyCpuInfoValue() : value_(ParseCpuInfo()) {} - const std::string& value() { return value_; } + const std::string& brand() const { return brand_; } + bool has_broken_neon() const { return has_broken_neon_; } private: - const std::string value_; + std::string brand_; + bool has_broken_neon_; DISALLOW_COPY_AND_ASSIGN(LazyCpuInfoValue); }; -base::LazyInstance g_lazy_cpu_brand = +base::LazyInstance::Leaky g_lazy_cpuinfo = LAZY_INSTANCE_INITIALIZER; -const std::string& CpuBrandInfo() { - return g_lazy_cpu_brand.Get().value(); -} - #endif // defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || // defined(OS_LINUX)) @@ -219,7 +269,8 @@ void CPU::Initialize() { has_non_stop_time_stamp_counter_ = (cpu_info[3] & (1 << 8)) != 0; } #elif defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) - cpu_brand_.assign(CpuBrandInfo()); + cpu_brand_.assign(g_lazy_cpuinfo.Get().brand()); + has_broken_neon_ = g_lazy_cpuinfo.Get().has_broken_neon(); #endif } diff --git a/base/cpu.h b/base/cpu.h index 595ed97b7ce2c..0c809f00c8b98 100644 --- a/base/cpu.h +++ b/base/cpu.h @@ -56,6 +56,11 @@ class BASE_EXPORT CPU { bool has_non_stop_time_stamp_counter() const { return has_non_stop_time_stamp_counter_; } + // has_broken_neon is only valid on ARM chips. If true, it indicates that we + // believe that the NEON unit on the current CPU is flawed and cannot execute + // some code. See https://code.google.com/p/chromium/issues/detail?id=341598 + bool has_broken_neon() const { return has_broken_neon_; } + IntelMicroArchitecture GetIntelMicroArchitecture() const; const std::string& cpu_brand() const { return cpu_brand_; } @@ -81,6 +86,7 @@ class BASE_EXPORT CPU { bool has_avx_hardware_; bool has_aesni_; bool has_non_stop_time_stamp_counter_; + bool has_broken_neon_; std::string cpu_vendor_; std::string cpu_brand_; }; diff --git a/base/debug/proc_maps_linux.cc b/base/debug/proc_maps_linux.cc index 1e0209e9bf768..4c1aedf003297 100644 --- a/base/debug/proc_maps_linux.cc +++ b/base/debug/proc_maps_linux.cc @@ -10,7 +10,7 @@ #include #endif -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/strings/string_split.h" diff --git a/base/debug/task_annotator.cc b/base/debug/task_annotator.cc new file mode 100644 index 0000000000000..c6576c829e83f --- /dev/null +++ b/base/debug/task_annotator.cc @@ -0,0 +1,74 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/task_annotator.h" + +#include "base/debug/alias.h" +#include "base/debug/trace_event.h" +#include "base/pending_task.h" +#include "base/tracked_objects.h" + +namespace base { +namespace debug { + +TaskAnnotator::TaskAnnotator() { +} + +TaskAnnotator::~TaskAnnotator() { +} + +void TaskAnnotator::DidQueueTask(const char* queue_function, + const PendingTask& pending_task) { + TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), + queue_function, + TRACE_ID_MANGLE(GetTaskTraceID(pending_task))); +} + +void TaskAnnotator::RunTask(const char* queue_function, + const char* run_function, + const PendingTask& pending_task) { + tracked_objects::TrackedTime start_time = + tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally); + tracked_objects::Duration queue_duration = + start_time - pending_task.EffectiveTimePosted(); + + TRACE_EVENT_FLOW_END1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), + queue_function, + TRACE_ID_MANGLE(GetTaskTraceID(pending_task)), + "queue_duration", + queue_duration.InMilliseconds()); + + // When tracing memory for posted tasks it's more valuable to attribute the + // memory allocations to the source function than generically to the task + // runner. + TRACE_EVENT_WITH_MEMORY_TAG2( + "toplevel", + run_function, + pending_task.posted_from.function_name(), // Name for memory tracking. + "src_file", + pending_task.posted_from.file_name(), + "src_func", + pending_task.posted_from.function_name()); + + // Before running the task, store the program counter where it was posted + // and deliberately alias it to ensure it is on the stack if the task + // crashes. Be careful not to assume that the variable itself will have the + // expected value when displayed by the optimizer in an optimized build. + // Look at a memory dump of the stack. + const void* program_counter = pending_task.posted_from.program_counter(); + debug::Alias(&program_counter); + + pending_task.task.Run(); + + tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking( + pending_task, start_time, tracked_objects::ThreadData::NowForEndOfRun()); +} + +uint64 TaskAnnotator::GetTaskTraceID(const PendingTask& task) const { + return (static_cast(task.sequence_num) << 32) | + ((static_cast(reinterpret_cast(this)) << 32) >> 32); +} + +} // namespace debug +} // namespace base diff --git a/base/debug/task_annotator.h b/base/debug/task_annotator.h new file mode 100644 index 0000000000000..aa5f17b11fee1 --- /dev/null +++ b/base/debug/task_annotator.h @@ -0,0 +1,46 @@ +// 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 BASE_DEBUG_TASK_ANNOTATOR_H_ +#define BASE_DEBUG_TASK_ANNOTATOR_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" + +namespace base { +struct PendingTask; +namespace debug { + +// Implements common debug annotations for posted tasks. This includes data +// such as task origins, queueing durations and memory usage. +class BASE_EXPORT TaskAnnotator { + public: + TaskAnnotator(); + ~TaskAnnotator(); + + // Called to indicate that a task has been queued to run in the future. + // |queue_function| is used as the trace flow event name. + void DidQueueTask(const char* queue_function, + const PendingTask& pending_task); + + // Run a previously queued task. |queue_function| should match what was + // passed into |DidQueueTask| for this task. |run_function| is used as the + // name for the trace event that surrounds the task's execution. + void RunTask(const char* queue_function, + const char* run_function, + const PendingTask& pending_task); + + private: + // Creates a process-wide unique ID to represent this task in trace events. + // This will be mangled with a Process ID hash to reduce the likelyhood of + // colliding with TaskAnnotator pointers on other processes. + uint64 GetTaskTraceID(const PendingTask& task) const; + + DISALLOW_COPY_AND_ASSIGN(TaskAnnotator); +}; + +} // namespace debug +} // namespace base + +#endif // BASE_DEBUG_TASK_ANNOTATOR_H_ diff --git a/base/debug/task_annotator_unittest.cc b/base/debug/task_annotator_unittest.cc new file mode 100644 index 0000000000000..ddffc21ab028c --- /dev/null +++ b/base/debug/task_annotator_unittest.cc @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/task_annotator.h" +#include "base/bind.h" +#include "base/pending_task.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +namespace debug { +namespace { + +void TestTask(int* result) { + *result = 123; +} + +} // namespace + +TEST(TaskAnnotatorTest, QueueAndRunTask) { + int result = 0; + PendingTask pending_task(FROM_HERE, Bind(&TestTask, &result)); + + TaskAnnotator annotator; + annotator.DidQueueTask("TaskAnnotatorTest::Queue", pending_task); + EXPECT_EQ(0, result); + annotator.RunTask( + "TaskAnnotatorTest::Queue", "TaskAnnotatorTest::Run", pending_task); + EXPECT_EQ(123, result); +} + +} // namespace debug +} // namespace base diff --git a/base/debug/trace_event_win_unittest.cc b/base/debug/trace_event_win_unittest.cc index a79e9a18f2460..378264511a2c1 100644 --- a/base/debug/trace_event_win_unittest.cc +++ b/base/debug/trace_event_win_unittest.cc @@ -8,9 +8,9 @@ #include "base/at_exit.h" #include "base/basictypes.h" -#include "base/file_util.h" #include "base/debug/trace_event.h" #include "base/debug/trace_event_win.h" +#include "base/files/file_util.h" #include "base/win/event_trace_consumer.h" #include "base/win/event_trace_controller.h" #include "base/win/event_trace_provider.h" diff --git a/base/event_recorder_win.cc b/base/event_recorder_win.cc index 39c4220897e2b..b3076a11318c5 100644 --- a/base/event_recorder_win.cc +++ b/base/event_recorder_win.cc @@ -7,7 +7,7 @@ #include #include "base/event_recorder.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" // A note about time. diff --git a/base/file_util.h b/base/file_util.h index ab1432d779146..9760a3499c095 100644 --- a/base/file_util.h +++ b/base/file_util.h @@ -1,439 +1,6 @@ -// Copyright (c) 2012 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. -// This file contains utility functions for dealing with the local -// filesystem. - -#ifndef BASE_FILE_UTIL_H_ -#define BASE_FILE_UTIL_H_ - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include -#elif defined(OS_POSIX) -#include -#include -#endif - -#include - -#include -#include -#include - -#include "base/base_export.h" -#include "base/basictypes.h" -#include "base/files/file.h" -#include "base/files/file_path.h" -#include "base/memory/scoped_ptr.h" -#include "base/strings/string16.h" - -#if defined(OS_POSIX) -#include "base/file_descriptor_posix.h" -#include "base/logging.h" -#include "base/posix/eintr_wrapper.h" -#endif - -namespace base { - -class Time; - -//----------------------------------------------------------------------------- -// Functions that involve filesystem access or modification: - -// Returns an absolute version of a relative path. Returns an empty path on -// error. On POSIX, this function fails if the path does not exist. This -// function can result in I/O so it can be slow. -BASE_EXPORT FilePath MakeAbsoluteFilePath(const FilePath& input); - -// Returns the total number of bytes used by all the files under |root_path|. -// If the path does not exist the function returns 0. -// -// This function is implemented using the FileEnumerator class so it is not -// particularly speedy in any platform. -BASE_EXPORT int64 ComputeDirectorySize(const FilePath& root_path); - -// Deletes the given path, whether it's a file or a directory. -// If it's a directory, it's perfectly happy to delete all of the -// directory's contents. Passing true to recursive deletes -// subdirectories and their contents as well. -// Returns true if successful, false otherwise. It is considered successful -// to attempt to delete a file that does not exist. -// -// In posix environment and if |path| is a symbolic link, this deletes only -// the symlink. (even if the symlink points to a non-existent file) -// -// WARNING: USING THIS WITH recursive==true IS EQUIVALENT -// TO "rm -rf", SO USE WITH CAUTION. -BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive); - -#if defined(OS_WIN) -// Schedules to delete the given path, whether it's a file or a directory, until -// the operating system is restarted. -// Note: -// 1) The file/directory to be deleted should exist in a temp folder. -// 2) The directory to be deleted must be empty. -BASE_EXPORT bool DeleteFileAfterReboot(const FilePath& path); -#endif - -// Moves the given path, whether it's a file or a directory. -// If a simple rename is not possible, such as in the case where the paths are -// on different volumes, this will attempt to copy and delete. Returns -// true for success. -// This function fails if either path contains traversal components ('..'). -BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path); - -// Renames file |from_path| to |to_path|. Both paths must be on the same -// volume, or the function will fail. Destination file will be created -// if it doesn't exist. Prefer this function over Move when dealing with -// temporary files. On Windows it preserves attributes of the target file. -// Returns true on success, leaving *error unchanged. -// Returns false on failure and sets *error appropriately, if it is non-NULL. -BASE_EXPORT bool ReplaceFile(const FilePath& from_path, - const FilePath& to_path, - File::Error* error); - -// Copies a single file. Use CopyDirectory to copy directories. -// This function fails if either path contains traversal components ('..'). -// -// This function keeps the metadata on Windows. The read only bit on Windows is -// not kept. -BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path); - -// Copies the given path, and optionally all subdirectories and their contents -// as well. -// -// If there are files existing under to_path, always overwrite. Returns true -// if successful, false otherwise. Wildcards on the names are not supported. -// -// This function calls into CopyFile() so the same behavior w.r.t. metadata -// applies. -// -// If you only need to copy a file use CopyFile, it's faster. -BASE_EXPORT bool CopyDirectory(const FilePath& from_path, - const FilePath& to_path, - bool recursive); - -// Returns true if the given path exists on the local filesystem, -// false otherwise. -BASE_EXPORT bool PathExists(const FilePath& path); - -// Returns true if the given path is writable by the user, false otherwise. -BASE_EXPORT bool PathIsWritable(const FilePath& path); - -// Returns true if the given path exists and is a directory, false otherwise. -BASE_EXPORT bool DirectoryExists(const FilePath& path); - -// Returns true if the contents of the two files given are equal, false -// otherwise. If either file can't be read, returns false. -BASE_EXPORT bool ContentsEqual(const FilePath& filename1, - const FilePath& filename2); - -// Returns true if the contents of the two text files given are equal, false -// otherwise. This routine treats "\r\n" and "\n" as equivalent. -BASE_EXPORT bool TextContentsEqual(const FilePath& filename1, - const FilePath& filename2); - -// Reads the file at |path| into |contents| and returns true on success and -// false on error. For security reasons, a |path| containing path traversal -// components ('..') is treated as a read error and |contents| is set to empty. -// In case of I/O error, |contents| holds the data that could be read from the -// file before the error occurred. -// |contents| may be NULL, in which case this function is useful for its side -// effect of priming the disk cache (could be used for unit tests). -BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents); - -// Reads the file at |path| into |contents| and returns true on success and -// false on error. For security reasons, a |path| containing path traversal -// components ('..') is treated as a read error and |contents| is set to empty. -// In case of I/O error, |contents| holds the data that could be read from the -// file before the error occurred. When the file size exceeds |max_size|, the -// function returns false with |contents| holding the file truncated to -// |max_size|. -// |contents| may be NULL, in which case this function is useful for its side -// effect of priming the disk cache (could be used for unit tests). -BASE_EXPORT bool ReadFileToString(const FilePath& path, - std::string* contents, - size_t max_size); - -#if defined(OS_POSIX) - -// Read exactly |bytes| bytes from file descriptor |fd|, storing the result -// in |buffer|. This function is protected against EINTR and partial reads. -// Returns true iff |bytes| bytes have been successfully read from |fd|. -BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes); - -// Creates a symbolic link at |symlink| pointing to |target|. Returns -// false on failure. -BASE_EXPORT bool CreateSymbolicLink(const FilePath& target, - const FilePath& symlink); - -// Reads the given |symlink| and returns where it points to in |target|. -// Returns false upon failure. -BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target); - -// Bits and masks of the file permission. -enum FilePermissionBits { - FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO, - FILE_PERMISSION_USER_MASK = S_IRWXU, - FILE_PERMISSION_GROUP_MASK = S_IRWXG, - FILE_PERMISSION_OTHERS_MASK = S_IRWXO, - - FILE_PERMISSION_READ_BY_USER = S_IRUSR, - FILE_PERMISSION_WRITE_BY_USER = S_IWUSR, - FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR, - FILE_PERMISSION_READ_BY_GROUP = S_IRGRP, - FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP, - FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP, - FILE_PERMISSION_READ_BY_OTHERS = S_IROTH, - FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH, - FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH, -}; - -// Reads the permission of the given |path|, storing the file permission -// bits in |mode|. If |path| is symbolic link, |mode| is the permission of -// a file which the symlink points to. -BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode); -// Sets the permission of the given |path|. If |path| is symbolic link, sets -// the permission of a file which the symlink points to. -BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode); - -#endif // OS_POSIX - -// Returns true if the given directory is empty -BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path); - -// Get the temporary directory provided by the system. -// -// WARNING: In general, you should use CreateTemporaryFile variants below -// instead of this function. Those variants will ensure that the proper -// permissions are set so that other users on the system can't edit them while -// they're open (which can lead to security issues). -BASE_EXPORT bool GetTempDir(FilePath* path); - -// Get the home directory. This is more complicated than just getenv("HOME") -// as it knows to fall back on getpwent() etc. -// -// You should not generally call this directly. Instead use DIR_HOME with the -// path service which will use this function but cache the value. -// Path service may also override DIR_HOME. -BASE_EXPORT FilePath GetHomeDir(); - -// Creates a temporary file. The full path is placed in |path|, and the -// function returns true if was successful in creating the file. The file will -// be empty and all handles closed after this function returns. -BASE_EXPORT bool CreateTemporaryFile(FilePath* path); - -// Same as CreateTemporaryFile but the file is created in |dir|. -BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir, - FilePath* temp_file); - -// Create and open a temporary file. File is opened for read/write. -// The full path is placed in |path|. -// Returns a handle to the opened file or NULL if an error occurred. -BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path); - -// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. -BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, - FilePath* path); - -// Create a new directory. If prefix is provided, the new directory name is in -// the format of prefixyyyy. -// NOTE: prefix is ignored in the POSIX implementation. -// If success, return true and output the full path of the directory created. -BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix, - FilePath* new_temp_path); - -// Create a directory within another directory. -// Extra characters will be appended to |prefix| to ensure that the -// new directory does not have the same name as an existing directory. -BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir, - const FilePath::StringType& prefix, - FilePath* new_dir); - -// Creates a directory, as well as creating any parent directories, if they -// don't exist. Returns 'true' on successful creation, or if the directory -// already exists. The directory is only readable by the current user. -// Returns true on success, leaving *error unchanged. -// Returns false on failure and sets *error appropriately, if it is non-NULL. -BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path, - File::Error* error); - -// Backward-compatible convenience method for the above. -BASE_EXPORT bool CreateDirectory(const FilePath& full_path); - -// Returns the file size. Returns true on success. -BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64* file_size); - -// Sets |real_path| to |path| with symbolic links and junctions expanded. -// On windows, make sure the path starts with a lettered drive. -// |path| must reference a file. Function will fail if |path| points to -// a directory or to a nonexistent path. On windows, this function will -// fail if |path| is a junction or symlink that points to an empty file, -// or if |real_path| would be longer than MAX_PATH characters. -BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path); - -#if defined(OS_WIN) - -// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."), -// return in |drive_letter_path| the equivalent path that starts with -// a drive letter ("C:\..."). Return false if no such path exists. -BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path, - FilePath* drive_letter_path); - -// Given an existing file in |path|, set |real_path| to the path -// in native NT format, of the form "\Device\HarddiskVolumeXX\..". -// Returns false if the path can not be found. Empty files cannot -// be resolved with this function. -BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path, - FilePath* nt_path); -#endif - -// This function will return if the given file is a symlink or not. -BASE_EXPORT bool IsLink(const FilePath& file_path); - -// Returns information about the given file path. -BASE_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info); - -// Sets the time of the last access and the time of the last modification. -BASE_EXPORT bool TouchFile(const FilePath& path, - const Time& last_accessed, - const Time& last_modified); - -// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. -BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode); - -// Closes file opened by OpenFile. Returns true on success. -BASE_EXPORT bool CloseFile(FILE* file); - -// Associates a standard FILE stream with an existing File. Note that this -// functions take ownership of the existing File. -BASE_EXPORT FILE* FileToFILE(File file, const char* mode); - -// Truncates an open file to end at the location of the current file pointer. -// This is a cross-platform analog to Windows' SetEndOfFile() function. -BASE_EXPORT bool TruncateFile(FILE* file); - -// Reads at most the given number of bytes from the file into the buffer. -// Returns the number of read bytes, or -1 on error. -BASE_EXPORT int ReadFile(const FilePath& filename, char* data, int max_size); - -// Writes the given buffer into the file, overwriting any data that was -// previously there. Returns the number of bytes written, or -1 on error. -BASE_EXPORT int WriteFile(const FilePath& filename, const char* data, - int size); - -#if defined(OS_POSIX) -// Append the data to |fd|. Does not close |fd| when done. -BASE_EXPORT int WriteFileDescriptor(const int fd, const char* data, int size); -#endif - -// Append the given buffer into the file. Returns the number of bytes written, -// or -1 on error. -BASE_EXPORT int AppendToFile(const FilePath& filename, - const char* data, int size); - -// Gets the current working directory for the process. -BASE_EXPORT bool GetCurrentDirectory(FilePath* path); - -// Sets the current working directory for the process. -BASE_EXPORT bool SetCurrentDirectory(const FilePath& path); - -// Attempts to find a number that can be appended to the |path| to make it -// unique. If |path| does not exist, 0 is returned. If it fails to find such -// a number, -1 is returned. If |suffix| is not empty, also checks the -// existence of it with the given suffix. -BASE_EXPORT int GetUniquePathNumber(const FilePath& path, - const FilePath::StringType& suffix); - -#if defined(OS_POSIX) -// Test that |path| can only be changed by a given user and members of -// a given set of groups. -// Specifically, test that all parts of |path| under (and including) |base|: -// * Exist. -// * Are owned by a specific user. -// * Are not writable by all users. -// * Are owned by a member of a given set of groups, or are not writable by -// their group. -// * Are not symbolic links. -// This is useful for checking that a config file is administrator-controlled. -// |base| must contain |path|. -BASE_EXPORT bool VerifyPathControlledByUser(const base::FilePath& base, - const base::FilePath& path, - uid_t owner_uid, - const std::set& group_gids); -#endif // defined(OS_POSIX) - -#if defined(OS_MACOSX) && !defined(OS_IOS) -// Is |path| writable only by a user with administrator privileges? -// This function uses Mac OS conventions. The super user is assumed to have -// uid 0, and the administrator group is assumed to be named "admin". -// Testing that |path|, and every parent directory including the root of -// the filesystem, are owned by the superuser, controlled by the group -// "admin", are not writable by all users, and contain no symbolic links. -// Will return false if |path| does not exist. -BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path); -#endif // defined(OS_MACOSX) && !defined(OS_IOS) - -// Returns the maximum length of path component on the volume containing -// the directory |path|, in the number of FilePath::CharType, or -1 on failure. -BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path); - -#if defined(OS_LINUX) -// Broad categories of file systems as returned by statfs() on Linux. -enum FileSystemType { - FILE_SYSTEM_UNKNOWN, // statfs failed. - FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS. - FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2 - FILE_SYSTEM_NFS, - FILE_SYSTEM_SMB, - FILE_SYSTEM_CODA, - FILE_SYSTEM_MEMORY, // in-memory file system - FILE_SYSTEM_CGROUP, // cgroup control. - FILE_SYSTEM_OTHER, // any other value. - FILE_SYSTEM_TYPE_COUNT -}; - -// Attempts determine the FileSystemType for |path|. -// Returns false if |path| doesn't exist. -BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type); -#endif - -#if defined(OS_POSIX) -// Get a temporary directory for shared memory files. The directory may depend -// on whether the destination is intended for executable files, which in turn -// depends on how /dev/shmem was mounted. As a result, you must supply whether -// you intend to create executable shmem segments so this function can find -// an appropriate location. -BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path); -#endif - -// Internal -------------------------------------------------------------------- - -namespace internal { - -// Same as Move but allows paths with traversal components. -// Use only with extreme care. -BASE_EXPORT bool MoveUnsafe(const FilePath& from_path, - const FilePath& to_path); - -// Same as CopyFile but allows paths with traversal components. -// Use only with extreme care. -BASE_EXPORT bool CopyFileUnsafe(const FilePath& from_path, - const FilePath& to_path); - -#if defined(OS_WIN) -// Copy from_path to to_path recursively and then delete from_path recursively. -// Returns true if all operations succeed. -// This function simulates Move(), but unlike Move() it works across volumes. -// This function is not transactional. -BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path, - const FilePath& to_path); -#endif // defined(OS_WIN) - -} // namespace internal -} // namespace base - -#endif // BASE_FILE_UTIL_H_ +// TODO(brettw) update callers to use the new location and remove this file. +#include "base/files/file_util.h" diff --git a/base/files/file_enumerator.cc b/base/files/file_enumerator.cc index e49f465f67340..974998036d3ae 100644 --- a/base/files/file_enumerator.cc +++ b/base/files/file_enumerator.cc @@ -4,7 +4,7 @@ #include "base/files/file_enumerator.h" -#include "base/file_util.h" +#include "base/files/file_util.h" namespace base { diff --git a/base/files/file_path_watcher_browsertest.cc b/base/files/file_path_watcher_browsertest.cc index ff6d9d597317b..5cf75fc35c953 100644 --- a/base/files/file_path_watcher_browsertest.cc +++ b/base/files/file_path_watcher_browsertest.cc @@ -17,8 +17,8 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/compiler_specific.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#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" diff --git a/base/files/file_path_watcher_fsevents.cc b/base/files/file_path_watcher_fsevents.cc index edf4d23946580..f658efe742b43 100644 --- a/base/files/file_path_watcher_fsevents.cc +++ b/base/files/file_path_watcher_fsevents.cc @@ -7,7 +7,7 @@ #include #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/mac/libdispatch_task_runner.h" diff --git a/base/files/file_path_watcher_kqueue.cc b/base/files/file_path_watcher_kqueue.cc index c38e3448e732b..8941d2e419f9a 100644 --- a/base/files/file_path_watcher_kqueue.cc +++ b/base/files/file_path_watcher_kqueue.cc @@ -8,7 +8,7 @@ #include #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/stringprintf.h" diff --git a/base/files/file_path_watcher_linux.cc b/base/files/file_path_watcher_linux.cc index 915ad50abb26d..26eafb379c079 100644 --- a/base/files/file_path_watcher_linux.cc +++ b/base/files/file_path_watcher_linux.cc @@ -20,9 +20,9 @@ #include "base/bind.h" #include "base/containers/hash_tables.h" #include "base/debug/trace_event.h" -#include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/location.h" #include "base/logging.h" diff --git a/base/files/file_path_watcher_win.cc b/base/files/file_path_watcher_win.cc index ec2fe6d22620c..a30214045c24e 100644 --- a/base/files/file_path_watcher_win.cc +++ b/base/files/file_path_watcher_win.cc @@ -5,9 +5,9 @@ #include "base/files/file_path_watcher.h" #include "base/bind.h" -#include "base/file_util.h" #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop_proxy.h" diff --git a/base/files/file_proxy.cc b/base/files/file_proxy.cc index 291b98d1117fb..53b14fef2fd6c 100644 --- a/base/files/file_proxy.cc +++ b/base/files/file_proxy.cc @@ -6,8 +6,8 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/file_util.h" #include "base/files/file.h" +#include "base/files/file_util.h" #include "base/location.h" #include "base/message_loop/message_loop_proxy.h" #include "base/task_runner.h" diff --git a/base/files/file_proxy_unittest.cc b/base/files/file_proxy_unittest.cc index 9be8abf67d291..2c62fa966030d 100644 --- a/base/files/file_proxy_unittest.cc +++ b/base/files/file_proxy_unittest.cc @@ -5,8 +5,8 @@ #include "base/files/file_proxy.h" #include "base/bind.h" -#include "base/file_util.h" #include "base/files/file.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" diff --git a/base/files/file_unittest.cc b/base/files/file_unittest.cc index de88145ac38f1..6616f6af47eaa 100644 --- a/base/files/file_unittest.cc +++ b/base/files/file_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/file_util.h" #include "base/files/file.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/base/file_util.cc b/base/files/file_util.cc similarity index 99% rename from base/file_util.cc rename to base/files/file_util.cc index d11cd15a2e003..96a7164720d1e 100644 --- a/base/file_util.cc +++ b/base/files/file_util.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 "base/file_util.h" +#include "base/files/file_util.h" #if defined(OS_WIN) #include diff --git a/base/files/file_util.h b/base/files/file_util.h new file mode 100644 index 0000000000000..feebeed919697 --- /dev/null +++ b/base/files/file_util.h @@ -0,0 +1,439 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions for dealing with the local +// filesystem. + +#ifndef BASE_FILES_FILE_UTIL_H_ +#define BASE_FILES_FILE_UTIL_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#include +#endif + +#include + +#include +#include +#include + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/files/file.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string16.h" + +#if defined(OS_POSIX) +#include "base/file_descriptor_posix.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#endif + +namespace base { + +class Time; + +//----------------------------------------------------------------------------- +// Functions that involve filesystem access or modification: + +// Returns an absolute version of a relative path. Returns an empty path on +// error. On POSIX, this function fails if the path does not exist. This +// function can result in I/O so it can be slow. +BASE_EXPORT FilePath MakeAbsoluteFilePath(const FilePath& input); + +// Returns the total number of bytes used by all the files under |root_path|. +// If the path does not exist the function returns 0. +// +// This function is implemented using the FileEnumerator class so it is not +// particularly speedy in any platform. +BASE_EXPORT int64 ComputeDirectorySize(const FilePath& root_path); + +// Deletes the given path, whether it's a file or a directory. +// If it's a directory, it's perfectly happy to delete all of the +// directory's contents. Passing true to recursive deletes +// subdirectories and their contents as well. +// Returns true if successful, false otherwise. It is considered successful +// to attempt to delete a file that does not exist. +// +// In posix environment and if |path| is a symbolic link, this deletes only +// the symlink. (even if the symlink points to a non-existent file) +// +// WARNING: USING THIS WITH recursive==true IS EQUIVALENT +// TO "rm -rf", SO USE WITH CAUTION. +BASE_EXPORT bool DeleteFile(const FilePath& path, bool recursive); + +#if defined(OS_WIN) +// Schedules to delete the given path, whether it's a file or a directory, until +// the operating system is restarted. +// Note: +// 1) The file/directory to be deleted should exist in a temp folder. +// 2) The directory to be deleted must be empty. +BASE_EXPORT bool DeleteFileAfterReboot(const FilePath& path); +#endif + +// Moves the given path, whether it's a file or a directory. +// If a simple rename is not possible, such as in the case where the paths are +// on different volumes, this will attempt to copy and delete. Returns +// true for success. +// This function fails if either path contains traversal components ('..'). +BASE_EXPORT bool Move(const FilePath& from_path, const FilePath& to_path); + +// Renames file |from_path| to |to_path|. Both paths must be on the same +// volume, or the function will fail. Destination file will be created +// if it doesn't exist. Prefer this function over Move when dealing with +// temporary files. On Windows it preserves attributes of the target file. +// Returns true on success, leaving *error unchanged. +// Returns false on failure and sets *error appropriately, if it is non-NULL. +BASE_EXPORT bool ReplaceFile(const FilePath& from_path, + const FilePath& to_path, + File::Error* error); + +// Copies a single file. Use CopyDirectory to copy directories. +// This function fails if either path contains traversal components ('..'). +// +// This function keeps the metadata on Windows. The read only bit on Windows is +// not kept. +BASE_EXPORT bool CopyFile(const FilePath& from_path, const FilePath& to_path); + +// Copies the given path, and optionally all subdirectories and their contents +// as well. +// +// If there are files existing under to_path, always overwrite. Returns true +// if successful, false otherwise. Wildcards on the names are not supported. +// +// This function calls into CopyFile() so the same behavior w.r.t. metadata +// applies. +// +// If you only need to copy a file use CopyFile, it's faster. +BASE_EXPORT bool CopyDirectory(const FilePath& from_path, + const FilePath& to_path, + bool recursive); + +// Returns true if the given path exists on the local filesystem, +// false otherwise. +BASE_EXPORT bool PathExists(const FilePath& path); + +// Returns true if the given path is writable by the user, false otherwise. +BASE_EXPORT bool PathIsWritable(const FilePath& path); + +// Returns true if the given path exists and is a directory, false otherwise. +BASE_EXPORT bool DirectoryExists(const FilePath& path); + +// Returns true if the contents of the two files given are equal, false +// otherwise. If either file can't be read, returns false. +BASE_EXPORT bool ContentsEqual(const FilePath& filename1, + const FilePath& filename2); + +// Returns true if the contents of the two text files given are equal, false +// otherwise. This routine treats "\r\n" and "\n" as equivalent. +BASE_EXPORT bool TextContentsEqual(const FilePath& filename1, + const FilePath& filename2); + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +BASE_EXPORT bool ReadFileToString(const FilePath& path, std::string* contents); + +// Reads the file at |path| into |contents| and returns true on success and +// false on error. For security reasons, a |path| containing path traversal +// components ('..') is treated as a read error and |contents| is set to empty. +// In case of I/O error, |contents| holds the data that could be read from the +// file before the error occurred. When the file size exceeds |max_size|, the +// function returns false with |contents| holding the file truncated to +// |max_size|. +// |contents| may be NULL, in which case this function is useful for its side +// effect of priming the disk cache (could be used for unit tests). +BASE_EXPORT bool ReadFileToString(const FilePath& path, + std::string* contents, + size_t max_size); + +#if defined(OS_POSIX) + +// Read exactly |bytes| bytes from file descriptor |fd|, storing the result +// in |buffer|. This function is protected against EINTR and partial reads. +// Returns true iff |bytes| bytes have been successfully read from |fd|. +BASE_EXPORT bool ReadFromFD(int fd, char* buffer, size_t bytes); + +// Creates a symbolic link at |symlink| pointing to |target|. Returns +// false on failure. +BASE_EXPORT bool CreateSymbolicLink(const FilePath& target, + const FilePath& symlink); + +// Reads the given |symlink| and returns where it points to in |target|. +// Returns false upon failure. +BASE_EXPORT bool ReadSymbolicLink(const FilePath& symlink, FilePath* target); + +// Bits and masks of the file permission. +enum FilePermissionBits { + FILE_PERMISSION_MASK = S_IRWXU | S_IRWXG | S_IRWXO, + FILE_PERMISSION_USER_MASK = S_IRWXU, + FILE_PERMISSION_GROUP_MASK = S_IRWXG, + FILE_PERMISSION_OTHERS_MASK = S_IRWXO, + + FILE_PERMISSION_READ_BY_USER = S_IRUSR, + FILE_PERMISSION_WRITE_BY_USER = S_IWUSR, + FILE_PERMISSION_EXECUTE_BY_USER = S_IXUSR, + FILE_PERMISSION_READ_BY_GROUP = S_IRGRP, + FILE_PERMISSION_WRITE_BY_GROUP = S_IWGRP, + FILE_PERMISSION_EXECUTE_BY_GROUP = S_IXGRP, + FILE_PERMISSION_READ_BY_OTHERS = S_IROTH, + FILE_PERMISSION_WRITE_BY_OTHERS = S_IWOTH, + FILE_PERMISSION_EXECUTE_BY_OTHERS = S_IXOTH, +}; + +// Reads the permission of the given |path|, storing the file permission +// bits in |mode|. If |path| is symbolic link, |mode| is the permission of +// a file which the symlink points to. +BASE_EXPORT bool GetPosixFilePermissions(const FilePath& path, int* mode); +// Sets the permission of the given |path|. If |path| is symbolic link, sets +// the permission of a file which the symlink points to. +BASE_EXPORT bool SetPosixFilePermissions(const FilePath& path, int mode); + +#endif // OS_POSIX + +// Returns true if the given directory is empty +BASE_EXPORT bool IsDirectoryEmpty(const FilePath& dir_path); + +// Get the temporary directory provided by the system. +// +// WARNING: In general, you should use CreateTemporaryFile variants below +// instead of this function. Those variants will ensure that the proper +// permissions are set so that other users on the system can't edit them while +// they're open (which can lead to security issues). +BASE_EXPORT bool GetTempDir(FilePath* path); + +// Get the home directory. This is more complicated than just getenv("HOME") +// as it knows to fall back on getpwent() etc. +// +// You should not generally call this directly. Instead use DIR_HOME with the +// path service which will use this function but cache the value. +// Path service may also override DIR_HOME. +BASE_EXPORT FilePath GetHomeDir(); + +// Creates a temporary file. The full path is placed in |path|, and the +// function returns true if was successful in creating the file. The file will +// be empty and all handles closed after this function returns. +BASE_EXPORT bool CreateTemporaryFile(FilePath* path); + +// Same as CreateTemporaryFile but the file is created in |dir|. +BASE_EXPORT bool CreateTemporaryFileInDir(const FilePath& dir, + FilePath* temp_file); + +// Create and open a temporary file. File is opened for read/write. +// The full path is placed in |path|. +// Returns a handle to the opened file or NULL if an error occurred. +BASE_EXPORT FILE* CreateAndOpenTemporaryFile(FilePath* path); + +// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. +BASE_EXPORT FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, + FilePath* path); + +// Create a new directory. If prefix is provided, the new directory name is in +// the format of prefixyyyy. +// NOTE: prefix is ignored in the POSIX implementation. +// If success, return true and output the full path of the directory created. +BASE_EXPORT bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path); + +// Create a directory within another directory. +// Extra characters will be appended to |prefix| to ensure that the +// new directory does not have the same name as an existing directory. +BASE_EXPORT bool CreateTemporaryDirInDir(const FilePath& base_dir, + const FilePath::StringType& prefix, + FilePath* new_dir); + +// Creates a directory, as well as creating any parent directories, if they +// don't exist. Returns 'true' on successful creation, or if the directory +// already exists. The directory is only readable by the current user. +// Returns true on success, leaving *error unchanged. +// Returns false on failure and sets *error appropriately, if it is non-NULL. +BASE_EXPORT bool CreateDirectoryAndGetError(const FilePath& full_path, + File::Error* error); + +// Backward-compatible convenience method for the above. +BASE_EXPORT bool CreateDirectory(const FilePath& full_path); + +// Returns the file size. Returns true on success. +BASE_EXPORT bool GetFileSize(const FilePath& file_path, int64* file_size); + +// Sets |real_path| to |path| with symbolic links and junctions expanded. +// On windows, make sure the path starts with a lettered drive. +// |path| must reference a file. Function will fail if |path| points to +// a directory or to a nonexistent path. On windows, this function will +// fail if |path| is a junction or symlink that points to an empty file, +// or if |real_path| would be longer than MAX_PATH characters. +BASE_EXPORT bool NormalizeFilePath(const FilePath& path, FilePath* real_path); + +#if defined(OS_WIN) + +// Given a path in NT native form ("\Device\HarddiskVolumeXX\..."), +// return in |drive_letter_path| the equivalent path that starts with +// a drive letter ("C:\..."). Return false if no such path exists. +BASE_EXPORT bool DevicePathToDriveLetterPath(const FilePath& device_path, + FilePath* drive_letter_path); + +// Given an existing file in |path|, set |real_path| to the path +// in native NT format, of the form "\Device\HarddiskVolumeXX\..". +// Returns false if the path can not be found. Empty files cannot +// be resolved with this function. +BASE_EXPORT bool NormalizeToNativeFilePath(const FilePath& path, + FilePath* nt_path); +#endif + +// This function will return if the given file is a symlink or not. +BASE_EXPORT bool IsLink(const FilePath& file_path); + +// Returns information about the given file path. +BASE_EXPORT bool GetFileInfo(const FilePath& file_path, File::Info* info); + +// Sets the time of the last access and the time of the last modification. +BASE_EXPORT bool TouchFile(const FilePath& path, + const Time& last_accessed, + const Time& last_modified); + +// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. +BASE_EXPORT FILE* OpenFile(const FilePath& filename, const char* mode); + +// Closes file opened by OpenFile. Returns true on success. +BASE_EXPORT bool CloseFile(FILE* file); + +// Associates a standard FILE stream with an existing File. Note that this +// functions take ownership of the existing File. +BASE_EXPORT FILE* FileToFILE(File file, const char* mode); + +// Truncates an open file to end at the location of the current file pointer. +// This is a cross-platform analog to Windows' SetEndOfFile() function. +BASE_EXPORT bool TruncateFile(FILE* file); + +// Reads at most the given number of bytes from the file into the buffer. +// Returns the number of read bytes, or -1 on error. +BASE_EXPORT int ReadFile(const FilePath& filename, char* data, int max_size); + +// Writes the given buffer into the file, overwriting any data that was +// previously there. Returns the number of bytes written, or -1 on error. +BASE_EXPORT int WriteFile(const FilePath& filename, const char* data, + int size); + +#if defined(OS_POSIX) +// Append the data to |fd|. Does not close |fd| when done. +BASE_EXPORT int WriteFileDescriptor(const int fd, const char* data, int size); +#endif + +// Append the given buffer into the file. Returns the number of bytes written, +// or -1 on error. +BASE_EXPORT int AppendToFile(const FilePath& filename, + const char* data, int size); + +// Gets the current working directory for the process. +BASE_EXPORT bool GetCurrentDirectory(FilePath* path); + +// Sets the current working directory for the process. +BASE_EXPORT bool SetCurrentDirectory(const FilePath& path); + +// Attempts to find a number that can be appended to the |path| to make it +// unique. If |path| does not exist, 0 is returned. If it fails to find such +// a number, -1 is returned. If |suffix| is not empty, also checks the +// existence of it with the given suffix. +BASE_EXPORT int GetUniquePathNumber(const FilePath& path, + const FilePath::StringType& suffix); + +#if defined(OS_POSIX) +// Test that |path| can only be changed by a given user and members of +// a given set of groups. +// Specifically, test that all parts of |path| under (and including) |base|: +// * Exist. +// * Are owned by a specific user. +// * Are not writable by all users. +// * Are owned by a member of a given set of groups, or are not writable by +// their group. +// * Are not symbolic links. +// This is useful for checking that a config file is administrator-controlled. +// |base| must contain |path|. +BASE_EXPORT bool VerifyPathControlledByUser(const base::FilePath& base, + const base::FilePath& path, + uid_t owner_uid, + const std::set& group_gids); +#endif // defined(OS_POSIX) + +#if defined(OS_MACOSX) && !defined(OS_IOS) +// Is |path| writable only by a user with administrator privileges? +// This function uses Mac OS conventions. The super user is assumed to have +// uid 0, and the administrator group is assumed to be named "admin". +// Testing that |path|, and every parent directory including the root of +// the filesystem, are owned by the superuser, controlled by the group +// "admin", are not writable by all users, and contain no symbolic links. +// Will return false if |path| does not exist. +BASE_EXPORT bool VerifyPathControlledByAdmin(const base::FilePath& path); +#endif // defined(OS_MACOSX) && !defined(OS_IOS) + +// Returns the maximum length of path component on the volume containing +// the directory |path|, in the number of FilePath::CharType, or -1 on failure. +BASE_EXPORT int GetMaximumPathComponentLength(const base::FilePath& path); + +#if defined(OS_LINUX) +// Broad categories of file systems as returned by statfs() on Linux. +enum FileSystemType { + FILE_SYSTEM_UNKNOWN, // statfs failed. + FILE_SYSTEM_0, // statfs.f_type == 0 means unknown, may indicate AFS. + FILE_SYSTEM_ORDINARY, // on-disk filesystem like ext2 + FILE_SYSTEM_NFS, + FILE_SYSTEM_SMB, + FILE_SYSTEM_CODA, + FILE_SYSTEM_MEMORY, // in-memory file system + FILE_SYSTEM_CGROUP, // cgroup control. + FILE_SYSTEM_OTHER, // any other value. + FILE_SYSTEM_TYPE_COUNT +}; + +// Attempts determine the FileSystemType for |path|. +// Returns false if |path| doesn't exist. +BASE_EXPORT bool GetFileSystemType(const FilePath& path, FileSystemType* type); +#endif + +#if defined(OS_POSIX) +// Get a temporary directory for shared memory files. The directory may depend +// on whether the destination is intended for executable files, which in turn +// depends on how /dev/shmem was mounted. As a result, you must supply whether +// you intend to create executable shmem segments so this function can find +// an appropriate location. +BASE_EXPORT bool GetShmemTempDir(bool executable, FilePath* path); +#endif + +// Internal -------------------------------------------------------------------- + +namespace internal { + +// Same as Move but allows paths with traversal components. +// Use only with extreme care. +BASE_EXPORT bool MoveUnsafe(const FilePath& from_path, + const FilePath& to_path); + +// Same as CopyFile but allows paths with traversal components. +// Use only with extreme care. +BASE_EXPORT bool CopyFileUnsafe(const FilePath& from_path, + const FilePath& to_path); + +#if defined(OS_WIN) +// Copy from_path to to_path recursively and then delete from_path recursively. +// Returns true if all operations succeed. +// This function simulates Move(), but unlike Move() it works across volumes. +// This function is not transactional. +BASE_EXPORT bool CopyAndDeleteDirectory(const FilePath& from_path, + const FilePath& to_path); +#endif // defined(OS_WIN) + +} // namespace internal +} // namespace base + +#endif // BASE_FILES_FILE_UTIL_H_ diff --git a/base/file_util_android.cc b/base/files/file_util_android.cc similarity index 92% rename from base/file_util_android.cc rename to base/files/file_util_android.cc index def4d7cab96d9..b8b3b3720fb62 100644 --- a/base/file_util_android.cc +++ b/base/files/file_util_android.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 "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/file_path.h" #include "base/path_service.h" diff --git a/base/file_util_linux.cc b/base/files/file_util_linux.cc similarity index 98% rename from base/file_util_linux.cc rename to base/files/file_util_linux.cc index 2910c9cb4ec73..532962f195c67 100644 --- a/base/file_util_linux.cc +++ b/base/files/file_util_linux.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 "base/file_util.h" +#include "base/files/file_util.h" #include #include diff --git a/base/file_util_mac.mm b/base/files/file_util_mac.mm similarity index 97% rename from base/file_util_mac.mm rename to base/files/file_util_mac.mm index 4aa6d552a76a8..695935a2e31fe 100644 --- a/base/file_util_mac.mm +++ b/base/files/file_util_mac.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/file_util.h" +#include "base/files/file_util.h" #import #include diff --git a/base/file_util_posix.cc b/base/files/file_util_posix.cc similarity index 99% rename from base/file_util_posix.cc rename to base/files/file_util_posix.cc index 92e8cad160543..07c21d1430f94 100644 --- a/base/file_util_posix.cc +++ b/base/files/file_util_posix.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 "base/file_util.h" +#include "base/files/file_util.h" #include #include diff --git a/base/files/file_util_proxy.cc b/base/files/file_util_proxy.cc index 72d9436fa1f6d..0942e7a276102 100644 --- a/base/files/file_util_proxy.cc +++ b/base/files/file_util_proxy.cc @@ -6,7 +6,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/location.h" #include "base/task_runner.h" #include "base/task_runner_util.h" diff --git a/base/files/file_util_proxy_unittest.cc b/base/files/file_util_proxy_unittest.cc index 52073ae3b50b7..a18cd4357d375 100644 --- a/base/files/file_util_proxy_unittest.cc +++ b/base/files/file_util_proxy_unittest.cc @@ -5,7 +5,7 @@ #include "base/files/file_util_proxy.h" #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" diff --git a/base/file_util_unittest.cc b/base/files/file_util_unittest.cc similarity index 99% rename from base/file_util_unittest.cc rename to base/files/file_util_unittest.cc index eb88ba83459d2..e085aefa12189 100644 --- a/base/file_util_unittest.cc +++ b/base/files/file_util_unittest.cc @@ -24,9 +24,9 @@ #include #include "base/base_paths.h" -#include "base/file_util.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" #include "base/path_service.h" @@ -52,8 +52,8 @@ namespace base { namespace { -// To test that file_util::Normalize FilePath() deals with NTFS reparse points -// correctly, we need functions to create and delete reparse points. +// To test that NormalizeFilePath() deals with NTFS reparse points correctly, +// we need functions to create and delete reparse points. #if defined(OS_WIN) typedef struct _REPARSE_DATA_BUFFER { ULONG ReparseTag; diff --git a/base/file_util_win.cc b/base/files/file_util_win.cc similarity index 99% rename from base/file_util_win.cc rename to base/files/file_util_win.cc index e3cd1f83ac29f..82b53c5fd85ad 100644 --- a/base/file_util_win.cc +++ b/base/files/file_util_win.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 "base/file_util.h" +#include "base/files/file_util.h" #include #include diff --git a/base/files/important_file_writer.cc b/base/files/important_file_writer.cc index bf4e0033e3a9b..6e3be2aec6ce2 100644 --- a/base/files/important_file_writer.cc +++ b/base/files/important_file_writer.cc @@ -10,9 +10,9 @@ #include "base/bind.h" #include "base/critical_closure.h" -#include "base/file_util.h" #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" diff --git a/base/files/important_file_writer_unittest.cc b/base/files/important_file_writer_unittest.cc index 3f62fe4953b8f..c55f0cc037f2e 100644 --- a/base/files/important_file_writer_unittest.cc +++ b/base/files/important_file_writer_unittest.cc @@ -6,8 +6,8 @@ #include "base/bind.h" #include "base/compiler_specific.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" diff --git a/base/files/memory_mapped_file_unittest.cc b/base/files/memory_mapped_file_unittest.cc index 21b0df4034e1a..6627d4044287d 100644 --- a/base/files/memory_mapped_file_unittest.cc +++ b/base/files/memory_mapped_file_unittest.cc @@ -4,8 +4,8 @@ #include "base/files/memory_mapped_file.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" diff --git a/base/files/scoped_temp_dir.cc b/base/files/scoped_temp_dir.cc index b893b02ca8ad9..27b758ed9038e 100644 --- a/base/files/scoped_temp_dir.cc +++ b/base/files/scoped_temp_dir.cc @@ -4,7 +4,7 @@ #include "base/files/scoped_temp_dir.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" namespace base { diff --git a/base/files/scoped_temp_dir_unittest.cc b/base/files/scoped_temp_dir_unittest.cc index da222304a09b1..a19f34ddce056 100644 --- a/base/files/scoped_temp_dir_unittest.cc +++ b/base/files/scoped_temp_dir_unittest.cc @@ -4,8 +4,8 @@ #include -#include "base/file_util.h" #include "base/files/file.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/base/i18n/build_utf8_validator_tables.cc b/base/i18n/build_utf8_validator_tables.cc index d37a75172a701..45fc6cbf92b9c 100644 --- a/base/i18n/build_utf8_validator_tables.cc +++ b/base/i18n/build_utf8_validator_tables.cc @@ -37,8 +37,8 @@ #include "base/basictypes.h" #include "base/command_line.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/numerics/safe_conversions.h" #include "base/strings/stringprintf.h" diff --git a/base/i18n/file_util_icu_unittest.cc b/base/i18n/file_util_icu_unittest.cc index cdb48a12ab973..369345b6add77 100644 --- a/base/i18n/file_util_icu_unittest.cc +++ b/base/i18n/file_util_icu_unittest.cc @@ -4,7 +4,7 @@ #include "base/i18n/file_util_icu.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" diff --git a/base/json/json_file_value_serializer.cc b/base/json/json_file_value_serializer.cc index e33080ccd6b91..d7d54f2e9118f 100644 --- a/base/json/json_file_value_serializer.cc +++ b/base/json/json_file_value_serializer.cc @@ -4,7 +4,7 @@ #include "base/json/json_file_value_serializer.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/json/json_string_value_serializer.h" #include "base/logging.h" diff --git a/base/json/json_reader_unittest.cc b/base/json/json_reader_unittest.cc index eceb5386a9dec..67070fff6dab9 100644 --- a/base/json/json_reader_unittest.cc +++ b/base/json/json_reader_unittest.cc @@ -5,7 +5,7 @@ #include "base/json/json_reader.h" #include "base/base_paths.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" diff --git a/base/json/json_value_serializer_unittest.cc b/base/json/json_value_serializer_unittest.cc index f8d3a20109ac5..3be8bbfd11d02 100644 --- a/base/json/json_value_serializer_unittest.cc +++ b/base/json/json_value_serializer_unittest.cc @@ -4,7 +4,7 @@ #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/json/json_file_value_serializer.h" #include "base/json/json_reader.h" diff --git a/base/linux_util.cc b/base/linux_util.cc index 36710f2813719..29e1980c9755f 100644 --- a/base/linux_util.cc +++ b/base/linux_util.cc @@ -15,7 +15,7 @@ #include #include "base/command_line.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/path_service.h" diff --git a/base/location.cc b/base/location.cc index 08263cebaad91..8b32b9785ba25 100644 --- a/base/location.cc +++ b/base/location.cc @@ -5,10 +5,7 @@ #include "build/build_config.h" #if defined(COMPILER_MSVC) -// MSDN says to #include , but that breaks the VS2005 build. -extern "C" { - void* _ReturnAddress(); -} +#include #endif #include "base/location.h" diff --git a/base/mac/mac_util_unittest.mm b/base/mac/mac_util_unittest.mm index 956036eaf1bc5..aae40a6337270 100644 --- a/base/mac/mac_util_unittest.mm +++ b/base/mac/mac_util_unittest.mm @@ -6,8 +6,8 @@ #include "base/mac/mac_util.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/mac/foundation_util.h" #include "base/mac/scoped_cftyperef.h" diff --git a/base/mac/scoped_objc_class_swizzler.h b/base/mac/scoped_objc_class_swizzler.h new file mode 100644 index 0000000000000..e18e4abfda553 --- /dev/null +++ b/base/mac/scoped_objc_class_swizzler.h @@ -0,0 +1,49 @@ +// 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 BASE_MAC_SCOPED_OBJC_CLASS_SWIZZLER_H_ +#define BASE_MAC_SCOPED_OBJC_CLASS_SWIZZLER_H_ + +#import + +#include "base/base_export.h" +#include "base/macros.h" + +namespace base { +namespace mac { + +// Within a given scope, swaps method implementations of a class interface, or +// between two class interfaces. The argument and return types must match. +class BASE_EXPORT ScopedObjCClassSwizzler { + public: + // Given two classes that each respond to |selector|, swap the implementations + // of those methods. + ScopedObjCClassSwizzler(Class target, Class source, SEL selector); + + // Given two selectors on the same class interface, |target| (e.g. via + // inheritance or categories), swap the implementations of methods |original| + // and |alternate|. + ScopedObjCClassSwizzler(Class target, SEL original, SEL alternate); + + ~ScopedObjCClassSwizzler(); + + // Return a callable function pointer for the replaced method. To call this + // from the replacing function, the first two arguments should be |self| and + // |_cmd|. These are followed by the (variadic) method arguments. + IMP GetOriginalImplementation(); + + private: + // Delegated constructor. + void Init(Class target, Class source, SEL original, SEL alternate); + + Method old_selector_impl_; + Method new_selector_impl_; + + DISALLOW_COPY_AND_ASSIGN(ScopedObjCClassSwizzler); +}; + +} // namespace mac +} // namespace base + +#endif // BASE_MAC_SCOPED_OBJC_CLASS_SWIZZLER_H_ diff --git a/base/mac/scoped_objc_class_swizzler.mm b/base/mac/scoped_objc_class_swizzler.mm new file mode 100644 index 0000000000000..20e5c569ae496 --- /dev/null +++ b/base/mac/scoped_objc_class_swizzler.mm @@ -0,0 +1,71 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "base/mac/scoped_objc_class_swizzler.h" + +#include + +#include "base/logging.h" + +namespace base { +namespace mac { + +ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, + Class source, + SEL selector) + : old_selector_impl_(NULL), new_selector_impl_(NULL) { + Init(target, source, selector, selector); +} + +ScopedObjCClassSwizzler::ScopedObjCClassSwizzler(Class target, + SEL original, + SEL alternate) + : old_selector_impl_(NULL), new_selector_impl_(NULL) { + Init(target, target, original, alternate); +} + +ScopedObjCClassSwizzler::~ScopedObjCClassSwizzler() { + if (old_selector_impl_ && new_selector_impl_) + method_exchangeImplementations(old_selector_impl_, new_selector_impl_); +} + +IMP ScopedObjCClassSwizzler::GetOriginalImplementation() { + // Note that while the swizzle is in effect the "new" method is actually + // pointing to the original implementation, since they have been swapped. + return method_getImplementation(new_selector_impl_); +} + +void ScopedObjCClassSwizzler::Init(Class target, + Class source, + SEL original, + SEL alternate) { + old_selector_impl_ = class_getInstanceMethod(target, original); + new_selector_impl_ = class_getInstanceMethod(source, alternate); + if (!old_selector_impl_ && !new_selector_impl_) { + // Try class methods. + old_selector_impl_ = class_getClassMethod(target, original); + new_selector_impl_ = class_getClassMethod(source, alternate); + } + + DCHECK(old_selector_impl_); + DCHECK(new_selector_impl_); + if (!old_selector_impl_ || !new_selector_impl_) + return; + + // The argument and return types must match exactly. + const char* old_types = method_getTypeEncoding(old_selector_impl_); + const char* new_types = method_getTypeEncoding(new_selector_impl_); + DCHECK(old_types); + DCHECK(new_types); + DCHECK_EQ(0, strcmp(old_types, new_types)); + if (!old_types || !new_types || strcmp(old_types, new_types)) { + old_selector_impl_ = new_selector_impl_ = NULL; + return; + } + + method_exchangeImplementations(old_selector_impl_, new_selector_impl_); +} + +} // namespace mac +} // namespace base diff --git a/base/mac/scoped_objc_class_swizzler_unittest.mm b/base/mac/scoped_objc_class_swizzler_unittest.mm new file mode 100644 index 0000000000000..eacd10518218e --- /dev/null +++ b/base/mac/scoped_objc_class_swizzler_unittest.mm @@ -0,0 +1,166 @@ +// 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 "base/mac/scoped_objc_class_swizzler.h" + +#import "base/mac/scoped_nsobject.h" +#include "testing/gtest/include/gtest/gtest.h" + +@interface ObjCClassSwizzlerTestOne : NSObject ++ (NSInteger)function; +- (NSInteger)method; +- (NSInteger)modifier; +@end + +@interface ObjCClassSwizzlerTestTwo : NSObject ++ (NSInteger)function; +- (NSInteger)method; +- (NSInteger)modifier; +@end + +@implementation ObjCClassSwizzlerTestOne : NSObject + ++ (NSInteger)function { + return 10; +} + +- (NSInteger)method { + // Multiply by a modifier to ensure |self| in a swizzled implementation + // refers to the original object. + return 1 * [self modifier]; +} + +- (NSInteger)modifier { + return 3; +} + +@end + +@implementation ObjCClassSwizzlerTestTwo : NSObject + ++ (NSInteger)function { + return 20; +} + +- (NSInteger)method { + return 2 * [self modifier]; +} + +- (NSInteger)modifier { + return 7; +} + +@end + +@interface ObjCClassSwizzlerTestOne (AlternateCategory) +- (NSInteger)alternate; +@end + +@implementation ObjCClassSwizzlerTestOne (AlternateCategory) +- (NSInteger)alternate { + return 3 * [self modifier]; +} +@end + +@interface ObjCClassSwizzlerTestOneChild : ObjCClassSwizzlerTestOne +- (NSInteger)childAlternate; +@end + +@implementation ObjCClassSwizzlerTestOneChild +- (NSInteger)childAlternate { + return 5 * [self modifier]; +} +@end + +namespace base { +namespace mac { + +TEST(ObjCClassSwizzlerTest, SwizzleInstanceMethods) { + base::scoped_nsobject object_one( + [[ObjCClassSwizzlerTestOne alloc] init]); + base::scoped_nsobject object_two( + [[ObjCClassSwizzlerTestTwo alloc] init]); + EXPECT_EQ(3, [object_one method]); + EXPECT_EQ(14, [object_two method]); + + { + base::mac::ScopedObjCClassSwizzler swizzler( + [ObjCClassSwizzlerTestOne class], + [ObjCClassSwizzlerTestTwo class], + @selector(method)); + EXPECT_EQ(6, [object_one method]); + EXPECT_EQ(7, [object_two method]); + + IMP original = swizzler.GetOriginalImplementation(); + id expected_result = reinterpret_cast(3); + EXPECT_EQ(expected_result, original(object_one, @selector(method))); + } + + EXPECT_EQ(3, [object_one method]); + EXPECT_EQ(14, [object_two method]); +} + +TEST(ObjCClassSwizzlerTest, SwizzleClassMethods) { + EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]); + EXPECT_EQ(20, [ObjCClassSwizzlerTestTwo function]); + + { + base::mac::ScopedObjCClassSwizzler swizzler( + [ObjCClassSwizzlerTestOne class], + [ObjCClassSwizzlerTestTwo class], + @selector(function)); + EXPECT_EQ(20, [ObjCClassSwizzlerTestOne function]); + EXPECT_EQ(10, [ObjCClassSwizzlerTestTwo function]); + + IMP original = swizzler.GetOriginalImplementation(); + id expected_result = reinterpret_cast(10); + EXPECT_EQ(expected_result, original(nil, @selector(function))); + } + + EXPECT_EQ(10, [ObjCClassSwizzlerTestOne function]); + EXPECT_EQ(20, [ObjCClassSwizzlerTestTwo function]); +} + +TEST(ObjCClassSwizzlerTest, SwizzleViaCategory) { + base::scoped_nsobject object_one( + [[ObjCClassSwizzlerTestOne alloc] init]); + EXPECT_EQ(3, [object_one method]); + + { + base::mac::ScopedObjCClassSwizzler swizzler( + [ObjCClassSwizzlerTestOne class], + @selector(method), + @selector(alternate)); + EXPECT_EQ(9, [object_one method]); + + IMP original = swizzler.GetOriginalImplementation(); + id expected_result = reinterpret_cast(3); + EXPECT_EQ(expected_result, original(object_one, @selector(method))); + } + + EXPECT_EQ(3, [object_one method]); +} + +TEST(ObjCClassSwizzlerTest, SwizzleViaInheritance) { + base::scoped_nsobject child( + [[ObjCClassSwizzlerTestOneChild alloc] init]); + EXPECT_EQ(3, [child method]); + + { + base::mac::ScopedObjCClassSwizzler swizzler( + [ObjCClassSwizzlerTestOneChild class], + @selector(method), + @selector(childAlternate)); + EXPECT_EQ(15, [child method]); + + IMP original = swizzler.GetOriginalImplementation(); + id expected_result = reinterpret_cast(3); + EXPECT_EQ(expected_result, original(child, @selector(method))); + } + + EXPECT_EQ(3, [child method]); +} + +} // namespace mac +} // namespace base diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h index 61b4a6a99912a..fad141fbb9b11 100644 --- a/base/mac/sdk_forward_declarations.h +++ b/base/mac/sdk_forward_declarations.h @@ -254,4 +254,18 @@ typedef NSUInteger NSWindowOcclusionState; #endif // MAC_OS_X_VERSION_10_9 +#if !defined(MAC_OS_X_VERSION_10_10) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 + +@interface NSUserActivity : NSObject + +@property (readonly, copy) NSString* activityType; +@property (copy) NSURL* webPageURL; + +@end + +BASE_EXPORT extern "C" NSString* const NSUserActivityTypeBrowsingWeb; + +#endif // MAC_OS_X_VERSION_10_10 + #endif // BASE_MAC_SDK_FORWARD_DECLARATIONS_H_ diff --git a/base/mac/sdk_forward_declarations.mm b/base/mac/sdk_forward_declarations.mm index a402a417d85ed..22986da5352c1 100644 --- a/base/mac/sdk_forward_declarations.mm +++ b/base/mac/sdk_forward_declarations.mm @@ -12,3 +12,12 @@ @"NSWindowWillEnterFullScreenNotification"; #endif // MAC_OS_X_VERSION_10_7 + +// Replicate specific 10.10 SDK declarations for building with prior SDKs. +#if !defined(MAC_OS_X_VERSION_10_10) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 + +NSString* const NSUserActivityTypeBrowsingWeb = + @"NSUserActivityTypeBrowsingWeb"; + +#endif // MAC_OS_X_VERSION_10_10 diff --git a/base/memory/discardable_memory.h b/base/memory/discardable_memory.h index d16ed3e8480f9..5b74705234ff6 100644 --- a/base/memory/discardable_memory.h +++ b/base/memory/discardable_memory.h @@ -64,14 +64,6 @@ class BASE_EXPORT DiscardableMemory { public: virtual ~DiscardableMemory() {} - // Call this on a thread with a MessageLoop current to allow discardable - // memory implementations to respond to memory pressure signals. - static void RegisterMemoryPressureListeners(); - - // Call this to prevent discardable memory implementations from responding - // to memory pressure signals. - static void UnregisterMemoryPressureListeners(); - // Gets the discardable memory type with a given name. static DiscardableMemoryType GetNamedType(const std::string& name); diff --git a/base/memory/discardable_memory_android.cc b/base/memory/discardable_memory_android.cc index 8988c2b8d2bd4..acf29ac85f11e 100644 --- a/base/memory/discardable_memory_android.cc +++ b/base/memory/discardable_memory_android.cc @@ -32,10 +32,7 @@ size_t GetOptimalAshmemRegionSizeForAllocator() { // Holds the shared state used for allocations. struct SharedState { SharedState() - : manager(kAshmemMemoryLimit, - kAshmemMemoryLimit, - kAshmemMemoryLimit, - TimeDelta::Max()), + : manager(kAshmemMemoryLimit, kAshmemMemoryLimit, TimeDelta::Max()), allocator(kAshmemAllocatorName, GetOptimalAshmemRegionSizeForAllocator()) {} @@ -46,16 +43,6 @@ LazyInstance::Leaky g_shared_state = LAZY_INSTANCE_INITIALIZER; } // namespace -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_ashmem_allocator.cc b/base/memory/discardable_memory_ashmem_allocator.cc index 249952ec07e1d..3d4af925af3dc 100644 --- a/base/memory/discardable_memory_ashmem_allocator.cc +++ b/base/memory/discardable_memory_ashmem_allocator.cc @@ -15,7 +15,7 @@ #include "base/basictypes.h" #include "base/containers/hash_tables.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_vector.h" diff --git a/base/memory/discardable_memory_emulated.cc b/base/memory/discardable_memory_emulated.cc index 340a181834ad3..f9097b186e140 100644 --- a/base/memory/discardable_memory_emulated.cc +++ b/base/memory/discardable_memory_emulated.cc @@ -13,14 +13,12 @@ namespace { // This is admittedly pretty magical. const size_t kEmulatedMemoryLimit = 512 * 1024 * 1024; const size_t kEmulatedSoftMemoryLimit = 32 * 1024 * 1024; -const size_t kEmulatedBytesToKeepUnderModeratePressure = 4 * 1024 * 1024; const size_t kEmulatedHardMemoryLimitExpirationTimeMs = 1000; struct SharedState { SharedState() : manager(kEmulatedMemoryLimit, kEmulatedSoftMemoryLimit, - kEmulatedBytesToKeepUnderModeratePressure, TimeDelta::FromMilliseconds( kEmulatedHardMemoryLimitExpirationTimeMs)) {} @@ -45,18 +43,14 @@ DiscardableMemoryEmulated::~DiscardableMemoryEmulated() { } // static -void DiscardableMemoryEmulated::RegisterMemoryPressureListeners() { - g_shared_state.Pointer()->manager.RegisterMemoryPressureListener(); -} - -// static -void DiscardableMemoryEmulated::UnregisterMemoryPressureListeners() { - g_shared_state.Pointer()->manager.UnregisterMemoryPressureListener(); +bool DiscardableMemoryEmulated::ReduceMemoryUsage() { + return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); } // static -bool DiscardableMemoryEmulated::ReduceMemoryUsage() { - return g_shared_state.Pointer()->manager.ReduceMemoryUsage(); +void DiscardableMemoryEmulated::ReduceMemoryUsageUntilWithinLimit( + size_t bytes) { + g_shared_state.Pointer()->manager.ReduceMemoryUsageUntilWithinLimit(bytes); } // static diff --git a/base/memory/discardable_memory_emulated.h b/base/memory/discardable_memory_emulated.h index 64e99511b7caa..d928513527ea8 100644 --- a/base/memory/discardable_memory_emulated.h +++ b/base/memory/discardable_memory_emulated.h @@ -19,10 +19,13 @@ class DiscardableMemoryEmulated explicit DiscardableMemoryEmulated(size_t bytes); virtual ~DiscardableMemoryEmulated(); - static void RegisterMemoryPressureListeners(); - static void UnregisterMemoryPressureListeners(); static bool ReduceMemoryUsage(); + // TODO(reveman): Remove this as it is breaking the discardable memory design + // principle that implementations should not rely on information this is + // unavailable in kernel space. crbug.com/400423 + BASE_EXPORT static void ReduceMemoryUsageUntilWithinLimit(size_t bytes); + static void PurgeForTesting(); bool Initialize(); diff --git a/base/memory/discardable_memory_linux.cc b/base/memory/discardable_memory_linux.cc index b9342e92043ca..578b2c1c75604 100644 --- a/base/memory/discardable_memory_linux.cc +++ b/base/memory/discardable_memory_linux.cc @@ -10,16 +10,6 @@ namespace base { -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_mac.cc b/base/memory/discardable_memory_mac.cc index b2184e7d589c4..fa6a23145c389 100644 --- a/base/memory/discardable_memory_mac.cc +++ b/base/memory/discardable_memory_mac.cc @@ -25,11 +25,7 @@ namespace { const size_t kMacMemoryLimit = 512 * 1024 * 1024; struct SharedState { - SharedState() - : manager(kMacMemoryLimit, - kMacMemoryLimit, - kMacMemoryLimit, - TimeDelta::Max()) {} + SharedState() : manager(kMacMemoryLimit, kMacMemoryLimit, TimeDelta::Max()) {} internal::DiscardableMemoryManager manager; }; @@ -159,16 +155,6 @@ class DiscardableMemoryMac } // namespace -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/discardable_memory_manager.cc b/base/memory/discardable_memory_manager.cc index d976da203c4a8..3647b7b2f91a6 100644 --- a/base/memory/discardable_memory_manager.cc +++ b/base/memory/discardable_memory_manager.cc @@ -18,14 +18,11 @@ namespace internal { DiscardableMemoryManager::DiscardableMemoryManager( size_t memory_limit, size_t soft_memory_limit, - size_t bytes_to_keep_under_moderate_pressure, TimeDelta hard_memory_limit_expiration_time) : allocations_(AllocationMap::NO_AUTO_EVICT), bytes_allocated_(0u), memory_limit_(memory_limit), soft_memory_limit_(soft_memory_limit), - bytes_to_keep_under_moderate_pressure_( - bytes_to_keep_under_moderate_pressure), hard_memory_limit_expiration_time_(hard_memory_limit_expiration_time) { BytesAllocatedChanged(bytes_allocated_); } @@ -35,20 +32,6 @@ DiscardableMemoryManager::~DiscardableMemoryManager() { DCHECK_EQ(0u, bytes_allocated_); } -void DiscardableMemoryManager::RegisterMemoryPressureListener() { - AutoLock lock(lock_); - DCHECK(base::MessageLoop::current()); - DCHECK(!memory_pressure_listener_); - memory_pressure_listener_.reset(new MemoryPressureListener(base::Bind( - &DiscardableMemoryManager::OnMemoryPressure, Unretained(this)))); -} - -void DiscardableMemoryManager::UnregisterMemoryPressureListener() { - AutoLock lock(lock_); - DCHECK(memory_pressure_listener_); - memory_pressure_listener_.reset(); -} - void DiscardableMemoryManager::SetMemoryLimit(size_t bytes) { AutoLock lock(lock_); memory_limit_ = bytes; @@ -61,12 +44,6 @@ void DiscardableMemoryManager::SetSoftMemoryLimit(size_t bytes) { soft_memory_limit_ = bytes; } -void DiscardableMemoryManager::SetBytesToKeepUnderModeratePressure( - size_t bytes) { - AutoLock lock(lock_); - bytes_to_keep_under_moderate_pressure_ = bytes; -} - void DiscardableMemoryManager::SetHardMemoryLimitExpirationTime( TimeDelta hard_memory_limit_expiration_time) { AutoLock lock(lock_); @@ -77,13 +54,14 @@ bool DiscardableMemoryManager::ReduceMemoryUsage() { return PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit(); } +void DiscardableMemoryManager::ReduceMemoryUsageUntilWithinLimit(size_t bytes) { + AutoLock lock(lock_); + PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired(Now(), + bytes); +} + void DiscardableMemoryManager::Register(Allocation* allocation, size_t bytes) { AutoLock lock(lock_); - // A registered memory listener is currently required. This DCHECK can be - // moved or removed if we decide that it's useful to relax this condition. - // TODO(reveman): Enable this DCHECK when skia and blink are able to - // register memory pressure listeners. crbug.com/333907 - // DCHECK(memory_pressure_listener_); DCHECK(allocations_.Peek(allocation) == allocations_.end()); allocations_.Put(allocation, AllocationInfo(bytes)); } @@ -182,28 +160,6 @@ size_t DiscardableMemoryManager::GetBytesAllocatedForTest() const { return bytes_allocated_; } -void DiscardableMemoryManager::OnMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level) { - switch (pressure_level) { - case MemoryPressureListener::MEMORY_PRESSURE_MODERATE: - PurgeUntilWithinBytesToKeepUnderModeratePressure(); - return; - case MemoryPressureListener::MEMORY_PRESSURE_CRITICAL: - PurgeAll(); - return; - } - - NOTREACHED(); -} - -void -DiscardableMemoryManager::PurgeUntilWithinBytesToKeepUnderModeratePressure() { - AutoLock lock(lock_); - - PurgeIfNotUsedSinceTimestampUntilUsageIsWithinLimitWithLockAcquired( - Now(), bytes_to_keep_under_moderate_pressure_); -} - bool DiscardableMemoryManager:: PurgeIfNotUsedSinceHardLimitCutoffUntilWithinSoftMemoryLimit() { AutoLock lock(lock_); diff --git a/base/memory/discardable_memory_manager.h b/base/memory/discardable_memory_manager.h index a61f141c43034..94b3c55108f22 100644 --- a/base/memory/discardable_memory_manager.h +++ b/base/memory/discardable_memory_manager.h @@ -8,7 +8,6 @@ #include "base/base_export.h" #include "base/containers/hash_tables.h" #include "base/containers/mru_cache.h" -#include "base/memory/memory_pressure_listener.h" #include "base/synchronization/lock.h" #include "base/time/time.h" @@ -60,27 +59,15 @@ namespace internal { // of all allocation instances (in case they need to be purged), and the total // amount of allocated memory (in case this forces a purge). When memory usage // reaches the limit, the manager purges the LRU memory. -// -// When notified of memory pressure, the manager either purges the LRU memory -- -// if the pressure is moderate -- or all discardable memory if the pressure is -// critical. class BASE_EXPORT_PRIVATE DiscardableMemoryManager { public: typedef DiscardableMemoryManagerAllocation Allocation; DiscardableMemoryManager(size_t memory_limit, size_t soft_memory_limit, - size_t bytes_to_keep_under_moderate_pressure, TimeDelta hard_memory_limit_expiration_time); virtual ~DiscardableMemoryManager(); - // Call this to register memory pressure listener. Must be called on a thread - // with a MessageLoop current. - void RegisterMemoryPressureListener(); - - // Call this to unregister memory pressure listener. - void UnregisterMemoryPressureListener(); - // The maximum number of bytes of memory that may be allocated before we force // a purge. void SetMemoryLimit(size_t bytes); @@ -89,9 +76,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // limit expiration time without getting purged. void SetSoftMemoryLimit(size_t bytes); - // Sets the amount of memory to keep when we're under moderate pressure. - void SetBytesToKeepUnderModeratePressure(size_t bytes); - // Sets the memory usage cutoff time for hard memory limit. void SetHardMemoryLimitExpirationTime( TimeDelta hard_memory_limit_expiration_time); @@ -101,6 +85,10 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // have been used. bool ReduceMemoryUsage(); + // This can be called to attempt to reduce memory footprint until within + // limit for bytes to keep under moderate pressure. + void ReduceMemoryUsageUntilWithinLimit(size_t bytes); + // Adds the given allocation to the manager's collection. void Register(Allocation* allocation, size_t bytes); @@ -141,14 +129,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { }; typedef HashingMRUCache AllocationMap; - // This can be called as a hint that the system is under memory pressure. - void OnMemoryPressure( - MemoryPressureListener::MemoryPressureLevel pressure_level); - - // Purges memory until usage is less or equal to - // |bytes_to_keep_under_moderate_pressure_|. - void PurgeUntilWithinBytesToKeepUnderModeratePressure(); - // Purges memory not used since |hard_memory_limit_expiration_time_| before // "right now" until usage is less or equal to |soft_memory_limit_|. // Returns true if total amount of memory is less or equal to soft memory @@ -185,14 +165,6 @@ class BASE_EXPORT_PRIVATE DiscardableMemoryManager { // notification. size_t soft_memory_limit_; - // Under moderate memory pressure, we will purge memory until usage is within - // this limit. - size_t bytes_to_keep_under_moderate_pressure_; - - // Allows us to be respond when the system reports that it is under memory - // pressure. - scoped_ptr memory_pressure_listener_; - // Amount of time it takes for an allocation to become affected by // |soft_memory_limit_|. TimeDelta hard_memory_limit_expiration_time_; diff --git a/base/memory/discardable_memory_manager_unittest.cc b/base/memory/discardable_memory_manager_unittest.cc index ef5739a6526a5..674499fe0ea0b 100644 --- a/base/memory/discardable_memory_manager_unittest.cc +++ b/base/memory/discardable_memory_manager_unittest.cc @@ -5,7 +5,6 @@ #include "base/memory/discardable_memory_manager.h" #include "base/bind.h" -#include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread.h" #include "testing/gtest/include/gtest/gtest.h" @@ -46,7 +45,6 @@ class TestAllocationImpl : public internal::DiscardableMemoryManagerAllocation { // something else needs to explicit set the limit. const size_t kDefaultMemoryLimit = 1024; const size_t kDefaultSoftMemoryLimit = kDefaultMemoryLimit; -const size_t kDefaultBytesToKeepUnderModeratePressure = kDefaultMemoryLimit; class TestDiscardableMemoryManagerImpl : public internal::DiscardableMemoryManager { @@ -54,7 +52,6 @@ class TestDiscardableMemoryManagerImpl TestDiscardableMemoryManagerImpl() : DiscardableMemoryManager(kDefaultMemoryLimit, kDefaultSoftMemoryLimit, - kDefaultBytesToKeepUnderModeratePressure, TimeDelta::Max()) {} void SetNow(TimeTicks now) { now_ = now; } @@ -68,9 +65,7 @@ class TestDiscardableMemoryManagerImpl class DiscardableMemoryManagerTestBase { public: - DiscardableMemoryManagerTestBase() { - manager_.RegisterMemoryPressureListener(); - } + DiscardableMemoryManagerTestBase() {} protected: enum LockStatus { @@ -85,10 +80,6 @@ class DiscardableMemoryManagerTestBase { void SetSoftMemoryLimit(size_t bytes) { manager_.SetSoftMemoryLimit(bytes); } - void SetBytesToKeepUnderModeratePressure(size_t bytes) { - manager_.SetBytesToKeepUnderModeratePressure(bytes); - } - void SetHardMemoryLimitExpirationTime(TimeDelta time) { manager_.SetHardMemoryLimitExpirationTime(time); } @@ -127,10 +118,15 @@ class DiscardableMemoryManagerTestBase { void SetNow(TimeTicks now) { manager_.SetNow(now); } + void PurgeAll() { return manager_.PurgeAll(); } + bool ReduceMemoryUsage() { return manager_.ReduceMemoryUsage(); } + void ReduceMemoryUsageUntilWithinLimit(size_t bytes) { + manager_.ReduceMemoryUsageUntilWithinLimit(bytes); + } + private: - MessageLoopForIO message_loop_; TestDiscardableMemoryManagerImpl manager_; }; @@ -192,11 +188,7 @@ TEST_F(DiscardableMemoryManagerTest, LockAfterPurge) { EXPECT_TRUE(CanBePurged(&allocation)); // Force the system to purge. - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - - // Required because ObserverListThreadSafe notifies via PostTask. - RunLoop().RunUntilIdle(); + PurgeAll(); EXPECT_EQ(LOCK_STATUS_PURGED, Lock(&allocation)); EXPECT_FALSE(CanBePurged(&allocation)); @@ -301,17 +293,14 @@ class DiscardableMemoryManagerPermutationTest TestAllocationImpl allocation_[3]; }; -// Verify that memory was discarded in the correct order after applying -// memory pressure. -TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { +// Verify that memory was discarded in the correct order after reducing usage to +// limit. +TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscarded) { RegisterAndUseAllocations(); - SetBytesToKeepUnderModeratePressure(1024); SetMemoryLimit(2048); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_MODERATE); - RunLoop().RunUntilIdle(); + ReduceMemoryUsageUntilWithinLimit(1024); EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); EXPECT_EQ(LOCK_STATUS_PURGED, Lock(allocation(1))); @@ -326,7 +315,6 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedModeratePressure) { TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { RegisterAndUseAllocations(); - SetBytesToKeepUnderModeratePressure(1024); SetMemoryLimit(2048); EXPECT_NE(LOCK_STATUS_FAILED, Lock(allocation(2))); @@ -340,7 +328,6 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedExceedLimit) { // Verify that no more memory than necessary was discarded after changing // memory limit. TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { - SetBytesToKeepUnderModeratePressure(2048); SetMemoryLimit(4096); RegisterAndUseAllocations(); @@ -358,9 +345,7 @@ TEST_P(DiscardableMemoryManagerPermutationTest, LRUDiscardedAmount) { TEST_P(DiscardableMemoryManagerPermutationTest, PurgeFreesAllUnlocked) { RegisterAndUseAllocations(); - MemoryPressureListener::NotifyMemoryPressure( - MemoryPressureListener::MEMORY_PRESSURE_CRITICAL); - RunLoop().RunUntilIdle(); + PurgeAll(); for (int i = 0; i < 3; ++i) { if (i == 0) diff --git a/base/memory/discardable_memory_unittest.cc b/base/memory/discardable_memory_unittest.cc index dc0e2cd21262b..516a96b5a4fa2 100644 --- a/base/memory/discardable_memory_unittest.cc +++ b/base/memory/discardable_memory_unittest.cc @@ -6,7 +6,6 @@ #include -#include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_ANDROID) @@ -19,12 +18,8 @@ namespace { class DiscardableMemoryTest : public testing::TestWithParam { public: - DiscardableMemoryTest() : message_loop_(MessageLoop::TYPE_IO) { - // Register memory pressure listeners now that we have a message loop. - DiscardableMemory::RegisterMemoryPressureListeners(); - } + DiscardableMemoryTest() {} virtual ~DiscardableMemoryTest() { - DiscardableMemory::UnregisterMemoryPressureListeners(); } protected: @@ -32,9 +27,6 @@ class DiscardableMemoryTest return DiscardableMemory::CreateLockedMemoryWithType( GetParam(), size).Pass(); } - - private: - MessageLoop message_loop_; }; const size_t kSize = 1024; diff --git a/base/memory/discardable_memory_win.cc b/base/memory/discardable_memory_win.cc index b9342e92043ca..578b2c1c75604 100644 --- a/base/memory/discardable_memory_win.cc +++ b/base/memory/discardable_memory_win.cc @@ -10,16 +10,6 @@ namespace base { -// static -void DiscardableMemory::RegisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::RegisterMemoryPressureListeners(); -} - -// static -void DiscardableMemory::UnregisterMemoryPressureListeners() { - internal::DiscardableMemoryEmulated::UnregisterMemoryPressureListeners(); -} - // static bool DiscardableMemory::ReduceMemoryUsage() { return internal::DiscardableMemoryEmulated::ReduceMemoryUsage(); diff --git a/base/memory/shared_memory.h b/base/memory/shared_memory.h index d48071e2f73a3..7254950feb080 100644 --- a/base/memory/shared_memory.h +++ b/base/memory/shared_memory.h @@ -21,7 +21,7 @@ #if defined(OS_POSIX) #include "base/file_descriptor_posix.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #endif diff --git a/base/memory/shared_memory_posix.cc b/base/memory/shared_memory_posix.cc index f0a28ba0625f5..0358e63e33843 100644 --- a/base/memory/shared_memory_posix.cc +++ b/base/memory/shared_memory_posix.cc @@ -11,7 +11,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/lazy_instance.h" #include "base/logging.h" diff --git a/base/message_loop/incoming_task_queue.cc b/base/message_loop/incoming_task_queue.cc index bcc712b4a5d98..2ca32d6254b6a 100644 --- a/base/message_loop/incoming_task_queue.cc +++ b/base/message_loop/incoming_task_queue.cc @@ -4,16 +4,17 @@ #include "base/message_loop/incoming_task_queue.h" -#include "base/debug/trace_event.h" #include "base/location.h" #include "base/message_loop/message_loop.h" #include "base/synchronization/waitable_event.h" +#include "base/time/time.h" namespace base { namespace internal { IncomingTaskQueue::IncomingTaskQueue(MessageLoop* message_loop) - : message_loop_(message_loop), + : high_res_task_count_(0), + message_loop_(message_loop), next_sequence_num_(0) { } @@ -25,15 +26,23 @@ bool IncomingTaskQueue::AddToIncomingQueue( AutoLock locked(incoming_queue_lock_); PendingTask pending_task( from_here, task, CalculateDelayedRuntime(delay), nestable); +#if defined(OS_WIN) + // We consider the task needs a high resolution timer if the delay is + // more than 0 and less than 32ms. This caps the relative error to + // less than 50% : a 33ms wait can wake at 48ms since the default + // resolution on Windows is between 10 and 15ms. + if (delay > TimeDelta() && + delay.InMilliseconds() < (2 * Time::kMinLowResolutionThresholdMs)) { + ++high_res_task_count_; + pending_task.is_high_res = true; + } +#endif return PostPendingTask(&pending_task); } -bool IncomingTaskQueue::IsHighResolutionTimerEnabledForTesting() { -#if defined(OS_WIN) - return !high_resolution_timer_expiration_.is_null(); -#else - return true; -#endif +bool IncomingTaskQueue::HasHighResolutionTasks() { + AutoLock lock(incoming_queue_lock_); + return high_res_task_count_ > 0; } bool IncomingTaskQueue::IsIdleForTesting() { @@ -41,29 +50,22 @@ bool IncomingTaskQueue::IsIdleForTesting() { return incoming_queue_.empty(); } -void IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { +int IncomingTaskQueue::ReloadWorkQueue(TaskQueue* work_queue) { // Make sure no tasks are lost. DCHECK(work_queue->empty()); // Acquire all we can from the inter-thread queue with one lock acquisition. AutoLock lock(incoming_queue_lock_); if (!incoming_queue_.empty()) - incoming_queue_.Swap(work_queue); // Constant time + incoming_queue_.Swap(work_queue); - DCHECK(incoming_queue_.empty()); + // Reset the count of high resolution tasks since our queue is now empty. + int high_res_tasks = high_res_task_count_; + high_res_task_count_ = 0; + return high_res_tasks; } void IncomingTaskQueue::WillDestroyCurrentMessageLoop() { -#if defined(OS_WIN) - // If we left the high-resolution timer activated, deactivate it now. - // Doing this is not-critical, it is mainly to make sure we track - // the high resolution timer activations properly in our unit tests. - if (!high_resolution_timer_expiration_.is_null()) { - Time::ActivateHighResolutionTimer(false); - high_resolution_timer_expiration_ = TimeTicks(); - } -#endif - AutoLock lock(incoming_queue_lock_); message_loop_ = NULL; } @@ -75,40 +77,10 @@ IncomingTaskQueue::~IncomingTaskQueue() { TimeTicks IncomingTaskQueue::CalculateDelayedRuntime(TimeDelta delay) { TimeTicks delayed_run_time; - if (delay > TimeDelta()) { + if (delay > TimeDelta()) delayed_run_time = TimeTicks::Now() + delay; - -#if defined(OS_WIN) - if (high_resolution_timer_expiration_.is_null()) { - // Windows timers are granular to 15.6ms. If we only set high-res - // timers for those under 15.6ms, then a 18ms timer ticks at ~32ms, - // which as a percentage is pretty inaccurate. So enable high - // res timers for any timer which is within 2x of the granularity. - // This is a tradeoff between accuracy and power management. - bool needs_high_res_timers = delay.InMilliseconds() < - (2 * Time::kMinLowResolutionThresholdMs); - if (needs_high_res_timers) { - if (Time::ActivateHighResolutionTimer(true)) { - high_resolution_timer_expiration_ = TimeTicks::Now() + - TimeDelta::FromMilliseconds( - MessageLoop::kHighResolutionTimerModeLeaseTimeMs); - } - } - } -#endif - } else { + else DCHECK_EQ(delay.InMilliseconds(), 0) << "delay should not be negative"; - } - -#if defined(OS_WIN) - if (!high_resolution_timer_expiration_.is_null()) { - if (TimeTicks::Now() > high_resolution_timer_expiration_) { - Time::ActivateHighResolutionTimer(false); - high_resolution_timer_expiration_ = TimeTicks(); - } - } -#endif - return delayed_run_time; } @@ -130,9 +102,8 @@ bool IncomingTaskQueue::PostPendingTask(PendingTask* pending_task) { // delayed_run_time value) and for identifying the task in about:tracing. pending_task->sequence_num = next_sequence_num_++; - TRACE_EVENT_FLOW_BEGIN0(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), - "MessageLoop::PostTask", - TRACE_ID_MANGLE(message_loop_->GetTaskTraceID(*pending_task))); + message_loop_->task_annotator()->DidQueueTask("MessageLoop::PostTask", + *pending_task); bool was_empty = incoming_queue_.empty(); incoming_queue_.push(*pending_task); diff --git a/base/message_loop/incoming_task_queue.h b/base/message_loop/incoming_task_queue.h index 56c5638295b84..30f26033a2d5d 100644 --- a/base/message_loop/incoming_task_queue.h +++ b/base/message_loop/incoming_task_queue.h @@ -38,16 +38,17 @@ class BASE_EXPORT IncomingTaskQueue TimeDelta delay, bool nestable); - // Returns true if the message loop has high resolution timers enabled. - // Provided for testing. - bool IsHighResolutionTimerEnabledForTesting(); + // Returns true if the queue contains tasks that require higher than default + // timer resolution. Currently only needed for Windows. + bool HasHighResolutionTasks(); // Returns true if the message loop is "idle". Provided for testing. bool IsIdleForTesting(); // Loads tasks from the |incoming_queue_| into |*work_queue|. Must be called - // from the thread that is running the loop. - void ReloadWorkQueue(TaskQueue* work_queue); + // from the thread that is running the loop. Returns the number of tasks that + // require high resolution timers. + int ReloadWorkQueue(TaskQueue* work_queue); // Disconnects |this| from the parent message loop. void WillDestroyCurrentMessageLoop(); @@ -65,12 +66,11 @@ class BASE_EXPORT IncomingTaskQueue // does not retain |pending_task->task| beyond this function call. bool PostPendingTask(PendingTask* pending_task); -#if defined(OS_WIN) - TimeTicks high_resolution_timer_expiration_; -#endif + // Number of tasks that require high resolution timing. This value is kept + // so that ReloadWorkQueue() completes in constant time. + int high_res_task_count_; - // The lock that protects access to |incoming_queue_|, |message_loop_| and - // |next_sequence_num_|. + // The lock that protects access to the members of this class. base::Lock incoming_queue_lock_; // An incoming queue of tasks that are acquired under a mutex for processing diff --git a/base/message_loop/message_loop.cc b/base/message_loop/message_loop.cc index a7217eba2f728..c01e5421bc347 100644 --- a/base/message_loop/message_loop.cc +++ b/base/message_loop/message_loop.cc @@ -8,8 +8,6 @@ #include "base/bind.h" #include "base/compiler_specific.h" -#include "base/debug/alias.h" -#include "base/debug/trace_event.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" @@ -92,6 +90,8 @@ MessageLoop::MessagePumpFactory* message_pump_for_ui_factory_ = NULL; // time for every task that is added to the MessageLoop incoming queue. bool AlwaysNotifyPump(MessageLoop::Type type) { #if defined(OS_ANDROID) + // The Android UI message loop needs to get notified each time a task is added + // to the incoming queue. return type == MessageLoop::TYPE_UI || type == MessageLoop::TYPE_JAVA; #else return false; @@ -127,6 +127,8 @@ MessageLoop::DestructionObserver::~DestructionObserver() { MessageLoop::MessageLoop(Type type) : type_(type), + pending_high_res_tasks_(0), + in_high_res_mode_(false), nestable_tasks_allowed_(true), #if defined(OS_WIN) os_modal_loop_(false), @@ -141,6 +143,8 @@ MessageLoop::MessageLoop(Type type) MessageLoop::MessageLoop(scoped_ptr pump) : pump_(pump.Pass()), type_(TYPE_CUSTOM), + pending_high_res_tasks_(0), + in_high_res_mode_(false), nestable_tasks_allowed_(true), #if defined(OS_WIN) os_modal_loop_(false), @@ -155,7 +159,10 @@ MessageLoop::~MessageLoop() { DCHECK_EQ(this, current()); DCHECK(!run_loop_); - +#if defined(OS_WIN) + if (in_high_res_mode_) + Time::ActivateHighResolutionTimer(false); +#endif // Clean up any unprocessed tasks, but take care: deleting a task could // result in the addition of more tasks (e.g., via DeleteSoon). We set a // limit on the number of times we will allow a deleted task to generate more @@ -369,8 +376,8 @@ bool MessageLoop::is_running() const { return run_loop_ != NULL; } -bool MessageLoop::IsHighResolutionTimerEnabledForTesting() { - return incoming_task_queue_->IsHighResolutionTimerEnabledForTesting(); +bool MessageLoop::HasHighResolutionTasks() { + return incoming_task_queue_->HasHighResolutionTasks(); } bool MessageLoop::IsIdleForTesting() { @@ -423,45 +430,24 @@ bool MessageLoop::ProcessNextDelayedNonNestableTask() { } void MessageLoop::RunTask(const PendingTask& pending_task) { - tracked_objects::TrackedTime start_time = - tracked_objects::ThreadData::NowForStartOfRun(pending_task.birth_tally); - - TRACE_EVENT_FLOW_END1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"), - "MessageLoop::PostTask", TRACE_ID_MANGLE(GetTaskTraceID(pending_task)), - "queue_duration", - (start_time - pending_task.EffectiveTimePosted()).InMilliseconds()); - // When tracing memory for posted tasks it's more valuable to attribute the - // memory allocations to the source function than generically to "RunTask". - TRACE_EVENT_WITH_MEMORY_TAG2( - "toplevel", "MessageLoop::RunTask", - pending_task.posted_from.function_name(), // Name for memory tracking. - "src_file", pending_task.posted_from.file_name(), - "src_func", pending_task.posted_from.function_name()); - DCHECK(nestable_tasks_allowed_); + + if (pending_task.is_high_res) { + pending_high_res_tasks_--; + CHECK(pending_high_res_tasks_ >= 0); + } // Execute the task and assume the worst: It is probably not reentrant. nestable_tasks_allowed_ = false; - // Before running the task, store the program counter where it was posted - // and deliberately alias it to ensure it is on the stack if the task - // crashes. Be careful not to assume that the variable itself will have the - // expected value when displayed by the optimizer in an optimized build. - // Look at a memory dump of the stack. - const void* program_counter = - pending_task.posted_from.program_counter(); - debug::Alias(&program_counter); - HistogramEvent(kTaskRunEvent); FOR_EACH_OBSERVER(TaskObserver, task_observers_, WillProcessTask(pending_task)); - pending_task.task.Run(); + task_annotator_.RunTask( + "MessageLoop::PostTask", "MessageLoop::RunTask", pending_task); FOR_EACH_OBSERVER(TaskObserver, task_observers_, DidProcessTask(pending_task)); - tracked_objects::ThreadData::TallyRunOnNamedThreadIfTracking(pending_task, - start_time, tracked_objects::ThreadData::NowForEndOfRun()); - nestable_tasks_allowed_ = true; } @@ -513,23 +499,18 @@ bool MessageLoop::DeletePendingTasks() { return did_work; } -uint64 MessageLoop::GetTaskTraceID(const PendingTask& task) { - return (static_cast(task.sequence_num) << 32) | - ((static_cast(reinterpret_cast(this)) << 32) >> 32); -} - void MessageLoop::ReloadWorkQueue() { // We can improve performance of our loading tasks from the incoming queue to // |*work_queue| by waiting until the last minute (|*work_queue| is empty) to // load. That reduces the number of locks-per-task significantly when our // queues get large. - if (work_queue_.empty()) - incoming_task_queue_->ReloadWorkQueue(&work_queue_); + if (work_queue_.empty()) { + pending_high_res_tasks_ += + incoming_task_queue_->ReloadWorkQueue(&work_queue_); + } } void MessageLoop::ScheduleWork(bool was_empty) { - // The Android UI message loop needs to get notified each time - // a task is added to the incoming queue. if (was_empty || AlwaysNotifyPump(type_)) pump_->ScheduleWork(); } @@ -629,6 +610,18 @@ bool MessageLoop::DoIdleWork() { if (run_loop_->quit_when_idle_received_) pump_->Quit(); + // When we return we will do a kernel wait for more tasks. +#if defined(OS_WIN) + // On Windows we activate the high resolution timer so that the wait + // _if_ triggered by the timer happens with good resolution. If we don't + // do this the default resolution is 15ms which might not be acceptable + // for some tasks. + bool high_res = pending_high_res_tasks_ > 0; + if (high_res != in_high_res_mode_) { + in_high_res_mode_ = high_res; + Time::ActivateHighResolutionTimer(in_high_res_mode_); + } +#endif return false; } @@ -662,16 +655,6 @@ void MessageLoopForUI::Attach() { } #endif -#if defined(OS_WIN) -void MessageLoopForUI::AddObserver(Observer* observer) { - static_cast(pump_.get())->AddObserver(observer); -} - -void MessageLoopForUI::RemoveObserver(Observer* observer) { - static_cast(pump_.get())->RemoveObserver(observer); -} -#endif // defined(OS_WIN) - #if defined(USE_OZONE) || (defined(USE_X11) && !defined(USE_GLIB)) bool MessageLoopForUI::WatchFileDescriptor( int fd, diff --git a/base/message_loop/message_loop.h b/base/message_loop/message_loop.h index a281435dc3eeb..8db1e77daf38b 100644 --- a/base/message_loop/message_loop.h +++ b/base/message_loop/message_loop.h @@ -11,6 +11,7 @@ #include "base/base_export.h" #include "base/basictypes.h" #include "base/callback_forward.h" +#include "base/debug/task_annotator.h" #include "base/location.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -38,7 +39,6 @@ namespace base { class HistogramBase; -class MessagePumpObserver; class RunLoop; class ThreadTaskRunnerHandle; class WaitableEvent; @@ -371,10 +371,6 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { void AddTaskObserver(TaskObserver* task_observer); void RemoveTaskObserver(TaskObserver* task_observer); - // When we go into high resolution timer mode, we will stay in hi-res mode - // for at least 1s. - static const int kHighResolutionTimerModeLeaseTimeMs = 1000; - #if defined(OS_WIN) void set_os_modal_loop(bool os_modal_loop) { os_modal_loop_ = os_modal_loop; @@ -390,7 +386,7 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // Returns true if the message loop has high resolution timers enabled. // Provided for testing. - bool IsHighResolutionTimerEnabledForTesting(); + bool HasHighResolutionTasks(); // Returns true if the message loop is "idle". Provided for testing. bool IsIdleForTesting(); @@ -427,10 +423,9 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // true if some work was done. bool DeletePendingTasks(); - // Creates a process-wide unique ID to represent this task in trace events. - // This will be mangled with a Process ID hash to reduce the likelyhood of - // colliding with MessageLoop pointers on other processes. - uint64 GetTaskTraceID(const PendingTask& task); + // Returns the TaskAnnotator which is used to add debug information to posted + // tasks. + debug::TaskAnnotator* task_annotator() { return &task_annotator_; } // Loads tasks from the incoming queue to |work_queue_| if the latter is // empty. @@ -460,6 +455,14 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { // this queue is only accessed (push/pop) by our current thread. TaskQueue work_queue_; + // How many high resolution tasks are in the pending task queue. This value + // increases by N every time we call ReloadWorkQueue() and decreases by 1 + // every time we call RunTask() if the task needs a high resolution timer. + int pending_high_res_tasks_; + // Tracks if we have requested high resolution timers. Its only use is to + // turn off the high resolution timer upon loop destruction. + bool in_high_res_mode_; + // Contains delayed tasks, sorted by their 'delayed_run_time' property. DelayedTaskQueue delayed_work_queue_; @@ -491,6 +494,8 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate { ObserverList task_observers_; + debug::TaskAnnotator task_annotator_; + scoped_refptr incoming_task_queue_; // The message loop proxy associated with this message loop. @@ -551,14 +556,6 @@ class BASE_EXPORT MessageLoopForUI : public MessageLoop { void Start(); #endif -#if defined(OS_WIN) - typedef MessagePumpObserver Observer; - - // Please see message_pump_win for definitions of these methods. - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); -#endif - #if defined(USE_OZONE) || (defined(USE_X11) && !defined(USE_GLIB)) // Please see MessagePumpLibevent for definition. bool WatchFileDescriptor( diff --git a/base/message_loop/message_loop_unittest.cc b/base/message_loop/message_loop_unittest.cc index 1b09eb070fa32..0a6e817f33f89 100644 --- a/base/message_loop/message_loop_unittest.cc +++ b/base/message_loop/message_loop_unittest.cc @@ -726,34 +726,26 @@ TEST(MessageLoopTest, WaitForIO) { TEST(MessageLoopTest, HighResolutionTimer) { MessageLoop loop; + Time::EnableHighResolutionTimer(true); const TimeDelta kFastTimer = TimeDelta::FromMilliseconds(5); const TimeDelta kSlowTimer = TimeDelta::FromMilliseconds(100); - EXPECT_FALSE(loop.IsHighResolutionTimerEnabledForTesting()); - + EXPECT_FALSE(loop.HasHighResolutionTasks()); // Post a fast task to enable the high resolution timers. loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1), kFastTimer); + EXPECT_TRUE(loop.HasHighResolutionTasks()); loop.Run(); - EXPECT_TRUE(loop.IsHighResolutionTimerEnabledForTesting()); - - // Post a slow task and verify high resolution timers - // are still enabled. - loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1), - kSlowTimer); - loop.Run(); - EXPECT_TRUE(loop.IsHighResolutionTimerEnabledForTesting()); - - // Wait for a while so that high-resolution mode elapses. - PlatformThread::Sleep(TimeDelta::FromMilliseconds( - MessageLoop::kHighResolutionTimerModeLeaseTimeMs)); - - // Post a slow task to disable the high resolution timers. + EXPECT_FALSE(loop.HasHighResolutionTasks()); + EXPECT_FALSE(Time::IsHighResolutionTimerInUse()); + // Check that a slow task does not trigger the high resolution logic. loop.PostDelayedTask(FROM_HERE, Bind(&PostNTasksThenQuit, 1), kSlowTimer); + EXPECT_FALSE(loop.HasHighResolutionTasks()); loop.Run(); - EXPECT_FALSE(loop.IsHighResolutionTimerEnabledForTesting()); + EXPECT_FALSE(loop.HasHighResolutionTasks()); + Time::EnableHighResolutionTimer(false); } #endif // defined(OS_WIN) diff --git a/base/message_loop/message_pump.h b/base/message_loop/message_pump.h index 4dc501b660839..a2edb458a96b5 100644 --- a/base/message_loop/message_pump.h +++ b/base/message_loop/message_pump.h @@ -39,7 +39,8 @@ class BASE_EXPORT MessagePump : public NonThreadSafe { virtual bool DoDelayedWork(TimeTicks* next_delayed_work_time) = 0; // Called from within Run just before the message pump goes to sleep. - // Returns true to indicate that idle work was done. + // Returns true to indicate that idle work was done. Returning false means + // the pump will now wait. virtual bool DoIdleWork() = 0; }; diff --git a/base/message_loop/message_pump_observer.h b/base/message_loop/message_pump_observer.h deleted file mode 100644 index d6e7abf786ada..0000000000000 --- a/base/message_loop/message_pump_observer.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_OBSERVER_H -#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_OBSERVER_H - -#include "base/base_export.h" -#include "base/event_types.h" - -#if !defined(OS_WIN) -#error Should not be here. -#endif - -namespace base { - -// A MessagePumpObserver is an object that receives global -// notifications from the UI MessageLoop with MessagePumpWin. -// -// NOTE: An Observer implementation should be extremely fast! -class BASE_EXPORT MessagePumpObserver { - public: - // This method is called before processing a NativeEvent. - virtual void WillProcessEvent(const NativeEvent& event) = 0; - - // This method is called after processing a message. - virtual void DidProcessEvent(const NativeEvent& event) = 0; - - protected: - virtual ~MessagePumpObserver() {} -}; - -} // namespace base - -#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_OBSERVER_H diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc index ae022bf0957f4..ad422260364db 100644 --- a/base/message_loop/message_pump_win.cc +++ b/base/message_loop/message_pump_win.cc @@ -35,22 +35,6 @@ static const int kMsgHaveWork = WM_USER + 1; //----------------------------------------------------------------------------- // MessagePumpWin public: -void MessagePumpWin::AddObserver(MessagePumpObserver* observer) { - observers_.AddObserver(observer); -} - -void MessagePumpWin::RemoveObserver(MessagePumpObserver* observer) { - observers_.RemoveObserver(observer); -} - -void MessagePumpWin::WillProcessMessage(const MSG& msg) { - FOR_EACH_OBSERVER(MessagePumpObserver, observers_, WillProcessEvent(msg)); -} - -void MessagePumpWin::DidProcessMessage(const MSG& msg) { - FOR_EACH_OBSERVER(MessagePumpObserver, observers_, DidProcessEvent(msg)); -} - void MessagePumpWin::RunWithDispatcher( Delegate* delegate, MessagePumpDispatcher* dispatcher) { RunState s; @@ -369,8 +353,6 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { if (CallMsgFilter(const_cast(&msg), kMessageFilterCode)) return true; - WillProcessMessage(msg); - uint32_t action = MessagePumpDispatcher::POST_DISPATCH_PERFORM_DEFAULT; if (state_->dispatcher) action = state_->dispatcher->Dispatch(msg); @@ -381,7 +363,6 @@ bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { DispatchMessage(&msg); } - DidProcessMessage(msg); return true; } diff --git a/base/message_loop/message_pump_win.h b/base/message_loop/message_pump_win.h index 535a21320e020..b25731742285a 100644 --- a/base/message_loop/message_pump_win.h +++ b/base/message_loop/message_pump_win.h @@ -13,7 +13,6 @@ #include "base/basictypes.h" #include "base/message_loop/message_pump.h" #include "base/message_loop/message_pump_dispatcher.h" -#include "base/message_loop/message_pump_observer.h" #include "base/observer_list.h" #include "base/time/time.h" #include "base/win/scoped_handle.h" @@ -28,18 +27,6 @@ class BASE_EXPORT MessagePumpWin : public MessagePump { MessagePumpWin() : have_work_(0), state_(NULL) {} virtual ~MessagePumpWin() {} - // Add an Observer, which will start receiving notifications immediately. - void AddObserver(MessagePumpObserver* observer); - - // Remove an Observer. It is safe to call this method while an Observer is - // receiving a notification callback. - void RemoveObserver(MessagePumpObserver* observer); - - // Give a chance to code processing additional messages to notify the - // message loop observers that another message has been processed. - void WillProcessMessage(const MSG& msg); - void DidProcessMessage(const MSG& msg); - // Like MessagePump::Run, but MSG objects are routed through dispatcher. void RunWithDispatcher(Delegate* delegate, MessagePumpDispatcher* dispatcher); @@ -62,8 +49,6 @@ class BASE_EXPORT MessagePumpWin : public MessagePump { virtual void DoRunLoop() = 0; int GetCurrentDelay() const; - ObserverList observers_; - // The time at which delayed work should run. TimeTicks delayed_work_time_; diff --git a/base/native_library_mac.mm b/base/native_library_mac.mm index a24586e1ee440..8122c28cf4ae6 100644 --- a/base/native_library_mac.mm +++ b/base/native_library_mac.mm @@ -7,8 +7,8 @@ #include #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/mac/scoped_cftyperef.h" #include "base/strings/string_util.h" diff --git a/base/native_library_win.cc b/base/native_library_win.cc index 43528b977e477..1ca3e92f54768 100644 --- a/base/native_library_win.cc +++ b/base/native_library_win.cc @@ -6,7 +6,7 @@ #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" diff --git a/base/nix/xdg_util.cc b/base/nix/xdg_util.cc index 4086a3d0af18a..e889b5681a035 100644 --- a/base/nix/xdg_util.cc +++ b/base/nix/xdg_util.cc @@ -8,8 +8,8 @@ #include "base/base_paths.h" #include "base/environment.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/path_service.h" #include "base/third_party/xdg_user_dirs/xdg_user_dir_lookup.h" diff --git a/base/numerics/safe_numerics_unittest.cc b/base/numerics/safe_numerics_unittest.cc index 09ad1305a148f..23c2c78a7866c 100644 --- a/base/numerics/safe_numerics_unittest.cc +++ b/base/numerics/safe_numerics_unittest.cc @@ -34,6 +34,13 @@ inline void ResetFloatingPointUnit() { #endif } +// These tests deliberately cause arithmetic overflows. If the compiler is +// aggressive enough, it can const fold these overflows. Disable warnings about +// overflows for const expressions. +#if defined(OS_WIN) +#pragma warning(disable:4756) +#endif + // Helper macros to wrap displaying the conversion types and line numbers. #define TEST_EXPECTED_VALIDITY(expected, actual) \ EXPECT_EQ(expected, CheckedNumeric(actual).validity()) \ diff --git a/base/os_compat_android_unittest.cc b/base/os_compat_android_unittest.cc index a1d1fb19c91b5..7fbdc6dace63d 100644 --- a/base/os_compat_android_unittest.cc +++ b/base/os_compat_android_unittest.cc @@ -4,7 +4,7 @@ #include "base/os_compat_android.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { diff --git a/base/path_service.cc b/base/path_service.cc index 75833db9a111b..ce4966ed70916 100644 --- a/base/path_service.cc +++ b/base/path_service.cc @@ -11,8 +11,8 @@ #endif #include "base/containers/hash_tables.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/synchronization/lock.h" diff --git a/base/path_service_unittest.cc b/base/path_service_unittest.cc index 7a70a89c41a97..c6cc0e6739c6d 100644 --- a/base/path_service_unittest.cc +++ b/base/path_service_unittest.cc @@ -5,8 +5,8 @@ #include "base/path_service.h" #include "base/basictypes.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/strings/string_util.h" #include "build/build_config.h" @@ -15,10 +15,7 @@ #include "testing/platform_test.h" #if defined(OS_WIN) -#include #include "base/win/windows_version.h" -// userenv.dll is required for GetDefaultUserProfileDirectory(). -#pragma comment(lib, "userenv.lib") #endif namespace { @@ -45,18 +42,7 @@ bool ReturnsValidPath(int dir_type) { check_path_exists = false; #endif #if defined(OS_WIN) - if (dir_type == base::DIR_DEFAULT_USER_QUICK_LAUNCH) { - // On Windows XP, the Quick Launch folder for the "Default User" doesn't - // exist by default. At least confirm that the path returned begins with the - // Default User's profile path. - if (base::win::GetVersion() < base::win::VERSION_VISTA) { - wchar_t default_profile_path[MAX_PATH]; - DWORD size = arraysize(default_profile_path); - return (result && - ::GetDefaultUserProfileDirectory(default_profile_path, &size) && - StartsWith(path.value(), default_profile_path, false)); - } - } else if (dir_type == base::DIR_TASKBAR_PINS) { + if (dir_type == base::DIR_TASKBAR_PINS) { // There is no pinned-to-taskbar shortcuts prior to Win7. if (base::win::GetVersion() < base::win::VERSION_WIN7) check_path_exists = false; diff --git a/base/pending_task.cc b/base/pending_task.cc index b288f28d09f8a..3d7891414c8dc 100644 --- a/base/pending_task.cc +++ b/base/pending_task.cc @@ -8,19 +8,14 @@ namespace base { -#if _MSC_VER >= 1700 -// This a temporary fix for compiling on VS2012. http://crbug.com/154744 -PendingTask::PendingTask() : sequence_num(-1), nestable(false) { -} -#endif - PendingTask::PendingTask(const tracked_objects::Location& posted_from, const base::Closure& task) : base::TrackingInfo(posted_from, TimeTicks()), task(task), posted_from(posted_from), sequence_num(0), - nestable(true) { + nestable(true), + is_high_res(false) { } PendingTask::PendingTask(const tracked_objects::Location& posted_from, @@ -31,7 +26,8 @@ PendingTask::PendingTask(const tracked_objects::Location& posted_from, task(task), posted_from(posted_from), sequence_num(0), - nestable(nestable) { + nestable(nestable), + is_high_res(false) { } PendingTask::~PendingTask() { diff --git a/base/pending_task.h b/base/pending_task.h index 65e17f8d8cb57..a2edc6947d09f 100644 --- a/base/pending_task.h +++ b/base/pending_task.h @@ -18,9 +18,6 @@ namespace base { // Contains data about a pending task. Stored in TaskQueue and DelayedTaskQueue // for use by classes that queue and execute tasks. struct BASE_EXPORT PendingTask : public TrackingInfo { -#if _MSC_VER >= 1700 - PendingTask(); -#endif PendingTask(const tracked_objects::Location& posted_from, const Closure& task); PendingTask(const tracked_objects::Location& posted_from, @@ -43,6 +40,9 @@ struct BASE_EXPORT PendingTask : public TrackingInfo { // OK to dispatch from a nested loop. bool nestable; + + // Needs high resolution timers. + bool is_high_res; }; // Wrapper around std::queue specialized for PendingTask which adds a Swap diff --git a/base/posix/unix_domain_socket_linux_unittest.cc b/base/posix/unix_domain_socket_linux_unittest.cc index 7b2e2af23328f..60b2bb8a47070 100644 --- a/base/posix/unix_domain_socket_linux_unittest.cc +++ b/base/posix/unix_domain_socket_linux_unittest.cc @@ -8,7 +8,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/memory/scoped_vector.h" #include "base/pickle.h" diff --git a/base/prefs/json_pref_store.cc b/base/prefs/json_pref_store.cc index 5275ce101f349..bc12e8d8b2eb1 100644 --- a/base/prefs/json_pref_store.cc +++ b/base/prefs/json_pref_store.cc @@ -8,8 +8,8 @@ #include "base/bind.h" #include "base/callback.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/json/json_file_value_serializer.h" #include "base/json/json_string_value_serializer.h" #include "base/memory/ref_counted.h" diff --git a/base/prefs/json_pref_store_unittest.cc b/base/prefs/json_pref_store_unittest.cc index 441c22932986d..d09d9d3926cac 100644 --- a/base/prefs/json_pref_store_unittest.cc +++ b/base/prefs/json_pref_store_unittest.cc @@ -5,7 +5,7 @@ #include "base/prefs/json_pref_store.h" #include "base/bind.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" diff --git a/base/process/internal_linux.cc b/base/process/internal_linux.cc index 57a3ab4271a87..ed7d7f4cf5e1d 100644 --- a/base/process/internal_linux.cc +++ b/base/process/internal_linux.cc @@ -10,7 +10,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" diff --git a/base/process/kill_mac.cc b/base/process/kill_mac.cc index 1589a641a0484..9257ee6929a44 100644 --- a/base/process/kill_mac.cc +++ b/base/process/kill_mac.cc @@ -9,7 +9,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" diff --git a/base/process/kill_posix.cc b/base/process/kill_posix.cc index 18c14fd3a77c6..adc1db31c31e7 100644 --- a/base/process/kill_posix.cc +++ b/base/process/kill_posix.cc @@ -9,7 +9,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" diff --git a/base/process/launch_posix.cc b/base/process/launch_posix.cc index 78a3be47eb04b..bc6294b2dad1b 100644 --- a/base/process/launch_posix.cc +++ b/base/process/launch_posix.cc @@ -24,8 +24,8 @@ #include "base/compiler_specific.h" #include "base/debug/debugger.h" #include "base/debug/stack_trace.h" -#include "base/file_util.h" #include "base/files/dir_reader_posix.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" diff --git a/base/process/memory_linux.cc b/base/process/memory_linux.cc index befd832d66734..6dbe8b7b3948f 100644 --- a/base/process/memory_linux.cc +++ b/base/process/memory_linux.cc @@ -6,8 +6,8 @@ #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/process/internal_linux.h" #include "base/strings/string_number_conversions.h" diff --git a/base/process/process_handle_linux.cc b/base/process/process_handle_linux.cc index 6c5cd2fa182b2..950b888cfaa11 100644 --- a/base/process/process_handle_linux.cc +++ b/base/process/process_handle_linux.cc @@ -4,7 +4,7 @@ #include "base/process/process_handle.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/process/internal_linux.h" namespace base { diff --git a/base/process/process_iterator_linux.cc b/base/process/process_iterator_linux.cc index 8f746aa3391ea..3319552f38de1 100644 --- a/base/process/process_iterator_linux.cc +++ b/base/process/process_iterator_linux.cc @@ -4,7 +4,7 @@ #include "base/process/process_iterator.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/process/internal_linux.h" #include "base/strings/string_util.h" diff --git a/base/process/process_linux.cc b/base/process/process_linux.cc index d92d7c30eee75..2c22d26484294 100644 --- a/base/process/process_linux.cc +++ b/base/process/process_linux.cc @@ -7,7 +7,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/strings/string_split.h" diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc index 0e12595f92efd..c472d73782c0d 100644 --- a/base/process/process_metrics_linux.cc +++ b/base/process/process_metrics_linux.cc @@ -11,7 +11,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/process/internal_linux.h" #include "base/strings/string_number_conversions.h" diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc index 0bf8b9b0cf7c7..69f5e837cb98b 100644 --- a/base/process/process_metrics_unittest.cc +++ b/base/process/process_metrics_unittest.cc @@ -329,7 +329,8 @@ TEST(ProcessMetricsTest, ParseProcStatCPU) { // Disable on Android because base_unittests runs inside a Dalvik VM that // starts and stop threads (crbug.com/175563). #if defined(OS_LINUX) -TEST(ProcessMetricsTest, GetNumberOfThreads) { +// http://crbug.com/396455 +TEST(ProcessMetricsTest, DISABLED_GetNumberOfThreads) { const base::ProcessHandle current = base::GetCurrentProcessHandle(); const int initial_threads = base::GetNumberOfThreads(current); ASSERT_GT(initial_threads, 0); diff --git a/base/rand_util_posix.cc b/base/rand_util_posix.cc index 0a72a20d64209..fe73b960f9482 100644 --- a/base/rand_util_posix.cc +++ b/base/rand_util_posix.cc @@ -8,7 +8,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" diff --git a/base/security_unittest.cc b/base/security_unittest.cc index 960bc20276c70..0ecaec415412e 100644 --- a/base/security_unittest.cc +++ b/base/security_unittest.cc @@ -12,7 +12,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "build/build_config.h" diff --git a/base/strings/stringprintf.h b/base/strings/stringprintf.h index 3c0e399c0a797..d924a0e59f217 100644 --- a/base/strings/stringprintf.h +++ b/base/strings/stringprintf.h @@ -16,16 +16,16 @@ namespace base { // Return a C++ string given printf-like input. BASE_EXPORT std::string StringPrintf(const char* format, ...) - PRINTF_FORMAT(1, 2); + PRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; // OS_ANDROID's libc does not support wchar_t, so several overloads are omitted. #if !defined(OS_ANDROID) BASE_EXPORT std::wstring StringPrintf(const wchar_t* format, ...) - WPRINTF_FORMAT(1, 2); + WPRINTF_FORMAT(1, 2) WARN_UNUSED_RESULT; #endif // Return a C++ string given vprintf-like input. BASE_EXPORT std::string StringPrintV(const char* format, va_list ap) - PRINTF_FORMAT(1, 0); + PRINTF_FORMAT(1, 0) WARN_UNUSED_RESULT; // Store result into a supplied string and return it. BASE_EXPORT const std::string& SStringPrintf(std::string* dst, diff --git a/base/sync_socket_posix.cc b/base/sync_socket_posix.cc index 6d397ffa2ec99..93a01d1b1c206 100644 --- a/base/sync_socket_posix.cc +++ b/base/sync_socket_posix.cc @@ -16,7 +16,7 @@ #include #endif -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/threading/thread_restrictions.h" diff --git a/base/sys_info_chromeos.cc b/base/sys_info_chromeos.cc index 4f32f9bfe63c2..d3a1b2c22069f 100644 --- a/base/sys_info_chromeos.cc +++ b/base/sys_info_chromeos.cc @@ -6,9 +6,9 @@ #include "base/basictypes.h" #include "base/environment.h" -#include "base/file_util.h" #include "base/files/file.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_piece.h" diff --git a/base/sys_info_linux.cc b/base/sys_info_linux.cc index 6f1e5eb7d1fea..2e679ed39f3a5 100644 --- a/base/sys_info_linux.cc +++ b/base/sys_info_linux.cc @@ -6,7 +6,7 @@ #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" @@ -36,12 +36,11 @@ size_t MaxSharedMemorySize() { contents.erase(contents.length() - 1); } - int64 limit; - if (!base::StringToInt64(contents, &limit)) { + uint64 limit; + if (!base::StringToUint64(contents, &limit)) { limit = 0; } - if (limit < 0 || - static_cast(limit) > std::numeric_limits::max()) { + if (limit > std::numeric_limits::max()) { limit = 0; } DCHECK(limit > 0); diff --git a/base/sys_info_posix.cc b/base/sys_info_posix.cc index 90baa69a2f61e..c201ae1fe11bf 100644 --- a/base/sys_info_posix.cc +++ b/base/sys_info_posix.cc @@ -12,7 +12,7 @@ #include #include "base/basictypes.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" diff --git a/base/sys_info_unittest.cc b/base/sys_info_unittest.cc index e771b2b9879a4..bfd822470af75 100644 --- a/base/sys_info_unittest.cc +++ b/base/sys_info_unittest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/environment.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/sys_info.h" #include "base/threading/platform_thread.h" #include "base/time/time.h" diff --git a/base/test/gtest_xml_util.cc b/base/test/gtest_xml_util.cc index f52c2f6bfa2b9..8a153dc8cc790 100644 --- a/base/test/gtest_xml_util.cc +++ b/base/test/gtest_xml_util.cc @@ -4,7 +4,7 @@ #include "base/test/gtest_xml_util.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/stringprintf.h" #include "base/test/launcher/test_launcher.h" diff --git a/base/test/launcher/test_launcher.cc b/base/test/launcher/test_launcher.cc index ca60cbacea0da..9150e636c3c0b 100644 --- a/base/test/launcher/test_launcher.cc +++ b/base/test/launcher/test_launcher.cc @@ -12,8 +12,8 @@ #include "base/bind.h" #include "base/command_line.h" #include "base/environment.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/format_macros.h" #include "base/hash.h" @@ -41,6 +41,10 @@ #include "base/mac/scoped_nsautorelease_pool.h" #endif +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif + namespace base { // Launches a child process using |command_line|. If the child process is still @@ -48,7 +52,7 @@ namespace base { // Returns exit code of the process. int LaunchChildTestProcessWithOptions(const CommandLine& command_line, const LaunchOptions& options, - bool use_job_objects, + int flags, base::TimeDelta timeout, bool* was_timeout); @@ -223,7 +227,7 @@ void RunCallback( void DoLaunchChildTestProcess( const CommandLine& command_line, base::TimeDelta timeout, - bool use_job_objects, + int flags, bool redirect_stdio, scoped_refptr message_loop_proxy, const TestLauncher::LaunchChildGTestProcessCallback& callback) { @@ -275,7 +279,7 @@ void DoLaunchChildTestProcess( bool was_timeout = false; int exit_code = LaunchChildTestProcessWithOptions( - command_line, options, use_job_objects, timeout, &was_timeout); + command_line, options, flags, timeout, &was_timeout); if (redirect_stdio) { #if defined(OS_WIN) @@ -336,20 +340,6 @@ TestLauncher::TestLauncher(TestLauncherDelegate* launcher_delegate, this, &TestLauncher::OnOutputTimeout), parallel_jobs_(parallel_jobs) { - if (BotModeEnabled()) { - fprintf(stdout, - "Enabling defaults optimized for continuous integration bots.\n"); - fflush(stdout); - - // Enable test retries by default for bots. This can be still overridden - // from command line using --test-launcher-retry-limit flag. - retry_limit_ = 3; - } else { - // Default to serial test execution if not running on a bot. This makes it - // possible to disable stdio redirection and can still be overridden with - // --test-launcher-jobs flag. - parallel_jobs_ = 1; - } } TestLauncher::~TestLauncher() { @@ -409,7 +399,7 @@ void TestLauncher::LaunchChildGTestProcess( const CommandLine& command_line, const std::string& wrapper, base::TimeDelta timeout, - bool use_job_objects, + int flags, const LaunchChildGTestProcessCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); @@ -427,7 +417,7 @@ void TestLauncher::LaunchChildGTestProcess( Bind(&DoLaunchChildTestProcess, new_command_line, timeout, - use_job_objects, + flags, redirect_stdio, MessageLoopProxy::current(), Bind(&TestLauncher::OnLaunchTestProcessFinished, @@ -650,6 +640,9 @@ bool TestLauncher::Init() { } retry_limit_ = retry_limit; + } else if (!CommandLine::ForCurrentProcess()->HasSwitch(kGTestFilterFlag)) { + // Retry failures 3 times by default if we are running all of the tests. + retry_limit_ = 3; } if (CommandLine::ForCurrentProcess()->HasSwitch( @@ -663,7 +656,12 @@ bool TestLauncher::Init() { } parallel_jobs_ = jobs; + } else if (CommandLine::ForCurrentProcess()->HasSwitch(kGTestFilterFlag)) { + // Do not run jobs in parallel by default if we are running a subset of + // the tests. + parallel_jobs_ = 1; } + fprintf(stdout, "Using %" PRIuS " parallel jobs.\n", parallel_jobs_); fflush(stdout); worker_pool_owner_.reset( @@ -805,6 +803,9 @@ void TestLauncher::RunTests() { continue; } + if (!launcher_delegate_->ShouldRunTest(test_case, test_info)) + continue; + // Skip the test that doesn't match the filter (if given). if (!positive_test_filter_.empty()) { bool found = false; @@ -828,9 +829,6 @@ void TestLauncher::RunTests() { if (excluded) continue; - if (!launcher_delegate_->ShouldRunTest(test_case, test_info)) - continue; - if (base::Hash(test_name) % total_shards_ != static_cast(shard_index_)) { continue; @@ -1009,7 +1007,7 @@ CommandLine PrepareCommandLineForGTest(const CommandLine& command_line, // TODO(phajdan.jr): Move to anonymous namespace. int LaunchChildTestProcessWithOptions(const CommandLine& command_line, const LaunchOptions& options, - bool use_job_objects, + int flags, base::TimeDelta timeout, bool* was_timeout) { #if defined(OS_POSIX) @@ -1023,19 +1021,23 @@ int LaunchChildTestProcessWithOptions(const CommandLine& command_line, DCHECK(!new_options.job_handle); win::ScopedHandle job_handle; - if (use_job_objects) { + if (flags & TestLauncher::USE_JOB_OBJECTS) { job_handle.Set(CreateJobObject(NULL, NULL)); if (!job_handle.IsValid()) { LOG(ERROR) << "Could not create JobObject."; return -1; } + DWORD job_flags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE; + // Allow break-away from job since sandbox and few other places rely on it // on Windows versions prior to Windows 8 (which supports nested jobs). - // TODO(phajdan.jr): Do not allow break-away on Windows 8. - if (!SetJobObjectLimitFlags(job_handle.Get(), - JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | - JOB_OBJECT_LIMIT_BREAKAWAY_OK)) { + if (win::GetVersion() < win::VERSION_WIN8 && + flags & TestLauncher::ALLOW_BREAKAWAY_FROM_JOB) { + job_flags |= JOB_OBJECT_LIMIT_BREAKAWAY_OK; + } + + if (!SetJobObjectLimitFlags(job_handle.Get(), job_flags)) { LOG(ERROR) << "Could not SetJobObjectLimitFlags."; return -1; } diff --git a/base/test/launcher/test_launcher.h b/base/test/launcher/test_launcher.h index d50bf2fb3b52b..1837aad1b725a 100644 --- a/base/test/launcher/test_launcher.h +++ b/base/test/launcher/test_launcher.h @@ -69,6 +69,18 @@ class TestLauncherDelegate { // Launches tests using a TestLauncherDelegate. class TestLauncher { public: + // Flags controlling behavior of LaunchChildGTestProcess. + enum LaunchChildGTestProcessFlags { + // Allows usage of job objects on Windows. Helps properly clean up child + // processes. + USE_JOB_OBJECTS = (1 << 0), + + // Allows breakaway from job on Windows. May result in some child processes + // not being properly terminated after launcher dies if these processes + // fail to cooperate. + ALLOW_BREAKAWAY_FROM_JOB = (1 << 1), + }; + // Constructor. |parallel_jobs| is the limit of simultaneous parallel test // jobs. TestLauncher(TestLauncherDelegate* launcher_delegate, size_t parallel_jobs); @@ -87,13 +99,12 @@ class TestLauncher { // Launches a child process (assumed to be gtest-based binary) using // |command_line|. If |wrapper| is not empty, it is prepended to the final // command line. If the child process is still running after |timeout|, it - // is terminated. |use_job_objects| determines whether job objects are used - // on Windows (if unsure pass true). After the child process finishes - // |callback| is called on the same thread this method was called. + // is terminated. After the child process finishes |callback| is called + // on the same thread this method was called. void LaunchChildGTestProcess(const CommandLine& command_line, const std::string& wrapper, base::TimeDelta timeout, - bool use_job_objects, + int flags, const LaunchChildGTestProcessCallback& callback); // Called when a test has finished running. diff --git a/base/test/launcher/test_results_tracker.cc b/base/test/launcher/test_results_tracker.cc index fb2e550c7f5a4..1e3b2b059470c 100644 --- a/base/test/launcher/test_results_tracker.cc +++ b/base/test/launcher/test_results_tracker.cc @@ -6,8 +6,8 @@ #include "base/base64.h" #include "base/command_line.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/format_macros.h" #include "base/json/json_file_value_serializer.h" #include "base/json/string_escape.h" diff --git a/base/test/launcher/unit_test_launcher.cc b/base/test/launcher/unit_test_launcher.cc index 87784d96647fb..0cbae2fac366d 100644 --- a/base/test/launcher/unit_test_launcher.cc +++ b/base/test/launcher/unit_test_launcher.cc @@ -9,7 +9,7 @@ #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/debug/debugger.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/format_macros.h" #include "base/message_loop/message_loop.h" @@ -187,7 +187,7 @@ class UnitTestLauncherDelegate : public TestLauncherDelegate { cmd_line, std::string(), TestTimeouts::test_launcher_timeout(), - use_job_objects_, + use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, Bind(&UnitTestLauncherDelegate::SerialGTestCallback, Unretained(this), callback_state, @@ -229,7 +229,7 @@ class UnitTestLauncherDelegate : public TestLauncherDelegate { cmd_line, std::string(), timeout, - use_job_objects_, + use_job_objects_ ? TestLauncher::USE_JOB_OBJECTS : 0, Bind(&UnitTestLauncherDelegate::GTestCallback, Unretained(this), callback_state)); diff --git a/base/test/perf_log.cc b/base/test/perf_log.cc index 5d5027dbae974..22884b8e69b09 100644 --- a/base/test/perf_log.cc +++ b/base/test/perf_log.cc @@ -4,7 +4,7 @@ #include "base/test/perf_log.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/logging.h" namespace base { diff --git a/base/test/test_file_util_mac.cc b/base/test/test_file_util_mac.cc index a4ed1d3764b0a..11592c31854f2 100644 --- a/base/test/test_file_util_mac.cc +++ b/base/test/test_file_util_mac.cc @@ -7,9 +7,9 @@ #include #include -#include "base/logging.h" -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/files/memory_mapped_file.h" +#include "base/logging.h" namespace base { diff --git a/base/test/test_file_util_posix.cc b/base/test/test_file_util_posix.cc index cbcabd65eddd9..12b892c826bf1 100644 --- a/base/test/test_file_util_posix.cc +++ b/base/test/test_file_util_posix.cc @@ -11,8 +11,8 @@ #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" diff --git a/base/test/test_file_util_win.cc b/base/test/test_file_util_win.cc index 2656e31255c11..94eb3ef66ebee 100644 --- a/base/test/test_file_util_win.cc +++ b/base/test/test_file_util_win.cc @@ -10,8 +10,8 @@ #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/strings/string_split.h" #include "base/threading/platform_thread.h" diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc index 5777c5da879ec..f12965fc58639 100644 --- a/base/test/test_suite.cc +++ b/base/test/test_suite.cc @@ -11,8 +11,8 @@ #include "base/command_line.h" #include "base/debug/debugger.h" #include "base/debug/stack_trace.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/i18n/icu_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" diff --git a/base/threading/thread_local.h b/base/threading/thread_local.h index df9c4b72573be..b6bfb8a2dd9d5 100644 --- a/base/threading/thread_local.h +++ b/base/threading/thread_local.h @@ -80,6 +80,7 @@ struct BASE_EXPORT ThreadLocalPlatform { } // namespace internal +#if !defined(OS_ANDROID) template class ThreadLocalPointer { public: @@ -108,6 +109,28 @@ class ThreadLocalPointer { DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer); }; +#else +template + class ThreadLocalPointer { + public: + ThreadLocalPointer() {} + + ~ThreadLocalPointer() { slot_.Free(); } + + Type* Get() { + return static_cast(slot_.Get()); + } + + void Set(Type* ptr) { + slot_.Set(const_cast(static_cast(ptr))); + } + + private: + ThreadLocalStorage::Slot slot_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer); +}; +#endif // !OS_ANDROID class ThreadLocalBoolean { public: diff --git a/base/time/time.h b/base/time/time.h index 79346f1f0569c..084ff4553d7dd 100644 --- a/base/time/time.h +++ b/base/time/time.h @@ -340,13 +340,7 @@ class BASE_EXPORT Time { // treat it as static across all windows versions. static const int kMinLowResolutionThresholdMs = 16; - // Enable or disable Windows high resolution timer. If the high resolution - // timer is not enabled, calls to ActivateHighResolutionTimer will fail. - // When disabling the high resolution timer, this function will not cause - // the high resolution timer to be deactivated, but will prevent future - // activations. - // Must be called from the main thread. - // For more details see comments in time_win.cc. + // Enable or disable Windows high resolution timer. static void EnableHighResolutionTimer(bool enable); // Activates or deactivates the high resolution timer based on the |activate| @@ -493,16 +487,6 @@ class BASE_EXPORT Time { // platform-dependent epoch. static const int64 kTimeTToMicrosecondsOffset; -#if defined(OS_WIN) - // Indicates whether fast timers are usable right now. For instance, - // when using battery power, we might elect to prevent high speed timers - // which would draw more power. - static bool high_resolution_timer_enabled_; - // Count of activations on the high resolution timer. Only use in tests - // which are single threaded. - static int high_resolution_timer_activated_; -#endif - // Time in microseconds in UTC. int64 us_; }; diff --git a/base/time/time_posix.cc b/base/time/time_posix.cc index b5b8213846dbb..dcbd83cd54e4f 100644 --- a/base/time/time_posix.cc +++ b/base/time/time_posix.cc @@ -26,9 +26,19 @@ #include "base/os_compat_nacl.h" #endif +#if !defined(OS_MACOSX) +#include "base/lazy_instance.h" +#include "base/synchronization/lock.h" +#endif + namespace { #if !defined(OS_MACOSX) +// This prevents a crash on traversing the environment global and looking up +// the 'TZ' variable in libc. See: crbug.com/390567. +base::LazyInstance::Leaky + g_sys_time_to_time_struct_lock = LAZY_INSTANCE_INITIALIZER; + // Define a system-specific SysTime that wraps either to a time_t or // a time64_t depending on the host system, and associated convertion. // See crbug.com/162007 @@ -36,6 +46,7 @@ namespace { typedef time64_t SysTime; SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { + base::AutoLock locked(g_sys_time_to_time_struct_lock.Get()); if (is_local) return mktime64(timestruct); else @@ -43,6 +54,7 @@ SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { } void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) { + base::AutoLock locked(g_sys_time_to_time_struct_lock.Get()); if (is_local) localtime64_r(&t, timestruct); else @@ -53,6 +65,7 @@ void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) { typedef time_t SysTime; SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { + base::AutoLock locked(g_sys_time_to_time_struct_lock.Get()); if (is_local) return mktime(timestruct); else @@ -60,6 +73,7 @@ SysTime SysTimeFromTimeStruct(struct tm* timestruct, bool is_local) { } void SysTimeToTimeStruct(SysTime t, struct tm* timestruct, bool is_local) { + base::AutoLock locked(g_sys_time_to_time_struct_lock.Get()); if (is_local) localtime_r(&t, timestruct); else diff --git a/base/time/time_win.cc b/base/time/time_win.cc index 5fa899d1f97f6..2586cb318a4a1 100644 --- a/base/time/time_win.cc +++ b/base/time/time_win.cc @@ -29,10 +29,7 @@ // // To work around all this, we're going to generally use timeGetTime(). We // will only increase the system-wide timer if we're not running on battery -// power. Using timeBeginPeriod(1) is a requirement in order to make our -// message loop waits have the same resolution that our time measurements -// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when -// there is nothing else to waken the Wait. +// power. #include "base/time/time.h" @@ -87,6 +84,19 @@ void InitializeClock() { initial_time = CurrentWallclockMicroseconds(); } +// The two values that ActivateHighResolutionTimer uses to set the systemwide +// timer interrupt frequency on Windows. It controls how precise timers are +// but also has a big impact on battery life. +const int kMinTimerIntervalHighResMs = 1; +const int kMinTimerIntervalLowResMs = 4; +// Track if kMinTimerIntervalHighResMs or kMinTimerIntervalLowResMs is active. +bool g_high_res_timer_enabled = false; +// How many times the high resolution timer has been called. +uint32_t g_high_res_timer_count = 0; +// The lock to control access to the above two variables. +base::LazyInstance::Leaky g_high_res_lock = + LAZY_INSTANCE_INITIALIZER; + } // namespace // Time ----------------------------------------------------------------------- @@ -98,9 +108,6 @@ void InitializeClock() { // static const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); -bool Time::high_resolution_timer_enabled_ = false; -int Time::high_resolution_timer_activated_ = 0; - // static Time Time::Now() { if (initial_time == 0) @@ -165,44 +172,54 @@ FILETIME Time::ToFileTime() const { // static void Time::EnableHighResolutionTimer(bool enable) { - // Test for single-threaded access. - static PlatformThreadId my_thread = PlatformThread::CurrentId(); - DCHECK(PlatformThread::CurrentId() == my_thread); - - if (high_resolution_timer_enabled_ == enable) + base::AutoLock lock(g_high_res_lock.Get()); + if (g_high_res_timer_enabled == enable) return; - - high_resolution_timer_enabled_ = enable; + g_high_res_timer_enabled = enable; + if (!g_high_res_timer_count) + return; + // Since g_high_res_timer_count != 0, an ActivateHighResolutionTimer(true) + // was called which called timeBeginPeriod with g_high_res_timer_enabled + // with a value which is the opposite of |enable|. With that information we + // call timeEndPeriod with the same value used in timeBeginPeriod and + // therefore undo the period effect. + if (enable) { + timeEndPeriod(kMinTimerIntervalLowResMs); + timeBeginPeriod(kMinTimerIntervalHighResMs); + } else { + timeEndPeriod(kMinTimerIntervalHighResMs); + timeBeginPeriod(kMinTimerIntervalLowResMs); + } } // static bool Time::ActivateHighResolutionTimer(bool activating) { - if (!high_resolution_timer_enabled_ && activating) - return false; - - // Using anything other than 1ms makes timers granular - // to that interval. - const int kMinTimerIntervalMs = 1; - MMRESULT result; + // We only do work on the transition from zero to one or one to zero so we + // can easily undo the effect (if necessary) when EnableHighResolutionTimer is + // called. + const uint32_t max = std::numeric_limits::max(); + + base::AutoLock lock(g_high_res_lock.Get()); + UINT period = g_high_res_timer_enabled ? kMinTimerIntervalHighResMs + : kMinTimerIntervalLowResMs; if (activating) { - result = timeBeginPeriod(kMinTimerIntervalMs); - high_resolution_timer_activated_++; + DCHECK(g_high_res_timer_count != max); + ++g_high_res_timer_count; + if (g_high_res_timer_count == 1) + timeBeginPeriod(period); } else { - result = timeEndPeriod(kMinTimerIntervalMs); - high_resolution_timer_activated_--; + DCHECK(g_high_res_timer_count != 0); + --g_high_res_timer_count; + if (g_high_res_timer_count == 0) + timeEndPeriod(period); } - return result == TIMERR_NOERROR; + return (period == kMinTimerIntervalHighResMs); } // static bool Time::IsHighResolutionTimerInUse() { - // Note: we should track the high_resolution_timer_activated_ value - // under a lock if we want it to be accurate in a system with multiple - // message loops. We don't do that - because we don't want to take the - // expense of a lock for this. We *only* track this value so that unit - // tests can see if the high resolution timer is on or off. - return high_resolution_timer_enabled_ && - high_resolution_timer_activated_ > 0; + base::AutoLock lock(g_high_res_lock.Get()); + return g_high_res_timer_enabled && g_high_res_timer_count > 0; } // static diff --git a/base/timer/hi_res_timer_manager_unittest.cc b/base/timer/hi_res_timer_manager_unittest.cc index ce10e3786d70e..5475a91446bde 100644 --- a/base/timer/hi_res_timer_manager_unittest.cc +++ b/base/timer/hi_res_timer_manager_unittest.cc @@ -14,25 +14,22 @@ namespace base { #if defined(OS_WIN) -// http://crbug.com/114048 -TEST(HiResTimerManagerTest, DISABLED_ToggleOnOff) { - base::MessageLoop loop; +TEST(HiResTimerManagerTest, ToggleOnOff) { + // The power monitor creates Window to receive power notifications from + // Windows, which makes this test flaky if you run while the machine + // goes in or out of AC power. + base::MessageLoop loop(base::MessageLoop::TYPE_UI); scoped_ptr power_monitor_source( new base::PowerMonitorDeviceSource()); scoped_ptr power_monitor( new base::PowerMonitor(power_monitor_source.Pass())); - HighResolutionTimerManager manager; - // At this point, we don't know if the high resolution timers are on or off, - // it depends on what system the tests are running on (for example, if this - // test is running on a laptop/battery, then the PowerMonitor would have - // already set the PowerState to battery power; but if we're running on a - // desktop, then the PowerState will be non-battery power). Simulate a power - // level change to get to a deterministic state. - manager.OnPowerStateChange(/* on_battery */ false); + HighResolutionTimerManager manager; + // Simulate a on-AC power event to get to a known initial state. + manager.OnPowerStateChange(false); // Loop a few times to test power toggling. - for (int loop = 2; loop >= 0; --loop) { + for (int times = 0; times != 3; ++times) { // The manager has the high resolution clock enabled now. EXPECT_TRUE(manager.hi_res_clock_available()); // But the Time class has it off, because it hasn't been activated. @@ -43,12 +40,12 @@ TEST(HiResTimerManagerTest, DISABLED_ToggleOnOff) { EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse()); // Simulate a on-battery power event. - manager.OnPowerStateChange(/* on_battery */ true); + manager.OnPowerStateChange(true); EXPECT_FALSE(manager.hi_res_clock_available()); EXPECT_FALSE(base::Time::IsHighResolutionTimerInUse()); - // Simulate a off-battery power event. - manager.OnPowerStateChange(/* on_battery */ false); + // Back to on-AC power. + manager.OnPowerStateChange(false); EXPECT_TRUE(manager.hi_res_clock_available()); EXPECT_TRUE(base::Time::IsHighResolutionTimerInUse()); diff --git a/base/win/event_trace_consumer_unittest.cc b/base/win/event_trace_consumer_unittest.cc index 8544f5e414b7f..3231240877ef9 100644 --- a/base/win/event_trace_consumer_unittest.cc +++ b/base/win/event_trace_consumer_unittest.cc @@ -10,8 +10,8 @@ #include #include "base/basictypes.h" -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/process/process.h" diff --git a/base/win/event_trace_controller_unittest.cc b/base/win/event_trace_controller_unittest.cc index 4d23eddc3bbd6..8ca9cf5e25b2f 100644 --- a/base/win/event_trace_controller_unittest.cc +++ b/base/win/event_trace_controller_unittest.cc @@ -7,8 +7,8 @@ #include #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/process/process.h" diff --git a/base/win/scoped_handle.h b/base/win/scoped_handle.h index 00063afe3526f..a85e08d26e5f2 100644 --- a/base/win/scoped_handle.h +++ b/base/win/scoped_handle.h @@ -166,7 +166,7 @@ class BASE_EXPORT VerifierTraits { DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits); }; -typedef GenericScopedHandle ScopedHandle; +typedef GenericScopedHandle ScopedHandle; } // namespace win } // namespace base diff --git a/base/win/shortcut.cc b/base/win/shortcut.cc index 7dace5988d03c..f56db9fb00517 100644 --- a/base/win/shortcut.cc +++ b/base/win/shortcut.cc @@ -8,7 +8,7 @@ #include #include -#include "base/file_util.h" +#include "base/files/file_util.h" #include "base/threading/thread_restrictions.h" #include "base/win/scoped_comptr.h" #include "base/win/scoped_propvariant.h" diff --git a/base/win/shortcut_unittest.cc b/base/win/shortcut_unittest.cc index 7a6f41dae92fc..53fbd3460e41c 100644 --- a/base/win/shortcut_unittest.cc +++ b/base/win/shortcut_unittest.cc @@ -6,8 +6,8 @@ #include -#include "base/file_util.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/test/test_file_util.h" #include "base/test/test_shortcut_win.h" diff --git a/breakpad/BUILD.gn b/breakpad/BUILD.gn index 0ae5a2b6934f2..97472124845b4 100644 --- a/breakpad/BUILD.gn +++ b/breakpad/BUILD.gn @@ -324,61 +324,64 @@ if (is_linux || is_android) { } } -if (is_linux && current_toolchain == host_toolchain) { - executable("dump_syms") { - sources = [ - "src/common/dwarf/bytereader.cc", - "src/common/dwarf_cfi_to_module.cc", - "src/common/dwarf_cfi_to_module.h", - "src/common/dwarf_cu_to_module.cc", - "src/common/dwarf_cu_to_module.h", - "src/common/dwarf/dwarf2diehandler.cc", - "src/common/dwarf/dwarf2reader.cc", - "src/common/dwarf_line_to_module.cc", - "src/common/dwarf_line_to_module.h", - "src/common/language.cc", - "src/common/language.h", - "src/common/linux/crc32.cc", - "src/common/linux/crc32.h", - "src/common/linux/dump_symbols.cc", - "src/common/linux/dump_symbols.h", - "src/common/linux/elf_symbols_to_module.cc", - "src/common/linux/elf_symbols_to_module.h", - "src/common/linux/elfutils.cc", - "src/common/linux/elfutils.h", - "src/common/linux/file_id.cc", - "src/common/linux/file_id.h", - "src/common/linux/linux_libc_support.cc", - "src/common/linux/linux_libc_support.h", - "src/common/linux/memory_mapped_file.cc", - "src/common/linux/memory_mapped_file.h", - "src/common/linux/guid_creator.h", - "src/common/module.cc", - "src/common/module.h", - "src/common/stabs_reader.cc", - "src/common/stabs_reader.h", - "src/common/stabs_to_module.cc", - "src/common/stabs_to_module.h", - "src/tools/linux/dump_syms/dump_syms.cc", - ] - - # There are some warnings in this code. - configs -= [ "//build/config/compiler:chromium_code" ] - configs += [ "//build/config/compiler:no_chromium_code" ] - - # dwarf2reader.cc uses dynamic_cast. Because we don't typically - # don't support RTTI, we enable it for this single target. Since - # dump_syms doesn't share any object files with anything else, - # this doesn't end up polluting Chrome itself. - configs -= [ "//build/config/compiler:no_rtti" ] - configs += [ "//build/config/compiler:rtti" ] - - # Breakpad rev 583 introduced this flag. - # Using this define, stabs_reader.h will include a.out.h to - # build on Linux. - defines = [ "HAVE_A_OUT_H" ] - - include_dirs = [ "src" ] +if (is_linux) { + if (current_toolchain == host_toolchain) { + # dump_syms is a host tool, so only compile it for the host system. + executable("dump_syms") { + sources = [ + "src/common/dwarf/bytereader.cc", + "src/common/dwarf_cfi_to_module.cc", + "src/common/dwarf_cfi_to_module.h", + "src/common/dwarf_cu_to_module.cc", + "src/common/dwarf_cu_to_module.h", + "src/common/dwarf/dwarf2diehandler.cc", + "src/common/dwarf/dwarf2reader.cc", + "src/common/dwarf_line_to_module.cc", + "src/common/dwarf_line_to_module.h", + "src/common/language.cc", + "src/common/language.h", + "src/common/linux/crc32.cc", + "src/common/linux/crc32.h", + "src/common/linux/dump_symbols.cc", + "src/common/linux/dump_symbols.h", + "src/common/linux/elf_symbols_to_module.cc", + "src/common/linux/elf_symbols_to_module.h", + "src/common/linux/elfutils.cc", + "src/common/linux/elfutils.h", + "src/common/linux/file_id.cc", + "src/common/linux/file_id.h", + "src/common/linux/linux_libc_support.cc", + "src/common/linux/linux_libc_support.h", + "src/common/linux/memory_mapped_file.cc", + "src/common/linux/memory_mapped_file.h", + "src/common/linux/guid_creator.h", + "src/common/module.cc", + "src/common/module.h", + "src/common/stabs_reader.cc", + "src/common/stabs_reader.h", + "src/common/stabs_to_module.cc", + "src/common/stabs_to_module.h", + "src/tools/linux/dump_syms/dump_syms.cc", + ] + + # There are some warnings in this code. + configs -= [ "//build/config/compiler:chromium_code" ] + configs += [ "//build/config/compiler:no_chromium_code" ] + + # dwarf2reader.cc uses dynamic_cast. Because we don't typically + # don't support RTTI, we enable it for this single target. Since + # dump_syms doesn't share any object files with anything else, + # this doesn't end up polluting Chrome itself. + configs -= [ "//build/config/compiler:no_rtti" ] + configs += [ "//build/config/compiler:rtti" ] + + # Breakpad rev 583 introduced this flag. + # Using this define, stabs_reader.h will include a.out.h to + # build on Linux. + defines = [ "HAVE_A_OUT_H" ] + + include_dirs = [ "src" ] + } } static_library("client") { @@ -437,10 +440,6 @@ if (is_linux && current_toolchain == host_toolchain) { configs += [ "//build/config/compiler:no_chromium_code" ] direct_dependent_configs = [ ":client_config" ] - # Android NDK toolchain doesn't support -mimplicit-it=always - if (cpu_arch == "arm" && !is_android) { - cflags = [ "-Wa,-mimplicit-it=always" ] - } if (cpu_arch == "arm" && is_chromeos) { # Avoid running out of registers in # linux_syscall_support.h:sys_clone()'s inline assembly. diff --git a/breakpad/breakpad.gyp b/breakpad/breakpad.gyp index 18a43553074bd..ac6a122f40bbf 100644 --- a/breakpad/breakpad.gyp +++ b/breakpad/breakpad.gyp @@ -501,10 +501,6 @@ ], 'conditions': [ - # Android NDK toolchain doesn't support -mimplicit-it=always - ['target_arch=="arm" and OS!="android"', { - 'cflags': ['-Wa,-mimplicit-it=always'], - }], ['target_arch=="arm" and chromeos==1', { # Avoid running out of registers in # linux_syscall_support.h:sys_clone()'s inline assembly. diff --git a/build/all.gyp b/build/all.gyp index 96d4ea8cacd73..e4448b4a7f4eb 100644 --- a/build/all.gyp +++ b/build/all.gyp @@ -51,6 +51,7 @@ '<@(android_app_targets)', 'android_builder_tests', '../android_webview/android_webview.gyp:android_webview_apk', + '../android_webview/android_webview_telemetry_shell.gyp:android_webview_telemetry_shell_apk', '../chrome/chrome.gyp:chrome_shell_apk', '../remoting/remoting.gyp:remoting_apk', '../tools/telemetry/telemetry.gyp:*#host', @@ -218,6 +219,7 @@ ['use_openssl==1', { 'dependencies': [ '../third_party/boringssl/boringssl.gyp:*', + '../third_party/boringssl/boringssl_tests.gyp:*', ], }], ['enable_app_list==1', { @@ -407,6 +409,11 @@ '../components/nacl.gyp:nacl_loader_unittests', ], }], + ['disable_nacl==0 and disable_nacl_untrusted==0', { + 'dependencies': [ + '../testing/gtest_nacl.gyp:*', + ], + }], ], }, # target_name: chromium_builder_tests ], diff --git a/build/android/adb_install_apk.py b/build/android/adb_install_apk.py index 5d0fd17149409..ac6e505609873 100755 --- a/build/android/adb_install_apk.py +++ b/build/android/adb_install_apk.py @@ -9,6 +9,7 @@ import optparse import os import sys +import time from pylib import android_commands from pylib import constants @@ -71,13 +72,25 @@ def main(argv): constants.SetBuildType(options.build_type) ValidateInstallAPKOption(parser, options, args) - devices = android_commands.GetAttachedDevices() - - if options.device: - if options.device not in devices: - raise Exception('Error: %s not in attached devices %s' % (options.device, - ','.join(devices))) - devices = [options.device] + retry_times = 5 + retry_interval = 15 + while retry_times > 0: + devices = android_commands.GetAttachedDevices() + if options.device: + if options.device not in devices: + raise Exception('Error: %s not in attached devices %s' % \ + (options.device, ','.join(devices))) + devices = [options.device] + + if not devices: + print 'No connected devices found, '\ + 'kill adb server and retry in %d seconds...' % retry_interval + android_commands.AndroidCommands().KillAdbServer() + time.sleep(retry_interval) + retry_interval *= 2 + retry_times -= 1 + else: + break if not devices: raise Exception('Error: no connected devices') diff --git a/build/android/android_exports.gyp b/build/android/android_exports.gyp new file mode 100644 index 0000000000000..c259eee3756c4 --- /dev/null +++ b/build/android/android_exports.gyp @@ -0,0 +1,39 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'android_exports', + 'type': 'none', + 'inputs': [ + '<(DEPTH)/build/android/android_exports.lst', + ], + 'outputs': [ + '<(android_linker_script)', + ], + 'copies': [ + { + 'destination': '<(SHARED_INTERMEDIATE_DIR)', + 'files': [ + '<@(_inputs)', + ], + }, + ], + 'conditions': [ + ['component=="static_library"', { + 'link_settings': { + 'ldflags': [ + # Only export symbols that are specified in version script. + '-Wl,--version-script=<(android_linker_script)', + ], + 'ldflags!': [ + '-Wl,--exclude-libs=ALL', + ], + }, + }], + ], + }, + ], +} diff --git a/build/android/android_exports.lst b/build/android/android_exports.lst new file mode 100644 index 0000000000000..820d6ec169148 --- /dev/null +++ b/build/android/android_exports.lst @@ -0,0 +1,14 @@ +# 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. + +# Default exports specification for chromium shared libraries on android. +# Check ld version script manual: +# https://sourceware.org/binutils/docs-2.24/ld/VERSION.html#VERSION + +{ + global: + Java_*_native*; + JNI_OnLoad; + local: *; +}; diff --git a/build/android/arm-linux-androideabi-gold/arm-linux-androideabi-ld b/build/android/arm-linux-androideabi-gold/arm-linux-androideabi-ld deleted file mode 120000 index 5b178e9f42157..0000000000000 --- a/build/android/arm-linux-androideabi-gold/arm-linux-androideabi-ld +++ /dev/null @@ -1 +0,0 @@ -../../../third_party/android_tools/ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ld.gold \ No newline at end of file diff --git a/build/android/arm-linux-androideabi-gold/ld b/build/android/arm-linux-androideabi-gold/ld deleted file mode 120000 index 2366ddac0694d..0000000000000 --- a/build/android/arm-linux-androideabi-gold/ld +++ /dev/null @@ -1 +0,0 @@ -../../../third_party/android_tools/ndk/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86_64/arm-linux-androideabi/bin/ld.gold \ No newline at end of file diff --git a/build/android/buildbot/bb_device_status_check.py b/build/android/buildbot/bb_device_status_check.py index 41bf1721e33eb..fe40488d8f011 100755 --- a/build/android/buildbot/bb_device_status_check.py +++ b/build/android/buildbot/bb_device_status_check.py @@ -81,8 +81,10 @@ def _GetData(re_expression, line, lambda_function=lambda x:x): ''] errors = [] + dev_good = True if battery_level < 15: errors += ['Device critically low in battery. Turning off device.'] + dev_good = False if not options.no_provisioning_check: setup_wizard_disabled = ( device_adb.GetProp('ro.setupwizard.mode') == 'DISABLED') @@ -103,7 +105,7 @@ def _GetData(re_expression, line, lambda_function=lambda x:x): logging.error(str(e)) device_adb.old_interface.Shutdown() full_report = '\n'.join(report) - return device_type, device_build, battery_level, full_report, errors, True + return device_type, device_build, battery_level, full_report, errors, dev_good def CheckForMissingDevices(options, adb_online_devs): diff --git a/build/android/buildbot/bb_device_steps.py b/build/android/buildbot/bb_device_steps.py index 05160524b860f..8ff5a20ad63ac 100755 --- a/build/android/buildbot/bb_device_steps.py +++ b/build/android/buildbot/bb_device_steps.py @@ -96,6 +96,32 @@ def _GetRevision(options): return revision +def _RunTest(options, cmd, suite): + """Run test command with runtest.py. + + Args: + options: options object. + cmd: the command to run. + suite: test name. + """ + property_args = bb_utils.EncodeProperties(options) + args = [os.path.join(SLAVE_SCRIPTS_DIR, 'runtest.py')] + property_args + args += ['--test-platform', 'android'] + if options.factory_properties.get('generate_gtest_json'): + args.append('--generate-json-file') + args += ['-o', 'gtest-results/%s' % suite, + '--annotate', 'gtest', + '--build-number', str(options.build_properties.get('buildnumber', + '')), + '--builder-name', options.build_properties.get('buildername', '')] + if options.target == 'Release': + args += ['--target', 'Release'] + else: + args += ['--target', 'Debug'] + args += cmd + RunCmd(args, cwd=DIR_BUILD_ROOT) + + def RunTestSuites(options, suites, suites_options=None): """Manages an invocation of test_runner.py for gtests. @@ -121,11 +147,11 @@ def RunTestSuites(options, suites, suites_options=None): for suite in suites: bb_annotations.PrintNamedStep(suite) - cmd = ['build/android/test_runner.py', 'gtest', '-s', suite] + args + cmd = [suite] + args cmd += suites_options.get(suite, []) if suite == 'content_browsertests': cmd.append('--num_retries=1') - RunCmd(cmd) + _RunTest(options, cmd, suite) def RunChromeDriverTests(options): @@ -634,6 +660,9 @@ def MainTestWrapper(options): # KillHostHeartbeat() has logic to check if heartbeat process is running, # and kills only if it finds the process is running on the host. provision_devices.KillHostHeartbeat() + if options.cleanup: + shutil.rmtree(os.path.join(CHROME_OUT_DIR, options.target), + ignore_errors=True) def GetDeviceStepsOptParser(): @@ -679,6 +708,8 @@ def GetDeviceStepsOptParser(): help='Do not run stack tool.') parser.add_option('--asan-symbolize', action='store_true', help='Run stack tool for ASAN') + parser.add_option('--cleanup', action='store_true', + help='Delete out/ directory at the end of the run.') return parser diff --git a/build/android/buildbot/bb_run_bot.py b/build/android/buildbot/bb_run_bot.py index 878e36a92851c..7dbd2111588df 100755 --- a/build/android/buildbot/bb_run_bot.py +++ b/build/android/buildbot/bb_run_bot.py @@ -144,8 +144,9 @@ def GetBotStepMap(): B('main-clobber', H(compile_step)), B('main-tests-rel', H(std_test_steps), T(std_tests + telemetry_tests + chrome_proxy_tests, - [flakiness_server])), - B('main-tests', H(std_test_steps), T(std_tests, [flakiness_server])), + ['--cleanup', flakiness_server])), + B('main-tests', H(std_test_steps), + T(std_tests,['--cleanup', flakiness_server])), # Other waterfalls B('asan-builder-tests', H(compile_step, @@ -153,7 +154,8 @@ def GetBotStepMap(): T(std_tests, ['--asan', '--asan-symbolize'])), B('blink-try-builder', H(compile_step)), B('chromedriver-fyi-tests-dbg', H(std_test_steps), - T(['chromedriver'], ['--install=ChromeShell', '--skip-wipe'])), + T(['chromedriver'], ['--install=ChromeShell', '--skip-wipe', + '--cleanup'])), B('fyi-x86-builder-dbg', H(compile_step + std_host_tests, experimental, target_arch='x86')), B('fyi-builder-dbg', @@ -164,7 +166,8 @@ def GetBotStepMap(): B('fyi-builder-rel', H(std_build_steps, experimental)), B('fyi-tests', H(std_test_steps), T(std_tests, ['--experimental', flakiness_server, - '--coverage-bucket', CHROMIUM_COVERAGE_BUCKET])), + '--coverage-bucket', CHROMIUM_COVERAGE_BUCKET, + '--cleanup'])), B('fyi-component-builder-tests-dbg', H(compile_step, extra_gyp='component=shared_library'), T(std_tests, ['--experimental', flakiness_server])), @@ -176,9 +179,9 @@ def GetBotStepMap(): H(['bisect_perf_regression']), T([], ['--chrome-output-dir', bisect_chrome_output_dir])), B('perf-tests-rel', H(std_test_steps), - T([], ['--install=ChromeShell'])), + T([], ['--install=ChromeShell', '--cleanup'])), B('webkit-latest-webkit-tests', H(std_test_steps), - T(['webkit_layout', 'webkit'], ['--auto-reconnect'])), + T(['webkit_layout', 'webkit'], ['--cleanup', '--auto-reconnect'])), B('webkit-latest-contentshell', H(compile_step), T(['webkit_layout'], ['--auto-reconnect'])), B('builder-unit-tests', H(compile_step), T(['unit'])), @@ -191,9 +194,9 @@ def GetBotStepMap(): extra_gyp='include_tests=1 enable_tracing=1')), B('webrtc-chromium-tests', H(std_test_steps), T(['webrtc_chromium'], - [flakiness_server, '--gtest-filter=WebRtc*'])), + [flakiness_server, '--gtest-filter=WebRtc*', '--cleanup'])), B('webrtc-native-tests', H(std_test_steps), - T(['webrtc_native'], [flakiness_server])), + T(['webrtc_native'], ['--cleanup', flakiness_server])), # Generic builder config (for substring match). B('builder', H(std_build_steps)), diff --git a/build/android/gyp/lint.py b/build/android/gyp/lint.py index 1940c0eb6989e..48ab837e71bdd 100755 --- a/build/android/gyp/lint.py +++ b/build/android/gyp/lint.py @@ -20,7 +20,7 @@ def _RunLint(lint_path, config_path, processed_config_path, manifest_path, - result_path, product_dir, src_dirs, classes_dir): + result_path, product_dir, src_dirs, jar_path): def _RelativizePath(path): """Returns relative path to top-level src dir. @@ -76,7 +76,7 @@ def _ParseAndShowResultFile(): cmd = [ lint_path, '-Werror', '--exitcode', '--showall', '--config', _RelativizePath(processed_config_path), - '--classpath', _RelativizePath(classes_dir), + '--classpath', _RelativizePath(jar_path), '--xml', _RelativizePath(result_path), ] for src in src_dirs: @@ -135,7 +135,7 @@ def main(): parser.add_option('--result-path', help='Path to XML lint result file.') parser.add_option('--product-dir', help='Path to product dir.') parser.add_option('--src-dirs', help='Directories containing java files.') - parser.add_option('--classes-dir', help='Directory containing class files.') + parser.add_option('--jar-path', help='Jar file containing class files.') parser.add_option('--stamp', help='Path to touch on success.') parser.add_option('--enable', action='store_true', help='Run lint instead of just touching stamp.') @@ -146,7 +146,7 @@ def main(): options, parser, required=['lint_path', 'config_path', 'processed_config_path', 'manifest_path', 'result_path', 'product_dir', 'src_dirs', - 'classes_dir']) + 'jar_path']) src_dirs = build_utils.ParseGypList(options.src_dirs) @@ -156,7 +156,7 @@ def main(): rc = _RunLint(options.lint_path, options.config_path, options.processed_config_path, options.manifest_path, options.result_path, - options.product_dir, src_dirs, options.classes_dir) + options.product_dir, src_dirs, options.jar_path) if options.stamp and not rc: build_utils.Touch(options.stamp) diff --git a/build/android/lint_action.gypi b/build/android/lint_action.gypi index dd0bbd2f984ed..186d64c4fa28e 100644 --- a/build/android/lint_action.gypi +++ b/build/android/lint_action.gypi @@ -22,6 +22,7 @@ '<(DEPTH)/build/android/gyp/lint.py', '<(DEPTH)/build/android/lint/suppressions.xml', '<(DEPTH)/build/android/AndroidManifest.xml', + '<(lint_jar_path)', ], 'action': [ 'python', '<(DEPTH)/build/android/gyp/lint.py', @@ -32,7 +33,7 @@ '--result-path=<(result_path)', '--product-dir=<(PRODUCT_DIR)', '--src-dirs=>(src_dirs)', - '--classes-dir=<(classes_dir)', + '--jar-path=<(lint_jar_path)', '--stamp=<(stamp_path)', '<(is_enabled)', ], diff --git a/build/android/provision_devices.py b/build/android/provision_devices.py index 35cfb814c6fc1..44ae971876e0c 100755 --- a/build/android/provision_devices.py +++ b/build/android/provision_devices.py @@ -35,7 +35,7 @@ def KillHostHeartbeat(): matches = re.findall('\\n.*host_heartbeat.*', stdout) for match in matches: print 'An instance of host heart beart running... will kill' - pid = re.findall('(\d+)', match)[1] + pid = re.findall('(\d+)', match)[0] subprocess.call(['kill', str(pid)]) diff --git a/build/android/push_libraries.gypi b/build/android/push_libraries.gypi index ea89e92440412..f5f557087f3b3 100644 --- a/build/android/push_libraries.gypi +++ b/build/android/push_libraries.gypi @@ -30,6 +30,7 @@ '<(DEPTH)/build/android/gyp/util/md5_check.py', '<(DEPTH)/build/android/gyp/push_libraries.py', '<(strip_stamp)', + '<(strip_additional_stamp)', '<(build_device_config_path)', ], 'outputs': [ diff --git a/build/android/pylib/OWNERS b/build/android/pylib/OWNERS index 3ae19a560c3bb..3899fa3e73290 100644 --- a/build/android/pylib/OWNERS +++ b/build/android/pylib/OWNERS @@ -1,4 +1,4 @@ -craigdh@chromium.org frankf@chromium.org +jbudorick@chromium.org navabi@chromium.org skyostil@chromium.org diff --git a/build/android/pylib/android_commands.py b/build/android/pylib/android_commands.py index 5fc2fbd0b001b..e53f774ca9a43 100644 --- a/build/android/pylib/android_commands.py +++ b/build/android/pylib/android_commands.py @@ -497,8 +497,10 @@ def Install(self, package_file_path, reinstall=False): install_cmd = ' '.join(install_cmd) self._LogShell(install_cmd) + # FIXME(wang16): Change the timeout here to five minutes. Revert + # the change when slaves can run kvm enabled x86 android emulators. return self._adb.SendCommand(install_cmd, - timeout_time=2 * 60, + timeout_time=5 * 60, retry_count=0) def ManagedInstall(self, apk_path, keep_data=False, package_name=None, diff --git a/build/android/pylib/base/test_dispatcher.py b/build/android/pylib/base/test_dispatcher.py index 196f1acc1ca25..cb789de8d25d7 100644 --- a/build/android/pylib/base/test_dispatcher.py +++ b/build/android/pylib/base/test_dispatcher.py @@ -330,9 +330,31 @@ def _TearDownRunners(runners, timeout=None): threads.JoinAll(watchdog_timer.WatchdogTimer(timeout)) +def ApplyMaxPerRun(tests, max_per_run): + """Rearrange the tests so that no group contains more than max_per_run tests. + + Args: + tests: + max_per_run: + + Returns: + A list of tests with no more than max_per_run per run. + """ + tests_expanded = [] + for test_group in tests: + if type(test_group) != str: + # Do not split test objects which are not strings. + tests_expanded.append(test_group) + else: + test_split = test_group.split(':') + for i in range(0, len(test_split), max_per_run): + tests_expanded.append(':'.join(test_split[i:i+max_per_run])) + return tests_expanded + + def RunTests(tests, runner_factory, devices, shard=True, test_timeout=DEFAULT_TIMEOUT, setup_timeout=DEFAULT_TIMEOUT, - num_retries=2): + num_retries=2, max_per_run=256): """Run all tests on attached devices, retrying tests that don't pass. Args: @@ -349,6 +371,7 @@ def RunTests(tests, runner_factory, devices, shard=True, setup_timeout: Watchdog timeout in seconds for creating and cleaning up test runners. num_retries: Number of retries for a test. + max_per_run: Maximum number of tests to run in any group. Returns: A tuple of (base_test_result.TestRunResults object, exit code). @@ -357,21 +380,24 @@ def RunTests(tests, runner_factory, devices, shard=True, logging.critical('No tests to run.') return (base_test_result.TestRunResults(), constants.ERROR_EXIT_CODE) + tests_expanded = ApplyMaxPerRun(tests, max_per_run) if shard: # Generate a shared _TestCollection object for all test runners, so they # draw from a common pool of tests. - shared_test_collection = _TestCollection([_Test(t) for t in tests]) + shared_test_collection = _TestCollection([_Test(t) for t in tests_expanded]) test_collection_factory = lambda: shared_test_collection tag_results_with_device = False log_string = 'sharded across devices' else: # Generate a unique _TestCollection object for each test runner, but use # the same set of tests. - test_collection_factory = lambda: _TestCollection([_Test(t) for t in tests]) + test_collection_factory = lambda: _TestCollection( + [_Test(t) for t in tests_expanded]) tag_results_with_device = True log_string = 'replicated on each device' - logging.info('Will run %d tests (%s): %s', len(tests), log_string, str(tests)) + logging.info('Will run %d tests (%s): %s', + len(tests_expanded), log_string, str(tests_expanded)) runners = _CreateRunners(runner_factory, devices, setup_timeout) try: return _RunAllTests(runners, test_collection_factory, diff --git a/build/android/pylib/base/test_dispatcher_unittest.py b/build/android/pylib/base/test_dispatcher_unittest.py index 1206a93cee139..d349f32ab5ad2 100644 --- a/build/android/pylib/base/test_dispatcher_unittest.py +++ b/build/android/pylib/base/test_dispatcher_unittest.py @@ -22,7 +22,6 @@ from pylib.utils import watchdog_timer - class TestException(Exception): pass @@ -120,6 +119,11 @@ def testThreadSafeCounter(self): for i in xrange(5): self.assertEqual(counter.GetAndIncrement(), i) + def testApplyMaxPerRun(self): + self.assertEqual( + ['A:B', 'C:D', 'E', 'F:G', 'H:I'], + test_dispatcher.ApplyMaxPerRun(['A:B', 'C:D:E', 'F:G:H:I'], 2)) + class TestThreadGroupFunctions(unittest.TestCase): """Tests test_dispatcher._RunAllTests and test_dispatcher._CreateRunners.""" @@ -188,15 +192,6 @@ def testNoTests(self): self.assertEqual(len(results.GetAll()), 0) self.assertEqual(exit_code, constants.ERROR_EXIT_CODE) - def testTestsRemainWithAllDevicesOffline(self): - attached_devices = android_commands.GetAttachedDevices - android_commands.GetAttachedDevices = lambda: [] - try: - with self.assertRaises(AssertionError): - _results, _exit_code = TestShard._RunShard(MockRunner) - finally: - android_commands.GetAttachedDevices = attached_devices - class TestReplicate(unittest.TestCase): """Tests test_dispatcher.RunTests with replication.""" diff --git a/build/android/pylib/constants.py b/build/android/pylib/constants.py index a41438c852037..3d0dfbc941bd2 100644 --- a/build/android/pylib/constants.py +++ b/build/android/pylib/constants.py @@ -24,6 +24,12 @@ 'test_package']) PACKAGE_INFO = { + 'chrome_document': PackageInfo( + 'com.google.android.apps.chrome.document', + 'com.google.android.apps.chrome.document.ChromeLauncherActivity', + '/data/local/chrome-command-line', + 'chrome_devtools_remote', + None), 'chrome': PackageInfo( 'com.google.android.apps.chrome', 'com.google.android.apps.chrome.Main', diff --git a/build/android/pylib/content_settings.py b/build/android/pylib/content_settings.py index 6993eb375324d..d2220537202f9 100644 --- a/build/android/pylib/content_settings.py +++ b/build/android/pylib/content_settings.py @@ -50,7 +50,10 @@ def iteritems(self): key = v elif k == 'value': value = v - assert key, value + if not key: + continue + if not value: + value = '' yield key, value def __getitem__(self, key): diff --git a/build/android/pylib/instrumentation/test_jar.py b/build/android/pylib/instrumentation/test_jar.py index c1b63c926720e..964dca7572187 100644 --- a/build/android/pylib/instrumentation/test_jar.py +++ b/build/android/pylib/instrumentation/test_jar.py @@ -29,7 +29,8 @@ class TestJar(object): _ANNOTATIONS = frozenset( ['Smoke', 'SmallTest', 'MediumTest', 'LargeTest', 'EnormousTest', - 'FlakyTest', 'DisabledTest', 'Manual', 'PerfTest', 'HostDrivenTest']) + 'FlakyTest', 'DisabledTest', 'Manual', 'PerfTest', 'HostDrivenTest', + 'IntegrationTest']) _DEFAULT_ANNOTATION = 'SmallTest' _PROGUARD_CLASS_RE = re.compile(r'\s*?- Program class:\s*([\S]+)$') _PROGUARD_SUPERCLASS_RE = re.compile(r'\s*? Superclass:\s*([\S]+)$') diff --git a/build/android/pylib/instrumentation/test_options.py b/build/android/pylib/instrumentation/test_options.py index cdf3f3f228296..23fd82cafbcd9 100644 --- a/build/android/pylib/instrumentation/test_options.py +++ b/build/android/pylib/instrumentation/test_options.py @@ -22,4 +22,5 @@ 'test_apk_path', 'test_apk_jar_path', 'test_runner', - 'test_support_apk_path']) + 'test_support_apk_path', + 'device_flags']) diff --git a/build/android/pylib/instrumentation/test_runner.py b/build/android/pylib/instrumentation/test_runner.py index 3ff0744620b56..0a448c3384aaa 100644 --- a/build/android/pylib/instrumentation/test_runner.py +++ b/build/android/pylib/instrumentation/test_runner.py @@ -116,7 +116,8 @@ def PushDataDeps(self): dst_src = dest_host_pair.split(':', 1) dst_layer = dst_src[0] host_src = dst_src[1] - host_test_files_path = '%s/%s' % (constants.DIR_SOURCE_ROOT, host_src) + host_test_files_path = os.path.join(constants.DIR_SOURCE_ROOT, + host_src) if os.path.exists(host_test_files_path): self.device.PushChangedFiles( host_test_files_path, @@ -163,6 +164,10 @@ def SetUp(self): os.path.join(constants.DIR_SOURCE_ROOT), self._lighttp_port) if self.flags: self.flags.AddFlags(['--disable-fre', '--enable-test-intents']) + if self.options.device_flags: + with open(self.options.device_flags) as device_flags_file: + stripped_flags = (l.strip() for l in device_flags_file) + self.flags.AddFlags([flag for flag in stripped_flags if flag]) def TearDown(self): """Cleans up the test harness and saves outstanding data from test run.""" @@ -319,6 +324,8 @@ def _GetIndividualTestTimeoutSecs(self, test): annotations = self.test_pkg.GetTestAnnotations(test) if 'Manual' in annotations: return 10 * 60 * 60 + if 'IntegrationTest' in annotations: + return 30 * 60 if 'External' in annotations: return 10 * 60 if 'EnormousTest' in annotations: diff --git a/build/android/pylib/uiautomator/test_runner.py b/build/android/pylib/uiautomator/test_runner.py index 910bbf54b25aa..794c313d43244 100644 --- a/build/android/pylib/uiautomator/test_runner.py +++ b/build/android/pylib/uiautomator/test_runner.py @@ -40,7 +40,8 @@ def __init__(self, test_options, device, shard_index, test_pkg): test_apk_path=None, test_apk_jar_path=None, test_runner=None, - test_support_apk_path=None) + test_support_apk_path=None, + device_flags=None) super(TestRunner, self).__init__(instrumentation_options, device, shard_index, test_pkg) diff --git a/build/android/pylib/utils/findbugs.py b/build/android/pylib/utils/findbugs.py index fb672680fe1b6..208b0cf45dc8e 100644 --- a/build/android/pylib/utils/findbugs.py +++ b/build/android/pylib/utils/findbugs.py @@ -66,16 +66,17 @@ def _Rebaseline(current_warnings_set, known_bugs_file): return 0 -def _GetChromeClasses(release_version): +def _GetChromeJars(release_version): version = 'Debug' if release_version: version = 'Release' - path = os.path.join(constants.DIR_SOURCE_ROOT, 'out', version) - cmd = 'find %s -name "*.class"' % path + path = os.path.join(constants.DIR_SOURCE_ROOT, 'out', version, 'lib.java') + cmd = 'find %s -name "*.jar"' % path out = cmd_helper.GetCmdOutput(shlex.split(cmd)) + out = [p for p in out.splitlines() if not p.endswith('.dex.jar')] if not out: print 'No classes found in %s' % path - return out + return ' '.join(out) def _Run(exclude, known_bugs, classes_to_analyze, auxiliary_classes, @@ -135,7 +136,7 @@ def _Run(exclude, known_bugs, classes_to_analyze, auxiliary_classes, if findbug_args: cmd = '%s %s ' % (cmd, findbug_args) - chrome_classes = _GetChromeClasses(release_version) + chrome_classes = _GetChromeJars(release_version) if not chrome_classes: return 1 cmd = '%s %s ' % (cmd, chrome_classes) diff --git a/build/android/test_runner.py b/build/android/test_runner.py index 8aebb8b4b0323..8a4b9fa4c8a79 100755 --- a/build/android/test_runner.py +++ b/build/android/test_runner.py @@ -201,7 +201,7 @@ def ProcessJavaTestOptions(options): options.annotations = [] else: options.annotations = ['Smoke', 'SmallTest', 'MediumTest', 'LargeTest', - 'EnormousTest'] + 'EnormousTest', 'IntegrationTest'] if options.exclude_annotation_str: options.exclude_annotations = options.exclude_annotation_str.split(',') @@ -237,6 +237,9 @@ def AddInstrumentationTestOptions(option_parser): option_parser.add_option('--coverage-dir', help=('Directory in which to place all generated ' 'EMMA coverage files.')) + option_parser.add_option('--device-flags', dest='device_flags', default='', + help='The relative filepath to a file containing ' + 'command-line flags to set on the device') def ProcessInstrumentationOptions(options, error_func): @@ -299,7 +302,8 @@ def ProcessInstrumentationOptions(options, error_func): options.test_apk_path, options.test_apk_jar_path, options.test_runner, - options.test_support_apk_path + options.test_support_apk_path, + options.device_flags ) @@ -597,6 +601,10 @@ def _RunInstrumentationTests(options, error_func, devices): if test_exit_code and exit_code != constants.ERROR_EXIT_CODE: exit_code = test_exit_code + if options.device_flags: + options.device_flags = os.path.join(constants.DIR_SOURCE_ROOT, + options.device_flags) + report_results.LogFull( results=results, test_type='Instrumentation', diff --git a/build/build_config.h b/build/build_config.h index 6e31a730918e9..b48db72743d61 100644 --- a/build/build_config.h +++ b/build/build_config.h @@ -113,10 +113,17 @@ #define ARCH_CPU_32_BITS 1 #define ARCH_CPU_LITTLE_ENDIAN 1 #elif defined(__MIPSEL__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS64_FAMILY 1 +#define ARCH_CPU_MIPS64EL 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#else #define ARCH_CPU_MIPS_FAMILY 1 #define ARCH_CPU_MIPSEL 1 #define ARCH_CPU_32_BITS 1 #define ARCH_CPU_LITTLE_ENDIAN 1 +#endif #else #error Please add support for your architecture in build/build_config.h #endif diff --git a/build/common.gypi b/build/common.gypi index 79d3f986192bb..5ce3249a554dc 100644 --- a/build/common.gypi +++ b/build/common.gypi @@ -1618,7 +1618,7 @@ # not using the "current" SDK. 'ios_sdk%': '', 'ios_sdk_path%': '', - 'ios_deployment_target%': '6.0', + 'ios_deployment_target%': '7.0', 'conditions': [ # ios_product_name is set to the name of the .app bundle as it should @@ -1780,6 +1780,9 @@ # Copy it out one scope. 'android_webview_build%': '<(android_webview_build)', + + # Default android linker script for shared library exports. + 'android_linker_script%': '<(SHARED_INTERMEDIATE_DIR)/android_exports.lst', }], # OS=="android" ['embedded==1', { 'use_system_fontconfig%': 0, @@ -1787,6 +1790,7 @@ 'use_system_fontconfig%': 1, }], ['chromecast==1', { + 'enable_mpeg2ts_stream_parser%': 1, 'ffmpeg_branding%': 'Chrome', 'ozone_platform_ozonex%': 1, 'conditions': [ @@ -1794,7 +1798,6 @@ 'arm_arch%': '', 'arm_tune%': 'cortex-a9', 'arm_thumb%': 1, - 'enable_mpeg2ts_stream_parser%': 1, 'video_hole%': 1, }], ], @@ -2331,6 +2334,13 @@ '<(PRODUCT_DIR)/default_apps/drive.crx', '<(PRODUCT_DIR)/default_apps/docs.crx', ], + + # Whether to allow building of the GPU-related isolates. + 'archive_gpu_tests%': 0, + + # Flags to enable Murphy resource policy daemon integration on Tizen. + 'tizen%': 0, + 'enable_murphy%': 0, }, 'target_defaults': { 'variables': { @@ -2469,8 +2479,6 @@ 'defines': [ # Don't use deprecated V8 APIs anywhere. 'V8_DEPRECATION_WARNINGS', - # Temporary suppression until Blink code can be removed. - 'BLINK_SCALE_FILTERS_AT_RECORD_TIME', ], 'include_dirs': [ '<(SHARED_INTERMEDIATE_DIR)', @@ -3805,15 +3813,9 @@ '-no-integrated-as', '-B<(android_toolchain)', # Else /usr/bin/as gets picked up. ], - - 'ldflags!': [ - # Clang does not support the following options. - '-fuse-ld=gold', - ], 'ldflags': [ - # As long as -fuse-ld=gold doesn't work, add a dummy directory - # with an 'ld' that redirects to gold, so that clang uses gold. - '-B=48 and clang==0', { - 'target_conditions': [ - ['_toolset=="target"', { - 'ldflags': [ - '-fuse-ld=gold', - ], - }], - ], - }], - ['host_gcc_version>=48 and clang==0', { - 'target_conditions': [ - ['_toolset=="host"', { - 'ldflags': [ - '-fuse-ld=gold', - ], - }], - ], - }], ], }], ['linux_use_bundled_binutils==1', { @@ -4350,7 +4334,7 @@ }, 'target_conditions': [ ['_type=="shared_library"', { - 'product_extension': '<(android_product_extension)', + 'product_extension': '<(android_product_extension)', }], # Settings for building device targets using Android's toolchain. @@ -4416,8 +4400,6 @@ 'ldflags': [ '-nostdlib', '-Wl,--no-undefined', - # Don't export symbols from statically linked libraries. - '-Wl,--exclude-libs=ALL', ], 'libraries': [ '-l<(android_stlport_library)', @@ -4428,8 +4410,8 @@ '-lm', ], 'conditions': [ - ['component=="shared_library"', { - 'ldflags!': [ + ['component=="static_library"', { + 'ldflags': [ '-Wl,--exclude-libs=ALL', ], }], @@ -5042,6 +5024,10 @@ 'CLANG_CXX_LIBRARY': 'libc++', # -stdlib=libc++ }] ], + }, { + # The default for deployment target of 7.0+ is libc++, so force + # the old behavior unless libc++ is enabled. + 'CLANG_CXX_LIBRARY': 'libstdc++', # -stdlib=libstdc++ }], ], }, diff --git a/build/config/BUILD.gn b/build/config/BUILD.gn index 1203c0839b426..b0daaf1dd31a6 100644 --- a/build/config/BUILD.gn +++ b/build/config/BUILD.gn @@ -35,8 +35,6 @@ config("feature_flags") { "ENABLE_EGLIMAGE=1", "ENABLE_BACKGROUND=1", "V8_DEPRECATION_WARNINGS", # Don't use deprecated V8 APIs anywhere. - # Temporary suppression until Blink code can be removed. - "BLINK_SCALE_FILTERS_AT_RECORD_TIME", ] if (cld_version > 0) { diff --git a/build/config/android/config.gni b/build/config/android/config.gni index d7026d0a5c025..f509677441556 100644 --- a/build/config/android/config.gni +++ b/build/config/android/config.gni @@ -87,14 +87,17 @@ if (is_android) { # only need to define the current one, rather than one for every platform # like the toolchain roots. if (cpu_arch == "x86") { + android_toolchain_root = "$x86_android_toolchain_root" android_libgcc_file = - "$x86_android_toolchain_root/lib/gcc/i686-linux-android/${_android_toolchain_version}/libgcc.a" + "$android_toolchain_root/lib/gcc/i686-linux-android/${_android_toolchain_version}/libgcc.a" } else if (cpu_arch == "arm") { + android_toolchain_root = "$arm_android_toolchain_root" android_libgcc_file = - "$arm_android_toolchain_root/lib/gcc/arm-linux-androideabi/${_android_toolchain_version}/libgcc.a" + "$android_toolchain_root/lib/gcc/arm-linux-androideabi/${_android_toolchain_version}/libgcc.a" } else if (cpu_arch == "mipsel") { + android_toolchain_root = "$mips_android_toolchain_root" android_libgcc_file = - "$mips_android_toolchain_root/lib/gcc/mipsel-linux-android/${_android_toolchain_version}/libgcc.a" + "$android_toolchain_root/lib/gcc/mipsel-linux-android/${_android_toolchain_version}/libgcc.a" } else { assert(false, "Need android libgcc support for your target arch.") } diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn index a15232d8c62ef..613223539baa2 100644 --- a/build/config/compiler/BUILD.gn +++ b/build/config/compiler/BUILD.gn @@ -275,15 +275,11 @@ config("compiler") { # Use gold for Android for most CPU architectures. if (cpu_arch == "x86" || cpu_arch == "x64" || cpu_arch == "arm") { + ldflags += [ "-fuse-ld=gold" ] if (is_clang) { - # Clang does not support -fuse-ld to invoke the built-in gold linker, - # so use the -B option which requires us to specify the path. - ldflags += [ - "-B" + rebase_path("//build/android/arm-linux-androideabi-gold", - root_build_dir) - ] - } else { - ldflags += [ "-fuse-ld=gold" ] + # Let clang find the ld.gold in the NDK. + ldflags += [ "--gcc-toolchain=" + rebase_path(android_toolchain_root, + root_build_dir) ] } } diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn index c19758867b0ac..f26b5101da6cb 100644 --- a/build/config/linux/BUILD.gn +++ b/build/config/linux/BUILD.gn @@ -105,7 +105,7 @@ config("xscrnsaver") { } config("xfixes") { - libs = [ "xfixes" ] + libs = [ "Xfixes" ] } config("libcap") { diff --git a/build/get_landmines.py b/build/get_landmines.py index ee7f76af8f501..3aac6e760b45e 100755 --- a/build/get_landmines.py +++ b/build/get_landmines.py @@ -8,7 +8,6 @@ (or a list of 'landmines'). """ -import optparse import sys import landmine_utils @@ -21,17 +20,15 @@ platform = landmine_utils.platform -def print_landmines(target): +def print_landmines(): """ ALL LANDMINES ARE EMITTED FROM HERE. - target can be one of {'Release', 'Debug', 'Debug_x64', 'Release_x64'}. """ if (distributor() == 'goma' and platform() == 'win32' and builder() == 'ninja'): print 'Need to clobber winja goma due to backend cwd cache fix.' if platform() == 'android': - print 'Clobber: To avoid unresolved link errors on Breakpad roll.' - print 'Clobber: To get rid of generated files in the wrong package.' + print 'Clobber: To delete generated class files (we just use jars now).' if platform() == 'win' and builder() == 'ninja': print 'Compile on cc_unittests fails due to symbols removed in r185063.' if platform() == 'linux' and builder() == 'ninja': @@ -61,16 +58,7 @@ def print_landmines(target): def main(): - parser = optparse.OptionParser() - parser.add_option('-t', '--target', - help=='Target for which the landmines have to be emitted') - - options, args = parser.parse_args() - - if args: - parser.error('Unknown arguments %s' % args) - - print_landmines(options.target) + print_landmines() return 0 diff --git a/build/gyp_chromium b/build/gyp_chromium index 3c0aa6f7dbb2f..f87761dbd6bb5 100755 --- a/build/gyp_chromium +++ b/build/gyp_chromium @@ -8,7 +8,7 @@ # is invoked by Chromium beyond what can be done in the gclient hooks. import glob -import gyp_helper +import gyp_environment import os import re import shlex @@ -31,6 +31,7 @@ sys.path.insert(1, os.path.join(chrome_src, 'tools')) sys.path.insert(1, os.path.join(chrome_src, 'tools', 'generate_shim_headers')) sys.path.insert(1, os.path.join(chrome_src, 'tools', 'grit')) sys.path.insert(1, os.path.join(chrome_src, 'chrome', 'tools', 'build')) +sys.path.insert(1, os.path.join(chrome_src, 'chromecast', 'tools', 'build')) sys.path.insert(1, os.path.join(chrome_src, 'native_client', 'build')) sys.path.insert(1, os.path.join(chrome_src, 'native_client_sdk', 'src', 'build_tools')) @@ -189,24 +190,14 @@ def additional_include_files(supplemental_files, args=[]): if __name__ == '__main__': args = sys.argv[1:] - # TODO(sky): remove analyzer2 once updated recipes. use_analyzer = len(args) and args[0] == '--analyzer' if use_analyzer: - args.pop(0) - os.environ['GYP_GENERATORS'] = 'analyzer' - args.append('-Gfile_path=' + args.pop(0)) - elif len(args) and args[0] == '--analyzer2': - use_analyzer = True args.pop(0) os.environ['GYP_GENERATORS'] = 'analyzer' args.append('-Gconfig_path=' + args.pop(0)) args.append('-Ganalyzer_output_path=' + args.pop(0)) if int(os.environ.get('GYP_CHROMIUM_NO_ACTION', 0)): - # Check for landmines (reasons to clobber the build) in any case. - print 'Running build/landmines.py...' - subprocess.check_call( - [sys.executable, os.path.join(script_dir, 'landmines.py')]) print 'Skipping gyp_chromium due to GYP_CHROMIUM_NO_ACTION env var.' sys.exit(0) @@ -231,8 +222,6 @@ if __name__ == '__main__': p.communicate() sys.exit(p.returncode) - gyp_helper.apply_chromium_gyp_env() - # This could give false positives since it doesn't actually do real option # parsing. Oh well. gyp_file_specified = False @@ -241,6 +230,8 @@ if __name__ == '__main__': gyp_file_specified = True break + gyp_environment.SetEnvironment() + # If we didn't get a file, check an env var, and then fall back to # assuming 'all.gyp' from the same directory as the script. if not gyp_file_specified: @@ -270,21 +261,6 @@ if __name__ == '__main__': print 'Error: make gyp generator not supported (check GYP_GENERATORS).' sys.exit(1) - # Default to ninja on linux and windows, but only if no generator has - # explicitly been set. - # Also default to ninja on mac, but only when not building chrome/ios. - # . -f / --format has precedence over the env var, no need to check for it - # . set the env var only if it hasn't been set yet - # . chromium.gyp_env has been applied to os.environ at this point already - if sys.platform.startswith(('linux', 'win', 'freebsd')) and \ - not os.environ.get('GYP_GENERATORS'): - os.environ['GYP_GENERATORS'] = 'ninja' - elif sys.platform == 'darwin' and not os.environ.get('GYP_GENERATORS') and \ - not 'OS=ios' in os.environ.get('GYP_DEFINES', []): - os.environ['GYP_GENERATORS'] = 'ninja' - - vs2013_runtime_dll_dirs = vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() - # If CHROMIUM_GYP_SYNTAX_CHECK is set to 1, it will invoke gyp with --check # to enfore syntax checking. syntax_check = os.environ.get('CHROMIUM_GYP_SYNTAX_CHECK') @@ -328,13 +304,7 @@ if __name__ == '__main__': gyp_rc = gyp.main(args) if not use_analyzer: - # Check for landmines (reasons to clobber the build). This must be run here, - # rather than a separate runhooks step so that any environment modifications - # from above are picked up. - print 'Running build/landmines.py...' - subprocess.check_call( - [sys.executable, os.path.join(script_dir, 'landmines.py')]) - + vs2013_runtime_dll_dirs = vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() if vs2013_runtime_dll_dirs: x64_runtime, x86_runtime = vs2013_runtime_dll_dirs vs_toolchain.CopyVsRuntimeDlls( diff --git a/build/gyp_environment.py b/build/gyp_environment.py new file mode 100644 index 0000000000000..fb50645d56fc5 --- /dev/null +++ b/build/gyp_environment.py @@ -0,0 +1,33 @@ +# 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. + +""" +Sets up various automatic gyp environment variables. These are used by +gyp_chromium and landmines.py which run at different stages of runhooks. To +make sure settings are consistent between them, all setup should happen here. +""" + +import gyp_helper +import os +import sys +import vs_toolchain + +def SetEnvironment(): + """Sets defaults for GYP_* variables.""" + gyp_helper.apply_chromium_gyp_env() + + # Default to ninja on linux and windows, but only if no generator has + # explicitly been set. + # Also default to ninja on mac, but only when not building chrome/ios. + # . -f / --format has precedence over the env var, no need to check for it + # . set the env var only if it hasn't been set yet + # . chromium.gyp_env has been applied to os.environ at this point already + if sys.platform.startswith(('linux', 'win', 'freebsd')) and \ + not os.environ.get('GYP_GENERATORS'): + os.environ['GYP_GENERATORS'] = 'ninja' + elif sys.platform == 'darwin' and not os.environ.get('GYP_GENERATORS') and \ + not 'OS=ios' in os.environ.get('GYP_DEFINES', []): + os.environ['GYP_GENERATORS'] = 'ninja' + + vs_toolchain.SetEnvironmentAndGetRuntimeDllDirs() diff --git a/build/install-build-deps-android.sh b/build/install-build-deps-android.sh index 9d8301933e739..ae46fa24a14a2 100755 --- a/build/install-build-deps-android.sh +++ b/build/install-build-deps-android.sh @@ -42,7 +42,7 @@ sudo apt-get -f install # be installed manually on late-model versions. # common -sudo apt-get -y install checkstyle lighttpd python-pexpect xvfb x11-utils +sudo apt-get -y install lighttpd python-pexpect xvfb x11-utils # Few binaries in the Android SDK require 32-bit libraries on the host. sudo apt-get -y install lib32z1 g++-multilib diff --git a/build/jar_file_jni_generator.gypi b/build/jar_file_jni_generator.gypi index dc43c49070d2e..4c01c8a02c87d 100644 --- a/build/jar_file_jni_generator.gypi +++ b/build/jar_file_jni_generator.gypi @@ -26,6 +26,7 @@ 'jni_generator_includes%': ( 'base/android/jni_generator/jni_generator_helper.h' ), + 'native_exports%': '', }, 'actions': [ { @@ -54,6 +55,7 @@ '<(jni_generator_includes)', '--optimize_generation', '<(optimize_jni_generation)', + '<(native_exports)', ], 'message': 'Generating JNI bindings from <(input_jar_file)/<(input_java_class)', 'process_outputs_as_sources': 1, @@ -62,4 +64,14 @@ # This target exports a hard dependency because it generates header # files. 'hard_dependency': 1, + 'conditions': [ + ['android_webview_build==1', { + 'variables': { + 'native_exports%': '--native_exports', + }, + 'dependencies': [ + '<(DEPTH)/build/android/android_exports.gyp:android_exports', + ], + }], + ], } diff --git a/build/java.gypi b/build/java.gypi index 6f860300ad7f9..bf6f56c8ed954 100644 --- a/build/java.gypi +++ b/build/java.gypi @@ -69,7 +69,6 @@ 'res_v14_verify_only%': 0, 'resource_input_paths': ['>@(res_extra_files)'], 'intermediate_dir': '<(SHARED_INTERMEDIATE_DIR)/<(_target_name)', - 'classes_dir': '<(intermediate_dir)/classes', 'compile_stamp': '<(intermediate_dir)/compile.stamp', 'lint_stamp': '<(intermediate_dir)/lint.stamp', 'lint_result': '<(intermediate_dir)/lint_result.xml', @@ -234,7 +233,6 @@ ], 'action': [ 'python', '<(DEPTH)/build/android/gyp/javac.py', - '--classes-dir=<(classes_dir)', '--classpath=>(input_jars_paths)', '--src-gendirs=>(generated_src_dirs)', '--javac-includes=<(javac_includes)', @@ -245,6 +243,23 @@ '>@(java_sources)', ] }, + { + 'action_name': 'instr_jar_<(_target_name)', + 'message': 'Instrumenting <(_target_name) jar', + 'variables': { + 'input_path': '<(jar_path)', + 'output_path': '<(jar_final_path)', + 'stamp_path': '<(instr_stamp)', + 'instr_type': 'jar', + }, + 'outputs': [ + '<(jar_final_path)', + ], + 'inputs': [ + '<(jar_path)', + ], + 'includes': [ 'android/instr_action.gypi' ], + }, { 'variables': { 'src_dirs': [ @@ -254,8 +269,10 @@ 'stamp_path': '<(lint_stamp)', 'result_path': '<(lint_result)', 'config_path': '<(lint_config)', + 'lint_jar_path': '<(jar_final_path)', }, 'inputs': [ + '<(jar_final_path)', '<(compile_stamp)', ], 'outputs': [ @@ -263,23 +280,6 @@ ], 'includes': [ 'android/lint_action.gypi' ], }, - { - 'action_name': 'instr_jar_<(_target_name)', - 'message': 'Instrumenting <(_target_name) jar', - 'variables': { - 'input_path': '<(jar_path)', - 'output_path': '<(jar_final_path)', - 'stamp_path': '<(instr_stamp)', - 'instr_type': 'jar', - }, - 'outputs': [ - '<(jar_final_path)', - ], - 'inputs': [ - '<(jar_path)', - ], - 'includes': [ 'android/instr_action.gypi' ], - }, { 'action_name': 'jar_toc_<(_target_name)', 'message': 'Creating <(_target_name) jar.TOC', diff --git a/build/java_apk.gypi b/build/java_apk.gypi index c7ef1591b46ed..be5033bb4a9dc 100644 --- a/build/java_apk.gypi +++ b/build/java_apk.gypi @@ -106,7 +106,6 @@ 'strip_stamp': '<(intermediate_dir)/strip.stamp', 'stripped_libraries_dir': '<(intermediate_dir)/stripped_libraries', 'strip_additional_stamp': '<(intermediate_dir)/strip_additional.stamp', - 'classes_dir': '<(intermediate_dir)/classes/2', 'javac_includes': [], 'jar_excluded_classes': [], 'javac_jar_path': '<(intermediate_dir)/<(_target_name).javac.jar', @@ -646,7 +645,6 @@ ], 'action': [ 'python', '<(DEPTH)/build/android/gyp/javac.py', - '--classes-dir=<(classes_dir)', '--classpath=>(input_jars_paths) <(android_sdk_jar)', '--src-gendirs=>(gen_src_dirs)', '--javac-includes=<(javac_includes)', @@ -657,24 +655,6 @@ '>@(java_sources)', ], }, - { - 'variables': { - 'src_dirs': [ - '<(java_in_dir)/src', - '>@(additional_src_dirs)', - ], - 'stamp_path': '<(lint_stamp)', - 'result_path': '<(lint_result)', - 'config_path': '<(lint_config)', - }, - 'inputs': [ - '<(compile_stamp)', - ], - 'outputs': [ - '<(lint_stamp)', - ], - 'includes': [ 'android/lint_action.gypi' ], - }, { 'action_name': 'instr_jar_<(_target_name)', 'message': 'Instrumenting <(_target_name) jar', @@ -693,6 +673,22 @@ ], 'includes': [ 'android/instr_action.gypi' ], }, + { + 'variables': { + 'src_dirs': [ + '<(java_in_dir)/src', + '>@(additional_src_dirs)', + ], + 'lint_jar_path': '<(jar_path)', + 'stamp_path': '<(lint_stamp)', + 'result_path': '<(lint_result)', + 'config_path': '<(lint_config)', + }, + 'outputs': [ + '<(lint_stamp)', + ], + 'includes': [ 'android/lint_action.gypi' ], + }, { 'action_name': 'obfuscate_<(_target_name)', 'message': 'Obfuscating <(_target_name)', diff --git a/build/jni_generator.gypi b/build/jni_generator.gypi index da99331ec522b..6edc512ecf4db 100644 --- a/build/jni_generator.gypi +++ b/build/jni_generator.gypi @@ -36,6 +36,7 @@ 'jni_generator_includes%': ( 'base/android/jni_generator/jni_generator_helper.h' ), + 'native_exports%': '', }, 'rules': [ { @@ -61,6 +62,7 @@ '<(jni_generator_jarjar_file)', '--ptr_type', '<(jni_generator_ptr_type)', + '<(native_exports)', ], 'message': 'Generating JNI bindings from <(RULE_INPUT_PATH)', 'process_outputs_as_sources': 1, @@ -81,4 +83,15 @@ # This target exports a hard dependency because it generates header # files. 'hard_dependency': 1, + 'conditions': [ + ['android_webview_build==1', { + 'variables': { + 'native_exports%': '--native_exports', + }, + 'dependencies': [ + '<(DEPTH)/build/android/android_exports.gyp:android_exports', + ], + }], + ], } + diff --git a/build/landmines.py b/build/landmines.py index 220b8a7989f45..c31bac7901c36 100755 --- a/build/landmines.py +++ b/build/landmines.py @@ -4,10 +4,8 @@ # found in the LICENSE file. """ -This script runs every build as a hook. If it detects that the build should -be clobbered, it will touch the file /.landmine_triggered. The -various build scripts will then check for the presence of this file and clobber -accordingly. The script will also emit the reasons for the clobber to stdout. +This script runs every build as the first hook (See DEPS). If it detects that +the build should be clobbered, it will remove the build directory. A landmine is tripped when a builder checks out a different revision, and the diff between the new landmines and the old ones is non-null. At this point, the @@ -16,9 +14,11 @@ import difflib import errno +import gyp_environment import logging import optparse import os +import shutil import sys import subprocess import time @@ -29,35 +29,32 @@ SRC_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) -def get_target_build_dir(build_tool, target, is_iphone=False): +def get_build_dir(build_tool, is_iphone=False): """ Returns output directory absolute path dependent on build and targets. Examples: - r'c:\b\build\slave\win\build\src\out\Release' - '/mnt/data/b/build/slave/linux/build/src/out/Debug' - '/b/build/slave/ios_rel_device/build/src/xcodebuild/Release-iphoneos' + r'c:\b\build\slave\win\build\src\out' + '/mnt/data/b/build/slave/linux/build/src/out' + '/b/build/slave/ios_rel_device/build/src/xcodebuild' Keep this function in sync with tools/build/scripts/slave/compile.py """ ret = None if build_tool == 'xcode': - ret = os.path.join(SRC_DIR, 'xcodebuild', - target + ('-iphoneos' if is_iphone else '')) + ret = os.path.join(SRC_DIR, 'xcodebuild') elif build_tool in ['make', 'ninja', 'ninja-ios']: # TODO: Remove ninja-ios. - ret = os.path.join(SRC_DIR, 'out', target) + ret = os.path.join(SRC_DIR, 'out') elif build_tool in ['msvs', 'vs', 'ib']: - ret = os.path.join(SRC_DIR, 'build', target) + ret = os.path.join(SRC_DIR, 'build') else: raise NotImplementedError('Unexpected GYP_GENERATORS (%s)' % build_tool) return os.path.abspath(ret) -def set_up_landmines(target, new_landmines): +def clobber_if_necessary(new_landmines): """Does the work of setting, planting, and triggering landmines.""" - out_dir = get_target_build_dir(landmine_utils.builder(), target, - landmine_utils.platform() == 'ios') - - landmines_path = os.path.join(out_dir, '.landmines') + out_dir = get_build_dir(landmine_utils.builder()) + landmines_path = os.path.normpath(os.path.join(out_dir, '..', '.landmines')) try: os.makedirs(out_dir) except OSError as e: @@ -65,7 +62,6 @@ def set_up_landmines(target, new_landmines): pass if os.path.exists(landmines_path): - triggered = os.path.join(out_dir, '.landmines_triggered') with open(landmines_path, 'r') as f: old_landmines = f.readlines() if old_landmines != new_landmines: @@ -73,12 +69,13 @@ def set_up_landmines(target, new_landmines): diff = difflib.unified_diff(old_landmines, new_landmines, fromfile='old_landmines', tofile='new_landmines', fromfiledate=old_date, tofiledate=time.ctime(), n=0) + sys.stdout.write('Clobbering due to:\n') + sys.stdout.writelines(diff) + + # Clobber. + shutil.rmtree(out_dir) - with open(triggered, 'w') as f: - f.writelines(diff) - elif os.path.exists(triggered): - # Remove false triggered landmines. - os.remove(triggered) + # Save current set of landmines for next time. with open(landmines_path, 'w') as f: f.writelines(new_landmines) @@ -119,14 +116,14 @@ def main(): if landmine_utils.builder() in ('dump_dependency_json', 'eclipse'): return 0 - for target in ('Debug', 'Release', 'Debug_x64', 'Release_x64'): - landmines = [] - for s in landmine_scripts: - proc = subprocess.Popen([sys.executable, s, '-t', target], - stdout=subprocess.PIPE) - output, _ = proc.communicate() - landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()]) - set_up_landmines(target, landmines) + gyp_environment.SetEnvironment() + + landmines = [] + for s in landmine_scripts: + proc = subprocess.Popen([sys.executable, s], stdout=subprocess.PIPE) + output, _ = proc.communicate() + landmines.extend([('%s\n' % l.strip()) for l in output.splitlines()]) + clobber_if_necessary(landmines) return 0 diff --git a/build/linux/system.gyp b/build/linux/system.gyp index e6c5a026d7dc1..9c9def9e9da8d 100644 --- a/build/linux/system.gyp +++ b/build/linux/system.gyp @@ -12,6 +12,10 @@ }], ], + # If any of the linux_link_FOO below are set to 1, then the corresponding + # target will be linked against the FOO library (either dynamically or + # statically, depending on the pkg-config files), as opposed to loading the + # FOO library dynamically with dlopen. 'linux_link_libgps%': 0, 'linux_link_libpci%': 0, 'linux_link_libspeechd%': 0, @@ -1050,5 +1054,27 @@ }], ], }, + { + 'target_name': 'resource_manager', + 'type': 'none', + 'toolsets': ['host', 'target'], + 'conditions': [ + ['tizen==1 and enable_murphy==1', { + 'direct_dependent_settings': { + 'cflags': [ + ' 1 and split[0].startswith(exclusion): + continue + if relpath.startswith(exclusion): # Multiple exclusions can match the same path. Go through all of them # and mark each one as used. @@ -60,6 +68,13 @@ def DoMain(argv): if f.endswith('.gyp') or f.endswith('.gypi'): continue + # Deleting .isolate files leads to gyp failures. They are usually + # not used by a distro build anyway. + # See http://www.chromium.org/developers/testing/isolated-testing + # for more info. + if f.endswith('.isolate'): + continue + if options.do_remove: # Delete the file - best way to ensure it's not used during build. os.remove(path) diff --git a/build/secondary/testing/gtest/BUILD.gn b/build/secondary/testing/gtest/BUILD.gn index eb8604dc153a1..f7c1e0d7208c9 100644 --- a/build/secondary/testing/gtest/BUILD.gn +++ b/build/secondary/testing/gtest/BUILD.gn @@ -9,7 +9,6 @@ config("gtest_config") { ] defines = [ - "UNIT_TEST", # In order to allow regex matches in gtest to be shared between Windows # and other systems, we tell gtest to always use it's internal engine. @@ -54,6 +53,11 @@ config("gtest_config") { } } +config("gtest_direct_config") { + visibility = ":*" + defines = [ "UNIT_TEST" ] +} + static_library("gtest") { sources = [ "include/gtest/gtest-death-test.h", @@ -100,6 +104,7 @@ static_library("gtest") { include_dirs = [ "." ] all_dependent_configs = [ ":gtest_config" ] + direct_dependent_configs = [ ":gtest_direct_config" ] configs -= [ "//build/config/compiler:chromium_code" ] configs += [ "//build/config/compiler:no_chromium_code" ] diff --git a/build/secondary/tools/grit/grit_rule.gni b/build/secondary/tools/grit/grit_rule.gni index e27f914755960..e1cf53f748c40 100644 --- a/build/secondary/tools/grit/grit_rule.gni +++ b/build/secondary/tools/grit/grit_rule.gni @@ -34,15 +34,24 @@ # List of strings containing extra command-line flags to pass to Grit. # # resource_ids (optional) -# Path to a grit "firstidsfile". Default is -# //tools/gritsettings/resource_ids. Set to "" to use the value specified in -# the nodes of the processed files. +# Path to a grit "firstidsfile". Default is +# //tools/gritsettings/resource_ids. Set to "" to use the value specified +# in the nodes of the processed files. # # output_dir (optional) -# Directory for generated files. +# Directory for generated files. If you specify this, you will often +# want to specify output_name if the target name is not particularly +# unique, since this can cause files from multiple grit targets to +# overwrite each other. +# +# output_name (optiona) +# Provide an alternate base name for the generated files, like the .d +# files. Normally these are based on the target name and go in the +# output_dir, but if multiple targets with the same name end up in +# the same output_dir, they can collide. # # use_qualified_include (optional) -# If set, output_dir is not added to include_dirs. +# If set, output_dir is not added to include_dirs. # # deps (optional) # visibility (optional) @@ -207,6 +216,12 @@ template("grit") { output_dir = target_gen_dir } + if (defined(invoker.output_name)) { + grit_output_name = invoker.output_name + } else { + grit_output_name = target_name + } + # These are all passed as arguments to the script so have to be relative to # the build directory. if (resource_ids != "") { @@ -229,7 +244,8 @@ template("grit") { # writing. We write the list to a file (some of the output lists are long # enough to not fit on a Windows command line) and ask Grit to verify those # are the actual outputs at runtime. - asserted_list_file = "$target_out_dir/${target_name}_expected_outputs.txt" + asserted_list_file = + "$target_out_dir/${grit_output_name}_expected_outputs.txt" write_file(asserted_list_file, rebase_path(invoker.outputs, root_build_dir, output_dir)) assert_files_flags += [ @@ -241,7 +257,7 @@ template("grit") { # The config and the action below get this visibility son only the generated # source set can depend on them. The variable "target_name" will get - # overwritten inside the innter classes so we need to compute it here. + # overwritten inside the inner classes so we need to compute it here. target_visibility = ":$target_name" # The current grit setup makes an file in $output_dir/grit/foo.h that @@ -262,7 +278,7 @@ template("grit") { script = "//tools/grit/grit.py" inputs = grit_inputs outputs = grit_outputs - depfile = "$output_dir/${target_name}.d" + depfile = "$output_dir/${grit_output_name}.d" args = [ "-i", source_path, "build", @@ -304,8 +320,6 @@ template("grit") { if (defined(invoker.visibility)) { visibility = invoker.visibility } - if (defined(invoker.output_name)) { - output_name = invoker.output_name - } + output_name = grit_output_name } } diff --git a/build/toolchain/android/find_android_compiler.py b/build/toolchain/android/find_android_compiler.py deleted file mode 100644 index d806ead80d554..0000000000000 --- a/build/toolchain/android/find_android_compiler.py +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# This script locates the Android compilers given the bin directory of the -# android toolchain. - -import glob -import subprocess -import sys - -if len(sys.argv) != 2: - print "Error: expecting one argument of the android toolchain dir." - sys.exit(1) - -# TODO(brettw) this logic seems like a bad idea. It was copied from -# common.gypi. It seems like the toolchain should just know the name given the -# current platform rather than having to rely on glob. -android_toolchain = sys.argv[1] -cc = glob.glob(android_toolchain + "/*-gcc") -cxx = glob.glob(android_toolchain + "/*-g++") - -# We tolerate "no matches." In the Android AOSP WebView build, it runs this -# logic and the directory doesn't exist, giving no matches. But that build runs -# GYP to generate Android Makefiles which specify the compiler separately. So -# all we need to do in this case is ignore the error and continue with empty -# target compilers. -if len(cc) == 0: - cc = [""] -if len(cxx) == 0: - cxx = [""] -if len(cc) != 1 or len(cxx) != 1: - print "More than one matching compiler." - sys.exit(1) - -# Get the host compilers from the current path. -which_gcc = subprocess.check_output(["which gcc"], shell=True).strip() -which_gxx = subprocess.check_output(["which g++"], shell=True).strip() - -print ('["' + cc[0] + '","' + cxx[0] + '","' + which_gcc + '","' + - which_gxx + '"]') diff --git a/build/toolchain_vs2013.hash b/build/toolchain_vs2013.hash deleted file mode 100644 index e0c4395120026..0000000000000 --- a/build/toolchain_vs2013.hash +++ /dev/null @@ -1,2 +0,0 @@ -27eac9b2869ef6c89391f305a3f01285ea317867 -9d9a93134b3eabd003b85b4e7dea06c0eae150ed diff --git a/build/vs_toolchain.py b/build/vs_toolchain.py index aa4fec2d1282f..735827e94316f 100644 --- a/build/vs_toolchain.py +++ b/build/vs_toolchain.py @@ -30,6 +30,8 @@ def SetEnvironmentAndGetRuntimeDllDirs(): depot_tools_win_toolchain = \ bool(int(os.environ.get('DEPOT_TOOLS_WIN_TOOLCHAIN', '1'))) if sys.platform in ('win32', 'cygwin') and depot_tools_win_toolchain: + if not os.path.exists(json_data_file): + Update() with open(json_data_file, 'r') as tempf: toolchain_data = json.load(tempf) @@ -129,7 +131,8 @@ def copy_runtime(target_dir, source_dir, dll_pattern): def _GetDesiredVsToolchainHashes(): """Load a list of SHA1s corresponding to the toolchains that we want installed to build with.""" - sha1path = os.path.join(script_dir, 'toolchain_vs2013.hash') + sha1path = os.path.join(script_dir, + '..', 'buildtools', 'toolchain_vs2013.hash') with open(sha1path, 'rb') as f: return f.read().strip().splitlines() diff --git a/build/whitespace_file.txt b/build/whitespace_file.txt index ccc1cb8222e0c..544d0f01865d3 100644 --- a/build/whitespace_file.txt +++ b/build/whitespace_file.txt @@ -94,6 +94,7 @@ This sentence is false. Beauty is in the eyes of a Beholder. +I'm the best at space. The first time Yossarian saw the chaplain, he fell madly in love with him. * @@ -126,4 +127,10 @@ seasons. And so they modified the whitespace file with these immortal lines, and visited it upon the bots, that great destruction might be wrought upon their outdated binaries. In clobberus, veritas. -As the git approaches, light begins to shine through the SCM once again... +As the git approaches, light begins to shine through the SCM thrice again... +However, the git, is, after all, quite stupid. +* +** +*** + +Whitespace change to ensure that the numbering daemon is working. diff --git a/cc/BUILD.gn b/cc/BUILD.gn index aabbdf685ef87..ddabbdd4da170 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn @@ -289,6 +289,7 @@ component("cc") { "quads/draw_quad.h", "quads/io_surface_draw_quad.cc", "quads/io_surface_draw_quad.h", + "quads/largest_draw_quad.h", "quads/picture_draw_quad.cc", "quads/picture_draw_quad.h", "quads/render_pass.cc", @@ -616,6 +617,7 @@ source_set("test_support") { "//gpu/command_buffer/client:gles2_c_lib", "//gpu/command_buffer/client:gles2_implementation", "//gpu/command_buffer/client:gl_in_process_context", + "//gpu/command_buffer/common:gles2_utils", "//gpu/skia_bindings", "//skia", "//testing/gmock", @@ -654,6 +656,7 @@ test("cc_unittests") { "layers/delegated_frame_provider_unittest.cc", "layers/delegated_frame_resource_collection_unittest.cc", "layers/delegated_renderer_layer_impl_unittest.cc", + "layers/delegated_renderer_layer_unittest.cc", "layers/heads_up_display_unittest.cc", "layers/heads_up_display_layer_impl_unittest.cc", "layers/io_surface_layer_impl_unittest.cc", @@ -760,6 +763,7 @@ test("cc_unittests") { "//cc/surfaces", "//gpu", "//gpu:test_support", + "//gpu/command_buffer/common:gles2_utils", "//media", "//testing/gmock", "//testing/gtest", @@ -791,6 +795,7 @@ test("cc_perftests") { "//base", "//gpu", "//gpu:test_support", + "//gpu/command_buffer/common:gles2_utils", "//media", "//skia", "//testing/gmock", diff --git a/cc/animation/animation.cc b/cc/animation/animation.cc index 4adb88a12e5ea..138664161c049 100644 --- a/cc/animation/animation.cc +++ b/cc/animation/animation.cc @@ -194,10 +194,12 @@ double Animation::TrimTimeToCurrentIteration( // We need to know the current iteration if we're alternating. int iteration = 0; - // If we are past the active interval, return iteration duration. + // If we are past the active interval, return iteration duration of last + // iteration if (is_past_total_duration) { iteration = iterations_ - 1; - trimmed_in_seconds = curve_->Duration(); + double frac = fmod(curve_->Duration() * iterations_, curve_->Duration()); + trimmed_in_seconds = frac == 0 ? curve_->Duration() : frac; } else { iteration = static_cast(trimmed_in_seconds / curve_->Duration()); // Calculate x where trimmed = x + n * curve_->Duration() for some positive diff --git a/cc/animation/animation.h b/cc/animation/animation.h index 02bb2c6013ce9..508651d5558cd 100644 --- a/cc/animation/animation.h +++ b/cc/animation/animation.h @@ -68,8 +68,8 @@ class CC_EXPORT Animation { // This is the number of times that the animation will play. If this // value is zero the animation will not play. If it is negative, then // the animation will loop indefinitely. - int iterations() const { return iterations_; } - void set_iterations(int n) { iterations_ = n; } + double iterations() const { return iterations_; } + void set_iterations(double n) { iterations_ = n; } base::TimeTicks start_time() const { return start_time_; } @@ -161,7 +161,7 @@ class CC_EXPORT Animation { TargetProperty target_property_; RunState run_state_; - int iterations_; + double iterations_; base::TimeTicks start_time_; Direction direction_; diff --git a/cc/animation/animation_unittest.cc b/cc/animation/animation_unittest.cc index 1d4b8f7a167f0..5f936aeda7864 100644 --- a/cc/animation/animation_unittest.cc +++ b/cc/animation/animation_unittest.cc @@ -18,7 +18,7 @@ static base::TimeTicks TicksFromSecondsF(double seconds) { base::Time::kMicrosecondsPerSecond); } -scoped_ptr CreateAnimation(int iterations, double duration) { +scoped_ptr CreateAnimation(double iterations, double duration) { scoped_ptr to_return(Animation::Create( make_scoped_ptr( new FakeFloatAnimationCurve(duration)).PassAs(), @@ -29,7 +29,7 @@ scoped_ptr CreateAnimation(int iterations, double duration) { return to_return.Pass(); } -scoped_ptr CreateAnimation(int iterations) { +scoped_ptr CreateAnimation(double iterations) { return CreateAnimation(iterations, 1); } @@ -48,11 +48,22 @@ TEST(AnimationTest, TrimTimeOneIteration) { EXPECT_EQ(1, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); } +TEST(AnimationTest, TrimTimeOneHalfIteration) { + scoped_ptr anim(CreateAnimation(1.5)); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(-1.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); + EXPECT_EQ(0.9, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.9))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); +} + TEST(AnimationTest, TrimTimeInfiniteIterations) { scoped_ptr anim(CreateAnimation(-1)); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); - EXPECT_EQ(0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.5))); } @@ -103,6 +114,22 @@ TEST(AnimationTest, TrimTimeAlternateTwoIterations) { EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); } +TEST(AnimationTest, TrimTimeAlternateTwoHalfIterations) { + scoped_ptr anim(CreateAnimation(2.5)); + anim->set_direction(Animation::Alternate); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.0))); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.25))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.5))); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(0.75))); + EXPECT_EQ(1.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.0))); + EXPECT_EQ(0.75, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.25))); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(1.75))); + EXPECT_EQ(0.0, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.0))); + EXPECT_EQ(0.25, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.25))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.50))); + EXPECT_EQ(0.5, anim->TrimTimeToCurrentIteration(TicksFromSecondsF(2.75))); +} + TEST(AnimationTest, TrimTimeAlternateReverseInfiniteIterations) { scoped_ptr anim(CreateAnimation(-1)); anim->set_direction(Animation::AlternateReverse); diff --git a/cc/animation/layer_animation_controller.cc b/cc/animation/layer_animation_controller.cc index c5e317df926a8..fbd02f38be914 100644 --- a/cc/animation/layer_animation_controller.cc +++ b/cc/animation/layer_animation_controller.cc @@ -854,7 +854,8 @@ void LayerAnimationController::TickAnimations(base::TimeTicks monotonic_time) { case Animation::Opacity: { const FloatAnimationCurve* float_animation_curve = animations_[i]->curve()->ToFloatAnimationCurve(); - const float opacity = float_animation_curve->GetValue(trimmed); + const float opacity = std::max( + std::min(float_animation_curve->GetValue(trimmed), 1.0f), 0.f); NotifyObserversOpacityAnimated( opacity, animations_[i]->affects_active_observers(), diff --git a/cc/animation/layer_animation_controller_unittest.cc b/cc/animation/layer_animation_controller_unittest.cc index 1e938cc517927..3f8a89f93f42c 100644 --- a/cc/animation/layer_animation_controller_unittest.cc +++ b/cc/animation/layer_animation_controller_unittest.cc @@ -2085,6 +2085,38 @@ TEST(LayerAnimationControllerTest, ActivationBetweenAnimateAndUpdateState) { EXPECT_EQ(0.75f, dummy_impl.opacity()); } +TEST(LayerAnimationControllerTest, ClippedOpacityValues) { + FakeLayerAnimationValueObserver dummy; + scoped_refptr controller( + LayerAnimationController::Create(0)); + controller->AddValueObserver(&dummy); + + AddOpacityTransitionToController(controller.get(), 1, 1.f, 2.f, true); + + controller->Animate(kInitialTickTime); + EXPECT_EQ(1.f, dummy.opacity()); + + // Opacity values are clipped [0,1] + controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); + EXPECT_EQ(1.f, dummy.opacity()); +} + +TEST(LayerAnimationControllerTest, ClippedNegativeOpacityValues) { + FakeLayerAnimationValueObserver dummy; + scoped_refptr controller( + LayerAnimationController::Create(0)); + controller->AddValueObserver(&dummy); + + AddOpacityTransitionToController(controller.get(), 1, 0.f, -2.f, true); + + controller->Animate(kInitialTickTime); + EXPECT_EQ(0.f, dummy.opacity()); + + // Opacity values are clipped [0,1] + controller->Animate(kInitialTickTime + TimeDelta::FromMilliseconds(1000)); + EXPECT_EQ(0.f, dummy.opacity()); +} + TEST(LayerAnimationControllerTest, PushedDeletedAnimationWaitsForActivation) { scoped_ptr events( make_scoped_ptr(new AnimationEventsVector)); diff --git a/cc/base/latency_info_swap_promise.cc b/cc/base/latency_info_swap_promise.cc index c846436aedc56..8c6ee0ec1313a 100644 --- a/cc/base/latency_info_swap_promise.cc +++ b/cc/base/latency_info_swap_promise.cc @@ -45,4 +45,8 @@ void LatencyInfoSwapPromise::DidNotSwap(DidNotSwapReason reason) { // DCHECK(latency_.terminated); } +int64 LatencyInfoSwapPromise::TraceId() const { + return latency_.trace_id; +} + } // namespace cc diff --git a/cc/base/latency_info_swap_promise.h b/cc/base/latency_info_swap_promise.h index 1a2b1d6b206a3..a210a3f1ea949 100644 --- a/cc/base/latency_info_swap_promise.h +++ b/cc/base/latency_info_swap_promise.h @@ -19,6 +19,8 @@ class CC_EXPORT LatencyInfoSwapPromise : public SwapPromise { virtual void DidSwap(CompositorFrameMetadata* metadata) OVERRIDE; virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE; + virtual int64 TraceId() const OVERRIDE; + private: ui::LatencyInfo latency_; }; diff --git a/cc/base/latency_info_swap_promise_monitor.cc b/cc/base/latency_info_swap_promise_monitor.cc index 0f2ff7481bb33..f724d6c05b78b 100644 --- a/cc/base/latency_info_swap_promise_monitor.cc +++ b/cc/base/latency_info_swap_promise_monitor.cc @@ -4,11 +4,38 @@ #include "cc/base/latency_info_swap_promise_monitor.h" +#include "base/threading/platform_thread.h" #include "cc/base/latency_info_swap_promise.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_impl.h" #include "cc/trees/layer_tree_impl.h" +namespace { + +bool AddRenderingScheduledComponent(ui::LatencyInfo* latency_info) { + if (latency_info->FindLatency( + ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, NULL)) + return false; + latency_info->AddLatencyNumber( + ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0); + return true; +} + +bool AddForwardingScrollUpdateToMainComponent(ui::LatencyInfo* latency_info) { + if (latency_info->FindLatency( + ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, + 0, + NULL)) + return false; + latency_info->AddLatencyNumber( + ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT, + 0, + latency_info->trace_id); + return true; +} + +} // namespace + namespace cc { LatencyInfoSwapPromiseMonitor::LatencyInfoSwapPromiseMonitor( @@ -21,23 +48,50 @@ LatencyInfoSwapPromiseMonitor::LatencyInfoSwapPromiseMonitor( LatencyInfoSwapPromiseMonitor::~LatencyInfoSwapPromiseMonitor() {} void LatencyInfoSwapPromiseMonitor::OnSetNeedsCommitOnMain() { - if (!latency_->FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0)) { - latency_->AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0); + if (AddRenderingScheduledComponent(latency_)) { scoped_ptr swap_promise(new LatencyInfoSwapPromise(*latency_)); layer_tree_host_->QueueSwapPromise(swap_promise.Pass()); } } void LatencyInfoSwapPromiseMonitor::OnSetNeedsRedrawOnImpl() { - if (!latency_->FindLatency( - ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0)) { - latency_->AddLatencyNumber( - ui::INPUT_EVENT_LATENCY_RENDERING_SCHEDULED_COMPONENT, 0, 0); + if (AddRenderingScheduledComponent(latency_)) { scoped_ptr swap_promise(new LatencyInfoSwapPromise(*latency_)); layer_tree_host_impl_->active_tree()->QueueSwapPromise(swap_promise.Pass()); } } +void LatencyInfoSwapPromiseMonitor::OnForwardScrollUpdateToMainThreadOnImpl() { + if (AddForwardingScrollUpdateToMainComponent(latency_)) { + int64 new_sequence_number = 0; + for (ui::LatencyInfo::LatencyMap::const_iterator it = + latency_->latency_components.begin(); + it != latency_->latency_components.end(); + ++it) { + if (it->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT) { + new_sequence_number = + (static_cast(base::PlatformThread::CurrentId()) << 32) | + (it->second.sequence_number & 0xffffffff); + DCHECK(new_sequence_number != it->second.sequence_number); + break; + } + } + if (!new_sequence_number) + return; + ui::LatencyInfo new_latency; + new_latency.AddLatencyNumber( + ui::INPUT_EVENT_LATENCY_BEGIN_SCROLL_UPDATE_MAIN_COMPONENT, + 0, + new_sequence_number); + new_latency.TraceEventType("ScrollUpdate"); + new_latency.CopyLatencyFrom( + *latency_, + ui::INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT); + scoped_ptr swap_promise( + new LatencyInfoSwapPromise(new_latency)); + layer_tree_host_impl_->QueueSwapPromiseForMainThreadScrollUpdate( + swap_promise.Pass()); + } +} + } // namespace cc diff --git a/cc/base/latency_info_swap_promise_monitor.h b/cc/base/latency_info_swap_promise_monitor.h index a463fdbeeff48..1a114fb07a737 100644 --- a/cc/base/latency_info_swap_promise_monitor.h +++ b/cc/base/latency_info_swap_promise_monitor.h @@ -26,6 +26,7 @@ class CC_EXPORT LatencyInfoSwapPromiseMonitor : public SwapPromiseMonitor { virtual void OnSetNeedsCommitOnMain() OVERRIDE; virtual void OnSetNeedsRedrawOnImpl() OVERRIDE; + virtual void OnForwardScrollUpdateToMainThreadOnImpl() OVERRIDE; private: ui::LatencyInfo* latency_; diff --git a/cc/base/math_util.cc b/cc/base/math_util.cc index 25049248f2979..7f6178dc60de5 100644 --- a/cc/base/math_util.cc +++ b/cc/base/math_util.cc @@ -743,6 +743,12 @@ void MathUtil::AddToTracedValue(const gfx::Vector2d& v, res->AppendInteger(v.y()); } +void MathUtil::AddToTracedValue(const gfx::Vector2dF& v, + base::debug::TracedValue* res) { + res->AppendDouble(v.x()); + res->AppendDouble(v.y()); +} + void MathUtil::AddToTracedValue(const gfx::QuadF& q, base::debug::TracedValue* res) { res->AppendDouble(q.p1().x()); diff --git a/cc/base/math_util.h b/cc/base/math_util.h index cf18b53250e5b..486a654a5e49a 100644 --- a/cc/base/math_util.h +++ b/cc/base/math_util.h @@ -204,6 +204,8 @@ class CC_EXPORT MathUtil { base::debug::TracedValue* res); static void AddToTracedValue(const gfx::Vector2d& v, base::debug::TracedValue* res); + static void AddToTracedValue(const gfx::Vector2dF& v, + base::debug::TracedValue* res); static void AddToTracedValue(const gfx::QuadF& q, base::debug::TracedValue* res); static void AddToTracedValue(const gfx::RectF& rect, diff --git a/cc/base/swap_promise.h b/cc/base/swap_promise.h index 00614cf65239f..a406fda6678b1 100644 --- a/cc/base/swap_promise.h +++ b/cc/base/swap_promise.h @@ -41,6 +41,11 @@ class CC_EXPORT SwapPromise { virtual void DidSwap(CompositorFrameMetadata* metadata) = 0; virtual void DidNotSwap(DidNotSwapReason reason) = 0; + + // A non-zero trace id identifies a trace flow object that is embedded in the + // swap promise. This can be used for registering additional flow steps to + // visualize the object's path through the system. + virtual int64 TraceId() const = 0; }; } // namespace cc diff --git a/cc/base/swap_promise_monitor.h b/cc/base/swap_promise_monitor.h index 21a159ad32a37..b8c8cd05b8a55 100644 --- a/cc/base/swap_promise_monitor.h +++ b/cc/base/swap_promise_monitor.h @@ -33,6 +33,7 @@ class CC_EXPORT SwapPromiseMonitor { virtual void OnSetNeedsCommitOnMain() = 0; virtual void OnSetNeedsRedrawOnImpl() = 0; + virtual void OnForwardScrollUpdateToMainThreadOnImpl() = 0; protected: LayerTreeHost* layer_tree_host_; diff --git a/cc/base/tiling_data.cc b/cc/base/tiling_data.cc index 9d4185dc4f8c7..879d421878749 100644 --- a/cc/base/tiling_data.cc +++ b/cc/base/tiling_data.cc @@ -126,7 +126,7 @@ int TilingData::LastBorderTileYIndexFromSrcCoord(int src_position) const { return std::min(std::max(y, 0), num_tiles_y_ - 1); } -gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBoundsWithBorders( +gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBounds( const gfx::Rect& rect) const { if (rect.IsEmpty() || has_empty_bounds()) return gfx::Rect(); @@ -137,8 +137,8 @@ gfx::Rect TilingData::ExpandRectIgnoringBordersToTileBoundsWithBorders( int index_right = TileXIndexFromSrcCoord(rect.right() - 1); int index_bottom = TileYIndexFromSrcCoord(rect.bottom() - 1); - gfx::Rect rect_top_left(TileBoundsWithBorder(index_x, index_y)); - gfx::Rect rect_bottom_right(TileBoundsWithBorder(index_right, index_bottom)); + gfx::Rect rect_top_left(TileBounds(index_x, index_y)); + gfx::Rect rect_bottom_right(TileBounds(index_right, index_bottom)); return gfx::UnionRects(rect_top_left, rect_bottom_right); } @@ -379,24 +379,17 @@ TilingData::DifferenceIterator::DifferenceIterator( return; } - consider_left_ = - tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x()); - consider_top_ = - tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y()); - consider_right_ = - tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1); + consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x()); + consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y()); + consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1); consider_bottom_ = - tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1); + tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1); if (!ignore.IsEmpty()) { - ignore_left_ = - tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x()); - ignore_top_ = - tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y()); - ignore_right_ = - tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1); - ignore_bottom_ = - tiling_data_->LastBorderTileYIndexFromSrcCoord(ignore.bottom() - 1); + ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x()); + ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y()); + ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1); + ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1); // Clamp ignore indices to consider indices. ignore_left_ = std::max(ignore_left_, consider_left_); @@ -488,21 +481,17 @@ TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator( return; } - consider_left_ = - tiling_data_->FirstBorderTileXIndexFromSrcCoord(consider.x()); - consider_top_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(consider.y()); - consider_right_ = - tiling_data_->LastBorderTileXIndexFromSrcCoord(consider.right() - 1); + consider_left_ = tiling_data_->TileXIndexFromSrcCoord(consider.x()); + consider_top_ = tiling_data_->TileYIndexFromSrcCoord(consider.y()); + consider_right_ = tiling_data_->TileXIndexFromSrcCoord(consider.right() - 1); consider_bottom_ = - tiling_data_->LastBorderTileYIndexFromSrcCoord(consider.bottom() - 1); + tiling_data_->TileYIndexFromSrcCoord(consider.bottom() - 1); if (!ignore.IsEmpty()) { - ignore_left_ = tiling_data_->FirstBorderTileXIndexFromSrcCoord(ignore.x()); - ignore_top_ = tiling_data_->FirstBorderTileYIndexFromSrcCoord(ignore.y()); - ignore_right_ = - tiling_data_->LastBorderTileXIndexFromSrcCoord(ignore.right() - 1); - ignore_bottom_ = - tiling_data_->LastBorderTileYIndexFromSrcCoord(ignore.bottom() - 1); + ignore_left_ = tiling_data_->TileXIndexFromSrcCoord(ignore.x()); + ignore_top_ = tiling_data_->TileYIndexFromSrcCoord(ignore.y()); + ignore_right_ = tiling_data_->TileXIndexFromSrcCoord(ignore.right() - 1); + ignore_bottom_ = tiling_data_->TileYIndexFromSrcCoord(ignore.bottom() - 1); // Clamp ignore indices to consider indices. ignore_left_ = std::max(ignore_left_, consider_left_); @@ -524,7 +513,7 @@ TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator( else if (center.x() > tiling_data->tiling_size().width()) around_left = tiling_data->num_tiles_x(); else - around_left = tiling_data->FirstBorderTileXIndexFromSrcCoord(center.x()); + around_left = tiling_data->TileXIndexFromSrcCoord(center.x()); // Determine around top, such that it is between -1 and num_tiles_y. int around_top = 0; @@ -533,7 +522,7 @@ TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator( else if (center.y() > tiling_data->tiling_size().height()) around_top = tiling_data->num_tiles_y(); else - around_top = tiling_data->FirstBorderTileYIndexFromSrcCoord(center.y()); + around_top = tiling_data->TileYIndexFromSrcCoord(center.y()); // Determine around right, such that it is between -1 and num_tiles_x. int right_src_coord = center.right() - 1; @@ -543,8 +532,7 @@ TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator( } else if (right_src_coord > tiling_data->tiling_size().width()) { around_right = tiling_data->num_tiles_x(); } else { - around_right = - tiling_data->LastBorderTileXIndexFromSrcCoord(right_src_coord); + around_right = tiling_data->TileXIndexFromSrcCoord(right_src_coord); } // Determine around bottom, such that it is between -1 and num_tiles_y. @@ -555,8 +543,7 @@ TilingData::SpiralDifferenceIterator::SpiralDifferenceIterator( } else if (bottom_src_coord > tiling_data->tiling_size().height()) { around_bottom = tiling_data->num_tiles_y(); } else { - around_bottom = - tiling_data->LastBorderTileYIndexFromSrcCoord(bottom_src_coord); + around_bottom = tiling_data->TileYIndexFromSrcCoord(bottom_src_coord); } vertical_step_count_ = around_bottom - around_top + 1; diff --git a/cc/base/tiling_data.h b/cc/base/tiling_data.h index 7cd9457095fac..a2e0a136967df 100644 --- a/cc/base/tiling_data.h +++ b/cc/base/tiling_data.h @@ -52,8 +52,7 @@ class CC_EXPORT TilingData { int LastBorderTileXIndexFromSrcCoord(int src_position) const; int LastBorderTileYIndexFromSrcCoord(int src_position) const; - gfx::Rect ExpandRectIgnoringBordersToTileBoundsWithBorders( - const gfx::Rect& rect) const; + gfx::Rect ExpandRectIgnoringBordersToTileBounds(const gfx::Rect& rect) const; gfx::Rect ExpandRectToTileBounds(const gfx::Rect& rect) const; gfx::Rect TileBounds(int i, int j) const; @@ -103,14 +102,13 @@ class CC_EXPORT TilingData { int bottom_; }; - // Iterate through all indices whose bounds + border intersect with - // |consider| but which also do not intersect with |ignore|. + // Iterate through all indices whose bounds (not including borders) intersect + // with |consider| but which also do not intersect with |ignore|. class CC_EXPORT DifferenceIterator : public BaseIterator { public: - DifferenceIterator( - const TilingData* tiling_data, - const gfx::Rect& consider_rect, - const gfx::Rect& ignore_rect); + DifferenceIterator(const TilingData* tiling_data, + const gfx::Rect& consider_rect, + const gfx::Rect& ignore_rect); DifferenceIterator& operator++(); private: diff --git a/cc/base/tiling_data_unittest.cc b/cc/base/tiling_data_unittest.cc index 60e5a21e89f73..9c2f9449b40bd 100644 --- a/cc/base/tiling_data_unittest.cc +++ b/cc/base/tiling_data_unittest.cc @@ -773,92 +773,82 @@ TEST(TilingDataTest, SetMaxTextureSizeBorders) { EXPECT_EQ(10, data.num_tiles_y()); } -TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsWithBordersEmpty) { +TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsEmpty) { TilingData empty_total_size(gfx::Size(0, 0), gfx::Size(8, 8), true); EXPECT_RECT_EQ( gfx::Rect(), - empty_total_size.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect())); - EXPECT_RECT_EQ( - gfx::Rect(), - empty_total_size.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect(100, 100, 100, 100))); - EXPECT_RECT_EQ( - gfx::Rect(), - empty_total_size.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect(100, 100))); + empty_total_size.ExpandRectIgnoringBordersToTileBounds(gfx::Rect())); + EXPECT_RECT_EQ(gfx::Rect(), + empty_total_size.ExpandRectIgnoringBordersToTileBounds( + gfx::Rect(100, 100, 100, 100))); + EXPECT_RECT_EQ(gfx::Rect(), + empty_total_size.ExpandRectIgnoringBordersToTileBounds( + gfx::Rect(100, 100))); TilingData empty_max_texture_size(gfx::Size(8, 8), gfx::Size(0, 0), true); - EXPECT_RECT_EQ( - gfx::Rect(), - empty_max_texture_size.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect())); - EXPECT_RECT_EQ( - gfx::Rect(), - empty_max_texture_size.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect(100, 100, 100, 100))); - EXPECT_RECT_EQ( - gfx::Rect(), - empty_max_texture_size.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect(100, 100))); + EXPECT_RECT_EQ(gfx::Rect(), + empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds( + gfx::Rect())); + EXPECT_RECT_EQ(gfx::Rect(), + empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds( + gfx::Rect(100, 100, 100, 100))); + EXPECT_RECT_EQ(gfx::Rect(), + empty_max_texture_size.ExpandRectIgnoringBordersToTileBounds( + gfx::Rect(100, 100))); } -TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBoundsWithBorders) { +TEST(TilingDataTest, ExpandRectIgnoringBordersToTileBounds) { TilingData data(gfx::Size(4, 4), gfx::Size(16, 32), true); // Small rect at origin rounds up to tile 0, 0. gfx::Rect at_origin_src(1, 1); - gfx::Rect at_origin_result(data.TileBoundsWithBorder(0, 0)); + gfx::Rect at_origin_result(data.TileBounds(0, 0)); EXPECT_NE(at_origin_src, at_origin_result); - EXPECT_RECT_EQ( - at_origin_result, - data.ExpandRectIgnoringBordersToTileBoundsWithBorders(at_origin_src)); + EXPECT_RECT_EQ(at_origin_result, + data.ExpandRectIgnoringBordersToTileBounds(at_origin_src)); // Arbitrary internal rect. gfx::Rect rect_src(6, 6, 1, 3); // Tile 2, 2 => gfx::Rect(4, 4, 4, 4) // Tile 2, 3 => gfx::Rect(4, 6, 4, 4) - gfx::Rect rect_result(gfx::UnionRects(data.TileBoundsWithBorder(2, 2), - data.TileBoundsWithBorder(2, 3))); + gfx::Rect rect_result( + gfx::UnionRects(data.TileBounds(2, 2), data.TileBounds(2, 3))); EXPECT_NE(rect_src, rect_result); - EXPECT_RECT_EQ( - rect_result, - data.ExpandRectIgnoringBordersToTileBoundsWithBorders(rect_src)); + EXPECT_RECT_EQ(rect_result, + data.ExpandRectIgnoringBordersToTileBounds(rect_src)); // On tile bounds does not round up to next tile (ignores the border). gfx::Rect border_rect_src( gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4))); - gfx::Rect border_rect_result(gfx::UnionRects( - data.TileBoundsWithBorder(1, 2), data.TileBoundsWithBorder(3, 4))); - EXPECT_RECT_EQ( - border_rect_result, - data.ExpandRectIgnoringBordersToTileBoundsWithBorders(border_rect_src)); + gfx::Rect border_rect_result( + gfx::UnionRects(data.TileBounds(1, 2), data.TileBounds(3, 4))); + EXPECT_RECT_EQ(border_rect_result, + data.ExpandRectIgnoringBordersToTileBounds(border_rect_src)); // Equal to tiling rect. EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()), - data.ExpandRectIgnoringBordersToTileBoundsWithBorders( + data.ExpandRectIgnoringBordersToTileBounds( gfx::Rect(data.tiling_size()))); // Containing, but larger than tiling rect. - EXPECT_RECT_EQ(gfx::Rect(data.tiling_size()), - data.ExpandRectIgnoringBordersToTileBoundsWithBorders( - gfx::Rect(100, 100))); + EXPECT_RECT_EQ( + gfx::Rect(data.tiling_size()), + data.ExpandRectIgnoringBordersToTileBounds(gfx::Rect(100, 100))); // Non-intersecting with tiling rect. gfx::Rect non_intersect(200, 200, 100, 100); EXPECT_FALSE(non_intersect.Intersects(gfx::Rect(data.tiling_size()))); - EXPECT_RECT_EQ( - gfx::Rect(), - data.ExpandRectIgnoringBordersToTileBoundsWithBorders(non_intersect)); + EXPECT_RECT_EQ(gfx::Rect(), + data.ExpandRectIgnoringBordersToTileBounds(non_intersect)); TilingData data2(gfx::Size(8, 8), gfx::Size(32, 64), true); // Inside other tile border texels doesn't include other tiles. gfx::Rect inner_rect_src(data2.TileBounds(1, 1)); inner_rect_src.Inset(data2.border_texels(), data.border_texels()); - gfx::Rect inner_rect_result(data2.TileBoundsWithBorder(1, 1)); + gfx::Rect inner_rect_result(data2.TileBounds(1, 1)); gfx::Rect expanded = - data2.ExpandRectIgnoringBordersToTileBoundsWithBorders(inner_rect_src); + data2.ExpandRectIgnoringBordersToTileBounds(inner_rect_src); EXPECT_EQ(inner_rect_result.ToString(), expanded.ToString()); } @@ -1086,11 +1076,11 @@ void TestIterate(const TilingData& data, } // Make sure this also works with a difference iterator and an empty ignore. - // The difference iterator always includes borders, so ignore it otherwise. - if (include_borders) { + // The difference iterator never includes borders, so ignore it otherwise. + if (!include_borders) { std::vector > expected = original_expected; - for (TilingData::DifferenceIterator iter(&data, rect, gfx::Rect()); - iter; ++iter) { + for (TilingData::DifferenceIterator iter(&data, rect, gfx::Rect()); iter; + ++iter) { bool found = false; for (size_t i = 0; i < expected.size(); ++i) { if (expected[i] == iter.index()) { @@ -1250,16 +1240,14 @@ TEST(TilingDataTest, IteratorNoTiles) { TestIterateAll(data, gfx::Rect(100, 100), 0, 0, -1, -1); } -void TestDiff( - const TilingData& data, - gfx::Rect consider, - gfx::Rect ignore, - size_t num_tiles) { - +void TestDiff(const TilingData& data, + gfx::Rect consider, + gfx::Rect ignore, + size_t num_tiles) { std::vector > expected; for (int y = 0; y < data.num_tiles_y(); ++y) { for (int x = 0; x < data.num_tiles_x(); ++x) { - gfx::Rect bounds = data.TileBoundsWithBorder(x, y); + gfx::Rect bounds = data.TileBounds(x, y); if (bounds.Intersects(consider) && !bounds.Intersects(ignore)) expected.push_back(std::make_pair(x, y)); } @@ -1268,8 +1256,8 @@ void TestDiff( // Sanity check the test. EXPECT_EQ(num_tiles, expected.size()); - for (TilingData::DifferenceIterator iter(&data, consider, ignore); - iter; ++iter) { + for (TilingData::DifferenceIterator iter(&data, consider, ignore); iter; + ++iter) { bool found = false; for (size_t i = 0; i < expected.size(); ++i) { if (expected[i] == iter.index()) { @@ -1339,16 +1327,26 @@ TEST(TilingDataTest, DifferenceIteratorIgnoreGeometry) { TEST(TilingDataTest, DifferenceIteratorManyBorderTexels) { // X border index by src coord: [0-50), [10-60), [20-65) // Y border index by src coord: [0-60), [20-80), [40-100), [60-110) + // X tile bounds by src coord: [0-30), [30-40), [40-65) + // Y tile bounds by src coord: [0-40), [40-60), [60-80), [80-110) TilingData data(gfx::Size(50, 60), gfx::Size(65, 110), 20); - // Ignore one column, three rows - TestDiff(data, gfx::Rect(0, 30, 55, 80), gfx::Rect(5, 30, 5, 15), 9); + // Knock out two rows, but not the left column. + TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 2), 8); - // Knock out three columns, leaving only one. - TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(30, 59, 20, 1), 3); + // Knock out one row. + TestDiff(data, gfx::Rect(10, 30, 55, 80), gfx::Rect(29, 59, 20, 1), 9); // Overlap all tiles with ignore rect. - TestDiff(data, gfx::Rect(65, 110), gfx::Rect(30, 59, 1, 2), 0); + TestDiff(data, gfx::Rect(65, 110), gfx::Rect(29, 39, 12, 42), 0); + + gfx::Rect tile = data.TileBounds(1, 1); + + // Ignore one tile. + TestDiff(data, gfx::Rect(20, 30, 45, 80), tile, 11); + + // Include one tile. + TestDiff(data, tile, gfx::Rect(), 1); } TEST(TilingDataTest, DifferenceIteratorOneTile) { diff --git a/cc/cc.gyp b/cc/cc.gyp index 83d408ece4d69..eac03d7a9fa81 100644 --- a/cc/cc.gyp +++ b/cc/cc.gyp @@ -318,6 +318,7 @@ 'quads/draw_quad.h', 'quads/io_surface_draw_quad.cc', 'quads/io_surface_draw_quad.h', + 'quads/largest_draw_quad.h', 'quads/picture_draw_quad.cc', 'quads/picture_draw_quad.h', 'quads/render_pass.cc', diff --git a/cc/cc_tests.gyp b/cc/cc_tests.gyp index 18d38f94e0bee..83ac8ffc881ee 100644 --- a/cc/cc_tests.gyp +++ b/cc/cc_tests.gyp @@ -30,6 +30,7 @@ 'layers/delegated_frame_provider_unittest.cc', 'layers/delegated_frame_resource_collection_unittest.cc', 'layers/delegated_renderer_layer_impl_unittest.cc', + 'layers/delegated_renderer_layer_unittest.cc', 'layers/heads_up_display_unittest.cc', 'layers/heads_up_display_layer_impl_unittest.cc', 'layers/io_surface_layer_impl_unittest.cc', @@ -250,6 +251,7 @@ 'type': '<(gtest_target_type)', 'dependencies': [ '../base/base.gyp:test_support_base', + '../gpu/command_buffer/command_buffer.gyp:gles2_utils', '../gpu/gpu.gyp:gpu', '../gpu/gpu.gyp:gpu_unittest_utils', '../media/media.gyp:media', @@ -304,6 +306,7 @@ 'type': '<(gtest_target_type)', 'dependencies': [ '../base/base.gyp:test_support_base', + '../gpu/command_buffer/command_buffer.gyp:gles2_utils', '../gpu/gpu.gyp:gpu', '../gpu/gpu.gyp:gpu_unittest_utils', '../media/media.gyp:media', @@ -366,6 +369,7 @@ 'dependencies': [ '../base/base.gyp:base', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../gpu/command_buffer/command_buffer.gyp:gles2_utils', '../gpu/gpu.gyp:gles2_c_lib', '../gpu/gpu.gyp:gles2_implementation', '../gpu/gpu.gyp:gl_in_process_context', diff --git a/cc/input/input_handler.h b/cc/input/input_handler.h index 9cf764eeca146..880c208771202 100644 --- a/cc/input/input_handler.h +++ b/cc/input/input_handler.h @@ -35,7 +35,8 @@ class CC_EXPORT InputHandlerClient { // Called when scroll deltas reaching the root scrolling layer go unused. // The accumulated overscroll is scoped by the most recent call to // InputHandler::ScrollBegin. - virtual void DidOverscroll(const gfx::Vector2dF& accumulated_overscroll, + virtual void DidOverscroll(const gfx::PointF& causal_event_viewport_point, + const gfx::Vector2dF& accumulated_overscroll, const gfx::Vector2dF& latest_overscroll_delta) = 0; protected: diff --git a/cc/layers/content_layer.cc b/cc/layers/content_layer.cc index d720501b56ddb..c1eec735e0bca 100644 --- a/cc/layers/content_layer.cc +++ b/cc/layers/content_layer.cc @@ -45,8 +45,13 @@ ContentLayer::ContentLayer(ContentLayerClient* client) ContentLayer::~ContentLayer() {} -bool ContentLayer::DrawsContent() const { - return TiledLayer::DrawsContent() && client_; +void ContentLayer::ClearClient() { + client_ = NULL; + UpdateDrawsContent(HasDrawableContent()); +} + +bool ContentLayer::HasDrawableContent() const { + return client_ && TiledLayer::HasDrawableContent(); } void ContentLayer::SetLayerTreeHost(LayerTreeHost* host) { diff --git a/cc/layers/content_layer.h b/cc/layers/content_layer.h index 9bef22bb70472..c440c6fa11c13 100644 --- a/cc/layers/content_layer.h +++ b/cc/layers/content_layer.h @@ -38,9 +38,8 @@ class CC_EXPORT ContentLayer : public TiledLayer { public: static scoped_refptr Create(ContentLayerClient* client); - void ClearClient() { client_ = NULL; } + void ClearClient(); - virtual bool DrawsContent() const OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* layer_tree_host) OVERRIDE; virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) OVERRIDE; @@ -60,6 +59,8 @@ class CC_EXPORT ContentLayer : public TiledLayer { explicit ContentLayer(ContentLayerClient* client); virtual ~ContentLayer(); + virtual bool HasDrawableContent() const OVERRIDE; + // TiledLayer implementation. virtual LayerUpdater* Updater() const OVERRIDE; diff --git a/cc/layers/contents_scaling_layer.cc b/cc/layers/contents_scaling_layer.cc index a53482a67274f..5811bdc53208b 100644 --- a/cc/layers/contents_scaling_layer.cc +++ b/cc/layers/contents_scaling_layer.cc @@ -22,10 +22,6 @@ ContentsScalingLayer::~ContentsScalingLayer() { void ContentsScalingLayer::CalculateContentsScale( float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { diff --git a/cc/layers/contents_scaling_layer.h b/cc/layers/contents_scaling_layer.h index 3e959622d753b..84780d25a6ed9 100644 --- a/cc/layers/contents_scaling_layer.h +++ b/cc/layers/contents_scaling_layer.h @@ -15,10 +15,6 @@ namespace cc { class CC_EXPORT ContentsScalingLayer : public Layer { public: virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE; diff --git a/cc/layers/delegated_renderer_layer.cc b/cc/layers/delegated_renderer_layer.cc index c47206172ceed..2dd12344df8ed 100644 --- a/cc/layers/delegated_renderer_layer.cc +++ b/cc/layers/delegated_renderer_layer.cc @@ -98,4 +98,8 @@ bool DelegatedRendererLayer::Update(ResourceUpdateQueue* queue, return true; } +bool DelegatedRendererLayer::HasDelegatedContent() const { + return true; +} + } // namespace cc diff --git a/cc/layers/delegated_renderer_layer.h b/cc/layers/delegated_renderer_layer.h index 3bc0aa8758f6b..0e4b95810dc9b 100644 --- a/cc/layers/delegated_renderer_layer.h +++ b/cc/layers/delegated_renderer_layer.h @@ -32,6 +32,7 @@ class CC_EXPORT DelegatedRendererLayer : public Layer { // Called by the DelegatedFrameProvider when a new frame is available to be // picked up. void ProviderHasNewFrame(); + virtual bool HasDelegatedContent() const OVERRIDE; protected: DelegatedRendererLayer( diff --git a/cc/layers/delegated_renderer_layer_impl.cc b/cc/layers/delegated_renderer_layer_impl.cc index 0fea04e852c89..9032fad7c9831 100644 --- a/cc/layers/delegated_renderer_layer_impl.cc +++ b/cc/layers/delegated_renderer_layer_impl.cc @@ -397,13 +397,14 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads( for (size_t i = 0; i < delegated_render_pass->quad_list.size(); ++i) { const DrawQuad* delegated_quad = delegated_render_pass->quad_list[i]; + bool is_root_delegated_render_pass = + delegated_render_pass == render_passes_in_draw_order_.back(); + if (delegated_quad->shared_quad_state != delegated_shared_quad_state) { delegated_shared_quad_state = delegated_quad->shared_quad_state; output_shared_quad_state = render_pass->CreateAndAppendSharedQuadState(); output_shared_quad_state->CopyFrom(delegated_shared_quad_state); - bool is_root_delegated_render_pass = - delegated_render_pass == render_passes_in_draw_order_.back(); if (is_root_delegated_render_pass) { gfx::Transform delegated_frame_to_target_transform = draw_transform(); delegated_frame_to_target_transform.Scale(inverse_device_scale_factor_, @@ -436,9 +437,17 @@ void DelegatedRendererLayerImpl::AppendRenderPassQuads( } DCHECK(output_shared_quad_state); + gfx::Transform quad_content_to_delegated_target_space = + output_shared_quad_state->content_to_target_transform; + if (!is_root_delegated_render_pass) { + quad_content_to_delegated_target_space.ConcatTransform( + render_pass->transform_to_root_target); + quad_content_to_delegated_target_space.ConcatTransform(draw_transform()); + } + gfx::Rect quad_visible_rect = occlusion_tracker.UnoccludedContentRect( - delegated_quad->visible_rect, - output_shared_quad_state->content_to_target_transform); + delegated_quad->visible_rect, quad_content_to_delegated_target_space); + if (quad_visible_rect.IsEmpty()) continue; diff --git a/cc/layers/delegated_renderer_layer_impl_unittest.cc b/cc/layers/delegated_renderer_layer_impl_unittest.cc index 33082cc6d2b70..69f54f9a4c873 100644 --- a/cc/layers/delegated_renderer_layer_impl_unittest.cc +++ b/cc/layers/delegated_renderer_layer_impl_unittest.cc @@ -311,6 +311,8 @@ TEST_F(DelegatedRendererLayerImplTestSimple, DoesOwnARenderSurfaceForOpacity) { delegated_renderer_layer_->SetOpacity(0.5f); LayerTreeHostImpl::FrameData frame; + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren( + host_impl_->active_tree()->root_layer()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); // This test case has quads from multiple layers in the delegated renderer, so @@ -329,6 +331,8 @@ TEST_F(DelegatedRendererLayerImplTestSimple, delegated_renderer_layer_->SetTransform(rotation); LayerTreeHostImpl::FrameData frame; + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren( + host_impl_->active_tree()->root_layer()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); // This test case has quads from multiple layers in the delegated renderer, so @@ -578,11 +582,10 @@ class DelegatedRendererLayerImplTestTransform gfx::Rect(5, 5, 7, 7), // quad_rect gfx::Rect(5, 5, 7, 7), // visible_rect RenderPass::Id(10, 7), // render_pass_id - false, // is_replica 0, // mask_resource_id - child_pass_rect, // contents_changed_since_last_frame gfx::RectF(), // mask_uv_rect FilterOperations(), // filters + gfx::Vector2dF(), // filters_scale FilterOperations()); // background_filters SolidColorDrawQuad* color_quad; @@ -1014,11 +1017,10 @@ class DelegatedRendererLayerImplTestClip gfx::Rect(5, 5, 7, 7), // quad_rect gfx::Rect(5, 5, 7, 7), // visible_quad_rect RenderPass::Id(10, 7), // render_pass_id - false, // is_replica 0, // mask_resource_id - child_pass_rect, // contents_changed_since_last_frame gfx::RectF(), // mask_uv_rect FilterOperations(), // filters + gfx::Vector2dF(), // filters_scale FilterOperations()); // background_filters SolidColorDrawQuad* color_quad; @@ -1507,6 +1509,33 @@ TEST_F(DelegatedRendererLayerImplTest, Occlusion) { impl.quad_list()[0]->visible_rect.ToString()); } } + { + gfx::Rect occluded(0, 0, 500, 1000); + // Move the occlusion to where it is in the contributing surface. + occluded -= quad_rect.OffsetFromOrigin() + gfx::Vector2d(11, 0); + + SCOPED_TRACE("Contributing render pass with transformed root"); + + delegated_renderer_layer_impl->SetTransform(transform); + impl.CalcDrawProps(viewport_size); + + impl.AppendQuadsForPassWithOcclusion( + delegated_renderer_layer_impl, pass2_id, occluded); + size_t partially_occluded_count = 0; + LayerTestCommon::VerifyQuadsCoverRectWithOcclusion( + impl.quad_list(), + gfx::Rect(quad_rect.size()), + occluded, + &partially_occluded_count); + // The layer outputs one quad, which is partially occluded. + EXPECT_EQ(1u, impl.quad_list().size()); + EXPECT_EQ(1u, partially_occluded_count); + // The quad in the contributing surface is at (222,300) in the transformed + // root. The occlusion extends to 500 in the x-axis, pushing the left of the + // visible part of the quad to 500 - 222 = 300 - 22 inside the quad. + EXPECT_EQ(gfx::Rect(300 - 22, 0, 100 + 22, 500).ToString(), + impl.quad_list()[0]->visible_rect.ToString()); + } } TEST_F(DelegatedRendererLayerImplTest, PushPropertiesTo) { diff --git a/cc/layers/delegated_renderer_layer_unittest.cc b/cc/layers/delegated_renderer_layer_unittest.cc new file mode 100644 index 0000000000000..b997a262a099d --- /dev/null +++ b/cc/layers/delegated_renderer_layer_unittest.cc @@ -0,0 +1,78 @@ +// 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 "cc/layers/delegated_renderer_layer.h" + +#include "cc/layers/delegated_frame_provider.h" +#include "cc/layers/delegated_frame_resource_collection.h" +#include "cc/layers/solid_color_layer.h" +#include "cc/output/delegated_frame_data.h" +#include "cc/test/fake_delegated_renderer_layer.h" +#include "cc/test/fake_layer_tree_host.h" +#include "cc/test/fake_proxy.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace cc { +namespace { + +class DelegatedRendererLayerTest : public testing::Test { + public: + DelegatedRendererLayerTest() : proxy_() { + LayerTreeSettings settings; + settings.minimum_occlusion_tracking_size = gfx::Size(); + + host_impl_ = FakeLayerTreeHost::Create(settings); + host_impl_->SetViewportSize(gfx::Size(10, 10)); + } + + protected: + FakeProxy proxy_; + TestSharedBitmapManager shared_bitmap_manager_; + scoped_ptr host_impl_; +}; + +class DelegatedRendererLayerTestSimple : public DelegatedRendererLayerTest { + public: + DelegatedRendererLayerTestSimple() : DelegatedRendererLayerTest() { + scoped_ptr root_pass(RenderPass::Create()); + root_pass->SetNew(RenderPass::Id(1, 1), + gfx::Rect(1, 1), + gfx::Rect(1, 1), + gfx::Transform()); + scoped_ptr frame_data(new DelegatedFrameData); + frame_data->render_pass_list.push_back(root_pass.Pass()); + resources_ = new DelegatedFrameResourceCollection; + provider_ = new DelegatedFrameProvider(resources_, frame_data.Pass()); + root_layer_ = SolidColorLayer::Create(); + layer_before_ = SolidColorLayer::Create(); + delegated_renderer_layer_ = FakeDelegatedRendererLayer::Create(provider_); + } + + protected: + scoped_refptr root_layer_; + scoped_refptr layer_before_; + scoped_refptr delegated_renderer_layer_; + scoped_refptr resources_; + scoped_refptr provider_; +}; + +TEST_F(DelegatedRendererLayerTestSimple, DelegatedManyDescendants) { + EXPECT_EQ(0, root_layer_->NumDescendantsThatDrawContent()); + root_layer_->AddChild(layer_before_); + EXPECT_EQ(0, root_layer_->NumDescendantsThatDrawContent()); + layer_before_->SetIsDrawable(true); + EXPECT_EQ(1, root_layer_->NumDescendantsThatDrawContent()); + EXPECT_EQ(0, layer_before_->NumDescendantsThatDrawContent()); + layer_before_->AddChild(delegated_renderer_layer_); + EXPECT_EQ(0, layer_before_->NumDescendantsThatDrawContent()); + EXPECT_EQ(0, delegated_renderer_layer_->NumDescendantsThatDrawContent()); + EXPECT_EQ(1, root_layer_->NumDescendantsThatDrawContent()); + delegated_renderer_layer_->SetIsDrawable(true); + EXPECT_EQ(1000, delegated_renderer_layer_->NumDescendantsThatDrawContent()); + EXPECT_EQ(1001, layer_before_->NumDescendantsThatDrawContent()); + EXPECT_EQ(1002, root_layer_->NumDescendantsThatDrawContent()); +} + +} // namespace +} // namespace cc diff --git a/cc/layers/draw_properties.h b/cc/layers/draw_properties.h index a0bfc2a6f2f74..4bbce727deff7 100644 --- a/cc/layers/draw_properties.h +++ b/cc/layers/draw_properties.h @@ -26,7 +26,6 @@ struct CC_EXPORT DrawProperties { render_target(NULL), contents_scale_x(1.f), contents_scale_y(1.f), - num_descendants_that_draw_content(0), num_unclipped_descendants(0), layer_or_descendant_has_copy_request(false), layer_or_descendant_has_input_handler(false), @@ -96,9 +95,6 @@ struct CC_EXPORT DrawProperties { float contents_scale_y; gfx::Size content_bounds; - // Does not include this layer itself, only its children and descendants. - int num_descendants_that_draw_content; - // Number of descendants with a clip parent that is our ancestor. NB - this // does not include our clip children because they are clipped by us. int num_unclipped_descendants; diff --git a/cc/layers/heads_up_display_layer.cc b/cc/layers/heads_up_display_layer.cc index ca63471e303d9..6adf8d06ff200 100644 --- a/cc/layers/heads_up_display_layer.cc +++ b/cc/layers/heads_up_display_layer.cc @@ -16,7 +16,10 @@ scoped_refptr HeadsUpDisplayLayer::Create() { return make_scoped_refptr(new HeadsUpDisplayLayer()); } -HeadsUpDisplayLayer::HeadsUpDisplayLayer() {} +HeadsUpDisplayLayer::HeadsUpDisplayLayer() { + SetIsDrawable(true); + UpdateDrawsContent(HasDrawableContent()); +} HeadsUpDisplayLayer::~HeadsUpDisplayLayer() {} @@ -47,7 +50,9 @@ void HeadsUpDisplayLayer::PrepareForCalculateDrawProperties( SetTransform(matrix); } -bool HeadsUpDisplayLayer::DrawsContent() const { return true; } +bool HeadsUpDisplayLayer::HasDrawableContent() const { + return true; +} scoped_ptr HeadsUpDisplayLayer::CreateLayerImpl( LayerTreeImpl* tree_impl) { diff --git a/cc/layers/heads_up_display_layer.h b/cc/layers/heads_up_display_layer.h index 4750fafc7ed34..12d6aefe56aa1 100644 --- a/cc/layers/heads_up_display_layer.h +++ b/cc/layers/heads_up_display_layer.h @@ -20,13 +20,13 @@ class CC_EXPORT HeadsUpDisplayLayer : public ContentsScalingLayer { void PrepareForCalculateDrawProperties( const gfx::Size& device_viewport, float device_scale_factor); - virtual bool DrawsContent() const OVERRIDE; virtual scoped_ptr CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; protected: HeadsUpDisplayLayer(); + virtual bool HasDrawableContent() const OVERRIDE; private: virtual ~HeadsUpDisplayLayer(); diff --git a/cc/layers/image_layer.cc b/cc/layers/image_layer.cc index cada84df3ccb4..67e620c6e2ed7 100644 --- a/cc/layers/image_layer.cc +++ b/cc/layers/image_layer.cc @@ -30,9 +30,14 @@ void ImageLayer::SetBitmap(const SkBitmap& bitmap) { return; bitmap_ = bitmap; + UpdateDrawsContent(HasDrawableContent()); SetNeedsDisplay(); } +bool ImageLayer::HasDrawableContent() const { + return !bitmap_.isNull() && TiledLayer::HasDrawableContent(); +} + void ImageLayer::SetTexturePriorities(const PriorityCalculator& priority_calc) { // Update the tile data before creating all the layer's tiles. UpdateTileSizeAndTilingOption(); @@ -65,10 +70,6 @@ LayerUpdater* ImageLayer::Updater() const { } void ImageLayer::CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { @@ -77,10 +78,6 @@ void ImageLayer::CalculateContentsScale(float ideal_contents_scale, *content_bounds = gfx::Size(bitmap_.width(), bitmap_.height()); } -bool ImageLayer::DrawsContent() const { - return !bitmap_.isNull() && TiledLayer::DrawsContent(); -} - void ImageLayer::OnOutputSurfaceCreated() { SetTextureFormat( layer_tree_host()->GetRendererCapabilities().best_texture_format); diff --git a/cc/layers/image_layer.h b/cc/layers/image_layer.h index 2f3255f092af7..63d467ae2e8c7 100644 --- a/cc/layers/image_layer.h +++ b/cc/layers/image_layer.h @@ -19,16 +19,11 @@ class CC_EXPORT ImageLayer : public TiledLayer { static scoped_refptr Create(); // Layer implementation. - virtual bool DrawsContent() const OVERRIDE; virtual void SetTexturePriorities(const PriorityCalculator& priority_calc) OVERRIDE; virtual bool Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE; @@ -36,6 +31,9 @@ class CC_EXPORT ImageLayer : public TiledLayer { void SetBitmap(const SkBitmap& image); + protected: + virtual bool HasDrawableContent() const OVERRIDE; + private: ImageLayer(); virtual ~ImageLayer(); diff --git a/cc/layers/io_surface_layer.cc b/cc/layers/io_surface_layer.cc index 98d3dfef1ba7b..0682aee8a2407 100644 --- a/cc/layers/io_surface_layer.cc +++ b/cc/layers/io_surface_layer.cc @@ -20,6 +20,7 @@ void IOSurfaceLayer::SetIOSurfaceProperties(uint32_t io_surface_id, const gfx::Size& size) { io_surface_id_ = io_surface_id; io_surface_size_ = size; + UpdateDrawsContent(HasDrawableContent()); SetNeedsCommit(); } @@ -28,8 +29,8 @@ scoped_ptr IOSurfaceLayer::CreateLayerImpl( return IOSurfaceLayerImpl::Create(tree_impl, layer_id_).PassAs(); } -bool IOSurfaceLayer::DrawsContent() const { - return io_surface_id_ && Layer::DrawsContent(); +bool IOSurfaceLayer::HasDrawableContent() const { + return io_surface_id_ && Layer::HasDrawableContent(); } void IOSurfaceLayer::PushPropertiesTo(LayerImpl* layer) { diff --git a/cc/layers/io_surface_layer.h b/cc/layers/io_surface_layer.h index 80e5b6917376f..ad06e7fc7b36e 100644 --- a/cc/layers/io_surface_layer.h +++ b/cc/layers/io_surface_layer.h @@ -18,12 +18,12 @@ class CC_EXPORT IOSurfaceLayer : public Layer { virtual scoped_ptr CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; - virtual bool DrawsContent() const OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; virtual bool Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; protected: + virtual bool HasDrawableContent() const OVERRIDE; IOSurfaceLayer(); private: diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc index 8722aa695a739..57c055b7fea35 100644 --- a/cc/layers/layer.cc +++ b/cc/layers/layer.cc @@ -47,6 +47,7 @@ Layer::Layer() parent_(NULL), layer_tree_host_(NULL), scroll_clip_layer_id_(INVALID_ID), + num_descendants_that_draw_content_(0), should_scroll_on_main_thread_(false), have_wheel_event_handlers_(false), have_scroll_event_handlers_(false), @@ -55,6 +56,7 @@ Layer::Layer() is_root_for_isolated_group_(false), is_container_for_fixed_position_layers_(false), is_drawable_(false), + draws_content_(false), hide_layer_and_subtree_(false), masks_to_bounds_(false), contents_opaque_(false), @@ -245,6 +247,8 @@ void Layer::AddChild(scoped_refptr child) { void Layer::InsertChild(scoped_refptr child, size_t index) { DCHECK(IsPropertyChangeAllowed()); child->RemoveFromParent(); + AddDrawableDescendants(child->NumDescendantsThatDrawContent() + + (child->DrawsContent() ? 1 : 0)); child->SetParent(this); child->stacking_order_changed_ = true; @@ -280,6 +284,8 @@ void Layer::RemoveChildOrDependent(Layer* child) { continue; child->SetParent(NULL); + AddDrawableDescendants(-child->NumDescendantsThatDrawContent() - + (child->DrawsContent() ? 1 : 0)); children_.erase(iter); SetNeedsFullTreeSync(); return; @@ -396,10 +402,6 @@ SkColor Layer::SafeOpaqueBackgroundColor() const { } void Layer::CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { @@ -779,7 +781,7 @@ void Layer::SetIsDrawable(bool is_drawable) { return; is_drawable_ = is_drawable; - SetNeedsCommit(); + UpdateDrawsContent(HasDrawableContent()); } void Layer::SetHideLayerAndSubtree(bool hide) { @@ -902,6 +904,7 @@ void Layer::PushPropertiesTo(LayerImpl* layer) { layer->SetTransformAndInvertibility(transform_, transform_is_invertible_); DCHECK(!(TransformIsAnimating() && layer->TransformIsAnimatingOnImplOnly())); layer->Set3dSortingContextId(sorting_context_id_); + layer->SetNumDescendantsThatDrawContent(num_descendants_that_draw_content_); layer->SetScrollClipLayer(scroll_clip_layer_id_); layer->set_user_scrollable_horizontal(user_scrollable_horizontal_); @@ -1008,9 +1011,38 @@ scoped_ptr Layer::CreateLayerImpl(LayerTreeImpl* tree_impl) { } bool Layer::DrawsContent() const { + return draws_content_; +} + +bool Layer::HasDrawableContent() const { return is_drawable_; } +void Layer::UpdateDrawsContent(bool has_drawable_content) { + bool draws_content = has_drawable_content; + DCHECK(is_drawable_ || !has_drawable_content); + if (draws_content == draws_content_) + return; + + if (HasDelegatedContent()) { + // Layers with delegated content need to be treated as if they have as + // many children as the number of layers they own delegated quads for. + // Since we don't know this number right now, we choose one that acts like + // infinity for our purposes. + AddDrawableDescendants(draws_content ? 1000 : -1000); + } + + if (parent()) + parent()->AddDrawableDescendants(draws_content ? 1 : -1); + + draws_content_ = draws_content; + SetNeedsCommit(); +} + +int Layer::NumDescendantsThatDrawContent() const { + return num_descendants_that_draw_content_; +} + void Layer::SavePaintProperties() { DCHECK(layer_tree_host_); @@ -1191,7 +1223,23 @@ void Layer::RemoveFromClipTree() { clip_parent_ = NULL; } +void Layer::AddDrawableDescendants(int num) { + DCHECK_GE(num_descendants_that_draw_content_, 0); + DCHECK_GE(num_descendants_that_draw_content_ + num, 0); + if (num == 0) + return; + num_descendants_that_draw_content_ += num; + SetNeedsCommit(); + if (parent()) + parent()->AddDrawableDescendants(num); +} + void Layer::RunMicroBenchmark(MicroBenchmark* benchmark) { benchmark->RunOnLayer(this); } + +bool Layer::HasDelegatedContent() const { + return false; +} + } // namespace cc diff --git a/cc/layers/layer.h b/cc/layers/layer.h index ab897a8ac9914..dfbbdf7b6f732 100644 --- a/cc/layers/layer.h +++ b/cc/layers/layer.h @@ -331,7 +331,7 @@ class CC_EXPORT Layer : public base::RefCounted, virtual void SetLayerTreeHost(LayerTreeHost* host); - bool HasDelegatedContent() const { return false; } + virtual bool HasDelegatedContent() const; bool HasContributingDelegatedRenderPasses() const { return false; } void SetIsDrawable(bool is_drawable); @@ -350,8 +350,13 @@ class CC_EXPORT Layer : public base::RefCounted, (mask_layer_.get() || replica_layer_->mask_layer_.get()); } - // These methods typically need to be overwritten by derived classes. + int NumDescendantsThatDrawContent() const; + + // This is only virtual for tests. + // TODO(awoloszyn): Remove this once we no longer need it for tests virtual bool DrawsContent() const; + + // This methods typically need to be overwritten by derived classes. virtual void SavePaintProperties(); // Returns true iff any resources were updated that need to be committed. virtual bool Update(ResourceUpdateQueue* queue, @@ -381,10 +386,6 @@ class CC_EXPORT Layer : public base::RefCounted, gfx::Size content_bounds() const { return draw_properties_.content_bounds; } virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds); @@ -486,6 +487,14 @@ class CC_EXPORT Layer : public base::RefCounted, // unused resources on the impl thread are returned before commit completes. void SetNextCommitWaitsForActivation(); + // Will recalculate whether the layer draws content and set draws_content_ + // appropriately. + void UpdateDrawsContent(bool has_drawable_content); + virtual bool HasDrawableContent() const; + + // Called when the layer's number of drawable descendants changes. + void AddDrawableDescendants(int num); + void AddDependentNeedsPushProperties(); void RemoveDependentNeedsPushProperties(); bool parent_should_know_need_push_properties() const { @@ -579,6 +588,7 @@ class CC_EXPORT Layer : public base::RefCounted, // transformed relative to this layer, defines the maximum scroll offset for // this layer. int scroll_clip_layer_id_; + int num_descendants_that_draw_content_; bool should_scroll_on_main_thread_ : 1; bool have_wheel_event_handlers_ : 1; bool have_scroll_event_handlers_ : 1; @@ -587,6 +597,7 @@ class CC_EXPORT Layer : public base::RefCounted, bool is_root_for_isolated_group_ : 1; bool is_container_for_fixed_position_layers_ : 1; bool is_drawable_ : 1; + bool draws_content_ : 1; bool hide_layer_and_subtree_ : 1; bool masks_to_bounds_ : 1; bool contents_opaque_ : 1; diff --git a/cc/layers/layer_impl.cc b/cc/layers/layer_impl.cc index bd9a2d7b21e22..87cb2f8a8cfe4 100644 --- a/cc/layers/layer_impl.cc +++ b/cc/layers/layer_impl.cc @@ -64,6 +64,7 @@ LayerImpl::LayerImpl(LayerTreeImpl* tree_impl, int id) background_color_(0), opacity_(1.0), blend_mode_(SkXfermode::kSrcOver_Mode), + num_descendants_that_draw_content_(0), draw_depth_(0.f), needs_push_properties_(false), num_dependents_need_push_properties_(0), @@ -176,6 +177,13 @@ void LayerImpl::SetScrollChildren(std::set* children) { SetNeedsPushProperties(); } +void LayerImpl::SetNumDescendantsThatDrawContent(int num_descendants) { + if (num_descendants_that_draw_content_ == num_descendants) + return; + num_descendants_that_draw_content_ = num_descendants; + SetNeedsPushProperties(); +} + void LayerImpl::SetClipParent(LayerImpl* ancestor) { if (clip_parent_ == ancestor) return; @@ -532,6 +540,7 @@ void LayerImpl::PushPropertiesTo(LayerImpl* layer) { scroll_offset_, layer->ScrollDelta() - layer->sent_scroll_delta()); layer->SetSentScrollDelta(gfx::Vector2d()); layer->Set3dSortingContextId(sorting_context_id_); + layer->SetNumDescendantsThatDrawContent(num_descendants_that_draw_content_); LayerImpl* scroll_parent = NULL; if (scroll_parent_) { @@ -762,7 +771,7 @@ bool LayerImpl::IsActive() const { // TODO(wjmaclean) Convert so that bounds returns SizeF. gfx::Size LayerImpl::bounds() const { - return ToFlooredSize(temporary_impl_bounds_); + return ToCeiledSize(temporary_impl_bounds_); } void LayerImpl::SetBounds(const gfx::Size& bounds) { @@ -1521,6 +1530,10 @@ void LayerImpl::RunMicroBenchmark(MicroBenchmarkImpl* benchmark) { benchmark->RunOnLayer(this); } +int LayerImpl::NumDescendantsThatDrawContent() const { + return num_descendants_that_draw_content_; +} + void LayerImpl::NotifyAnimationFinished( base::TimeTicks monotonic_time, Animation::TargetProperty target_property) { diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h index c1ce9df5c0198..b09cb3f497079 100644 --- a/cc/layers/layer_impl.h +++ b/cc/layers/layer_impl.h @@ -140,6 +140,7 @@ class CC_EXPORT LayerImpl : public LayerAnimationValueObserver, return scroll_children_.get(); } + void SetNumDescendantsThatDrawContent(int num_descendants); void SetClipParent(LayerImpl* ancestor); LayerImpl* clip_parent() { @@ -211,6 +212,7 @@ class CC_EXPORT LayerImpl : public LayerAnimationValueObserver, void SetDrawsContent(bool draws_content); bool DrawsContent() const { return draws_content_; } + int NumDescendantsThatDrawContent() const; void SetHideLayerAndSubtree(bool hide); bool hide_layer_and_subtree() const { return hide_layer_and_subtree_; } @@ -650,6 +652,8 @@ class CC_EXPORT LayerImpl : public LayerAnimationValueObserver, gfx::Vector2d sent_scroll_delta_; gfx::Vector2dF last_scroll_offset_; + int num_descendants_that_draw_content_; + // The global depth value of the center of the layer. This value is used // to sort layers from back to front. float draw_depth_; diff --git a/cc/layers/layer_impl_unittest.cc b/cc/layers/layer_impl_unittest.cc index 39469c1293dfc..c5cac8dfe8413 100644 --- a/cc/layers/layer_impl_unittest.cc +++ b/cc/layers/layer_impl_unittest.cc @@ -200,6 +200,8 @@ TEST(LayerImplTest, VerifyLayerChangesAreTrackedProperly) { root->SetClipParent(clip_parent.get())); EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE( root->SetClipChildren(clip_children)); + EXECUTE_AND_VERIFY_NEEDS_PUSH_PROPERTIES_AND_SUBTREE_DID_NOT_CHANGE( + root->SetNumDescendantsThatDrawContent(10)); // After setting all these properties already, setting to the exact same // values again should not cause any change. diff --git a/cc/layers/layer_unittest.cc b/cc/layers/layer_unittest.cc index 246ec43420fc3..61d05270265fc 100644 --- a/cc/layers/layer_unittest.cc +++ b/cc/layers/layer_unittest.cc @@ -56,7 +56,6 @@ class MockLayerPainter : public LayerPainter { gfx::RectF* opaque) OVERRIDE {} }; - class LayerTest : public testing::Test { public: LayerTest() @@ -1152,5 +1151,50 @@ TEST_F(LayerTest, SafeOpaqueBackgroundColor) { } } +class DrawsContentChangeLayer : public Layer { + public: + static scoped_refptr Create() { + return make_scoped_refptr(new DrawsContentChangeLayer()); + } + + virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE { + Layer::SetLayerTreeHost(host); + SetFakeDrawsContent(!fake_draws_content_); + } + + virtual bool HasDrawableContent() const OVERRIDE { + return fake_draws_content_ && Layer::HasDrawableContent(); + } + + void SetFakeDrawsContent(bool fake_draws_content) { + fake_draws_content_ = fake_draws_content; + UpdateDrawsContent(HasDrawableContent()); + } + + private: + DrawsContentChangeLayer() : Layer(), fake_draws_content_(false) {} + virtual ~DrawsContentChangeLayer() OVERRIDE {} + + bool fake_draws_content_; +}; + +TEST_F(LayerTest, DrawsContentChangedInSetLayerTreeHost) { + scoped_refptr root_layer = Layer::Create(); + scoped_refptr becomes_not_draws_content = + DrawsContentChangeLayer::Create(); + scoped_refptr becomes_draws_content = + DrawsContentChangeLayer::Create(); + root_layer->SetIsDrawable(true); + becomes_not_draws_content->SetIsDrawable(true); + becomes_not_draws_content->SetFakeDrawsContent(true); + EXPECT_EQ(0, root_layer->NumDescendantsThatDrawContent()); + root_layer->AddChild(becomes_not_draws_content); + EXPECT_EQ(0, root_layer->NumDescendantsThatDrawContent()); + + becomes_draws_content->SetIsDrawable(true); + root_layer->AddChild(becomes_draws_content); + EXPECT_EQ(1, root_layer->NumDescendantsThatDrawContent()); +} + } // namespace } // namespace cc diff --git a/cc/layers/painted_scrollbar_layer.cc b/cc/layers/painted_scrollbar_layer.cc index 3764689d0e39d..c0b2a728e3c90 100644 --- a/cc/layers/painted_scrollbar_layer.cc +++ b/cc/layers/painted_scrollbar_layer.cc @@ -98,19 +98,11 @@ float PaintedScrollbarLayer::ClampScaleToMaxTextureSize(float scale) { void PaintedScrollbarLayer::CalculateContentsScale( float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { ContentsScalingLayer::CalculateContentsScale( ClampScaleToMaxTextureSize(ideal_contents_scale), - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, contents_scale_x, contents_scale_y, content_bounds); diff --git a/cc/layers/painted_scrollbar_layer.h b/cc/layers/painted_scrollbar_layer.h index d684d322fada7..cd6a0460a43fa 100644 --- a/cc/layers/painted_scrollbar_layer.h +++ b/cc/layers/painted_scrollbar_layer.h @@ -43,10 +43,6 @@ class CC_EXPORT PaintedScrollbarLayer : public ScrollbarLayerInterface, virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; virtual void PushScrollClipPropertiesTo(LayerImpl* layer) OVERRIDE; virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE; diff --git a/cc/layers/picture_image_layer.cc b/cc/layers/picture_image_layer.cc index a65ac8c9318c4..4e468c8179cb3 100644 --- a/cc/layers/picture_image_layer.cc +++ b/cc/layers/picture_image_layer.cc @@ -24,8 +24,8 @@ scoped_ptr PictureImageLayer::CreateLayerImpl( return PictureImageLayerImpl::Create(tree_impl, id()).PassAs(); } -bool PictureImageLayer::DrawsContent() const { - return !bitmap_.isNull() && PictureLayer::DrawsContent(); +bool PictureImageLayer::HasDrawableContent() const { + return !bitmap_.isNull() && PictureLayer::HasDrawableContent(); } void PictureImageLayer::SetBitmap(const SkBitmap& bitmap) { @@ -37,6 +37,7 @@ void PictureImageLayer::SetBitmap(const SkBitmap& bitmap) { return; bitmap_ = bitmap; + UpdateDrawsContent(HasDrawableContent()); SetNeedsDisplay(); } @@ -54,17 +55,14 @@ void PictureImageLayer::PaintContents( SkFloatToScalar(static_cast(bounds().height()) / bitmap_.height()); canvas->scale(content_to_layer_scale_x, content_to_layer_scale_y); - // Because PictureImageLayer always FillsBoundsCompletely it will not clear - // before painting on playback. As a result we must configure the paint to - // copy over the uncleared destination, rather than blending with it. - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - canvas->drawBitmap(bitmap_, 0, 0, &paint); + // Because Android WebView resourceless software draw mode rasters directly + // to the root canvas, this draw must use the kSrcOver_Mode so that + // transparent images blend correctly. + canvas->drawBitmap(bitmap_, 0, 0); } bool PictureImageLayer::FillsBoundsCompletely() const { - // PictureImageLayer will always paint to the entire layer bounds. - return true; + return false; } } // namespace cc diff --git a/cc/layers/picture_image_layer.h b/cc/layers/picture_image_layer.h index 69b89d1b68ac3..0b3e32c404ff8 100644 --- a/cc/layers/picture_image_layer.h +++ b/cc/layers/picture_image_layer.h @@ -22,7 +22,6 @@ class CC_EXPORT PictureImageLayer : public PictureLayer, ContentLayerClient { // Layer implementation. virtual scoped_ptr CreateLayerImpl( LayerTreeImpl* tree_impl) OVERRIDE; - virtual bool DrawsContent() const OVERRIDE; // ContentLayerClient implementation. virtual void PaintContents( @@ -33,6 +32,9 @@ class CC_EXPORT PictureImageLayer : public PictureLayer, ContentLayerClient { virtual void DidChangeLayerCanUseLCDText() OVERRIDE {} virtual bool FillsBoundsCompletely() const OVERRIDE; + protected: + virtual bool HasDrawableContent() const OVERRIDE; + private: PictureImageLayer(); virtual ~PictureImageLayer(); diff --git a/cc/layers/picture_layer.cc b/cc/layers/picture_layer.cc index b9f265a02a4b1..4207aa248142b 100644 --- a/cc/layers/picture_layer.cc +++ b/cc/layers/picture_layer.cc @@ -4,6 +4,7 @@ #include "cc/layers/picture_layer.h" +#include "base/auto_reset.h" #include "cc/layers/content_layer_client.h" #include "cc/layers/picture_layer_impl.h" #include "cc/trees/layer_tree_impl.h" @@ -20,7 +21,6 @@ PictureLayer::PictureLayer(ContentLayerClient* client) : client_(client), pile_(make_scoped_refptr(new PicturePile())), instrumentation_object_tracker_(id()), - is_mask_(false), update_source_frame_number_(-1), can_use_lcd_text_last_frame_(can_use_lcd_text()) { } @@ -28,10 +28,6 @@ PictureLayer::PictureLayer(ContentLayerClient* client) PictureLayer::~PictureLayer() { } -bool PictureLayer::DrawsContent() const { - return Layer::DrawsContent() && client_; -} - scoped_ptr PictureLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) { return PictureLayerImpl::Create(tree_impl, id()).PassAs(); } @@ -53,8 +49,6 @@ void PictureLayer::PushPropertiesTo(LayerImpl* base_layer) { DCHECK_EQ(layer_impl->bounds().ToString(), pile_->tiling_size().ToString()); } - layer_impl->SetIsMask(is_mask_); - // Unlike other properties, invalidation must always be set on layer_impl. // See PictureLayerImpl::PushPropertiesTo for more details. layer_impl->invalidation_.Clear(); @@ -89,7 +83,11 @@ bool PictureLayer::Update(ResourceUpdateQueue* queue, update_source_frame_number_ = layer_tree_host()->source_frame_number(); bool updated = Layer::Update(queue, occlusion); - UpdateCanUseLCDText(); + { + base::AutoReset ignore_set_needs_commit(&ignore_set_needs_commit_, + true); + UpdateCanUseLCDText(); + } gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect( visible_content_rect(), 1.f / contents_scale_x()); @@ -148,7 +146,7 @@ bool PictureLayer::Update(ResourceUpdateQueue* queue, } void PictureLayer::SetIsMask(bool is_mask) { - is_mask_ = is_mask; + pile_->set_is_mask(is_mask); } Picture::RecordingMode PictureLayer::RecordingMode() const { @@ -200,6 +198,15 @@ bool PictureLayer::IsSuitableForGpuRasterization() const { return pile_->is_suitable_for_gpu_rasterization(); } +void PictureLayer::ClearClient() { + client_ = NULL; + UpdateDrawsContent(HasDrawableContent()); +} + +bool PictureLayer::HasDrawableContent() const { + return client_ && Layer::HasDrawableContent(); +} + void PictureLayer::RunMicroBenchmark(MicroBenchmark* benchmark) { benchmark->RunOnLayer(this); } diff --git a/cc/layers/picture_layer.h b/cc/layers/picture_layer.h index 406ffad2f1785..80bb4d867a6b0 100644 --- a/cc/layers/picture_layer.h +++ b/cc/layers/picture_layer.h @@ -21,10 +21,9 @@ class CC_EXPORT PictureLayer : public Layer { public: static scoped_refptr Create(ContentLayerClient* client); - void ClearClient() { client_ = NULL; } + void ClearClient(); // Layer interface. - virtual bool DrawsContent() const OVERRIDE; virtual scoped_ptr CreateLayerImpl( LayerTreeImpl* tree_impl) OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; @@ -49,6 +48,7 @@ class CC_EXPORT PictureLayer : public Layer { explicit PictureLayer(ContentLayerClient* client); virtual ~PictureLayer(); + virtual bool HasDrawableContent() const OVERRIDE; void UpdateCanUseLCDText(); private: diff --git a/cc/layers/picture_layer_impl.cc b/cc/layers/picture_layer_impl.cc index 54875bcdfc826..7636baff27958 100644 --- a/cc/layers/picture_layer_impl.cc +++ b/cc/layers/picture_layer_impl.cc @@ -60,7 +60,6 @@ PictureLayerImpl::PictureLayerImpl(LayerTreeImpl* tree_impl, int id) : LayerImpl(tree_impl, id), twin_layer_(NULL), pile_(PicturePileImpl::Create()), - is_mask_(false), ideal_page_scale_(0.f), ideal_device_scale_(0.f), ideal_source_scale_(0.f), @@ -108,11 +107,10 @@ void PictureLayerImpl::PushPropertiesTo(LayerImpl* base_layer) { LayerImpl::PushPropertiesTo(base_layer); // When the pending tree pushes to the active tree, the pending twin - // disappears. + // becomes recycled. layer_impl->twin_layer_ = NULL; twin_layer_ = NULL; - layer_impl->SetIsMask(is_mask_); layer_impl->pile_ = pile_; // Tilings would be expensive to push, so we swap. @@ -264,6 +262,12 @@ void PictureLayerImpl::AppendQuads( // unused can be considered for removal. std::vector seen_tilings; + // Ignore missing tiles outside of viewport for tile priority. This is + // normally the same as draw viewport but can be independently overridden by + // embedders like Android WebView with SetExternalDrawConstraints. + gfx::Rect scaled_viewport_for_tile_priority = gfx::ScaleToEnclosingRect( + GetViewportForTilePriorityInContentSpace(), max_contents_scale); + size_t missing_tile_count = 0u; size_t on_demand_missing_tile_count = 0u; for (PictureLayerTilingSet::CoverageIterator iter(tilings_.get(), @@ -281,6 +285,7 @@ void PictureLayerImpl::AppendQuads( append_quads_data->visible_content_area += visible_geometry_rect.width() * visible_geometry_rect.height(); + bool has_draw_quad = false; if (*iter && iter->IsReadyToDraw()) { const ManagedTileState::TileVersion& tile_version = iter->GetTileVersionForDrawing(); @@ -290,8 +295,10 @@ void PictureLayerImpl::AppendQuads( gfx::Rect opaque_rect = iter->opaque_rect(); opaque_rect.Intersect(geometry_rect); - if (iter->contents_scale() != ideal_contents_scale_) + if (iter->contents_scale() != ideal_contents_scale_ && + geometry_rect.Intersects(scaled_viewport_for_tile_priority)) { append_quads_data->num_incomplete_tiles++; + } TileDrawQuad* quad = render_pass->CreateAndAppendDrawQuad(); @@ -303,6 +310,7 @@ void PictureLayerImpl::AppendQuads( texture_rect, iter.texture_size(), tile_version.contents_swizzled()); + has_draw_quad = true; break; } case ManagedTileState::TileVersion::PICTURE_PILE_MODE: { @@ -333,6 +341,7 @@ void PictureLayerImpl::AppendQuads( iter->content_rect(), iter->contents_scale(), pile_); + has_draw_quad = true; break; } case ManagedTileState::TileVersion::SOLID_COLOR_MODE: { @@ -343,10 +352,13 @@ void PictureLayerImpl::AppendQuads( visible_geometry_rect, tile_version.get_solid_color(), false); + has_draw_quad = true; break; } } - } else { + } + + if (!has_draw_quad) { if (draw_checkerboard_for_missing_tiles()) { CheckerboardDrawQuad* quad = render_pass->CreateAndAppendDrawQuad(); @@ -364,10 +376,12 @@ void PictureLayerImpl::AppendQuads( false); } - append_quads_data->num_missing_tiles++; + if (geometry_rect.Intersects(scaled_viewport_for_tile_priority)) { + append_quads_data->num_missing_tiles++; + ++missing_tile_count; + } append_quads_data->approximated_visible_content_area += visible_geometry_rect.width() * visible_geometry_rect.height(); - ++missing_tile_count; continue; } @@ -472,6 +486,28 @@ void PictureLayerImpl::UpdateTilePriorities( if (!tiling_needs_update) return; + gfx::Rect visible_rect_in_content_space( + GetViewportForTilePriorityInContentSpace()); + visible_rect_in_content_space.Intersect(visible_content_rect()); + gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect( + visible_rect_in_content_space, 1.f / contents_scale_x()); + WhichTree tree = + layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE; + for (size_t i = 0; i < tilings_->num_tilings(); ++i) { + tilings_->tiling_at(i)->UpdateTilePriorities(tree, + visible_layer_rect, + ideal_contents_scale_, + current_frame_time_in_seconds, + occlusion_tracker, + render_target(), + draw_transform()); + } + + // Tile priorities were modified. + layer_tree_impl()->DidModifyTilePriorities(); +} + +gfx::Rect PictureLayerImpl::GetViewportForTilePriorityInContentSpace() const { // If visible_rect_for_tile_priority_ is empty or // viewport_rect_for_tile_priority_ is set to be different from the device // viewport, try to inverse project the viewport into layer space and use @@ -492,22 +528,13 @@ void PictureLayerImpl::UpdateTilePriorities( } } - gfx::Rect visible_layer_rect = gfx::ScaleToEnclosingRect( - visible_rect_in_content_space, 1.f / contents_scale_x()); - WhichTree tree = - layer_tree_impl()->IsActiveTree() ? ACTIVE_TREE : PENDING_TREE; - for (size_t i = 0; i < tilings_->num_tilings(); ++i) { - tilings_->tiling_at(i)->UpdateTilePriorities(tree, - visible_layer_rect, - ideal_contents_scale_, - current_frame_time_in_seconds, - occlusion_tracker, - render_target(), - draw_transform()); - } + return visible_rect_in_content_space; +} - // Tile priorities were modified. - layer_tree_impl()->DidModifyTilePriorities(); +PictureLayerImpl* PictureLayerImpl::GetRecycledTwinLayer() { + // TODO(vmpstr): Maintain recycled twin as a member. crbug.com/407418 + return static_cast( + layer_tree_impl()->FindRecycleTreeLayerById(id())); } void PictureLayerImpl::NotifyTileStateChanged(const Tile* tile) { @@ -552,9 +579,12 @@ scoped_refptr PictureLayerImpl::CreateTile(PictureLayerTiling* tiling, // TODO(vmpstr): Revisit this. For now, enabling analysis means that we get as // much savings on memory as we can. However, for some cases like ganesh or // small layers, the amount of time we spend analyzing might not justify - // memory savings that we can get. + // memory savings that we can get. Note that we don't handle solid color + // masks, so we shouldn't bother analyzing those. // Bugs: crbug.com/397198, crbug.com/396908 - int flags = Tile::USE_PICTURE_ANALYSIS; + int flags = 0; + if (!pile_->is_mask()) + flags = Tile::USE_PICTURE_ANALYSIS; return layer_tree_impl()->tile_manager()->CreateTile( pile_.get(), @@ -586,6 +616,14 @@ const PictureLayerTiling* PictureLayerImpl::GetTwinTiling( return NULL; } +PictureLayerTiling* PictureLayerImpl::GetRecycledTwinTiling( + const PictureLayerTiling* tiling) { + PictureLayerImpl* recycled_twin = GetRecycledTwinLayer(); + if (!recycled_twin || !recycled_twin->tilings_) + return NULL; + return recycled_twin->tilings_->TilingAtScale(tiling->contents_scale()); +} + size_t PictureLayerImpl::GetMaxTilesForInterestArea() const { return layer_tree_impl()->settings().max_tiles_for_interest_area; } @@ -608,16 +646,18 @@ int PictureLayerImpl::GetSkewportExtrapolationLimitInContentPixels() const { gfx::Size PictureLayerImpl::CalculateTileSize( const gfx::Size& content_bounds) const { - if (is_mask_) { - int max_size = layer_tree_impl()->MaxTextureSize(); - return gfx::Size( - std::min(max_size, content_bounds.width()), - std::min(max_size, content_bounds.height())); - } - int max_texture_size = layer_tree_impl()->resource_provider()->max_texture_size(); + if (pile_->is_mask()) { + // Masks are not tiled, so if we can't cover the whole mask with one tile, + // don't make any tiles at all. Returning an empty size signals this. + if (content_bounds.width() > max_texture_size || + content_bounds.height() > max_texture_size) + return gfx::Size(); + return content_bounds; + } + gfx::Size default_tile_size = layer_tree_impl()->settings().default_tile_size; if (layer_tree_impl()->use_gpu_rasterization()) { // TODO(ernstm) crbug.com/365877: We need a unified way to override the @@ -719,14 +759,6 @@ void PictureLayerImpl::SyncTiling( } } -void PictureLayerImpl::SetIsMask(bool is_mask) { - if (is_mask_ == is_mask) - return; - is_mask_ = is_mask; - if (tilings_) - tilings_->RemoveAllTiles(); -} - ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const { gfx::Rect content_rect(content_bounds()); float scale = MaximumTilingContentsScale(); @@ -738,8 +770,9 @@ ResourceProvider::ResourceId PictureLayerImpl::ContentsResourceId() const { return 0; // Masks only supported if they fit on exactly one tile. - if (iter.geometry_rect() != content_rect) - return 0; + DCHECK(iter.geometry_rect() == content_rect) + << "iter rect " << iter.geometry_rect().ToString() << " content rect " + << content_rect.ToString(); const ManagedTileState::TileVersion& tile_version = iter->GetTileVersionForDrawing(); @@ -765,6 +798,12 @@ void PictureLayerImpl::MarkVisibleResourcesAsRequired() const { gfx::Rect rect(visible_content_rect()); + // Only mark tiles inside the viewport for tile priority as required for + // activation. This viewport is normally the same as the draw viewport but + // can be independently overridden by embedders like Android WebView with + // SetExternalDrawConstraints. + rect.Intersect(GetViewportForTilePriorityInContentSpace()); + float min_acceptable_scale = std::min(raster_contents_scale_, ideal_contents_scale_); @@ -1140,13 +1179,14 @@ void PictureLayerImpl::RecalculateRasterScales() { } } - // If this layer would only create one tile at this content scale, + // If this layer would create zero or one tiles at this content scale, // don't create a low res tiling. gfx::Size content_bounds = gfx::ToCeiledSize(gfx::ScaleSize(bounds(), raster_contents_scale_)); gfx::Size tile_size = CalculateTileSize(content_bounds); - if (tile_size.width() >= content_bounds.width() && - tile_size.height() >= content_bounds.height()) { + bool tile_covers_bounds = tile_size.width() >= content_bounds.width() && + tile_size.height() >= content_bounds.height(); + if (tile_size.IsEmpty() || tile_covers_bounds) { low_res_raster_contents_scale_ = raster_contents_scale_; return; } @@ -1214,8 +1254,7 @@ void PictureLayerImpl::CleanUpTilingsOnActiveLayer( if (to_remove.empty()) return; - PictureLayerImpl* recycled_twin = static_cast( - layer_tree_impl()->FindRecycleTreeLayerById(id())); + PictureLayerImpl* recycled_twin = GetRecycledTwinLayer(); // Remove tilings on this tree and the twin tree. for (size_t i = 0; i < to_remove.size(); ++i) { const PictureLayerTiling* twin_tiling = GetTwinTiling(to_remove[i]); @@ -1550,39 +1589,34 @@ const Tile* PictureLayerImpl::LayerRasterTileIterator::operator*() const { } PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator() - : current_range_offset_(0), - current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), - current_stage_(EVENTUALLY), + : layer_(NULL), tree_priority_(SAME_PRIORITY_FOR_BOTH_TREES), - layer_(NULL) { + current_category_(PictureLayerTiling::EVENTUALLY), + current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), + current_tiling_(0u) { } PictureLayerImpl::LayerEvictionTileIterator::LayerEvictionTileIterator( PictureLayerImpl* layer, TreePriority tree_priority) - : current_range_offset_(0), - current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), - current_stage_(EVENTUALLY), + : layer_(layer), tree_priority_(tree_priority), - layer_(layer) { - // Early out if the tilings_ object doesn't exist. + current_category_(PictureLayerTiling::EVENTUALLY), + current_tiling_range_type_(PictureLayerTilingSet::HIGHER_THAN_HIGH_RES), + current_tiling_(CurrentTilingRange().start - 1u) { // TODO(vmpstr): Once tile priorities are determined by the iterators, ensure // that layers that don't have valid tile priorities have lowest priorities so // they evict their tiles first (crbug.com/381704) - if (!layer_->tilings_) - return; - - if (!CurrentRange().IsIndexWithinRange(CurrentTilingIndex())) - AdvanceRange(); - - iterator_ = PictureLayerTiling::TilingEvictionTileIterator( - layer_->tilings_->tiling_at(CurrentTilingIndex()), - tree_priority, - PriorityBinFromIterationStage(current_stage_), - RequiredForActivationFromIterationStage(current_stage_)); + DCHECK(layer_->tilings_); + do { + if (!AdvanceToNextTiling()) + break; - if (!iterator_) - AdvanceToNextIterator(); + current_iterator_ = PictureLayerTiling::TilingEvictionTileIterator( + layer_->tilings_->tiling_at(CurrentTilingIndex()), + tree_priority, + current_category_); + } while (!current_iterator_); } PictureLayerImpl::LayerEvictionTileIterator::~LayerEvictionTileIterator() { @@ -1590,160 +1624,121 @@ PictureLayerImpl::LayerEvictionTileIterator::~LayerEvictionTileIterator() { Tile* PictureLayerImpl::LayerEvictionTileIterator::operator*() { DCHECK(*this); - return *iterator_; + return *current_iterator_; } const Tile* PictureLayerImpl::LayerEvictionTileIterator::operator*() const { DCHECK(*this); - return *iterator_; + return *current_iterator_; } PictureLayerImpl::LayerEvictionTileIterator& PictureLayerImpl::LayerEvictionTileIterator:: operator++() { DCHECK(*this); - ++iterator_; - - if (!iterator_) - AdvanceToNextIterator(); - return *this; -} - -void PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextIterator() { - DCHECK(!iterator_); - while (!iterator_) { - bool success = AdvanceTiling(); - if (!success) - success = AdvanceRange(); - if (!success) - success = AdvanceStage(); - if (!success) + ++current_iterator_; + while (!current_iterator_) { + if (!AdvanceToNextTiling()) break; - iterator_ = PictureLayerTiling::TilingEvictionTileIterator( + current_iterator_ = PictureLayerTiling::TilingEvictionTileIterator( layer_->tilings_->tiling_at(CurrentTilingIndex()), tree_priority_, - PriorityBinFromIterationStage(current_stage_), - RequiredForActivationFromIterationStage(current_stage_)); + current_category_); } + return *this; } -bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceTiling() { - DCHECK(CurrentRange().IsIndexWithinRange(CurrentTilingIndex())); - ++current_range_offset_; - return CurrentRange().IsIndexWithinRange(CurrentTilingIndex()); -} - -bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceRange() { - DCHECK(!CurrentRange().IsIndexWithinRange(CurrentTilingIndex())); - bool wrapped_around = false; - while (!CurrentRange().IsIndexWithinRange(CurrentTilingIndex())) { - switch (current_tiling_range_type_) { - case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: - current_tiling_range_type_ = PictureLayerTilingSet::LOWER_THAN_LOW_RES; - break; - case PictureLayerTilingSet::LOWER_THAN_LOW_RES: - current_tiling_range_type_ = - PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES; - break; - case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: - current_tiling_range_type_ = PictureLayerTilingSet::LOW_RES; - break; - case PictureLayerTilingSet::LOW_RES: - current_tiling_range_type_ = PictureLayerTilingSet::HIGH_RES; - break; - case PictureLayerTilingSet::HIGH_RES: - current_tiling_range_type_ = - PictureLayerTilingSet::HIGHER_THAN_HIGH_RES; - wrapped_around = true; - break; - } - current_range_offset_ = 0; - } - // If we wrapped around the ranges, we need to indicate that we should advance - // the stage. - return !wrapped_around; -} - -TilePriority::PriorityBin -PictureLayerImpl::LayerEvictionTileIterator::PriorityBinFromIterationStage( - IterationStage stage) { - switch (stage) { - case EVENTUALLY: - case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION: - return TilePriority::EVENTUALLY; - case SOON: - case SOON_AND_REQUIRED_FOR_ACTIVATION: - return TilePriority::SOON; - case NOW: - case NOW_AND_REQUIRED_FOR_ACTIVATION: - return TilePriority::NOW; +PictureLayerImpl::LayerEvictionTileIterator::operator bool() const { + return !!current_iterator_; +} + +bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextCategory() { + switch (current_category_) { + case PictureLayerTiling::EVENTUALLY: + current_category_ = + PictureLayerTiling::EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION; + return true; + case PictureLayerTiling::EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION: + current_category_ = PictureLayerTiling::SOON; + return true; + case PictureLayerTiling::SOON: + current_category_ = PictureLayerTiling::SOON_AND_REQUIRED_FOR_ACTIVATION; + return true; + case PictureLayerTiling::SOON_AND_REQUIRED_FOR_ACTIVATION: + current_category_ = PictureLayerTiling::NOW; + return true; + case PictureLayerTiling::NOW: + current_category_ = PictureLayerTiling::NOW_AND_REQUIRED_FOR_ACTIVATION; + return true; + case PictureLayerTiling::NOW_AND_REQUIRED_FOR_ACTIVATION: + return false; } NOTREACHED(); - return TilePriority::EVENTUALLY; + return false; } -bool PictureLayerImpl::LayerEvictionTileIterator:: - RequiredForActivationFromIterationStage(IterationStage stage) { - switch (stage) { - case EVENTUALLY: - case SOON: - case NOW: - return false; - case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION: - case SOON_AND_REQUIRED_FOR_ACTIVATION: - case NOW_AND_REQUIRED_FOR_ACTIVATION: +bool +PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextTilingRangeType() { + switch (current_tiling_range_type_) { + case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: + current_tiling_range_type_ = PictureLayerTilingSet::LOWER_THAN_LOW_RES; + return true; + case PictureLayerTilingSet::LOWER_THAN_LOW_RES: + current_tiling_range_type_ = + PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES; + return true; + case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: + current_tiling_range_type_ = PictureLayerTilingSet::LOW_RES; + return true; + case PictureLayerTilingSet::LOW_RES: + current_tiling_range_type_ = PictureLayerTilingSet::HIGH_RES; + return true; + case PictureLayerTilingSet::HIGH_RES: + if (!AdvanceToNextCategory()) + return false; + + current_tiling_range_type_ = PictureLayerTilingSet::HIGHER_THAN_HIGH_RES; return true; } NOTREACHED(); return false; } +bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceToNextTiling() { + DCHECK_NE(current_tiling_, CurrentTilingRange().end); + ++current_tiling_; + while (current_tiling_ == CurrentTilingRange().end) { + if (!AdvanceToNextTilingRangeType()) + return false; + + current_tiling_ = CurrentTilingRange().start; + } + return true; +} + PictureLayerTilingSet::TilingRange -PictureLayerImpl::LayerEvictionTileIterator::CurrentRange() { +PictureLayerImpl::LayerEvictionTileIterator::CurrentTilingRange() const { return layer_->tilings_->GetTilingRange(current_tiling_range_type_); } -int PictureLayerImpl::LayerEvictionTileIterator::CurrentTilingIndex() { - const PictureLayerTilingSet::TilingRange& range = CurrentRange(); +size_t PictureLayerImpl::LayerEvictionTileIterator::CurrentTilingIndex() const { + DCHECK_NE(current_tiling_, CurrentTilingRange().end); switch (current_tiling_range_type_) { case PictureLayerTilingSet::HIGHER_THAN_HIGH_RES: case PictureLayerTilingSet::LOW_RES: case PictureLayerTilingSet::HIGH_RES: - return range.start + current_range_offset_; + return current_tiling_; + // Tilings in the following ranges are accessed in reverse order. case PictureLayerTilingSet::BETWEEN_HIGH_AND_LOW_RES: - case PictureLayerTilingSet::LOWER_THAN_LOW_RES: - return static_cast(range.end) - 1 - current_range_offset_; + case PictureLayerTilingSet::LOWER_THAN_LOW_RES: { + PictureLayerTilingSet::TilingRange tiling_range = CurrentTilingRange(); + size_t current_tiling_range_offset = current_tiling_ - tiling_range.start; + return tiling_range.end - 1 - current_tiling_range_offset; + } } NOTREACHED(); return 0; } -bool PictureLayerImpl::LayerEvictionTileIterator::AdvanceStage() { - switch (current_stage_) { - case EVENTUALLY: - current_stage_ = EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION; - break; - case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION: - current_stage_ = SOON; - break; - case SOON: - current_stage_ = SOON_AND_REQUIRED_FOR_ACTIVATION; - break; - case SOON_AND_REQUIRED_FOR_ACTIVATION: - current_stage_ = NOW; - break; - case NOW: - current_stage_ = NOW_AND_REQUIRED_FOR_ACTIVATION; - break; - case NOW_AND_REQUIRED_FOR_ACTIVATION: - return false; - } - return true; -} - -PictureLayerImpl::LayerEvictionTileIterator::operator bool() const { - return !!iterator_; -} - } // namespace cc diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h index d4af4785cfb6e..616672063a4e1 100644 --- a/cc/layers/picture_layer_impl.h +++ b/cc/layers/picture_layer_impl.h @@ -77,34 +77,20 @@ class CC_EXPORT PictureLayerImpl operator bool() const; private: - enum IterationStage { - EVENTUALLY, - EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION, - SOON, - SOON_AND_REQUIRED_FOR_ACTIVATION, - NOW, - NOW_AND_REQUIRED_FOR_ACTIVATION - }; - - TilePriority::PriorityBin PriorityBinFromIterationStage( - IterationStage stage); - bool RequiredForActivationFromIterationStage(IterationStage stage); + bool AdvanceToNextCategory(); + bool AdvanceToNextTilingRangeType(); + bool AdvanceToNextTiling(); - PictureLayerTilingSet::TilingRange CurrentRange(); - int CurrentTilingIndex(); + PictureLayerTilingSet::TilingRange CurrentTilingRange() const; + size_t CurrentTilingIndex() const; - void AdvanceToNextIterator(); - bool AdvanceTiling(); - bool AdvanceRange(); - bool AdvanceStage(); + PictureLayerImpl* layer_; + TreePriority tree_priority_; - PictureLayerTiling::TilingEvictionTileIterator iterator_; - int current_range_offset_; + PictureLayerTiling::EvictionCategory current_category_; PictureLayerTilingSet::TilingRangeType current_tiling_range_type_; - IterationStage current_stage_; - - TreePriority tree_priority_; - PictureLayerImpl* layer_; + size_t current_tiling_; + PictureLayerTiling::TilingEvictionTileIterator current_iterator_; }; static scoped_ptr Create(LayerTreeImpl* tree_impl, int id) { @@ -138,6 +124,8 @@ class CC_EXPORT PictureLayerImpl virtual const Region* GetInvalidation() OVERRIDE; virtual const PictureLayerTiling* GetTwinTiling( const PictureLayerTiling* tiling) const OVERRIDE; + virtual PictureLayerTiling* GetRecycledTwinTiling( + const PictureLayerTiling* tiling) OVERRIDE; virtual size_t GetMaxTilesForInterestArea() const OVERRIDE; virtual float GetSkewportTargetTimeInSeconds() const OVERRIDE; virtual int GetSkewportExtrapolationLimitInContentPixels() const OVERRIDE; @@ -146,8 +134,7 @@ class CC_EXPORT PictureLayerImpl // PushPropertiesTo active tree => pending tree. void SyncTiling(const PictureLayerTiling* tiling); - // Mask-related functions - void SetIsMask(bool is_mask); + // Mask-related functions. virtual ResourceProvider::ResourceId ContentsResourceId() const OVERRIDE; virtual size_t GPUMemoryUsageInBytes() const OVERRIDE; @@ -157,7 +144,8 @@ class CC_EXPORT PictureLayerImpl // Functions used by tile manager. PictureLayerImpl* GetTwinLayer() { return twin_layer_; } bool IsOnActiveOrPendingTree() const; - bool HasValidTilePriorities() const; + // Virtual for testing. + virtual bool HasValidTilePriorities() const; bool AllTilesRequiredForActivationAreReadyToDraw() const; protected: @@ -185,6 +173,8 @@ class CC_EXPORT PictureLayerImpl float contents_scale, const gfx::Rect& rect, const Region& missing_region) const; + gfx::Rect GetViewportForTilePriorityInContentSpace() const; + PictureLayerImpl* GetRecycledTwinLayer(); void DoPostCommitInitializationIfNeeded() { if (needs_post_commit_initialization_) @@ -209,8 +199,6 @@ class CC_EXPORT PictureLayerImpl scoped_refptr pile_; Region invalidation_; - bool is_mask_; - float ideal_page_scale_; float ideal_device_scale_; float ideal_source_scale_; diff --git a/cc/layers/picture_layer_impl_unittest.cc b/cc/layers/picture_layer_impl_unittest.cc index 5eae6a6c52ae7..38e968efde881 100644 --- a/cc/layers/picture_layer_impl_unittest.cc +++ b/cc/layers/picture_layer_impl_unittest.cc @@ -51,7 +51,10 @@ class PictureLayerImplTest : public testing::Test { host_impl_(ImplSidePaintingSettings(), &proxy_, &shared_bitmap_manager_), - id_(7) {} + id_(7), + pending_layer_(NULL), + old_pending_layer_(NULL), + active_layer_(NULL) {} explicit PictureLayerImplTest(const LayerTreeSettings& settings) : proxy_(base::MessageLoopProxy::current()), @@ -84,6 +87,8 @@ class PictureLayerImplTest : public testing::Test { void ActivateTree() { host_impl_.ActivateSyncTree(); CHECK(!host_impl_.pending_tree()); + CHECK(host_impl_.recycle_tree()); + old_pending_layer_ = pending_layer_; pending_layer_ = NULL; active_layer_ = static_cast( host_impl_.active_tree()->LayerById(id_)); @@ -102,8 +107,6 @@ class PictureLayerImplTest : public testing::Test { SetupPendingTree(active_pile); ActivateTree(); SetupPendingTree(pending_pile); - host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f); - host_impl_.active_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f); } void CreateHighLowResAndSetAllTilesVisible() { @@ -127,6 +130,7 @@ class PictureLayerImplTest : public testing::Test { void SetupPendingTree(scoped_refptr pile) { host_impl_.CreatePendingTree(); + host_impl_.pending_tree()->SetPageScaleFactorAndLimits(1.f, 0.25f, 100.f); LayerTreeImpl* pending_tree = host_impl_.pending_tree(); // Clear recycled tree. pending_tree->DetachLayerTree(); @@ -264,6 +268,7 @@ class PictureLayerImplTest : public testing::Test { FakeLayerTreeHostImpl host_impl_; int id_; FakePictureLayerImpl* pending_layer_; + FakePictureLayerImpl* old_pending_layer_; FakePictureLayerImpl* active_layer_; private: @@ -1079,6 +1084,12 @@ TEST_F(PictureLayerImplTest, CleanUpTilings) { EXPECT_EQ(x, active_layer_->expression); \ } while (false) +#define EXPECT_BOTH_NE(expression, x) \ + do { \ + EXPECT_NE(x, pending_layer_->expression); \ + EXPECT_NE(x, active_layer_->expression); \ + } while (false) + TEST_F(PictureLayerImplTest, DontAddLowResDuringAnimation) { // Make sure this layer covers multiple tiles, since otherwise low // res won't get created because it is too small. @@ -1190,8 +1201,8 @@ TEST_F(PictureLayerImplTest, DontAddLowResForSmallLayers) { ResetTilingsAndRasterScales(); // Mask layers dont create low res since they always fit on one tile. - pending_layer_->SetIsMask(true); - active_layer_->SetIsMask(true); + pending_layer_->pile()->set_is_mask(true); + active_layer_->pile()->set_is_mask(true); SetContentsScaleOnBothLayers(contents_scale, device_scale, page_scale, @@ -1201,6 +1212,53 @@ TEST_F(PictureLayerImplTest, DontAddLowResForSmallLayers) { EXPECT_BOTH_EQ(num_tilings(), 1u); } +TEST_F(PictureLayerImplTest, HugeMasksDontGetTiles) { + gfx::Size tile_size(100, 100); + + scoped_refptr valid_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, gfx::Size(1000, 1000)); + valid_pile->set_is_mask(true); + SetupPendingTree(valid_pile); + + SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false); + EXPECT_EQ(1.f, pending_layer_->HighResTiling()->contents_scale()); + EXPECT_EQ(1u, pending_layer_->num_tilings()); + + pending_layer_->HighResTiling()->CreateAllTilesForTesting(); + host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting( + pending_layer_->HighResTiling()->AllTilesForTesting()); + + ActivateTree(); + + // Mask layers have a tiling with a single tile in it. + EXPECT_EQ(1u, active_layer_->HighResTiling()->AllTilesForTesting().size()); + // The mask resource exists. + EXPECT_NE(0u, active_layer_->ContentsResourceId()); + + // Resize larger than the max texture size. + int max_texture_size = host_impl_.GetRendererCapabilities().max_texture_size; + scoped_refptr huge_pile = + FakePicturePileImpl::CreateFilledPile( + tile_size, gfx::Size(max_texture_size + 1, 10)); + huge_pile->set_is_mask(true); + SetupPendingTree(huge_pile); + + SetupDrawPropertiesAndUpdateTiles(pending_layer_, 1.f, 1.f, 1.f, 1.f, false); + EXPECT_EQ(1.f, pending_layer_->HighResTiling()->contents_scale()); + EXPECT_EQ(1u, pending_layer_->num_tilings()); + + pending_layer_->HighResTiling()->CreateAllTilesForTesting(); + host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting( + pending_layer_->HighResTiling()->AllTilesForTesting()); + + ActivateTree(); + + // Mask layers have a tiling, but there should be no tiles in it. + EXPECT_EQ(0u, active_layer_->HighResTiling()->AllTilesForTesting().size()); + // The mask resource is empty. + EXPECT_EQ(0u, active_layer_->ContentsResourceId()); +} + TEST_F(PictureLayerImplTest, ReleaseResources) { gfx::Size tile_size(400, 400); gfx::Size layer_bounds(1300, 1900); @@ -1450,6 +1508,97 @@ TEST_F(PictureLayerImplTest, MarkRequiredOffscreenTiles) { EXPECT_GT(num_offscreen, 0); } +TEST_F(PictureLayerImplTest, TileOutsideOfViewportForTilePriorityNotRequired) { + base::TimeTicks time_ticks; + time_ticks += base::TimeDelta::FromMilliseconds(1); + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + + gfx::Size tile_size(100, 100); + gfx::Size layer_bounds(400, 400); + gfx::Rect external_viewport_for_tile_priority(400, 200); + gfx::Rect visible_content_rect(200, 400); + + scoped_refptr active_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + scoped_refptr pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + SetupTrees(pending_pile, active_pile); + + active_layer_->set_fixed_tile_size(tile_size); + pending_layer_->set_fixed_tile_size(tile_size); + ASSERT_TRUE(pending_layer_->CanHaveTilings()); + PictureLayerTiling* tiling = pending_layer_->AddTiling(1.f); + + // Set external viewport for tile priority. + gfx::Rect viewport = gfx::Rect(layer_bounds); + gfx::Transform transform; + gfx::Transform transform_for_tile_priority; + bool resourceless_software_draw = false; + host_impl_.SetExternalDrawConstraints(transform, + viewport, + viewport, + external_viewport_for_tile_priority, + transform_for_tile_priority, + resourceless_software_draw); + host_impl_.pending_tree()->UpdateDrawProperties(); + + // Set visible content rect that is different from + // external_viewport_for_tile_priority. + pending_layer_->draw_properties().visible_content_rect = visible_content_rect; + time_ticks += base::TimeDelta::FromMilliseconds(200); + host_impl_.SetCurrentFrameTimeTicks(time_ticks); + pending_layer_->UpdateTiles(NULL); + + pending_layer_->MarkVisibleResourcesAsRequired(); + + // Intersect the two rects. Any tile outside should not be required for + // activation. + gfx::Rect viewport_for_tile_priority = + pending_layer_->GetViewportForTilePriorityInContentSpace(); + viewport_for_tile_priority.Intersect(pending_layer_->visible_content_rect()); + + int num_inside = 0; + int num_outside = 0; + for (PictureLayerTiling::CoverageIterator iter( + tiling, pending_layer_->contents_scale_x(), gfx::Rect(layer_bounds)); + iter; + ++iter) { + if (!*iter) + continue; + Tile* tile = *iter; + if (viewport_for_tile_priority.Intersects(iter.geometry_rect())) { + num_inside++; + // Mark everything in viewport for tile priority as ready to draw. + ManagedTileState::TileVersion& tile_version = + tile->GetTileVersionForTesting( + tile->DetermineRasterModeForTree(PENDING_TREE)); + tile_version.SetSolidColorForTesting(SK_ColorRED); + } else { + num_outside++; + EXPECT_FALSE(tile->required_for_activation()); + } + } + + EXPECT_GT(num_inside, 0); + EXPECT_GT(num_outside, 0); + + // Activate and draw active layer. + host_impl_.ActivateSyncTree(); + host_impl_.active_tree()->UpdateDrawProperties(); + active_layer_->draw_properties().visible_content_rect = visible_content_rect; + + MockOcclusionTracker occlusion_tracker; + scoped_ptr render_pass = RenderPass::Create(); + AppendQuadsData data; + active_layer_->WillDraw(DRAW_MODE_SOFTWARE, NULL); + active_layer_->AppendQuads(render_pass.get(), occlusion_tracker, &data); + active_layer_->DidDraw(NULL); + + // All tiles in activation rect is ready to draw. + EXPECT_EQ(0u, data.num_missing_tiles); + EXPECT_EQ(0u, data.num_incomplete_tiles); +} + TEST_F(PictureLayerImplTest, HighResRequiredWhenUnsharedActiveAllReady) { gfx::Size layer_bounds(400, 400); gfx::Size tile_size(100, 100); @@ -3072,10 +3221,10 @@ TEST_F(PictureLayerImplTest, UpdateTilesForMasksWithNoVisibleContent) { scoped_refptr pending_pile = FakePicturePileImpl::CreateFilledPile(tile_size, bounds); + pending_pile->set_is_mask(true); scoped_ptr mask = FakePictureLayerImpl::CreateWithPile( host_impl_.pending_tree(), 3, pending_pile); - mask->SetIsMask(true); mask->SetBounds(bounds); mask->SetContentBounds(bounds); mask->SetDrawsContent(true); @@ -3101,6 +3250,66 @@ TEST_F(PictureLayerImplTest, UpdateTilesForMasksWithNoVisibleContent) { EXPECT_NE(0u, pending_mask_content->num_tilings()); } +class PictureLayerImplTestWithDelegatingRenderer : public PictureLayerImplTest { + public: + PictureLayerImplTestWithDelegatingRenderer() : PictureLayerImplTest() {} + + virtual void InitializeRenderer() OVERRIDE { + host_impl_.InitializeRenderer( + FakeOutputSurface::CreateDelegating3d().PassAs()); + } +}; + +TEST_F(PictureLayerImplTestWithDelegatingRenderer, + DelegatingRendererWithTileOOM) { + // This test is added for crbug.com/402321, where quad should be produced when + // raster on demand is not allowed and tile is OOM. + gfx::Size tile_size = host_impl_.settings().default_tile_size; + gfx::Size layer_bounds(1000, 1000); + + // Create tiles. + scoped_refptr pending_pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + SetupPendingTree(pending_pile); + pending_layer_->SetBounds(layer_bounds); + host_impl_.SetViewportSize(layer_bounds); + ActivateTree(); + host_impl_.active_tree()->UpdateDrawProperties(); + std::vector tiles = + active_layer_->HighResTiling()->AllTilesForTesting(); + host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles); + + // Force tiles after max_tiles to be OOM. TileManager uses + // GlobalStateThatImpactsTilesPriority from LayerTreeHostImpl, and we cannot + // directly set state to host_impl_, so we set policy that would change the + // state. We also need to update tree priority separately. + GlobalStateThatImpactsTilePriority state; + size_t max_tiles = 1; + size_t memory_limit = max_tiles * 4 * tile_size.width() * tile_size.height(); + size_t resource_limit = max_tiles; + ManagedMemoryPolicy policy(memory_limit, + gpu::MemoryAllocation::CUTOFF_ALLOW_EVERYTHING, + resource_limit); + host_impl_.SetMemoryPolicy(policy); + host_impl_.SetTreePriority(SAME_PRIORITY_FOR_BOTH_TREES); + host_impl_.ManageTiles(); + + MockOcclusionTracker occlusion_tracker; + scoped_ptr render_pass = RenderPass::Create(); + AppendQuadsData data; + active_layer_->WillDraw(DRAW_MODE_HARDWARE, NULL); + active_layer_->AppendQuads(render_pass.get(), occlusion_tracker, &data); + active_layer_->DidDraw(NULL); + + // Even when OOM, quads should be produced, and should be different material + // from quads with resource. + EXPECT_LT(max_tiles, render_pass->quad_list.size()); + EXPECT_EQ(DrawQuad::Material::TILED_CONTENT, + render_pass->quad_list.front()->material); + EXPECT_EQ(DrawQuad::Material::SOLID_COLOR, + render_pass->quad_list.back()->material); +} + class OcclusionTrackingSettings : public ImplSidePaintingSettings { public: OcclusionTrackingSettings() { use_occlusion_for_tile_prioritization = true; } @@ -3709,5 +3918,31 @@ TEST_F(OcclusionTrackingPictureLayerImplTest, VerifyEvictionConsidersOcclusion(active_layer_, total_expected_occluded_tile_count); } + +TEST_F(PictureLayerImplTest, RecycledTwinLayer) { + gfx::Size tile_size(102, 102); + gfx::Size layer_bounds(1000, 1000); + + scoped_refptr pile = + FakePicturePileImpl::CreateFilledPile(tile_size, layer_bounds); + SetupPendingTree(pile); + EXPECT_FALSE(pending_layer_->GetRecycledTwinLayer()); + + ActivateTree(); + EXPECT_TRUE(active_layer_->GetRecycledTwinLayer()); + EXPECT_EQ(old_pending_layer_, active_layer_->GetRecycledTwinLayer()); + + SetupPendingTree(pile); + EXPECT_FALSE(pending_layer_->GetRecycledTwinLayer()); + EXPECT_FALSE(active_layer_->GetRecycledTwinLayer()); + + ActivateTree(); + EXPECT_TRUE(active_layer_->GetRecycledTwinLayer()); + EXPECT_EQ(old_pending_layer_, active_layer_->GetRecycledTwinLayer()); + + host_impl_.ResetRecycleTreeForTesting(); + EXPECT_FALSE(active_layer_->GetRecycledTwinLayer()); +} + } // namespace } // namespace cc diff --git a/cc/layers/render_surface_impl.cc b/cc/layers/render_surface_impl.cc index 04a4da49c13af..2cc67af450c46 100644 --- a/cc/layers/render_surface_impl.cc +++ b/cc/layers/render_surface_impl.cc @@ -66,10 +66,6 @@ void RenderSurfaceImpl::SetClipRect(const gfx::Rect& clip_rect) { clip_rect_ = clip_rect; } -bool RenderSurfaceImpl::ContentsChanged() const { - return !damage_tracker_->current_damage_rect().IsEmpty(); -} - void RenderSurfaceImpl::SetContentRect(const gfx::Rect& content_rect) { if (content_rect_ == content_rect) return; @@ -221,8 +217,12 @@ void RenderSurfaceImpl::AppendQuads( ResourceProvider::ResourceId mask_resource_id = mask_layer ? mask_layer->ContentsResourceId() : 0; - gfx::Rect contents_changed_since_last_frame = - ContentsChanged() ? content_rect_ : gfx::Rect(); + + DCHECK(owning_layer_->draw_properties().target_space_transform.IsScale2d()); + gfx::Vector2dF owning_layer_to_target_scale = + owning_layer_->draw_properties().target_space_transform.Scale2d(); + owning_layer_to_target_scale.Scale(owning_layer_->contents_scale_x(), + owning_layer_->contents_scale_y()); RenderPassDrawQuad* quad = render_pass->CreateAndAppendDrawQuad(); @@ -230,11 +230,10 @@ void RenderSurfaceImpl::AppendQuads( content_rect_, visible_content_rect, render_pass_id, - for_replica, mask_resource_id, - contents_changed_since_last_frame, mask_uv_rect, owning_layer_->filters(), + owning_layer_to_target_scale, owning_layer_->background_filters()); } diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h index 6f7df69f70e3a..27da8405699af 100644 --- a/cc/layers/render_surface_impl.h +++ b/cc/layers/render_surface_impl.h @@ -116,8 +116,6 @@ class CC_EXPORT RenderSurfaceImpl { contributes_to_drawn_surface_ = contributes_to_drawn_surface; } - bool ContentsChanged() const; - void SetContentRect(const gfx::Rect& content_rect); gfx::Rect content_rect() const { return content_rect_; } diff --git a/cc/layers/surface_layer.cc b/cc/layers/surface_layer.cc index f345b7423ddbe..3ca179f2b8a05 100644 --- a/cc/layers/surface_layer.cc +++ b/cc/layers/surface_layer.cc @@ -19,6 +19,7 @@ SurfaceLayer::~SurfaceLayer() {} void SurfaceLayer::SetSurfaceId(SurfaceId surface_id) { surface_id_ = surface_id; + UpdateDrawsContent(HasDrawableContent()); SetNeedsPushProperties(); } @@ -26,8 +27,8 @@ scoped_ptr SurfaceLayer::CreateLayerImpl(LayerTreeImpl* tree_impl) { return SurfaceLayerImpl::Create(tree_impl, id()).PassAs(); } -bool SurfaceLayer::DrawsContent() const { - return !surface_id_.is_null() && Layer::DrawsContent(); +bool SurfaceLayer::HasDrawableContent() const { + return !surface_id_.is_null() && Layer::HasDrawableContent(); } void SurfaceLayer::PushPropertiesTo(LayerImpl* layer) { diff --git a/cc/layers/surface_layer.h b/cc/layers/surface_layer.h index cf150ab480e6b..d58d47d2d70b3 100644 --- a/cc/layers/surface_layer.h +++ b/cc/layers/surface_layer.h @@ -22,11 +22,11 @@ class CC_EXPORT SurfaceLayer : public Layer { // Layer overrides. virtual scoped_ptr CreateLayerImpl(LayerTreeImpl* tree_impl) OVERRIDE; - virtual bool DrawsContent() const OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; protected: SurfaceLayer(); + virtual bool HasDrawableContent() const OVERRIDE; private: virtual ~SurfaceLayer(); diff --git a/cc/layers/texture_layer.cc b/cc/layers/texture_layer.cc index 05391fe5d637b..5d386ca07cf7e 100644 --- a/cc/layers/texture_layer.cc +++ b/cc/layers/texture_layer.cc @@ -45,6 +45,7 @@ void TextureLayer::ClearClient() { layer_tree_host()->StopRateLimiter(); client_ = NULL; ClearTexture(); + UpdateDrawsContent(HasDrawableContent()); } void TextureLayer::ClearTexture() { @@ -136,6 +137,7 @@ void TextureLayer::SetTextureMailboxInternal( else SetNeedsPushProperties(); + UpdateDrawsContent(HasDrawableContent()); // The active frame needs to be replaced and the mailbox returned before the // commit is called complete. SetNextCommitWaitsForActivation(); @@ -198,8 +200,8 @@ void TextureLayer::SetLayerTreeHost(LayerTreeHost* host) { Layer::SetLayerTreeHost(host); } -bool TextureLayer::DrawsContent() const { - return (client_ || holder_ref_) && Layer::DrawsContent(); +bool TextureLayer::HasDrawableContent() const { + return (client_ || holder_ref_) && Layer::HasDrawableContent(); } bool TextureLayer::Update(ResourceUpdateQueue* queue, diff --git a/cc/layers/texture_layer.h b/cc/layers/texture_layer.h index aa2ed561daa50..fb25da629b8e4 100644 --- a/cc/layers/texture_layer.h +++ b/cc/layers/texture_layer.h @@ -134,7 +134,6 @@ class CC_EXPORT TextureLayer : public Layer { virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* layer_tree_host) OVERRIDE; - virtual bool DrawsContent() const OVERRIDE; virtual bool Update(ResourceUpdateQueue* queue, const OcclusionTracker* occlusion) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; @@ -143,6 +142,7 @@ class CC_EXPORT TextureLayer : public Layer { protected: explicit TextureLayer(TextureLayerClient* client); virtual ~TextureLayer(); + virtual bool HasDrawableContent() const OVERRIDE; private: void SetTextureMailboxInternal( diff --git a/cc/layers/texture_layer_unittest.cc b/cc/layers/texture_layer_unittest.cc index 3d4ea23a29f15..08309c7606713 100644 --- a/cc/layers/texture_layer_unittest.cc +++ b/cc/layers/texture_layer_unittest.cc @@ -1152,7 +1152,7 @@ class TextureLayerNoExtraCommitForMailboxTest virtual void DidCommitAndDrawFrame() OVERRIDE { switch (layer_tree_host()->source_frame_number()) { case 1: - EXPECT_FALSE(proxy()->CommitPendingForTesting()); + EXPECT_FALSE(proxy()->MainFrameWillHappenForTesting()); // Invalidate the texture layer to clear the mailbox before // ending the test. texture_layer_->SetNeedsDisplay(); diff --git a/cc/layers/tiled_layer.cc b/cc/layers/tiled_layer.cc index 6656405a72dbd..c1937e49f7e51 100644 --- a/cc/layers/tiled_layer.cc +++ b/cc/layers/tiled_layer.cc @@ -154,27 +154,26 @@ void TiledLayer::UpdateBounds() { for (Region::Iterator new_rects(new_region); new_rects.has_rect(); new_rects.next()) InvalidateContentRect(new_rects.rect()); + UpdateDrawsContent(HasDrawableContent()); } void TiledLayer::SetTileSize(const gfx::Size& size) { tiler_->SetTileSize(size); + UpdateDrawsContent(HasDrawableContent()); } void TiledLayer::SetBorderTexelOption( LayerTilingData::BorderTexelOption border_texel_option) { tiler_->SetBorderTexelOption(border_texel_option); + UpdateDrawsContent(HasDrawableContent()); } -bool TiledLayer::DrawsContent() const { - if (!ContentsScalingLayer::DrawsContent()) - return false; - +bool TiledLayer::HasDrawableContent() const { bool has_more_than_one_tile = - tiler_->num_tiles_x() > 1 || tiler_->num_tiles_y() > 1; - if (tiling_option_ == NEVER_TILE && has_more_than_one_tile) - return false; + (tiler_->num_tiles_x() > 1) || (tiler_->num_tiles_y() > 1); - return true; + return !(tiling_option_ == NEVER_TILE && has_more_than_one_tile) && + ContentsScalingLayer::HasDrawableContent(); } void TiledLayer::ReduceMemoryUsage() { diff --git a/cc/layers/tiled_layer.h b/cc/layers/tiled_layer.h index e272497e7ef46..4a83e457e0f1c 100644 --- a/cc/layers/tiled_layer.h +++ b/cc/layers/tiled_layer.h @@ -27,7 +27,6 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { // Layer implementation. virtual void SetIsMask(bool is_mask) OVERRIDE; virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; - virtual bool DrawsContent() const OVERRIDE; virtual void ReduceMemoryUsage() OVERRIDE; virtual void SetNeedsDisplayRect(const gfx::RectF& dirty_rect) OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* layer_tree_host) OVERRIDE; @@ -68,6 +67,8 @@ class CC_EXPORT TiledLayer : public ContentsScalingLayer { bool SkipsDraw() const { return skips_draw_; } + virtual bool HasDrawableContent() const OVERRIDE; + // Virtual for testing virtual PrioritizedResourceManager* ResourceManager(); const LayerTilingData* TilerForTesting() const { return tiler_.get(); } diff --git a/cc/layers/ui_resource_layer.cc b/cc/layers/ui_resource_layer.cc index b26ebaa6cceb7..59fbe8307b209 100644 --- a/cc/layers/ui_resource_layer.cc +++ b/cc/layers/ui_resource_layer.cc @@ -113,11 +113,11 @@ void UIResourceLayer::SetLayerTreeHost(LayerTreeHost* host) { void UIResourceLayer::RecreateUIResourceHolder() { ui_resource_holder_.reset(); - if (!layer_tree_host() || bitmap_.empty()) - return; - - ui_resource_holder_ = - ScopedUIResourceHolder::Create(layer_tree_host(), bitmap_); + if (layer_tree_host() && !bitmap_.empty()) { + ui_resource_holder_ = + ScopedUIResourceHolder::Create(layer_tree_host(), bitmap_); + } + UpdateDrawsContent(HasDrawableContent()); } void UIResourceLayer::SetBitmap(const SkBitmap& skbitmap) { @@ -137,12 +137,13 @@ void UIResourceLayer::SetUIResourceId(UIResourceId resource_id) { ui_resource_holder_.reset(); } + UpdateDrawsContent(HasDrawableContent()); SetNeedsCommit(); } -bool UIResourceLayer::DrawsContent() const { +bool UIResourceLayer::HasDrawableContent() const { return ui_resource_holder_ && ui_resource_holder_->id() && - Layer::DrawsContent(); + Layer::HasDrawableContent(); } void UIResourceLayer::PushPropertiesTo(LayerImpl* layer) { diff --git a/cc/layers/ui_resource_layer.h b/cc/layers/ui_resource_layer.h index d99c073a6dbd8..8e278a97bc601 100644 --- a/cc/layers/ui_resource_layer.h +++ b/cc/layers/ui_resource_layer.h @@ -20,8 +20,6 @@ class CC_EXPORT UIResourceLayer : public Layer { public: static scoped_refptr Create(); - virtual bool DrawsContent() const OVERRIDE; - virtual void PushPropertiesTo(LayerImpl* layer) OVERRIDE; virtual void SetLayerTreeHost(LayerTreeHost* host) OVERRIDE; @@ -51,6 +49,8 @@ class CC_EXPORT UIResourceLayer : public Layer { UIResourceLayer(); virtual ~UIResourceLayer(); + virtual bool HasDrawableContent() const OVERRIDE; + scoped_ptr ui_resource_holder_; SkBitmap bitmap_; diff --git a/cc/output/delegating_renderer.cc b/cc/output/delegating_renderer.cc index 206c05c5110bc..143596bf5d2d0 100644 --- a/cc/output/delegating_renderer.cc +++ b/cc/output/delegating_renderer.cc @@ -112,13 +112,6 @@ void DelegatingRenderer::ReceiveSwapBuffersAck( resource_provider_->ReceiveReturnsFromParent(ack.resources); } -bool DelegatingRenderer::IsContextLost() { - ContextProvider* context_provider = output_surface_->context_provider(); - if (!context_provider) - return false; - return context_provider->IsContextLost(); -} - void DelegatingRenderer::DidChangeVisibility() { ContextProvider* context_provider = output_surface_->context_provider(); if (!visible()) { diff --git a/cc/output/delegating_renderer.h b/cc/output/delegating_renderer.h index 10d95ce61a817..52dbe019e03d8 100644 --- a/cc/output/delegating_renderer.h +++ b/cc/output/delegating_renderer.h @@ -37,8 +37,6 @@ class CC_EXPORT DelegatingRenderer : public Renderer { virtual void SwapBuffers(const CompositorFrameMetadata& metadata) OVERRIDE; virtual void ReceiveSwapBuffersAck(const CompositorFrameAck&) OVERRIDE; - virtual bool IsContextLost() OVERRIDE; - private: DelegatingRenderer(RendererClient* client, const LayerTreeSettings* settings, diff --git a/cc/output/gl_renderer.cc b/cc/output/gl_renderer.cc index 54f74c45c22f2..30fde1addc049 100644 --- a/cc/output/gl_renderer.cc +++ b/cc/output/gl_renderer.cc @@ -633,6 +633,7 @@ static SkBitmap ApplyImageFilter( scoped_ptr use_gr_context, ResourceProvider* resource_provider, const gfx::Point& origin, + const gfx::Vector2dF& scale, SkImageFilter* filter, ScopedResource* source_texture_resource) { if (!filter) @@ -696,10 +697,8 @@ static SkBitmap ApplyImageFilter( paint.setImageFilter(filter); canvas.clear(SK_ColorTRANSPARENT); - // TODO(senorblanco): in addition to the origin translation here, the canvas - // should also be scaled to accomodate device pixel ratio and pinch zoom. See - // crbug.com/281516 and crbug.com/281518. canvas.translate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y())); + canvas.scale(scale.x(), scale.y()); canvas.drawSprite(source, 0, 0, &paint); // Flush the GrContext to ensure all buffered GL calls are drawn to the @@ -901,6 +900,7 @@ scoped_ptr GLRenderer::GetBackgroundWithFilters( ApplyImageFilter(ScopedUseGrContext::Create(this, frame), resource_provider_, quad->rect.origin(), + quad->filters_scale, filter.get(), device_background_texture.get()); } @@ -1031,6 +1031,7 @@ void GLRenderer::DrawRenderPassQuad(DrawingFrame* frame, ApplyImageFilter(ScopedUseGrContext::Create(this, frame), resource_provider_, quad->rect.origin(), + quad->filters_scale, filter.get(), contents_texture); } diff --git a/cc/output/gl_renderer.h b/cc/output/gl_renderer.h index d5c4f0a5def27..75a5d6e720e76 100644 --- a/cc/output/gl_renderer.h +++ b/cc/output/gl_renderer.h @@ -65,7 +65,7 @@ class CC_EXPORT GLRenderer : public DirectRenderer { virtual void DoNoOp() OVERRIDE; virtual void SwapBuffers(const CompositorFrameMetadata& metadata) OVERRIDE; - virtual bool IsContextLost() OVERRIDE; + virtual bool IsContextLost(); static void DebugGLCall(gpu::gles2::GLES2Interface* gl, const char* command, diff --git a/cc/output/renderer.cc b/cc/output/renderer.cc index dedc204e61082..2de463054b366 100644 --- a/cc/output/renderer.cc +++ b/cc/output/renderer.cc @@ -10,10 +10,6 @@ bool Renderer::HasAllocatedResourcesForTesting(RenderPass::Id id) const { return false; } -bool Renderer::IsContextLost() { - return false; -} - void Renderer::SetVisible(bool visible) { if (visible_ == visible) return; diff --git a/cc/output/renderer.h b/cc/output/renderer.h index b9376c97114ef..b6d0cd43cff7d 100644 --- a/cc/output/renderer.h +++ b/cc/output/renderer.h @@ -73,8 +73,6 @@ class CC_EXPORT Renderer { virtual void SwapBuffers(const CompositorFrameMetadata& metadata) = 0; virtual void ReceiveSwapBuffersAck(const CompositorFrameAck& ack) {} - virtual bool IsContextLost(); - bool visible() const { return visible_; } void SetVisible(bool visible); diff --git a/cc/output/renderer_pixeltest.cc b/cc/output/renderer_pixeltest.cc index bd1a79a65e687..d4885d3d27b9c 100644 --- a/cc/output/renderer_pixeltest.cc +++ b/cc/output/renderer_pixeltest.cc @@ -104,11 +104,10 @@ void CreateTestRenderPassDrawQuad(const SharedQuadState* shared_state, rect, rect, pass_id, - false, // is_replica 0, // mask_resource_id - rect, // contents_changed_since_last_frame gfx::RectF(1.f, 1.f), // mask_uv_rect FilterOperations(), // foreground filters + gfx::Vector2dF(), // filters scale FilterOperations()); // background filters } @@ -806,11 +805,10 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlpha) { pass_rect, pass_rect, child_pass_id, - false, 0, - pass_rect, gfx::RectF(), filters, + gfx::Vector2dF(), FilterOperations()); RenderPassList pass_list; @@ -878,11 +876,10 @@ TYPED_TEST(RendererPixelTest, FastPassSaturateFilter) { pass_rect, pass_rect, child_pass_id, - false, 0, - pass_rect, gfx::RectF(), filters, + gfx::Vector2dF(), FilterOperations()); RenderPassList pass_list; @@ -949,11 +946,10 @@ TYPED_TEST(RendererPixelTest, FastPassFilterChain) { pass_rect, pass_rect, child_pass_id, - false, 0, - pass_rect, gfx::RectF(), filters, + gfx::Vector2dF(), FilterOperations()); RenderPassList pass_list; @@ -1042,11 +1038,10 @@ TYPED_TEST(RendererPixelTest, FastPassColorFilterAlphaTranslation) { pass_rect, pass_rect, child_pass_id, - false, 0, - pass_rect, gfx::RectF(), filters, + gfx::Vector2dF(), FilterOperations()); RenderPassList pass_list; @@ -1250,11 +1245,10 @@ TYPED_TEST(RendererPixelTest, RenderPassAndMaskWithPartialQuad) { sub_rect, sub_rect, child_pass_id, - false, // is_replica mask_resource_id, - sub_rect, // contents_changed_since_last_frame gfx::RectF(1.f, 1.f), // mask_uv_rect FilterOperations(), // foreground filters + gfx::Vector2dF(), // filters scale FilterOperations()); // background filters // White background behind the masked render pass. @@ -1324,11 +1318,10 @@ class RendererPixelTestWithBackgroundFilter filter_pass_content_rect_, filter_pass_content_rect_, filter_pass_id, - false, // is_replica 0, // mask_resource_id - filter_pass_content_rect_, // contents_changed_since_last_frame gfx::RectF(), // mask_uv_rect FilterOperations(), // filters + gfx::Vector2dF(), // filters_scale this->background_filters_); } @@ -2115,6 +2108,48 @@ TYPED_TEST(RendererPixelTest, PictureDrawQuadNonIdentityScale) { ExactPixelComparator(true))); } +TEST_F(GLRendererPixelTest, PictureDrawQuadTexture4444) { + gfx::Size pile_tile_size(1000, 1000); + gfx::Rect viewport(this->device_viewport_size_); + ResourceFormat texture_format = RGBA_4444; + + RenderPass::Id id(1, 1); + gfx::Transform transform_to_root; + scoped_ptr pass = + CreateTestRenderPass(id, viewport, transform_to_root); + + // One viewport-filling blue quad + scoped_refptr blue_pile = + FakePicturePileImpl::CreateFilledPile(pile_tile_size, viewport.size()); + SkPaint blue_paint; + blue_paint.setColor(SK_ColorBLUE); + blue_pile->add_draw_rect_with_paint(viewport, blue_paint); + blue_pile->RerecordPile(); + + gfx::Transform blue_content_to_target_transform; + SharedQuadState* blue_shared_state = CreateTestSharedQuadState( + blue_content_to_target_transform, viewport, pass.get()); + + PictureDrawQuad* blue_quad = pass->CreateAndAppendDrawQuad(); + blue_quad->SetNew(blue_shared_state, + viewport, + gfx::Rect(), + viewport, + gfx::RectF(0.f, 0.f, 1.f, 1.f), + viewport.size(), + texture_format, + viewport, + 1.f, + PicturePileImpl::CreateFromOther(blue_pile)); + + RenderPassList pass_list; + pass_list.push_back(pass.Pass()); + + EXPECT_TRUE(this->RunPixelTest(&pass_list, + base::FilePath(FILE_PATH_LITERAL("blue.png")), + ExactPixelComparator(true))); +} + TYPED_TEST(RendererPixelTest, WrapModeRepeat) { gfx::Rect rect(this->device_viewport_size_); diff --git a/cc/output/software_renderer.cc b/cc/output/software_renderer.cc index 163b48b7750d7..aaa951c2ae35f 100644 --- a/cc/output/software_renderer.cc +++ b/cc/output/software_renderer.cc @@ -526,9 +526,6 @@ void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame, if (!quad->filters.IsEmpty()) { skia::RefPtr filter = RenderSurfaceFilters::BuildImageFilter( quad->filters, content_texture->size()); - // TODO(ajuma): In addition origin translation, the canvas should also be - // scaled to accomodate device pixel ratio and pinch zoom. See - // crbug.com/281516 and crbug.com/281518. // TODO(ajuma): Apply the filter in the same pass as the content where // possible (e.g. when there's no origin offset). See crbug.com/308201. if (filter) { @@ -541,6 +538,7 @@ void SoftwareRenderer::DrawRenderPassQuad(const DrawingFrame* frame, canvas.clear(SK_ColorTRANSPARENT); canvas.translate(SkIntToScalar(-quad->rect.origin().x()), SkIntToScalar(-quad->rect.origin().y())); + canvas.scale(quad->filters_scale.x(), quad->filters_scale.y()); canvas.drawSprite(*content, 0, 0, &paint); } } diff --git a/cc/quads/draw_quad_unittest.cc b/cc/quads/draw_quad_unittest.cc index bf61b4b701a18..40dfe81fbb911 100644 --- a/cc/quads/draw_quad_unittest.cc +++ b/cc/quads/draw_quad_unittest.cc @@ -13,6 +13,7 @@ #include "cc/quads/checkerboard_draw_quad.h" #include "cc/quads/debug_border_draw_quad.h" #include "cc/quads/io_surface_draw_quad.h" +#include "cc/quads/largest_draw_quad.h" #include "cc/quads/picture_draw_quad.h" #include "cc/quads/render_pass.h" #include "cc/quads/render_pass_draw_quad.h" @@ -226,27 +227,6 @@ void CompareDrawQuad(DrawQuad* quad, } \ SETUP_AND_COPY_QUAD_ALL(Type, quad_all); -#define CREATE_QUAD_5_NEW_RP(Type, a, b, c, d, e, copy_a) \ - Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ - { QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e); } \ - SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a); - -#define CREATE_QUAD_5_ALL_RP(Type, a, b, c, d, e, copy_a) \ - Type* quad_all = render_pass->CreateAndAppendDrawQuad(); \ - { \ - QUAD_DATA quad_all->SetAll(shared_state, \ - quad_rect, \ - quad_opaque_rect, \ - quad_visible_rect, \ - needs_blending, \ - a, \ - b, \ - c, \ - d, \ - e); \ - } \ - SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a); - #define CREATE_QUAD_6_NEW(Type, a, b, c, d, e, f) \ Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ { QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, f); } \ @@ -294,31 +274,6 @@ void CompareDrawQuad(DrawQuad* quad, } \ SETUP_AND_COPY_QUAD_ALL(Type, quad_all); -#define CREATE_QUAD_7_NEW_RP(Type, a, b, c, d, e, f, g, copy_a) \ - Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ - { \ - QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, f, g); \ - } \ - SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a); - -#define CREATE_QUAD_7_ALL_RP(Type, a, b, c, d, e, f, g, copy_a) \ - Type* quad_all = render_pass->CreateAndAppendDrawQuad(); \ - { \ - QUAD_DATA quad_all->SetAll(shared_state, \ - quad_rect, \ - quad_opaque_rect, \ - quad_visible_rect, \ - needs_blending, \ - a, \ - b, \ - c, \ - d, \ - e, \ - f, \ - g); \ - } \ - SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a); - #define CREATE_QUAD_8_NEW(Type, a, b, c, d, e, f, g, h) \ Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ { \ @@ -346,33 +301,6 @@ void CompareDrawQuad(DrawQuad* quad, } \ SETUP_AND_COPY_QUAD_ALL(Type, quad_all); -#define CREATE_QUAD_8_NEW_RP(Type, a, b, c, d, e, f, g, h, copy_a) \ - Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ - { \ - QUAD_DATA quad_new->SetNew( \ - shared_state, quad_rect, a, b, c, d, e, f, g, h); \ - } \ - SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a); - -#define CREATE_QUAD_8_ALL_RP(Type, a, b, c, d, e, f, g, h, copy_a) \ - Type* quad_all = render_pass->CreateAndAppendDrawQuad(); \ - { \ - QUAD_DATA quad_all->SetAll(shared_state, \ - quad_rect, \ - quad_opaque_rect, \ - quad_visible_rect, \ - needs_blending, \ - a, \ - b, \ - c, \ - d, \ - e, \ - f, \ - g, \ - h); \ - } \ - SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a); - #define CREATE_QUAD_9_NEW(Type, a, b, c, d, e, f, g, h, i) \ Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ { \ @@ -400,6 +328,30 @@ void CompareDrawQuad(DrawQuad* quad, } \ SETUP_AND_COPY_QUAD_ALL(Type, quad_all); +#define CREATE_QUAD_ALL_RP(Type, a, b, c, d, e, f, copy_a) \ + Type* quad_all = render_pass->CreateAndAppendDrawQuad(); \ + { \ + QUAD_DATA quad_all->SetAll(shared_state, \ + quad_rect, \ + quad_opaque_rect, \ + quad_visible_rect, \ + needs_blending, \ + a, \ + b, \ + c, \ + d, \ + e, \ + f); \ + } \ + SETUP_AND_COPY_QUAD_ALL_RP(Type, quad_all, copy_a); + +#define CREATE_QUAD_NEW_RP(Type, a, b, c, d, e, f, g, copy_a) \ + Type* quad_new = render_pass->CreateAndAppendDrawQuad(); \ + { \ + QUAD_DATA quad_new->SetNew(shared_state, quad_rect, a, b, c, d, e, f, g); \ + } \ + SETUP_AND_COPY_QUAD_NEW_RP(Type, quad_new, copy_a); + TEST(DrawQuadTest, CopyCheckerboardDrawQuad) { gfx::Rect visible_rect(40, 50, 30, 20); SkColor color = 0xfabb0011; @@ -464,12 +416,11 @@ TEST(DrawQuadTest, CopyIOSurfaceDrawQuad) { TEST(DrawQuadTest, CopyRenderPassDrawQuad) { gfx::Rect visible_rect(40, 50, 30, 20); RenderPass::Id render_pass_id(22, 64); - bool is_replica = true; ResourceProvider::ResourceId mask_resource_id = 78; - gfx::Rect contents_changed_since_last_frame(42, 11, 74, 24); gfx::RectF mask_u_v_rect(-45.f, -21.f, 33.f, 19.f); FilterOperations filters; filters.Append(FilterOperation::CreateBlurFilter(1.f)); + gfx::Vector2dF filters_scale; FilterOperations background_filters; background_filters.Append( FilterOperation::CreateGrayscaleFilter(1.f)); @@ -477,44 +428,38 @@ TEST(DrawQuadTest, CopyRenderPassDrawQuad) { RenderPass::Id copied_render_pass_id(235, 11); CREATE_SHARED_STATE(); - CREATE_QUAD_8_NEW_RP(RenderPassDrawQuad, - visible_rect, - render_pass_id, - is_replica, - mask_resource_id, - contents_changed_since_last_frame, - mask_u_v_rect, - filters, - background_filters, - copied_render_pass_id); + CREATE_QUAD_NEW_RP(RenderPassDrawQuad, + visible_rect, + render_pass_id, + mask_resource_id, + mask_u_v_rect, + filters, + filters_scale, + background_filters, + copied_render_pass_id); EXPECT_EQ(DrawQuad::RENDER_PASS, copy_quad->material); EXPECT_RECT_EQ(visible_rect, copy_quad->visible_rect); EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id); - EXPECT_EQ(is_replica, copy_quad->is_replica); EXPECT_EQ(mask_resource_id, copy_quad->mask_resource_id); - EXPECT_RECT_EQ(contents_changed_since_last_frame, - copy_quad->contents_changed_since_last_frame); EXPECT_EQ(mask_u_v_rect.ToString(), copy_quad->mask_uv_rect.ToString()); EXPECT_EQ(filters, copy_quad->filters); + EXPECT_EQ(filters_scale, copy_quad->filters_scale); EXPECT_EQ(background_filters, copy_quad->background_filters); - CREATE_QUAD_7_ALL_RP(RenderPassDrawQuad, - render_pass_id, - is_replica, - mask_resource_id, - contents_changed_since_last_frame, - mask_u_v_rect, - filters, - background_filters, - copied_render_pass_id); + CREATE_QUAD_ALL_RP(RenderPassDrawQuad, + render_pass_id, + mask_resource_id, + mask_u_v_rect, + filters, + filters_scale, + background_filters, + copied_render_pass_id); EXPECT_EQ(DrawQuad::RENDER_PASS, copy_quad->material); EXPECT_EQ(copied_render_pass_id, copy_quad->render_pass_id); - EXPECT_EQ(is_replica, copy_quad->is_replica); EXPECT_EQ(mask_resource_id, copy_quad->mask_resource_id); - EXPECT_RECT_EQ(contents_changed_since_last_frame, - copy_quad->contents_changed_since_last_frame); EXPECT_EQ(mask_u_v_rect.ToString(), copy_quad->mask_uv_rect.ToString()); EXPECT_EQ(filters, copy_quad->filters); + EXPECT_EQ(filters_scale, copy_quad->filters_scale); EXPECT_EQ(background_filters, copy_quad->background_filters); } @@ -810,12 +755,11 @@ TEST_F(DrawQuadIteratorTest, IOSurfaceDrawQuad) { TEST_F(DrawQuadIteratorTest, RenderPassDrawQuad) { gfx::Rect visible_rect(40, 50, 30, 20); RenderPass::Id render_pass_id(22, 64); - bool is_replica = true; ResourceProvider::ResourceId mask_resource_id = 78; - gfx::Rect contents_changed_since_last_frame(42, 11, 74, 24); gfx::RectF mask_u_v_rect(-45.f, -21.f, 33.f, 19.f); FilterOperations filters; filters.Append(FilterOperation::CreateBlurFilter(1.f)); + gfx::Vector2dF filters_scale(2.f, 3.f); FilterOperations background_filters; background_filters.Append( FilterOperation::CreateGrayscaleFilter(1.f)); @@ -823,16 +767,15 @@ TEST_F(DrawQuadIteratorTest, RenderPassDrawQuad) { RenderPass::Id copied_render_pass_id(235, 11); CREATE_SHARED_STATE(); - CREATE_QUAD_8_NEW_RP(RenderPassDrawQuad, - visible_rect, - render_pass_id, - is_replica, - mask_resource_id, - contents_changed_since_last_frame, - mask_u_v_rect, - filters, - background_filters, - copied_render_pass_id); + CREATE_QUAD_NEW_RP(RenderPassDrawQuad, + visible_rect, + render_pass_id, + mask_resource_id, + mask_u_v_rect, + filters, + filters_scale, + background_filters, + copied_render_pass_id); EXPECT_EQ(mask_resource_id, quad_new->mask_resource_id); EXPECT_EQ(1, IterateAndCount(quad_new)); EXPECT_EQ(mask_resource_id + 1, quad_new->mask_resource_id); @@ -979,5 +922,96 @@ TEST_F(DrawQuadIteratorTest, DISABLED_PictureDrawQuad) { EXPECT_EQ(0, IterateAndCount(quad_new)); } +TEST(DrawQuadTest, LargestQuadType) { + size_t largest = 0; + + for (int i = 0; i <= DrawQuad::MATERIAL_LAST; ++i) { + switch (static_cast(i)) { + case DrawQuad::CHECKERBOARD: + largest = std::max(largest, sizeof(CheckerboardDrawQuad)); + break; + case DrawQuad::DEBUG_BORDER: + largest = std::max(largest, sizeof(DebugBorderDrawQuad)); + break; + case DrawQuad::IO_SURFACE_CONTENT: + largest = std::max(largest, sizeof(IOSurfaceDrawQuad)); + break; + case DrawQuad::PICTURE_CONTENT: + largest = std::max(largest, sizeof(PictureDrawQuad)); + break; + case DrawQuad::TEXTURE_CONTENT: + largest = std::max(largest, sizeof(TextureDrawQuad)); + break; + case DrawQuad::RENDER_PASS: + largest = std::max(largest, sizeof(RenderPassDrawQuad)); + break; + case DrawQuad::SOLID_COLOR: + largest = std::max(largest, sizeof(SolidColorDrawQuad)); + break; + case DrawQuad::SURFACE_CONTENT: + largest = std::max(largest, sizeof(SurfaceDrawQuad)); + break; + case DrawQuad::TILED_CONTENT: + largest = std::max(largest, sizeof(TileDrawQuad)); + break; + case DrawQuad::STREAM_VIDEO_CONTENT: + largest = std::max(largest, sizeof(StreamVideoDrawQuad)); + break; + case DrawQuad::YUV_VIDEO_CONTENT: + largest = std::max(largest, sizeof(YUVVideoDrawQuad)); + break; + case DrawQuad::INVALID: + break; + } + } + EXPECT_EQ(sizeof(kLargestDrawQuad), largest); + + if (!HasFailure()) + return; + + // On failure, output the size of all quads for debugging. + LOG(ERROR) << "largest " << largest; + LOG(ERROR) << "kLargestDrawQuad " << sizeof(kLargestDrawQuad); + for (int i = 0; i <= DrawQuad::MATERIAL_LAST; ++i) { + switch (static_cast(i)) { + case DrawQuad::CHECKERBOARD: + LOG(ERROR) << "CheckerboardDrawQuad " << sizeof(CheckerboardDrawQuad); + break; + case DrawQuad::DEBUG_BORDER: + LOG(ERROR) << "DebugBorderDrawQuad " << sizeof(DebugBorderDrawQuad); + break; + case DrawQuad::IO_SURFACE_CONTENT: + LOG(ERROR) << "IOSurfaceDrawQuad " << sizeof(IOSurfaceDrawQuad); + break; + case DrawQuad::PICTURE_CONTENT: + LOG(ERROR) << "PictureDrawQuad " << sizeof(PictureDrawQuad); + break; + case DrawQuad::TEXTURE_CONTENT: + LOG(ERROR) << "TextureDrawQuad " << sizeof(TextureDrawQuad); + break; + case DrawQuad::RENDER_PASS: + LOG(ERROR) << "RenderPassDrawQuad " << sizeof(RenderPassDrawQuad); + break; + case DrawQuad::SOLID_COLOR: + LOG(ERROR) << "SolidColorDrawQuad " << sizeof(SolidColorDrawQuad); + break; + case DrawQuad::SURFACE_CONTENT: + LOG(ERROR) << "SurfaceDrawQuad " << sizeof(SurfaceDrawQuad); + break; + case DrawQuad::TILED_CONTENT: + LOG(ERROR) << "TileDrawQuad " << sizeof(TileDrawQuad); + break; + case DrawQuad::STREAM_VIDEO_CONTENT: + LOG(ERROR) << "StreamVideoDrawQuad " << sizeof(StreamVideoDrawQuad); + break; + case DrawQuad::YUV_VIDEO_CONTENT: + LOG(ERROR) << "YUVVideoDrawQuad " << sizeof(YUVVideoDrawQuad); + break; + case DrawQuad::INVALID: + break; + } + } +} + } // namespace } // namespace cc diff --git a/cc/quads/largest_draw_quad.h b/cc/quads/largest_draw_quad.h new file mode 100644 index 0000000000000..d14906afad809 --- /dev/null +++ b/cc/quads/largest_draw_quad.h @@ -0,0 +1,20 @@ +// 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 CC_QUADS_LARGEST_DRAW_QUAD_H_ +#define CC_QUADS_LARGEST_DRAW_QUAD_H_ + +namespace cc { +class StreamVideoDrawQuad; +class RenderPassDrawQuad; + +#if defined(ARCH_CPU_64_BITS) +typedef RenderPassDrawQuad kLargestDrawQuad; +#else +typedef StreamVideoDrawQuad kLargestDrawQuad; +#endif + +} // namespace cc + +#endif // CC_QUADS_LARGEST_DRAW_QUAD_H_ diff --git a/cc/quads/render_pass_draw_quad.cc b/cc/quads/render_pass_draw_quad.cc index 380f2f6619337..aad279e64ce40 100644 --- a/cc/quads/render_pass_draw_quad.cc +++ b/cc/quads/render_pass_draw_quad.cc @@ -14,7 +14,6 @@ namespace cc { RenderPassDrawQuad::RenderPassDrawQuad() : render_pass_id(RenderPass::Id(-1, -1)), - is_replica(false), mask_resource_id(static_cast(-1)) { } @@ -26,20 +25,26 @@ void RenderPassDrawQuad::SetNew( const gfx::Rect& rect, const gfx::Rect& visible_rect, RenderPass::Id render_pass_id, - bool is_replica, ResourceProvider::ResourceId mask_resource_id, - const gfx::Rect& contents_changed_since_last_frame, const gfx::RectF& mask_uv_rect, const FilterOperations& filters, + const gfx::Vector2dF& filters_scale, const FilterOperations& background_filters) { DCHECK_GT(render_pass_id.layer_id, 0); DCHECK_GE(render_pass_id.index, 0); gfx::Rect opaque_rect; bool needs_blending = false; - SetAll(shared_quad_state, rect, opaque_rect, visible_rect, needs_blending, - render_pass_id, is_replica, mask_resource_id, - contents_changed_since_last_frame, mask_uv_rect, filters, + SetAll(shared_quad_state, + rect, + opaque_rect, + visible_rect, + needs_blending, + render_pass_id, + mask_resource_id, + mask_uv_rect, + filters, + filters_scale, background_filters); } @@ -50,11 +55,10 @@ void RenderPassDrawQuad::SetAll( const gfx::Rect& visible_rect, bool needs_blending, RenderPass::Id render_pass_id, - bool is_replica, ResourceProvider::ResourceId mask_resource_id, - const gfx::Rect& contents_changed_since_last_frame, const gfx::RectF& mask_uv_rect, const FilterOperations& filters, + const gfx::Vector2dF& filters_scale, const FilterOperations& background_filters) { DCHECK_GT(render_pass_id.layer_id, 0); DCHECK_GE(render_pass_id.index, 0); @@ -62,11 +66,10 @@ void RenderPassDrawQuad::SetAll( DrawQuad::SetAll(shared_quad_state, DrawQuad::RENDER_PASS, rect, opaque_rect, visible_rect, needs_blending); this->render_pass_id = render_pass_id; - this->is_replica = is_replica; this->mask_resource_id = mask_resource_id; - this->contents_changed_since_last_frame = contents_changed_since_last_frame; this->mask_uv_rect = mask_uv_rect; this->filters = filters; + this->filters_scale = filters_scale; this->background_filters = background_filters; } @@ -84,19 +87,20 @@ const RenderPassDrawQuad* RenderPassDrawQuad::MaterialCast( void RenderPassDrawQuad::ExtendValue(base::debug::TracedValue* value) const { TracedValue::SetIDRef(render_pass_id.AsTracingId(), value, "render_pass_id"); - value->SetBoolean("is_replica", is_replica); value->SetInteger("mask_resource_id", mask_resource_id); - value->BeginArray("contents_changed_since_last_frame"); - MathUtil::AddToTracedValue(contents_changed_since_last_frame, value); - value->EndArray(); value->BeginArray("mask_uv_rect"); MathUtil::AddToTracedValue(mask_uv_rect, value); value->EndArray(); + value->BeginDictionary("filters"); filters.AsValueInto(value); value->EndDictionary(); + value->BeginArray("filters_scale"); + MathUtil::AddToTracedValue(filters_scale, value); + value->EndArray(); + value->BeginDictionary("background_filters"); background_filters.AsValueInto(value); value->EndDictionary(); diff --git a/cc/quads/render_pass_draw_quad.h b/cc/quads/render_pass_draw_quad.h index 7237a8548d549..9cc4ba3d16ba6 100644 --- a/cc/quads/render_pass_draw_quad.h +++ b/cc/quads/render_pass_draw_quad.h @@ -24,11 +24,10 @@ class CC_EXPORT RenderPassDrawQuad : public DrawQuad { const gfx::Rect& rect, const gfx::Rect& visible_rect, RenderPass::Id render_pass_id, - bool is_replica, ResourceProvider::ResourceId mask_resource_id, - const gfx::Rect& contents_changed_since_last_frame, const gfx::RectF& mask_uv_rect, const FilterOperations& filters, + const gfx::Vector2dF& filters_scale, const FilterOperations& background_filters); void SetAll(const SharedQuadState* shared_quad_state, @@ -37,22 +36,25 @@ class CC_EXPORT RenderPassDrawQuad : public DrawQuad { const gfx::Rect& visible_rect, bool needs_blending, RenderPass::Id render_pass_id, - bool is_replica, ResourceProvider::ResourceId mask_resource_id, - const gfx::Rect& contents_changed_since_last_frame, const gfx::RectF& mask_uv_rect, const FilterOperations& filters, + const gfx::Vector2dF& filters_scale, const FilterOperations& background_filters); RenderPass::Id render_pass_id; - bool is_replica; ResourceProvider::ResourceId mask_resource_id; - gfx::Rect contents_changed_since_last_frame; gfx::RectF mask_uv_rect; // Post-processing filters, applied to the pixels in the render pass' texture. FilterOperations filters; + // The scale from layer space of the root layer of the render pass to + // the render pass physical pixels. This scale is applied to the filter + // parameters for pixel-moving filters. This scale should include + // content-to-target-space scale, and device pixel ratio. + gfx::Vector2dF filters_scale; + // Post-processing filters, applied to the pixels showing through the // background of the render pass, from behind it. FilterOperations background_filters; diff --git a/cc/quads/render_pass_unittest.cc b/cc/quads/render_pass_unittest.cc index 0d096940816f2..4e555b8f7ea97 100644 --- a/cc/quads/render_pass_unittest.cc +++ b/cc/quads/render_pass_unittest.cc @@ -218,11 +218,10 @@ TEST(RenderPassTest, CopyAllShouldBeIdentical) { contrib_output_rect, contrib_output_rect, contrib_id, - false, // is_replica 0, // mask_resource_id - contrib_damage_rect, gfx::RectF(), // mask_uv_rect FilterOperations(), + gfx::Vector2dF(), // filters_scale FilterOperations()); pass_list.push_back(pass.PassAs()); diff --git a/cc/resources/eviction_tile_priority_queue.cc b/cc/resources/eviction_tile_priority_queue.cc index 06e62e64e3009..9f05e81676461 100644 --- a/cc/resources/eviction_tile_priority_queue.cc +++ b/cc/resources/eviction_tile_priority_queue.cc @@ -16,11 +16,11 @@ class EvictionOrderComparator { bool operator()( const EvictionTilePriorityQueue::PairedPictureLayerQueue* a, const EvictionTilePriorityQueue::PairedPictureLayerQueue* b) const { - if (a->IsEmpty()) - return true; - - if (b->IsEmpty()) - return false; + // Note that in this function, we have to return true if and only if + // b is strictly lower priority than a. Note that for the sake of + // completeness, empty queue is considered to have lowest priority. + if (a->IsEmpty() || b->IsEmpty()) + return b->IsEmpty() < a->IsEmpty(); WhichTree a_tree = a->NextTileIteratorTree(tree_priority_); const PictureLayerImpl::LayerEvictionTileIterator* a_iterator = @@ -39,8 +39,6 @@ class EvictionOrderComparator { b_tile->priority_for_tree_priority(tree_priority_); bool prioritize_low_res = tree_priority_ == SMOOTHNESS_TAKES_PRIORITY; - // Now we have to return true iff b is lower priority than a. - // If the priority bin differs, b is lower priority if it has the higher // priority bin. if (a_priority.priority_bin != b_priority.priority_bin) @@ -60,7 +58,6 @@ class EvictionOrderComparator { if (prioritize_low_res) return a_priority.resolution == LOW_RESOLUTION; - return a_priority.resolution == HIGH_RESOLUTION; } diff --git a/cc/resources/picture_layer_tiling.cc b/cc/resources/picture_layer_tiling.cc index d2810540849ce..5c75f78e68f0c 100644 --- a/cc/resources/picture_layer_tiling.cc +++ b/cc/resources/picture_layer_tiling.cc @@ -10,6 +10,7 @@ #include "base/debug/trace_event.h" #include "base/debug/trace_event_argument.h" +#include "base/logging.h" #include "cc/base/math_util.h" #include "cc/resources/tile.h" #include "cc/resources/tile_priority.h" @@ -36,14 +37,8 @@ class TileEvictionOrder { const TilePriority& b_priority = b->priority_for_tree_priority(tree_priority_); - // Evict a before b if their priority bins differ and a has the higher - // priority bin. - if (a_priority.priority_bin != b_priority.priority_bin) - return a_priority.priority_bin > b_priority.priority_bin; - - // Or if a is not required and b is required. - if (a->required_for_activation() != b->required_for_activation()) - return b->required_for_activation(); + DCHECK(a_priority.priority_bin == b_priority.priority_bin); + DCHECK(a->required_for_activation() == b->required_for_activation()); // Or if a is occluded and b is unoccluded. bool a_is_occluded = a->is_occluded_for_tree_priority(tree_priority_); @@ -94,6 +89,10 @@ PictureLayerTiling::PictureLayerTiling(float contents_scale, gfx::Size content_bounds = gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds, contents_scale)); gfx::Size tile_size = client_->CalculateTileSize(content_bounds); + if (tile_size.IsEmpty()) { + layer_bounds_ = gfx::Size(); + content_bounds = gfx::Size(); + } DCHECK(!gfx::ToFlooredSize( gfx::ScaleSize(layer_bounds, contents_scale)).IsEmpty()) << @@ -147,7 +146,7 @@ Tile* PictureLayerTiling::CreateTile(int i, void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() { const PictureLayerTiling* twin_tiling = client_->GetTwinTiling(this); - bool include_borders = true; + bool include_borders = false; for (TilingData::Iterator iter( &tiling_data_, live_tiles_rect_, include_borders); iter; @@ -158,6 +157,8 @@ void PictureLayerTiling::CreateMissingTilesInLiveTilesRect() { continue; CreateTile(key.first, key.second, twin_tiling); } + + VerifyLiveTilesRect(); } void PictureLayerTiling::UpdateTilesToCurrentPile( @@ -165,19 +166,78 @@ void PictureLayerTiling::UpdateTilesToCurrentPile( const gfx::Size& new_layer_bounds) { DCHECK(!new_layer_bounds.IsEmpty()); - gfx::Size old_layer_bounds = layer_bounds_; - layer_bounds_ = new_layer_bounds; - - gfx::Size content_bounds = - gfx::ToCeiledSize(gfx::ScaleSize(layer_bounds_, contents_scale_)); gfx::Size tile_size = tiling_data_.max_texture_size(); - if (layer_bounds_ != old_layer_bounds) { - // Drop tiles outside the new layer bounds if the layer shrank. - SetLiveTilesRect( - gfx::IntersectRects(live_tiles_rect_, gfx::Rect(content_bounds))); - tiling_data_.SetTilingSize(content_bounds); + if (new_layer_bounds != layer_bounds_) { + gfx::Size content_bounds = + gfx::ToCeiledSize(gfx::ScaleSize(new_layer_bounds, contents_scale_)); + tile_size = client_->CalculateTileSize(content_bounds); + if (tile_size.IsEmpty()) { + layer_bounds_ = gfx::Size(); + content_bounds = gfx::Size(); + } else { + layer_bounds_ = new_layer_bounds; + } + + // The SetLiveTilesRect() method would drop tiles outside the new bounds, + // but may do so incorrectly if resizing the tiling causes the number of + // tiles in the tiling_data_ to change. + gfx::Rect content_rect(content_bounds); + int before_left = tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.x()); + int before_top = tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.y()); + int before_right = + tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1); + int before_bottom = + tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1); + + // The live_tiles_rect_ is clamped to stay within the tiling size as we + // change it. + live_tiles_rect_.Intersect(content_rect); + tiling_data_.SetTilingSize(content_bounds); + + int after_right = -1; + int after_bottom = -1; + if (!live_tiles_rect_.IsEmpty()) { + after_right = + tiling_data_.TileXIndexFromSrcCoord(live_tiles_rect_.right() - 1); + after_bottom = + tiling_data_.TileYIndexFromSrcCoord(live_tiles_rect_.bottom() - 1); + } + + // Drop tiles outside the new layer bounds if the layer shrank. + for (int i = after_right + 1; i <= before_right; ++i) { + for (int j = before_top; j <= before_bottom; ++j) { + TileMap::iterator found = tiles_.find(TileMapKey(i, j)); + if (found == tiles_.end()) + continue; + ReleaseTile(found->second.get(), client_->GetTree()); + tiles_.erase(found); + } + } + for (int i = before_left; i <= after_right; ++i) { + for (int j = after_bottom + 1; j <= before_bottom; ++j) { + TileMap::iterator found = tiles_.find(TileMapKey(i, j)); + if (found == tiles_.end()) + continue; + ReleaseTile(found->second.get(), client_->GetTree()); + tiles_.erase(found); + } + } + + // If the layer grew, the live_tiles_rect_ is not changed, but a new row + // and/or column of tiles may now exist inside the same live_tiles_rect_. + const PictureLayerTiling* twin_tiling = client_->GetTwinTiling(this); + if (after_right > before_right) { + DCHECK_EQ(after_right, before_right + 1); + for (int j = before_top; j <= after_bottom; ++j) + CreateTile(after_right, j, twin_tiling); + } + if (after_bottom > before_bottom) { + DCHECK_EQ(after_bottom, before_bottom + 1); + for (int i = before_left; i <= before_right; ++i) + CreateTile(i, after_bottom, twin_tiling); + } } if (tile_size != tiling_data_.max_texture_size()) { @@ -192,6 +252,7 @@ void PictureLayerTiling::UpdateTilesToCurrentPile( PicturePileImpl* pile = client_->GetPile(); for (TileMap::const_iterator it = tiles_.begin(); it != tiles_.end(); ++it) it->second->set_picture_pile(pile); + VerifyLiveTilesRect(); } void PictureLayerTiling::RemoveTilesInRegion(const Region& layer_region) { @@ -208,18 +269,24 @@ void PictureLayerTiling::DoInvalidate(const Region& layer_region, bool recreate_invalidated_tiles) { std::vector new_tile_keys; gfx::Rect expanded_live_tiles_rect = - tiling_data_.ExpandRectIgnoringBordersToTileBoundsWithBorders( - live_tiles_rect_); + tiling_data_.ExpandRectIgnoringBordersToTileBounds(live_tiles_rect_); for (Region::Iterator iter(layer_region); iter.has_rect(); iter.next()) { gfx::Rect layer_rect = iter.rect(); gfx::Rect content_rect = gfx::ScaleToEnclosingRect(layer_rect, contents_scale_); + // Consider tiles inside the live tiles rect even if only their border + // pixels intersect the invalidation. But don't consider tiles outside + // the live tiles rect with the same conditions, as they won't exist. + int border_pixels = tiling_data_.border_texels(); + content_rect.Inset(-border_pixels, -border_pixels); // Avoid needless work by not bothering to invalidate where there aren't // tiles. content_rect.Intersect(expanded_live_tiles_rect); if (content_rect.IsEmpty()) continue; - bool include_borders = true; + // Since the content_rect includes border pixels already, don't include + // borders when iterating to avoid double counting them. + bool include_borders = false; for (TilingData::Iterator iter( &tiling_data_, content_rect, include_borders); iter; @@ -503,11 +570,10 @@ void PictureLayerTiling::UpdateTilePriorities( eviction_tiles_cache_valid_ = false; TilePriority now_priority(resolution_, TilePriority::NOW, 0); - float content_to_screen_scale = - 1.0f / (contents_scale_ * ideal_contents_scale); + float content_to_screen_scale = ideal_contents_scale / contents_scale_; // Assign now priority to all visible tiles. - bool include_borders = true; + bool include_borders = false; has_visible_rect_tiles_ = false; for (TilingData::Iterator iter( &tiling_data_, visible_rect_in_content_space, include_borders); @@ -609,6 +675,15 @@ void PictureLayerTiling::UpdateTilePriorities( current_eventually_rect_ = eventually_rect; } +void PictureLayerTiling::RemoveTileAt(int i, int j) { + TileMapKey key(i, j); + TileMap::iterator found = tiles_.find(key); + if (found == tiles_.end()) + return; + ReleaseTile(found->second.get(), client_->GetTree()); + tiles_.erase(found); +} + void PictureLayerTiling::SetLiveTilesRect( const gfx::Rect& new_live_tiles_rect) { DCHECK(new_live_tiles_rect.IsEmpty() || @@ -619,6 +694,7 @@ void PictureLayerTiling::SetLiveTilesRect( return; // Iterate to delete all tiles outside of our new live_tiles rect. + PictureLayerTiling* recycled_twin = client_->GetRecycledTwinTiling(this); for (TilingData::DifferenceIterator iter(&tiling_data_, live_tiles_rect_, new_live_tiles_rect); @@ -631,6 +707,8 @@ void PictureLayerTiling::SetLiveTilesRect( if (found != tiles_.end()) { ReleaseTile(found->second.get(), client_->GetTree()); tiles_.erase(found); + if (recycled_twin) + recycled_twin->RemoveTileAt(iter.index_x(), iter.index_y()); } } @@ -647,6 +725,30 @@ void PictureLayerTiling::SetLiveTilesRect( } live_tiles_rect_ = new_live_tiles_rect; + VerifyLiveTilesRect(); +} + +void PictureLayerTiling::VerifyLiveTilesRect() { +#if DCHECK_IS_ON + for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) { + if (!it->second.get()) + continue; + DCHECK(it->first.first < tiling_data_.num_tiles_x()) + << this << " " << it->first.first << "," << it->first.second + << " num_tiles_x " << tiling_data_.num_tiles_x() << " live_tiles_rect " + << live_tiles_rect_.ToString(); + DCHECK(it->first.second < tiling_data_.num_tiles_y()) + << this << " " << it->first.first << "," << it->first.second + << " num_tiles_y " << tiling_data_.num_tiles_y() << " live_tiles_rect " + << live_tiles_rect_.ToString(); + DCHECK(tiling_data_.TileBounds(it->first.first, it->first.second) + .Intersects(live_tiles_rect_)) + << this << " " << it->first.first << "," << it->first.second + << " tile bounds " + << tiling_data_.TileBounds(it->first.first, it->first.second).ToString() + << " live_tiles_rect " << live_tiles_rect_.ToString(); + } +#endif } void PictureLayerTiling::DidBecomeRecycled() { @@ -847,10 +949,12 @@ void PictureLayerTiling::UpdateEvictionCacheIfNeeded( eviction_cache_tree_priority_ == tree_priority) return; - eventually_eviction_tiles_.clear(); - soon_eviction_tiles_.clear(); - now_required_for_activation_eviction_tiles_.clear(); - now_not_required_for_activation_eviction_tiles_.clear(); + eviction_tiles_now_.clear(); + eviction_tiles_now_and_required_for_activation_.clear(); + eviction_tiles_soon_.clear(); + eviction_tiles_soon_and_required_for_activation_.clear(); + eviction_tiles_eventually_.clear(); + eviction_tiles_eventually_and_required_for_activation_.clear(); for (TileMap::iterator it = tiles_.begin(); it != tiles_.end(); ++it) { // TODO(vmpstr): This should update the priority if UpdateTilePriorities @@ -860,16 +964,23 @@ void PictureLayerTiling::UpdateEvictionCacheIfNeeded( tile->priority_for_tree_priority(tree_priority); switch (priority.priority_bin) { case TilePriority::EVENTUALLY: - eventually_eviction_tiles_.push_back(tile); + if (tile->required_for_activation()) + eviction_tiles_eventually_and_required_for_activation_.push_back( + tile); + else + eviction_tiles_eventually_.push_back(tile); break; case TilePriority::SOON: - soon_eviction_tiles_.push_back(tile); + if (tile->required_for_activation()) + eviction_tiles_soon_and_required_for_activation_.push_back(tile); + else + eviction_tiles_soon_.push_back(tile); break; case TilePriority::NOW: if (tile->required_for_activation()) - now_required_for_activation_eviction_tiles_.push_back(tile); + eviction_tiles_now_and_required_for_activation_.push_back(tile); else - now_not_required_for_activation_eviction_tiles_.push_back(tile); + eviction_tiles_now_.push_back(tile); break; } } @@ -877,22 +988,48 @@ void PictureLayerTiling::UpdateEvictionCacheIfNeeded( // TODO(vmpstr): Do this lazily. One option is to have a "sorted" flag that // can be updated for each of the queues. TileEvictionOrder sort_order(tree_priority); - std::sort(eventually_eviction_tiles_.begin(), - eventually_eviction_tiles_.end(), + std::sort(eviction_tiles_now_.begin(), eviction_tiles_now_.end(), sort_order); + std::sort(eviction_tiles_now_and_required_for_activation_.begin(), + eviction_tiles_now_and_required_for_activation_.end(), sort_order); std::sort( - soon_eviction_tiles_.begin(), soon_eviction_tiles_.end(), sort_order); - std::sort(now_required_for_activation_eviction_tiles_.begin(), - now_required_for_activation_eviction_tiles_.end(), + eviction_tiles_soon_.begin(), eviction_tiles_soon_.end(), sort_order); + std::sort(eviction_tiles_soon_and_required_for_activation_.begin(), + eviction_tiles_soon_and_required_for_activation_.end(), + sort_order); + std::sort(eviction_tiles_eventually_.begin(), + eviction_tiles_eventually_.end(), sort_order); - std::sort(now_not_required_for_activation_eviction_tiles_.begin(), - now_not_required_for_activation_eviction_tiles_.end(), + std::sort(eviction_tiles_eventually_and_required_for_activation_.begin(), + eviction_tiles_eventually_and_required_for_activation_.end(), sort_order); eviction_tiles_cache_valid_ = true; eviction_cache_tree_priority_ = tree_priority; } +const std::vector* PictureLayerTiling::GetEvictionTiles( + TreePriority tree_priority, + EvictionCategory category) { + UpdateEvictionCacheIfNeeded(tree_priority); + switch (category) { + case EVENTUALLY: + return &eviction_tiles_eventually_; + case EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION: + return &eviction_tiles_eventually_and_required_for_activation_; + case SOON: + return &eviction_tiles_soon_; + case SOON_AND_REQUIRED_FOR_ACTIVATION: + return &eviction_tiles_soon_and_required_for_activation_; + case NOW: + return &eviction_tiles_now_; + case NOW_AND_REQUIRED_FOR_ACTIVATION: + return &eviction_tiles_now_and_required_for_activation_; + } + NOTREACHED(); + return &eviction_tiles_eventually_; +} + PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator() : tiling_(NULL), current_tile_(NULL) {} @@ -907,7 +1044,7 @@ PictureLayerTiling::TilingRasterTileIterator::TilingRasterTileIterator( visible_iterator_ = TilingData::Iterator(&tiling_->tiling_data_, tiling_->current_visible_rect_, - true /* include_borders */); + false /* include_borders */); if (!visible_iterator_) { AdvancePhase(); return; @@ -1018,56 +1155,37 @@ operator++() { } PictureLayerTiling::TilingEvictionTileIterator::TilingEvictionTileIterator() - : tiling_(NULL), eviction_tiles_(NULL) { + : eviction_tiles_(NULL), current_eviction_tiles_index_(0u) { } PictureLayerTiling::TilingEvictionTileIterator::TilingEvictionTileIterator( PictureLayerTiling* tiling, TreePriority tree_priority, - TilePriority::PriorityBin type, - bool required_for_activation) - : tiling_(tiling), tree_priority_(tree_priority), eviction_tiles_(NULL) { - if (required_for_activation && type != TilePriority::NOW) - return; - - tiling_->UpdateEvictionCacheIfNeeded(tree_priority_); - switch (type) { - case TilePriority::EVENTUALLY: - eviction_tiles_ = &tiling_->eventually_eviction_tiles_; - break; - case TilePriority::SOON: - eviction_tiles_ = &tiling_->soon_eviction_tiles_; - break; - case TilePriority::NOW: - if (required_for_activation) - eviction_tiles_ = &tiling_->now_required_for_activation_eviction_tiles_; - else - eviction_tiles_ = - &tiling_->now_not_required_for_activation_eviction_tiles_; - break; - } + EvictionCategory category) + : eviction_tiles_(tiling->GetEvictionTiles(tree_priority, category)), + // Note: initializing to "0 - 1" works as overflow is well defined for + // unsigned integers. + current_eviction_tiles_index_(static_cast(0) - 1) { DCHECK(eviction_tiles_); - tile_iterator_ = eviction_tiles_->begin(); - if (tile_iterator_ != eviction_tiles_->end() && - !(*tile_iterator_)->HasResources()) { - ++(*this); - } + ++(*this); } -PictureLayerTiling::TilingEvictionTileIterator::~TilingEvictionTileIterator() {} +PictureLayerTiling::TilingEvictionTileIterator::~TilingEvictionTileIterator() { +} PictureLayerTiling::TilingEvictionTileIterator::operator bool() const { - return eviction_tiles_ && tile_iterator_ != eviction_tiles_->end(); + return eviction_tiles_ && + current_eviction_tiles_index_ != eviction_tiles_->size(); } Tile* PictureLayerTiling::TilingEvictionTileIterator::operator*() { DCHECK(*this); - return *tile_iterator_; + return (*eviction_tiles_)[current_eviction_tiles_index_]; } const Tile* PictureLayerTiling::TilingEvictionTileIterator::operator*() const { DCHECK(*this); - return *tile_iterator_; + return (*eviction_tiles_)[current_eviction_tiles_index_]; } PictureLayerTiling::TilingEvictionTileIterator& @@ -1075,9 +1193,9 @@ PictureLayerTiling::TilingEvictionTileIterator:: operator++() { DCHECK(*this); do { - ++tile_iterator_; - } while (tile_iterator_ != eviction_tiles_->end() && - (!(*tile_iterator_)->HasResources())); + ++current_eviction_tiles_index_; + } while (current_eviction_tiles_index_ != eviction_tiles_->size() && + !(*eviction_tiles_)[current_eviction_tiles_index_]->HasResources()); return *this; } diff --git a/cc/resources/picture_layer_tiling.h b/cc/resources/picture_layer_tiling.h index a9b38a65cdfeb..9ca0d4147f487 100644 --- a/cc/resources/picture_layer_tiling.h +++ b/cc/resources/picture_layer_tiling.h @@ -44,6 +44,8 @@ class CC_EXPORT PictureLayerTilingClient { virtual const Region* GetInvalidation() = 0; virtual const PictureLayerTiling* GetTwinTiling( const PictureLayerTiling* tiling) const = 0; + virtual PictureLayerTiling* GetRecycledTwinTiling( + const PictureLayerTiling* tiling) = 0; virtual size_t GetMaxTilesForInterestArea() const = 0; virtual float GetSkewportTargetTimeInSeconds() const = 0; virtual int GetSkewportExtrapolationLimitInContentPixels() const = 0; @@ -55,6 +57,15 @@ class CC_EXPORT PictureLayerTilingClient { class CC_EXPORT PictureLayerTiling { public: + enum EvictionCategory { + EVENTUALLY, + EVENTUALLY_AND_REQUIRED_FOR_ACTIVATION, + SOON, + SOON_AND_REQUIRED_FOR_ACTIVATION, + NOW, + NOW_AND_REQUIRED_FOR_ACTIVATION + }; + class CC_EXPORT TilingRasterTileIterator { public: TilingRasterTileIterator(); @@ -109,8 +120,7 @@ class CC_EXPORT PictureLayerTiling { TilingEvictionTileIterator(); TilingEvictionTileIterator(PictureLayerTiling* tiling, TreePriority tree_priority, - TilePriority::PriorityBin type, - bool required_for_activation); + EvictionCategory category); ~TilingEvictionTileIterator(); operator bool() const; @@ -119,10 +129,8 @@ class CC_EXPORT PictureLayerTiling { TilingEvictionTileIterator& operator++(); private: - PictureLayerTiling* tiling_; - TreePriority tree_priority_; - std::vector::iterator tile_iterator_; - std::vector* eviction_tiles_; + const std::vector* eviction_tiles_; + size_t current_eviction_tiles_index_; }; ~PictureLayerTiling(); @@ -288,7 +296,9 @@ class CC_EXPORT PictureLayerTiling { const gfx::Size& layer_bounds, PictureLayerTilingClient* client); void SetLiveTilesRect(const gfx::Rect& live_tiles_rect); + void VerifyLiveTilesRect(); Tile* CreateTile(int i, int j, const PictureLayerTiling* twin_tiling); + void RemoveTileAt(int i, int j); // Computes a skewport. The calculation extrapolates the last visible // rect and the current visible rect to expand the skewport to where it @@ -299,6 +309,9 @@ class CC_EXPORT PictureLayerTiling { const; void UpdateEvictionCacheIfNeeded(TreePriority tree_priority); + const std::vector* GetEvictionTiles(TreePriority tree_priority, + EvictionCategory category); + void Invalidate(const Region& layer_region); void DoInvalidate(const Region& layer_region, @@ -330,10 +343,16 @@ class CC_EXPORT PictureLayerTiling { bool has_soon_border_rect_tiles_; bool has_eventually_rect_tiles_; - std::vector eventually_eviction_tiles_; - std::vector soon_eviction_tiles_; - std::vector now_required_for_activation_eviction_tiles_; - std::vector now_not_required_for_activation_eviction_tiles_; + // TODO(reveman): Remove this in favour of an array of eviction_tiles_ when we + // change all enums to have a consistent way of getting the count/last + // element. + std::vector eviction_tiles_now_; + std::vector eviction_tiles_now_and_required_for_activation_; + std::vector eviction_tiles_soon_; + std::vector eviction_tiles_soon_and_required_for_activation_; + std::vector eviction_tiles_eventually_; + std::vector eviction_tiles_eventually_and_required_for_activation_; + bool eviction_tiles_cache_valid_; TreePriority eviction_cache_tree_priority_; diff --git a/cc/resources/picture_layer_tiling_perftest.cc b/cc/resources/picture_layer_tiling_perftest.cc index d051897c99585..69bb01e9adbbb 100644 --- a/cc/resources/picture_layer_tiling_perftest.cc +++ b/cc/resources/picture_layer_tiling_perftest.cc @@ -73,7 +73,7 @@ class PictureLayerTilingPerfTest : public testing::Test { timer_.Reset(); do { - picture_layer_tiling_->UpdateTilePriorities(ACTIVE_TREE, + picture_layer_tiling_->UpdateTilePriorities(PENDING_TREE, viewport_rect, 1.f, timer_.NumLaps() + 1, @@ -103,7 +103,7 @@ class PictureLayerTilingPerfTest : public testing::Test { timer_.Reset(); do { - picture_layer_tiling_->UpdateTilePriorities(ACTIVE_TREE, + picture_layer_tiling_->UpdateTilePriorities(PENDING_TREE, viewport_rect, 1.f, timer_.NumLaps() + 1, @@ -204,8 +204,7 @@ class PictureLayerTilingPerfTest : public testing::Test { PictureLayerTiling::TilingEvictionTileIterator it( picture_layer_tiling_.get(), priorities[priority_count], - TilePriority::NOW, - false); + PictureLayerTiling::NOW); priority_count = (priority_count + 1) % arraysize(priorities); timer_.NextLap(); } while (!timer_.HasTimeLimitExpired()); @@ -251,8 +250,7 @@ class PictureLayerTilingPerfTest : public testing::Test { PictureLayerTiling::TilingEvictionTileIterator it( picture_layer_tiling_.get(), priorities[priority_count], - TilePriority::EVENTUALLY, - false); + PictureLayerTiling::EVENTUALLY); while (count--) { ASSERT_TRUE(it) << "count: " << count; ASSERT_TRUE(*it != NULL) << "count: " << count; diff --git a/cc/resources/picture_layer_tiling_set.h b/cc/resources/picture_layer_tiling_set.h index 5bdf0fb2bc190..f19c1b875ad7f 100644 --- a/cc/resources/picture_layer_tiling_set.h +++ b/cc/resources/picture_layer_tiling_set.h @@ -30,10 +30,6 @@ class CC_EXPORT PictureLayerTilingSet { struct TilingRange { TilingRange(size_t start, size_t end) : start(start), end(end) {} - bool IsIndexWithinRange(size_t index) const { - return index >= start && index < end; - } - size_t start; size_t end; }; diff --git a/cc/resources/picture_layer_tiling_unittest.cc b/cc/resources/picture_layer_tiling_unittest.cc index 22e55e6bd7441..f03c3bff32d04 100644 --- a/cc/resources/picture_layer_tiling_unittest.cc +++ b/cc/resources/picture_layer_tiling_unittest.cc @@ -65,6 +65,8 @@ class TestablePictureLayerTiling : public PictureLayerTiling { client)); } + gfx::Rect live_tiles_rect() const { return live_tiles_rect_; } + using PictureLayerTiling::ComputeSkewport; protected: @@ -210,6 +212,196 @@ TEST_F(PictureLayerTilingIteratorTest, ResizeDeletesTiles) { EXPECT_FALSE(tiling_->TileAt(0, 0)); } +TEST_F(PictureLayerTilingIteratorTest, CreateMissingTilesStaysInsideLiveRect) { + // The tiling has three rows and columns. + Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 250)); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x()); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_y()); + + // The live tiles rect is at the very edge of the right-most and + // bottom-most tiles. Their border pixels would still be inside the live + // tiles rect, but the tiles should not exist just for that. + int right = tiling_->TilingDataForTesting().TileBounds(2, 2).x(); + int bottom = tiling_->TilingDataForTesting().TileBounds(2, 2).y(); + + SetLiveRectAndVerifyTiles(gfx::Rect(right, bottom)); + EXPECT_FALSE(tiling_->TileAt(2, 0)); + EXPECT_FALSE(tiling_->TileAt(2, 1)); + EXPECT_FALSE(tiling_->TileAt(2, 2)); + EXPECT_FALSE(tiling_->TileAt(1, 2)); + EXPECT_FALSE(tiling_->TileAt(0, 2)); + + // Verify CreateMissingTilesInLiveTilesRect respects this. + tiling_->CreateMissingTilesInLiveTilesRect(); + EXPECT_FALSE(tiling_->TileAt(2, 0)); + EXPECT_FALSE(tiling_->TileAt(2, 1)); + EXPECT_FALSE(tiling_->TileAt(2, 2)); + EXPECT_FALSE(tiling_->TileAt(1, 2)); + EXPECT_FALSE(tiling_->TileAt(0, 2)); +} + +TEST_F(PictureLayerTilingIteratorTest, ResizeTilingOverTileBorders) { + // The tiling has four rows and three columns. + Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 350)); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x()); + EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y()); + + // The live tiles rect covers the whole tiling. + SetLiveRectAndVerifyTiles(gfx::Rect(250, 350)); + + // Tiles in the bottom row and right column exist. + EXPECT_TRUE(tiling_->TileAt(2, 0)); + EXPECT_TRUE(tiling_->TileAt(2, 1)); + EXPECT_TRUE(tiling_->TileAt(2, 2)); + EXPECT_TRUE(tiling_->TileAt(2, 3)); + EXPECT_TRUE(tiling_->TileAt(1, 3)); + EXPECT_TRUE(tiling_->TileAt(0, 3)); + + int right = tiling_->TilingDataForTesting().TileBounds(2, 2).x(); + int bottom = tiling_->TilingDataForTesting().TileBounds(2, 3).y(); + + // Shrink the tiling so that the last tile row/column is entirely in the + // border pixels of the interior tiles. That row/column is removed. + Region invalidation; + tiling_->UpdateTilesToCurrentPile(invalidation, + gfx::Size(right + 1, bottom + 1)); + EXPECT_EQ(2, tiling_->TilingDataForTesting().num_tiles_x()); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_y()); + + // The live tiles rect was clamped to the pile size. + EXPECT_EQ(gfx::Rect(right + 1, bottom + 1), tiling_->live_tiles_rect()); + + // Since the row/column is gone, the tiles should be gone too. + EXPECT_FALSE(tiling_->TileAt(2, 0)); + EXPECT_FALSE(tiling_->TileAt(2, 1)); + EXPECT_FALSE(tiling_->TileAt(2, 2)); + EXPECT_FALSE(tiling_->TileAt(2, 3)); + EXPECT_FALSE(tiling_->TileAt(1, 3)); + EXPECT_FALSE(tiling_->TileAt(0, 3)); + + // Growing outside the current right/bottom tiles border pixels should create + // the tiles again, even though the live rect has not changed size. + tiling_->UpdateTilesToCurrentPile(invalidation, + gfx::Size(right + 2, bottom + 2)); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x()); + EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y()); + + // Not changed. + EXPECT_EQ(gfx::Rect(right + 1, bottom + 1), tiling_->live_tiles_rect()); + + // The last row/column tiles are inside the live tiles rect. + EXPECT_TRUE(gfx::Rect(right + 1, bottom + 1).Intersects( + tiling_->TilingDataForTesting().TileBounds(2, 0))); + EXPECT_TRUE(gfx::Rect(right + 1, bottom + 1).Intersects( + tiling_->TilingDataForTesting().TileBounds(0, 3))); + + EXPECT_TRUE(tiling_->TileAt(2, 0)); + EXPECT_TRUE(tiling_->TileAt(2, 1)); + EXPECT_TRUE(tiling_->TileAt(2, 2)); + EXPECT_TRUE(tiling_->TileAt(2, 3)); + EXPECT_TRUE(tiling_->TileAt(1, 3)); + EXPECT_TRUE(tiling_->TileAt(0, 3)); +} + +TEST_F(PictureLayerTilingIteratorTest, ResizeLiveTileRectOverTileBorders) { + // The tiling has three rows and columns. + Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 350)); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x()); + EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y()); + + // The live tiles rect covers the whole tiling. + SetLiveRectAndVerifyTiles(gfx::Rect(250, 350)); + + // Tiles in the bottom row and right column exist. + EXPECT_TRUE(tiling_->TileAt(2, 0)); + EXPECT_TRUE(tiling_->TileAt(2, 1)); + EXPECT_TRUE(tiling_->TileAt(2, 2)); + EXPECT_TRUE(tiling_->TileAt(2, 3)); + EXPECT_TRUE(tiling_->TileAt(1, 3)); + EXPECT_TRUE(tiling_->TileAt(0, 3)); + + // Shrink the live tiles rect to the very edge of the right-most and + // bottom-most tiles. Their border pixels would still be inside the live + // tiles rect, but the tiles should not exist just for that. + int right = tiling_->TilingDataForTesting().TileBounds(2, 3).x(); + int bottom = tiling_->TilingDataForTesting().TileBounds(2, 3).y(); + + SetLiveRectAndVerifyTiles(gfx::Rect(right, bottom)); + EXPECT_FALSE(tiling_->TileAt(2, 0)); + EXPECT_FALSE(tiling_->TileAt(2, 1)); + EXPECT_FALSE(tiling_->TileAt(2, 2)); + EXPECT_FALSE(tiling_->TileAt(2, 3)); + EXPECT_FALSE(tiling_->TileAt(1, 3)); + EXPECT_FALSE(tiling_->TileAt(0, 3)); + + // Including the bottom row and right column again, should create the tiles. + SetLiveRectAndVerifyTiles(gfx::Rect(right + 1, bottom + 1)); + EXPECT_TRUE(tiling_->TileAt(2, 0)); + EXPECT_TRUE(tiling_->TileAt(2, 1)); + EXPECT_TRUE(tiling_->TileAt(2, 2)); + EXPECT_TRUE(tiling_->TileAt(2, 3)); + EXPECT_TRUE(tiling_->TileAt(1, 2)); + EXPECT_TRUE(tiling_->TileAt(0, 2)); + + // Shrink the live tiles rect to the very edge of the left-most and + // top-most tiles. Their border pixels would still be inside the live + // tiles rect, but the tiles should not exist just for that. + int left = tiling_->TilingDataForTesting().TileBounds(0, 0).right(); + int top = tiling_->TilingDataForTesting().TileBounds(0, 0).bottom(); + + SetLiveRectAndVerifyTiles(gfx::Rect(left, top, 250 - left, 350 - top)); + EXPECT_FALSE(tiling_->TileAt(0, 3)); + EXPECT_FALSE(tiling_->TileAt(0, 2)); + EXPECT_FALSE(tiling_->TileAt(0, 1)); + EXPECT_FALSE(tiling_->TileAt(0, 0)); + EXPECT_FALSE(tiling_->TileAt(1, 0)); + EXPECT_FALSE(tiling_->TileAt(2, 0)); + + // Including the top row and left column again, should create the tiles. + SetLiveRectAndVerifyTiles( + gfx::Rect(left - 1, top - 1, 250 - left, 350 - top)); + EXPECT_TRUE(tiling_->TileAt(0, 3)); + EXPECT_TRUE(tiling_->TileAt(0, 2)); + EXPECT_TRUE(tiling_->TileAt(0, 1)); + EXPECT_TRUE(tiling_->TileAt(0, 0)); + EXPECT_TRUE(tiling_->TileAt(1, 0)); + EXPECT_TRUE(tiling_->TileAt(2, 0)); +} + +TEST_F(PictureLayerTilingIteratorTest, ResizeLiveTileRectOverSameTiles) { + // The tiling has four rows and three columns. + Initialize(gfx::Size(100, 100), 1, gfx::Size(250, 350)); + EXPECT_EQ(3, tiling_->TilingDataForTesting().num_tiles_x()); + EXPECT_EQ(4, tiling_->TilingDataForTesting().num_tiles_y()); + + // The live tiles rect covers the whole tiling. + SetLiveRectAndVerifyTiles(gfx::Rect(250, 350)); + + // All tiles exist. + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 4; ++j) + EXPECT_TRUE(tiling_->TileAt(i, j)) << i << "," << j; + } + + // Shrink the live tiles rect, but still cover all the tiles. + SetLiveRectAndVerifyTiles(gfx::Rect(1, 1, 249, 349)); + + // All tiles still exist. + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 4; ++j) + EXPECT_TRUE(tiling_->TileAt(i, j)) << i << "," << j; + } + + // Grow the live tiles rect, but still cover all the same tiles. + SetLiveRectAndVerifyTiles(gfx::Rect(0, 0, 250, 350)); + + // All tiles still exist. + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 4; ++j) + EXPECT_TRUE(tiling_->TileAt(i, j)) << i << "," << j; + } +} + TEST_F(PictureLayerTilingIteratorTest, ResizeOverBorderPixelsDeletesTiles) { // Verifies that a resize with invalidation for newly exposed pixels will // deletes tiles that intersect that invalidation. @@ -513,11 +705,12 @@ TEST(PictureLayerTilingTest, ViewportDistanceWithScale) { Tile* tile = tiling->TileAt(i, j); TilePriority priority = tile->priority(ACTIVE_TREE); - if (viewport_in_content_space.Intersects(tile->content_rect())) { + gfx::Rect tile_rect = tiling->TilingDataForTesting().TileBounds(i, j); + if (viewport_in_content_space.Intersects(tile_rect)) { EXPECT_EQ(TilePriority::NOW, priority.priority_bin); EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible); have_now = true; - } else if (soon_rect_in_content_space.Intersects(tile->content_rect())) { + } else if (soon_rect_in_content_space.Intersects(tile_rect)) { EXPECT_EQ(TilePriority::SOON, priority.priority_bin); have_soon = true; } else { @@ -581,14 +774,15 @@ TEST(PictureLayerTilingTest, ViewportDistanceWithScale) { Tile* tile = tiling->TileAt(i, j); TilePriority priority = tile->priority(ACTIVE_TREE); - if (viewport_in_content_space.Intersects(tile->content_rect())) { + gfx::Rect tile_rect = tiling->TilingDataForTesting().TileBounds(i, j); + if (viewport_in_content_space.Intersects(tile_rect)) { EXPECT_EQ(TilePriority::NOW, priority.priority_bin) << "i: " << i << " j: " << j; EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible) << "i: " << i << " j: " << j; have_now = true; - } else if (skewport.Intersects(tile->content_rect()) || - soon_rect_in_content_space.Intersects(tile->content_rect())) { + } else if (skewport.Intersects(tile_rect) || + soon_rect_in_content_space.Intersects(tile_rect)) { EXPECT_EQ(TilePriority::SOON, priority.priority_bin) << "i: " << i << " j: " << j; EXPECT_GT(priority.distance_to_visible, 0.f) << "i: " << i @@ -615,20 +809,46 @@ TEST(PictureLayerTilingTest, ViewportDistanceWithScale) { EXPECT_FLOAT_EQ(28.f, priority.distance_to_visible); priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE); - EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible); + EXPECT_FLOAT_EQ(4.f, priority.distance_to_visible); // Change the underlying layer scale. tiling->UpdateTilePriorities( ACTIVE_TREE, viewport, 2.0f, 3.0, NULL, NULL, gfx::Transform()); priority = tiling->TileAt(5, 1)->priority(ACTIVE_TREE); - EXPECT_FLOAT_EQ(34.f, priority.distance_to_visible); + EXPECT_FLOAT_EQ(136.f, priority.distance_to_visible); priority = tiling->TileAt(2, 5)->priority(ACTIVE_TREE); - EXPECT_FLOAT_EQ(14.f, priority.distance_to_visible); + EXPECT_FLOAT_EQ(56.f, priority.distance_to_visible); priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE); - EXPECT_FLOAT_EQ(0.f, priority.distance_to_visible); + EXPECT_FLOAT_EQ(8.f, priority.distance_to_visible); + + // Test additional scales. + tiling = TestablePictureLayerTiling::Create(0.2f, layer_bounds, &client); + tiling->UpdateTilePriorities( + ACTIVE_TREE, viewport, 1.0f, 4.0, NULL, NULL, gfx::Transform()); + + priority = tiling->TileAt(5, 1)->priority(ACTIVE_TREE); + EXPECT_FLOAT_EQ(110.f, priority.distance_to_visible); + + priority = tiling->TileAt(2, 5)->priority(ACTIVE_TREE); + EXPECT_FLOAT_EQ(70.f, priority.distance_to_visible); + + priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE); + EXPECT_FLOAT_EQ(60.f, priority.distance_to_visible); + + tiling->UpdateTilePriorities( + ACTIVE_TREE, viewport, 0.5f, 5.0, NULL, NULL, gfx::Transform()); + + priority = tiling->TileAt(5, 1)->priority(ACTIVE_TREE); + EXPECT_FLOAT_EQ(55.f, priority.distance_to_visible); + + priority = tiling->TileAt(2, 5)->priority(ACTIVE_TREE); + EXPECT_FLOAT_EQ(35.f, priority.distance_to_visible); + + priority = tiling->TileAt(3, 4)->priority(ACTIVE_TREE); + EXPECT_FLOAT_EQ(30.f, priority.distance_to_visible); } TEST(PictureLayerTilingTest, ExpandRectEqual) { @@ -1057,7 +1277,7 @@ TEST(PictureLayerTilingTest, TilingEvictionTileIteratorStaticViewport) { std::vector all_tiles = tiling->AllTilesForTesting(); PictureLayerTiling::TilingEvictionTileIterator it( - tiling.get(), SMOOTHNESS_TAKES_PRIORITY, TilePriority::NOW, false); + tiling.get(), SMOOTHNESS_TAKES_PRIORITY, PictureLayerTiling::NOW); // Tiles don't have resources to evict. EXPECT_FALSE(it); @@ -1072,7 +1292,7 @@ TEST(PictureLayerTilingTest, TilingEvictionTileIteratorStaticViewport) { std::set eviction_tiles; it = PictureLayerTiling::TilingEvictionTileIterator( - tiling.get(), SMOOTHNESS_TAKES_PRIORITY, TilePriority::EVENTUALLY, false); + tiling.get(), SMOOTHNESS_TAKES_PRIORITY, PictureLayerTiling::EVENTUALLY); EXPECT_TRUE(it); for (; it; ++it) { Tile* tile = *it; @@ -1084,7 +1304,7 @@ TEST(PictureLayerTilingTest, TilingEvictionTileIteratorStaticViewport) { } it = PictureLayerTiling::TilingEvictionTileIterator( - tiling.get(), SMOOTHNESS_TAKES_PRIORITY, TilePriority::SOON, false); + tiling.get(), SMOOTHNESS_TAKES_PRIORITY, PictureLayerTiling::SOON); EXPECT_TRUE(it); for (; it; ++it) { Tile* tile = *it; @@ -1095,7 +1315,7 @@ TEST(PictureLayerTilingTest, TilingEvictionTileIteratorStaticViewport) { } it = PictureLayerTiling::TilingEvictionTileIterator( - tiling.get(), SMOOTHNESS_TAKES_PRIORITY, TilePriority::NOW, false); + tiling.get(), SMOOTHNESS_TAKES_PRIORITY, PictureLayerTiling::NOW); EXPECT_TRUE(it); for (; it; ++it) { Tile* tile = *it; @@ -1106,7 +1326,9 @@ TEST(PictureLayerTilingTest, TilingEvictionTileIteratorStaticViewport) { } it = PictureLayerTiling::TilingEvictionTileIterator( - tiling.get(), SMOOTHNESS_TAKES_PRIORITY, TilePriority::NOW, true); + tiling.get(), + SMOOTHNESS_TAKES_PRIORITY, + PictureLayerTiling::NOW_AND_REQUIRED_FOR_ACTIVATION); EXPECT_FALSE(it); EXPECT_GT(all_tiles_set.size(), 0u); @@ -1920,5 +2142,88 @@ TEST(PictureLayerTilingTest, ResetClearsPriorities) { tiles.clear(); } +TEST(PictureLayerTilingTest, RecycledTilesCleared) { + // This test performs the following: + // Setup: + // - Two tilings, one active one recycled with all tiles shared. + // Procedure: + // - Viewport moves somewhere far away and active tiling clears tiles. + // - Viewport moves back and a new active tiling tile is created. + // Result: + // - Recycle tiling does _not_ have the tile in the same location (thus it + // will be shared next time a pending tiling is created). + + FakePictureLayerTilingClient client; + scoped_ptr tiling; + + client.SetTileSize(gfx::Size(100, 100)); + client.set_tree(ACTIVE_TREE); + client.set_max_tiles_for_interest_area(10); + tiling = TestablePictureLayerTiling::Create(1.0f, // contents_scale + gfx::Size(10000, 10000), + &client); + // Create all tiles on this tiling. + tiling->UpdateTilePriorities(ACTIVE_TREE, + gfx::Rect(0, 0, 100, 100), + 1.0f, + 1.0f, + NULL, // occlusion tracker + NULL, // render target + gfx::Transform()); // draw transform + + FakePictureLayerTilingClient second_client; + second_client.SetTileSize(gfx::Size(100, 100)); + second_client.set_tree(PENDING_TREE); + second_client.set_twin_tiling(tiling.get()); + second_client.set_max_tiles_for_interest_area(10); + + scoped_ptr second_tiling; + second_tiling = TestablePictureLayerTiling::Create(1.0f, // contents_scale + gfx::Size(10000, 10000), + &second_client); + + // Create all tiles on the second tiling. All tiles should be shared. + second_tiling->UpdateTilePriorities(ACTIVE_TREE, + gfx::Rect(0, 0, 100, 100), + 1.0f, + 1.0f, + NULL, // occlusion tracker + NULL, // render target + gfx::Transform()); // draw transform + + // Verify that tiles exist and are shared. + ASSERT_TRUE(tiling->TileAt(0, 0)); + ASSERT_EQ(tiling->TileAt(0, 0), second_tiling->TileAt(0, 0)); + + // Set the second tiling as recycled. + client.set_twin_tiling(NULL); + client.set_recycled_twin_tiling(second_tiling.get()); + second_client.set_twin_tiling(NULL); + + // Move the viewport far away from the (0, 0) tile. + tiling->UpdateTilePriorities(ACTIVE_TREE, + gfx::Rect(9000, 9000, 100, 100), + 1.0f, + 2.0, + NULL, // occlusion tracker + NULL, // render target + gfx::Transform()); // draw transform + // Ensure the tile was deleted. + EXPECT_FALSE(tiling->TileAt(0, 0)); + + // Move the viewport back to (0, 0) tile. + tiling->UpdateTilePriorities(ACTIVE_TREE, + gfx::Rect(0, 0, 100, 100), + 1.0f, + 3.0, + NULL, // occlusion tracker + NULL, // render target + gfx::Transform()); // draw transform + + // Ensure that we now have a tile here, but the recycle tiling does not. + EXPECT_TRUE(tiling->TileAt(0, 0)); + EXPECT_FALSE(second_tiling->TileAt(0, 0)); +} + } // namespace } // namespace cc diff --git a/cc/resources/picture_pile.cc b/cc/resources/picture_pile.cc index 30375217b5825..46f589eddf40b 100644 --- a/cc/resources/picture_pile.cc +++ b/cc/resources/picture_pile.cc @@ -226,34 +226,151 @@ bool PicturePile::UpdateAndExpandInvalidation( gfx::Rect old_tiling_rect_over_tiles = tiling_.ExpandRectToTileBounds(gfx::Rect(old_tiling_size)); if (min_toss_x < tiling_.num_tiles_x()) { - int unrecorded_left = std::max(tiling_.TilePositionX(min_toss_x), - interest_rect_over_tiles.right()); + // The bounds which we want to invalidate are the tiles along the old + // edge of the pile. We'll call this bounding box the OLD EDGE RECT. + // + // In the picture below, the old edge rect would be the bounding box + // of tiles {h,i,j}. |min_toss_x| would be equal to the horizontal index + // of the same tiles. + // + // old pile edge-v new pile edge-v + // ---------------+ - - - - - - - -+ + // mmppssvvyybbeeh|h . + // mmppssvvyybbeeh|h . + // nnqqttwwzzccffi|i . + // nnqqttwwzzccffi|i . + // oorruuxxaaddggj|j . + // oorruuxxaaddggj|j . + // ---------------+ - - - - - - - -+ <- old pile edge + // . + // - - - - - - - - - - - - - - - -+ <- new pile edge + // + // If you were to slide a vertical beam from the left edge of the + // old edge rect toward the right, it would either hit the right edge + // of the old edge rect, or the interest rect (expanded to the bounds + // of the tiles it touches). The same is true for a beam parallel to + // any of the four edges, sliding accross the old edge rect. We use + // the union of these four rectangles generated by these beams to + // determine which part of the old edge rect is outside of the expanded + // interest rect. + // + // Case 1: Intersect rect is outside the old edge rect. It can be + // either on the left or the right. The |left_rect| and |right_rect|, + // cover this case, one will be empty and one will cover the full + // old edge rect. In the picture below, |left_rect| would cover the + // old edge rect, and |right_rect| would be empty. + // +----------------------+ |^^^^^^^^^^^^^^^| + // |===> OLD EDGE RECT | | | + // |===> | | INTEREST RECT | + // |===> | | | + // |===> | | | + // +----------------------+ |vvvvvvvvvvvvvvv| + // + // Case 2: Interest rect is inside the old edge rect. It will always + // fill the entire old edge rect horizontally since the old edge rect + // is a single tile wide, and the interest rect has been expanded to the + // bounds of the tiles it touches. In this case the |left_rect| and + // |right_rect| will be empty, but the case is handled by the |top_rect| + // and |bottom_rect|. In the picture below, neither the |top_rect| nor + // |bottom_rect| would empty, they would each cover the area of the old + // edge rect outside the expanded interest rect. + // +-----------------+ + // |:::::::::::::::::| + // |:::::::::::::::::| + // |vvvvvvvvvvvvvvvvv| + // | | + // +-----------------+ + // | INTEREST RECT | + // | | + // +-----------------+ + // | | + // | OLD EDGE RECT | + // +-----------------+ + // + // Lastly, we need to consider tiles inside the expanded interest rect. + // For those tiles, we want to invalidate exactly the newly exposed + // pixels. In the picture below the tiles in the old edge rect have been + // resized and the area covered by periods must be invalidated. The + // |exposed_rect| will cover exactly that area. + // v-old pile edge + // +---------+-------+ + // | ........| + // | ........| + // | OLD EDGE.RECT..| + // | ........| + // | ........| + // | ........| + // | ........| + // | ........| + // | ........| + // +---------+-------+ + + int left = tiling_.TilePositionX(min_toss_x); + int right = left + tiling_.TileSizeX(min_toss_x); + int top = old_tiling_rect_over_tiles.y(); + int bottom = old_tiling_rect_over_tiles.bottom(); + + int left_until = std::min(interest_rect_over_tiles.x(), right); + int right_until = std::max(interest_rect_over_tiles.right(), left); + int top_until = std::min(interest_rect_over_tiles.y(), bottom); + int bottom_until = std::max(interest_rect_over_tiles.bottom(), top); + int exposed_left = old_tiling_size.width(); - int left = std::min(unrecorded_left, exposed_left); - int tile_right = - tiling_.TilePositionX(min_toss_x) + tiling_.TileSizeX(min_toss_x); - int exposed_right = tiling_size().width(); - int right = std::min(tile_right, exposed_right); - gfx::Rect right_side(left, - old_tiling_rect_over_tiles.y(), - right - left, - old_tiling_rect_over_tiles.height()); - resize_invalidation.Union(right_side); + int exposed_left_until = tiling_size().width(); + int exposed_top = top; + int exposed_bottom = tiling_size().height(); + DCHECK_GE(exposed_left, left); + + gfx::Rect left_rect(left, top, left_until - left, bottom - top); + gfx::Rect right_rect(right_until, top, right - right_until, bottom - top); + gfx::Rect top_rect(left, top, right - left, top_until - top); + gfx::Rect bottom_rect( + left, bottom_until, right - left, bottom - bottom_until); + gfx::Rect exposed_rect(exposed_left, + exposed_top, + exposed_left_until - exposed_left, + exposed_bottom - exposed_top); + resize_invalidation.Union(left_rect); + resize_invalidation.Union(right_rect); + resize_invalidation.Union(top_rect); + resize_invalidation.Union(bottom_rect); + resize_invalidation.Union(exposed_rect); } if (min_toss_y < tiling_.num_tiles_y()) { - int unrecorded_top = std::max(tiling_.TilePositionY(min_toss_y), - interest_rect_over_tiles.bottom()); + // The same thing occurs here as in the case above, but the invalidation + // rect is the bounding box around the bottom row of tiles in the old + // pile. This would be tiles {o,r,u,x,a,d,g,j} in the above picture. + + int top = tiling_.TilePositionY(min_toss_y); + int bottom = top + tiling_.TileSizeY(min_toss_y); + int left = old_tiling_rect_over_tiles.x(); + int right = old_tiling_rect_over_tiles.right(); + + int top_until = std::min(interest_rect_over_tiles.y(), bottom); + int bottom_until = std::max(interest_rect_over_tiles.bottom(), top); + int left_until = std::min(interest_rect_over_tiles.x(), right); + int right_until = std::max(interest_rect_over_tiles.right(), left); + int exposed_top = old_tiling_size.height(); - int top = std::min(unrecorded_top, exposed_top); - int tile_bottom = - tiling_.TilePositionY(min_toss_y) + tiling_.TileSizeY(min_toss_y); - int exposed_bottom = tiling_size().height(); - int bottom = std::min(tile_bottom, exposed_bottom); - gfx::Rect bottom_side(old_tiling_rect_over_tiles.x(), - top, - old_tiling_rect_over_tiles.width(), - bottom - top); - resize_invalidation.Union(bottom_side); + int exposed_top_until = tiling_size().height(); + int exposed_left = left; + int exposed_right = tiling_size().width(); + DCHECK_GE(exposed_top, top); + + gfx::Rect left_rect(left, top, left_until - left, bottom - top); + gfx::Rect right_rect(right_until, top, right - right_until, bottom - top); + gfx::Rect top_rect(left, top, right - left, top_until - top); + gfx::Rect bottom_rect( + left, bottom_until, right - left, bottom - bottom_until); + gfx::Rect exposed_rect(exposed_left, + exposed_top, + exposed_right - exposed_left, + exposed_top_until - exposed_top); + resize_invalidation.Union(left_rect); + resize_invalidation.Union(right_rect); + resize_invalidation.Union(top_rect); + resize_invalidation.Union(bottom_rect); + resize_invalidation.Union(exposed_rect); } } diff --git a/cc/resources/picture_pile_base.cc b/cc/resources/picture_pile_base.cc index 146c886d4780d..c7a96808f9c47 100644 --- a/cc/resources/picture_pile_base.cc +++ b/cc/resources/picture_pile_base.cc @@ -47,7 +47,8 @@ PicturePileBase::PicturePileBase() contents_fill_bounds_completely_(false), show_debug_picture_borders_(false), clear_canvas_with_debug_color_(kDefaultClearCanvasSetting), - has_any_recordings_(false) { + has_any_recordings_(false), + is_mask_(false) { tiling_.SetMaxTextureSize(gfx::Size(kBasePictureSize, kBasePictureSize)); tile_grid_info_.fTileInterval.setEmpty(); tile_grid_info_.fMargin.setEmpty(); @@ -67,7 +68,9 @@ PicturePileBase::PicturePileBase(const PicturePileBase* other) contents_fill_bounds_completely_(other->contents_fill_bounds_completely_), show_debug_picture_borders_(other->show_debug_picture_borders_), clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), - has_any_recordings_(other->has_any_recordings_) {} + has_any_recordings_(other->has_any_recordings_), + is_mask_(other->is_mask_) { +} PicturePileBase::PicturePileBase(const PicturePileBase* other, unsigned thread_index) @@ -82,7 +85,8 @@ PicturePileBase::PicturePileBase(const PicturePileBase* other, contents_fill_bounds_completely_(other->contents_fill_bounds_completely_), show_debug_picture_borders_(other->show_debug_picture_borders_), clear_canvas_with_debug_color_(other->clear_canvas_with_debug_color_), - has_any_recordings_(other->has_any_recordings_) { + has_any_recordings_(other->has_any_recordings_), + is_mask_(other->is_mask_) { for (PictureMap::const_iterator it = other->picture_map_.begin(); it != other->picture_map_.end(); ++it) { diff --git a/cc/resources/picture_pile_base.h b/cc/resources/picture_pile_base.h index b188df7d62f6f..102d798a2a8d6 100644 --- a/cc/resources/picture_pile_base.h +++ b/cc/resources/picture_pile_base.h @@ -49,6 +49,9 @@ class CC_EXPORT PicturePileBase : public base::RefCounted { // If this pile contains any valid recordings. May have false positives. bool HasRecordings() const { return has_any_recordings_; } + void set_is_mask(bool is_mask) { is_mask_ = is_mask; } + bool is_mask() const { return is_mask_; } + static void ComputeTileGridInfo(const gfx::Size& tile_grid_size, SkTileGridFactory::TileGridInfo* info); @@ -117,6 +120,7 @@ class CC_EXPORT PicturePileBase : public base::RefCounted { // A hint about whether there are any recordings. This may be a false // positive. bool has_any_recordings_; + bool is_mask_; private: void SetBufferPixels(int buffer_pixels); diff --git a/cc/resources/picture_pile_unittest.cc b/cc/resources/picture_pile_unittest.cc index 31e0a2a32f638..ee78c9c00f3c6 100644 --- a/cc/resources/picture_pile_unittest.cc +++ b/cc/resources/picture_pile_unittest.cc @@ -36,9 +36,9 @@ class TestPicturePile : public PicturePile { virtual ~TestPicturePile() {} }; -class PicturePileTest : public testing::Test { +class PicturePileTestBase { public: - PicturePileTest() + PicturePileTestBase() : pile_(new TestPicturePile()), background_color_(SK_ColorBLUE), min_scale_(0.125), @@ -91,6 +91,8 @@ class PicturePileTest : public testing::Test { bool contents_opaque_; }; +class PicturePileTest : public PicturePileTestBase, public testing::Test {}; + TEST_F(PicturePileTest, SmallInvalidateInflated) { // Invalidate something inside a tile. Region invalidate_rect(gfx::Rect(50, 50, 1, 1)); @@ -384,14 +386,48 @@ TEST_F(PicturePileTest, InvalidationOutsideRecordingRect) { EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); } -TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { +enum Corner { + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT, +}; + +class PicturePileResizeCornerTest : public PicturePileTestBase, + public testing::TestWithParam { + protected: + static gfx::Rect CornerSinglePixelRect(Corner corner, const gfx::Size& s) { + switch (corner) { + case TOP_LEFT: + return gfx::Rect(0, 0, 1, 1); + case TOP_RIGHT: + return gfx::Rect(s.width() - 1, 0, 1, 1); + case BOTTOM_LEFT: + return gfx::Rect(0, s.height() - 1, 1, 1); + case BOTTOM_RIGHT: + return gfx::Rect(s.width() - 1, s.height() - 1, 1, 1); + } + NOTREACHED(); + return gfx::Rect(); + } +}; + +TEST_P(PicturePileResizeCornerTest, ResizePileOutsideInterestRect) { + Corner corner = GetParam(); + // This size chosen to be larger than the interest rect size, which is // at least kPixelDistanceToRecord * 2 in each dimension. int tile_size = 100000; - gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size); - gfx::Size grow_down_tiling_size(5 * tile_size, 7 * tile_size); - gfx::Size grow_right_tiling_size(7 * tile_size, 5 * tile_size); - gfx::Size grow_both_tiling_size(7 * tile_size, 7 * tile_size); + // The small number subtracted keeps the last tile in each axis larger than + // the interest rect also. + int offset = -100; + gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset); + gfx::Size grow_down_tiling_size(6 * tile_size + offset, + 8 * tile_size + offset); + gfx::Size grow_right_tiling_size(8 * tile_size + offset, + 6 * tile_size + offset); + gfx::Size grow_both_tiling_size(8 * tile_size + offset, + 8 * tile_size + offset); Region invalidation; Region expected_invalidation; @@ -412,13 +448,15 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { } UpdateAndExpandInvalidation( - &invalidation, grow_down_tiling_size, gfx::Rect(1, 1)); + &invalidation, + grow_down_tiling_size, + CornerSinglePixelRect(corner, grow_down_tiling_size)); // We should have lost the recordings in the bottom row. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); EXPECT_EQ(8, pile_->tiling().num_tiles_y()); - for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_->tiling().num_tiles_y(); ++j) { + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 6; ++j) { TestPicturePile::PictureMapKey key(i, j); TestPicturePile::PictureMap& map = pile_->picture_map(); TestPicturePile::PictureMap::iterator it = map.find(key); @@ -426,14 +464,22 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { } } - // We invalidated the old bottom row. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5), - pile_->tiling().TileBounds(5, 5)); + // We invalidated all new pixels in the recording. + expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), + gfx::Rect(base_tiling_size)); + // But the new pixels don't cover the whole bottom row. + gfx::Rect bottom_row = gfx::UnionRects(pile_->tiling().TileBounds(0, 5), + pile_->tiling().TileBounds(5, 5)); + EXPECT_FALSE(expected_invalidation.Contains(bottom_row)); + // We invalidated the entire old bottom row. + expected_invalidation.Union(bottom_row); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); + UpdateAndExpandInvalidation(&invalidation, + base_tiling_size, + CornerSinglePixelRect(corner, base_tiling_size)); // We should have lost the recordings that are now outside the tiling only. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); @@ -454,13 +500,15 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { UpdateWholePile(); UpdateAndExpandInvalidation( - &invalidation, grow_right_tiling_size, gfx::Rect(1, 1)); + &invalidation, + grow_right_tiling_size, + CornerSinglePixelRect(corner, grow_right_tiling_size)); // We should have lost the recordings in the right column. EXPECT_EQ(8, pile_->tiling().num_tiles_x()); EXPECT_EQ(6, pile_->tiling().num_tiles_y()); - for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_->tiling().num_tiles_y(); ++j) { + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 6; ++j) { TestPicturePile::PictureMapKey key(i, j); TestPicturePile::PictureMap& map = pile_->picture_map(); TestPicturePile::PictureMap::iterator it = map.find(key); @@ -468,14 +516,22 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { } } - // We invalidated the old right column. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), - pile_->tiling().TileBounds(5, 5)); + // We invalidated all new pixels in the recording. + expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), + gfx::Rect(base_tiling_size)); + // But the new pixels don't cover the whole right_column. + gfx::Rect right_column = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5)); + EXPECT_FALSE(expected_invalidation.Contains(right_column)); + // We invalidated the entire old right column. + expected_invalidation.Union(right_column); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); + UpdateAndExpandInvalidation(&invalidation, + base_tiling_size, + CornerSinglePixelRect(corner, base_tiling_size)); // We should have lost the recordings that are now outside the tiling only. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); @@ -496,13 +552,15 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { UpdateWholePile(); UpdateAndExpandInvalidation( - &invalidation, grow_both_tiling_size, gfx::Rect(1, 1)); + &invalidation, + grow_both_tiling_size, + CornerSinglePixelRect(corner, grow_both_tiling_size)); // We should have lost the recordings in the right column and bottom row. EXPECT_EQ(8, pile_->tiling().num_tiles_x()); EXPECT_EQ(8, pile_->tiling().num_tiles_y()); - for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) { - for (int j = 0; j < pile_->tiling().num_tiles_y(); ++j) { + for (int i = 0; i < 6; ++i) { + for (int j = 0; j < 6; ++j) { TestPicturePile::PictureMapKey key(i, j); TestPicturePile::PictureMap& map = pile_->picture_map(); TestPicturePile::PictureMap::iterator it = map.find(key); @@ -510,11 +568,18 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { } } - // We invalidated the old right column and the old bottom row. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), - pile_->tiling().TileBounds(5, 5)); - expected_invalidation.Union(gfx::UnionRects( - pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5))); + // We invalidated all new pixels in the recording. + expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), + gfx::Rect(base_tiling_size)); + // But the new pixels don't cover the whole right_column. + Region right_column_and_bottom_row = + UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5)), + gfx::UnionRects(pile_->tiling().TileBounds(0, 5), + pile_->tiling().TileBounds(5, 5))); + EXPECT_FALSE(expected_invalidation.Contains(right_column_and_bottom_row)); + // We invalidated the entire old right column and the old bottom row. + expected_invalidation.Union(right_column_and_bottom_row); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); @@ -539,14 +604,22 @@ TEST_F(PicturePileTest, ResizePileOutsideInterestRect) { invalidation.Clear(); } -TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { +TEST_P(PicturePileResizeCornerTest, SmallResizePileOutsideInterestRect) { + Corner corner = GetParam(); + // This size chosen to be larger than the interest rect size, which is // at least kPixelDistanceToRecord * 2 in each dimension. int tile_size = 100000; - gfx::Size base_tiling_size(5 * tile_size, 5 * tile_size); - gfx::Size grow_down_tiling_size(5 * tile_size, 5 * tile_size + 5); - gfx::Size grow_right_tiling_size(5 * tile_size + 5, 5 * tile_size); - gfx::Size grow_both_tiling_size(5 * tile_size + 5, 5 * tile_size + 5); + // The small number subtracted keeps the last tile in each axis larger than + // the interest rect also. + int offset = -100; + gfx::Size base_tiling_size(6 * tile_size + offset, 6 * tile_size + offset); + gfx::Size grow_down_tiling_size(6 * tile_size + offset, + 6 * tile_size + offset + 5); + gfx::Size grow_right_tiling_size(6 * tile_size + offset + 5, + 6 * tile_size + offset); + gfx::Size grow_both_tiling_size(6 * tile_size + offset + 5, + 6 * tile_size + offset + 5); Region invalidation; Region expected_invalidation; @@ -567,9 +640,12 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { } UpdateAndExpandInvalidation( - &invalidation, grow_down_tiling_size, gfx::Rect(1, 1)); + &invalidation, + grow_down_tiling_size, + CornerSinglePixelRect(corner, grow_down_tiling_size)); - // We should have lost the recordings in the bottom row. + // We should have lost the recordings in the bottom row that do not intersect + // the interest rect. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); EXPECT_EQ(6, pile_->tiling().num_tiles_y()); for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) { @@ -577,18 +653,53 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { TestPicturePile::PictureMapKey key(i, j); TestPicturePile::PictureMap& map = pile_->picture_map(); TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_EQ(j < 5, it != map.end() && it->second.GetPicture()); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + case TOP_RIGHT: + expect_tile = j < 5; + break; + case BOTTOM_LEFT: + // The interest rect in the bottom left tile means we'll record it. + expect_tile = j < 5 || (j == 5 && i == 0); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = j < 5 || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); } } - // We invalidated the bottom row. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5), - pile_->tiling().TileBounds(5, 5)); + // We invalidated the bottom row outside the new interest rect. The tile that + // insects the interest rect in invalidated only on its new pixels. + switch (corner) { + case TOP_LEFT: + case TOP_RIGHT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5), + pile_->tiling().TileBounds(5, 5)); + break; + case BOTTOM_LEFT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(1, 5), + pile_->tiling().TileBounds(5, 5)); + expected_invalidation.Union(SubtractRects( + pile_->tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size))); + break; + case BOTTOM_RIGHT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5), + pile_->tiling().TileBounds(4, 5)); + expected_invalidation.Union(SubtractRects( + pile_->tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); + break; + } EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); + UpdateAndExpandInvalidation(&invalidation, + base_tiling_size, + CornerSinglePixelRect(corner, base_tiling_size)); // We should have lost nothing. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); @@ -609,7 +720,9 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { UpdateWholePile(); UpdateAndExpandInvalidation( - &invalidation, grow_right_tiling_size, gfx::Rect(1, 1)); + &invalidation, + grow_right_tiling_size, + CornerSinglePixelRect(corner, grow_right_tiling_size)); // We should have lost the recordings in the right column. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); @@ -619,18 +732,53 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { TestPicturePile::PictureMapKey key(i, j); TestPicturePile::PictureMap& map = pile_->picture_map(); TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_EQ(i < 5, it != map.end() && it->second.GetPicture()); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + case BOTTOM_LEFT: + expect_tile = i < 5; + break; + case TOP_RIGHT: + // The interest rect in the top right tile means we'll record it. + expect_tile = i < 5 || (j == 0 && i == 5); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = i < 5 || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()); } } - // We invalidated the right column. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), - pile_->tiling().TileBounds(5, 5)); + // We invalidated the right column outside the new interest rect. The tile + // that insects the interest rect in invalidated only on its new pixels. + switch (corner) { + case TOP_LEFT: + case BOTTOM_LEFT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5)); + break; + case TOP_RIGHT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 1), + pile_->tiling().TileBounds(5, 5)); + expected_invalidation.Union(SubtractRects( + pile_->tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size))); + break; + case BOTTOM_RIGHT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 4)); + expected_invalidation.Union(SubtractRects( + pile_->tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); + break; + } EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); + UpdateAndExpandInvalidation(&invalidation, + base_tiling_size, + CornerSinglePixelRect(corner, base_tiling_size)); // We should have lost nothing. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); @@ -651,9 +799,12 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { UpdateWholePile(); UpdateAndExpandInvalidation( - &invalidation, grow_both_tiling_size, gfx::Rect(1, 1)); + &invalidation, + grow_both_tiling_size, + CornerSinglePixelRect(corner, grow_both_tiling_size)); - // We should have lost the recordings in the right column and bottom row. + // We should have lost the recordings in the right column and bottom row. The + // tile that insects the interest rect in invalidated only on its new pixels. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); EXPECT_EQ(6, pile_->tiling().num_tiles_y()); for (int i = 0; i < pile_->tiling().num_tiles_x(); ++i) { @@ -661,20 +812,71 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { TestPicturePile::PictureMapKey key(i, j); TestPicturePile::PictureMap& map = pile_->picture_map(); TestPicturePile::PictureMap::iterator it = map.find(key); - EXPECT_EQ(i < 5 && j < 5, it != map.end() && it->second.GetPicture()); + bool expect_tile; + switch (corner) { + case TOP_LEFT: + expect_tile = i < 5 && j < 5; + break; + case TOP_RIGHT: + // The interest rect in the top right tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 0 && i == 5); + break; + case BOTTOM_LEFT: + // The interest rect in the bottom left tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 5 && i == 0); + break; + case BOTTOM_RIGHT: + // The interest rect in the bottom right tile means we'll record it. + expect_tile = (i < 5 && j < 5) || (j == 5 && i == 5); + break; + } + EXPECT_EQ(expect_tile, it != map.end() && it->second.GetPicture()) + << i << "," << j; } } - // We invalidated the right column and the bottom row. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), - pile_->tiling().TileBounds(5, 5)); - expected_invalidation.Union(gfx::UnionRects( - pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5))); + // We invalidated the right column and the bottom row outside the new interest + // rect. The tile that insects the interest rect in invalidated only on its + // new pixels. + switch (corner) { + case TOP_LEFT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5)); + expected_invalidation.Union(gfx::UnionRects( + pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5))); + break; + case TOP_RIGHT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 1), + pile_->tiling().TileBounds(5, 5)); + expected_invalidation.Union(gfx::UnionRects( + pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5))); + expected_invalidation.Union(SubtractRects( + pile_->tiling().TileBounds(5, 0), gfx::Rect(base_tiling_size))); + break; + case BOTTOM_LEFT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5)); + expected_invalidation.Union(gfx::UnionRects( + pile_->tiling().TileBounds(1, 5), pile_->tiling().TileBounds(5, 5))); + expected_invalidation.Union(SubtractRects( + pile_->tiling().TileBounds(0, 5), gfx::Rect(base_tiling_size))); + break; + case BOTTOM_RIGHT: + expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 4)); + expected_invalidation.Union(gfx::UnionRects( + pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(4, 5))); + expected_invalidation.Union(SubtractRegions( + pile_->tiling().TileBounds(5, 5), gfx::Rect(base_tiling_size))); + break; + } EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); UpdateWholePile(); - UpdateAndExpandInvalidation(&invalidation, base_tiling_size, gfx::Rect(1, 1)); + UpdateAndExpandInvalidation(&invalidation, + base_tiling_size, + CornerSinglePixelRect(corner, base_tiling_size)); // We should have lost nothing. EXPECT_EQ(6, pile_->tiling().num_tiles_x()); @@ -694,6 +896,11 @@ TEST_F(PicturePileTest, SmallResizePileOutsideInterestRect) { invalidation.Clear(); } +INSTANTIATE_TEST_CASE_P( + PicturePileResizeCornerTests, + PicturePileResizeCornerTest, + ::testing::Values(TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT)); + TEST_F(PicturePileTest, ResizePileInsideInterestRect) { // This size chosen to be small enough that all the rects below fit inside the // the interest rect, so they are smaller than kPixelDistanceToRecord in each @@ -738,9 +945,13 @@ TEST_F(PicturePileTest, ResizePileInsideInterestRect) { } // We invalidated the newly exposed pixels on the bottom row of tiles. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(0, 5), - pile_->tiling().TileBounds(5, 5)); - expected_invalidation.Subtract(gfx::Rect(base_tiling_size)); + expected_invalidation = SubtractRegions(gfx::Rect(grow_down_tiling_size), + gfx::Rect(base_tiling_size)); + Region bottom_row_new_pixels = + SubtractRegions(gfx::UnionRects(pile_->tiling().TileBounds(0, 5), + pile_->tiling().TileBounds(5, 5)), + gfx::Rect(base_tiling_size)); + EXPECT_TRUE(expected_invalidation.Contains(bottom_row_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); @@ -780,9 +991,13 @@ TEST_F(PicturePileTest, ResizePileInsideInterestRect) { } // We invalidated the newly exposed pixels on the right column of tiles. - expected_invalidation = gfx::UnionRects(pile_->tiling().TileBounds(5, 0), - pile_->tiling().TileBounds(5, 5)); - expected_invalidation.Subtract(gfx::Rect(base_tiling_size)); + expected_invalidation = SubtractRegions(gfx::Rect(grow_right_tiling_size), + gfx::Rect(base_tiling_size)); + Region right_column_new_pixels = + SubtractRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5)), + gfx::Rect(base_tiling_size)); + EXPECT_TRUE(expected_invalidation.Contains(right_column_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); @@ -823,12 +1038,16 @@ TEST_F(PicturePileTest, ResizePileInsideInterestRect) { // We invalidated the newly exposed pixels on the bottom row and right column // of tiles. - expected_invalidation = - UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + expected_invalidation = SubtractRegions(gfx::Rect(grow_both_tiling_size), + gfx::Rect(base_tiling_size)); + Region bottom_row_and_right_column_new_pixels = SubtractRegions( + UnionRegions(gfx::UnionRects(pile_->tiling().TileBounds(0, 5), pile_->tiling().TileBounds(5, 5)), - gfx::UnionRects(pile_->tiling().TileBounds(0, 5), - pile_->tiling().TileBounds(5, 5))); - expected_invalidation.Subtract(gfx::Rect(base_tiling_size)); + gfx::UnionRects(pile_->tiling().TileBounds(5, 0), + pile_->tiling().TileBounds(5, 5))), + gfx::Rect(base_tiling_size)); + EXPECT_TRUE( + expected_invalidation.Contains(bottom_row_and_right_column_new_pixels)); EXPECT_EQ(expected_invalidation.ToString(), invalidation.ToString()); invalidation.Clear(); diff --git a/cc/resources/pixel_buffer_raster_worker_pool.cc b/cc/resources/pixel_buffer_raster_worker_pool.cc index d7554736942a6..67ae593bde0a5 100644 --- a/cc/resources/pixel_buffer_raster_worker_pool.cc +++ b/cc/resources/pixel_buffer_raster_worker_pool.cc @@ -515,10 +515,13 @@ void PixelBufferRasterWorkerPool::ScheduleMoreTasks() { continue; } - // All raster tasks need to be throttled by bytes of pending uploads. + // All raster tasks need to be throttled by bytes of pending uploads, + // but if it's the only task allow it to complete no matter what its size, + // to prevent starvation of the task queue. size_t new_bytes_pending_upload = bytes_pending_upload; new_bytes_pending_upload += task->resource()->bytes(); - if (new_bytes_pending_upload > max_bytes_pending_upload_) { + if (new_bytes_pending_upload > max_bytes_pending_upload_ && + bytes_pending_upload) { did_throttle_raster_tasks = true; if (item.required_for_activation) did_throttle_raster_tasks_required_for_activation = true; diff --git a/cc/resources/platform_color.h b/cc/resources/platform_color.h index ecdf7c166f2df..4945dccadc57d 100644 --- a/cc/resources/platform_color.h +++ b/cc/resources/platform_color.h @@ -44,7 +44,7 @@ class PlatformColor { case SOURCE_FORMAT_RGBA8: return format == RGBA_8888 || format == RGBA_4444; case SOURCE_FORMAT_BGRA8: - return format == BGRA_8888; + return format == BGRA_8888 || format == RGBA_4444; } NOTREACHED(); return false; diff --git a/cc/resources/prioritized_tile_set.cc b/cc/resources/prioritized_tile_set.cc index 82c7f16facdd4..d0104cbe331f8 100644 --- a/cc/resources/prioritized_tile_set.cc +++ b/cc/resources/prioritized_tile_set.cc @@ -40,13 +40,20 @@ class BinComparator { namespace { +bool TilePriorityTieBreaker(const Tile* tile_i, const Tile* tile_j) { + // When two tiles has same priority use Id as tie breaker. + return tile_i->id() < tile_j->id(); +} + typedef std::vector TileVector; void SortBinTiles(ManagedTileBin bin, TileVector* tiles) { switch (bin) { - case NOW_AND_READY_TO_DRAW_BIN: case NEVER_BIN: break; + case NOW_AND_READY_TO_DRAW_BIN: + std::sort(tiles->begin(), tiles->end(), TilePriorityTieBreaker); + break; case NOW_BIN: case SOON_BIN: case EVENTUALLY_AND_ACTIVE_BIN: diff --git a/cc/resources/raster_tile_priority_queue.cc b/cc/resources/raster_tile_priority_queue.cc index 97d9e831908b8..e0591b3d2a228 100644 --- a/cc/resources/raster_tile_priority_queue.cc +++ b/cc/resources/raster_tile_priority_queue.cc @@ -16,11 +16,11 @@ class RasterOrderComparator { bool operator()( const RasterTilePriorityQueue::PairedPictureLayerQueue* a, const RasterTilePriorityQueue::PairedPictureLayerQueue* b) const { - if (a->IsEmpty()) - return true; - - if (b->IsEmpty()) - return false; + // Note that in this function, we have to return true if and only if + // b is strictly lower priority than a. Note that for the sake of + // completeness, empty queue is considered to have lowest priority. + if (a->IsEmpty() || b->IsEmpty()) + return b->IsEmpty() < a->IsEmpty(); WhichTree a_tree = a->NextTileIteratorTree(tree_priority_); const PictureLayerImpl::LayerRasterTileIterator* a_iterator = @@ -39,8 +39,6 @@ class RasterOrderComparator { b_tile->priority_for_tree_priority(tree_priority_); bool prioritize_low_res = tree_priority_ == SMOOTHNESS_TAKES_PRIORITY; - // Now we have to return true iff b is higher priority than a. - // If the bin is the same but the resolution is not, then the order will be // determined by whether we prioritize low res or not. // TODO(vmpstr): Remove this when TilePriority is no longer a member of Tile @@ -56,10 +54,8 @@ class RasterOrderComparator { if (prioritize_low_res) return b_priority.resolution == LOW_RESOLUTION; - return b_priority.resolution == HIGH_RESOLUTION; } - return b_priority.IsHigherPriorityThan(a_priority); } @@ -86,7 +82,6 @@ void RasterTilePriorityQueue::Build( paired_queues_.push_back( make_scoped_ptr(new PairedPictureLayerQueue(*it, tree_priority_))); } - paired_queues_.make_heap(RasterOrderComparator(tree_priority_)); } diff --git a/cc/resources/raster_worker_pool_unittest.cc b/cc/resources/raster_worker_pool_unittest.cc index 3b43c099b9707..217916136c533 100644 --- a/cc/resources/raster_worker_pool_unittest.cc +++ b/cc/resources/raster_worker_pool_unittest.cc @@ -27,6 +27,11 @@ namespace cc { namespace { +const size_t kMaxTransferBufferUsageBytes = 10000U; +// A resource of this dimension^2 * 4 must be greater than the above transfer +// buffer constant. +const size_t kLargeResourceDimension = 1000U; + enum RasterWorkerPoolType { RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER, RASTER_WORKER_POOL_TYPE_IMAGE, @@ -126,7 +131,7 @@ class RasterWorkerPoolTest RasterWorkerPool::GetTaskGraphRunner(), context_provider_.get(), resource_provider_.get(), - std::numeric_limits::max()); + kMaxTransferBufferUsageBytes); break; case RASTER_WORKER_POOL_TYPE_IMAGE: raster_worker_pool_ = ImageRasterWorkerPool::Create( @@ -203,9 +208,7 @@ class RasterWorkerPoolTest raster_worker_pool_->AsRasterizer()->ScheduleTasks(&queue); } - void AppendTask(unsigned id) { - const gfx::Size size(1, 1); - + void AppendTask(unsigned id, const gfx::Size& size) { scoped_ptr resource( ScopedResource::Create(resource_provider_.get())); resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888); @@ -221,6 +224,8 @@ class RasterWorkerPoolTest &empty)); } + void AppendTask(unsigned id) { AppendTask(id, gfx::Size(1, 1)); } + void AppendBlockingTask(unsigned id, base::Lock* lock) { const gfx::Size size(1, 1); @@ -324,6 +329,27 @@ TEST_P(RasterWorkerPoolTest, FalseThrottling) { RunMessageLoopUntilAllTasksHaveCompleted(); } +TEST_P(RasterWorkerPoolTest, LargeResources) { + gfx::Size size(kLargeResourceDimension, kLargeResourceDimension); + + { + // Verify a resource of this size is larger than the transfer buffer. + scoped_ptr resource( + ScopedResource::Create(resource_provider_.get())); + resource->Allocate(size, ResourceProvider::TextureUsageAny, RGBA_8888); + EXPECT_GE(resource->bytes(), kMaxTransferBufferUsageBytes); + } + + AppendTask(0u, size); + AppendTask(1u, size); + AppendTask(2u, size); + ScheduleTasks(); + + // This will time out if a resource that is larger than the throttle limit + // never gets scheduled. + RunMessageLoopUntilAllTasksHaveCompleted(); +} + INSTANTIATE_TEST_CASE_P(RasterWorkerPoolTests, RasterWorkerPoolTest, ::testing::Values(RASTER_WORKER_POOL_TYPE_PIXEL_BUFFER, diff --git a/cc/resources/resource_format.cc b/cc/resources/resource_format.cc index f58831bfbc361..6cd0a93e921a3 100644 --- a/cc/resources/resource_format.cc +++ b/cc/resources/resource_format.cc @@ -14,6 +14,7 @@ SkColorType ResourceFormatToSkColorType(ResourceFormat format) { case BGRA_8888: return kN32_SkColorType; case ETC1: + case ALPHA_8: case LUMINANCE_8: case RGB_565: NOTREACHED(); diff --git a/cc/resources/resource_format.h b/cc/resources/resource_format.h index 816c285791397..b51ac7cfe721d 100644 --- a/cc/resources/resource_format.h +++ b/cc/resources/resource_format.h @@ -15,6 +15,7 @@ enum ResourceFormat { RGBA_8888, RGBA_4444, BGRA_8888, + ALPHA_8, LUMINANCE_8, RGB_565, ETC1, diff --git a/cc/resources/resource_provider.cc b/cc/resources/resource_provider.cc index ff0652465d904..d88bdd29cb19e 100644 --- a/cc/resources/resource_provider.cc +++ b/cc/resources/resource_provider.cc @@ -70,6 +70,7 @@ GLenum TextureToStorageFormat(ResourceFormat format) { storage_format = GL_BGRA8_EXT; break; case RGBA_4444: + case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: @@ -86,6 +87,7 @@ bool IsFormatSupportedForStorage(ResourceFormat format) { case BGRA_8888: return true; case RGBA_4444: + case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: @@ -109,25 +111,16 @@ GrPixelConfig ToGrPixelConfig(ResourceFormat format) { return kSkia8888_GrPixelConfig; } -class IdentityAllocator : public SkBitmap::Allocator { - public: - explicit IdentityAllocator(void* buffer) : buffer_(buffer) {} - virtual bool allocPixelRef(SkBitmap* dst, SkColorTable*) OVERRIDE { - dst->setPixels(buffer_); - return true; - } - - private: - void* buffer_; -}; - -void CopyBitmap(const SkBitmap& src, uint8_t* dst, SkColorType dst_colorType) { - SkBitmap dst_bitmap; - IdentityAllocator allocator(dst); - src.copyTo(&dst_bitmap, dst_colorType, &allocator); +void CopyBitmap(const SkBitmap& src, uint8_t* dst, SkColorType dst_color_type) { + SkImageInfo dst_info = src.info(); + dst_info.fColorType = dst_color_type; // TODO(kaanb): The GL pipeline assumes a 4-byte alignment for the - // bitmap data. This check will be removed once crbug.com/293728 is fixed. - CHECK_EQ(0u, dst_bitmap.rowBytes() % 4); + // bitmap data. There will be no need to call SkAlign4 once crbug.com/293728 + // is fixed. + const size_t dst_row_bytes = SkAlign4(dst_info.minRowBytes()); + CHECK_EQ(0u, dst_row_bytes % 4); + bool success = src.readPixels(dst_info, dst, dst_row_bytes, 0, 0); + CHECK_EQ(true, success); } class ScopedSetActiveTexture { @@ -516,6 +509,7 @@ SkCanvas* ResourceProvider::BitmapRasterBuffer::DoLockForWrite() { raster_bitmap_.installPixels(info, mapped_buffer_, stride); break; } + case ALPHA_8: case LUMINANCE_8: case RGB_565: case ETC1: diff --git a/cc/resources/resource_provider.h b/cc/resources/resource_provider.h index e44b75e876499..bf1c33bd5aa6c 100644 --- a/cc/resources/resource_provider.h +++ b/cc/resources/resource_provider.h @@ -663,6 +663,7 @@ inline unsigned BitsPerPixel(ResourceFormat format) { 32, // RGBA_8888 16, // RGBA_4444 32, // BGRA_8888 + 8, // ALPHA_8 8, // LUMINANCE_8 16, // RGB_565, 4 // ETC1 @@ -676,6 +677,7 @@ inline GLenum GLDataType(ResourceFormat format) { GL_UNSIGNED_BYTE, // RGBA_8888 GL_UNSIGNED_SHORT_4_4_4_4, // RGBA_4444 GL_UNSIGNED_BYTE, // BGRA_8888 + GL_UNSIGNED_BYTE, // ALPHA_8 GL_UNSIGNED_BYTE, // LUMINANCE_8 GL_UNSIGNED_SHORT_5_6_5, // RGB_565, GL_UNSIGNED_BYTE // ETC1 @@ -689,6 +691,7 @@ inline GLenum GLDataFormat(ResourceFormat format) { GL_RGBA, // RGBA_8888 GL_RGBA, // RGBA_4444 GL_BGRA_EXT, // BGRA_8888 + GL_ALPHA, // ALPHA_8 GL_LUMINANCE, // LUMINANCE_8 GL_RGB, // RGB_565 GL_ETC1_RGB8_OES // ETC1 diff --git a/cc/resources/texture_uploader_unittest.cc b/cc/resources/texture_uploader_unittest.cc index 8390b28ceef66..bf94065d88c9e 100644 --- a/cc/resources/texture_uploader_unittest.cc +++ b/cc/resources/texture_uploader_unittest.cc @@ -217,6 +217,15 @@ TEST(TextureUploaderTest, UploadContentsTest) { } UploadTexture(uploader.get(), RGBA_8888, gfx::Size(41, 43), buffer); + // Upload a tightly packed 41x86 ALPHA texture. + memset(buffer, 0, sizeof(buffer)); + for (int i = 0; i < 86; ++i) { + // Mark the beginning and end of each row, for the test. + buffer[i * 1 * 41] = 0x1; + buffer[(i + 1) * 41 - 1] = 0x2; + } + UploadTexture(uploader.get(), ALPHA_8, gfx::Size(41, 86), buffer); + // Upload a tightly packed 82x86 LUMINANCE texture. memset(buffer, 0, sizeof(buffer)); for (int i = 0; i < 86; ++i) { diff --git a/cc/resources/tile.cc b/cc/resources/tile.cc index 85ff831ca52dc..87e1e46eb2b1c 100644 --- a/cc/resources/tile.cc +++ b/cc/resources/tile.cc @@ -27,7 +27,7 @@ Tile::Tile(TileManager* tile_manager, int flags) : RefCountedManaged(tile_manager), tile_manager_(tile_manager), - tile_size_(tile_size), + size_(tile_size), content_rect_(content_rect), contents_scale_(contents_scale), opaque_rect_(opaque_rect), diff --git a/cc/resources/tile.h b/cc/resources/tile.h index 01a733cc6c9b9..5e1ce5a694af7 100644 --- a/cc/resources/tile.h +++ b/cc/resources/tile.h @@ -131,6 +131,7 @@ class CC_EXPORT Tile : public RefCountedManaged { void set_picture_pile(scoped_refptr pile) { DCHECK(pile->CanRaster(contents_scale_, content_rect_)) + << "Recording rect: " << gfx::ScaleToEnclosingRect(content_rect_, 1.f / contents_scale_) .ToString(); picture_pile_ = pile; @@ -138,7 +139,7 @@ class CC_EXPORT Tile : public RefCountedManaged { size_t GPUMemoryUsageInBytes() const; - gfx::Size size() const { return tile_size_.size(); } + gfx::Size size() const { return size_; } RasterMode DetermineRasterModeForTree(WhichTree tree) const; RasterMode DetermineOverallRasterMode() const; @@ -178,7 +179,7 @@ class CC_EXPORT Tile : public RefCountedManaged { TileManager* tile_manager_; scoped_refptr picture_pile_; - gfx::Rect tile_size_; + gfx::Size size_; gfx::Rect content_rect_; float contents_scale_; gfx::Rect opaque_rect_; diff --git a/cc/resources/tile_manager.cc b/cc/resources/tile_manager.cc index 452d9b45ccfa5..b264b4754696f 100644 --- a/cc/resources/tile_manager.cc +++ b/cc/resources/tile_manager.cc @@ -843,9 +843,7 @@ void TileManager::AssignGpuMemoryToTiles( // Tile is OOM. if (tile_bytes > tile_bytes_left || tile_resources > resources_left) { - bool was_ready_to_draw = tile->IsReadyToDraw(); - - FreeResourcesForTile(tile); + FreeResourcesForTileAndNotifyClientIfTileWasReadyToDraw(tile); // This tile was already on screen and now its resources have been // released. In order to prevent checkerboarding, set this tile as @@ -853,9 +851,6 @@ void TileManager::AssignGpuMemoryToTiles( if (mts.visible_and_ready_to_draw) tile_version.set_rasterize_on_demand(); - if (was_ready_to_draw) - client_->NotifyTileStateChanged(tile); - oomed_soft = true; if (tile_uses_hard_limit) { oomed_hard = true; @@ -1026,7 +1021,7 @@ scoped_refptr TileManager::CreateRasterTask(Tile* tile) { ManagedTileState& mts = tile->managed_state(); scoped_ptr resource = - resource_pool_->AcquireResource(tile->tile_size_.size()); + resource_pool_->AcquireResource(tile->size()); const ScopedResource* const_resource = resource.get(); // Create and queue all image decode tasks that this tile depends on. diff --git a/cc/resources/tile_manager.h b/cc/resources/tile_manager.h index fa5c650090207..eec56291040c0 100644 --- a/cc/resources/tile_manager.h +++ b/cc/resources/tile_manager.h @@ -123,7 +123,8 @@ class CC_EXPORT TileManager : public RasterizerClient, ManagedTileState::TileVersion& tile_version = mts.tile_versions[HIGH_QUALITY_RASTER_MODE]; - tile_version.resource_ = resource_pool_->AcquireResource(gfx::Size(1, 1)); + tile_version.resource_ = + resource_pool_->AcquireResource(tiles[i]->size()); bytes_releasable_ += BytesConsumedIfAllocated(tiles[i]); ++resources_releasable_; diff --git a/cc/resources/tile_manager_unittest.cc b/cc/resources/tile_manager_unittest.cc index 98e63fe011737..030f8ab77ed5c 100644 --- a/cc/resources/tile_manager_unittest.cc +++ b/cc/resources/tile_manager_unittest.cc @@ -898,6 +898,8 @@ TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) { tile_manager()->InitializeTilesWithResourcesForTesting( std::vector(all_tiles.begin(), all_tiles.end())); + pending_layer_->MarkVisibleResourcesAsRequired(); + Tile* last_tile = NULL; smoothness_tiles.clear(); tile_count = 0; @@ -916,8 +918,13 @@ TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) { tile->priority(ACTIVE_TREE).priority_bin); if (last_tile->priority(ACTIVE_TREE).priority_bin == tile->priority(ACTIVE_TREE).priority_bin) { - EXPECT_GE(last_tile->priority(ACTIVE_TREE).distance_to_visible, - tile->priority(ACTIVE_TREE).distance_to_visible); + EXPECT_LE(last_tile->required_for_activation(), + tile->required_for_activation()); + if (last_tile->required_for_activation() == + tile->required_for_activation()) { + EXPECT_GE(last_tile->priority(ACTIVE_TREE).distance_to_visible, + tile->priority(ACTIVE_TREE).distance_to_visible); + } } last_tile = tile; @@ -945,8 +952,13 @@ TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueue) { tile->priority(PENDING_TREE).priority_bin); if (last_tile->priority(PENDING_TREE).priority_bin == tile->priority(PENDING_TREE).priority_bin) { - EXPECT_GE(last_tile->priority(PENDING_TREE).distance_to_visible, - tile->priority(PENDING_TREE).distance_to_visible); + EXPECT_LE(last_tile->required_for_activation(), + tile->required_for_activation()); + if (last_tile->required_for_activation() == + tile->required_for_activation()) { + EXPECT_GE(last_tile->priority(PENDING_TREE).distance_to_visible, + tile->priority(PENDING_TREE).distance_to_visible); + } } last_tile = tile; @@ -1105,5 +1117,102 @@ TEST_F(TileManagerTilePriorityQueueTest, pending_child_high_res_tiles.size() + pending_child_low_res_tiles.size(); EXPECT_EQ(expected_occluded_count, occluded_count); } + +TEST_F(TileManagerTilePriorityQueueTest, RasterTilePriorityQueueEmptyLayers) { + SetupDefaultTrees(gfx::Size(1000, 1000)); + + active_layer_->CreateDefaultTilingsAndTiles(); + pending_layer_->CreateDefaultTilingsAndTiles(); + + RasterTilePriorityQueue queue; + host_impl_.BuildRasterQueue(&queue, SAME_PRIORITY_FOR_BOTH_TREES); + EXPECT_FALSE(queue.IsEmpty()); + + size_t tile_count = 0; + std::set all_tiles; + while (!queue.IsEmpty()) { + EXPECT_TRUE(queue.Top()); + all_tiles.insert(queue.Top()); + ++tile_count; + queue.Pop(); + } + + EXPECT_EQ(tile_count, all_tiles.size()); + EXPECT_EQ(17u, tile_count); + + queue.Reset(); + for (int i = 1; i < 10; ++i) { + scoped_ptr pending_layer = + FakePictureLayerImpl::Create(host_impl_.pending_tree(), id_ + i); + pending_layer->SetDrawsContent(true); + pending_layer->DoPostCommitInitializationIfNeeded(); + pending_layer->set_has_valid_tile_priorities(true); + pending_layer_->AddChild(pending_layer.PassAs()); + } + + host_impl_.BuildRasterQueue(&queue, SAME_PRIORITY_FOR_BOTH_TREES); + EXPECT_FALSE(queue.IsEmpty()); + + tile_count = 0; + all_tiles.clear(); + while (!queue.IsEmpty()) { + EXPECT_TRUE(queue.Top()); + all_tiles.insert(queue.Top()); + ++tile_count; + queue.Pop(); + } + EXPECT_EQ(tile_count, all_tiles.size()); + EXPECT_EQ(17u, tile_count); +} + +TEST_F(TileManagerTilePriorityQueueTest, EvictionTilePriorityQueueEmptyLayers) { + SetupDefaultTrees(gfx::Size(1000, 1000)); + + active_layer_->CreateDefaultTilingsAndTiles(); + pending_layer_->CreateDefaultTilingsAndTiles(); + + RasterTilePriorityQueue raster_queue; + host_impl_.BuildRasterQueue(&raster_queue, SAME_PRIORITY_FOR_BOTH_TREES); + EXPECT_FALSE(raster_queue.IsEmpty()); + + size_t tile_count = 0; + std::set all_tiles; + while (!raster_queue.IsEmpty()) { + EXPECT_TRUE(raster_queue.Top()); + all_tiles.insert(raster_queue.Top()); + ++tile_count; + raster_queue.Pop(); + } + EXPECT_EQ(tile_count, all_tiles.size()); + EXPECT_EQ(17u, tile_count); + + std::vector tiles(all_tiles.begin(), all_tiles.end()); + host_impl_.tile_manager()->InitializeTilesWithResourcesForTesting(tiles); + + EvictionTilePriorityQueue queue; + for (int i = 1; i < 10; ++i) { + scoped_ptr pending_layer = + FakePictureLayerImpl::Create(host_impl_.pending_tree(), id_ + i); + pending_layer->SetDrawsContent(true); + pending_layer->DoPostCommitInitializationIfNeeded(); + pending_layer->set_has_valid_tile_priorities(true); + pending_layer_->AddChild(pending_layer.PassAs()); + } + + host_impl_.BuildEvictionQueue(&queue, SAME_PRIORITY_FOR_BOTH_TREES); + EXPECT_FALSE(queue.IsEmpty()); + + tile_count = 0; + all_tiles.clear(); + while (!queue.IsEmpty()) { + EXPECT_TRUE(queue.Top()); + all_tiles.insert(queue.Top()); + ++tile_count; + queue.Pop(); + } + EXPECT_EQ(tile_count, all_tiles.size()); + EXPECT_EQ(17u, tile_count); +} + } // namespace } // namespace cc diff --git a/cc/resources/ui_resource_bitmap.cc b/cc/resources/ui_resource_bitmap.cc index a7e732c30ca29..e254cf71614e8 100644 --- a/cc/resources/ui_resource_bitmap.cc +++ b/cc/resources/ui_resource_bitmap.cc @@ -11,6 +11,26 @@ #include "third_party/skia/include/core/SkPixelRef.h" namespace cc { +namespace { + +UIResourceBitmap::UIResourceFormat SkColorTypeToUIResourceFormat( + SkColorType sk_type) { + UIResourceBitmap::UIResourceFormat format = UIResourceBitmap::RGBA8; + switch (sk_type) { + case kN32_SkColorType: + format = UIResourceBitmap::RGBA8; + break; + case kAlpha_8_SkColorType: + format = UIResourceBitmap::ALPHA_8; + break; + default: + NOTREACHED() << "Invalid SkColorType for UIResourceBitmap: " << sk_type; + break; + } + return format; +} + +} // namespace void UIResourceBitmap::Create(const skia::RefPtr& pixel_ref, const gfx::Size& size, @@ -29,14 +49,14 @@ void UIResourceBitmap::Create(const skia::RefPtr& pixel_ref, } UIResourceBitmap::UIResourceBitmap(const SkBitmap& skbitmap) { - DCHECK_EQ(skbitmap.colorType(), kN32_SkColorType); DCHECK_EQ(skbitmap.width(), skbitmap.rowBytesAsPixels()); DCHECK(skbitmap.isImmutable()); skia::RefPtr pixel_ref = skia::SharePtr(skbitmap.pixelRef()); const SkImageInfo& info = pixel_ref->info(); - Create( - pixel_ref, gfx::Size(info.fWidth, info.fHeight), UIResourceBitmap::RGBA8); + Create(pixel_ref, + gfx::Size(info.fWidth, info.fHeight), + SkColorTypeToUIResourceFormat(skbitmap.colorType())); SetOpaque(skbitmap.isOpaque()); } diff --git a/cc/resources/ui_resource_bitmap.h b/cc/resources/ui_resource_bitmap.h index b8863d8e994c3..09403a345daf5 100644 --- a/cc/resources/ui_resource_bitmap.h +++ b/cc/resources/ui_resource_bitmap.h @@ -27,6 +27,7 @@ class CC_EXPORT UIResourceBitmap { public: enum UIResourceFormat { RGBA8, + ALPHA_8, ETC1 }; enum UIResourceWrapMode { @@ -42,7 +43,7 @@ class CC_EXPORT UIResourceBitmap { void SetOpaque(bool opaque) { opaque_ = opaque; } // User must ensure that |skbitmap| is immutable. The SkBitmap Format should - // be 32-bit RGBA. + // be 32-bit RGBA or 8-bit ALPHA. explicit UIResourceBitmap(const SkBitmap& skbitmap); UIResourceBitmap(const gfx::Size& size, bool is_opaque); UIResourceBitmap(const skia::RefPtr& pixel_ref, diff --git a/cc/resources/video_resource_updater.cc b/cc/resources/video_resource_updater.cc index a1286b917b4f2..bf72b4597e866 100644 --- a/cc/resources/video_resource_updater.cc +++ b/cc/resources/video_resource_updater.cc @@ -249,10 +249,7 @@ VideoFrameExternalResources VideoResourceUpdater::CreateForSoftwarePlanes( { ResourceProvider::ScopedWriteLockSoftware lock( resource_provider_, plane_resources[0].resource_id); - video_renderer_->Paint(video_frame.get(), - lock.sk_canvas(), - video_frame->visible_rect(), - 0xff); + video_renderer_->Copy(video_frame.get(), lock.sk_canvas()); } RecycleResourceData recycle_data = { diff --git a/cc/scheduler/scheduler.h b/cc/scheduler/scheduler.h index afb9e9d0e8e54..f6dd2bcf4ec36 100644 --- a/cc/scheduler/scheduler.h +++ b/cc/scheduler/scheduler.h @@ -98,6 +98,13 @@ class CC_EXPORT Scheduler { void DidLoseOutputSurface(); void DidCreateAndInitializeOutputSurface(); + // Tests do not want to shut down until all possible BeginMainFrames have + // occured to prevent flakiness. + bool MainFrameForTestingWillHappen() const { + return state_machine_.CommitPending() || + state_machine_.CouldSendBeginMainFrame(); + } + bool CommitPending() const { return state_machine_.CommitPending(); } bool RedrawPending() const { return state_machine_.RedrawPending(); } bool ManageTilesPending() const { diff --git a/cc/scheduler/scheduler_state_machine.cc b/cc/scheduler/scheduler_state_machine.cc index a37a74a1b657f..c66b650d96afa 100644 --- a/cc/scheduler/scheduler_state_machine.cc +++ b/cc/scheduler/scheduler_state_machine.cc @@ -418,10 +418,21 @@ bool SchedulerStateMachine::ShouldAnimate() const { return needs_redraw_ || needs_animate_; } -bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { +bool SchedulerStateMachine::CouldSendBeginMainFrame() const { if (!needs_commit_) return false; + // We can not perform commits if we are not visible. + if (!visible_) + return false; + + return true; +} + +bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { + if (!CouldSendBeginMainFrame()) + return false; + // Only send BeginMainFrame when there isn't another commit pending already. if (commit_state_ != COMMIT_STATE_IDLE) return false; @@ -433,10 +444,6 @@ bool SchedulerStateMachine::ShouldSendBeginMainFrame() const { return false; } - // We do not need commits if we are not visible. - if (!visible_) - return false; - // We want to start the first commit after we get a new output surface ASAP. if (output_surface_state_ == OUTPUT_SURFACE_WAITING_FOR_FIRST_COMMIT) return true; @@ -1006,7 +1013,9 @@ void SchedulerStateMachine::DidDrawIfPossibleCompleted(DrawResult result) { } } -void SchedulerStateMachine::SetNeedsCommit() { needs_commit_ = true; } +void SchedulerStateMachine::SetNeedsCommit() { + needs_commit_ = true; +} void SchedulerStateMachine::NotifyReadyToCommit() { DCHECK(commit_state_ == COMMIT_STATE_BEGIN_MAIN_FRAME_STARTED) diff --git a/cc/scheduler/scheduler_state_machine.h b/cc/scheduler/scheduler_state_machine.h index d92a352f236af..bb033a15709f6 100644 --- a/cc/scheduler/scheduler_state_machine.h +++ b/cc/scheduler/scheduler_state_machine.h @@ -235,6 +235,8 @@ class CC_EXPORT SchedulerStateMachine { continuous_painting_ = continuous_painting; } + bool CouldSendBeginMainFrame() const; + protected: bool BeginFrameNeededToAnimateOrDraw() const; bool ProactiveBeginFrameWanted() const; diff --git a/cc/surfaces/surface_aggregator_test_helpers.cc b/cc/surfaces/surface_aggregator_test_helpers.cc index d843369d2400e..f5b623b47a777 100644 --- a/cc/surfaces/surface_aggregator_test_helpers.cc +++ b/cc/surfaces/surface_aggregator_test_helpers.cc @@ -69,11 +69,10 @@ void AddTestRenderPassQuad(TestRenderPass* pass, output_rect, output_rect, render_pass_id, - false, 0, - output_rect, gfx::RectF(), FilterOperations(), + gfx::Vector2dF(), FilterOperations()); } diff --git a/cc/test/DEPS b/cc/test/DEPS index 611de1b3b22ac..5146b46254025 100644 --- a/cc/test/DEPS +++ b/cc/test/DEPS @@ -3,5 +3,6 @@ include_rules = [ "+gpu/command_buffer/client/gles2_implementation.h", "+gpu/command_buffer/client/gles2_interface_stub.h", "+gpu/command_buffer/client/gles2_lib.h", + "+gpu/command_buffer/common/gles2_cmd_utils.h", "+gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h", ] diff --git a/cc/test/data/blue.png b/cc/test/data/blue.png new file mode 100644 index 0000000000000..48a268024e055 Binary files /dev/null and b/cc/test/data/blue.png differ diff --git a/cc/test/fake_delegated_renderer_layer_impl.h b/cc/test/fake_delegated_renderer_layer_impl.h index 157f5d6a39e6b..f909d24144a2e 100644 --- a/cc/test/fake_delegated_renderer_layer_impl.h +++ b/cc/test/fake_delegated_renderer_layer_impl.h @@ -28,7 +28,6 @@ class FakeDelegatedRendererLayerImpl : public DelegatedRendererLayerImpl { void SetFrameDataForRenderPasses(float device_scale_factor, RenderPassList* pass_list); - protected: FakeDelegatedRendererLayerImpl(LayerTreeImpl* tree_impl, int id); }; diff --git a/cc/test/fake_layer_tree_host_impl.cc b/cc/test/fake_layer_tree_host_impl.cc index 84f75e0016a60..0505bbd9b9e79 100644 --- a/cc/test/fake_layer_tree_host_impl.cc +++ b/cc/test/fake_layer_tree_host_impl.cc @@ -62,4 +62,26 @@ void FakeLayerTreeHostImpl::SetCurrentFrameTimeTicks( current_frame_time_ticks_ = current_frame_time_ticks; } +int FakeLayerTreeHostImpl::RecursiveUpdateNumChildren(LayerImpl* layer) { + int num_children_that_draw_content = 0; + for (size_t i = 0; i < layer->children().size(); ++i) { + num_children_that_draw_content += + RecursiveUpdateNumChildren(layer->children()[i]); + } + if (layer->DrawsContent() && layer->HasDelegatedContent()) + num_children_that_draw_content += 1000; + layer->SetNumDescendantsThatDrawContent(num_children_that_draw_content); + return num_children_that_draw_content + (layer->DrawsContent() ? 1 : 0); +} + +void FakeLayerTreeHostImpl::UpdateNumChildrenAndDrawPropertiesForActiveTree() { + UpdateNumChildrenAndDrawProperties(active_tree()); +} + +void FakeLayerTreeHostImpl::UpdateNumChildrenAndDrawProperties( + LayerTreeImpl* layerTree) { + RecursiveUpdateNumChildren(layerTree->root_layer()); + layerTree->UpdateDrawProperties(); +} + } // namespace cc diff --git a/cc/test/fake_layer_tree_host_impl.h b/cc/test/fake_layer_tree_host_impl.h index 38957a8bbd11d..0d0014cb6b4e3 100644 --- a/cc/test/fake_layer_tree_host_impl.h +++ b/cc/test/fake_layer_tree_host_impl.h @@ -30,6 +30,9 @@ class FakeLayerTreeHostImpl : public LayerTreeHostImpl { virtual base::TimeTicks CurrentFrameTimeTicks() OVERRIDE; void SetCurrentFrameTimeTicks(base::TimeTicks current_frame_time_ticks); + void UpdateNumChildrenAndDrawPropertiesForActiveTree(); + static void UpdateNumChildrenAndDrawProperties(LayerTreeImpl* layerTree); + static int RecursiveUpdateNumChildren(LayerImpl* layer); using LayerTreeHostImpl::ActivateSyncTree; using LayerTreeHostImpl::manage_tiles_needed; diff --git a/cc/test/fake_picture_layer_impl.cc b/cc/test/fake_picture_layer_impl.cc index d98a408b075d4..78924cdaa93b7 100644 --- a/cc/test/fake_picture_layer_impl.cc +++ b/cc/test/fake_picture_layer_impl.cc @@ -15,7 +15,9 @@ FakePictureLayerImpl::FakePictureLayerImpl(LayerTreeImpl* tree_impl, scoped_refptr pile) : PictureLayerImpl(tree_impl, id), append_quads_count_(0), - did_become_active_call_count_(0) { + did_become_active_call_count_(0), + has_valid_tile_priorities_(false), + use_set_valid_tile_priorities_flag_(false) { pile_ = pile; SetBounds(pile_->tiling_size()); SetContentBounds(pile_->tiling_size()); @@ -27,7 +29,9 @@ FakePictureLayerImpl::FakePictureLayerImpl(LayerTreeImpl* tree_impl, const gfx::Size& layer_bounds) : PictureLayerImpl(tree_impl, id), append_quads_count_(0), - did_become_active_call_count_(0) { + did_become_active_call_count_(0), + has_valid_tile_priorities_(false), + use_set_valid_tile_priorities_flag_(false) { pile_ = pile; SetBounds(layer_bounds); SetContentBounds(layer_bounds); @@ -36,7 +40,9 @@ FakePictureLayerImpl::FakePictureLayerImpl(LayerTreeImpl* tree_impl, FakePictureLayerImpl::FakePictureLayerImpl(LayerTreeImpl* tree_impl, int id) : PictureLayerImpl(tree_impl, id), append_quads_count_(0), - did_become_active_call_count_(0) { + did_become_active_call_count_(0), + has_valid_tile_priorities_(false), + use_set_valid_tile_priorities_flag_(false) { } scoped_ptr FakePictureLayerImpl::CreateLayerImpl( @@ -163,4 +169,10 @@ void FakePictureLayerImpl::DidBecomeActive() { ++did_become_active_call_count_; } +bool FakePictureLayerImpl::HasValidTilePriorities() const { + return use_set_valid_tile_priorities_flag_ + ? has_valid_tile_priorities_ + : PictureLayerImpl::HasValidTilePriorities(); +} + } // namespace cc diff --git a/cc/test/fake_picture_layer_impl.h b/cc/test/fake_picture_layer_impl.h index ab0c023654ff4..7b832268a4796 100644 --- a/cc/test/fake_picture_layer_impl.h +++ b/cc/test/fake_picture_layer_impl.h @@ -46,13 +46,21 @@ class FakePictureLayerImpl : public PictureLayerImpl { return did_become_active_call_count_; } + virtual bool HasValidTilePriorities() const OVERRIDE; + void set_has_valid_tile_priorities(bool has_valid_priorities) { + has_valid_tile_priorities_ = has_valid_priorities; + use_set_valid_tile_priorities_flag_ = true; + } + using PictureLayerImpl::AddTiling; using PictureLayerImpl::CleanUpTilingsOnActiveLayer; using PictureLayerImpl::CanHaveTilings; using PictureLayerImpl::MarkVisibleResourcesAsRequired; using PictureLayerImpl::DoPostCommitInitializationIfNeeded; using PictureLayerImpl::MinimumContentsScale; + using PictureLayerImpl::GetViewportForTilePriorityInContentSpace; using PictureLayerImpl::SanityCheckTilingState; + using PictureLayerImpl::GetRecycledTwinLayer; using PictureLayerImpl::UpdateIdealScales; using PictureLayerImpl::MaximumTilingContentsScale; @@ -116,6 +124,8 @@ class FakePictureLayerImpl : public PictureLayerImpl { size_t append_quads_count_; size_t did_become_active_call_count_; + bool has_valid_tile_priorities_; + bool use_set_valid_tile_priorities_flag_; }; } // namespace cc diff --git a/cc/test/fake_picture_layer_tiling_client.cc b/cc/test/fake_picture_layer_tiling_client.cc index 2bd756dfa43b9..0503d22ad6534 100644 --- a/cc/test/fake_picture_layer_tiling_client.cc +++ b/cc/test/fake_picture_layer_tiling_client.cc @@ -15,10 +15,12 @@ FakePictureLayerTilingClient::FakePictureLayerTilingClient() : tile_manager_(new FakeTileManager(&tile_manager_client_)), pile_(FakePicturePileImpl::CreateInfiniteFilledPile()), twin_tiling_(NULL), + recycled_twin_tiling_(NULL), allow_create_tile_(true), max_tiles_for_interest_area_(10000), skewport_target_time_in_seconds_(1.0f), - skewport_extrapolation_limit_in_content_pixels_(2000) {} + skewport_extrapolation_limit_in_content_pixels_(2000) { +} FakePictureLayerTilingClient::FakePictureLayerTilingClient( ResourceProvider* resource_provider) @@ -28,9 +30,11 @@ FakePictureLayerTilingClient::FakePictureLayerTilingClient( new FakeTileManager(&tile_manager_client_, resource_pool_.get())), pile_(FakePicturePileImpl::CreateInfiniteFilledPile()), twin_tiling_(NULL), + recycled_twin_tiling_(NULL), allow_create_tile_(true), max_tiles_for_interest_area_(10000), - skewport_target_time_in_seconds_(1.0f) {} + skewport_target_time_in_seconds_(1.0f) { +} FakePictureLayerTilingClient::~FakePictureLayerTilingClient() { } @@ -79,6 +83,11 @@ const PictureLayerTiling* FakePictureLayerTilingClient::GetTwinTiling( return twin_tiling_; } +PictureLayerTiling* FakePictureLayerTilingClient::GetRecycledTwinTiling( + const PictureLayerTiling* tiling) { + return recycled_twin_tiling_; +} + WhichTree FakePictureLayerTilingClient::GetTree() const { return tree_; } diff --git a/cc/test/fake_picture_layer_tiling_client.h b/cc/test/fake_picture_layer_tiling_client.h index f46cc84421212..edd2a61f7c6e7 100644 --- a/cc/test/fake_picture_layer_tiling_client.h +++ b/cc/test/fake_picture_layer_tiling_client.h @@ -36,9 +36,14 @@ class FakePictureLayerTilingClient : public PictureLayerTilingClient { virtual const Region* GetInvalidation() OVERRIDE; virtual const PictureLayerTiling* GetTwinTiling( const PictureLayerTiling* tiling) const OVERRIDE; + virtual PictureLayerTiling* GetRecycledTwinTiling( + const PictureLayerTiling* tiling) OVERRIDE; virtual WhichTree GetTree() const OVERRIDE; void set_twin_tiling(PictureLayerTiling* tiling) { twin_tiling_ = tiling; } + void set_recycled_twin_tiling(PictureLayerTiling* tiling) { + recycled_twin_tiling_ = tiling; + } void set_text_rect(const gfx::Rect& rect) { text_rect_ = rect; } void set_allow_create_tile(bool allow) { allow_create_tile_ = allow; } void set_invalidation(const Region& region) { invalidation_ = region; } @@ -64,6 +69,7 @@ class FakePictureLayerTilingClient : public PictureLayerTilingClient { scoped_refptr pile_; gfx::Size tile_size_; PictureLayerTiling* twin_tiling_; + PictureLayerTiling* recycled_twin_tiling_; gfx::Rect text_rect_; bool allow_create_tile_; Region invalidation_; diff --git a/cc/test/fake_proxy.cc b/cc/test/fake_proxy.cc index 7d0ad47955792..4490c7607ce68 100644 --- a/cc/test/fake_proxy.cc +++ b/cc/test/fake_proxy.cc @@ -34,7 +34,9 @@ void FakeProxy::SetMaxPartialTextureUpdates(size_t max) { bool FakeProxy::SupportsImplScrolling() const { return false; } -bool FakeProxy::CommitPendingForTesting() { return false; } +bool FakeProxy::MainFrameWillHappenForTesting() { + return false; +} void FakeProxy::AsValueInto(base::debug::TracedValue*) const { } diff --git a/cc/test/fake_proxy.h b/cc/test/fake_proxy.h index dc04c701894b5..27cd72cab558e 100644 --- a/cc/test/fake_proxy.h +++ b/cc/test/fake_proxy.h @@ -42,7 +42,7 @@ class FakeProxy : public Proxy { virtual size_t MaxPartialTextureUpdates() const OVERRIDE; virtual bool SupportsImplScrolling() const OVERRIDE; virtual void SetDebugState(const LayerTreeDebugState& debug_state) OVERRIDE {} - virtual bool CommitPendingForTesting() OVERRIDE; + virtual bool MainFrameWillHappenForTesting() OVERRIDE; virtual void AsValueInto(base::debug::TracedValue* state) const OVERRIDE; virtual RendererCapabilities& GetRendererCapabilities(); diff --git a/cc/test/layer_tree_test.cc b/cc/test/layer_tree_test.cc index d72860d81cef3..0be1ed1167b32 100644 --- a/cc/test/layer_tree_test.cc +++ b/cc/test/layer_tree_test.cc @@ -568,7 +568,8 @@ void LayerTreeTest::ScheduleComposite() { } void LayerTreeTest::RealEndTest() { - if (layer_tree_host_ && proxy()->CommitPendingForTesting()) { + if (layer_tree_host_ && !timed_out_ && + proxy()->MainFrameWillHappenForTesting()) { main_task_runner_->PostTask( FROM_HERE, base::Bind(&LayerTreeTest::RealEndTest, main_thread_weak_ptr_)); diff --git a/cc/test/render_pass_test_common.cc b/cc/test/render_pass_test_common.cc index 98d085c0874a0..8e81320fb795b 100644 --- a/cc/test/render_pass_test_common.cc +++ b/cc/test/render_pass_test_common.cc @@ -104,11 +104,10 @@ void TestRenderPass::AppendOneOfEveryQuadType( rect, visible_rect, child_pass, - false, resource5, - rect, gfx::RectF(), FilterOperations(), + gfx::Vector2dF(), FilterOperations()); RenderPassDrawQuad* render_pass_replica_quad = @@ -117,11 +116,10 @@ void TestRenderPass::AppendOneOfEveryQuadType( rect, visible_rect, child_pass, - true, resource5, - rect, gfx::RectF(), FilterOperations(), + gfx::Vector2dF(), FilterOperations()); } diff --git a/cc/test/render_pass_test_utils.cc b/cc/test/render_pass_test_utils.cc index 5925a2b81c970..95eb4ba049b19 100644 --- a/cc/test/render_pass_test_utils.cc +++ b/cc/test/render_pass_test_utils.cc @@ -99,11 +99,10 @@ void AddRenderPassQuad(TestRenderPass* to_pass, output_rect, output_rect, contributing_pass->id, - false, 0, - output_rect, gfx::RectF(), FilterOperations(), + gfx::Vector2dF(), FilterOperations()); } @@ -128,11 +127,10 @@ void AddRenderPassQuad(TestRenderPass* to_pass, output_rect, output_rect, contributing_pass->id, - false, mask_resource_id, - output_rect, gfx::RectF(), filters, + gfx::Vector2dF(), FilterOperations()); } diff --git a/cc/test/test_in_process_context_provider.cc b/cc/test/test_in_process_context_provider.cc index 55e3239590319..b4d580f9e0c0d 100644 --- a/cc/test/test_in_process_context_provider.cc +++ b/cc/test/test_in_process_context_provider.cc @@ -9,6 +9,7 @@ #include "gpu/command_buffer/client/gl_in_process_context.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/client/gles2_lib.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/skia_bindings/gl_bindings_skia_cmd_buffer.h" #include "third_party/khronos/GLES2/gl2.h" #include "third_party/khronos/GLES2/gl2ext.h" @@ -22,7 +23,7 @@ namespace cc { scoped_ptr CreateTestInProcessContext() { const bool is_offscreen = true; const bool share_resources = true; - gpu::GLInProcessContextAttribs attribs; + gpu::gles2::ContextCreationAttribHelper attribs; attribs.alpha_size = 8; attribs.blue_size = 8; attribs.green_size = 8; @@ -32,18 +33,21 @@ scoped_ptr CreateTestInProcessContext() { attribs.samples = 0; attribs.sample_buffers = 0; attribs.fail_if_major_perf_caveat = false; + attribs.bind_generates_resource = false; gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; - scoped_ptr context = make_scoped_ptr( - gpu::GLInProcessContext::Create(NULL, - NULL, - is_offscreen, - gfx::kNullAcceleratedWidget, - gfx::Size(1, 1), - NULL, - share_resources, - attribs, - gpu_preference)); + scoped_ptr context = + make_scoped_ptr(gpu::GLInProcessContext::Create( + NULL, + NULL, + is_offscreen, + gfx::kNullAcceleratedWidget, + gfx::Size(1, 1), + NULL, + share_resources, + attribs, + gpu_preference, + gpu::GLInProcessContextSharedMemoryLimits())); DCHECK(context); return context.Pass(); diff --git a/cc/test/tiled_layer_test_common.cc b/cc/test/tiled_layer_test_common.cc index cd4f9bde86ee1..81975ca12374e 100644 --- a/cc/test/tiled_layer_test_common.cc +++ b/cc/test/tiled_layer_test_common.cc @@ -114,10 +114,6 @@ PrioritizedResourceManager* FakeTiledLayer::ResourceManager() { void FakeTiledLayer::UpdateContentsScale(float ideal_contents_scale) { CalculateContentsScale(ideal_contents_scale, - 1.f, - 1.f, - 1.f, - false, // animating_transform_to_screen &draw_properties().contents_scale_x, &draw_properties().contents_scale_y, &draw_properties().content_bounds); @@ -155,10 +151,6 @@ void FakeTiledLayerWithScaledBounds::SetContentBounds( void FakeTiledLayerWithScaledBounds::CalculateContentsScale( float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) { diff --git a/cc/test/tiled_layer_test_common.h b/cc/test/tiled_layer_test_common.h index d407f6baa012e..f7e01584404ca 100644 --- a/cc/test/tiled_layer_test_common.h +++ b/cc/test/tiled_layer_test_common.h @@ -140,10 +140,6 @@ class FakeTiledLayerWithScaledBounds : public FakeTiledLayer { void SetContentBounds(const gfx::Size& content_bounds); virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE; diff --git a/cc/trees/damage_tracker_unittest.cc b/cc/trees/damage_tracker_unittest.cc index 169eea4cd5f59..b6a44094dc69c 100644 --- a/cc/trees/damage_tracker_unittest.cc +++ b/cc/trees/damage_tracker_unittest.cc @@ -30,6 +30,7 @@ void ExecuteCalculateDrawProperties(LayerImpl* root, ASSERT_TRUE(root->render_surface()); ASSERT_FALSE(render_surface_layer_list->size()); + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren(root); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), render_surface_layer_list); LayerTreeHostCommon::CalculateDrawProperties(&inputs); diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc index ed7472d638ba8..9eb6e0e3140ba 100644 --- a/cc/trees/layer_tree_host.cc +++ b/cc/trees/layer_tree_host.cc @@ -161,6 +161,8 @@ void LayerTreeHost::InitializeProxy(scoped_ptr proxy) { LayerTreeHost::~LayerTreeHost() { TRACE_EVENT0("cc", "LayerTreeHost::~LayerTreeHost"); + CHECK(swap_promise_monitor_.empty()); + BreakSwapPromises(SwapPromise::COMMIT_FAILS); overhang_ui_resource_.reset(); @@ -1032,31 +1034,40 @@ void LayerTreeHost::PaintLayerContents( in_paint_layer_contents_ = false; } -void LayerTreeHost::ApplyScrollAndScale(const ScrollAndScaleSet& info) { +void LayerTreeHost::ApplyScrollAndScale(ScrollAndScaleSet* info) { if (!root_layer_.get()) return; + ScopedPtrVector::iterator it = info->swap_promises.begin(); + for (; it != info->swap_promises.end(); ++it) { + scoped_ptr swap_promise(info->swap_promises.take(it)); + TRACE_EVENT_FLOW_STEP0("input", + "LatencyInfo.Flow", + TRACE_ID_DONT_MANGLE(swap_promise->TraceId()), + "Main thread scroll update"); + QueueSwapPromise(swap_promise.Pass()); + } + gfx::Vector2d inner_viewport_scroll_delta; gfx::Vector2d outer_viewport_scroll_delta; - for (size_t i = 0; i < info.scrolls.size(); ++i) { - Layer* layer = - LayerTreeHostCommon::FindLayerInSubtree(root_layer_.get(), - info.scrolls[i].layer_id); + for (size_t i = 0; i < info->scrolls.size(); ++i) { + Layer* layer = LayerTreeHostCommon::FindLayerInSubtree( + root_layer_.get(), info->scrolls[i].layer_id); if (!layer) continue; if (layer == outer_viewport_scroll_layer_.get()) { - outer_viewport_scroll_delta += info.scrolls[i].scroll_delta; + outer_viewport_scroll_delta += info->scrolls[i].scroll_delta; } else if (layer == inner_viewport_scroll_layer_.get()) { - inner_viewport_scroll_delta += info.scrolls[i].scroll_delta; + inner_viewport_scroll_delta += info->scrolls[i].scroll_delta; } else { layer->SetScrollOffsetFromImplSide(layer->scroll_offset() + - info.scrolls[i].scroll_delta); + info->scrolls[i].scroll_delta); } } if (!inner_viewport_scroll_delta.IsZero() || - !outer_viewport_scroll_delta.IsZero() || info.page_scale_delta != 1.f) { + !outer_viewport_scroll_delta.IsZero() || info->page_scale_delta != 1.f) { // SetScrollOffsetFromImplSide above could have destroyed the tree, // so re-get this layer before doing anything to it. @@ -1073,11 +1084,11 @@ void LayerTreeHost::ApplyScrollAndScale(const ScrollAndScaleSet& info) { outer_viewport_scroll_layer_->scroll_offset() + outer_viewport_scroll_delta); } - ApplyPageScaleDeltaFromImplSide(info.page_scale_delta); + ApplyPageScaleDeltaFromImplSide(info->page_scale_delta); client_->ApplyScrollAndScale( inner_viewport_scroll_delta + outer_viewport_scroll_delta, - info.page_scale_delta); + info->page_scale_delta); } } diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h index 2fa5b5880831b..0ae1c5ae44cf4 100644 --- a/cc/trees/layer_tree_host.h +++ b/cc/trees/layer_tree_host.h @@ -228,7 +228,7 @@ class CC_EXPORT LayerTreeHost { float scale, base::TimeDelta duration); - void ApplyScrollAndScale(const ScrollAndScaleSet& info); + void ApplyScrollAndScale(ScrollAndScaleSet* info); void SetImplTransform(const gfx::Transform& transform); // Virtual for tests. diff --git a/cc/trees/layer_tree_host_common.cc b/cc/trees/layer_tree_host_common.cc index 3bbbf6340b623..840a32b2ef4a5 100644 --- a/cc/trees/layer_tree_host_common.cc +++ b/cc/trees/layer_tree_host_common.cc @@ -583,7 +583,7 @@ static bool SubtreeShouldRenderToSeparateSurface( } int num_descendants_that_draw_content = - layer->draw_properties().num_descendants_that_draw_content; + layer->NumDescendantsThatDrawContent(); // If the layer flattens its subtree, but it is treated as a 3D object by its // parent (i.e. parent participates in a 3D rendering context). @@ -932,29 +932,14 @@ static inline void UpdateLayerScaleDrawProperties( layer->draw_properties().device_scale_factor = device_scale_factor; } -static inline void CalculateContentsScale( - LayerImpl* layer, - float contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen) { +static inline void CalculateContentsScale(LayerImpl* layer, + float contents_scale) { // LayerImpl has all of its content scales and bounds pushed from the Main // thread during commit and just uses those values as-is. } -static inline void CalculateContentsScale( - Layer* layer, - float contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen) { +static inline void CalculateContentsScale(Layer* layer, float contents_scale) { layer->CalculateContentsScale(contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, &layer->draw_properties().contents_scale_x, &layer->draw_properties().contents_scale_y, &layer->draw_properties().content_bounds); @@ -963,10 +948,6 @@ static inline void CalculateContentsScale( if (mask_layer) { mask_layer->CalculateContentsScale( contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, &mask_layer->draw_properties().contents_scale_x, &mask_layer->draw_properties().contents_scale_y, &mask_layer->draw_properties().content_bounds); @@ -977,10 +958,6 @@ static inline void CalculateContentsScale( if (replica_mask_layer) { replica_mask_layer->CalculateContentsScale( contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, &replica_mask_layer->draw_properties().contents_scale_x, &replica_mask_layer->draw_properties().contents_scale_y, &replica_mask_layer->draw_properties().content_bounds); @@ -993,14 +970,8 @@ static inline void UpdateLayerContentsScale( float ideal_contents_scale, float device_scale_factor, float page_scale_factor, - float maximum_animation_contents_scale, bool animating_transform_to_screen) { - CalculateContentsScale(layer, - ideal_contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen); + CalculateContentsScale(layer, ideal_contents_scale); } static inline void UpdateLayerContentsScale( @@ -1009,7 +980,6 @@ static inline void UpdateLayerContentsScale( float ideal_contents_scale, float device_scale_factor, float page_scale_factor, - float maximum_animation_contents_scale, bool animating_transform_to_screen) { if (can_adjust_raster_scale) { float ideal_raster_scale = @@ -1042,12 +1012,7 @@ static inline void UpdateLayerContentsScale( float old_contents_scale_y = layer->contents_scale_y(); float contents_scale = raster_scale * device_scale_factor * page_scale_factor; - CalculateContentsScale(layer, - contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen); + CalculateContentsScale(layer, contents_scale); if (layer->content_bounds() != old_content_bounds || layer->contents_scale_x() != old_contents_scale_x || @@ -1242,8 +1207,6 @@ template static void PreCalculateMetaInformation( LayerType* layer, PreCalculateMetaInformationRecursiveData* recursive_data) { - bool has_delegated_content = layer->HasDelegatedContent(); - int num_descendants_that_draw_content = 0; layer->draw_properties().sorted_for_recursion = false; layer->draw_properties().has_child_with_a_scroll_parent = false; @@ -1254,14 +1217,6 @@ static void PreCalculateMetaInformation( return; } - if (has_delegated_content) { - // Layers with delegated content need to be treated as if they have as - // many children as the number of layers they own delegated quads for. - // Since we don't know this number right now, we choose one that acts like - // infinity for our purposes. - num_descendants_that_draw_content = 1000; - } - if (layer->clip_parent()) recursive_data->num_unclipped_descendants++; @@ -1272,10 +1227,6 @@ static void PreCalculateMetaInformation( PreCalculateMetaInformationRecursiveData data_for_child; PreCalculateMetaInformation(child_layer, &data_for_child); - num_descendants_that_draw_content += child_layer->DrawsContent() ? 1 : 0; - num_descendants_that_draw_content += - child_layer->draw_properties().num_descendants_that_draw_content; - if (child_layer->scroll_parent()) layer->draw_properties().has_child_with_a_scroll_parent = true; recursive_data->Merge(data_for_child); @@ -1294,8 +1245,6 @@ static void PreCalculateMetaInformation( layer->have_wheel_event_handlers()) recursive_data->layer_or_descendant_has_input_handler = true; - layer->draw_properties().num_descendants_that_draw_content = - num_descendants_that_draw_content; layer->draw_properties().num_unclipped_descendants = recursive_data->num_unclipped_descendants; layer->draw_properties().layer_or_descendant_has_copy_request = @@ -1781,7 +1730,6 @@ static void CalculateDrawPropertiesInternal( data_from_ancestor.in_subtree_of_page_scale_application_layer ? globals.page_scale_factor : 1.f, - combined_maximum_animation_contents_scale, animating_transform_to_screen); UpdateLayerScaleDrawProperties( diff --git a/cc/trees/layer_tree_host_common.h b/cc/trees/layer_tree_host_common.h index 0e139471739b5..c86c4aa2c788a 100644 --- a/cc/trees/layer_tree_host_common.h +++ b/cc/trees/layer_tree_host_common.h @@ -21,6 +21,7 @@ namespace cc { class LayerImpl; class Layer; +class SwapPromise; class CC_EXPORT LayerTreeHostCommon { public: @@ -139,6 +140,7 @@ struct CC_EXPORT ScrollAndScaleSet { std::vector scrolls; float page_scale_delta; + ScopedPtrVector swap_promises; }; template diff --git a/cc/trees/layer_tree_host_common_unittest.cc b/cc/trees/layer_tree_host_common_unittest.cc index 8dcb6d7ad1ee4..66b63a560d2df 100644 --- a/cc/trees/layer_tree_host_common_unittest.cc +++ b/cc/trees/layer_tree_host_common_unittest.cc @@ -38,49 +38,16 @@ namespace { class LayerWithForcedDrawsContent : public Layer { public: - LayerWithForcedDrawsContent() : Layer(), last_device_scale_factor_(0.f) {} + LayerWithForcedDrawsContent() {} virtual bool DrawsContent() const OVERRIDE; - virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, - float* contents_scale_x, - float* contents_scale_y, - gfx::Size* content_bounds) OVERRIDE; - - float last_device_scale_factor() const { return last_device_scale_factor_; } private: virtual ~LayerWithForcedDrawsContent() {} - - // Parameters from last CalculateContentsScale. - float last_device_scale_factor_; }; bool LayerWithForcedDrawsContent::DrawsContent() const { return true; } -void LayerWithForcedDrawsContent::CalculateContentsScale( - float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, - float* contents_scale_x, - float* contents_scale_y, - gfx::Size* content_bounds) { - last_device_scale_factor_ = device_scale_factor; - Layer::CalculateContentsScale(ideal_contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, - contents_scale_x, - contents_scale_y, - content_bounds); -} - class MockContentLayerClient : public ContentLayerClient { public: MockContentLayerClient() {} @@ -1198,8 +1165,8 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_EQ(translate, root->draw_properties().target_space_transform); EXPECT_EQ(translate, child->draw_properties().target_space_transform); EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform()); - EXPECT_EQ(1.f, root->last_device_scale_factor()); - EXPECT_EQ(1.f, child->last_device_scale_factor()); + EXPECT_EQ(1.f, root->draw_properties().device_scale_factor); + EXPECT_EQ(1.f, child->draw_properties().device_scale_factor); } gfx::Transform scale; @@ -1213,8 +1180,8 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_EQ(scale, root->draw_properties().target_space_transform); EXPECT_EQ(scale, child->draw_properties().target_space_transform); EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform()); - EXPECT_EQ(2.f, root->last_device_scale_factor()); - EXPECT_EQ(2.f, child->last_device_scale_factor()); + EXPECT_EQ(2.f, root->draw_properties().device_scale_factor); + EXPECT_EQ(2.f, child->draw_properties().device_scale_factor); } gfx::Transform rotate; @@ -1228,8 +1195,8 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_EQ(rotate, root->draw_properties().target_space_transform); EXPECT_EQ(rotate, child->draw_properties().target_space_transform); EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform()); - EXPECT_EQ(1.f, root->last_device_scale_factor()); - EXPECT_EQ(1.f, child->last_device_scale_factor()); + EXPECT_EQ(1.f, root->draw_properties().device_scale_factor); + EXPECT_EQ(1.f, child->draw_properties().device_scale_factor); } gfx::Transform composite; @@ -1264,8 +1231,9 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_EQ(device_scaled_translate, child->draw_properties().target_space_transform); EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform()); - EXPECT_EQ(device_scale_factor, root->last_device_scale_factor()); - EXPECT_EQ(device_scale_factor, child->last_device_scale_factor()); + EXPECT_EQ(device_scale_factor, root->draw_properties().device_scale_factor); + EXPECT_EQ(device_scale_factor, + child->draw_properties().device_scale_factor); } // Verify it composes correctly with page scale. @@ -1285,8 +1253,8 @@ TEST_F(LayerTreeHostCommonTest, TransformAboveRootLayer) { EXPECT_EQ(page_scaled_translate, child->draw_properties().target_space_transform); EXPECT_EQ(identity_matrix, root->render_surface()->draw_transform()); - EXPECT_EQ(1.f, root->last_device_scale_factor()); - EXPECT_EQ(1.f, child->last_device_scale_factor()); + EXPECT_EQ(1.f, root->draw_properties().device_scale_factor); + EXPECT_EQ(1.f, child->draw_properties().device_scale_factor); } // Verify that it composes correctly with transforms directly on root layer. @@ -4049,19 +4017,11 @@ class NoScaleContentLayer : public ContentLayer { } virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* content_bounds) OVERRIDE { // Skip over the ContentLayer to the base Layer class. Layer::CalculateContentsScale(ideal_contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, contents_scale_x, contents_scale_y, content_bounds); @@ -6985,6 +6945,7 @@ TEST_F(LayerTreeHostCommonTest, CanRenderToSeparateSurface) { { LayerImplList render_surface_layer_list; + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren(root.get()); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root.get(), root->bounds(), &render_surface_layer_list); inputs.can_render_to_separate_surface = true; diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc index e155be69517bd..dd01600343837 100644 --- a/cc/trees/layer_tree_host_impl.cc +++ b/cc/trees/layer_tree_host_impl.cc @@ -257,9 +257,7 @@ LayerTreeHostImpl::LayerTreeHostImpl( rendering_stats_instrumentation_(rendering_stats_instrumentation), micro_benchmark_controller_(this), need_to_update_visible_tiles_before_draw_(false), -#if DCHECK_IS_ON - did_lose_called_(false), -#endif + have_valid_output_surface_(false), shared_bitmap_manager_(manager), id_(id), transfer_buffer_memory_limit_(0u) { @@ -505,6 +503,11 @@ LayerTreeHostImpl::CreateLatencyInfoSwapPromiseMonitor( new LatencyInfoSwapPromiseMonitor(latency, NULL, this)); } +void LayerTreeHostImpl::QueueSwapPromiseForMainThreadScrollUpdate( + scoped_ptr swap_promise) { + swap_promises_for_main_thread_scroll_update_.push_back(swap_promise.Pass()); +} + void LayerTreeHostImpl::TrackDamageForAllSurfaces( LayerImpl* root_draw_layer, const LayerImplList& render_surface_layer_list) { @@ -1165,6 +1168,12 @@ void LayerTreeHostImpl::ResetTreesForTesting() { recycle_tree_.reset(); } +void LayerTreeHostImpl::ResetRecycleTreeForTesting() { + if (recycle_tree_) + recycle_tree_->DetachLayerTree(); + recycle_tree_.reset(); +} + void LayerTreeHostImpl::EnforceManagedMemoryPolicy( const ManagedMemoryPolicy& policy) { @@ -1615,7 +1624,9 @@ void LayerTreeHostImpl::FinishAllRendering() { bool LayerTreeHostImpl::IsContextLost() { DCHECK(proxy_->IsImplThread()); - return renderer_ && renderer_->IsContextLost(); + // To avoid races, rely only on the lost-surface callback. + // See crbug.com/392891. + return !have_valid_output_surface_; } void LayerTreeHostImpl::SetUseGpuRasterization(bool use_gpu) { @@ -1720,6 +1731,9 @@ float LayerTreeHostImpl::VerticalAdjust() const { } void LayerTreeHostImpl::DidLoseOutputSurface() { + if (!have_valid_output_surface_) + return; + have_valid_output_surface_ = false; if (resource_provider_) resource_provider_->DidLoseOutputSurface(); // TODO(jamesr): The renderer_ check is needed to make some of the @@ -1727,9 +1741,6 @@ void LayerTreeHostImpl::DidLoseOutputSurface() { // important) in production. We should adjust the test to not need this. if (renderer_) client_->DidLoseOutputSurfaceOnImplThread(); -#if DCHECK_IS_ON - did_lose_called_ = true; -#endif } bool LayerTreeHostImpl::HaveRootScrollLayer() const { @@ -2077,9 +2088,6 @@ void LayerTreeHostImpl::EnforceZeroBudget(bool zero_budget) { bool LayerTreeHostImpl::InitializeRenderer( scoped_ptr output_surface) { TRACE_EVENT0("cc", "LayerTreeHostImpl::InitializeRenderer"); -#if DCHECK_IS_ON - DCHECK(!renderer_ || did_lose_called_); -#endif // Since we will create a new resource provider, we cannot continue to use // the old resources (i.e. render_surfaces and texture IDs). Clear them @@ -2096,6 +2104,7 @@ bool LayerTreeHostImpl::InitializeRenderer( return false; output_surface_ = output_surface.Pass(); + have_valid_output_surface_ = true; resource_provider_ = ResourceProvider::Create(output_surface_.get(), shared_bitmap_manager_, @@ -2650,6 +2659,11 @@ bool LayerTreeHostImpl::ScrollBy(const gfx::Point& viewport_point, bool did_scroll_content = did_scroll_x || did_scroll_y; if (did_scroll_content) { + // If we are scrolling with an active scroll handler, forward latency + // tracking information to the main thread so the delay introduced by the + // handler is accounted for. + if (scroll_affects_scroll_handler()) + NotifySwapPromiseMonitorsOfForwardingToMainThread(); client_->SetNeedsCommitOnImplThread(); SetNeedsRedraw(); client_->RenewTreePriority(); @@ -2664,8 +2678,8 @@ bool LayerTreeHostImpl::ScrollBy(const gfx::Point& viewport_point, accumulated_root_overscroll_ += unused_root_delta; bool did_overscroll = !unused_root_delta.IsZero(); if (did_overscroll && input_handler_client_) { - input_handler_client_->DidOverscroll(accumulated_root_overscroll_, - unused_root_delta); + input_handler_client_->DidOverscroll( + viewport_point, accumulated_root_overscroll_, unused_root_delta); } return did_scroll_content || did_scroll_top_controls; @@ -2951,6 +2965,7 @@ scoped_ptr LayerTreeHostImpl::ProcessScrollDeltas() { CollectScrollDeltas(scroll_info.get(), active_tree_->root_layer()); scroll_info->page_scale_delta = active_tree_->page_scale_delta(); active_tree_->set_sent_page_scale_delta(scroll_info->page_scale_delta); + scroll_info->swap_promises.swap(swap_promises_for_main_thread_scroll_update_); return scroll_info.Pass(); } @@ -3029,15 +3044,23 @@ void LayerTreeHostImpl::AnimatePageScale(base::TimeTicks monotonic_time) { void LayerTreeHostImpl::AnimateTopControls(base::TimeTicks time) { if (!top_controls_manager_ || !top_controls_manager_->animation()) return; + gfx::Vector2dF scroll = top_controls_manager_->Animate(time); + + if (top_controls_manager_->animation()) + SetNeedsAnimate(); + if (active_tree_->TotalScrollOffset().y() == 0.f) return; - if (!scroll.IsZero()) { - ScrollViewportBy(gfx::ScaleVector2d( - scroll, 1.f / active_tree_->total_page_scale_factor())); - SetNeedsRedraw(); - } - SetNeedsAnimate(); + + if (scroll.IsZero()) + return; + + ScrollViewportBy(gfx::ScaleVector2d( + scroll, 1.f / active_tree_->total_page_scale_factor())); + SetNeedsRedraw(); + client_->SetNeedsCommitOnImplThread(); + client_->RenewTreePriority(); } void LayerTreeHostImpl::AnimateLayers(base::TimeTicks monotonic_time) { @@ -3200,6 +3223,10 @@ void LayerTreeHostImpl::AsValueWithFrameInto( state->BeginArray("tiles"); tile_manager_->AllTilesAsValueInto(state); state->EndArray(); + + state->BeginDictionary("tile_manager_basic_state"); + tile_manager_->BasicStateAsValueInto(state); + state->EndDictionary(); } state->BeginDictionary("active_tree"); active_tree_->AsValueInto(state); @@ -3267,8 +3294,16 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid, DeleteUIResource(uid); ResourceFormat format = resource_provider_->best_texture_format(); - if (bitmap.GetFormat() == UIResourceBitmap::ETC1) - format = ETC1; + switch (bitmap.GetFormat()) { + case UIResourceBitmap::RGBA8: + break; + case UIResourceBitmap::ALPHA_8: + format = ALPHA_8; + break; + case UIResourceBitmap::ETC1: + format = ETC1; + break; + } id = resource_provider_->CreateResource( bitmap.GetSize(), wrap_mode, @@ -3364,6 +3399,12 @@ void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfSetNeedsRedraw() { (*it)->OnSetNeedsRedrawOnImpl(); } +void LayerTreeHostImpl::NotifySwapPromiseMonitorsOfForwardingToMainThread() { + std::set::iterator it = swap_promise_monitor_.begin(); + for (; it != swap_promise_monitor_.end(); it++) + (*it)->OnForwardScrollUpdateToMainThreadOnImpl(); +} + void LayerTreeHostImpl::RegisterPictureLayerImpl(PictureLayerImpl* layer) { DCHECK(std::find(picture_layers_.begin(), picture_layers_.end(), layer) == picture_layers_.end()); diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h index 821028fa64595..74e989654fdd0 100644 --- a/cc/trees/layer_tree_host_impl.h +++ b/cc/trees/layer_tree_host_impl.h @@ -217,6 +217,7 @@ class CC_EXPORT LayerTreeHostImpl // Resets all of the trees to an empty state. void ResetTreesForTesting(); + void ResetRecycleTreeForTesting(); DrawMode GetDrawMode() const; @@ -331,6 +332,8 @@ class CC_EXPORT LayerTreeHostImpl bool scroll_affects_scroll_handler() const { return scroll_affects_scroll_handler_; } + void QueueSwapPromiseForMainThreadScrollUpdate( + scoped_ptr swap_promise); bool IsCurrentlyScrolling() const; @@ -575,6 +578,7 @@ class CC_EXPORT LayerTreeHostImpl void MarkUIResourceNotEvicted(UIResourceId uid); void NotifySwapPromiseMonitorsOfSetNeedsRedraw(); + void NotifySwapPromiseMonitorsOfForwardingToMainThread(); typedef base::hash_map UIResourceMap; @@ -621,6 +625,7 @@ class CC_EXPORT LayerTreeHostImpl bool wheel_scrolling_; bool scroll_affects_scroll_handler_; int scroll_layer_id_when_mouse_over_scrollbar_; + ScopedPtrVector swap_promises_for_main_thread_scroll_update_; bool tile_priorities_dirty_; @@ -703,9 +708,7 @@ class CC_EXPORT LayerTreeHostImpl MicroBenchmarkControllerImpl micro_benchmark_controller_; bool need_to_update_visible_tiles_before_draw_; -#if DCHECK_IS_ON - bool did_lose_called_; -#endif + bool have_valid_output_surface_; // Optional callback to notify of new tree activations. base::Closure tree_activation_callback_; diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc index 5d45d53885f6f..f01d0f92ee2ad 100644 --- a/cc/trees/layer_tree_host_impl_unittest.cc +++ b/cc/trees/layer_tree_host_impl_unittest.cc @@ -972,6 +972,24 @@ TEST_F(LayerTreeHostImplTest, ImplPinchZoom) { } } +TEST_F(LayerTreeHostImplTest, ScrollWithSwapPromises) { + ui::LatencyInfo latency_info; + latency_info.trace_id = 1234; + scoped_ptr swap_promise( + new LatencyInfoSwapPromise(latency_info)); + + SetupScrollAndContentsLayers(gfx::Size(100, 100)); + EXPECT_EQ(InputHandler::ScrollStarted, + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); + host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10)); + host_impl_->QueueSwapPromiseForMainThreadScrollUpdate(swap_promise.Pass()); + host_impl_->ScrollEnd(); + + scoped_ptr scroll_info = host_impl_->ProcessScrollDeltas(); + EXPECT_EQ(1u, scroll_info->swap_promises.size()); + EXPECT_EQ(latency_info.trace_id, scroll_info->swap_promises[0]->TraceId()); +} + TEST_F(LayerTreeHostImplTest, MasksToBoundsDoesntClobberInnerContainerSize) { SetupScrollAndContentsLayers(gfx::Size(100, 100)); host_impl_->SetViewportSize(gfx::Size(50, 50)); @@ -1845,6 +1863,8 @@ TEST_F(LayerTreeHostImplTest, DidDrawCalledOnAllLayers) { EXPECT_FALSE(layer2->did_draw_called()); LayerTreeHostImpl::FrameData frame; + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren( + host_impl_->active_tree()->root_layer()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); host_impl_->DrawLayers(&frame, gfx::FrameTime::Now()); host_impl_->DidDrawAllLayers(frame); @@ -3020,6 +3040,21 @@ TEST_F(LayerTreeHostImplTest, ScrollScaledLayer) { wheel_scroll_delta); } +TEST_F(LayerTreeHostImplTest, ScrollViewportRounding) { + int width = 332; + int height = 20; + int scale = 3; + SetupScrollAndContentsLayers(gfx::Size(width, height)); + host_impl_->SetViewportSize(gfx::Size(width * scale - 1, height * scale)); + host_impl_->SetDeviceScaleFactor(scale); + host_impl_->active_tree()->SetPageScaleFactorAndLimits(1.f, 0.5f, 4.f); + + LayerImpl* inner_viewport_scroll_layer = + host_impl_->active_tree()->InnerViewportScrollLayer(); + EXPECT_EQ(gfx::Vector2d(0, 0), + inner_viewport_scroll_layer->MaxScrollOffset()); +} + class TestScrollOffsetDelegate : public LayerScrollOffsetDelegate { public: TestScrollOffsetDelegate() @@ -3678,6 +3713,8 @@ TEST_F(LayerTreeHostImplTest, BlendingOffWhenDrawingOpaqueLayers) { layer1->SetUpdateRect(gfx::RectF(layer1->content_bounds())); layer2->SetExpectation(false, false); layer2->SetUpdateRect(gfx::RectF(layer1->content_bounds())); + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren( + host_impl_->active_tree()->root_layer()); EXPECT_EQ(DRAW_SUCCESS, host_impl_->PrepareToDraw(&frame)); host_impl_->DrawLayers(&frame, gfx::FrameTime::Now()); EXPECT_TRUE(layer1->quads_appended()); @@ -5133,7 +5170,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerWithDifferentBounds) { frame.render_passes[0]->quad_list[1]->material); const RenderPassDrawQuad* replica_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[1]); - EXPECT_TRUE(replica_quad->is_replica); EXPECT_EQ(gfx::Rect(0, 0, 50, 50).ToString(), replica_quad->rect.ToString()); EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), @@ -5161,7 +5197,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerWithDifferentBounds) { frame.render_passes[0]->quad_list[1]->material); const RenderPassDrawQuad* replica_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[1]); - EXPECT_TRUE(replica_quad->is_replica); EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), replica_quad->rect.ToString()); EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), @@ -5192,7 +5227,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerWithDifferentBounds) { frame.render_passes[0]->quad_list[1]->material); const RenderPassDrawQuad* replica_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[1]); - EXPECT_TRUE(replica_quad->is_replica); EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), replica_quad->rect.ToString()); EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), @@ -5218,7 +5252,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerWithDifferentBounds) { frame.render_passes[0]->quad_list[1]->material); const RenderPassDrawQuad* replica_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[1]); - EXPECT_TRUE(replica_quad->is_replica); EXPECT_EQ(gfx::Rect(0, 0, 100, 100).ToString(), replica_quad->rect.ToString()); EXPECT_EQ(gfx::RectF(0.f, 0.f, 1.f, 1.f).ToString(), @@ -5296,7 +5329,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerForSurfaceWithUnclippedChild) { frame.render_passes[0]->quad_list[0]->material); const RenderPassDrawQuad* render_pass_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[0]); - EXPECT_FALSE(render_pass_quad->is_replica); EXPECT_EQ(gfx::Rect(0, 0, 100, 50).ToString(), render_pass_quad->rect.ToString()); @@ -5305,7 +5337,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerForSurfaceWithUnclippedChild) { frame.render_passes[0]->quad_list[1]->material); const RenderPassDrawQuad* replica_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[1]); - EXPECT_TRUE(replica_quad->is_replica); EXPECT_EQ(gfx::Rect(0, 0, 100, 50).ToString(), replica_quad->rect.ToString()); EXPECT_EQ(gfx::RectF(0.f, 0.f, 2.f, 1.f).ToString(), @@ -5330,7 +5361,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerForSurfaceWithUnclippedChild) { frame.render_passes[0]->quad_list[0]->material); const RenderPassDrawQuad* render_pass_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[0]); - EXPECT_FALSE(render_pass_quad->is_replica); EXPECT_EQ(gfx::Rect(-50, 0, 100, 50).ToString(), render_pass_quad->rect.ToString()); @@ -5339,7 +5369,6 @@ TEST_F(LayerTreeHostImplTest, ReflectionMaskLayerForSurfaceWithUnclippedChild) { frame.render_passes[0]->quad_list[1]->material); const RenderPassDrawQuad* replica_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[1]); - EXPECT_TRUE(replica_quad->is_replica); EXPECT_EQ(gfx::Rect(-50, 0, 100, 50).ToString(), replica_quad->rect.ToString()); EXPECT_EQ(gfx::RectF(-1.f, 0.f, 2.f, 1.f).ToString(), @@ -5424,7 +5453,6 @@ TEST_F(LayerTreeHostImplTest, MaskLayerForSurfaceWithClippedLayer) { frame.render_passes[0]->quad_list[0]->material); const RenderPassDrawQuad* render_pass_quad = RenderPassDrawQuad::MaterialCast(frame.render_passes[0]->quad_list[0]); - EXPECT_FALSE(render_pass_quad->is_replica); EXPECT_EQ(gfx::Rect(20, 10, 10, 20).ToString(), render_pass_quad->rect.ToString()); @@ -6367,10 +6395,12 @@ class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { SimpleSwapPromiseMonitor(LayerTreeHost* layer_tree_host, LayerTreeHostImpl* layer_tree_host_impl, int* set_needs_commit_count, - int* set_needs_redraw_count) + int* set_needs_redraw_count, + int* forward_to_main_count) : SwapPromiseMonitor(layer_tree_host, layer_tree_host_impl), set_needs_commit_count_(set_needs_commit_count), - set_needs_redraw_count_(set_needs_redraw_count) {} + set_needs_redraw_count_(set_needs_redraw_count), + forward_to_main_count_(forward_to_main_count) {} virtual ~SimpleSwapPromiseMonitor() {} @@ -6382,24 +6412,32 @@ class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { (*set_needs_redraw_count_)++; } + virtual void OnForwardScrollUpdateToMainThreadOnImpl() OVERRIDE { + (*forward_to_main_count_)++; + } + private: int* set_needs_commit_count_; int* set_needs_redraw_count_; + int* forward_to_main_count_; }; TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) { int set_needs_commit_count = 0; int set_needs_redraw_count = 0; + int forward_to_main_count = 0; { scoped_ptr swap_promise_monitor( new SimpleSwapPromiseMonitor(NULL, host_impl_.get(), &set_needs_commit_count, - &set_needs_redraw_count)); + &set_needs_redraw_count, + &forward_to_main_count)); host_impl_->SetNeedsRedraw(); EXPECT_EQ(0, set_needs_commit_count); EXPECT_EQ(1, set_needs_redraw_count); + EXPECT_EQ(0, forward_to_main_count); } // Now the monitor is destroyed, SetNeedsRedraw() is no longer being @@ -6407,16 +6445,19 @@ TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) { host_impl_->SetNeedsRedraw(); EXPECT_EQ(0, set_needs_commit_count); EXPECT_EQ(1, set_needs_redraw_count); + EXPECT_EQ(0, forward_to_main_count); { scoped_ptr swap_promise_monitor( new SimpleSwapPromiseMonitor(NULL, host_impl_.get(), &set_needs_commit_count, - &set_needs_redraw_count)); + &set_needs_redraw_count, + &forward_to_main_count)); host_impl_->SetNeedsRedrawRect(gfx::Rect(10, 10)); EXPECT_EQ(0, set_needs_commit_count); EXPECT_EQ(2, set_needs_redraw_count); + EXPECT_EQ(0, forward_to_main_count); } { @@ -6424,11 +6465,48 @@ TEST_F(LayerTreeHostImplTest, SimpleSwapPromiseMonitor) { new SimpleSwapPromiseMonitor(NULL, host_impl_.get(), &set_needs_commit_count, - &set_needs_redraw_count)); + &set_needs_redraw_count, + &forward_to_main_count)); // Empty damage rect won't signal the monitor. host_impl_->SetNeedsRedrawRect(gfx::Rect()); EXPECT_EQ(0, set_needs_commit_count); EXPECT_EQ(2, set_needs_redraw_count); + EXPECT_EQ(0, forward_to_main_count); + } + + { + set_needs_commit_count = 0; + set_needs_redraw_count = 0; + forward_to_main_count = 0; + scoped_ptr swap_promise_monitor( + new SimpleSwapPromiseMonitor(NULL, + host_impl_.get(), + &set_needs_commit_count, + &set_needs_redraw_count, + &forward_to_main_count)); + LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 100)); + + // Scrolling normally should not trigger any forwarding. + EXPECT_EQ(InputHandler::ScrollStarted, + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10))); + host_impl_->ScrollEnd(); + + EXPECT_EQ(0, set_needs_commit_count); + EXPECT_EQ(1, set_needs_redraw_count); + EXPECT_EQ(0, forward_to_main_count); + + // Scrolling with a scroll handler should defer the swap to the main + // thread. + scroll_layer->SetHaveScrollEventHandlers(true); + EXPECT_EQ(InputHandler::ScrollStarted, + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, 10))); + host_impl_->ScrollEnd(); + + EXPECT_EQ(0, set_needs_commit_count); + EXPECT_EQ(2, set_needs_redraw_count); + EXPECT_EQ(1, forward_to_main_count); } } @@ -6514,6 +6592,126 @@ TEST_F(LayerTreeHostImplWithTopControlsTest, ScrollHandledByTopControls) { host_impl_->ScrollEnd(); } +TEST_F(LayerTreeHostImplWithTopControlsTest, TopControlsAnimationAtOrigin) { + LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 200)); + host_impl_->SetViewportSize(gfx::Size(100, 200)); + DrawFrame(); + + EXPECT_EQ(InputHandler::ScrollStarted, + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); + EXPECT_EQ(0, host_impl_->top_controls_manager()->controls_top_offset()); + EXPECT_EQ(gfx::Vector2dF().ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + // Scroll the top controls partially. + const float residue = 35; + float offset = top_controls_height_ - residue; + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_EQ(-offset, host_impl_->top_controls_manager()->controls_top_offset()); + EXPECT_EQ(gfx::Vector2dF().ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + did_request_redraw_ = false; + did_request_animate_ = false; + did_request_commit_ = false; + + // End the scroll while the controls are still offset from their limit. + host_impl_->ScrollEnd(); + ASSERT_TRUE(host_impl_->top_controls_manager()->animation()); + EXPECT_TRUE(did_request_animate_); + EXPECT_TRUE(did_request_redraw_); + EXPECT_FALSE(did_request_commit_); + + // The top controls should properly animate until finished, despite the scroll + // offset being at the origin. + base::TimeTicks animation_time = gfx::FrameTime::Now(); + while (did_request_animate_) { + did_request_redraw_ = false; + did_request_animate_ = false; + did_request_commit_ = false; + + float old_offset = + host_impl_->top_controls_manager()->controls_top_offset(); + + animation_time += base::TimeDelta::FromMilliseconds(5); + host_impl_->Animate(animation_time); + EXPECT_EQ(gfx::Vector2dF().ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + float new_offset = + host_impl_->top_controls_manager()->controls_top_offset(); + + // No commit is needed as the controls are animating the content offset, + // not the scroll offset. + EXPECT_FALSE(did_request_commit_); + + if (new_offset != old_offset) + EXPECT_TRUE(did_request_redraw_); + + if (new_offset != 0) { + EXPECT_TRUE(host_impl_->top_controls_manager()->animation()); + EXPECT_TRUE(did_request_animate_); + } + } + EXPECT_FALSE(host_impl_->top_controls_manager()->animation()); +} + +TEST_F(LayerTreeHostImplWithTopControlsTest, TopControlsAnimationAfterScroll) { + LayerImpl* scroll_layer = SetupScrollAndContentsLayers(gfx::Size(100, 200)); + host_impl_->SetViewportSize(gfx::Size(100, 100)); + float initial_scroll_offset = 50; + scroll_layer->SetScrollOffset(gfx::Vector2d(0, initial_scroll_offset)); + DrawFrame(); + + EXPECT_EQ(InputHandler::ScrollStarted, + host_impl_->ScrollBegin(gfx::Point(), InputHandler::Gesture)); + EXPECT_EQ(0, host_impl_->top_controls_manager()->controls_top_offset()); + EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + // Scroll the top controls partially. + const float residue = 15; + float offset = top_controls_height_ - residue; + EXPECT_TRUE(host_impl_->ScrollBy(gfx::Point(), gfx::Vector2d(0, offset))); + EXPECT_EQ(-offset, host_impl_->top_controls_manager()->controls_top_offset()); + EXPECT_EQ(gfx::Vector2dF(0, initial_scroll_offset).ToString(), + scroll_layer->TotalScrollOffset().ToString()); + + did_request_redraw_ = false; + did_request_animate_ = false; + did_request_commit_ = false; + + // End the scroll while the controls are still offset from the limit. + host_impl_->ScrollEnd(); + ASSERT_TRUE(host_impl_->top_controls_manager()->animation()); + EXPECT_TRUE(did_request_animate_); + EXPECT_TRUE(did_request_redraw_); + EXPECT_FALSE(did_request_commit_); + + // Animate the top controls to the limit. + base::TimeTicks animation_time = gfx::FrameTime::Now(); + while (did_request_animate_) { + did_request_redraw_ = false; + did_request_animate_ = false; + did_request_commit_ = false; + + float old_offset = + host_impl_->top_controls_manager()->controls_top_offset(); + + animation_time += base::TimeDelta::FromMilliseconds(5); + host_impl_->Animate(animation_time); + + float new_offset = + host_impl_->top_controls_manager()->controls_top_offset(); + + if (new_offset != old_offset) { + EXPECT_TRUE(did_request_redraw_); + EXPECT_TRUE(did_request_commit_); + } + } + EXPECT_FALSE(host_impl_->top_controls_manager()->animation()); +} + class LayerTreeHostImplVirtualViewportTest : public LayerTreeHostImplTest { public: void SetupVirtualViewportLayers(const gfx::Size& content_size, @@ -6815,5 +7013,36 @@ TEST_F(LayerTreeHostImplTest, DidBecomeActive) { EXPECT_EQ(1u, raw_replica_mask_layer->did_become_active_call_count()); } +class LayerTreeHostImplCountingLostSurfaces : public LayerTreeHostImplTest { + public: + LayerTreeHostImplCountingLostSurfaces() : num_lost_surfaces_(0) {} + virtual void DidLoseOutputSurfaceOnImplThread() OVERRIDE { + num_lost_surfaces_++; + } + + protected: + int num_lost_surfaces_; +}; + +TEST_F(LayerTreeHostImplCountingLostSurfaces, TwiceLostSurface) { + // The medium term, we plan to remove LayerTreeHostImpl::IsContextLost(). + // Until then, we need the state variable + // LayerTreeHostImpl::have_valid_output_surface_ and we can + // enforce the following behaviour, where calling DidLoseOutputSurface + // twice in a row only causes one subsequent + // call to LayerTreeHostImplClient::DidLoseOutputSurfaceOnImplThread(). + // Really we just need at least one client notification each time + // we go from having a valid output surface to not having a valid output + // surface. + EXPECT_EQ(0, num_lost_surfaces_); + EXPECT_FALSE(host_impl_->IsContextLost()); + host_impl_->DidLoseOutputSurface(); + EXPECT_TRUE(host_impl_->IsContextLost()); + EXPECT_EQ(1, num_lost_surfaces_); + host_impl_->DidLoseOutputSurface(); + EXPECT_TRUE(host_impl_->IsContextLost()); + EXPECT_EQ(1, num_lost_surfaces_); +} + } // namespace } // namespace cc diff --git a/cc/trees/layer_tree_host_pixeltest_filters.cc b/cc/trees/layer_tree_host_pixeltest_filters.cc index c8c8c7adf3f5f..5303ed1afb9d7 100644 --- a/cc/trees/layer_tree_host_pixeltest_filters.cc +++ b/cc/trees/layer_tree_host_pixeltest_filters.cc @@ -157,6 +157,80 @@ TEST_F(LayerTreeHostFiltersPixelTest, BackgroundFilterBlurOffAxis) { "background_filter_blur_off_axis.png"))); } +class LayerTreeHostFiltersScaledPixelTest + : public LayerTreeHostFiltersPixelTest { + virtual void InitializeSettings(LayerTreeSettings* settings) OVERRIDE { + // Required so that device scale is inherited by content scale. + settings->layer_transforms_should_scale_layer_contents = true; + } + + virtual void SetupTree() OVERRIDE { + layer_tree_host()->SetDeviceScaleFactor(device_scale_factor_); + LayerTreePixelTest::SetupTree(); + } + + protected: + void RunPixelTestType(int content_size, + float device_scale_factor, + PixelTestType test_type) { + int half_content = content_size / 2; + + scoped_refptr root = CreateSolidColorLayer( + gfx::Rect(0, 0, content_size, content_size), SK_ColorWHITE); + + scoped_refptr background = CreateSolidColorLayer( + gfx::Rect(0, 0, content_size, content_size), SK_ColorGREEN); + root->AddChild(background); + + // Add a blue layer that completely covers the green layer. + scoped_refptr foreground = CreateSolidColorLayer( + gfx::Rect(0, 0, content_size, content_size), SK_ColorBLUE); + background->AddChild(foreground); + + // Add an alpha threshold filter to the blue layer which will filter out + // everything except the lower right corner. + FilterOperations filters; + SkRegion alpha_region; + alpha_region.setRect( + half_content, half_content, content_size, content_size); + filters.Append( + FilterOperation::CreateAlphaThresholdFilter(alpha_region, 1.f, 0.f)); + foreground->SetFilters(filters); + + device_scale_factor_ = device_scale_factor; + RunPixelTest( + test_type, + background, + base::FilePath(FILE_PATH_LITERAL("green_small_with_blue_corner.png"))); + } + + float device_scale_factor_; +}; + +TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_GLBitmap) { + RunPixelTestType(100, 1.f, GL_WITH_BITMAP); +} + +TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_GLDefault) { + RunPixelTestType(100, 1.f, GL_WITH_DEFAULT); +} + +TEST_F(LayerTreeHostFiltersScaledPixelTest, StandardDpi_Software) { + RunPixelTestType(100, 1.f, SOFTWARE_WITH_BITMAP); +} + +TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_GLBitmap) { + RunPixelTestType(50, 2.f, GL_WITH_BITMAP); +} + +TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_GLDefault) { + RunPixelTestType(50, 2.f, GL_WITH_DEFAULT); +} + +TEST_F(LayerTreeHostFiltersScaledPixelTest, HiDpi_Software) { + RunPixelTestType(50, 2.f, SOFTWARE_WITH_BITMAP); +} + class ImageFilterClippedPixelTest : public LayerTreeHostFiltersPixelTest { protected: void RunPixelTestType(PixelTestType test_type) { diff --git a/cc/trees/layer_tree_host_pixeltest_readback.cc b/cc/trees/layer_tree_host_pixeltest_readback.cc index 4c808397eba08..12008f0d7dad5 100644 --- a/cc/trees/layer_tree_host_pixeltest_readback.cc +++ b/cc/trees/layer_tree_host_pixeltest_readback.cc @@ -3,11 +3,12 @@ // found in the LICENSE file. #include "build/build_config.h" -#include "cc/layers/content_layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/layers/texture_layer.h" #include "cc/output/copy_output_request.h" #include "cc/output/copy_output_result.h" +#include "cc/test/fake_picture_layer.h" +#include "cc/test/fake_picture_layer_impl.h" #include "cc/test/layer_tree_pixel_test.h" #include "cc/test/paths.h" #include "cc/test/solid_color_content_layer_client.h" @@ -932,17 +933,20 @@ class LayerTreeHostReadbackDeviceScalePixelTest virtual void DrawLayersOnThread(LayerTreeHostImpl* host_impl) OVERRIDE { LayerImpl* root_impl = host_impl->active_tree()->root_layer(); - LayerImpl* background_impl = root_impl->children()[0]; - EXPECT_EQ(device_scale_factor_, background_impl->contents_scale_x()); - EXPECT_EQ(device_scale_factor_, background_impl->contents_scale_y()); + FakePictureLayerImpl* background_impl = + static_cast(root_impl->children()[0]); + EXPECT_EQ(device_scale_factor_, + background_impl->HighResTiling()->contents_scale()); - LayerImpl* green_impl = background_impl->children()[0]; - EXPECT_EQ(device_scale_factor_, green_impl->contents_scale_x()); - EXPECT_EQ(device_scale_factor_, green_impl->contents_scale_y()); + FakePictureLayerImpl* green_impl = + static_cast(background_impl->children()[0]); + EXPECT_EQ(device_scale_factor_, + green_impl->HighResTiling()->contents_scale()); - LayerImpl* blue_impl = green_impl->children()[0]; - EXPECT_EQ(device_scale_factor_, blue_impl->contents_scale_x()); - EXPECT_EQ(device_scale_factor_, blue_impl->contents_scale_y()); + FakePictureLayerImpl* blue_impl = + static_cast(green_impl->children()[0]); + EXPECT_EQ(device_scale_factor_, + blue_impl->HighResTiling()->contents_scale()); } float device_scale_factor_; @@ -953,16 +957,19 @@ class LayerTreeHostReadbackDeviceScalePixelTest TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackSubrect_Software) { - scoped_refptr background = ContentLayer::Create(&white_client_); + scoped_refptr background = + FakePictureLayer::Create(&white_client_); background->SetBounds(gfx::Size(100, 100)); background->SetIsDrawable(true); - scoped_refptr green = ContentLayer::Create(&green_client_); + scoped_refptr green = + FakePictureLayer::Create(&green_client_); green->SetBounds(gfx::Size(100, 100)); green->SetIsDrawable(true); background->AddChild(green); - scoped_refptr blue = ContentLayer::Create(&blue_client_); + scoped_refptr blue = + FakePictureLayer::Create(&blue_client_); blue->SetPosition(gfx::Point(50, 50)); blue->SetBounds(gfx::Size(25, 25)); blue->SetIsDrawable(true); @@ -971,8 +978,6 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, // Grab the middle of the root layer. copy_subrect_ = gfx::Rect(25, 25, 50, 50); device_scale_factor_ = 2.f; - - this->impl_side_painting_ = false; RunPixelTest(SOFTWARE_WITH_DEFAULT, background, base::FilePath(FILE_PATH_LITERAL( @@ -981,16 +986,19 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackSubrect_GL) { - scoped_refptr background = ContentLayer::Create(&white_client_); + scoped_refptr background = + FakePictureLayer::Create(&white_client_); background->SetBounds(gfx::Size(100, 100)); background->SetIsDrawable(true); - scoped_refptr green = ContentLayer::Create(&green_client_); + scoped_refptr green = + FakePictureLayer::Create(&green_client_); green->SetBounds(gfx::Size(100, 100)); green->SetIsDrawable(true); background->AddChild(green); - scoped_refptr blue = ContentLayer::Create(&blue_client_); + scoped_refptr blue = + FakePictureLayer::Create(&blue_client_); blue->SetPosition(gfx::Point(50, 50)); blue->SetBounds(gfx::Size(25, 25)); blue->SetIsDrawable(true); @@ -999,8 +1007,6 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, // Grab the middle of the root layer. copy_subrect_ = gfx::Rect(25, 25, 50, 50); device_scale_factor_ = 2.f; - - this->impl_side_painting_ = false; RunPixelTest(GL_WITH_DEFAULT, background, base::FilePath(FILE_PATH_LITERAL( @@ -1009,17 +1015,20 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackNonRootLayerSubrect_Software) { - scoped_refptr background = ContentLayer::Create(&white_client_); + scoped_refptr background = + FakePictureLayer::Create(&white_client_); background->SetBounds(gfx::Size(100, 100)); background->SetIsDrawable(true); - scoped_refptr green = ContentLayer::Create(&green_client_); + scoped_refptr green = + FakePictureLayer::Create(&green_client_); green->SetPosition(gfx::Point(10, 20)); green->SetBounds(gfx::Size(90, 80)); green->SetIsDrawable(true); background->AddChild(green); - scoped_refptr blue = ContentLayer::Create(&blue_client_); + scoped_refptr blue = + FakePictureLayer::Create(&blue_client_); blue->SetPosition(gfx::Point(50, 50)); blue->SetBounds(gfx::Size(25, 25)); blue->SetIsDrawable(true); @@ -1028,8 +1037,6 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, // Grab the green layer's content with blue in the bottom right. copy_subrect_ = gfx::Rect(25, 25, 50, 50); device_scale_factor_ = 2.f; - - this->impl_side_painting_ = false; RunPixelTestWithReadbackTarget(SOFTWARE_WITH_DEFAULT, background, green.get(), @@ -1039,17 +1046,20 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, ReadbackNonRootLayerSubrect_GL) { - scoped_refptr background = ContentLayer::Create(&white_client_); + scoped_refptr background = + FakePictureLayer::Create(&white_client_); background->SetBounds(gfx::Size(100, 100)); background->SetIsDrawable(true); - scoped_refptr green = ContentLayer::Create(&green_client_); + scoped_refptr green = + FakePictureLayer::Create(&green_client_); green->SetPosition(gfx::Point(10, 20)); green->SetBounds(gfx::Size(90, 80)); green->SetIsDrawable(true); background->AddChild(green); - scoped_refptr blue = ContentLayer::Create(&blue_client_); + scoped_refptr blue = + FakePictureLayer::Create(&blue_client_); blue->SetPosition(gfx::Point(50, 50)); blue->SetBounds(gfx::Size(25, 25)); blue->SetIsDrawable(true); @@ -1058,8 +1068,6 @@ TEST_F(LayerTreeHostReadbackDeviceScalePixelTest, // Grab the green layer's content with blue in the bottom right. copy_subrect_ = gfx::Rect(25, 25, 50, 50); device_scale_factor_ = 2.f; - - this->impl_side_painting_ = false; RunPixelTestWithReadbackTarget(GL_WITH_DEFAULT, background, green.get(), diff --git a/cc/trees/layer_tree_host_unittest.cc b/cc/trees/layer_tree_host_unittest.cc index ea806aa14b2a4..a17c80b0a6904 100644 --- a/cc/trees/layer_tree_host_unittest.cc +++ b/cc/trees/layer_tree_host_unittest.cc @@ -91,8 +91,8 @@ class LayerTreeHostTestSetNeedsCommit1 : public LayerTreeHostTest { } virtual void AfterTest() OVERRIDE { - EXPECT_GE(1, num_commits_); - EXPECT_GE(1, num_draws_); + EXPECT_LE(1, num_commits_); + EXPECT_LE(1, num_draws_); } private: @@ -694,31 +694,6 @@ class LayerTreeHostTestUndrawnLayersPushContentBoundsLater SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostTestUndrawnLayersPushContentBoundsLater); -class LayerTreeHostTestAbortFrameWhenInvisible : public LayerTreeHostTest { - public: - LayerTreeHostTestAbortFrameWhenInvisible() {} - - virtual void BeginTest() OVERRIDE { - // Request a commit (from the main thread), Which will trigger the commit - // flow from the impl side. - layer_tree_host()->SetNeedsCommit(); - // Then mark ourselves as not visible before processing any more messages - // on the main thread. - layer_tree_host()->SetVisible(false); - // If we make it without kicking a frame, we pass! - EndTestAfterDelay(1); - } - - virtual void Layout() OVERRIDE { - ASSERT_FALSE(true); - EndTest(); - } - - virtual void AfterTest() OVERRIDE {} -}; - -MULTI_THREAD_TEST_F(LayerTreeHostTestAbortFrameWhenInvisible); - // This test verifies that properties on the layer tree host are commited // to the impl side. class LayerTreeHostTestCommit : public LayerTreeHostTest { @@ -1050,19 +1025,11 @@ class NoScaleContentLayer : public ContentLayer { } virtual void CalculateContentsScale(float ideal_contents_scale, - float device_scale_factor, - float page_scale_factor, - float maximum_animation_contents_scale, - bool animating_transform_to_screen, float* contents_scale_x, float* contents_scale_y, gfx::Size* contentBounds) OVERRIDE { // Skip over the ContentLayer's method to the base Layer class. Layer::CalculateContentsScale(ideal_contents_scale, - device_scale_factor, - page_scale_factor, - maximum_animation_contents_scale, - animating_transform_to_screen, contents_scale_x, contents_scale_y, contentBounds); @@ -1838,47 +1805,6 @@ class LayerTreeHostTestEvictTextures : public LayerTreeHostTest { MULTI_THREAD_NOIMPL_TEST_F(LayerTreeHostTestEvictTextures); -class LayerTreeHostTestContinuousCommit : public LayerTreeHostTest { - public: - LayerTreeHostTestContinuousCommit() - : num_commit_complete_(0), num_draw_layers_(0) {} - - virtual void BeginTest() OVERRIDE { - layer_tree_host()->SetViewportSize(gfx::Size(10, 10)); - layer_tree_host()->root_layer()->SetBounds(gfx::Size(10, 10)); - - PostSetNeedsCommitToMainThread(); - } - - virtual void DidCommit() OVERRIDE { - if (num_draw_layers_ == 2) - return; - layer_tree_host()->SetNeedsCommit(); - } - - virtual void CommitCompleteOnThread(LayerTreeHostImpl* impl) OVERRIDE { - if (num_draw_layers_ == 1) - num_commit_complete_++; - } - - virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { - num_draw_layers_++; - if (num_draw_layers_ == 2) - EndTest(); - } - - virtual void AfterTest() OVERRIDE { - // Check that we didn't commit twice between first and second draw. - EXPECT_EQ(1, num_commit_complete_); - } - - private: - int num_commit_complete_; - int num_draw_layers_; -}; - -MULTI_THREAD_TEST_F(LayerTreeHostTestContinuousCommit); - class LayerTreeHostTestContinuousInvalidate : public LayerTreeHostTest { public: LayerTreeHostTestContinuousInvalidate() @@ -3004,6 +2930,8 @@ class PushPropertiesCountingLayer : public Layer { PassAs(); } + void SetDrawsContent(bool draws_content) { SetIsDrawable(draws_content); } + size_t push_properties_count() const { return push_properties_count_; } void reset_push_properties_count() { push_properties_count_ = 0; } @@ -3015,7 +2943,6 @@ class PushPropertiesCountingLayer : public Layer { PushPropertiesCountingLayer() : push_properties_count_(0), persist_needs_push_properties_(false) { SetBounds(gfx::Size(1, 1)); - SetIsDrawable(true); } virtual ~PushPropertiesCountingLayer() {} @@ -3472,6 +3399,59 @@ class LayerTreeHostTestPropertyChangesDuringUpdateArePushed MULTI_THREAD_TEST_F(LayerTreeHostTestPropertyChangesDuringUpdateArePushed); +class LayerTreeHostTestSetDrawableCausesCommit : public LayerTreeHostTest { + protected: + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + + virtual void SetupTree() OVERRIDE { + root_ = PushPropertiesCountingLayer::Create(); + child_ = PushPropertiesCountingLayer::Create(); + root_->AddChild(child_); + + layer_tree_host()->SetRootLayer(root_); + LayerTreeHostTest::SetupTree(); + } + + virtual void DidCommitAndDrawFrame() OVERRIDE { + switch (layer_tree_host()->source_frame_number()) { + case 0: + break; + case 1: { + // During update, the ignore_set_needs_commit_ bit is set to true to + // avoid causing a second commit to be scheduled. If a property change + // is made during this, however, it needs to be pushed in the upcoming + // commit. + EXPECT_FALSE(root_->needs_push_properties()); + EXPECT_FALSE(child_->needs_push_properties()); + EXPECT_EQ(0, root_->NumDescendantsThatDrawContent()); + root_->reset_push_properties_count(); + child_->reset_push_properties_count(); + child_->SetDrawsContent(true); + EXPECT_EQ(1, root_->NumDescendantsThatDrawContent()); + EXPECT_EQ(0u, root_->push_properties_count()); + EXPECT_EQ(0u, child_->push_properties_count()); + EXPECT_TRUE(root_->needs_push_properties()); + EXPECT_TRUE(child_->needs_push_properties()); + break; + } + case 2: + EXPECT_EQ(1u, root_->push_properties_count()); + EXPECT_EQ(1u, child_->push_properties_count()); + EXPECT_FALSE(root_->needs_push_properties()); + EXPECT_FALSE(child_->needs_push_properties()); + EndTest(); + break; + } + } + + virtual void AfterTest() OVERRIDE {} + + scoped_refptr root_; + scoped_refptr child_; +}; + +MULTI_THREAD_TEST_F(LayerTreeHostTestSetDrawableCausesCommit); + class LayerTreeHostTestCasePushPropertiesThreeGrandChildren : public LayerTreeHostTest { protected: @@ -4443,11 +4423,19 @@ class TestSwapPromise : public SwapPromise { virtual ~TestSwapPromise() { base::AutoLock lock(result_->lock); + LOG(ERROR) << "~TestSwapPromise() " + << " did_swap_called " << result_->did_swap_called + << " did_not_swap_called " << result_->did_not_swap_called + << " result addr " << result_; result_->dtor_called = true; } virtual void DidSwap(CompositorFrameMetadata* metadata) OVERRIDE { base::AutoLock lock(result_->lock); + LOG(ERROR) << "TestSwapPromise::DidSwap " + << " did_swap_called " << result_->did_swap_called + << " did_not_swap_called " << result_->did_not_swap_called + << " result addr " << result_; EXPECT_FALSE(result_->did_swap_called); EXPECT_FALSE(result_->did_not_swap_called); result_->did_swap_called = true; @@ -4455,12 +4443,19 @@ class TestSwapPromise : public SwapPromise { virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE { base::AutoLock lock(result_->lock); + LOG(ERROR) << "TestSwapPromise::DidNotSwap " + << " reason " << reason + << " did_swap_called " << result_->did_swap_called + << " did_not_swap_called " << result_->did_not_swap_called + << " result addr " << result_; EXPECT_FALSE(result_->did_swap_called); EXPECT_FALSE(result_->did_not_swap_called); result_->did_not_swap_called = true; result_->reason = reason; } + virtual int64 TraceId() const OVERRIDE { return 0; } + private: // Not owned. TestSwapPromiseResult* result_; @@ -4538,34 +4533,81 @@ class LayerTreeHostTestBreakSwapPromise : public LayerTreeHostTest { }; // TODO(miletus): Flaky test: crbug.com/393995 -// MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromise); +// Enabled with verbose logging information. +MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromise); -class LayerTreeHostTestBreakSwapPromiseForAbortedCommit +class LayerTreeHostTestBreakSwapPromiseForVisibilityAbortedCommit : public LayerTreeHostTest { protected: - LayerTreeHostTestBreakSwapPromiseForAbortedCommit() : commit_count_(0) {} - virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } - virtual void WillBeginMainFrame() OVERRIDE { + virtual void DidCommit() OVERRIDE { layer_tree_host()->SetDeferCommits(true); layer_tree_host()->SetNeedsCommit(); } virtual void DidDeferCommit() OVERRIDE { layer_tree_host()->SetVisible(false); + scoped_ptr swap_promise( + new TestSwapPromise(&swap_promise_result_)); + layer_tree_host()->QueueSwapPromise(swap_promise.Pass()); layer_tree_host()->SetDeferCommits(false); + } + + virtual void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl, + bool did_handle) OVERRIDE { + EndTest(); + } + + virtual void AfterTest() OVERRIDE { + { + base::AutoLock lock(swap_promise_result_.lock); + EXPECT_FALSE(swap_promise_result_.did_swap_called); + EXPECT_TRUE(swap_promise_result_.did_not_swap_called); + EXPECT_EQ(SwapPromise::COMMIT_FAILS, swap_promise_result_.reason); + EXPECT_TRUE(swap_promise_result_.dtor_called); + } + } + + TestSwapPromiseResult swap_promise_result_; +}; + +MULTI_THREAD_TEST_F( + LayerTreeHostTestBreakSwapPromiseForVisibilityAbortedCommit); + +class LayerTreeHostTestBreakSwapPromiseForContextAbortedCommit + : public LayerTreeHostTest { + protected: + virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } + virtual void DidCommit() OVERRIDE { + if (TestEnded()) + return; + layer_tree_host()->SetDeferCommits(true); + layer_tree_host()->SetNeedsCommit(); + } + + virtual void DidDeferCommit() OVERRIDE { + layer_tree_host()->DidLoseOutputSurface(); scoped_ptr swap_promise( new TestSwapPromise(&swap_promise_result_)); layer_tree_host()->QueueSwapPromise(swap_promise.Pass()); + layer_tree_host()->SetDeferCommits(false); } - virtual void DidCommit() OVERRIDE { PostSetNeedsCommitToMainThread(); } - virtual void BeginMainFrameAbortedOnThread(LayerTreeHostImpl* host_impl, bool did_handle) OVERRIDE { EndTest(); + // This lets the test finally commit and exit. + MainThreadTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&LayerTreeHostTestBreakSwapPromiseForContextAbortedCommit:: + FindOutputSurface, + base::Unretained(this))); + } + + void FindOutputSurface() { + layer_tree_host()->OnCreateAndInitializeOutputSurfaceAttempted(true); } virtual void AfterTest() OVERRIDE { @@ -4578,11 +4620,10 @@ class LayerTreeHostTestBreakSwapPromiseForAbortedCommit } } - int commit_count_; TestSwapPromiseResult swap_promise_result_; }; -MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromiseForAbortedCommit); +MULTI_THREAD_TEST_F(LayerTreeHostTestBreakSwapPromiseForContextAbortedCommit); class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { public: @@ -4591,8 +4632,7 @@ class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { int* set_needs_commit_count, int* set_needs_redraw_count) : SwapPromiseMonitor(layer_tree_host, layer_tree_host_impl), - set_needs_commit_count_(set_needs_commit_count), - set_needs_redraw_count_(set_needs_redraw_count) {} + set_needs_commit_count_(set_needs_commit_count) {} virtual ~SimpleSwapPromiseMonitor() {} @@ -4601,12 +4641,15 @@ class SimpleSwapPromiseMonitor : public SwapPromiseMonitor { } virtual void OnSetNeedsRedrawOnImpl() OVERRIDE { - (*set_needs_redraw_count_)++; + ADD_FAILURE() << "Should not get called on main thread."; + } + + virtual void OnForwardScrollUpdateToMainThreadOnImpl() OVERRIDE { + ADD_FAILURE() << "Should not get called on main thread."; } private: int* set_needs_commit_count_; - int* set_needs_redraw_count_; }; class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest { @@ -4614,6 +4657,9 @@ class LayerTreeHostTestSimpleSwapPromiseMonitor : public LayerTreeHostTest { virtual void BeginTest() OVERRIDE { PostSetNeedsCommitToMainThread(); } virtual void WillBeginMainFrame() OVERRIDE { + if (TestEnded()) + return; + int set_needs_commit_count = 0; int set_needs_redraw_count = 0; @@ -4924,15 +4970,21 @@ class LayerTreeHostTestContinuousPainting : public LayerTreeHostTest { } virtual void BeginTest() OVERRIDE { - // Wait 50x longer than expected. - double milliseconds_per_frame = - 1000 / layer_tree_host()->settings().refresh_rate; - EndTestAfterDelay(50 * kExpectedNumCommits * milliseconds_per_frame); MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind( &LayerTreeHostTestContinuousPainting::EnableContinuousPainting, base::Unretained(this))); + // Wait 50x longer than expected. + double milliseconds_per_frame = + 1000.0 / layer_tree_host()->settings().refresh_rate; + MainThreadTaskRunner()->PostDelayedTask( + FROM_HERE, + base::Bind( + &LayerTreeHostTestContinuousPainting::DisableContinuousPainting, + base::Unretained(this)), + base::TimeDelta::FromMilliseconds(50 * kExpectedNumCommits * + milliseconds_per_frame)); } virtual void Animate(base::TimeTicks monotonic_time) OVERRIDE { @@ -4963,6 +5015,13 @@ class LayerTreeHostTestContinuousPainting : public LayerTreeHostTest { layer_tree_host()->SetDebugState(debug_state); } + void DisableContinuousPainting() { + LayerTreeDebugState debug_state = layer_tree_host()->debug_state(); + debug_state.continuous_painting = false; + layer_tree_host()->SetDebugState(debug_state); + EndTest(); + } + int num_commits_; int num_draws_; const gfx::Size bounds_; diff --git a/cc/trees/layer_tree_host_unittest_animation.cc b/cc/trees/layer_tree_host_unittest_animation.cc index f717c2af9cf61..4706dad07ff37 100644 --- a/cc/trees/layer_tree_host_unittest_animation.cc +++ b/cc/trees/layer_tree_host_unittest_animation.cc @@ -722,62 +722,6 @@ class LayerTreeHostAnimationTestLayerAddedWithAnimation SINGLE_AND_MULTI_THREAD_TEST_F( LayerTreeHostAnimationTestLayerAddedWithAnimation); -class LayerTreeHostAnimationTestContinuousAnimate - : public LayerTreeHostAnimationTest { - public: - LayerTreeHostAnimationTestContinuousAnimate() - : num_commit_complete_(0), - num_draw_layers_(0) { - } - - virtual void SetupTree() OVERRIDE { - LayerTreeHostAnimationTest::SetupTree(); - // Create a fake content layer so we actually produce new content for every - // animation frame. - content_ = FakeContentLayer::Create(&client_); - content_->set_always_update_resources(true); - layer_tree_host()->root_layer()->AddChild(content_); - } - - virtual void BeginTest() OVERRIDE { - PostSetNeedsCommitToMainThread(); - } - - virtual void Animate(base::TimeTicks) OVERRIDE { - if (num_draw_layers_ == 2) - return; - layer_tree_host()->SetNeedsAnimate(); - } - - virtual void Layout() OVERRIDE { - layer_tree_host()->root_layer()->SetNeedsDisplay(); - } - - virtual void CommitCompleteOnThread(LayerTreeHostImpl* tree_impl) OVERRIDE { - if (num_draw_layers_ == 1) - num_commit_complete_++; - } - - virtual void DrawLayersOnThread(LayerTreeHostImpl* impl) OVERRIDE { - num_draw_layers_++; - if (num_draw_layers_ == 2) - EndTest(); - } - - virtual void AfterTest() OVERRIDE { - // Check that we didn't commit twice between first and second draw. - EXPECT_EQ(1, num_commit_complete_); - } - - private: - int num_commit_complete_; - int num_draw_layers_; - FakeContentLayerClient client_; - scoped_refptr content_; -}; - -MULTI_THREAD_TEST_F(LayerTreeHostAnimationTestContinuousAnimate); - class LayerTreeHostAnimationTestCancelAnimateCommit : public LayerTreeHostAnimationTest { public: diff --git a/cc/trees/layer_tree_host_unittest_context.cc b/cc/trees/layer_tree_host_unittest_context.cc index 80898d28e453f..8fd9743f6d11a 100644 --- a/cc/trees/layer_tree_host_unittest_context.cc +++ b/cc/trees/layer_tree_host_unittest_context.cc @@ -200,45 +200,53 @@ class LayerTreeHostContextTestLostContextSucceeds static const TestCase kTests[] = { // Losing the context and failing to recreate it (or losing it again // immediately) a small number of times should succeed. - {1, // times_to_lose_during_commit + { + 1, // times_to_lose_during_commit 0, // times_to_lose_during_draw 0, // times_to_fail_recreate false, // fallback_context_works }, - {0, // times_to_lose_during_commit + { + 0, // times_to_lose_during_commit 1, // times_to_lose_during_draw 0, // times_to_fail_recreate false, // fallback_context_works }, - {1, // times_to_lose_during_commit + { + 1, // times_to_lose_during_commit 0, // times_to_lose_during_draw 3, // times_to_fail_recreate false, // fallback_context_works }, - {0, // times_to_lose_during_commit + { + 0, // times_to_lose_during_commit 1, // times_to_lose_during_draw 3, // times_to_fail_recreate false, // fallback_context_works }, // Losing the context and recreating it any number of times should // succeed. - {10, // times_to_lose_during_commit + { + 10, // times_to_lose_during_commit 0, // times_to_lose_during_draw 0, // times_to_fail_recreate false, // fallback_context_works }, - {0, // times_to_lose_during_commit + { + 0, // times_to_lose_during_commit 10, // times_to_lose_during_draw 0, // times_to_fail_recreate false, // fallback_context_works }, // Losing the context, failing to reinitialize it, and making a fallback // context should work. - {0, // times_to_lose_during_commit + { + 0, // times_to_lose_during_commit 1, // times_to_lose_during_draw 0, // times_to_fail_recreate true, // fallback_context_works - }, }; + }, + }; if (test_case_ >= arraysize(kTests)) return false; diff --git a/cc/trees/layer_tree_host_unittest_delegated.cc b/cc/trees/layer_tree_host_unittest_delegated.cc index 028f4cf35c2f8..1607b71c886f1 100644 --- a/cc/trees/layer_tree_host_unittest_delegated.cc +++ b/cc/trees/layer_tree_host_unittest_delegated.cc @@ -190,11 +190,10 @@ class LayerTreeHostDelegatedTest : public LayerTreeTest { output_rect, output_rect, id, - false, // is_replica 0, // mask_resource_id - damage_rect, gfx::Rect(0, 0, 1, 1), // mask_uv_rect filters, + gfx::Vector2dF(), background_filters); } diff --git a/cc/trees/layer_tree_host_unittest_scroll.cc b/cc/trees/layer_tree_host_unittest_scroll.cc index 499f2d6310857..60ca59b834fdb 100644 --- a/cc/trees/layer_tree_host_unittest_scroll.cc +++ b/cc/trees/layer_tree_host_unittest_scroll.cc @@ -5,11 +5,13 @@ #include "cc/trees/layer_tree_host.h" #include "base/memory/weak_ptr.h" -#include "cc/layers/content_layer.h" #include "cc/layers/layer.h" #include "cc/layers/layer_impl.h" +#include "cc/layers/picture_layer.h" #include "cc/test/fake_content_layer_client.h" #include "cc/test/fake_layer_tree_host_client.h" +#include "cc/test/fake_picture_layer.h" +#include "cc/test/fake_picture_layer_impl.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/layer_tree_test.h" #include "cc/test/test_shared_bitmap_manager.h" @@ -449,7 +451,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { scoped_refptr root_layer = Layer::Create(); root_layer->SetBounds(gfx::Size(10, 10)); - root_scroll_layer_ = ContentLayer::Create(&fake_content_layer_client_); + root_scroll_layer_ = FakePictureLayer::Create(&fake_content_layer_client_); root_scroll_layer_->SetBounds(gfx::Size(110, 110)); root_scroll_layer_->SetPosition(gfx::Point()); @@ -459,7 +461,7 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { root_scroll_layer_->SetIsContainerForFixedPositionLayers(true); root_layer->AddChild(root_scroll_layer_); - child_layer_ = ContentLayer::Create(&fake_content_layer_client_); + child_layer_ = FakePictureLayer::Create(&fake_content_layer_client_); child_layer_->set_did_scroll_callback( base::Bind(&LayerTreeHostScrollTestCaseWithChild::DidScroll, base::Unretained(this))); @@ -540,8 +542,10 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { virtual void DidActivateTreeOnThread(LayerTreeHostImpl* impl) OVERRIDE { LayerImpl* root_impl = impl->active_tree()->root_layer(); - LayerImpl* root_scroll_layer_impl = root_impl->children()[0]; - LayerImpl* child_layer_impl = root_scroll_layer_impl->children()[0]; + FakePictureLayerImpl* root_scroll_layer_impl = + static_cast(root_impl->children()[0]); + FakePictureLayerImpl* child_layer_impl = static_cast( + root_scroll_layer_impl->children()[0]); LayerImpl* expected_scroll_layer_impl = NULL; LayerImpl* expected_no_scroll_layer_impl = NULL; @@ -558,14 +562,11 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { expected_no_scroll_layer_impl->ScrollDelta()); // Ensure device scale factor is affecting the layers. - gfx::Size expected_content_bounds = gfx::ToCeiledSize( - gfx::ScaleSize(root_scroll_layer_impl->bounds(), device_scale_factor_)); - EXPECT_SIZE_EQ(expected_content_bounds, - root_scroll_layer_->content_bounds()); + EXPECT_FLOAT_EQ(device_scale_factor_, + root_scroll_layer_impl->HighResTiling()->contents_scale()); - expected_content_bounds = gfx::ToCeiledSize( - gfx::ScaleSize(child_layer_impl->bounds(), device_scale_factor_)); - EXPECT_SIZE_EQ(expected_content_bounds, child_layer_->content_bounds()); + EXPECT_FLOAT_EQ(device_scale_factor_, + child_layer_impl->HighResTiling()->contents_scale()); switch (impl->active_tree()->source_frame_number()) { case 0: { @@ -644,28 +645,14 @@ class LayerTreeHostScrollTestCaseWithChild : public LayerTreeHostScrollTest { }; TEST_F(LayerTreeHostScrollTestCaseWithChild, - DeviceScaleFactor1_ScrollChild_DirectRenderer_MainThreadPaint) { - device_scale_factor_ = 1.f; - scroll_child_layer_ = true; - RunTest(true, false, false); -} - -TEST_F(LayerTreeHostScrollTestCaseWithChild, - DeviceScaleFactor1_ScrollChild_DirectRenderer_ImplSidePaint) { + DeviceScaleFactor1_ScrollChild_DirectRenderer) { device_scale_factor_ = 1.f; scroll_child_layer_ = true; RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestCaseWithChild, - DeviceScaleFactor1_ScrollChild_DelegatingRenderer_MainThreadPaint) { - device_scale_factor_ = 1.f; - scroll_child_layer_ = true; - RunTest(true, true, false); -} - -TEST_F(LayerTreeHostScrollTestCaseWithChild, - DeviceScaleFactor1_ScrollChild_DelegatingRenderer_ImplSidePaint) { + DeviceScaleFactor1_ScrollChild_DelegatingRenderer) { device_scale_factor_ = 1.f; scroll_child_layer_ = true; RunTest(true, true, true); @@ -728,14 +715,7 @@ TEST_F(LayerTreeHostScrollTestCaseWithChild, } TEST_F(LayerTreeHostScrollTestCaseWithChild, - DeviceScaleFactor2_ScrollRootScrollLayer_DirectRenderer_MainSidePaint) { - device_scale_factor_ = 2.f; - scroll_child_layer_ = false; - RunTest(true, false, false); -} - -TEST_F(LayerTreeHostScrollTestCaseWithChild, - DeviceScaleFactor2_ScrollRootScrollLayer_DirectRenderer_ImplSidePaint) { + DeviceScaleFactor2_ScrollRootScrollLayer_DirectRenderer) { device_scale_factor_ = 2.f; scroll_child_layer_ = false; RunTest(true, false, true); @@ -1093,9 +1073,10 @@ class ThreadCheckingInputHandlerClient : public InputHandlerClient { *received_stop_flinging_ = true; } - virtual void DidOverscroll(const gfx::Vector2dF& accumulated_overscroll, - const gfx::Vector2dF& latest_overscroll_delta) - OVERRIDE { + virtual void DidOverscroll( + const gfx::PointF& causal_event_viewport_point, + const gfx::Vector2dF& accumulated_overscroll, + const gfx::Vector2dF& latest_overscroll_delta) OVERRIDE { if (!task_runner_->BelongsToCurrentThread()) ADD_FAILURE() << "DidOverscroll called on wrong thread"; } @@ -1204,8 +1185,8 @@ class LayerTreeHostScrollTestLayerStructureChange }; Layer* CreateScrollLayer(Layer* parent, FakeLayerScrollClient* client) { - scoped_refptr scroll_layer = - ContentLayer::Create(&fake_content_layer_client_); + scoped_refptr scroll_layer = + PictureLayer::Create(&fake_content_layer_client_); scroll_layer->SetBounds(gfx::Size(110, 110)); scroll_layer->SetPosition(gfx::Point(0, 0)); scroll_layer->SetIsDrawable(true); @@ -1230,12 +1211,12 @@ class LayerTreeHostScrollTestLayerStructureChange }; TEST_F(LayerTreeHostScrollTestLayerStructureChange, ScrollDestroyLayer) { - RunTest(true, false, false); + RunTest(true, false, true); } TEST_F(LayerTreeHostScrollTestLayerStructureChange, ScrollDestroyWholeTree) { - scroll_destroy_whole_tree_ = true; - RunTest(true, false, false); + scroll_destroy_whole_tree_ = true; + RunTest(true, false, true); } } // namespace diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc index 759b80ef3feac..ecb25fa239a02 100644 --- a/cc/trees/layer_tree_impl.cc +++ b/cc/trees/layer_tree_impl.cc @@ -475,7 +475,7 @@ bool LayerTreeImpl::UpdateDrawProperties() { device_scale_factor(), total_page_scale_factor(), page_scale_layer, - MaxTextureSize(), + resource_provider()->max_texture_size(), settings().can_use_lcd_text, can_render_to_separate_surface, settings().layer_transforms_should_scale_layer_contents, @@ -736,10 +736,6 @@ LayerImpl* LayerTreeImpl::FindRecycleTreeLayerById(int id) { return tree->LayerById(id); } -int LayerTreeImpl::MaxTextureSize() const { - return layer_tree_host_impl_->GetRendererCapabilities().max_texture_size; -} - bool LayerTreeImpl::PinchGestureActive() const { return layer_tree_host_impl_->pinch_gesture_active(); } diff --git a/cc/trees/layer_tree_impl.h b/cc/trees/layer_tree_impl.h index fa6d63a978dcd..6ed3f4517e9c3 100644 --- a/cc/trees/layer_tree_impl.h +++ b/cc/trees/layer_tree_impl.h @@ -88,7 +88,6 @@ class CC_EXPORT LayerTreeImpl { LayerImpl* FindActiveTreeLayerById(int id); LayerImpl* FindPendingTreeLayerById(int id); LayerImpl* FindRecycleTreeLayerById(int id); - int MaxTextureSize() const; bool PinchGestureActive() const; base::TimeTicks CurrentFrameTimeTicks() const; base::TimeDelta begin_impl_frame_interval() const; diff --git a/cc/trees/layer_tree_impl_unittest.cc b/cc/trees/layer_tree_impl_unittest.cc index d2153491d6890..7c917e0eaa8ed 100644 --- a/cc/trees/layer_tree_impl_unittest.cc +++ b/cc/trees/layer_tree_impl_unittest.cc @@ -62,7 +62,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -128,7 +128,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleLayerAndHud) { host_impl().SetViewportSize(hud_bounds); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -186,7 +186,7 @@ TEST_F(LayerTreeImplTest, HitTestingForUninvertibleTransform) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); ASSERT_EQ(1u, root_layer()->render_surface()->layer_list().size()); @@ -252,7 +252,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSinglePositionedLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -308,7 +308,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleRotatedLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -377,7 +377,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSinglePerspectiveLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -457,7 +457,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSingleLayerWithScaledContents) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. // The visible content rect for test_layer is actually 100x100, even though @@ -548,7 +548,7 @@ TEST_F(LayerTreeImplTest, HitTestingForSimpleClippedLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -673,7 +673,7 @@ TEST_F(LayerTreeImplTest, HitTestingForMultiClippedRotatedLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. // The grand_child is expected to create a render surface because it @@ -796,7 +796,7 @@ TEST_F(LayerTreeImplTest, HitTestingForNonClippingIntermediateLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -904,7 +904,7 @@ TEST_F(LayerTreeImplTest, HitTestingForMultipleLayers) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_TRUE(child1); @@ -1052,7 +1052,7 @@ TEST_F(LayerTreeImplTest, HitTestingForMultipleLayersAtVaryingDepths) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_TRUE(child1); @@ -1170,7 +1170,7 @@ TEST_F(LayerTreeImplTest, HitTestingRespectsClipParents) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); gfx::Point test_point = gfx::Point(12, 52); LayerImpl* result_layer = @@ -1245,7 +1245,7 @@ TEST_F(LayerTreeImplTest, HitTestingRespectsScrollParents) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); gfx::Point test_point = gfx::Point(12, 52); LayerImpl* result_layer = @@ -1338,7 +1338,7 @@ TEST_F(LayerTreeImplTest, HitTestingForMultipleLayerLists) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_TRUE(child1); @@ -1428,7 +1428,7 @@ TEST_F(LayerTreeImplTest, HitCheckingTouchHandlerRegionsForSingleLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -1517,7 +1517,7 @@ TEST_F(LayerTreeImplTest, host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -1595,7 +1595,7 @@ TEST_F(LayerTreeImplTest, host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -1691,7 +1691,7 @@ TEST_F(LayerTreeImplTest, host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. // The visible content rect for test_layer is actually 100x100, even though @@ -1800,7 +1800,7 @@ TEST_F(LayerTreeImplTest, page_scale_factor, page_scale_factor, page_scale_factor); host_impl().active_tree()->SetRootLayer(root.Pass()); host_impl().active_tree()->SetViewportLayersFromIds(1, 1, Layer::INVALID_ID); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. // The visible content rect for test_layer is actually 100x100, even though @@ -1930,7 +1930,7 @@ TEST_F(LayerTreeImplTest, HitCheckingTouchHandlerRegionsForSimpleClippedLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -2028,7 +2028,7 @@ TEST_F(LayerTreeImplTest, HitCheckingTouchHandlerOverlappingRegions) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -2090,7 +2090,7 @@ TEST_F(LayerTreeImplTest, SelectionBoundsForSingleLayer) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -2191,7 +2191,7 @@ TEST_F(LayerTreeImplTest, SelectionBoundsForPartialOccludedLayers) { host_impl().SetViewportSize(root->bounds()); host_impl().active_tree()->SetRootLayer(root.Pass()); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); @@ -2286,7 +2286,7 @@ TEST_F(LayerTreeImplTest, SelectionBoundsForScaledLayers) { page_scale_factor, page_scale_factor, page_scale_factor); host_impl().active_tree()->SetRootLayer(root.Pass()); host_impl().active_tree()->SetViewportLayersFromIds(1, 1, Layer::INVALID_ID); - host_impl().active_tree()->UpdateDrawProperties(); + host_impl().UpdateNumChildrenAndDrawPropertiesForActiveTree(); // Sanity check the scenario we just created. ASSERT_EQ(1u, RenderSurfaceLayerList().size()); diff --git a/cc/trees/occlusion_tracker_unittest.cc b/cc/trees/occlusion_tracker_unittest.cc index 1a6529bc557f1..e6464a254dcd8 100644 --- a/cc/trees/occlusion_tracker_unittest.cc +++ b/cc/trees/occlusion_tracker_unittest.cc @@ -133,6 +133,8 @@ struct OcclusionTrackerTestMainThreadTypes { } static void DestroyLayer(LayerPtrType* layer) { *layer = NULL; } + + static void RecursiveUpdateNumChildren(LayerType* layerType) {} }; struct OcclusionTrackerTestImplThreadTypes { @@ -162,6 +164,10 @@ struct OcclusionTrackerTestImplThreadTypes { } static void DestroyLayer(LayerPtrType* layer) { layer->reset(); } + + static void RecursiveUpdateNumChildren(LayerType* layer) { + FakeLayerTreeHostImpl::RecursiveUpdateNumChildren(layer); + } }; int OcclusionTrackerTestImplThreadTypes::next_layer_impl_id = 1; @@ -304,6 +310,7 @@ template class OcclusionTrackerTest : public testing::Test { DCHECK(root == root_.get()); DCHECK(!root->render_surface()); + Types::RecursiveUpdateNumChildren(root); LayerTreeHostCommon::CalcDrawPropsImplInputsForTesting inputs( root, root->bounds(), &render_surface_layer_list_impl_); inputs.can_adjust_raster_scales = true; diff --git a/cc/trees/proxy.h b/cc/trees/proxy.h index 6100562f444c5..112c259dc47c0 100644 --- a/cc/trees/proxy.h +++ b/cc/trees/proxy.h @@ -101,7 +101,7 @@ class CC_EXPORT Proxy { virtual void SetDebugState(const LayerTreeDebugState& debug_state) = 0; // Testing hooks - virtual bool CommitPendingForTesting() = 0; + virtual bool MainFrameWillHappenForTesting() = 0; protected: Proxy(scoped_refptr main_task_runner, diff --git a/cc/trees/single_thread_proxy.cc b/cc/trees/single_thread_proxy.cc index 2f67740db632d..f400733001132 100644 --- a/cc/trees/single_thread_proxy.cc +++ b/cc/trees/single_thread_proxy.cc @@ -207,9 +207,13 @@ void SingleThreadProxy::SetNextCommitWaitsForActivation() { void SingleThreadProxy::SetDeferCommits(bool defer_commits) { } -bool SingleThreadProxy::CommitRequested() const { return false; } +bool SingleThreadProxy::CommitRequested() const { + return false; +} -bool SingleThreadProxy::BeginMainFrameRequested() const { return false; } +bool SingleThreadProxy::BeginMainFrameRequested() const { + return false; +} size_t SingleThreadProxy::MaxPartialTextureUpdates() const { return std::numeric_limits::max(); @@ -406,9 +410,8 @@ void SingleThreadProxy::UpdateBackgroundAnimateTicking() { !ShouldComposite() && layer_tree_host_impl_->active_tree()->root_layer()); } -bool SingleThreadProxy::DoComposite( - base::TimeTicks frame_begin_time, - LayerTreeHostImpl::FrameData* frame) { +bool SingleThreadProxy::DoComposite(base::TimeTicks frame_begin_time, + LayerTreeHostImpl::FrameData* frame) { TRACE_EVENT0("cc", "SingleThreadProxy::DoComposite"); DCHECK(!layer_tree_host_->output_surface_lost()); @@ -458,6 +461,8 @@ void SingleThreadProxy::DidSwapFrame() { } } -bool SingleThreadProxy::CommitPendingForTesting() { return false; } +bool SingleThreadProxy::MainFrameWillHappenForTesting() { + return false; +} } // namespace cc diff --git a/cc/trees/single_thread_proxy.h b/cc/trees/single_thread_proxy.h index 2b70d70504c07..685b11a8bd61b 100644 --- a/cc/trees/single_thread_proxy.h +++ b/cc/trees/single_thread_proxy.h @@ -20,7 +20,7 @@ class LayerTreeHost; class LayerTreeHostSingleThreadClient; class CC_EXPORT SingleThreadProxy : public Proxy, - NON_EXPORTED_BASE(LayerTreeHostImplClient) { + NON_EXPORTED_BASE(LayerTreeHostImplClient) { public: static scoped_ptr Create( LayerTreeHost* layer_tree_host, @@ -50,7 +50,7 @@ class CC_EXPORT SingleThreadProxy : public Proxy, virtual void ForceSerializeOnSwapBuffers() OVERRIDE; virtual bool SupportsImplScrolling() const OVERRIDE; virtual void AsValueInto(base::debug::TracedValue* state) const OVERRIDE; - virtual bool CommitPendingForTesting() OVERRIDE; + virtual bool MainFrameWillHappenForTesting() OVERRIDE; // LayerTreeHostImplClient implementation virtual void UpdateRendererCapabilitiesOnImplThread() OVERRIDE; diff --git a/cc/trees/thread_proxy.cc b/cc/trees/thread_proxy.cc index b18b93a63518f..81a90cc405fd9 100644 --- a/cc/trees/thread_proxy.cc +++ b/cc/trees/thread_proxy.cc @@ -52,11 +52,6 @@ class SwapPromiseChecker { } // namespace -struct ThreadProxy::CommitPendingRequest { - CompletionEvent completion; - bool commit_pending; -}; - struct ThreadProxy::SchedulerStateRequest { CompletionEvent completion; scoped_ptr state; @@ -335,14 +330,6 @@ void ThreadProxy::UpdateRendererCapabilitiesOnImplThread() { void ThreadProxy::DidLoseOutputSurfaceOnImplThread() { TRACE_EVENT0("cc", "ThreadProxy::DidLoseOutputSurfaceOnImplThread"); DCHECK(IsImplThread()); - CheckOutputSurfaceStatusOnImplThread(); -} - -void ThreadProxy::CheckOutputSurfaceStatusOnImplThread() { - TRACE_EVENT0("cc", "ThreadProxy::CheckOutputSurfaceStatusOnImplThread"); - DCHECK(IsImplThread()); - if (!impl().layer_tree_host_impl->IsContextLost()) - return; Proxy::MainThreadTaskRunner()->PostTask( FROM_HERE, base::Bind(&ThreadProxy::DidLoseOutputSurface, main_thread_weak_ptr_)); @@ -790,7 +777,8 @@ void ThreadProxy::BeginMainFrame( main().commit_requested = true; main().commit_request_sent_to_impl_thread = true; - layer_tree_host()->ApplyScrollAndScale(*begin_main_frame_state->scroll_info); + layer_tree_host()->ApplyScrollAndScale( + begin_main_frame_state->scroll_info.get()); layer_tree_host()->WillBeginMainFrame(); @@ -1132,9 +1120,6 @@ DrawResult ThreadProxy::DrawSwapInternal(bool forced_draw) { base::Bind(&ThreadProxy::DidCommitAndDrawFrame, main_thread_weak_ptr_)); } - if (draw_frame) - CheckOutputSurfaceStatusOnImplThread(); - if (result == DRAW_SUCCESS) impl().timing_history.DidFinishDrawing(); @@ -1281,6 +1266,12 @@ void ThreadProxy::LayerTreeHostClosedOnImplThread(CompletionEvent* completion) { impl().scheduler.reset(); impl().layer_tree_host_impl.reset(); impl().weak_factory.InvalidateWeakPtrs(); + // We need to explicitly cancel the notifier, since it isn't using weak ptrs. + // TODO(vmpstr): We should see if we can make it use weak ptrs and still keep + // the convention of having a weak ptr factory initialized last. Alternatively + // we should moved the notifier (and RenewTreePriority) to LTHI. See + // crbug.com/411972 + impl().smoothness_priority_expiration_notifier.Cancel(); impl().contents_texture_manager = NULL; completion->Signal(); } @@ -1320,29 +1311,33 @@ void ThreadProxy::AsValueOnImplThread(CompletionEvent* completion, completion->Signal(); } -bool ThreadProxy::CommitPendingForTesting() { +bool ThreadProxy::MainFrameWillHappenForTesting() { DCHECK(IsMainThread()); - CommitPendingRequest commit_pending_request; + CompletionEvent completion; + bool main_frame_will_happen = false; { DebugScopedSetMainThreadBlocked main_thread_blocked(this); Proxy::ImplThreadTaskRunner()->PostTask( FROM_HERE, - base::Bind(&ThreadProxy::CommitPendingOnImplThreadForTesting, + base::Bind(&ThreadProxy::MainFrameWillHappenOnImplThreadForTesting, impl_thread_weak_ptr_, - &commit_pending_request)); - commit_pending_request.completion.Wait(); + &completion, + &main_frame_will_happen)); + completion.Wait(); } - return commit_pending_request.commit_pending; + return main_frame_will_happen; } -void ThreadProxy::CommitPendingOnImplThreadForTesting( - CommitPendingRequest* request) { +void ThreadProxy::MainFrameWillHappenOnImplThreadForTesting( + CompletionEvent* completion, + bool* main_frame_will_happen) { DCHECK(IsImplThread()); - if (impl().layer_tree_host_impl->output_surface()) - request->commit_pending = impl().scheduler->CommitPending(); - else - request->commit_pending = false; - request->completion.Signal(); + if (impl().layer_tree_host_impl->output_surface()) { + *main_frame_will_happen = impl().scheduler->MainFrameForTestingWillHappen(); + } else { + *main_frame_will_happen = false; + } + completion->Signal(); } void ThreadProxy::RenewTreePriority() { diff --git a/cc/trees/thread_proxy.h b/cc/trees/thread_proxy.h index ace01988af772..1621ebc41ec1d 100644 --- a/cc/trees/thread_proxy.h +++ b/cc/trees/thread_proxy.h @@ -172,7 +172,7 @@ class CC_EXPORT ThreadProxy : public Proxy, virtual bool SupportsImplScrolling() const OVERRIDE; virtual void SetDebugState(const LayerTreeDebugState& debug_state) OVERRIDE; virtual void AsValueInto(base::debug::TracedValue* value) const OVERRIDE; - virtual bool CommitPendingForTesting() OVERRIDE; + virtual bool MainFrameWillHappenForTesting() OVERRIDE; // LayerTreeHostImplClient implementation virtual void UpdateRendererCapabilitiesOnImplThread() OVERRIDE; @@ -251,7 +251,6 @@ class CC_EXPORT ThreadProxy : public Proxy, void SendCommitRequestToImplThreadIfNeeded(); // Called on impl thread. - struct CommitPendingRequest; struct SchedulerStateRequest; void StartCommitOnImplThread(CompletionEvent* completion, @@ -272,8 +271,8 @@ class CC_EXPORT ThreadProxy : public Proxy, void LayerTreeHostClosedOnImplThread(CompletionEvent* completion); DrawResult DrawSwapInternal(bool forced_draw); void ForceSerializeOnSwapBuffersOnImplThread(CompletionEvent* completion); - void CheckOutputSurfaceStatusOnImplThread(); - void CommitPendingOnImplThreadForTesting(CommitPendingRequest* request); + void MainFrameWillHappenOnImplThreadForTesting(CompletionEvent* completion, + bool* main_frame_will_happen); void AsValueOnImplThread(CompletionEvent* completion, base::debug::TracedValue* state) const; void SetSwapUsedIncompleteTileOnImplThread(bool used_incomplete_tile); diff --git a/chrome/VERSION b/chrome/VERSION index 0fdfbfc4abddf..afc1a0db73ccc 100644 --- a/chrome/VERSION +++ b/chrome/VERSION @@ -1,4 +1,4 @@ MAJOR=38 MINOR=0 -BUILD=2117 -PATCH=0 +BUILD=2125 +PATCH=66 diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS index 1f00e3954d98f..8ee643b7e2cb6 100644 --- a/chrome/android/java/DEPS +++ b/chrome/android/java/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/web_contents_delegate_android", + "+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks", "+components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core", "+content/public/android/java", "+sync/android/java/src/org/chromium/sync/internal_api/pub", diff --git a/chrome/android/java/res/anim/menu_enter.xml b/chrome/android/java/res/anim/menu_enter.xml index 63194a97931e1..e6c49367c375f 100644 --- a/chrome/android/java/res/anim/menu_enter.xml +++ b/chrome/android/java/res/anim/menu_enter.xml @@ -16,4 +16,8 @@ + \ No newline at end of file diff --git a/chrome/android/java/res/drawable-ldrtl-sw600dp-hdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-ldrtl-sw600dp-hdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..8ea9405ce95c9 Binary files /dev/null and b/chrome/android/java/res/drawable-ldrtl-sw600dp-hdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-ldrtl-sw600dp-mdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-ldrtl-sw600dp-mdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..c25099847830c Binary files /dev/null and b/chrome/android/java/res/drawable-ldrtl-sw600dp-mdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-ldrtl-sw600dp-xhdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-ldrtl-sw600dp-xhdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..4424cb2096dd8 Binary files /dev/null and b/chrome/android/java/res/drawable-ldrtl-sw600dp-xhdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-ldrtl-sw600dp-xxhdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-ldrtl-sw600dp-xxhdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..e7c030de21f72 Binary files /dev/null and b/chrome/android/java/res/drawable-ldrtl-sw600dp-xxhdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-sw600dp-hdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-sw600dp-hdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..90c22748bcde3 Binary files /dev/null and b/chrome/android/java/res/drawable-sw600dp-hdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-sw600dp-mdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-sw600dp-mdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..ddc7409035b20 Binary files /dev/null and b/chrome/android/java/res/drawable-sw600dp-mdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-sw600dp-xhdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-sw600dp-xhdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..4a3f5148f756d Binary files /dev/null and b/chrome/android/java/res/drawable-sw600dp-xhdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable-sw600dp-xxhdpi/edge_menu_bg.9.png b/chrome/android/java/res/drawable-sw600dp-xxhdpi/edge_menu_bg.9.png new file mode 100644 index 0000000000000..da8bc44b36faf Binary files /dev/null and b/chrome/android/java/res/drawable-sw600dp-xxhdpi/edge_menu_bg.9.png differ diff --git a/chrome/android/java/res/drawable/website_settings_reset_cert_decisions.xml b/chrome/android/java/res/drawable/website_settings_reset_cert_decisions.xml new file mode 100644 index 0000000000000..d841e08746991 --- /dev/null +++ b/chrome/android/java/res/drawable/website_settings_reset_cert_decisions.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/chrome/android/java/res/layout/country_item.xml b/chrome/android/java/res/layout/country_item.xml new file mode 100644 index 0000000000000..9c5c895422e2a --- /dev/null +++ b/chrome/android/java/res/layout/country_item.xml @@ -0,0 +1,15 @@ + + + + diff --git a/chrome/android/java/res/layout/country_text.xml b/chrome/android/java/res/layout/country_text.xml new file mode 100644 index 0000000000000..44fd15502975e --- /dev/null +++ b/chrome/android/java/res/layout/country_text.xml @@ -0,0 +1,15 @@ + + + + diff --git a/chrome/android/java/res/layout/distilled_page_prefs_view.xml b/chrome/android/java/res/layout/distilled_page_prefs_view.xml index 94c1e98c03f64..2b134040b3a2b 100644 --- a/chrome/android/java/res/layout/distilled_page_prefs_view.xml +++ b/chrome/android/java/res/layout/distilled_page_prefs_view.xml @@ -9,22 +9,71 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal" + android:orientation="vertical" android:background="@drawable/distilled_page_pref_background" android:padding="10dp" > - + - + + + + + + + + + + + + + + + + + - diff --git a/chrome/android/java/res/values-sw600dp/dimens.xml b/chrome/android/java/res/values-sw600dp/dimens.xml new file mode 100644 index 0000000000000..ba5395e9bc189 --- /dev/null +++ b/chrome/android/java/res/values-sw600dp/dimens.xml @@ -0,0 +1,11 @@ + + + + + + + 6dp + \ No newline at end of file diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml index e7059bc0a2417..55a67dab568cb 100644 --- a/chrome/android/java/res/values-v17/styles.xml +++ b/chrome/android/java/res/values-v17/styles.xml @@ -23,6 +23,7 @@ 16dp 16dp 16sp + sans-serif " + "
" + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, result.text_direction); +} + +TEST_F(FormAutofillTest, DetectTextDirectionFromDirectDIRAttribute) { + LoadHTML("
" + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, result.text_direction); +} + +TEST_F(FormAutofillTest, DetectTextDirectionFromParentStyle) { + LoadHTML("" + "
" + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, result.text_direction); +} + +TEST_F(FormAutofillTest, DetectTextDirectionFromParentDIRAttribute) { + LoadHTML("
" + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, result.text_direction); +} + +TEST_F(FormAutofillTest, DetectTextDirectionWhenStyleAndDIRAttributMixed) { + LoadHTML("" + "
" + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, result.text_direction); +} + +TEST_F(FormAutofillTest, + DetectTextDirectionWhenParentHasBothDIRAttributeAndStyle) { + LoadHTML("" + "
" + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::LEFT_TO_RIGHT, result.text_direction); +} + +TEST_F(FormAutofillTest, DetectTextDirectionWhenAncestorHasInlineStyle) { + LoadHTML("
" + " " + " " + " " + "
"); + + WebFrame* frame = GetMainFrame(); + ASSERT_NE(static_cast(NULL), frame); + + WebElement web_element = frame->document().getElementById("element"); + WebFormControlElement element = web_element.to(); + + FormFieldData result; + WebFormControlElementToFormField(element, autofill::EXTRACT_VALUE, &result); + EXPECT_EQ(base::i18n::RIGHT_TO_LEFT, result.text_direction); +} + TEST_F(FormAutofillTest, WebFormElementToFormData) { - LoadHTML("
" - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " // The below inputs should be ignored - " " - " " + " " + " " "
"); WebFrame* frame = GetMainFrame(); @@ -779,9 +898,9 @@ TEST_F(FormAutofillTest, WebFormElementToFormData) { // We should not be able to serialize a form with too many fillable fields. TEST_F(FormAutofillTest, WebFormElementToFormDataTooManyFields) { std::string html = - "
"; + ""; for (size_t i = 0; i < (autofill::kMaxParseableFields + 1); ++i) { - html += ""; + html += ""; } html += "
"; LoadHTML(html.c_str()); @@ -808,26 +927,26 @@ TEST_F(FormAutofillTest, WebFormElementToFormDataTooManyFields) { TEST_F(FormAutofillTest, ExtractForms) { ExpectJohnSmithLabels( - "
" - " First name: " - " Last name: " - " Email: " - " " + "" + " First name: " + " Last name: " + " Email: " + " " "
"); } TEST_F(FormAutofillTest, ExtractMultipleForms) { - LoadHTML("
" - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " "
" - "
" - " " - " " - " " - " " + "" + " " + " " + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -990,10 +1109,10 @@ TEST_F(FormAutofillTest, OnlyExtractNewForms) { // We should not extract a form if it has too few fillable fields. TEST_F(FormAutofillTest, ExtractFormsTooFewFields) { - LoadHTML("
" - " " - " " - " " + LoadHTML("" + " " + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1007,9 +1126,9 @@ TEST_F(FormAutofillTest, ExtractFormsTooFewFields) { // We should not report additional forms for empty forms. TEST_F(FormAutofillTest, ExtractFormsSkippedForms) { - LoadHTML("
" - " " - " " + LoadHTML("" + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1023,7 +1142,7 @@ TEST_F(FormAutofillTest, ExtractFormsSkippedForms) { // We should not report additional forms for empty forms. TEST_F(FormAutofillTest, ExtractFormsNoFields) { - LoadHTML("
" + LoadHTML("" "
"); WebFrame* web_frame = GetMainFrame(); @@ -1038,12 +1157,12 @@ TEST_F(FormAutofillTest, ExtractFormsNoFields) { // We should not extract a form if it has too few fillable fields. // Make sure radio and checkbox fields don't count. TEST_F(FormAutofillTest, ExtractFormsTooFewFieldsSkipsCheckable) { - LoadHTML("
" - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1058,12 +1177,12 @@ TEST_F(FormAutofillTest, ExtractFormsTooFewFieldsSkipsCheckable) { TEST_F(FormAutofillTest, WebFormElementToFormDataAutocomplete) { { // Form is not auto-completable due to autocomplete=off. - LoadHTML("
" - " " - " " - " " - " " + " " + " " + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1085,14 +1204,14 @@ TEST_F(FormAutofillTest, WebFormElementToFormDataAutocomplete) { { // The firstname element is not auto-completable due to autocomplete=off. - LoadHTML("
" - " " + " " - " " - " " - " " - " " + " " + " " + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1134,13 +1253,13 @@ TEST_F(FormAutofillTest, WebFormElementToFormDataAutocomplete) { } TEST_F(FormAutofillTest, FindFormForInputElement) { - LoadHTML("
" - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1225,16 +1344,16 @@ TEST_F(FormAutofillTest, FindFormForInputElement) { } TEST_F(FormAutofillTest, FindFormForTextAreaElement) { - LoadHTML("
" - " " - " " - " " - " " - " " + " " "
"); WebFrame* web_frame = GetMainFrame(); @@ -1477,27 +1596,27 @@ TEST_F(FormAutofillTest, PreviewForm) { TEST_F(FormAutofillTest, Labels) { ExpectJohnSmithLabels( - "
" - " " - " " - " " - " " - " " - " " - " " + "" + " " + " " + " " + " " + " " + " " + " " "
"); } TEST_F(FormAutofillTest, LabelsWithSpans) { ExpectJohnSmithLabels( - "
" - " " - " " - " " - " " - " " - " " - " " + "" + " " + " " + " " + " " + " " + " " + " " "
"); } @@ -1509,14 +1628,14 @@ TEST_F(FormAutofillTest, LabelsWithSpans) { // label element and apply it to the following input field. TEST_F(FormAutofillTest, InvalidLabels) { ExpectJohnSmithLabels( - "
" - " " - " " - " " - " " - " " - " " - " " + "" + " " + " " + " " + " " + " " + " " + " " "
"); } @@ -1524,90 +1643,90 @@ TEST_F(FormAutofillTest, InvalidLabels) { // element associated with it. TEST_F(FormAutofillTest, OneLabelElement) { ExpectJohnSmithLabels( - "
" + "" " First name:" - " " - " " - " " + " " + " " + " " " Email:" - " " - " " + " " + " " "
"); } TEST_F(FormAutofillTest, LabelsInferredFromText) { ExpectJohnSmithLabels( - "
" + "" " First name:" - " " + " " " Last name:" - " " + " " " Email:" - " " - " " + " " + " " "
"); } TEST_F(FormAutofillTest, LabelsInferredFromParagraph) { ExpectJohnSmithLabels( - "
" - "

First name:

" + "" + "

First name:

" "

Last name:

" - " " + " " "

Email:

" - " " - " " + " " + " " "
"); } TEST_F(FormAutofillTest, LabelsInferredFromBold) { ExpectJohnSmithLabels( - "
" - " First name:" + "" + " First name:" " Last name:" - " " + " " " Email:" - " " - " " + " " + " " "
"); } TEST_F(FormAutofillTest, LabelsInferredPriorToImgOrBr) { ExpectJohnSmithLabels( - "
" - " First name:" + "" + " First name:" " Last name:" - " " + " " " Email:
" - " " - " " + " " + " " "
"); } TEST_F(FormAutofillTest, LabelsInferredFromTableCell) { ExpectJohnSmithLabels( - "
" + "" "" " " " " - " " + " " " " " " " " - " " + " " " " " " " " - " " + " " " " " " " " " " " " "
First name:
Last name:
Email:
" - " " + " " "
" @@ -1616,25 +1735,25 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableCell) { TEST_F(FormAutofillTest, LabelsInferredFromTableCellTH) { ExpectJohnSmithLabels( - "" + "" "" " " " " - " " + " " " " " " " " - " " + " " " " " " " " - " " + " " " " " " " " " " " " "
First name:
Last name:
Email:
" - " " + " " "
" @@ -1657,7 +1776,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableCellNested) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "" + "" "" " " " " " " " " @@ -1682,7 +1801,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableCellNested) { " " " " " " @@ -1694,14 +1813,14 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableCellNested) { " " " " " " " " " " " " " " "
" @@ -1670,7 +1789,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableCellNested) { " " " " - " " + " " " " "
" " " - " " + " " " " "
" " " - " " + " " " " "
" - " " + " " "
" @@ -1725,7 +1844,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableEmptyTDs) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "" + "" "" " " " " " " " " " " " " @@ -1744,7 +1863,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableEmptyTDs) { " " " " " " " " " " @@ -1754,13 +1873,13 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableEmptyTDs) { " " " " " " " " " " " " " " " " "
" @@ -1734,7 +1853,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableEmptyTDs) { " " - " " + " " "
" - " " + " " "
" - " " + " " "
" - " " + " " "
" @@ -1784,32 +1903,32 @@ TEST_F(FormAutofillTest, LabelsInferredFromPreviousTD) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "" + "" "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " "
* First Name" " Bogus" - " " - " " + " " + " " "
* Last Name" - " " + " " "
* Email" - " " + " " "
" - " " + " " "
" @@ -1850,7 +1969,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { control_types.push_back("text"); ExpectLabelsAndTypes( - "" + "" "" " " " " " " " " @@ -1872,7 +1991,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { " " - " " + " " " " " " " " @@ -1881,7 +2000,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { " Last Name" " " " " " " " " @@ -1890,10 +2009,10 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { " Country" " " " " " " @@ -1904,13 +2023,13 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { " " " " " " " " " " " " " " "
" @@ -1860,7 +1979,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { " " " " - " " + " " "
" - " " + " " "
" - " " + " " - " " + " " " " "
" " " - " " + " " "
" - " " + " " "
" @@ -1920,58 +2039,58 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableWithSpecialElements) { TEST_F(FormAutofillTest, LabelsInferredFromTableLabels) { ExpectJohnSmithLabels( - "" + "" "" " " " " " " " " " " " " " " " " " " "
" " " - " " + " " "
" " " - " " + " " "
" " " - " " + " " "
" - "" + "" "
"); } TEST_F(FormAutofillTest, LabelsInferredFromTableTDInterveningElements) { ExpectJohnSmithLabels( - "
" + "" "" " " " " " " " " " " " " " " " " " " "
" " First name:" "
" - " " + " " "
" " Last name:" "
" - " " + " " "
" " Email:" "
" - " " + " " "
" - "" + "" "
"); } @@ -1993,14 +2112,14 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableAdjacentElements) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "
" + "" "" " " " " " " " " " " @@ -2008,7 +2127,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableAdjacentElements) { " *Last Name" " " " " " " " " @@ -2016,12 +2135,12 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableAdjacentElements) { " *Email" " " " " " " " " " " " " "
" " *First Name" " " - " " + " " "
" - " " + " " "
" - " " + " " "
" - " " + " " "
" @@ -2047,7 +2166,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableRow) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "" + "" "" " " " " @@ -2056,18 +2175,18 @@ TEST_F(FormAutofillTest, LabelsInferredFromTableRow) { " " " " " " " " " " " " " " " " " " "
*First Name
" - " " + " " " " - " " + " " " " - " " + " " "
" - " " + " " "
", @@ -2091,19 +2210,19 @@ TEST_F(FormAutofillTest, LabelsInferredFromListItem) { values.push_back(ASCIIToUTF16("1212")); ExpectLabels( - "" + "" "
" "
  • " " Bogus" "
  • " "
  • " " " - " " - " " - " " + " " + " " + " " "
  • " "
  • " - " " + " " "
  • " "
    " "
    ", @@ -2126,7 +2245,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionList) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "
    " + "" "
    " "
    " " " @@ -2141,7 +2260,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionList) { "
    " "
    " " " - " " + " " " " "
    " "
    " @@ -2151,7 +2270,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionList) { "
    " "
    " " " - " " + " " " " "
    " "
    " @@ -2161,12 +2280,12 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionList) { "
    " "
    " " " - " " + " " " " "
    " "
    " "
    " - " " + " " "
    " "
    " "
    ", @@ -2189,14 +2308,14 @@ TEST_F(FormAutofillTest, LabelsInferredWithSameName) { values.push_back(base::string16()); ExpectLabels( - "
    " + "" " Address Line 1:" - " " + " " " Address Line 2:" - " " + " " " Address Line 3:" - " " - " " + " " + " " "
    ", labels, names, values); } @@ -2225,75 +2344,75 @@ TEST_F(FormAutofillTest, LabelsInferredWithImageTags) { values.push_back(base::string16()); ExpectLabels( - "
    " + "" " Phone:" - " " + " " " " " -" " " - " " + " " " " " -" " " - " " + " " " ext.:" - " " - " " - " " + " " + " " + " " "
    ", labels, names, values); } TEST_F(FormAutofillTest, LabelsInferredFromDivTable) { ExpectJohnSmithLabels( - "
    " + "" "
    First name:
    " " " - " " + " " " " "
    " "
    Last name:
    " " " - " " + " " " " "
    " "
    Email:
    " " " - " " + " " " " "
    " - "" + "" "
    "); } TEST_F(FormAutofillTest, LabelsInferredFromDivSiblingTable) { ExpectJohnSmithLabels( - "
    " + "" "
    First name:
    " "
    " " " - " " + " " " " "
    " "
    Last name:
    " "
    " " " - " " + " " " " "
    " "
    Email:
    " "
    " " " - " " + " " " " "
    " - "" + "" "
    "); } TEST_F(FormAutofillTest, LabelsInferredFromDefinitionListRatherThanDivTable) { ExpectJohnSmithLabels( - "
    " + "" "
    This is not a label.
    " "
    " "
    " @@ -2303,7 +2422,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionListRatherThanDivTable) { "
    " "
    " " " - " " + " " " " "
    " "
    " @@ -2313,7 +2432,7 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionListRatherThanDivTable) { "
    " "
    " " " - " " + " " " " "
    " "
    " @@ -2323,12 +2442,12 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionListRatherThanDivTable) { "
    " "
    " " " - " " + " " " " "
    " "
    " "
    " - " " + " " "
    " "
    " "
    " @@ -2336,11 +2455,11 @@ TEST_F(FormAutofillTest, LabelsInferredFromDefinitionListRatherThanDivTable) { } TEST_F(FormAutofillTest, FillFormMaxLength) { - LoadHTML("" - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -2436,11 +2555,11 @@ TEST_F(FormAutofillTest, FillFormMaxLength) { // In this case, the maxlength of the input elements is set to the default // maxlength (defined in WebKit.) TEST_F(FormAutofillTest, FillFormNegativeMaxLength) { - LoadHTML("
    " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -2517,11 +2636,11 @@ TEST_F(FormAutofillTest, FillFormNegativeMaxLength) { } TEST_F(FormAutofillTest, FillFormEmptyName) { - LoadHTML("
    " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -2601,17 +2720,17 @@ TEST_F(FormAutofillTest, FillFormEmptyName) { } TEST_F(FormAutofillTest, FillFormEmptyFormNames) { - LoadHTML("
    " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " "
    " - "
    " - " " - " " - " " - " " + "" + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -2697,16 +2816,16 @@ TEST_F(FormAutofillTest, FillFormEmptyFormNames) { } TEST_F(FormAutofillTest, ThreePartPhone) { - LoadHTML("
    " + LoadHTML("" " Phone:" - " " + " " " -" - " " + " " " -" - " " + " " " ext.:" - " " - " " + " " + " " "
    "); @@ -2754,19 +2873,19 @@ TEST_F(FormAutofillTest, ThreePartPhone) { TEST_F(FormAutofillTest, MaxLengthFields) { - LoadHTML("
    " + LoadHTML("" " Phone:" - " " + " " " -" - " " + " " " -" - " " + " " " ext.:" - " " - " " - " " - " " + " " + " " + " " + " " "
    "); WebFrame* frame = GetMainFrame(); @@ -2830,11 +2949,11 @@ TEST_F(FormAutofillTest, MaxLengthFields) { // profile from the Autofill suggestions popup. The field that is being typed // into should be filled even though it's not technically empty. TEST_F(FormAutofillTest, FillFormNonEmptyField) { - LoadHTML("
    " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -2936,20 +3055,20 @@ TEST_F(FormAutofillTest, FillFormNonEmptyField) { TEST_F(FormAutofillTest, ClearFormWithNode) { LoadHTML( - "
    " - " " - " " - " " - " " - " " - " " - " " - " " + " " - " " - " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3053,16 +3172,16 @@ TEST_F(FormAutofillTest, ClearFormWithNode) { TEST_F(FormAutofillTest, ClearFormWithNodeContainingSelectOne) { LoadHTML( - "
    " - " " - " " - " " + " " + " " - " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3135,13 +3254,13 @@ TEST_F(FormAutofillTest, ClearFormWithNodeContainingSelectOne) { } TEST_F(FormAutofillTest, ClearPreviewedFormWithElement) { - LoadHTML("
    " - " " - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3203,13 +3322,13 @@ TEST_F(FormAutofillTest, ClearPreviewedFormWithElement) { } TEST_F(FormAutofillTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { - LoadHTML("
    " - " " - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3271,13 +3390,13 @@ TEST_F(FormAutofillTest, ClearPreviewedFormWithNonEmptyInitiatingNode) { } TEST_F(FormAutofillTest, ClearPreviewedFormWithAutofilledInitiatingNode) { - LoadHTML("
    " - " " - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3341,12 +3460,12 @@ TEST_F(FormAutofillTest, ClearPreviewedFormWithAutofilledInitiatingNode) { TEST_F(FormAutofillTest, ClearOnlyAutofilledFields) { // Load the form. LoadHTML( - "
    " - " " - " " - " " - " " - " " + "" + " " + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3390,13 +3509,13 @@ TEST_F(FormAutofillTest, ClearOnlyAutofilledFields) { } TEST_F(FormAutofillTest, FormWithNodeIsAutofilled) { - LoadHTML("
    " - " " - " " - " " - " " - " " - " " + LoadHTML("" + " " + " " + " " + " " + " " + " " "
    "); WebFrame* web_frame = GetMainFrame(); @@ -3436,24 +3555,24 @@ TEST_F(FormAutofillTest, MultipleLabelsPerElement) { values.push_back(ASCIIToUTF16("john@example.com")); ExpectLabels( - "
    " - " " - " " - " " - " " - " " - " " - " " - " " - " " - " " + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " "
    ", labels, names, values); } TEST_F(FormAutofillTest, ClickElement) { - LoadHTML("" - ""); + LoadHTML("" + ""); WebFrame* frame = GetMainFrame(); ASSERT_NE(static_cast(NULL), frame); @@ -3465,7 +3584,7 @@ TEST_F(FormAutofillTest, ClickElement) { // Successful retrieval by css selector. clicker.retrieval_method = autofill::WebElementDescriptor::CSS_SELECTOR; - clicker.descriptor = "button[name=\"button\"]"; + clicker.descriptor = "button[name='button']"; EXPECT_TRUE(ClickElement(frame->document(), clicker)); // Unsuccessful retrieval due to invalid CSS selector. @@ -3478,15 +3597,15 @@ TEST_F(FormAutofillTest, ClickElement) { } TEST_F(FormAutofillTest, SelectOneAsText) { - LoadHTML("
    " - " " - " " - " " + " " + " " - " " + " " "
    "); WebFrame* frame = GetMainFrame(); diff --git a/chrome/renderer/autofill/password_generation_agent_browsertest.cc b/chrome/renderer/autofill/password_generation_agent_browsertest.cc index 35eae491ecb4e..60bdaa3efa8a9 100644 --- a/chrome/renderer/autofill/password_generation_agent_browsertest.cc +++ b/chrome/renderer/autofill/password_generation_agent_browsertest.cc @@ -58,7 +58,6 @@ class PasswordGenerationAgentTest : public ChromeRenderViewTest { WebElement element = document.getElementById(WebString::fromUTF8(element_id)); ASSERT_FALSE(element.isNull()); - WebInputElement target_element = element.to(); ExecuteJavaScript( base::StringPrintf("document.getElementById('%s').focus();", element_id).c_str()); @@ -256,4 +255,79 @@ TEST_F(PasswordGenerationAgentTest, AccountCreationFormsDetectedTest) { ExpectPasswordGenerationAvailable("first_password", true); } +TEST_F(PasswordGenerationAgentTest, MaximumOfferSize) { + LoadHTML(kAccountCreationFormHTML); + SetNotBlacklistedMessage(kAccountCreationFormHTML); + SetAccountCreationFormsDetectedMessage(kAccountCreationFormHTML); + ExpectPasswordGenerationAvailable("first_password", true); + + WebDocument document = GetMainFrame()->document(); + WebElement element = + document.getElementById(WebString::fromUTF8("first_password")); + ASSERT_FALSE(element.isNull()); + WebInputElement first_password_element = element.to(); + + // Make a password just under maximum offer size. + first_password_element.setValue( + base::ASCIIToUTF16( + std::string(password_generation_->kMaximumOfferSize - 1, 'a'))); + // Cast to WebAutofillClient where textFieldDidChange() is public. + static_cast(autofill_agent_)->textFieldDidChange( + first_password_element); + // textFieldDidChange posts a task, so we need to wait until it's been + // processed. + base::MessageLoop::current()->RunUntilIdle(); + // There should now be a message to show the UI. + ASSERT_EQ(1u, password_generation_->messages().size()); + EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID, + password_generation_->messages()[0]->type()); + password_generation_->clear_messages(); + + // Simulate a user typing a password just over maximum offer size. + first_password_element.setValue( + base::ASCIIToUTF16( + std::string(password_generation_->kMaximumOfferSize + 1, 'a'))); + // Cast to WebAutofillClient where textFieldDidChange() is public. + static_cast(autofill_agent_)->textFieldDidChange( + first_password_element); + // textFieldDidChange posts a task, so we need to wait until it's been + // processed. + base::MessageLoop::current()->RunUntilIdle(); + // There should now be a message to hide the UI. + ASSERT_EQ(1u, password_generation_->messages().size()); + EXPECT_EQ(AutofillHostMsg_HidePasswordGenerationPopup::ID, + password_generation_->messages()[0]->type()); + password_generation_->clear_messages(); + + // Simulate the user deleting characters. The generation popup should be shown + // again. + first_password_element.setValue( + base::ASCIIToUTF16( + std::string(password_generation_->kMaximumOfferSize, 'a'))); + // Cast to WebAutofillClient where textFieldDidChange() is public. + static_cast(autofill_agent_)->textFieldDidChange( + first_password_element); + // textFieldDidChange posts a task, so we need to wait until it's been + // processed. + base::MessageLoop::current()->RunUntilIdle(); + // There should now be a message to show the UI. + ASSERT_EQ(1u, password_generation_->messages().size()); + EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID, + password_generation_->messages()[0]->type()); + password_generation_->clear_messages(); + + // Change focus. Bubble should be hidden, but that is handled by AutofilAgent, + // so no messages are sent. + ExecuteJavaScript("document.getElementById('username').focus();"); + EXPECT_EQ(0u, password_generation_->messages().size()); + password_generation_->clear_messages(); + + // Focusing the password field will bring up the generation UI again. + ExecuteJavaScript("document.getElementById('first_password').focus();"); + EXPECT_EQ(1u, password_generation_->messages().size()); + EXPECT_EQ(AutofillHostMsg_ShowPasswordGenerationPopup::ID, + password_generation_->messages()[0]->type()); + password_generation_->clear_messages(); +} + } // namespace autofill diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index d2a85a59fc0b1..9eb68b5b6f9f8 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -28,6 +28,9 @@ #include "chrome/common/pepper_permission_util.h" #include "chrome/common/render_messages.h" #include "chrome/common/url_constants.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/grit/locale_settings.h" +#include "chrome/grit/renderer_resources.h" #include "chrome/renderer/benchmarking_extension.h" #include "chrome/renderer/chrome_render_frame_observer.h" #include "chrome/renderer/chrome_render_process_observer.h" @@ -88,9 +91,6 @@ #include "extensions/renderer/dispatcher.h" #include "extensions/renderer/extension_helper.h" #include "extensions/renderer/script_context.h" -#include "grit/generated_resources.h" -#include "grit/locale_settings.h" -#include "grit/renderer_resources.h" #include "ipc/ipc_sync_channel.h" #include "net/base/net_errors.h" #include "ppapi/c/private/ppb_nacl_private.h" @@ -245,6 +245,14 @@ bool ShouldUseJavaScriptSettingForPlugin(const WebPluginInfo& plugin) { return false; } +void IsGuestViewApiAvailableToScriptContext( + bool* api_is_available, + extensions::ScriptContext* context) { + if (context->GetAvailability("guestViewInternal").is_available()) { + *api_is_available = true; + } +} + } // namespace ChromeContentRendererClient::ChromeContentRendererClient() { @@ -520,20 +528,13 @@ bool ChromeContentRendererClient::OverrideCreatePlugin( WebPlugin** plugin) { std::string orig_mime_type = params.mimeType.utf8(); if (orig_mime_type == content::kBrowserPluginMimeType) { - WebDocument document = frame->document(); - const Extension* extension = - GetExtensionByOrigin(document.securityOrigin()); - if (extension) { - const extensions::APIPermission::ID perms[] = { - extensions::APIPermission::kAppView, - extensions::APIPermission::kEmbeddedExtensionOptions, - extensions::APIPermission::kWebView, - }; - for (size_t i = 0; i < arraysize(perms); ++i) { - if (extension->permissions_data()->HasAPIPermission(perms[i])) - return false; - } - } + bool guest_view_api_available = false; + extension_dispatcher_->script_context_set().ForEach( + render_frame->GetRenderView(), + base::Bind(&IsGuestViewApiAvailableToScriptContext, + &guest_view_api_available)); + if (guest_view_api_available) + return false; } ChromeViewHostMsg_GetPluginInfo_Output output; diff --git a/chrome/renderer/content_settings_observer.cc b/chrome/renderer/content_settings_observer.cc index ba6eb9eb9274e..57de0757f4b3e 100644 --- a/chrome/renderer/content_settings_observer.cc +++ b/chrome/renderer/content_settings_observer.cc @@ -13,10 +13,6 @@ #include "content/public/renderer/navigation_state.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" -#include "extensions/common/constants.h" -#include "extensions/common/extension.h" -#include "extensions/common/permissions/permissions_data.h" -#include "extensions/renderer/dispatcher.h" #include "third_party/WebKit/public/platform/WebPermissionCallbacks.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebDataSource.h" @@ -28,6 +24,9 @@ #if defined(ENABLE_EXTENSIONS) #include "chrome/common/extensions/chrome_extension_messages.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/renderer/dispatcher.h" #endif using blink::WebDataSource; @@ -40,7 +39,6 @@ using blink::WebURL; using blink::WebView; using content::DocumentState; using content::NavigationState; -using extensions::APIPermission; namespace { @@ -154,7 +152,9 @@ ContentSettingsObserver::ContentSettingsObserver( : content::RenderFrameObserver(render_frame), content::RenderFrameObserverTracker( render_frame), +#if defined(ENABLE_EXTENSIONS) extension_dispatcher_(extension_dispatcher), +#endif allow_displaying_insecure_content_(false), allow_running_insecure_content_(false), content_setting_rules_(NULL), @@ -429,19 +429,11 @@ bool ContentSettingsObserver::allowWriteToClipboard(bool default_value) { } bool ContentSettingsObserver::allowMutationEvents(bool default_value) { - WebFrame* frame = render_frame()->GetWebFrame(); - WebSecurityOrigin origin = frame->document().securityOrigin(); - const extensions::Extension* extension = GetExtension(origin); - if (extension && extension->is_platform_app()) - return false; - return default_value; + return IsPlatformApp() ? false : default_value; } bool ContentSettingsObserver::allowPushState() { - WebFrame* frame = render_frame()->GetWebFrame(); - WebSecurityOrigin origin = frame->document().securityOrigin(); - const extensions::Extension* extension = GetExtension(origin); - return !extension || !extension->is_platform_app(); + return !IsPlatformApp(); } static void SendInsecureContentSignal(int signal) { @@ -635,6 +627,18 @@ void ContentSettingsObserver::ClearBlockedContentSettings() { cached_script_permissions_.clear(); } +bool ContentSettingsObserver::IsPlatformApp() { +#if defined(ENABLE_EXTENSIONS) + WebFrame* frame = render_frame()->GetWebFrame(); + WebSecurityOrigin origin = frame->document().securityOrigin(); + const extensions::Extension* extension = GetExtension(origin); + return extension && extension->is_platform_app(); +#else + return false; +#endif +} + +#if defined(ENABLE_EXTENSIONS) const extensions::Extension* ContentSettingsObserver::GetExtension( const WebSecurityOrigin& origin) const { if (!EqualsASCII(origin.protocol(), extensions::kExtensionScheme)) @@ -646,6 +650,7 @@ const extensions::Extension* ContentSettingsObserver::GetExtension( return extension_dispatcher_->extensions()->GetByID(extension_id); } +#endif bool ContentSettingsObserver::IsWhitelistedForContentSettings( content::RenderFrame* frame) { @@ -678,8 +683,10 @@ bool ContentSettingsObserver::IsWhitelistedForContentSettings( if (EqualsASCII(origin.protocol(), content::kChromeDevToolsScheme)) return true; // DevTools UI elements should still work. +#if defined(ENABLE_EXTENSIONS) if (EqualsASCII(origin.protocol(), extensions::kExtensionScheme)) return true; +#endif // TODO(creis, fsamuel): Remove this once the concept of swapped out // RenderFrames goes away. diff --git a/chrome/renderer/content_settings_observer.h b/chrome/renderer/content_settings_observer.h index ba7296116eced..1c795007ba729 100644 --- a/chrome/renderer/content_settings_observer.h +++ b/chrome/renderer/content_settings_observer.h @@ -12,7 +12,6 @@ #include "components/content_settings/core/common/content_settings_types.h" #include "content/public/renderer/render_frame_observer.h" #include "content/public/renderer/render_frame_observer_tracker.h" -#include "extensions/common/permissions/api_permission.h" #include "third_party/WebKit/public/web/WebPermissionClient.h" class GURL; @@ -104,11 +103,17 @@ class ContentSettingsObserver // Resets the |content_blocked_| array. void ClearBlockedContentSettings(); + // Whether the observed RenderFrame is for a platform app. + bool IsPlatformApp(); + +#if defined(ENABLE_EXTENSIONS) // If |origin| corresponds to an installed extension, returns that extension. // Otherwise returns NULL. const extensions::Extension* GetExtension( const blink::WebSecurityOrigin& origin) const; +#endif + // Helpers. // True if |frame| contains content that is white-listed for content settings. static bool IsWhitelistedForContentSettings(content::RenderFrame* frame); @@ -116,8 +121,10 @@ class ContentSettingsObserver const blink::WebSecurityOrigin& origin, const GURL& document_url); +#if defined(ENABLE_EXTENSIONS) // Owned by ChromeContentRendererClient and outlive us. extensions::Dispatcher* extension_dispatcher_; +#endif // Insecure content may be permitted for the duration of this render view. bool allow_displaying_insecure_content_; diff --git a/chrome/renderer/content_settings_observer_unittest.cc b/chrome/renderer/content_settings_observer_unittest.cc index cb2f69d5fe031..11505b61fa8b6 100644 --- a/chrome/renderer/content_settings_observer_unittest.cc +++ b/chrome/renderer/content_settings_observer_unittest.cc @@ -6,12 +6,15 @@ #include "chrome/common/url_constants.h" #include "content/public/common/url_constants.h" -#include "extensions/common/constants.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "url/gurl.h" +#if defined(ENABLE_EXTENSIONS) +#include "extensions/common/constants.h" +#endif + using blink::WebSecurityOrigin; typedef testing::Test ContentSettingsObserverTest; @@ -31,11 +34,13 @@ TEST_F(ContentSettingsObserverTest, WhitelistedSchemes) { WebSecurityOrigin::create(chrome_dev_tools_url), GURL())); +#if defined(ENABLE_EXTENSIONS) GURL extension_url = GURL(std::string(extensions::kExtensionScheme).append(end_url)); EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings( WebSecurityOrigin::create(extension_url), GURL())); +#endif GURL file_url("file:///dir/"); EXPECT_TRUE(ContentSettingsObserver::IsWhitelistedForContentSettings( diff --git a/chrome/renderer/extensions/app_window_custom_bindings.cc b/chrome/renderer/extensions/app_window_custom_bindings.cc index 53cef266f1ca4..35dd1bea7adf7 100644 --- a/chrome/renderer/extensions/app_window_custom_bindings.cc +++ b/chrome/renderer/extensions/app_window_custom_bindings.cc @@ -8,6 +8,7 @@ #include "base/command_line.h" #include "chrome/common/chrome_switches.h" +#include "chrome/grit/renderer_resources.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/render_view_observer.h" @@ -18,7 +19,6 @@ #include "extensions/renderer/scoped_persistent.h" #include "extensions/renderer/script_context.h" #include "extensions/renderer/script_context_set.h" -#include "grit/renderer_resources.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebView.h" #include "ui/base/resource/resource_bundle.h" diff --git a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc index 253dd2c13bf98..47600fbbd34a5 100644 --- a/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc +++ b/chrome/renderer/extensions/chrome_extensions_dispatcher_delegate.cc @@ -12,6 +12,7 @@ #include "chrome/common/crash_keys.h" #include "chrome/common/extensions/features/feature_channel.h" #include "chrome/common/url_constants.h" +#include "chrome/grit/renderer_resources.h" #include "chrome/renderer/extensions/app_bindings.h" #include "chrome/renderer/extensions/app_window_custom_bindings.h" #include "chrome/renderer/extensions/automation_internal_custom_bindings.h" @@ -40,7 +41,6 @@ #include "extensions/renderer/native_handler.h" #include "extensions/renderer/resource_bundle_source_map.h" #include "extensions/renderer/script_context.h" -#include "grit/renderer_resources.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebSecurityPolicy.h" @@ -289,6 +289,7 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules( // The API will be automatically set up when first used. if (context_type == extensions::Feature::BLESSED_EXTENSION_CONTEXT || context_type == extensions::Feature::UNBLESSED_EXTENSION_CONTEXT) { + // TODO(fsamuel): Use context->GetAvailability("webViewInternal"). if (extension->permissions_data()->HasAPIPermission( extensions::APIPermission::kWebView)) { module_system->Require("webView"); @@ -313,6 +314,7 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules( } if (context_type == extensions::Feature::BLESSED_EXTENSION_CONTEXT) { + // TODO(fsamuel): Use context->GetAvailability("appViewInternal"). if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableAppView) && extension->permissions_data()->HasAPIPermission( extensions::APIPermission::kAppView)) { @@ -322,10 +324,8 @@ void ChromeExtensionsDispatcherDelegate::RequireAdditionalModules( } } - if (context_type == extensions::Feature::BLESSED_EXTENSION_CONTEXT && - extensions::FeatureSwitch::embedded_extension_options()->IsEnabled() && - extension->permissions_data()->HasAPIPermission( - extensions::APIPermission::kEmbeddedExtensionOptions)) { + if (extensions::FeatureSwitch::embedded_extension_options()->IsEnabled() && + context->GetAvailability("extensionOptionsInternal").is_available()) { module_system->Require("extensionOptions"); } } diff --git a/chrome/renderer/extensions/extension_localization_peer.cc b/chrome/renderer/extensions/extension_localization_peer.cc index 7373995ab1bf0..8c20331d8ab70 100644 --- a/chrome/renderer/extensions/extension_localization_peer.cc +++ b/chrome/renderer/extensions/extension_localization_peer.cc @@ -10,7 +10,6 @@ #include "extensions/common/constants.h" #include "extensions/common/extension_messages.h" #include "extensions/common/message_bundle.h" -#include "grit/generated_resources.h" #include "ipc/ipc_sender.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" @@ -47,8 +46,7 @@ void ExtensionLocalizationPeer::OnUploadProgress( } bool ExtensionLocalizationPeer::OnReceivedRedirect( - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const content::ResourceResponseInfo& info) { NOTREACHED(); return false; diff --git a/chrome/renderer/extensions/extension_localization_peer.h b/chrome/renderer/extensions/extension_localization_peer.h index 4d6c056c7e0a1..95127cd2ef115 100644 --- a/chrome/renderer/extensions/extension_localization_peer.h +++ b/chrome/renderer/extensions/extension_localization_peer.h @@ -34,8 +34,7 @@ class ExtensionLocalizationPeer : public content::RequestPeer { // content::RequestPeer methods. virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; virtual bool OnReceivedRedirect( - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const content::ResourceResponseInfo& info) OVERRIDE; virtual void OnReceivedResponse( const content::ResourceResponseInfo& info) OVERRIDE; diff --git a/chrome/renderer/extensions/extension_localization_peer_unittest.cc b/chrome/renderer/extensions/extension_localization_peer_unittest.cc index 750405b335b10..38ad056241c88 100644 --- a/chrome/renderer/extensions/extension_localization_peer_unittest.cc +++ b/chrome/renderer/extensions/extension_localization_peer_unittest.cc @@ -11,6 +11,7 @@ #include "ipc/ipc_sender.h" #include "ipc/ipc_sync_message.h" #include "net/base/net_errors.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request_status.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -55,9 +56,8 @@ class MockRequestPeer : public content::RequestPeer { virtual ~MockRequestPeer() {} MOCK_METHOD2(OnUploadProgress, void(uint64 position, uint64 size)); - MOCK_METHOD3(OnReceivedRedirect, - bool(const GURL& new_url, - const GURL& new_first_party_for_cookies, + MOCK_METHOD2(OnReceivedRedirect, + bool(const net::RedirectInfo& redirect_info, const content::ResourceResponseInfo& info)); MOCK_METHOD1(OnReceivedResponse, void(const content::ResourceResponseInfo& info)); diff --git a/chrome/renderer/media/cast_rtp_stream.cc b/chrome/renderer/media/cast_rtp_stream.cc index 6d9c68b636ccb..dfcca7ac9c87d 100644 --- a/chrome/renderer/media/cast_rtp_stream.cc +++ b/chrome/renderer/media/cast_rtp_stream.cc @@ -236,14 +236,14 @@ class CastVideoSink : public base::SupportsWeakPtr, public content::MediaStreamVideoSink { public: // |track| provides data for this sink. - // |expected_coded_size| is the expected dimension of the video frame. + // |expected_natural_size| is the expected dimension of the video frame. // |error_callback| is called if video formats don't match. CastVideoSink(const blink::WebMediaStreamTrack& track, - const gfx::Size& expected_coded_size, + const gfx::Size& expected_natural_size, const CastRtpStream::ErrorCallback& error_callback) : track_(track), sink_added_(false), - expected_coded_size_(expected_coded_size), + expected_natural_size_(expected_natural_size), error_callback_(error_callback) {} virtual ~CastVideoSink() { @@ -254,21 +254,21 @@ class CastVideoSink : public base::SupportsWeakPtr, // This static method is used to forward video frames to |frame_input|. static void OnVideoFrame( // These parameters are already bound when callback is created. - const gfx::Size& expected_coded_size, + const gfx::Size& expected_natural_size, const CastRtpStream::ErrorCallback& error_callback, const scoped_refptr frame_input, // These parameters are passed for each frame. const scoped_refptr& frame, const media::VideoCaptureFormat& format, const base::TimeTicks& estimated_capture_time) { - if (frame->coded_size() != expected_coded_size) { + if (frame->natural_size() != expected_natural_size) { error_callback.Run( base::StringPrintf("Video frame resolution does not match config." " Expected %dx%d. Got %dx%d.", - expected_coded_size.width(), - expected_coded_size.height(), - frame->coded_size().width(), - frame->coded_size().height())); + expected_natural_size.width(), + expected_natural_size.height(), + frame->natural_size().width(), + frame->natural_size().height())); return; } @@ -297,7 +297,7 @@ class CastVideoSink : public base::SupportsWeakPtr, this, base::Bind( &CastVideoSink::OnVideoFrame, - expected_coded_size_, + expected_natural_size_, error_callback_, frame_input), track_); @@ -306,7 +306,7 @@ class CastVideoSink : public base::SupportsWeakPtr, private: blink::WebMediaStreamTrack track_; bool sink_added_; - gfx::Size expected_coded_size_; + gfx::Size expected_natural_size_; CastRtpStream::ErrorCallback error_callback_; DISALLOW_COPY_AND_ASSIGN(CastVideoSink); diff --git a/chrome/renderer/net/net_error_helper.cc b/chrome/renderer/net/net_error_helper.cc index 8ccc64e2209a0..b1854d0fea8cf 100644 --- a/chrome/renderer/net/net_error_helper.cc +++ b/chrome/renderer/net/net_error_helper.cc @@ -16,6 +16,7 @@ #include "chrome/common/localized_error.h" #include "chrome/common/net/net_error_info.h" #include "chrome/common/render_messages.h" +#include "chrome/grit/renderer_resources.h" #include "chrome/renderer/net/net_error_page_controller.h" #include "content/public/common/content_client.h" #include "content/public/common/url_constants.h" @@ -25,7 +26,6 @@ #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/resource_fetcher.h" -#include "grit/renderer_resources.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" #include "third_party/WebKit/public/platform/WebURL.h" diff --git a/chrome/renderer/net/net_error_helper_core.cc b/chrome/renderer/net/net_error_helper_core.cc index 434a058e7adb9..94196295869d1 100644 --- a/chrome/renderer/net/net_error_helper_core.cc +++ b/chrome/renderer/net/net_error_helper_core.cc @@ -22,8 +22,8 @@ #include "base/strings/string_util.h" #include "base/values.h" #include "chrome/common/localized_error.h" +#include "chrome/grit/generated_resources.h" #include "content/public/common/url_constants.h" -#include "grit/generated_resources.h" #include "net/base/escape.h" #include "net/base/net_errors.h" #include "net/base/net_util.h" diff --git a/chrome/renderer/page_load_histograms.cc b/chrome/renderer/page_load_histograms.cc index 21dd2efcd547a..83604280256c0 100644 --- a/chrome/renderer/page_load_histograms.cc +++ b/chrome/renderer/page_load_histograms.cc @@ -177,7 +177,8 @@ bool DataReductionProxyWasUsed(WebFrame* frame) { std::replace(headers.begin(), headers.end(), '\n', '\0'); scoped_refptr response_headers( new net::HttpResponseHeaders(headers)); - return data_reduction_proxy::HasDataReductionProxyViaHeader(response_headers); + return data_reduction_proxy::HasDataReductionProxyViaHeader( + response_headers, NULL); } // Returns true if the provided URL is a referrer string that came from diff --git a/chrome/renderer/pepper/pepper_pdf_host.cc b/chrome/renderer/pepper/pepper_pdf_host.cc index 1b1d3de3203f3..399ae4d358ad5 100644 --- a/chrome/renderer/pepper/pepper_pdf_host.cc +++ b/chrome/renderer/pepper/pepper_pdf_host.cc @@ -7,13 +7,13 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/printing/print_web_view_helper.h" +#include "content/app/strings/grit/content_strings.h" #include "content/public/common/referrer.h" #include "content/public/renderer/pepper_plugin_instance.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "content/public/renderer/renderer_ppapi_host.h" #include "grit/webkit_resources.h" -#include "grit/webkit_strings.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/host_message_context.h" #include "ppapi/host/ppapi_host.h" diff --git a/chrome/renderer/pepper/ppb_pdf_impl.cc b/chrome/renderer/pepper/ppb_pdf_impl.cc index aadb9d957b6fb..16317adf2e728 100644 --- a/chrome/renderer/pepper/ppb_pdf_impl.cc +++ b/chrome/renderer/pepper/ppb_pdf_impl.cc @@ -11,13 +11,13 @@ #include "build/build_config.h" #include "chrome/common/render_messages.h" #include "chrome/renderer/printing/print_web_view_helper.h" +#include "content/app/strings/grit/content_strings.h" #include "content/public/common/child_process_sandbox_support_linux.h" #include "content/public/common/referrer.h" #include "content/public/renderer/pepper_plugin_instance.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" #include "grit/webkit_resources.h" -#include "grit/webkit_strings.h" #include "ppapi/c/pp_resource.h" #include "ppapi/c/private/ppb_pdf.h" #include "ppapi/c/trusted/ppb_browser_font_trusted.h" diff --git a/chrome/renderer/plugins/DEPS b/chrome/renderer/plugins/DEPS index 39298501524be..7c86850fada0e 100644 --- a/chrome/renderer/plugins/DEPS +++ b/chrome/renderer/plugins/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+gin", "+third_party/widevine", + "+webkit/grit", ] diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc index 0d606aa56a5f4..ae6989af7bf11 100644 --- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc +++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc @@ -8,17 +8,17 @@ #include "base/values.h" #include "chrome/common/prerender_messages.h" #include "chrome/common/render_messages.h" +#include "chrome/grit/generated_resources.h" +#include "chrome/grit/renderer_resources.h" #include "chrome/renderer/chrome_content_renderer_client.h" #include "chrome/renderer/custom_menu_commands.h" #include "chrome/renderer/plugins/plugin_uma.h" +#include "content/app/strings/grit/content_strings.h" #include "content/public/common/context_menu_params.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "gin/handle.h" #include "gin/object_template_builder.h" -#include "grit/generated_resources.h" -#include "grit/renderer_resources.h" -#include "grit/webkit_strings.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebInputEvent.h" #include "third_party/WebKit/public/web/WebKit.h" diff --git a/chrome/renderer/printing/print_web_view_helper.cc b/chrome/renderer/printing/print_web_view_helper.cc index 420af085b49e2..5e1328f2ab0bb 100644 --- a/chrome/renderer/printing/print_web_view_helper.cc +++ b/chrome/renderer/printing/print_web_view_helper.cc @@ -19,12 +19,12 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/print_messages.h" #include "chrome/common/render_messages.h" +#include "chrome/grit/browser_resources.h" #include "chrome/renderer/prerender/prerender_helper.h" #include "content/public/common/web_preferences.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" #include "content/public/renderer/render_view.h" -#include "grit/browser_resources.h" #include "net/base/escape.h" #include "printing/metafile.h" #include "printing/metafile_impl.h" @@ -820,8 +820,10 @@ void PrintWebViewHelper::DidStartLoading() { void PrintWebViewHelper::DidStopLoading() { is_loading_ = false; - if (!on_stop_loading_closure_.is_null()) + if (!on_stop_loading_closure_.is_null()) { on_stop_loading_closure_.Run(); + on_stop_loading_closure_.Reset(); + } } // Prints |frame| which called window.print(). @@ -1744,11 +1746,11 @@ void PrintWebViewHelper::RequestPrintPreview(PrintPreviewRequestType type) { } case PRINT_PREVIEW_USER_INITIATED_SELECTION: { DCHECK(has_selection); + DCHECK(!GetPlugin(print_preview_context_.source_frame())); params.selection_only = has_selection; break; } case PRINT_PREVIEW_USER_INITIATED_CONTEXT_NODE: { - // Same situation as in PRINT_PREVIEW_USER_INITIATED_ENTIRE_FRAME. if (is_loading_ && GetPlugin(print_preview_context_.source_frame())) { on_stop_loading_closure_ = base::Bind(&PrintWebViewHelper::RequestPrintPreview, diff --git a/chrome/renderer/resources/extensions/app_view.js b/chrome/renderer/resources/extensions/app_view.js index 4b2296294dafc..e73b53a3e3be9 100644 --- a/chrome/renderer/resources/extensions/app_view.js +++ b/chrome/renderer/resources/extensions/app_view.js @@ -39,14 +39,15 @@ AppViewInternal.prototype.createBrowserPluginNode = function() { return browserPluginNode; }; -AppViewInternal.prototype.connect = function(app, callback) { - var params = { - 'appId': app +AppViewInternal.prototype.connect = function(app, data, callback) { + var createParams = { + 'appId': app, + 'data': data }; var self = this; GuestViewInternal.createGuest( 'appview', - params, + createParams, function(instanceId) { if (!instanceId) { self.browserPluginNode.style.visibility = 'hidden'; diff --git a/chrome/renderer/resources/extensions/app_window_custom_bindings.js b/chrome/renderer/resources/extensions/app_window_custom_bindings.js index c666d32557717..812d3d6c6af54 100644 --- a/chrome/renderer/resources/extensions/app_window_custom_bindings.js +++ b/chrome/renderer/resources/extensions/app_window_custom_bindings.js @@ -243,7 +243,7 @@ appWindow.registerCustomHook(function(bindingsAPI) { }; AppWindow.prototype.alphaEnabled = function() { return appWindowData.alphaEnabled; - } + }; AppWindow.prototype.handleWindowFirstShownForTests = function(callback) { // This allows test apps to get have their callback run even if they // call this after the first show has happened. diff --git a/chrome/renderer/resources/extensions/extension_options.js b/chrome/renderer/resources/extensions/extension_options.js index b459db7c13666..854b550e19a10 100644 --- a/chrome/renderer/resources/extensions/extension_options.js +++ b/chrome/renderer/resources/extensions/extension_options.js @@ -26,6 +26,13 @@ function ExtensionOptionsInternal(extensionoptionsNode) { // on* Event handlers. this.eventHandlers = {}; + + // setupEventProperty is normally called in extension_options_events.js to + // register events, but the createfailed event is registered here because + // the event is fired from here instead of through + // extension_options_events.js. + this.setupEventProperty('createfailed'); + new ExtensionOptionsEvents(this, this.viewInstanceId); this.setupNodeProperties(); @@ -62,21 +69,13 @@ ExtensionOptionsInternal.prototype.createGuest = function() { params, function(instanceId) { if (instanceId == 0) { + // Fire a createfailed event here rather than in ExtensionOptionsGuest + // because the guest will not be created, and cannot fire an event. this.initCalled = false; + var createFailedEvent = new Event('createfailed', { bubbles: true }); + this.dispatchEvent(createFailedEvent); } else { this.attachWindow(instanceId); - GuestViewInternal.setAutoSize(this.instanceId, { - 'enableAutoSize': - this.extensionoptionsNode.hasAttribute('autosize'), - 'min': { - 'width': parseInt(this.minwidth || 0), - 'height': parseInt(this.minheight || 0) - }, - 'max': { - 'width': parseInt(this.maxwidth || 0), - 'height': parseInt(this.maxheight || 0) - } - }); } }.bind(this)); }; @@ -142,13 +141,8 @@ ExtensionOptionsInternal.prototype.onSizeChanged = function(width, height) { ExtensionOptionsInternal.prototype.parseExtensionAttribute = function() { if (this.extensionoptionsNode.hasAttribute('extension')) { - var extensionId = this.extensionoptionsNode.getAttribute('extension'); - // Only allow extensions to embed their own options page (if it has one). - if (chrome.runtime.id == extensionId && - chrome.runtime.getManifest().hasOwnProperty('options_page')) { - this.extensionId = extensionId; - return true; - } + this.extensionId = this.extensionoptionsNode.getAttribute('extension'); + return true; } return false; }; @@ -157,20 +151,19 @@ ExtensionOptionsInternal.prototype.parseExtensionAttribute = function() { // an event handler. ExtensionOptionsInternal.prototype.setupEventProperty = function(eventName) { var propertyName = 'on' + eventName.toLowerCase(); - var self = this; var extensionoptionsNode = this.extensionoptionsNode; Object.defineProperty(extensionoptionsNode, propertyName, { get: function() { - return self.eventHandlers[propertyName]; - }, + return this.eventHandlers[propertyName]; + }.bind(this), set: function(value) { - if (self.eventHandlers[propertyName]) + if (this.eventHandlers[propertyName]) extensionoptionsNode.removeEventListener( - eventName, self.eventHandlers[propertyName]); - self.eventHandlers[propertyName] = value; + eventName, this.eventHandlers[propertyName]); + this.eventHandlers[propertyName] = value; if (value) extensionoptionsNode.addEventListener(eventName, value); - }, + }.bind(this), enumerable: true }); }; diff --git a/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc b/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc index 0cba71167490d..de488f4d59952 100644 --- a/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc +++ b/chrome/renderer/safe_browsing/phishing_term_feature_extractor.cc @@ -21,7 +21,6 @@ #include "chrome/renderer/safe_browsing/features.h" #include "chrome/renderer/safe_browsing/murmurhash3_util.h" #include "crypto/sha2.h" -#include "ui/base/l10n/l10n_util.h" namespace safe_browsing { diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc index 4adb387ab32c6..7c898c5b27432 100644 --- a/chrome/renderer/searchbox/searchbox.cc +++ b/chrome/renderer/searchbox/searchbox.cc @@ -17,12 +17,10 @@ #include "chrome/renderer/searchbox/searchbox_extension.h" #include "components/favicon_base/favicon_types.h" #include "content/public/renderer/render_view.h" -#include "grit/renderer_resources.h" #include "net/base/escape.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebView.h" -#include "ui/base/resource/resource_bundle.h" #include "url/gurl.h" namespace { diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc index c94966757078a..a9030b8836664 100644 --- a/chrome/renderer/searchbox/searchbox_extension.cc +++ b/chrome/renderer/searchbox/searchbox_extension.cc @@ -13,10 +13,10 @@ #include "chrome/common/instant_types.h" #include "chrome/common/ntp_logging_events.h" #include "chrome/common/url_constants.h" +#include "chrome/grit/renderer_resources.h" #include "chrome/renderer/searchbox/searchbox.h" #include "content/public/renderer/render_view.h" #include "extensions/common/extension.h" -#include "grit/renderer_resources.h" #include "third_party/WebKit/public/platform/WebURLRequest.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" diff --git a/chrome/renderer/security_filter_peer.cc b/chrome/renderer/security_filter_peer.cc index 4a2ad8b1d169f..e43e7ec7edc68 100644 --- a/chrome/renderer/security_filter_peer.cc +++ b/chrome/renderer/security_filter_peer.cc @@ -6,7 +6,7 @@ #include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" -#include "grit/generated_resources.h" +#include "chrome/grit/generated_resources.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" #include "ui/base/l10n/l10n_util.h" @@ -69,8 +69,7 @@ void SecurityFilterPeer::OnUploadProgress(uint64 position, uint64 size) { } bool SecurityFilterPeer::OnReceivedRedirect( - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const content::ResourceResponseInfo& info) { NOTREACHED(); return false; diff --git a/chrome/renderer/security_filter_peer.h b/chrome/renderer/security_filter_peer.h index 8fa5749702391..792fba46d73f7 100644 --- a/chrome/renderer/security_filter_peer.h +++ b/chrome/renderer/security_filter_peer.h @@ -14,7 +14,7 @@ // unsafe resources (such as mixed-content resource). // Call the factory method CreateSecurityFilterPeer() to obtain an instance of // SecurityFilterPeer based on the original Peer. -// NOTE: subclasses should insure they delete themselves at the end of the +// NOTE: subclasses should ensure they delete themselves at the end of the // OnReceiveComplete call. class SecurityFilterPeer : public content::RequestPeer { public: @@ -32,8 +32,7 @@ class SecurityFilterPeer : public content::RequestPeer { // content::RequestPeer methods. virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; virtual bool OnReceivedRedirect( - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const content::ResourceResponseInfo& info) OVERRIDE; virtual void OnReceivedResponse( const content::ResourceResponseInfo& info) OVERRIDE; diff --git a/chrome/renderer/spellchecker/spellcheck_unittest.cc b/chrome/renderer/spellchecker/spellcheck_unittest.cc index 68ff023ee5bf0..302cd2565c389 100644 --- a/chrome/renderer/spellchecker/spellcheck_unittest.cc +++ b/chrome/renderer/spellchecker/spellcheck_unittest.cc @@ -15,7 +15,6 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/web/WebTextCheckingCompletion.h" #include "third_party/WebKit/public/web/WebTextCheckingResult.h" -#include "ui/base/l10n/l10n_util.h" namespace { @@ -127,7 +126,7 @@ class MockTextCheckingCompletion : public blink::WebTextCheckingCompletion { blink::WebVector last_results_; }; -// Operates unit tests for the webkit_glue::SpellCheckWord() function +// Operates unit tests for the content::SpellCheck::SpellCheckWord() function // with the US English dictionary. // The unit tests in this function consist of: // * Tests for the function with empty strings; diff --git a/chrome/renderer/web_apps.cc b/chrome/renderer/web_apps.cc index 736015b46284c..2c9801ed07988 100644 --- a/chrome/renderer/web_apps.cc +++ b/chrome/renderer/web_apps.cc @@ -12,12 +12,11 @@ #include "base/strings/string16.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/web_application_info.h" -#include "grit/common_resources.h" -#include "grit/generated_resources.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/web/WebDocument.h" @@ -25,8 +24,6 @@ #include "third_party/WebKit/public/web/WebFrame.h" #include "third_party/WebKit/public/web/WebNode.h" #include "third_party/WebKit/public/web/WebNodeList.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/size.h" #include "url/gurl.h" diff --git a/chrome/service/DEPS b/chrome/service/DEPS index be16d49a5e951..8a4c07a4e6d1c 100644 --- a/chrome/service/DEPS +++ b/chrome/service/DEPS @@ -1,4 +1,4 @@ include_rules = [ - # For generated headers. - "+grit", + "+chrome/grit", # For generated headers. + "+sandbox/win/src", ] diff --git a/chrome/service/cloud_print/cloud_print_connector.cc b/chrome/service/cloud_print/cloud_print_connector.cc index 59aa5ddf56d6f..4abc4b4bf8562 100644 --- a/chrome/service/cloud_print/cloud_print_connector.cc +++ b/chrome/service/cloud_print/cloud_print_connector.cc @@ -10,13 +10,14 @@ #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_split.h" +#include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/common/cloud_print/cloud_print_constants.h" #include "chrome/common/cloud_print/cloud_print_helpers.h" +#include "chrome/grit/generated_resources.h" #include "chrome/service/cloud_print/cloud_print_service_helpers.h" -#include "grit/generated_resources.h" #include "net/base/mime_util.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chrome/service/cloud_print/cloud_print_proxy_backend.cc b/chrome/service/cloud_print/cloud_print_proxy_backend.cc index 228aba7b4c5c4..20c72465e1d0e 100644 --- a/chrome/service/cloud_print/cloud_print_proxy_backend.cc +++ b/chrome/service/cloud_print/cloud_print_proxy_backend.cc @@ -12,6 +12,7 @@ #include "base/compiler_specific.h" #include "base/metrics/histogram.h" #include "base/rand_util.h" +#include "base/strings/string_util.h" #include "base/values.h" #include "chrome/common/cloud_print/cloud_print_constants.h" #include "chrome/service/cloud_print/cloud_print_auth.h" @@ -24,11 +25,9 @@ #include "components/cloud_devices/common/cloud_devices_switches.h" #include "google_apis/gaia/gaia_oauth_client.h" #include "google_apis/gaia/gaia_urls.h" -#include "grit/generated_resources.h" #include "jingle/notifier/base/notifier_options.h" #include "jingle/notifier/listener/push_client.h" #include "jingle/notifier/listener/push_client_observer.h" -#include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" namespace cloud_print { diff --git a/chrome/service/cloud_print/print_system_cups.cc b/chrome/service/cloud_print/print_system_cups.cc index 9b3ec232d4ebb..41445175a9b80 100644 --- a/chrome/service/cloud_print/print_system_cups.cc +++ b/chrome/service/cloud_print/print_system_cups.cc @@ -28,11 +28,9 @@ #include "chrome/common/cloud_print/cloud_print_constants.h" #include "chrome/common/crash_keys.h" #include "chrome/service/cloud_print/cloud_print_service_helpers.h" -#include "grit/generated_resources.h" #include "printing/backend/cups_helper.h" #include "printing/backend/print_backend.h" #include "printing/backend/print_backend_consts.h" -#include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" namespace { diff --git a/chrome/service/cloud_print/print_system_win.cc b/chrome/service/cloud_print/print_system_win.cc index 34d158f1539eb..a0939f564dd0e 100644 --- a/chrome/service/cloud_print/print_system_win.cc +++ b/chrome/service/cloud_print/print_system_win.cc @@ -20,12 +20,10 @@ #include "chrome/service/cloud_print/cdd_conversion_win.h" #include "chrome/service/service_process.h" #include "chrome/service/service_utility_process_host.h" -#include "grit/generated_resources.h" #include "printing/backend/win_helper.h" #include "printing/emf_win.h" #include "printing/page_range.h" #include "printing/printing_utils.h" -#include "ui/base/l10n/l10n_util.h" namespace cloud_print { diff --git a/chrome/service/cloud_print/printer_job_handler.cc b/chrome/service/cloud_print/printer_job_handler.cc index f92ca1b38bf10..b53c13c334008 100644 --- a/chrome/service/cloud_print/printer_job_handler.cc +++ b/chrome/service/cloud_print/printer_job_handler.cc @@ -15,9 +15,9 @@ #include "base/values.h" #include "chrome/common/cloud_print/cloud_print_constants.h" #include "chrome/common/cloud_print/cloud_print_helpers.h" +#include "chrome/grit/generated_resources.h" #include "chrome/service/cloud_print/cloud_print_service_helpers.h" #include "chrome/service/cloud_print/job_status_updater.h" -#include "grit/generated_resources.h" #include "net/base/mime_util.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc index d6cb9d418ccc3..fe4fe6d703a68 100644 --- a/chrome/service/service_process.cc +++ b/chrome/service/service_process.cc @@ -23,12 +23,12 @@ #include "chrome/common/env_vars.h" #include "chrome/common/pref_names.h" #include "chrome/common/service_process_util.h" +#include "chrome/grit/chromium_strings.h" +#include "chrome/grit/generated_resources.h" #include "chrome/service/cloud_print/cloud_print_proxy.h" #include "chrome/service/net/service_url_request_context_getter.h" #include "chrome/service/service_ipc_server.h" #include "chrome/service/service_process_prefs.h" -#include "grit/chromium_strings.h" -#include "grit/generated_resources.h" #include "net/base/network_change_notifier.h" #include "net/url_request/url_fetcher.h" #include "ui/base/l10n/l10n_util.h" @@ -177,7 +177,8 @@ bool ServiceProcess::Initialize(base::MessageLoopForUI* message_loop, if (locale.empty()) locale = kDefaultServiceProcessLocale; } - ResourceBundle::InitSharedInstanceWithLocale(locale, NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + locale, NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); PrepareRestartOnCrashEnviroment(command_line); diff --git a/chrome/service/service_utility_process_host.cc b/chrome/service/service_utility_process_host.cc index 96cd0d1465ccf..b9764484e3e4d 100644 --- a/chrome/service/service_utility_process_host.cc +++ b/chrome/service/service_utility_process_host.cc @@ -32,6 +32,7 @@ #include "content/public/common/sandbox_init.h" #include "content/public/common/sandboxed_process_launcher_delegate.h" #include "printing/emf_win.h" +#include "sandbox/win/src/sandbox_policy_base.h" namespace { @@ -49,6 +50,13 @@ class ServiceSandboxedProcessLauncherDelegate *exposed_dir = exposed_dir_; } + virtual void PreSpawnTarget(sandbox::TargetPolicy* policy, + bool* success) OVERRIDE { + // Service process may run as windows service and it fails to create a + // window station. + policy->SetAlternateDesktop(false); + } + private: base::FilePath exposed_dir_; }; @@ -72,6 +80,7 @@ enum ServiceUtilityProcessHostEvent { SERVICE_UTILITY_SEMANTIC_CAPS_REQUEST, SERVICE_UTILITY_SEMANTIC_CAPS_SUCCEEDED, SERVICE_UTILITY_SEMANTIC_CAPS_FAILED, + SERVICE_UTILITY_FAILED_TO_START, SERVICE_UTILITY_EVENT_MAX, }; } // namespace @@ -179,6 +188,9 @@ bool ServiceUtilityProcessHost::StartProcess( SERVICE_UTILITY_EVENT_MAX); return true; } + UMA_HISTOGRAM_ENUMERATION("CloudPrint.ServiceUtilityProcessHostEvent", + SERVICE_UTILITY_FAILED_TO_START, + SERVICE_UTILITY_EVENT_MAX); return false; } diff --git a/chrome/test/DEPS b/chrome/test/DEPS index a3fac31973af1..db5b57fe42f9e 100644 --- a/chrome/test/DEPS +++ b/chrome/test/DEPS @@ -3,6 +3,7 @@ include_rules = [ # rely on components. "+ash", "+chrome", + "+chrome/grit", # For generated headers "+chromeos", "+components", "+extensions", @@ -15,7 +16,7 @@ include_rules = [ "+content/public", "+gin/public", - "+grit", # For generated headers + "+grit", # For generated headers. TODO(thestig): Remove. "+media/base", "+mojo/embedder", "+sandbox/win/tests", diff --git a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java index 0133f42efd5a7..bbdb83c924d90 100644 --- a/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java +++ b/chrome/test/android/javatests/src/org/chromium/chrome/test/util/InfoBarTestAnimationListener.java @@ -15,7 +15,7 @@ */ public class InfoBarTestAnimationListener implements InfoBarContainer.InfoBarAnimationListener { - private static long WAIT_MILLIS = TimeUnit.SECONDS.toMillis(5); + private static final long WAIT_MILLIS = TimeUnit.SECONDS.toMillis(5); private static class ConditionalWait { private volatile Boolean mCondition; @@ -33,7 +33,7 @@ private static class ConditionalWait { * @return true if the condition becomes true before the specified {@code millis}. */ public boolean waitAndExpire(long millis) throws InterruptedException { - synchronized(mLock) { + synchronized (mLock) { while (!mCondition && millis > 0) { long start = SystemClock.elapsedRealtime(); mLock.wait(millis); @@ -46,7 +46,7 @@ public boolean waitAndExpire(long millis) throws InterruptedException { } public void set(boolean value) { - synchronized(mLock) { + synchronized (mLock) { mCondition = value; if (value) { mLock.notify(); diff --git a/chrome/test/base/browser_with_test_window_test.h b/chrome/test/base/browser_with_test_window_test.h index cc5cde80548ae..cb65ebcb4d3e0 100644 --- a/chrome/test/base/browser_with_test_window_test.h +++ b/chrome/test/base/browser_with_test_window_test.h @@ -16,7 +16,7 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_CHROMEOS) -#include "chrome/browser/chromeos/login/users/user_manager.h" +#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #endif diff --git a/chrome/test/base/chrome_unit_test_suite.cc b/chrome/test/base/chrome_unit_test_suite.cc index 6d8b4efa7b700..22f518893b4fd 100644 --- a/chrome/test/base/chrome_unit_test_suite.cc +++ b/chrome/test/base/chrome_unit_test_suite.cc @@ -121,10 +121,7 @@ void ChromeUnitTestSuite::InitializeProviders() { chrome::RegisterPathProvider(); content::RegisterPathProvider(); ui::RegisterPathProvider(); - - base::FilePath user_data; - if (PathService::Get(chrome::DIR_USER_DATA, &user_data)) - component_updater::RegisterPathProvider(user_data); + component_updater::RegisterPathProvider(chrome::DIR_USER_DATA); #if defined(OS_CHROMEOS) chromeos::RegisterPathProvider(); @@ -149,7 +146,8 @@ void ChromeUnitTestSuite::InitializeProviders() { void ChromeUnitTestSuite::InitializeResourceBundle() { // Force unittests to run using en-US so if we test against string // output, it'll pass regardless of the system language. - ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); base::FilePath resources_pack_path; #if defined(OS_MACOSX) && !defined(OS_IOS) PathService::Get(base::DIR_MODULE, &resources_pack_path); diff --git a/chrome/test/base/in_process_browser_test.cc b/chrome/test/base/in_process_browser_test.cc index 9fa29b50bb5f1..220865d3bb19e 100644 --- a/chrome/test/base/in_process_browser_test.cc +++ b/chrome/test/base/in_process_browser_test.cc @@ -70,6 +70,10 @@ #include "components/storage_monitor/test_storage_monitor.h" #endif +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/input_method/input_method_configuration.h" +#endif + namespace { // Passed as value of kTestType. @@ -176,6 +180,8 @@ void InProcessBrowserTest::SetUp() { // Make sure that the log directory exists. base::FilePath log_dir = logging::GetSessionLogFile(*command_line).DirName(); base::CreateDirectory(log_dir); + // Disable IME extension loading to avoid many browser tests failures. + chromeos::input_method::DisableExtensionLoading(); #endif // defined(OS_CHROMEOS) #if defined(OS_MACOSX) diff --git a/chrome/test/base/interactive_test_utils.h b/chrome/test/base/interactive_test_utils.h index c7ada3cd56f94..0779a4f862ee7 100644 --- a/chrome/test/base/interactive_test_utils.h +++ b/chrome/test/base/interactive_test_utils.h @@ -9,14 +9,16 @@ #include "chrome/test/base/ui_test_utils.h" #include "ui/base/test/ui_controls.h" -#if defined(TOOLKIT_VIEWS) -#include "ui/views/view.h" -#endif - namespace gfx { class Point; } +#if defined(TOOLKIT_VIEWS) +namespace views { +class View; +} +#endif + namespace ui_test_utils { // Brings the native window for |browser| to the foreground. Returns true on @@ -120,18 +122,14 @@ bool SendKeyPressAndWaitWithDetails( } // A combination of SendMouseMove to the middle of the view followed by -// SendMouseEvents. -void MoveMouseToCenterAndPress( +// SendMouseEvents. Only exposed for toolkit-views. +// Alternatives: ClickOnView() and ui::test::EventGenerator. #if defined(TOOLKIT_VIEWS) - views::View* view, -#elif defined(OS_IOS) - UIView* view, -#elif defined(OS_MACOSX) - NSView* view, +void MoveMouseToCenterAndPress(views::View* view, + ui_controls::MouseButton button, + int state, + const base::Closure& task); #endif - ui_controls::MouseButton button, - int state, - const base::Closure& task); namespace internal { diff --git a/chrome/test/base/interactive_test_utils_mac.mm b/chrome/test/base/interactive_test_utils_mac.mm index 44ad8ccd3f7b3..e2d5cdb5fa17b 100644 --- a/chrome/test/base/interactive_test_utils_mac.mm +++ b/chrome/test/base/interactive_test_utils_mac.mm @@ -17,6 +17,32 @@ namespace ui_test_utils { +namespace { + +void MoveMouseToNSViewCenterAndPress( + NSView* view, + ui_controls::MouseButton button, + int state, + const base::Closure& task) { + NSWindow* window = [view window]; + NSScreen* screen = [window screen]; + DCHECK(screen); + + // Converts the center position of the view into the coordinates accepted + // by SendMouseMoveNotifyWhenDone() method. + NSRect bounds = [view bounds]; + NSPoint center = NSMakePoint(NSMidX(bounds), NSMidY(bounds)); + center = [view convertPoint:center toView:nil]; + center = [window convertBaseToScreen:center]; + center = NSMakePoint(center.x, [screen frame].size.height - center.y); + + ui_controls::SendMouseMoveNotifyWhenDone( + center.x, center.y, + base::Bind(&internal::ClickTask, button, state, task)); +} + +} // namespace + bool IsViewFocused(const Browser* browser, ViewID vid) { NSWindow* window = browser->window()->GetNativeWindow(); DCHECK(window); @@ -44,7 +70,7 @@ void ClickOnView(const Browser* browser, ViewID vid) { DCHECK(window); NSView* view = view_id_util::GetView(window, vid); DCHECK(view); - MoveMouseToCenterAndPress( + MoveMouseToNSViewCenterAndPress( view, ui_controls::LEFT, ui_controls::DOWN | ui_controls::UP, @@ -66,28 +92,4 @@ bool ShowAndFocusNativeWindow(gfx::NativeWindow window) { return true; } -void MoveMouseToCenterAndPress( - NSView* view, - ui_controls::MouseButton button, - int state, - const base::Closure& task) { - DCHECK(view); - NSWindow* window = [view window]; - DCHECK(window); - NSScreen* screen = [window screen]; - DCHECK(screen); - - // Converts the center position of the view into the coordinates accepted - // by SendMouseMoveNotifyWhenDone() method. - NSRect bounds = [view bounds]; - NSPoint center = NSMakePoint(NSMidX(bounds), NSMidY(bounds)); - center = [view convertPoint:center toView:nil]; - center = [window convertBaseToScreen:center]; - center = NSMakePoint(center.x, [screen frame].size.height - center.y); - - ui_controls::SendMouseMoveNotifyWhenDone( - center.x, center.y, - base::Bind(&internal::ClickTask, button, state, task)); -} - } // namespace ui_test_utils diff --git a/chrome/test/base/interactive_test_utils_views.cc b/chrome/test/base/interactive_test_utils_views.cc index 2a39ba442709c..67bcf63360c91 100644 --- a/chrome/test/base/interactive_test_utils_views.cc +++ b/chrome/test/base/interactive_test_utils_views.cc @@ -8,20 +8,23 @@ #include "base/message_loop/message_loop.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/views/frame/browser_view.h" -#include "ui/aura/window.h" #include "ui/base/test/ui_controls.h" #include "ui/compositor/layer.h" +#include "ui/compositor/layer_animator.h" #include "ui/views/focus/focus_manager.h" namespace ui_test_utils { +// Until the whole browser UI is ported to tookit-views on Mac, these need to +// use the definitions in interactive_test_utils_mac.mm. +#if !defined(OS_MACOSX) + bool IsViewFocused(const Browser* browser, ViewID vid) { BrowserWindow* browser_window = browser->window(); DCHECK(browser_window); gfx::NativeWindow window = browser_window->GetNativeWindow(); DCHECK(window); - const views::Widget* widget = - views::Widget::GetTopLevelWidgetForNativeView(window); + const views::Widget* widget = views::Widget::GetWidgetForNativeWindow(window); DCHECK(widget); const views::FocusManager* focus_manager = widget->GetFocusManager(); DCHECK(focus_manager); @@ -40,6 +43,8 @@ void ClickOnView(const Browser* browser, ViewID vid) { content::RunMessageLoop(); } +#endif // defined(OS_MACOSX) + void MoveMouseToCenterAndPress(views::View* view, ui_controls::MouseButton button, int state, @@ -49,9 +54,9 @@ void MoveMouseToCenterAndPress(views::View* view, // Complete any in-progress animation before sending the events so that the // mouse-event targetting happens reliably, and does not flake because of // unreliable animation state. - aura::Window* window = view->GetWidget()->GetNativeView(); - if (window && window->layer()) { - ui::LayerAnimator* animator = window->layer()->GetAnimator(); + ui::Layer* layer = view->GetWidget()->GetLayer(); + if (layer) { + ui::LayerAnimator* animator = layer->GetAnimator(); if (animator && animator->is_animating()) animator->StopAnimating(); } diff --git a/chrome/test/base/test_switches.cc b/chrome/test/base/test_switches.cc index b1b16ac4a581b..a092c4267355d 100644 --- a/chrome/test/base/test_switches.cc +++ b/chrome/test/base/test_switches.cc @@ -9,9 +9,6 @@ namespace switches { // Also emit full event trace logs for successful tests. const char kAlsoEmitSuccessLogs[] = "also-emit-success-logs"; -// Enable displaying error dialogs (for debugging). -const char kEnableErrorDialogs[] = "enable-errdialogs"; - #if defined(OS_WIN) // Force browser tests to run in Ash/Metro on Windows 8. const char kAshBrowserTests[] = "ash-browsertests"; diff --git a/chrome/test/base/test_switches.h b/chrome/test/base/test_switches.h index 5c801408acdaf..5d3544c6c2b7d 100644 --- a/chrome/test/base/test_switches.h +++ b/chrome/test/base/test_switches.h @@ -12,7 +12,6 @@ namespace switches { // All switches in alphabetical order. The switches should be documented // alongside the definition of their values in the .cc file. extern const char kAlsoEmitSuccessLogs[]; -extern const char kEnableErrorDialogs[]; #if defined(OS_WIN) extern const char kAshBrowserTests[]; diff --git a/chrome/test/base/testing_profile.cc b/chrome/test/base/testing_profile.cc index 72f1e80b7b13e..7aede511fc9e3 100644 --- a/chrome/test/base/testing_profile.cc +++ b/chrome/test/base/testing_profile.cc @@ -84,8 +84,8 @@ #include "chrome/browser/extensions/extension_special_storage_policy.h" #include "chrome/browser/extensions/extension_system_factory.h" #include "chrome/browser/extensions/test_extension_system.h" -#include "chrome/browser/guest_view/guest_view_manager.h" #include "extensions/browser/extension_system.h" +#include "extensions/browser/guest_view/guest_view_manager.h" #endif #if defined(OS_ANDROID) @@ -820,7 +820,7 @@ HostContentSettingsMap* TestingProfile::GetHostContentSettingsMap() { content::BrowserPluginGuestManager* TestingProfile::GetGuestManager() { #if defined(ENABLE_EXTENSIONS) - return GuestViewManager::FromBrowserContext(this); + return extensions::GuestViewManager::FromBrowserContext(this); #else return NULL; #endif diff --git a/chrome/test/base/testing_profile_manager.cc b/chrome/test/base/testing_profile_manager.cc index 66c54a9570aa4..dd41700b322bf 100644 --- a/chrome/test/base/testing_profile_manager.cc +++ b/chrome/test/base/testing_profile_manager.cc @@ -34,7 +34,8 @@ class ProfileManager : public ::ProfileManagerWithoutInit { TestingProfileManager::TestingProfileManager(TestingBrowserProcess* process) : called_set_up_(false), browser_process_(process), - local_state_(process) { + local_state_(process), + profile_manager_(NULL) { } TestingProfileManager::~TestingProfileManager() { diff --git a/chrome/test/base/view_event_test_base.cc b/chrome/test/base/view_event_test_base.cc index d3c4f1cde51e6..b8c2a3a248101 100644 --- a/chrome/test/base/view_event_test_base.cc +++ b/chrome/test/base/view_event_test_base.cc @@ -5,42 +5,16 @@ #include "chrome/test/base/view_event_test_base.h" #include "base/bind.h" -#include "base/bind_helpers.h" #include "base/message_loop/message_loop.h" -#include "base/strings/string_number_conversions.h" #include "chrome/test/base/chrome_unit_test_suite.h" #include "chrome/test/base/interactive_test_utils.h" #include "chrome/test/base/testing_browser_process.h" -#include "chrome/test/base/ui_test_utils.h" -#include "ui/aura/client/event_client.h" -#include "ui/aura/env.h" -#include "ui/aura/test/aura_test_helper.h" -#include "ui/aura/window_event_dispatcher.h" -#include "ui/aura/window_tree_host.h" +#include "chrome/test/base/view_event_test_platform_part.h" #include "ui/base/ime/input_method_initializer.h" #include "ui/base/test/ui_controls.h" #include "ui/compositor/test/context_factories_for_test.h" -#include "ui/compositor/test/context_factories_for_test.h" -#include "ui/message_center/message_center.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" -#include "ui/wm/core/default_activation_client.h" -#include "ui/wm/core/wm_state.h" - -#if defined(USE_ASH) -#include "ash/shell.h" -#include "ash/shell_init_params.h" -#include "ash/test/test_session_state_delegate.h" -#include "ash/test/test_shell_delegate.h" -#endif - -#if defined(OS_CHROMEOS) -#include "chromeos/audio/cras_audio_handler.h" -#include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/network/network_handler.h" -#else // !defined(OS_CHROMEOS) -#include "ui/views/widget/desktop_aura/desktop_screen.h" -#endif namespace { @@ -101,51 +75,16 @@ void ViewEventTestBase::SetUpTestCase() { } void ViewEventTestBase::SetUp() { - wm_state_.reset(new wm::WMState); - views::ViewsDelegate::views_delegate = &views_delegate_; ui::InitializeInputMethodForTesting(); - gfx::NativeView context = NULL; // The ContextFactory must exist before any Compositors are created. bool enable_pixel_output = false; ui::ContextFactory* context_factory = ui::InitializeContextFactoryForTests(enable_pixel_output); -#if defined(OS_CHROMEOS) - // Ash Shell can't just live on its own without a browser process, we need to - // also create the message center. - message_center::MessageCenter::Initialize(); - chromeos::DBusThreadManager::InitializeWithStub(); - chromeos::CrasAudioHandler::InitializeForTesting(); - chromeos::NetworkHandler::Initialize(); - ash::test::TestShellDelegate* shell_delegate = - new ash::test::TestShellDelegate(); - ash::ShellInitParams init_params; - init_params.delegate = shell_delegate; - init_params.context_factory = context_factory; - ash::Shell::CreateInstance(init_params); - shell_delegate->test_session_state_delegate() - ->SetActiveUserSessionStarted(true); - context = ash::Shell::GetPrimaryRootWindow(); - context->GetHost()->Show(); -#elif defined(USE_ASH) - // http://crbug.com/154081 use ash::Shell code path below on win_ash bots when - // interactive_ui_tests is brought up on that platform. - gfx::Screen::SetScreenInstance( - gfx::SCREEN_TYPE_NATIVE, views::CreateDesktopScreen()); - aura::Env::CreateInstance(true); - aura::Env::GetInstance()->set_context_factory(context_factory); -#elif defined(USE_AURA) - // Instead of using the ash shell, use an AuraTestHelper to create and manage - // the test screen. - aura_test_helper_.reset( - new aura::test::AuraTestHelper(base::MessageLoopForUI::current())); - aura_test_helper_->SetUp(context_factory); - new wm::DefaultActivationClient(aura_test_helper_->root_window()); - context = aura_test_helper_->root_window(); -#endif - + platform_part_.reset(ViewEventTestPlatformPart::Create(context_factory)); + gfx::NativeWindow context = platform_part_->GetContext(); window_ = views::Widget::CreateWindowWithContext(this, context); } @@ -157,28 +96,12 @@ void ViewEventTestBase::TearDown() { } ui::Clipboard::DestroyClipboardForCurrentThread(); - -#if defined(USE_ASH) -#if defined(OS_CHROMEOS) - ash::Shell::DeleteInstance(); - chromeos::NetworkHandler::Shutdown(); - chromeos::CrasAudioHandler::Shutdown(); - chromeos::DBusThreadManager::Shutdown(); - // Ash Shell can't just live on its own without a browser process, we need to - // also shut down the message center. - message_center::MessageCenter::Shutdown(); -#endif - aura::Env::DeleteInstance(); -#elif defined(USE_AURA) - aura_test_helper_->TearDown(); -#endif // !USE_ASH && USE_AURA + platform_part_.reset(); ui::TerminateContextFactoryForTests(); ui::ShutdownInputMethodForTesting(); views::ViewsDelegate::views_delegate = NULL; - - wm_state_.reset(); } bool ViewEventTestBase::CanResize() const { diff --git a/chrome/test/base/view_event_test_base.h b/chrome/test/base/view_event_test_base.h index 424ef0f2a3206..a3d7290abafd0 100644 --- a/chrome/test/base/view_event_test_base.h +++ b/chrome/test/base/view_event_test_base.h @@ -24,19 +24,11 @@ #include "ui/base/win/scoped_ole_initializer.h" #endif -namespace aura { -namespace test { -class AuraTestHelper; -} -} - namespace gfx { class Size; } -namespace wm { -class WMState; -} +class ViewEventTestPlatformPart; // Base class for Views based tests that dispatch events. // @@ -154,10 +146,7 @@ class ViewEventTestBase : public views::WidgetDelegate, ui::ScopedOleInitializer ole_initializer_; #endif -#if defined(USE_AURA) - scoped_ptr aura_test_helper_; - scoped_ptr wm_state_; -#endif + scoped_ptr platform_part_; ChromeViewsDelegate views_delegate_; diff --git a/chrome/test/base/view_event_test_platform_part.h b/chrome/test/base/view_event_test_platform_part.h new file mode 100644 index 0000000000000..4fe53fcd4ce42 --- /dev/null +++ b/chrome/test/base/view_event_test_platform_part.h @@ -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. + +#ifndef CHROME_TEST_BASE_VIEW_EVENT_TEST_PLATFORM_PART_H_ +#define CHROME_TEST_BASE_VIEW_EVENT_TEST_PLATFORM_PART_H_ + +#include "base/macros.h" +#include "ui/gfx/native_widget_types.h" + +namespace ui { +class ContextFactory; +} + +// A helper class owned by tests that performs platform specific initialization. +// ViewEventTestPlatformPart behaves a bit like views::ViewsTestHelper, but on +// ChromeOS it will create an Ash shell environment, rather than using an +// AuraTestHelper. +class ViewEventTestPlatformPart { + public: + virtual ~ViewEventTestPlatformPart() {} + + // Set up the platform-specific environment. Teardown is performed in the + // destructor. + static ViewEventTestPlatformPart* Create(ui::ContextFactory* context_factory); + + // The Widget context for creating the test window. This will be the Ash root + // window on ChromeOS environments. Otherwise it should return NULL. + virtual gfx::NativeWindow GetContext() = 0; + + protected: + ViewEventTestPlatformPart() {} + + private: + DISALLOW_COPY_AND_ASSIGN(ViewEventTestPlatformPart); +}; + +#endif // CHROME_TEST_BASE_VIEW_EVENT_TEST_PLATFORM_PART_H_ diff --git a/chrome/test/base/view_event_test_platform_part_ash.cc b/chrome/test/base/view_event_test_platform_part_ash.cc new file mode 100644 index 0000000000000..51ed1c42ca9cb --- /dev/null +++ b/chrome/test/base/view_event_test_platform_part_ash.cc @@ -0,0 +1,52 @@ +// 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 "chrome/test/base/view_event_test_platform_part.h" + +#include "ui/aura/env.h" +#include "ui/gfx/screen.h" +#include "ui/views/widget/desktop_aura/desktop_screen.h" +#include "ui/wm/core/wm_state.h" + +namespace { + +// ChromeViewsTestHelper implementation for non-ChromeOS environments, where the +// Ash desktop environment is available (use_ash=1, chromeos=0). +class ViewEventTestPlatformPartAsh : public ViewEventTestPlatformPart { + public: + explicit ViewEventTestPlatformPartAsh(ui::ContextFactory* context_factory); + virtual ~ViewEventTestPlatformPartAsh(); + + // Overridden from ViewEventTestPlatformPart: + virtual gfx::NativeWindow GetContext() OVERRIDE { + return NULL; // No context, so that desktop tree hosts are used by default. + } + + private: + wm::WMState wm_state_; + + DISALLOW_COPY_AND_ASSIGN(ViewEventTestPlatformPartAsh); +}; + +ViewEventTestPlatformPartAsh::ViewEventTestPlatformPartAsh( + ui::ContextFactory* context_factory) { + // http://crbug.com/154081 use ash::Shell code path below on win_ash bots when + // interactive_ui_tests is brought up on that platform. + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, + views::CreateDesktopScreen()); + aura::Env::CreateInstance(true); + aura::Env::GetInstance()->set_context_factory(context_factory); +} + +ViewEventTestPlatformPartAsh::~ViewEventTestPlatformPartAsh() { + aura::Env::DeleteInstance(); +} + +} // namespace + +// static +ViewEventTestPlatformPart* ViewEventTestPlatformPart::Create( + ui::ContextFactory* context_factory) { + return new ViewEventTestPlatformPartAsh(context_factory); +} diff --git a/chrome/test/base/view_event_test_platform_part_chromeos.cc b/chrome/test/base/view_event_test_platform_part_chromeos.cc new file mode 100644 index 0000000000000..9e5e6102f72c4 --- /dev/null +++ b/chrome/test/base/view_event_test_platform_part_chromeos.cc @@ -0,0 +1,76 @@ +// 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 "chrome/test/base/view_event_test_platform_part.h" + +#include "ash/shell.h" +#include "ash/shell_init_params.h" +#include "ash/test/test_session_state_delegate.h" +#include "ash/test/test_shell_delegate.h" +#include "chromeos/audio/cras_audio_handler.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/network/network_handler.h" +#include "ui/aura/env.h" +#include "ui/aura/window_tree_host.h" +#include "ui/message_center/message_center.h" +#include "ui/wm/core/wm_state.h" + +namespace { + +// ViewEventTestPlatformPart implementation for ChromeOS (chromeos=1). +class ViewEventTestPlatformPartChromeOS : public ViewEventTestPlatformPart { + public: + explicit ViewEventTestPlatformPartChromeOS( + ui::ContextFactory* context_factory); + virtual ~ViewEventTestPlatformPartChromeOS(); + + // Overridden from ViewEventTestPlatformPart: + virtual gfx::NativeWindow GetContext() OVERRIDE { + return ash::Shell::GetPrimaryRootWindow(); + } + + private: + wm::WMState wm_state_; + + DISALLOW_COPY_AND_ASSIGN(ViewEventTestPlatformPartChromeOS); +}; + +ViewEventTestPlatformPartChromeOS::ViewEventTestPlatformPartChromeOS( + ui::ContextFactory* context_factory) { + // Ash Shell can't just live on its own without a browser process, we need to + // also create the message center. + message_center::MessageCenter::Initialize(); + chromeos::DBusThreadManager::InitializeWithStub(); + chromeos::CrasAudioHandler::InitializeForTesting(); + chromeos::NetworkHandler::Initialize(); + ash::test::TestShellDelegate* shell_delegate = + new ash::test::TestShellDelegate(); + ash::ShellInitParams init_params; + init_params.delegate = shell_delegate; + init_params.context_factory = context_factory; + ash::Shell::CreateInstance(init_params); + shell_delegate->test_session_state_delegate()->SetActiveUserSessionStarted( + true); + GetContext()->GetHost()->Show(); +} + +ViewEventTestPlatformPartChromeOS::~ViewEventTestPlatformPartChromeOS() { + ash::Shell::DeleteInstance(); + chromeos::NetworkHandler::Shutdown(); + chromeos::CrasAudioHandler::Shutdown(); + chromeos::DBusThreadManager::Shutdown(); + // Ash Shell can't just live on its own without a browser process, we need to + // also shut down the message center. + message_center::MessageCenter::Shutdown(); + + aura::Env::DeleteInstance(); +} + +} // namespace + +// static +ViewEventTestPlatformPart* ViewEventTestPlatformPart::Create( + ui::ContextFactory* context_factory) { + return new ViewEventTestPlatformPartChromeOS(context_factory); +} diff --git a/chrome/test/base/view_event_test_platform_part_mac.mm b/chrome/test/base/view_event_test_platform_part_mac.mm new file mode 100644 index 0000000000000..d20ab7fa38e1e --- /dev/null +++ b/chrome/test/base/view_event_test_platform_part_mac.mm @@ -0,0 +1,27 @@ +// 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 "chrome/test/base/view_event_test_platform_part.h" + +namespace { + +// ViewEventTestPlatformPart implementation for toolkit-views on Mac. +class ViewEventTestPlatformPartMac : public ViewEventTestPlatformPart { + public: + ViewEventTestPlatformPartMac() {} + + // Overridden from ViewEventTestPlatformPart: + virtual gfx::NativeWindow GetContext() OVERRIDE { return NULL; } + + private: + DISALLOW_COPY_AND_ASSIGN(ViewEventTestPlatformPartMac); +}; + +} // namespace + +// static +ViewEventTestPlatformPart* ViewEventTestPlatformPart::Create( + ui::ContextFactory* context_factory) { + return new ViewEventTestPlatformPartMac(); +} diff --git a/chrome/test/chromedriver/OWNERS b/chrome/test/chromedriver/OWNERS index b2fe24d73218b..b397f827df8e8 100644 --- a/chrome/test/chromedriver/OWNERS +++ b/chrome/test/chromedriver/OWNERS @@ -1,3 +1,4 @@ craigdh@chromium.org frankf@chromium.org +samuong@chromium.org stgao@chromium.org diff --git a/chrome/test/chromedriver/chrome/navigation_tracker.cc b/chrome/test/chromedriver/chrome/navigation_tracker.cc index aad70e5db6e56..6d3f2f3d2c0e5 100644 --- a/chrome/test/chromedriver/chrome/navigation_tracker.cc +++ b/chrome/test/chromedriver/chrome/navigation_tracker.cc @@ -14,8 +14,7 @@ NavigationTracker::NavigationTracker(DevToolsClient* client, const BrowserInfo* browser_info) : client_(client), loading_state_(kUnknown), - browser_info_(browser_info), - num_frames_pending_(0) { + browser_info_(browser_info) { client_->AddListener(this); } @@ -24,8 +23,7 @@ NavigationTracker::NavigationTracker(DevToolsClient* client, const BrowserInfo* browser_info) : client_(client), loading_state_(known_state), - browser_info_(browser_info), - num_frames_pending_(0) { + browser_info_(browser_info) { client_->AddListener(this); } @@ -87,8 +85,11 @@ Status NavigationTracker::OnEvent(DevToolsClient* client, const std::string& method, const base::DictionaryValue& params) { if (method == "Page.frameStartedLoading") { + std::string frame_id; + if (!params.GetString("frameId", &frame_id)) + return Status(kUnknownError, "missing or invalid 'frameId'"); + pending_frame_set_.insert(frame_id); loading_state_ = kLoading; - num_frames_pending_++; } else if (method == "Page.frameStoppedLoading") { // Versions of Blink before revision 170248 sent a single // Page.frameStoppedLoading event per page, but 170248 and newer revisions @@ -111,10 +112,14 @@ Status NavigationTracker::OnEvent(DevToolsClient* client, expecting_single_stop_event = browser_info_->blink_revision < 170248; } - num_frames_pending_--; + std::string frame_id; + if (!params.GetString("frameId", &frame_id)) + return Status(kUnknownError, "missing or invalid 'frameId'"); + + pending_frame_set_.erase(frame_id); - if (num_frames_pending_ <= 0 || expecting_single_stop_event) { - num_frames_pending_ = 0; + if (pending_frame_set_.empty() || expecting_single_stop_event) { + pending_frame_set_.clear(); loading_state_ = kNotLoading; } } else if (method == "Page.frameScheduledNavigation") { @@ -146,7 +151,7 @@ Status NavigationTracker::OnEvent(DevToolsClient* client, // See crbug.com/180742. const base::Value* unused_value; if (!params.Get("frame.parentId", &unused_value)) { - num_frames_pending_ = 0; + pending_frame_set_.clear(); scheduled_frame_set_.clear(); } } else if (method == "Inspector.targetCrashed") { @@ -200,6 +205,6 @@ Status NavigationTracker::OnCommandSuccess(DevToolsClient* client, void NavigationTracker::ResetLoadingState(LoadingState loading_state) { loading_state_ = loading_state; - num_frames_pending_ = 0; + pending_frame_set_.clear(); scheduled_frame_set_.clear(); } diff --git a/chrome/test/chromedriver/chrome/navigation_tracker.h b/chrome/test/chromedriver/chrome/navigation_tracker.h index e6d198dbe0609..0d02138177f0e 100644 --- a/chrome/test/chromedriver/chrome/navigation_tracker.h +++ b/chrome/test/chromedriver/chrome/navigation_tracker.h @@ -55,7 +55,7 @@ class NavigationTracker : public DevToolsEventListener { DevToolsClient* client_; LoadingState loading_state_; const BrowserInfo* browser_info_; - int num_frames_pending_; + std::set pending_frame_set_; std::set scheduled_frame_set_; void ResetLoadingState(LoadingState loading_state); diff --git a/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc b/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc index e2f333a451f15..6febf8de9d1d3 100644 --- a/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc +++ b/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc @@ -27,19 +27,20 @@ void AssertTrackerExpectsSingleStopEvent(BrowserInfo* browser_info) { StubDevToolsClient client; NavigationTracker tracker(&client, browser_info); base::DictionaryValue params; + params.SetString("frameId", "f"); - // num_frames_pending_ == 0 + // pending_frames_set_.size() == 0 ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); - // num_frames_pending_ == 1 + // pending_frames_set_.size() == 1 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); - // num_frames_pending_ == 2 + // pending_frames_set_.size() == 2 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); - // num_frames_pending_ == 0 + // pending_frames_set_.size() == 0 ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); } @@ -48,31 +49,36 @@ void AssertTrackerExpectsMultipleStopEvents(BrowserInfo* browser_info) { NavigationTracker tracker(&client, browser_info); base::DictionaryValue params; - // num_frames_pending_ == 0 + // pending_frames_set_.size() == 0 + params.SetString("frameId", "1"); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); - // num_frames_pending_ == 1 - ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); + // pending_frames_set_.size() == 1 + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "1", true)); + params.SetString("frameId", "2"); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); - // num_frames_pending_ == 2 - ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); + // pending_frames_set_.size() == 2 + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "2", true)); + params.SetString("frameId", "2"); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); - // num_frames_pending_ == 1 - ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); + // pending_frames_set_.size() == 1 + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "2", true)); + params.SetString("frameId", "1"); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); - // num_frames_pending_ == 0 - ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); + // pending_frames_set_.size() == 0 + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "1", false)); + params.SetString("frameId", "3"); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); - // num_frames_pending_ == 0 - ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); + // pending_frames_set_.size() == 0 + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "3", false)); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); - // num_frames_pending_ == 1 - ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); + // pending_frames_set_.size() == 1 + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "3", true)); } } // namespace @@ -83,6 +89,30 @@ TEST(NavigationTracker, FrameLoadStartStop) { NavigationTracker tracker(&client, &browser_info); base::DictionaryValue params; + params.SetString("frameId", "f"); + + ASSERT_EQ( + kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); + ASSERT_EQ( + kOk, tracker.OnEvent(&client, "Page.frameStoppedLoading", params).code()); + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", false)); +} + +// When a frame fails to load due to (for example) a DNS resolution error, we +// can sometimes see two Page.frameStartedLoading events with only a single +// Page.frameStoppedLoading event. +TEST(NavigationTracker, FrameLoadStartStartStop) { + StubDevToolsClient client; + BrowserInfo browser_info; + NavigationTracker tracker(&client, &browser_info); + + base::DictionaryValue params; + params.SetString("frameId", "f"); + + ASSERT_EQ( + kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); + ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); ASSERT_EQ( kOk, tracker.OnEvent(&client, "Page.frameStartedLoading", params).code()); ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, "f", true)); @@ -298,6 +328,7 @@ TEST(NavigationTracker, UnknownStateForcesStart) { TEST(NavigationTracker, UnknownStateForcesStartReceivesStop) { base::DictionaryValue params; + params.SetString("frameId", "f"); DeterminingLoadStateDevToolsClient client( true, "Page.frameStoppedLoading", ¶ms); BrowserInfo browser_info; @@ -307,6 +338,7 @@ TEST(NavigationTracker, UnknownStateForcesStartReceivesStop) { TEST(NavigationTracker, OnSuccessfulNavigate) { base::DictionaryValue params; + params.SetString("frameId", "f"); DeterminingLoadStateDevToolsClient client( true, "Page.frameStoppedLoading", ¶ms); BrowserInfo browser_info; @@ -318,6 +350,7 @@ TEST(NavigationTracker, OnSuccessfulNavigate) { TEST(NavigationTracker, OnSuccessfulNavigateStillWaiting) { base::DictionaryValue params; + params.SetString("frameId", "f"); DeterminingLoadStateDevToolsClient client(true, std::string(), ¶ms); BrowserInfo browser_info; NavigationTracker tracker( diff --git a/chrome/test/data/extensions/api_test/cast_channel/api/common.js b/chrome/test/data/extensions/api_test/cast_channel/api/common.js index 92eaea6be588c..b92579b20b94c 100644 --- a/chrome/test/data/extensions/api_test/cast_channel/api/common.js +++ b/chrome/test/data/extensions/api_test/cast_channel/api/common.js @@ -14,6 +14,10 @@ assertOpenChannel = function(channel) { }; assertClosedChannel = function(channel) { + assertClosedChannelWithError(channel, undefined); +}; + +assertClosedChannelWithError = function(channel, error) { chrome.test.assertTrue(!!channel); chrome.test.assertTrue(channel.channelId > 0); chrome.test.assertTrue(channel.url == 'cast://192.168.1.1:8009'); @@ -21,5 +25,5 @@ assertClosedChannel = function(channel) { chrome.test.assertTrue(channel.connectInfo.port == 8009); chrome.test.assertTrue(channel.connectInfo.auth == 'ssl'); chrome.test.assertTrue(channel.readyState == 'closed'); - chrome.test.assertTrue(channel.errorState == undefined); + chrome.test.assertTrue(channel.errorState == error); }; diff --git a/chrome/test/data/extensions/api_test/cast_channel/api/test_get_logs.html b/chrome/test/data/extensions/api_test/cast_channel/api/test_get_logs.html new file mode 100644 index 0000000000000..f7e005f1bae45 --- /dev/null +++ b/chrome/test/data/extensions/api_test/cast_channel/api/test_get_logs.html @@ -0,0 +1,2 @@ + + diff --git a/chrome/test/data/extensions/api_test/cast_channel/api/test_get_logs.js b/chrome/test/data/extensions/api_test/cast_channel/api/test_get_logs.js new file mode 100644 index 0000000000000..99acf9faa0fe3 --- /dev/null +++ b/chrome/test/data/extensions/api_test/cast_channel/api/test_get_logs.js @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var message = { + 'namespace_': 'foo', + 'sourceId': 'src', + 'destinationId': 'dest', + 'data': 'some-string' +}; + +var onGetLogs = function(blob) { + chrome.test.assertTrue(blob.byteLength > 0); + chrome.test.succeed(); +} + +var onClose = function(channel) { + assertClosedChannel(channel); + chrome.cast.channel.getLogs(onGetLogs); +}; + +var onSend = function(channel) { + assertOpenChannel(channel); + chrome.cast.channel.close(channel, onClose); +}; + +var onOpen = function(channel) { + assertOpenChannel(channel); + chrome.cast.channel.send(channel, message, onSend); +}; + +chrome.cast.channel.open({ipAddress: '192.168.1.1', port: 8009, auth: 'ssl'}, + onOpen); diff --git a/chrome/test/data/extensions/api_test/cast_channel/api/test_open_error.js b/chrome/test/data/extensions/api_test/cast_channel/api/test_open_error.js index e9766f359691b..135ee7ddb9a42 100644 --- a/chrome/test/data/extensions/api_test/cast_channel/api/test_open_error.js +++ b/chrome/test/data/extensions/api_test/cast_channel/api/test_open_error.js @@ -2,18 +2,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +var errorEvent = false; +var openCallback = false; + var onClose = function(channel) { - assertClosedChannel(channel); + assertClosedChannelWithError(channel, 'connect_error'); + chrome.test.succeed(); + chrome.test.notifyPass(); } -var onError = function(channel) { - chrome.cast.channel.close(channel, onClose); - chrome.test.notifyPass(); +var onError = function(channel, error) { + errorEvent = true; + chrome.test.assertTrue(error.errorState == 'connect_error'); + chrome.test.assertTrue(error.challengeReplyErrorType == 9); + chrome.test.assertTrue(error.nssErrorCode == -8164); + chrome.test.assertTrue(error.netReturnValue == 0); + maybeClose(channel); } var onOpen = function(channel) { - assertClosedChannel(channel); - chrome.test.succeed(); + openCallback = true; + assertClosedChannelWithError(channel, 'connect_error'); + maybeClose(channel); +}; + +var maybeClose = function(channel) { + if (errorEvent && openCallback) { + console.log("closing " + JSON.toString(channel)); + chrome.cast.channel.close(channel, onClose); + } }; chrome.cast.channel.onError.addListener(onError); diff --git a/chrome/test/data/extensions/api_test/cast_streaming/bad_logging.js b/chrome/test/data/extensions/api_test/cast_streaming/bad_logging.js index a7862b2bfcd0f..632df0d1a9278 100644 --- a/chrome/test/data/extensions/api_test/cast_streaming/bad_logging.js +++ b/chrome/test/data/extensions/api_test/cast_streaming/bad_logging.js @@ -29,8 +29,8 @@ chrome.test.runTests([ var expectEmptyLogs = function(rawEvents) { chrome.test.assertEq(0, rawEvents.byteLength); } - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); - chrome.test.assertEq(videoParams.payload.codecName, "VP8"); + chrome.test.assertTrue(!!audioParams.payload.codecName); + chrome.test.assertTrue(!!videoParams.payload.codecName); udpTransport.setDestination(udpId, {address: "127.0.0.1", port: 2344}); rtpStream.onStarted.addListener( @@ -55,8 +55,8 @@ chrome.test.runTests([ rtpStream.destroy(audioId); rtpStream.destroy(videoId); udpTransport.destroy(udpId); - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); - chrome.test.assertEq(videoParams.payload.codecName, "VP8"); + chrome.test.assertTrue(!!audioParams.payload.codecName); + chrome.test.assertTrue(!!videoParams.payload.codecName); chrome.test.succeed(); }.bind(null, stream, audioId, videoId, udpId)); rtpStream.start(audioId, audioParams); diff --git a/chrome/test/data/extensions/api_test/cast_streaming/basics.js b/chrome/test/data/extensions/api_test/cast_streaming/basics.js index da887619165fe..591e129b901a6 100644 --- a/chrome/test/data/extensions/api_test/cast_streaming/basics.js +++ b/chrome/test/data/extensions/api_test/cast_streaming/basics.js @@ -28,8 +28,8 @@ chrome.test.runTests([ udpId); var audioParams = rtpStream.getSupportedParams(audioId)[0]; var videoParams = rtpStream.getSupportedParams(videoId)[0]; - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); - chrome.test.assertEq(videoParams.payload.codecName, "VP8"); + chrome.test.assertTrue(!!audioParams.payload.codecName); + chrome.test.assertTrue(!!videoParams.payload.codecName); udpTransport.setDestination(udpId, {address: "127.0.0.1", port: 2344}); rtpStream.onStarted.addListener( @@ -63,8 +63,8 @@ chrome.test.runTests([ rtpStream.destroy(audioId); rtpStream.destroy(videoId); udpTransport.destroy(udpId); - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); - chrome.test.assertEq(videoParams.payload.codecName, "VP8"); + chrome.test.assertTrue(!!audioParams.payload.codecName); + chrome.test.assertTrue(!!videoParams.payload.codecName); chrome.test.succeed(); }.bind(null, stream, audioId, videoId, udpId)); rtpStream.start(audioId, audioParams); diff --git a/chrome/test/data/extensions/api_test/cast_streaming/destination_not_set.js b/chrome/test/data/extensions/api_test/cast_streaming/destination_not_set.js index f14810726f0a2..3d37987f3e56d 100644 --- a/chrome/test/data/extensions/api_test/cast_streaming/destination_not_set.js +++ b/chrome/test/data/extensions/api_test/cast_streaming/destination_not_set.js @@ -24,7 +24,7 @@ chrome.test.runTests([ videoId, udpId); var audioParams = rtpStream.getSupportedParams(audioId)[0]; - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); + chrome.test.assertTrue(!!audioParams.payload.codecName); var expectError = function(id, message) { chrome.test.assertEq("Destination not set.", message); chrome.test.succeed(); diff --git a/chrome/test/data/extensions/api_test/cast_streaming/stats.js b/chrome/test/data/extensions/api_test/cast_streaming/stats.js index c8be6cecd090a..234e2a946e78d 100644 --- a/chrome/test/data/extensions/api_test/cast_streaming/stats.js +++ b/chrome/test/data/extensions/api_test/cast_streaming/stats.js @@ -25,8 +25,8 @@ chrome.test.runTests([ udpId); var audioParams = rtpStream.getSupportedParams(audioId)[0]; var videoParams = rtpStream.getSupportedParams(videoId)[0]; - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); - chrome.test.assertEq(videoParams.payload.codecName, "VP8"); + chrome.test.assertTrue(!!audioParams.payload.codecName); + chrome.test.assertTrue(!!videoParams.payload.codecName); udpTransport.setDestination(udpId, {address: "127.0.0.1", port: 2344}); rtpStream.onStarted.addListener( @@ -58,8 +58,8 @@ chrome.test.runTests([ rtpStream.destroy(audioId); rtpStream.destroy(videoId); udpTransport.destroy(udpId); - chrome.test.assertEq(audioParams.payload.codecName, "OPUS"); - chrome.test.assertEq(videoParams.payload.codecName, "VP8"); + chrome.test.assertTrue(!!audioParams.payload.codecName); + chrome.test.assertTrue(!!videoParams.payload.codecName); chrome.test.succeed(); }.bind(null, stream, audioId, videoId, udpId)); rtpStream.start(audioId, audioParams); diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_invalid/manifest.json b/chrome/test/data/extensions/api_test/extension_options/embed_invalid/manifest.json new file mode 100644 index 0000000000000..521bc2e5e14b0 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_invalid/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "embed_invalid_test", + "version": "0.1", + "manifest_version": 2, + "description": "Tests if fires createfailed if given a nonexistent extension", + "permissions": [ + "embeddedExtensionOptions" + ] +} diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_invalid/test.html b/chrome/test/data/extensions/api_test/extension_options/embed_invalid/test.html new file mode 100644 index 0000000000000..9580339ef42e7 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_invalid/test.html @@ -0,0 +1,10 @@ + + + + + + diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_invalid/test.js b/chrome/test/data/extensions/api_test/extension_options/embed_invalid/test.js new file mode 100644 index 0000000000000..fcf8f7dd037d1 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_invalid/test.js @@ -0,0 +1,28 @@ +// 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. + +function testCannotEmbedExtension(id) { + var done = chrome.test.callbackAdded(); + var extensionoptions = document.createElement('extensionoptions'); + extensionoptions.addEventListener('createfailed', function() { + document.body.removeChild(extensionoptions); + done(); + }); + extensionoptions.addEventListener('load', function () { + document.body.removeChild(extensionoptions); + chrome.test.fail(); + }); + extensionoptions.setAttribute('extension', id); + document.body.appendChild(extensionoptions); +} + +chrome.test.runTests([ + function cannotEmbedInvalidExtensionId() { + testCannotEmbedExtension('thisisprobablynotrealextensionid'); + }, + + function cannotEmbedSelfIfNoOptionsPage() { + testCannotEmbedExtension(chrome.runtime.id); + } +]); diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/background.js b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/background.js new file mode 100644 index 0000000000000..265358c43c050 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/background.js @@ -0,0 +1,12 @@ +// 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. + +chrome.runtime.onMessageExternal.addListener( + function(message, sender, sendResponse) { + var optionsPages = chrome.extension.getViews().filter(function(view) { + return view.document.location.pathname == '/options.html'; + }); + var response = { hasOptionsPage: optionsPages.length > 0 }; + sendResponse(response); +}); diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/manifest.json b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/manifest.json new file mode 100644 index 0000000000000..8f2144ae46b2f --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "embed_other_test_embedded", + "version": "0.1", + "manifest_version": 2, + "background": { + "scripts": ["background.js"] + }, + "options_page": "options.html", + "permissions": [ + "embeddedExtensionOptions" + ] +} diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/options.html b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/options.html new file mode 100644 index 0000000000000..1cddc4a3a17ce --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedded/options.html @@ -0,0 +1,10 @@ + + + +

    Test test test

    + + diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/background.html b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/background.html new file mode 100644 index 0000000000000..a1c5bc9149ba8 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/background.html @@ -0,0 +1,8 @@ + diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/manifest.json b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/manifest.json new file mode 100644 index 0000000000000..11e34a99c1107 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "embed_other_test_embedder", + "version": "0.1", + "manifest_version": 2, + "background": { + "page": "background.html" + }, + "permissions": [ + "embeddedExtensionOptions", + "storage" + ] +} diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/test.html b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/test.html new file mode 100644 index 0000000000000..9580339ef42e7 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/test.html @@ -0,0 +1,10 @@ + + + + + + diff --git a/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/test.js b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/test.js new file mode 100644 index 0000000000000..d0b7dc0f4b974 --- /dev/null +++ b/chrome/test/data/extensions/api_test/extension_options/embed_other/embedder/test.js @@ -0,0 +1,31 @@ +// 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. + +chrome.test.runTests([ + function cannotEmbedOtherExtensionsOptions() { + var pass = chrome.test.callbackPass; + chrome.storage.local.get('embeddedId', pass(function(items) { + var done = chrome.test.callbackAdded(); + var extensionoptions = document.createElement('extensionoptions'); + extensionoptions.addEventListener('createfailed', function() { + try { + chrome.runtime.sendMessage(items['embeddedId'], + 'checking for options page', + function(response) { + chrome.test.assertFalse(response.hasOptionsPage); + done(); + }); + } finally { + document.body.removeChild(extensionoptions); + } + }); + extensionoptions.addEventListener('load', function () { + document.body.removeChild(extensionoptions); + chrome.test.fail(); + }); + extensionoptions.setAttribute('extension', items.embeddedId); + document.body.appendChild(extensionoptions); + })); + } +]); diff --git a/chrome/test/data/extensions/api_test/file_manager_browsertest/file_manager/multi_profile.js b/chrome/test/data/extensions/api_test/file_manager_browsertest/file_manager/multi_profile.js index 32f032e6ff96f..65423d511703b 100644 --- a/chrome/test/data/extensions/api_test/file_manager_browsertest/file_manager/multi_profile.js +++ b/chrome/test/data/extensions/api_test/file_manager_browsertest/file_manager/multi_profile.js @@ -49,65 +49,6 @@ function waitForVisitDesktopMenu(windowId, profileId, waitMode) { }); } -testcase.multiProfileBadge = function() { - var appId; - StepsRunner.run([ - function() { - setupAndWaitUntilReady(null, RootPath.DOWNLOADS, this.next); - }, - // Add all users. - function(inAppId) { - appId = inAppId; - chrome.test.sendMessage(JSON.stringify({name: 'addAllUsers'}), - this.next); - }, - // Get the badge element. - function(json) { - chrome.test.assertTrue(JSON.parse(json)); - waitForElement(appId, '#profile-badge').then(this.next); - }, - // Verify no badge image is shown yet. Move to other deskop. - function(element) { - chrome.test.assertTrue(element.hidden, 'Badge hidden initially'); - callRemoteTestUtil('visitDesktop', - appId, - ['bob@invalid.domain'], - this.next); - }, - // Get the badge element again. - function(result) { - chrome.test.assertTrue(result); - waitForElement(appId, '#profile-badge:not([hidden])').then(this.next); - }, - // Verify an image source is filled. Go back to the original desktop - function(element) { - callRemoteTestUtil('queryAllElements', - appId, - ['#profile-badge', - null, - ['background']]).then(this.next); - }, - function(elements) { - chrome.test.assertTrue( - elements[0].styles.background.indexOf('data:image') !== -1, - 'Badge shown after moving desktop'); - callRemoteTestUtil('visitDesktop', - appId, - ['alice@invalid.domain'], - this.next); - }, - // Wait for #profile-badge element to disappear. - function(result) { - chrome.test.assertTrue(result); - waitForElementLost(appId, '#profile-badge:not([hidden])').then(this.next); - }, - // The image is gone. - function(result) { - checkIfNoErrorsOccured(this.next); - } - ]); -}; - testcase.multiProfileVisitDesktopMenu = function() { var appId; StepsRunner.run([ diff --git a/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/photo_editor.js b/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/photo_editor.js index 0bc1ac97c95c6..76a423c9e181b 100644 --- a/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/photo_editor.js +++ b/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/photo_editor.js @@ -28,12 +28,14 @@ function waitForPressEnterMessage(appWindow) { */ function setupPhotoEditor(testVolumeName, volumeType) { // Lauch the gallery. + observeWindowError(window); var launchedPromise = launchWithTestEntries( testVolumeName, volumeType, [ENTRIES.desktop]); return launchedPromise.then(function(args) { var appWindow = args.appWindow; + observeWindowError(appWindow.contentWindow); // Show the slide image. var slideImagePromise = waitForSlideImage( @@ -90,7 +92,7 @@ function rotateImage(testVolumeName, volumeType) { } /** - * Tests to crop an image. + * Tests to crop an image and undoes it. * * @param {string} testVolumeName Test volume name passed to the addEntries * function. Either 'drive' or 'local'. @@ -123,6 +125,17 @@ function cropImage(testVolumeName, volumeType) { 533, 400, 'My Desktop Background'); + }). + then(function() { + return waitAndClickElement( + appWindow, '.gallery:not([locked]) button.undo'); + }). + then(function() { + return waitForSlideImage( + appWindow.contentWindow.document, + 800, + 600, + 'My Desktop Background'); }); }); } diff --git a/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/test_util.js b/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/test_util.js index f81f6b692168e..25c7f3b6e8d96 100644 --- a/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/test_util.js +++ b/chrome/test/data/extensions/api_test/file_manager_browsertest/gallery/test_util.js @@ -145,3 +145,13 @@ function sendKeyDown(appWindow, query, keyIdentifier) { 'keydown', {bubbles: true, keyIdentifier: keyIdentifier})); } + +/** + * Observes window errors that should fail the browser tests. + * @param {DOMWindow} window Windows to be obserbed. + */ +function observeWindowError(window) { + window.onerror = function(error) { + chrome.test.fail('window.onerror: ' + error); + }; +} diff --git a/chrome/test/data/extensions/api_test/image_writer_private/list_devices/test.js b/chrome/test/data/extensions/api_test/image_writer_private/list_devices/test.js index 03b75454fa48b..ec7be06b9966f 100644 --- a/chrome/test/data/extensions/api_test/image_writer_private/list_devices/test.js +++ b/chrome/test/data/extensions/api_test/image_writer_private/list_devices/test.js @@ -9,10 +9,12 @@ var expectedDevices = [{ 'vendor': 'Vendor 1', 'model': 'Model 1', 'capacity': 1 << 15, + 'removable': true, }, { 'vendor': 'Vendor 2', 'model': 'Model 2', 'capacity': 1 << 17, + 'removable': false, }]; @@ -35,6 +37,7 @@ function listRemovableDevicesCallback(deviceList) { chrome.test.assertEq(expected.vendor, dev.vendor); chrome.test.assertEq(expected.model, dev.model); chrome.test.assertEq(expected.capacity, dev.capacity); + chrome.test.assertEq(expected.removable, dev.removable); }); } diff --git a/chrome/test/data/extensions/api_test/notification_provider/basic_usage/background.js b/chrome/test/data/extensions/api_test/notification_provider/basic_usage/background.js index 176b4f05bdd4f..f62df7753cf55 100644 --- a/chrome/test/data/extensions/api_test/notification_provider/basic_usage/background.js +++ b/chrome/test/data/extensions/api_test/notification_provider/basic_usage/background.js @@ -4,6 +4,29 @@ const notificationProvider = chrome.notificationProvider; +var myId = chrome.runtime.id; +var id1 = "id1"; +var returnId = myId + "-" + id1; +var content = { + type: "basic", + iconUrl: "icon.png", + title: "Title", + message: "This is the message." +}; + +function createNotification(notificationId, options) { + return new Promise(function (resolve, reject) { + chrome.notifications.create(notificationId, options, function (id) { + if (chrome.runtime.lastError) { + reject(new Error("Unable to create notification")); + return; + } + resolve(id); + return; + }); + }); +}; + function notifyOnCleared(senderId, notificationId) { return new Promise(function (resolve, reject) { notificationProvider.notifyOnCleared(senderId, @@ -50,31 +73,35 @@ function notifyOnButtonClicked(senderId, notificationId, buttonIndex) { }); }; -function notifyOnPermissionLevelChanged(senderId, permissionLevel) { +function notifyOnPermissionLevelChanged(senderId, + notifierType, + permissionLevel) { return new Promise(function (resolve, reject) { notificationProvider.notifyOnPermissionLevelChanged( - senderId, - permissionLevel, - function (notifierExists) { - if (chrome.runtime.lastError || !notifierExists) { + senderId, + notifierType, + permissionLevel, + function (wasChanged) { + if (chrome.runtime.lastError || !wasChanged) { reject(new Error("Unable to send onPermissionLevelChanged message")); return; } - resolve(notifierExists); + resolve(wasChanged); return; }); }); }; -function notifyOnShowSettings(senderId) { +function notifyOnShowSettings(senderId, notifierType) { return new Promise(function (resolve, reject) { notificationProvider.notifyOnShowSettings(senderId, - function (notifierExists) { - if (chrome.runtime.lastError || !notifierExists) { + notifierType, + function (hasSettings) { + if (chrome.runtime.lastError || !hasSettings) { reject(new Error("Unable to send onShowSettings message")); return; } - resolve(notifierExists); + resolve(hasSettings); return; }); }); @@ -84,29 +111,74 @@ function failTest(testName) { chrome.test.fail(testName); }; -function testFunctions() { - var testName = "testFunctions"; - var notifierId; - var notId; - notificationProvider.onCreated.addListener(function(senderId, - notificationId, - content) { - notifierId = senderId; - notId = notificationId; - - notifyOnClicked(notifierId, notId) - .catch(function() { failTest("NotifyOnClickedFailed"); }) - .then(function() { return notifyOnButtonClicked(notifierId, notId, 0); }) - .catch(function() { failTest("NotifyOnButtonClickedFailed"); }) - .then(function () { return notifyOnCleared(notifierId, notId); }) - .catch(function() { failTest("NotifyOnClearedFailed"); }) - .then(function () { return notifyOnPermissionLevelChanged(notifierId, - "granted"); }) - .catch(function() { failTest("NotifyOnPermissionLevelChangedFailed"); }) - .then(function () { return notifyOnShowSettings(notifierId); }) - .catch(function() { failTest("NotifyOnShowSettingsFailed"); }) - .then(function() { chrome.test.succeed(testName); }); +function testNotifyOnClicked(){ + chrome.notifications.onClicked.addListener(function(id) { + chrome.test.succeed(); }); -}; -chrome.test.runTests([ testFunctions ]); + // Create a notification, so there will be one existing notification. + createNotification(id1, content) + .catch(function() { failTest("notifications.create"); }) + // Try to notify that an non-existent notification was clicked. + .then(function() { return notifyOnClicked(myId, "doesNotExist"); }) + // Fail if it returns true. + .then(function() { failTest("NotifyOnClicked"); }) + // Notify the sender that a notificaion was clicked. + .catch(function() { return notifyOnClicked(myId, returnId); }) + .catch(function() { failTest("NotifyOnClicked"); }); +} + +function testNotifyOnButtonClicked() { + chrome.notifications.onButtonClicked.addListener(function(id, buttonIndex) { + chrome.test.succeed(); + }); + + // Create a notification, so there will be one existing notification. + createNotification(id1, content) + .catch(function() { failTest("notifications.create"); }) + // Try to notify that a non-existent notification button was clicked. + .then(function() { return notifyOnButtonClicked(myId, "doesNotExist", 0)}) + .then(function() { failTest("NotifyOnButtonClicked"); }) + // Notify the sender that a notificaion button was clicked. + .catch(function() { return notifyOnButtonClicked(myId, returnId, 0); }) + .catch(function() { failTest("NotifyOnButtonClicked"); }); +} + +function testNotifyOnClosed() { + chrome.notifications.onClosed.addListener(function(id, byUser) { + chrome.test.succeed(); + }); + + // Create a notification, so there will be one existing notification. + createNotification(id1, content) + .catch(function() { failTest("notifications.create"); }) + // Try to notify that an non-existent notification was cleared. + .then(function () { return notifyOnCleared(myId, "doesNotExist"); }) + .then(function() { failTest("NotifyOnCleared"); }) + // Notify that the original notification was cleared. + .catch(function() { return notifyOnCleared(myId, returnId); }) + .catch(function() { failTest("NotifyOnCleared"); }); +} + +function testNotifyOnShowSettings() { + chrome.notifications.onShowSettings.addListener(function() { + chrome.test.succeed(); + }); + + // Create a notification, so there will be one existing notification. + createNotification(id1, content) + .catch(function() { failTest("notifications.create"); }) + // Try to notify a non existent sender that a user checked its settings. + .then(function () { return notifyOnShowSettings("DoesNotExist", + "application"); }) + // Fail if it returns true. + .then(function() { failTest("NotifyOnShowSettings"); }) + // Notify current notifier that a user checked its settings. + .catch(function () { return notifyOnShowSettings(myId, "application"); }) + .catch(function() { failTest("NotifyOnShowSettings"); }) +} + +chrome.test.runTests([ testNotifyOnClicked, + testNotifyOnButtonClicked, + testNotifyOnClosed, + testNotifyOnShowSettings ]); \ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/notification_provider/basic_usage/icon.png b/chrome/test/data/extensions/api_test/notification_provider/basic_usage/icon.png new file mode 100644 index 0000000000000..0692461fe6474 Binary files /dev/null and b/chrome/test/data/extensions/api_test/notification_provider/basic_usage/icon.png differ diff --git a/chrome/test/data/extensions/api_test/notification_provider/basic_usage/manifest.json b/chrome/test/data/extensions/api_test/notification_provider/basic_usage/manifest.json index 7fbb02299e917..026fc0087d76f 100644 --- a/chrome/test/data/extensions/api_test/notification_provider/basic_usage/manifest.json +++ b/chrome/test/data/extensions/api_test/notification_provider/basic_usage/manifest.json @@ -8,6 +8,6 @@ } }, "permissions": [ - "notificationProvider" + "notificationProvider", "notifications" ] } \ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/notification_provider/events/icon.png b/chrome/test/data/extensions/api_test/notification_provider/events/icon.png new file mode 100644 index 0000000000000..0692461fe6474 Binary files /dev/null and b/chrome/test/data/extensions/api_test/notification_provider/events/icon.png differ diff --git a/chrome/test/data/extensions/api_test/notification_provider/events/manifest.json b/chrome/test/data/extensions/api_test/notification_provider/events/manifest.json index 7c058e336aade..a4716d4025107 100644 --- a/chrome/test/data/extensions/api_test/notification_provider/events/manifest.json +++ b/chrome/test/data/extensions/api_test/notification_provider/events/manifest.json @@ -8,6 +8,6 @@ } }, "permissions": [ - "notificationProvider" + "notificationProvider", "notifications" ] } \ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/notification_provider/events/test.js b/chrome/test/data/extensions/api_test/notification_provider/events/test.js index 215699db68f71..d89fbe751c043 100644 --- a/chrome/test/data/extensions/api_test/notification_provider/events/test.js +++ b/chrome/test/data/extensions/api_test/notification_provider/events/test.js @@ -1,24 +1,30 @@ // Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -var testEvents = function() { - chrome.notificationProvider.onCreated.addListener(function(senderId, - notificationId, - options) { - chrome.test.succeed(); - }); +var idString = "id1"; + +function testOnCreated() { - chrome.notificationProvider.onUpdated.addListener(function(senderId, + var content = { + type: "basic", + iconUrl: "icon.png", + title: "Title", + message: "This is the message." + }; + + var createCallback = function (id) {} + chrome.notifications.create(idString, content, createCallback); + + chrome.notificationProvider.onCreated.addListener(function(senderId, notificationId, options) { - chrome.test.succeed(); - }); - - chrome.notificationProvider.onCleared.addListener(function(senderId, - notificationId) { + var str = notificationId.split("-"); + chrome.test.assertEq(idString, str[1]); + chrome.test.assertEq(options.title, content.title); + chrome.test.assertEq(options.message, content.message); chrome.test.succeed(); }); }; -chrome.test.runTests([ testEvents ]); \ No newline at end of file +chrome.test.runTests([ testOnCreated ]); \ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/notification_provider/test_app/app.js b/chrome/test/data/extensions/api_test/notification_provider/test_app/app.js new file mode 100644 index 0000000000000..27e345590003f --- /dev/null +++ b/chrome/test/data/extensions/api_test/notification_provider/test_app/app.js @@ -0,0 +1,10 @@ +// 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. + +chrome.app.runtime.onLaunched.addListener(function () { + chrome.app.window.create("window.html", { + id: "mainwin", + bounds: {width:600, height:1000} + }); +}); diff --git a/chrome/test/data/extensions/api_test/notification_provider/test_app/main.js b/chrome/test/data/extensions/api_test/notification_provider/test_app/main.js new file mode 100644 index 0000000000000..64858443d7b27 --- /dev/null +++ b/chrome/test/data/extensions/api_test/notification_provider/test_app/main.js @@ -0,0 +1,172 @@ +// 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. + +// Window initialization code. Set up event handlers. +window.addEventListener("load", function() { + // set up the event listeners. + chrome.notificationProvider.onCreated.addListener(notificationCreated); + chrome.notificationProvider.onUpdated.addListener(notificationUpdated); + chrome.notificationProvider.onCleared.addListener(notificationCleared); +}); + +function displayImage(text, bitmap, div) { + var image = document.createElement("p"); + image.appendChild(document.createTextNode(text)); + var imageCanvas = document.createElement("canvas"); + image.appendChild(imageCanvas); + div.appendChild(image); + + var imageContext = imageCanvas.getContext('2d'); + imageCanvas.width = bitmap.width; + imageCanvas.height = bitmap.height; + var imagedata = imageContext.createImageData(bitmap.width, + bitmap.height); + var dataView = new Uint8Array(bitmap.data); + for (var i = 0; i < bitmap.width * bitmap.height * 4; i += 1) { + imagedata.data[i] = dataView[i]; + } + imageContext.putImageData(imagedata, 0, 0); +} + +function addNotification(senderId, notificationId, details) { + var list = document.getElementById("notification_list"); + + var div = document.createElement("div"); + div.class = "notifications"; + div.id = senderId + notificationId; + + line = document.createElement("br"); + div.appendChild(line); + + // Create a close button. + var closeButton = document.createElement("button"); + closeButton.class = "closeButtons"; + var buttonText = document.createTextNode("close notificaiton"); + closeButton.appendChild(buttonText); + div.appendChild(closeButton); + closeButton.addEventListener("click", function(){ + clearNotification(senderId, notificationId) + }, false); + + // Display title. + var bold = document.createElement("b"); + var title = document.createElement("p"); + bold.appendChild(document.createTextNode(details.title)); + title.appendChild(bold); + div.appendChild(title); + + // Display notification ID. + var id = document.createElement("p"); + id.appendChild(document.createTextNode("Notification ID: " + notificationId)); + div.appendChild(id); + + // Disply the type of notication. + var notType = document.createElement("p"); + notType.appendChild(document.createTextNode("Type: " + details.type)); + div.appendChild(notType); + + // Display the priority field of the notification. + var priority = document.createElement("p"); + priority.appendChild(document.createTextNode("Priority: " + + details.priority)); + div.appendChild(priority); + + // Display the message of the notification. + var message = document.createElement("p"); + message.appendChild(document.createTextNode("Message: " + details.message)); + div.appendChild(message); + + // Display the icon image of the notification. + displayImage("Icon: ", details.iconBitmap, div); + + // Display the context message of the notification if it has one. + if ("contextMessage" in details) { + var message = document.createElement("p"); + message.appendChild(document.createTextNode("Message: " + details.message)); + div.appendChild(message); + } + + // Display the progress of the notification if it has one. + if (details.type == "progress" && "progress" in details) { + var progress = document.createElement("p"); + progress.appendChild(document.createTextNode("Progress: " + + details.progress)); + div.appendChild(progress); + } + + // Display if the notification is clickable. + if ("isClickable" in details) { + var clickable = document.createElement("p"); + clickable.appendChild(document.createTextNode("IsClickable: " + + details.isClickable)); + div.appendChild(clickable); + } + + // Display the time the notification was created. + if ("eventTime" in details) { + var time = document.createElement("p"); + time.appendChild(document.createTextNode("Event Time: " + + details.eventTime)); + div.appendChild(time); + } + + // Display the list data of the notification if it's of list type. + if (details.type = "list" && "items" in details) { + for (var i = 0, size = details.items.length; i < size; i++) { + var item = document.createElement("p"); + item.appendChild(document.createTextNode( + "Item " + (i+1) + ": " + + details.items[i].title + " - " + + details.items[i].message)); + div.appendChild(item); + } + } + + // Display image of the notification if it's of image type. + if (details.type = "image" && "imageBitmap" in details) { + displayImage("Image: ", details.imageBitmap, div); + } + + // Display the buttons of the notification if it has some. + if ("buttons" in details) { + for (var i = 0, size = details.buttons.length; i < size; i++) { + var button = document.createElement("p"); + button.appendChild(document.createTextNode( + "Button " + (i+1) + ": " + details.buttons[i].title)); + div.appendChild(button); + if ("iconBitmap" in details.buttons[i]) { + displayImage("Button " + (i+1) + ": ", + details.buttons[i].iconBitmap, + div); + } + } + } + + div.appendChild(document.createElement("br")); + list.appendChild(div); +} + +function clearedCallback(ifCleared){} + +function clearNotification(senderId, notificationId) { + var list = document.getElementById("notification_list"); + list.removeChild(document.getElementById(senderId + notificationId)); + chrome.notificationProvider.notifyOnCleared(senderId, + notificationId, + clearedCallback); +} + +function notificationCreated(senderId, notificationId, details){ + addNotification(senderId, notificationId, details); +} + +function notificationUpdated(senderId, notificationId, details){ + var list = document.getElementById("notification_list"); + list.removeChild(document.getElementById(senderId + notificationId)); + addNotification(senderId, notificationId, details); +} + +function notificationCleared(senderId, notificationId){ + clearNotification(senderId, notificationId); +} diff --git a/chrome/test/data/extensions/api_test/notification_provider/test_app/manifest.json b/chrome/test/data/extensions/api_test/notification_provider/test_app/manifest.json new file mode 100644 index 0000000000000..7ae65cea6391d --- /dev/null +++ b/chrome/test/data/extensions/api_test/notification_provider/test_app/manifest.json @@ -0,0 +1,14 @@ +{ + "name": "New API Notification Provider Test", + "description": "Tests the notificationProvider API", + "manifest_version" : 2, + "version" : "0.1.0", + "app" : { + "background" : { + "scripts" : ["app.js"] + } + }, + "permissions" : [ + "notificationProvider" + ] +} diff --git a/chrome/test/data/extensions/api_test/notification_provider/test_app/styles.css b/chrome/test/data/extensions/api_test/notification_provider/test_app/styles.css new file mode 100644 index 0000000000000..dd1c6ed37d254 --- /dev/null +++ b/chrome/test/data/extensions/api_test/notification_provider/test_app/styles.css @@ -0,0 +1,12 @@ +/* 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. */ + +h1 { + margin-top: 0 +} + +body { + font-family: "Open Sans", Helvetica, Arial; + font-size: 10pt; +} diff --git a/chrome/test/data/extensions/api_test/notification_provider/test_app/window.html b/chrome/test/data/extensions/api_test/notification_provider/test_app/window.html new file mode 100644 index 0000000000000..9af9d34a0eab5 --- /dev/null +++ b/chrome/test/data/extensions/api_test/notification_provider/test_app/window.html @@ -0,0 +1,24 @@ + + + + + Notification Provider API Test + + + +

    Notification Provider API Test

    +

    This sample demonstrates the use of the notificationProvider API.

    +
    +
    +

    Notifications will be displayed in this format:

    +

    Notification ID: 10000000

    +

    Type: basic

    +

    Message: content goes here

    +
    +
    +
    + + + diff --git a/chrome/test/data/extensions/platform_apps/app_view/shim/main.js b/chrome/test/data/extensions/platform_apps/app_view/shim/main.js index 8a2f041e2388b..5b356a2d061d6 100644 --- a/chrome/test/data/extensions/platform_apps/app_view/shim/main.js +++ b/chrome/test/data/extensions/platform_apps/app_view/shim/main.js @@ -73,7 +73,7 @@ function testAppViewBasic(appToEmbed) { LOG('appToEmbed ' + appToEmbed); // Step 1: Attempt to connect to a non-existant app. LOG('attempting to connect to non-existant app.'); - appview.connect('abc123', function(success) { + appview.connect('abc123', {}, function(success) { // Make sure we fail. if (success) { embedder.test.fail(); @@ -82,7 +82,7 @@ function testAppViewBasic(appToEmbed) { LOG('failed to connect to non-existant app.'); LOG('attempting to connect to known app.'); // Step 2: Attempt to connect to an app we know exists. - appview.connect(appToEmbed, function(success) { + appview.connect(appToEmbed, {}, function(success) { // Make sure we don't fail. if (!success) { embedder.test.fail(); @@ -94,8 +94,41 @@ function testAppViewBasic(appToEmbed) { document.body.appendChild(appview); }; +function testAppViewRefusedDataShouldFail(appToEmbed) { + var appview = new AppView(); + LOG('appToEmbed ' + appToEmbed); + LOG('Attempting to connect to app with refused params.'); + appview.connect(appToEmbed, { 'foo': 'bar' }, function(success) { + // Make sure we fail. + if (success) { + embedder.test.fail(); + return; + } + embedder.test.succeed(); + }); + document.body.appendChild(appview); +}; + +function testAppViewGoodDataShouldSucceed(appToEmbed) { + var appview = new AppView(); + LOG('appToEmbed ' + appToEmbed); + LOG('Attempting to connect to app with good params.'); + // Step 2: Attempt to connect to an app with good params. + appview.connect(appToEmbed, { 'foo': 'bleep' }, function(success) { + // Make sure we don't fail. + if (!success) { + embedder.test.fail(); + return; + } + embedder.test.succeed(); + }); + document.body.appendChild(appview); +}; + embedder.test.testList = { - 'testAppViewBasic': testAppViewBasic + 'testAppViewBasic': testAppViewBasic, + 'testAppViewRefusedDataShouldFail': testAppViewRefusedDataShouldFail, + 'testAppViewGoodDataShouldSucceed': testAppViewGoodDataShouldSucceed }; onload = function() { diff --git a/chrome/test/data/extensions/platform_apps/app_view/shim/skeleton/main.js b/chrome/test/data/extensions/platform_apps/app_view/shim/skeleton/main.js index 3b58b2f02ceac..2430c37a987f1 100644 --- a/chrome/test/data/extensions/platform_apps/app_view/shim/skeleton/main.js +++ b/chrome/test/data/extensions/platform_apps/app_view/shim/skeleton/main.js @@ -3,6 +3,13 @@ // found in the LICENSE file. chrome.app.runtime.onEmbedRequested.addListener(function(request) { - request.allow('main.html'); + if (!request.data.foo) { + request.allow('main.html'); + return; + } else if (request.data.foo == 'bar') { + request.deny(); + } else if (request.data.foo == 'bleep') { + request.allow('main.html'); + } }); diff --git a/chrome/test/data/extensions/platform_apps/custom_launcher_page/manifest.json b/chrome/test/data/extensions/platform_apps/custom_launcher_page/manifest.json index 5850b28f5ed20..175bc5ea4a253 100644 --- a/chrome/test/data/extensions/platform_apps/custom_launcher_page/manifest.json +++ b/chrome/test/data/extensions/platform_apps/custom_launcher_page/manifest.json @@ -4,10 +4,12 @@ "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv7Qi8BghDr2b2Wdg5vv7vGWQxWpo6dD4Jt3okhb3oOF0zmnhr1G/e16J8WxtygaF2mshjVP11/j/yu8n7AsrFw5hwi3ROwmsB8T1vB+rHGh9NfF/iX8w1z2rLkXlPemHof7nzC67Y3TRrl0ONqIO4ef9z4NEnnzQ0EeIX51924G5pj9YjTderWIso9+8mehelDwMgBZu66T1jTuxq4SOEvuDe9IKXwJfVPfhTf0f8YAH+NUdleKY+2zR7u8BDK42OkhhKs4XB3ZHDTr+n7KObXYXJukpr/eNqbXyU4lsUlJobFDzygZxjEOw87HhP9fK3V0v+eYrQ4+9JctqxorT5wIDAQAB", "version": "1", "manifest_version": 2, - // Currently necessary to give it an app context. "app": { "background": { "scripts": ["dummy.js"] } + }, + "launcher_page": { + "page": "main.html" } } diff --git a/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/index.html b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/index.html new file mode 100644 index 0000000000000..84c6401f6f0a3 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/index.html @@ -0,0 +1,11 @@ + + + + + + + diff --git a/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/index.js b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/index.js new file mode 100644 index 0000000000000..ba707833f374e --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/index.js @@ -0,0 +1,8 @@ +// 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. + +onload = function() { + chrome.power.requestKeepAwake('system'); + chrome.test.sendMessage('launched'); +}; diff --git a/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/main.js b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/main.js new file mode 100644 index 0000000000000..f9e810e42ff1b --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/main.js @@ -0,0 +1,7 @@ +// 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. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.app.window.create('index.html', {}); +}); diff --git a/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/manifest.json b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/manifest.json new file mode 100644 index 0000000000000..d904774051a74 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/ephemeral_apps/power/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Ephemeral Apps Power Tests", + "version": "1.0", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["main.js"] + } + }, + "permissions": ["power"] +} diff --git a/chrome/test/data/extensions/platform_apps/outer_bounds/main.html b/chrome/test/data/extensions/platform_apps/outer_bounds/main.html new file mode 100644 index 0000000000000..8fe0840e5ef49 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/outer_bounds/main.html @@ -0,0 +1,10 @@ + + + + + + diff --git a/chrome/test/data/extensions/platform_apps/outer_bounds/manifest.json b/chrome/test/data/extensions/platform_apps/outer_bounds/manifest.json new file mode 100644 index 0000000000000..86fc75688cb58 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/outer_bounds/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Platform App Test: outer bounds platform app", + "version": "1", + "app": { + "background": { + "scripts": ["test.js"] + } + } +} diff --git a/chrome/test/data/extensions/platform_apps/outer_bounds/test.js b/chrome/test/data/extensions/platform_apps/outer_bounds/test.js new file mode 100644 index 0000000000000..331d2b118a69e --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/outer_bounds/test.js @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.sendMessage('Launched', function (response) { + var frameType = response == 'color' ? { color: '#ff0000' } : response; + chrome.app.window.create('main.html', { + frame: frameType, + outerBounds: { + left: 10, + top: 11, + width: 300, + height: 301, + minWidth: 200, + minHeight: 201, + maxWidth: 400, + maxHeight: 401 + } + }, function () { + // Send this again as the test is waiting for the window to be ready. + chrome.test.sendMessage('Launched'); + }); + }); +}); diff --git a/chrome/test/data/extensions/platform_apps/web_view/background/background.html b/chrome/test/data/extensions/platform_apps/web_view/background/background.html new file mode 100644 index 0000000000000..b8da29fe4e650 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/background/background.html @@ -0,0 +1,11 @@ + + + + + + + diff --git a/chrome/test/data/extensions/platform_apps/web_view/background/background.js b/chrome/test/data/extensions/platform_apps/web_view/background/background.js new file mode 100644 index 0000000000000..b32413ba8462b --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/background/background.js @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var pass = chrome.test.callbackPass; + +var WEBVIEW_SRC = "data:text/html,One"; + +chrome.test.runTests([ + // Tests that embedding inside a background page loads. + function inDOM() { + var webview = document.querySelector('webview'); + webview.addEventListener('contentload', pass()); + webview.setAttribute('src', WEBVIEW_SRC); + }, + + // Tests that creating and attaching a WebView element inside a background + // page loads. + function newWebView() { + var webview = new WebView(); + webview.addEventListener('contentload', pass()); + webview.src = WEBVIEW_SRC; + document.body.appendChild(webview); + } +]); diff --git a/chrome/test/data/extensions/platform_apps/web_view/background/manifest.json b/chrome/test/data/extensions/platform_apps/web_view/background/manifest.json new file mode 100644 index 0000000000000..bd464a14fd787 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/web_view/background/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "Platform App Test: background page access", + "version": "1", + "permissions": [ + "webview" + ], + "app": { + "background": { + "page": "background.html" + } + } +} diff --git a/chrome/test/data/extensions/platform_apps/window_api_interactive/test.js b/chrome/test/data/extensions/platform_apps/window_api_interactive/test.js index 165c9ab4944a8..9f6784901fbb6 100644 --- a/chrome/test/data/extensions/platform_apps/window_api_interactive/test.js +++ b/chrome/test/data/extensions/platform_apps/window_api_interactive/test.js @@ -49,6 +49,58 @@ function testWindowNeverGetsFocus(win) { }); } +// Test that the window's content size is the same as our inner bounds. +// This has to be an interactive test because contentWindow.innerWidth|Height is +// sometimes 0 in the browser test due to an unidentified race condition. +function testInnerBounds() { + var innerBounds = { + width: 300, + height: 301, + minWidth: 200, + minHeight: 201, + maxWidth: 400, + maxHeight: 401 + }; + + function assertInnerBounds(win) { + chrome.test.assertEq(300, win.contentWindow.innerWidth); + chrome.test.assertEq(301, win.contentWindow.innerHeight); + + chrome.test.assertEq(300, win.innerBounds.width); + chrome.test.assertEq(301, win.innerBounds.height); + chrome.test.assertEq(200, win.innerBounds.minWidth); + chrome.test.assertEq(201, win.innerBounds.minHeight); + chrome.test.assertEq(400, win.innerBounds.maxWidth); + chrome.test.assertEq(401, win.innerBounds.maxHeight); + } + + chrome.test.runTests([ + function createFrameChrome() { + chrome.app.window.create('test.html', { + innerBounds: innerBounds + }, callbackPass(function (win) { + assertInnerBounds(win); + })); + }, + function createFrameNone() { + chrome.app.window.create('test.html', { + frame: 'none', + innerBounds: innerBounds + }, callbackPass(function (win) { + assertInnerBounds(win); + })); + }, + function createFrameColor() { + chrome.app.window.create('test.html', { + frame: {color: '#ff0000'}, + innerBounds: innerBounds + }, callbackPass(function (win) { + assertInnerBounds(win); + })); + } + ]); +} + function testCreate() { chrome.test.runTests([ function createUnfocusedWindow() { diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/background.js b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/background.js new file mode 100644 index 0000000000000..e4515fd18f68f --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/background.js @@ -0,0 +1,42 @@ +// 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. + +function testAlphaEnabled(testId, setOption, setValue, expectedValue) { + var createOptions = { frame: 'none' }; + if (setOption) + createOptions.alphaEnabled = setValue; + + chrome.app.window.create('index.html', + createOptions, + chrome.test.callbackPass(function(win) { + chrome.test.assertEq(expectedValue, win.alphaEnabled()); + })); +} + +// All these tests are run with app.window.alpha permission +// set and on a system with alpha (transparency) support. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + + // Window is created with alphaEnabled set to true. + function testAlphaEnabledPermTransInitTrue() { + testAlphaEnabled('testAlphaEnabledPermTransInitTrue', + true, true, true); + }, + + // Window is created with alphaEnabled set to false. + function testAlphaEnabledPermTransInitFalse() { + testAlphaEnabled('testAlphaEnabledPermTransInitFalse', + true, false, false); + }, + + // Window is created with alphaEnabled not explicitly set. + function testAlphaEnabledPermTransNoInit() { + testAlphaEnabled('testAlphaEnabledPermTransNoInit', + false, false, false); + } + + ]); +}); diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/index.html b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/index.html new file mode 100644 index 0000000000000..c341a403902b9 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/index.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/manifest.json new file mode 100644 index 0000000000000..091850c227dab --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_has_alpha/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Windows API - alphaEnabled (platform supports Alpha)", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": ["app.window.alpha"] +} diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/background.js b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/background.js new file mode 100644 index 0000000000000..5fa77a72945ab --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/background.js @@ -0,0 +1,42 @@ +// 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. + +function testAlphaEnabled(testId, setOption, setValue, expectedValue) { + var createOptions = { frame: 'none' }; + if (setOption) + createOptions.alphaEnabled = setValue; + + chrome.app.window.create('index.html', + createOptions, + chrome.test.callbackPass(function(win) { + chrome.test.assertEq(expectedValue, win.alphaEnabled()); + })); +} + +// All these tests are run with app.window.alpha permission +// set and on a system without alpha (transparency) support. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + + // Window is created with alphaEnabled set to true. + function testAlphaEnabledPermNoTransInitTrue() { + testAlphaEnabled('testAlphaEnabledPermNoTransInitTrue', + true, true, false); + }, + + // Window is created with alphaEnabled set to false. + function testAlphaEnabledPermNoTransInitFalse() { + testAlphaEnabled('testAlphaEnabledPermNoTransInitFalse', + true, false, false); + }, + + // Window is created with alphaEnabled not explicitly set. + function testAlphaEnabledPermNoTransNoInit() { + testAlphaEnabled('testAlphaEnabledPermNoTransNoInit', + false, false, false); + } + + ]); +}); diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/index.html b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/index.html new file mode 100644 index 0000000000000..c341a403902b9 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/index.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/manifest.json new file mode 100644 index 0000000000000..e4d08efd1c62b --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/has_permissions_no_alpha/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Windows API - alphaEnabled (Platform doesn't support Alpha)", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": ["app.window.alpha"] +} diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/background.js b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/background.js new file mode 100644 index 0000000000000..b4048e2b7b31c --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/background.js @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var error = "The alphaEnabled option requires dev channel or newer."; + +function testAlphaEnabled(testId, setValue) { + var createOptions = { frame: 'none' }; + createOptions.alphaEnabled = setValue; + + chrome.app.window.create('index.html', + createOptions, + chrome.test.callbackFail(error)); +} + +// All these tests are run in Stable channel with app.window.alpha +// permission set. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + + // Window is created with alphaEnabled set to true. + function testAlphaEnabledStableInitTrue() { + testAlphaEnabled('testAlphaEnabledStableInitTrue', true); + }, + + // Window is created with alphaEnabled set to false. + function testAlphaEnabledStableInitFalse() { + testAlphaEnabled('testAlphaEnabledStableInitFalse', false); + }, + + ]); +}); diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/index.html b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/index.html new file mode 100644 index 0000000000000..c341a403902b9 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/index.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/manifest.json new file mode 100644 index 0000000000000..3ba4e42165d80 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/in_stable/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Windows API - alphaEnabled (in Stable Channel)", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": ["app.window.alpha"] +} diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/background.js b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/background.js new file mode 100644 index 0000000000000..a34199ec678b0 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/background.js @@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var error = "The alphaEnabled option requires app.window.alpha permission."; + +function testAlphaEnabled(testId, setValue) { + var createOptions = { frame: 'none' }; + createOptions.alphaEnabled = setValue; + + chrome.app.window.create('index.html', + createOptions, + chrome.test.callbackFail(error)); +} + +// All these tests are run without app.window.alpha permission +// set. Test results are the same regardless of whether or not +// alpha (transparency) is supported by the platform. + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + + // Window is created with alphaEnabled set to true. + function testAlphaEnabledNoPermInitTrue() { + testAlphaEnabled('testAlphaEnabledNoPermInitTrue', true); + }, + + // Window is created with alphaEnabled set to false. + function testAlphaEnabledNoPermInitFalse() { + testAlphaEnabled('testAlphaEnabledNoPermInitFalse', false); + }, + + ]); +}); + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/index.html b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/index.html new file mode 100644 index 0000000000000..c341a403902b9 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/index.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/manifest.json new file mode 100644 index 0000000000000..3856afc428a85 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/no_permissions/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Windows API - alphaEnabled (No Permissions)", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + } +} diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/background.js b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/background.js new file mode 100644 index 0000000000000..0f078430d149d --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/background.js @@ -0,0 +1,26 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var error = 'The alphaEnabled option can only be used with "frame: \'none\'".'; + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + function testAlphaEnabledFrameNone() { + chrome.app.window.create('index.html', { + frame: 'none', + alphaEnabled: true, + }, chrome.test.callbackPass(function (win) {})); + }, + function testAlphaEnabledFrameChrome() { + chrome.app.window.create('index.html', { + alphaEnabled: true, + }, chrome.test.callbackFail(error)); + }, + function testAlphaDisabledFrameChrome() { + chrome.app.window.create('index.html', { + }, chrome.test.callbackPass(function (win) {})); + }, + ]); +}); + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/index.html b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/index.html new file mode 100644 index 0000000000000..c341a403902b9 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/index.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/manifest.json new file mode 100644 index 0000000000000..eee48fa3093cb --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_alpha_enabled/wrong_frame_type/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Windows API - alphaEnabled (wrong frame type)", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": ["app.window.alpha"] +} diff --git a/chrome/test/data/extensions/platform_apps/windows_api_shape/background.js b/chrome/test/data/extensions/platform_apps/windows_api_shape/background.js new file mode 100644 index 0000000000000..d7624d2d7d31d --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_shape/background.js @@ -0,0 +1,42 @@ +// 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. + +function testWindowShape(testId, region) { + var createOptions = { id: testId, frame: 'none' }; + + chrome.app.window.create('index.html', + createOptions, + chrome.test.callbackPass(function(win) { + win.setShape(region) + })); +} + +chrome.app.runtime.onLaunched.addListener(function() { + chrome.test.runTests([ + + // Window shape is a single rect. + function testWindowShapeSingleRect() { + testWindowShape('testWindowShapeSingleRect', + {rects: [{left:100, top:50, width:50, height:100}]}); + }, + + // Window shape is multiple rects. + function testWindowShapeMultipleRects() { + testWindowShape('testWindowShapeMultipleRects', + {rects: [{left:100, top:50, width:50, height:100}, + {left:200, top:100, width:50, height:50}]}); + }, + + // Window shape is null. + function testWindowShapeNull() { + testWindowShape('testWindowShapeNull', {}); + }, + + // Window shape is empty. + function testWindowShapeEmpty() { + testWindowShape('testWindowShapeEmpty', {rects: []}); + }, + + ]); +}); \ No newline at end of file diff --git a/chrome/test/data/extensions/platform_apps/windows_api_shape/index.html b/chrome/test/data/extensions/platform_apps/windows_api_shape/index.html new file mode 100644 index 0000000000000..c341a403902b9 --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_shape/index.html @@ -0,0 +1 @@ + diff --git a/chrome/test/data/extensions/platform_apps/windows_api_shape/manifest.json b/chrome/test/data/extensions/platform_apps/windows_api_shape/manifest.json new file mode 100644 index 0000000000000..d2f4b849d042a --- /dev/null +++ b/chrome/test/data/extensions/platform_apps/windows_api_shape/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Windows API - setShape", + "version": "1", + "manifest_version": 2, + "app": { + "background": { + "scripts": ["background.js"] + } + }, + "permissions": ["app.window.shape"] +} diff --git a/chrome/test/data/extensions/webui/can_embed_extension_options.js b/chrome/test/data/extensions/webui/can_embed_extension_options.js new file mode 100644 index 0000000000000..57f538dee8048 --- /dev/null +++ b/chrome/test/data/extensions/webui/can_embed_extension_options.js @@ -0,0 +1,23 @@ +// 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. + +// out/Debug/browser_tests +// --gtest_filter=ExtensionWebUITest.CanEmbedExtensionOptions +if (!chrome || !chrome.test || !chrome.test.sendMessage) { + console.error('chrome.test.sendMessage is unavailable on ' + + document.location.href); + domAutomationController.send(false); + return; +} + +chrome.test.sendMessage('ready', function(reply) { + var extensionoptions = document.createElement('extensionoptions'); + extensionoptions.addEventListener('load', function() { + chrome.test.sendMessage('guest loaded'); + }); + extensionoptions.setAttribute('extension', reply); + document.body.appendChild(extensionoptions); +}); + +domAutomationController.send(true); diff --git a/chrome/test/data/file_manager/unit_tests/device_handler_unittest.js b/chrome/test/data/file_manager/unit_tests/device_handler_unittest.js index e0c897667ff72..be06f80aebb0c 100644 --- a/chrome/test/data/file_manager/unit_tests/device_handler_unittest.js +++ b/chrome/test/data/file_manager/unit_tests/device_handler_unittest.js @@ -69,7 +69,10 @@ function setUp() { } }, runtime: { - getURL: function(path) { return path; } + getURL: function(path) { return path; }, + onStartup: { + addListener: function() {} + } } }; diff --git a/chrome/test/data/file_manager/unit_tests/file_operation_manager_unittest.js b/chrome/test/data/file_manager/unit_tests/file_operation_manager_unittest.js index 260b8b82b1060..d447c1b9cdc7a 100644 --- a/chrome/test/data/file_manager/unit_tests/file_operation_manager_unittest.js +++ b/chrome/test/data/file_manager/unit_tests/file_operation_manager_unittest.js @@ -3,6 +3,47 @@ // found in the LICENSE file. 'use strict'; +/** + * Mock of chrome.runtime. + * @type {Object} + * @const + */ +chrome.runtime = { + lastError: null +}; + +/** + * Mock of chrome.power. + * @type {Object} + * @const + */ +chrome.power = { + requestKeepAwake: function() { + chrome.power.keepAwakeRequested = true; + }, + releaseKeepAwake: function() { + chrome.power.keepAwakeRequested = false; + }, + keepAwakeRequested: false +}; + +/** + * Mock of chrome.fileBrowserPrivate. + * @type {Object} + * @const + */ +chrome.fileBrowserPrivate = { + onCopyProgress: { + addListener: function(callback) { + chrome.fileBrowserPrivate.onCopyProgress.listener_ = callback; + }, + removeListener: function() { + chrome.fileBrowserPrivate.onCopyProgress.listener_ = null; + }, + listener_: null + } +}; + /** * Reports the result of promise to the test system. * @param {Promise} promise Promise to be fulfilled or rejected. @@ -14,36 +55,113 @@ function reportPromise(promise, callback) { callback.bind(null, false), function(error) { if (error instanceof FileOperationManager.Error) { - console.log('FileOperationManager.Error: code=' + error.code); + console.error('FileOperationManager.Error: code=' + error.code); } else { - console.log(error.stack || error.name || error); + console.error(error.stack || error.name || error); } callback(true); }); } +/** + * Size of directory. + * @type {number} + * @const + */ +var DIRECTORY_SIZE = -1; + +/** + * Creates test file system. + * @param {string} id File system ID. + * @param {Object.} entries Map of entries' paths and their + * size. If the size is equals to DIRECTORY_SIZE, the entry is derectory. + */ +function createTestFileSystem(id, entries) { + var fileSystem = new TestFileSystem(id); + for (var path in entries) { + if (entries[path] === DIRECTORY_SIZE) { + fileSystem.entries[path] = new MockDirectoryEntry(fileSystem, path); + } else { + fileSystem.entries[path] = + new MockFileEntry(fileSystem, path, {size: entries[path]}); + } + } + return fileSystem; +} + +/** + * Resolves URL on the file system. + * @param {FakeFileSystem} fileSystem Fake file system. + * @param {string} url URL. + * @param {function(MockEntry)} success Success callback. + * @param {function()} failure Failure callback. + */ +function resolveTestFileSystemURL(fileSystem, url, success, failure) { + for (var name in fileSystem.entries) { + var entry = fileSystem.entries[name]; + if (entry.toURL() == url) { + success(entry); + return; + } + } + failure(); +} + +/** + * Waits for events until 'success'. + * @param {FileOperationManager} fileOperationManager File operation manager. + * @return {Promise} Promise to be fulfilled with an event list. + */ +function waitForEvents(fileOperationManager) { + return new Promise(function(fulfill) { + var events = []; + fileOperationManager.addEventListener('copy-progress', function(event) { + events.push(event); + if (event.reason === 'SUCCESS') + fulfill(events); + }); + fileOperationManager.addEventListener('entries-changed', function(event) { + events.push(event); + }); + fileOperationManager.addEventListener('delete', function(event) { + events.push(event); + if (event.reason === 'SUCCESS') + fulfill(events); + }); + }); +} + +/** + * Test target. + * @type {FileOperationManager} + */ +var fileOperationManager; + +/** + * Initializes the test environment. + */ +function setUp() { + fileOperationManager = new FileOperationManager(); +} + /** * Tests the fileOperationUtil.resolvePath function. * @param {function(boolean:hasError)} callback Callback to be passed true on * error. */ function testResolvePath(callback) { - var fileEntry = new MockFileEntry('testVolume', '/file', {}); - var directoryEntry = new MockDirectoryEntry('testVolume', '/directory', {}); - var root = new MockDirectoryEntry('testVolume', '/', { - '/file': fileEntry, - '/directory': directoryEntry + var fileSystem = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/file': 10, + '/directory': DIRECTORY_SIZE }); + var root = fileSystem.root; var rootPromise = fileOperationUtil.resolvePath(root, '/'); var filePromise = fileOperationUtil.resolvePath(root, '/file'); var directoryPromise = fileOperationUtil.resolvePath(root, '/directory'); var errorPromise = fileOperationUtil.resolvePath(root, '/not_found').then( - function() { - assertTrue(false, 'The NOT_FOUND error is not reported.'); - }, - function(error) { - assertEquals('NotFoundError', error.name); - }); + function() { assertTrue(false, 'The NOT_FOUND error is not reported.'); }, + function(error) { return error.name; }); reportPromise(Promise.all([ rootPromise, filePromise, @@ -51,10 +169,10 @@ function testResolvePath(callback) { errorPromise ]).then(function(results) { assertArrayEquals([ - root, - fileEntry, - directoryEntry, - undefined + fileSystem.entries['/'], + fileSystem.entries['/file'], + fileSystem.entries['/directory'], + 'NotFoundError' ], results); }), callback); } @@ -65,39 +183,37 @@ function testResolvePath(callback) { * error. */ function testDeduplicatePath(callback) { - var directoryEntry1 = new MockDirectoryEntry('testVolume', '/directory', {}); - var directoryEntry2 = new MockDirectoryEntry( - 'testVolume', - '/directory', - {'file.txt': new MockFileEntry('testVolume', '/file.txt', {})}); - var directoryEntry3 = new MockDirectoryEntry( - 'testVolume', - '/directory', - { - 'file.txt': new MockFileEntry('testVolume', '/file.txt', {}), - 'file (1).txt': new MockFileEntry('testVolume', '/file (1).txt', {}), - 'file (2).txt': new MockFileEntry('testVolume', '/file (2).txt', {}), - 'file (3).txt': new MockFileEntry('testVolume', '/file (3).txt', {}), - 'file (4).txt': new MockFileEntry('testVolume', '/file (4).txt', {}), - 'file (5).txt': new MockFileEntry('testVolume', '/file (5).txt', {}), - 'file (6).txt': new MockFileEntry('testVolume', '/file (6).txt', {}), - 'file (7).txt': new MockFileEntry('testVolume', '/file (7).txt', {}), - 'file (8).txt': new MockFileEntry('testVolume', '/file (8).txt', {}), - 'file (9).txt': new MockFileEntry('testVolume', '/file (9).txt', {}) - }); + var fileSystem1 = createTestFileSystem('testVolume', {'/': DIRECTORY_SIZE}); + var fileSystem2 = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/file.txt': 10 + }); + var fileSystem3 = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/file.txt': 10, + '/file (1).txt': 10, + '/file (2).txt': 10, + '/file (3).txt': 10, + '/file (4).txt': 10, + '/file (5).txt': 10, + '/file (6).txt': 10, + '/file (7).txt': 10, + '/file (8).txt': 10, + '/file (9).txt': 10, + }); var nonExistingPromise = - fileOperationUtil.deduplicatePath(directoryEntry1, 'file.txt'). + fileOperationUtil.deduplicatePath(fileSystem1.root, 'file.txt'). then(function(path) { assertEquals('file.txt', path); }); var existingPathPromise = - fileOperationUtil.deduplicatePath(directoryEntry2, 'file.txt'). + fileOperationUtil.deduplicatePath(fileSystem2.root, 'file.txt'). then(function(path) { assertEquals('file (1).txt', path); }); var failedPromise = - fileOperationUtil.deduplicatePath(directoryEntry3, 'file.txt'). + fileOperationUtil.deduplicatePath(fileSystem3.root, 'file.txt'). then(function() { assertTrue(false, 'FileOperationManager.Error is not reported.'); }, function(error) { @@ -112,3 +228,206 @@ function testDeduplicatePath(callback) { ]); reportPromise(testPromise, callback); } + +/** + * Tests the fileOperationUtil.paste. + * @param {function(boolean:hasError)} callback Callback to be passed true on + * error. + */ +function testCopy(callback) { + // Prepare entries and their resolver. + var fileSystem = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/test.txt': 10, + }); + window.webkitResolveLocalFileSystemURL = + resolveTestFileSystemURL.bind(null, fileSystem); + + chrome.fileBrowserPrivate.startCopy = + function(source, destination, newName, callback) { + var makeStatus = function(type) { + return {type: type, sourceUrl: source, destinationUrl: destination}; + }; + callback(1); + var listener = chrome.fileBrowserPrivate.onCopyProgress.listener_; + listener(1, makeStatus('begin_copy_entry')); + listener(1, makeStatus('progress')); + var newPath = joinPath('/', newName); + fileSystem.entries[newPath] = + fileSystem.entries['/test.txt'].clone(newPath); + listener(1, makeStatus('end_copy_entry')); + listener(1, makeStatus('success')); + }; + + // Observing manager's events. + var eventsPromise = waitForEvents(fileOperationManager); + + // Verify the events. + reportPromise(eventsPromise.then(function(events) { + var firstEvent = events[0]; + assertEquals('BEGIN', firstEvent.reason); + assertEquals(1, firstEvent.status.numRemainingItems); + assertEquals(0, firstEvent.status.processedBytes); + assertEquals(1, firstEvent.status.totalBytes); + + var lastEvent = events[events.length - 1]; + assertEquals('SUCCESS', lastEvent.reason); + assertEquals(0, lastEvent.status.numRemainingItems); + assertEquals(10, lastEvent.status.processedBytes); + assertEquals(10, lastEvent.status.totalBytes); + + assertTrue(events.some(function(event) { + return event.type === 'entries-changed' && + event.kind === util.EntryChangedKind.CREATED && + event.entries[0].fullPath === '/test (1).txt'; + })); + + assertFalse(events.some(function(event) { + return event.type === 'delete'; + })); + }), callback); + + fileOperationManager.paste( + [fileSystem.entries['/test.txt']], + fileSystem.entries['/'], + false); +} + +/** + * Tests the fileOperationUtil.paste for move. + * @param {function(boolean:hasError)} callback Callback to be passed true on + * error. + */ +function testMove(callback) { + // Prepare entries and their resolver. + var fileSystem = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/directory': DIRECTORY_SIZE, + '/test.txt': 10, + }); + window.webkitResolveLocalFileSystemURL = + resolveTestFileSystemURL.bind(null, fileSystem); + + // Observing manager's events. + var eventsPromise = waitForEvents(fileOperationManager); + + // Verify the events. + reportPromise(eventsPromise.then(function(events) { + var firstEvent = events[0]; + assertEquals('BEGIN', firstEvent.reason); + assertEquals(1, firstEvent.status.numRemainingItems); + assertEquals(0, firstEvent.status.processedBytes); + assertEquals(1, firstEvent.status.totalBytes); + + var lastEvent = events[events.length - 1]; + assertEquals('SUCCESS', lastEvent.reason); + assertEquals(0, lastEvent.status.numRemainingItems); + assertEquals(1, lastEvent.status.processedBytes); + assertEquals(1, lastEvent.status.totalBytes); + + assertTrue(events.some(function(event) { + return event.type === 'entries-changed' && + event.kind === util.EntryChangedKind.DELETED && + event.entries[0].fullPath === '/test.txt'; + })); + + assertTrue(events.some(function(event) { + return event.type === 'entries-changed' && + event.kind === util.EntryChangedKind.CREATED && + event.entries[0].fullPath === '/directory/test.txt'; + })); + + assertFalse(events.some(function(event) { + return event.type === 'delete'; + })); + }), callback); + + fileOperationManager.paste( + [fileSystem.entries['/test.txt']], + fileSystem.entries['/directory'], + true); +} + +/** + * Tests the fileOperationUtil.deleteEntries. + * @param {function(boolean:hasError)} callback Callback to be passed true on + * error. + */ +function testDelete(callback) { + // Prepare entries and their resolver. + var fileSystem = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/test.txt': 10, + }); + window.webkitResolveLocalFileSystemURL = + resolveTestFileSystemURL.bind(null, fileSystem); + + // Observing manager's events. + reportPromise(waitForEvents(fileOperationManager).then(function(events) { + assertEquals('delete', events[0].type); + assertEquals('BEGIN', events[0].reason); + assertEquals(10, events[0].totalBytes); + assertEquals(0, events[0].processedBytes); + + var lastEvent = events[events.length - 1]; + assertEquals('delete', lastEvent.type); + assertEquals('SUCCESS', lastEvent.reason); + assertEquals(10, lastEvent.totalBytes); + assertEquals(10, lastEvent.processedBytes); + + assertFalse(events.some(function(event) { + return event.type === 'copy-progress'; + })); + }), callback); + + fileOperationManager.deleteEntries([fileSystem.entries['/test.txt']]); +} + +/** + * Tests the fileOperationUtil.zipSelection. + * @param {function(boolean:hasError)} callback Callback to be passed true on + * error. + */ +function testZip(callback) { + // Prepare entries and their resolver. + var fileSystem = createTestFileSystem('testVolume', { + '/': DIRECTORY_SIZE, + '/test.txt': 10, + }); + window.webkitResolveLocalFileSystemURL = + resolveTestFileSystemURL.bind(null, fileSystem); + chrome.fileBrowserPrivate.zipSelection = + function(parentURL, sources, newName, success, error) { + var newPath = joinPath('/', newName); + var newEntry = new MockFileEntry(fileSystem, newPath, {size: 10}); + fileSystem.entries[newPath] = newEntry; + success(newEntry); + }; + + // Observing manager's events. + reportPromise(waitForEvents(fileOperationManager).then(function(events) { + assertEquals('copy-progress', events[0].type); + assertEquals('BEGIN', events[0].reason); + assertEquals(1, events[0].status.totalBytes); + assertEquals(0, events[0].status.processedBytes); + + var lastEvent = events[events.length - 1]; + assertEquals('copy-progress', lastEvent.type); + assertEquals('SUCCESS', lastEvent.reason); + assertEquals(10, lastEvent.status.totalBytes); + assertEquals(10, lastEvent.status.processedBytes); + + assertFalse(events.some(function(event) { + return event.type === 'delete'; + })); + + assertTrue(events.some(function(event) { + return event.type === 'entries-changed' && + event.entries[0].fullPath === '/test.zip'; + })); + }), callback); + + fileOperationManager.zipSelection( + fileSystem.entries['/'], + [fileSystem.entries['/test.txt']]); +} diff --git a/chrome/test/data/file_manager/unit_tests/mocks/mock_entry.js b/chrome/test/data/file_manager/unit_tests/mocks/mock_entry.js index 5110334478a56..9c0b5924ec584 100644 --- a/chrome/test/data/file_manager/unit_tests/mocks/mock_entry.js +++ b/chrome/test/data/file_manager/unit_tests/mocks/mock_entry.js @@ -3,55 +3,195 @@ // found in the LICENSE file. /** - * Mock class for FileEntry. + * Joins paths so that the two paths are connected by only 1 '/'. + * @param {string} a Path. + * @param {string} b Path. + * @return {string} Joined path. + */ +function joinPath(a, b) { + return a.replace(/\/+$/, '') + '/' + b.replace(/^\/+/, ''); +}; + +/** + * Test file system. * - * @param {string} volumeId Id of the volume containing the entry. - * @param {string} fullPath Full path for the entry. + * @param {string} fileSystemId File system ID. + * @constructor + */ +function TestFileSystem(fileSystemId) { + this.fileSystemId = fileSystemId; + this.entries = {}; +}; + +TestFileSystem.prototype = { + get root() { return this.entries['/']; } +}; + +/** + * Base class of mock entries. + * + * @param {TestFileSystem} filesystem File system where the entry is localed. + * @param {string} fullpath Full path of the entry. * @constructor */ -function MockFileEntry(volumeId, fullPath) { - this.volumeId = volumeId; +function MockEntry(filesystem, fullPath) { + this.filesystem = filesystem; this.fullPath = fullPath; } +MockEntry.prototype = { + /** + * @return {string} Name of the entry. + */ + get name() { + return this.fullPath.replace(/^.*\//, ''); + } +}; + /** * Returns fake URL. * * @return {string} Fake URL. */ -MockFileEntry.prototype.toURL = function() { - return 'filesystem:' + this.volumeId + this.fullPath; +MockEntry.prototype.toURL = function() { + return 'filesystem:' + this.filesystem.fileSystemId + this.fullPath; +}; + +/** + * Obtains parent directory. + * + * @param {function(MockDirectoryEntry)} onSuccess Callback invoked with + * the parent directory. + * @param {function(Object)} onError Callback invoked with an error + * object. + */ +MockEntry.prototype.getParent = function( + onSuccess, onError) { + var path = this.fullPath.replace(/\/[^\/]+$/, '') || '/'; + if (this.filesystem.entries[path]) + onSuccess(this.filesystem.entries[path]); + else + onError({name: util.FileError.NOT_FOUND_ERR}); +}; + +/** + * Moves the entry to the directory. + * + * @param {MockDirectoryEntry} parent Destination directory. + * @param {string=} opt_newName New name. + * @param {function(MockDirectoryEntry)} onSuccess Callback invoked with the + * moved entry. + * @param {function(Object)} onError Callback invoked with an error object. + */ +MockEntry.prototype.moveTo = function(parent, opt_newName, onSuccess, onError) { + Promise.resolve().then(function() { + this.filesystem.entries[this.fullPath] = null; + return this.clone(joinPath(parent.fullPath, opt_newName || this.name)); + }.bind(this)).then(onSuccess, onError); +}; + +/** + * Removes the entry. + * + * @param {function()} onSuccess Success callback. + * @param {function(Object)} onError Callback invoked with an error object. + */ +MockEntry.prototype.remove = function(onSuccess, onError) { + Promise.resolve().then(function() { + this.filesystem.entries[this.fullPath] = null; + }.bind(this)).then(onSuccess, onError); +}; + +/** + * Clones the entry with the new fullpath. + * + * @param {string} fullpath New fullpath. + * @return {MockEntry} Cloned entry. + */ +MockEntry.prototype.clone = function(fullpath) { + throw new Error('Not implemented.'); +}; + +/** + * Mock class for FileEntry. + * + * @param {FileSystem} filesystem File system where the entry is localed. + * @param {string} fullPath Full path for the entry. + * @param {Object} metadata Metadata. + * @extends {MockEntry} + * @constructor + */ +function MockFileEntry(filesystem, fullPath, metadata) { + MockEntry.call(this, filesystem, fullPath); + this.metadata_ = metadata; + this.isFile = true; + this.isDirectory = false; +} + +MockFileEntry.prototype = { + __proto__: MockEntry.prototype +}; + +/** + * Obtains metadata of the entry. + * @param {function(Object)} callback Function to take the metadata. + */ +MockFileEntry.prototype.getMetadata = function(callback) { + Promise.resolve(this.metadata_).then(callback).catch(function(error) { + console.error(error.stack || error); + window.onerror(); + }); +}; + +/** + * @override + */ +MockFileEntry.prototype.clone = function(path) { + return new MockFileEntry(this.filesystem, path, this.metadata); }; /** * Mock class for DirectoryEntry. * - * @param {string} volumeId Id of the volume containing the entry. + * @param {FileSystem} filesystem File system where the entry is localed. * @param {string} fullPath Full path for the entry. - * @param {Object.} contents Map of - * path and MockEntry contained in the directory. + * @extends {MockEntry} * @constructor */ -function MockDirectoryEntry(volumeId, fullPath, contents) { - this.contents_ = contents; +function MockDirectoryEntry(filesystem, fullPath) { + MockEntry.call(this, filesystem, fullPath); + this.isFile = false; + this.isDirectory = true; } +MockDirectoryEntry.prototype = { + __proto__: MockEntry.prototype +}; + +/** + * @override + */ +MockDirectoryEntry.prototype.clone = function(path) { + return new MockDirectoryEntry(this.filesystem, path); +}; + /** * Returns a file under the directory. * * @param {string} path Path. * @param {Object} option Option. - * @param {callback(MockFileEntry)} successCallback Success callback. - * @param {callback(Object)} failureCallback Failure callback; + * @param {callback(MockFileEntry)} onSuccess Success callback. + * @param {callback(Object)} onError Failure callback; */ MockDirectoryEntry.prototype.getFile = function( - path, option, successCallback, failureCallback) { - if (!this.contents_[path]) - failureCallback({name: util.FileError.NOT_FOUND_ERR}); - else if (!(this.contents_[path] instanceof MockFileEntry)) - failureCallback({name: util.FileError.TYPE_MISMATCH_ERR}); + path, option, onSuccess, onError) { + var fullPath = path[0] === '/' ? path : joinPath(this.fullPath, path); + if (!this.filesystem.entries[fullPath]) + onError({name: util.FileError.NOT_FOUND_ERR}); + else if (!(this.filesystem.entries[fullPath] instanceof MockFileEntry)) + onError({name: util.FileError.TYPE_MISMATCH_ERR}); else - successCallback(this.contents_[path]); + onSuccess(this.filesystem.entries[fullPath]); }; /** @@ -59,15 +199,16 @@ MockDirectoryEntry.prototype.getFile = function( * * @param {string} path Path. * @param {Object} option Option. - * @param {callback(MockDirectoryEntry)} successCallback Success callback. - * @param {callback(Object)} failureCallback Failure callback; + * @param {callback(MockDirectoryEntry)} onSuccess Success callback. + * @param {callback(Object)} onError Failure callback; */ MockDirectoryEntry.prototype.getDirectory = - function(path, option, successCallback, failureCallback) { - if (!this.contents_[path]) - failureCallback({name: util.FileError.NOT_FOUND_ERR}); - else if (!(this.contents_[path] instanceof MockDirectoryEntry)) - failureCallback({name: util.FileError.TYPE_MISMATCH_ERR}); + function(path, option, onSuccess, onError) { + var fullPath = path[0] === '/' ? path : joinPath(this.fullPath, path); + if (!this.filesystem.entries[fullPath]) + onError({name: util.FileError.NOT_FOUND_ERR}); + else if (!(this.filesystem.entries[fullPath] instanceof MockDirectoryEntry)) + onError({name: util.FileError.TYPE_MISMATCH_ERR}); else - successCallback(this.contents_[path]); + onSuccess(this.filesystem.entries[fullPath]); }; diff --git a/chrome/test/data/nacl/nacl_test_data.gyp b/chrome/test/data/nacl/nacl_test_data.gyp index c27ab2ebea3cd..dc14c0abca327 100644 --- a/chrome/test/data/nacl/nacl_test_data.gyp +++ b/chrome/test/data/nacl/nacl_test_data.gyp @@ -62,12 +62,6 @@ 'nacl_load_test.html', ], }, - # We only need the pexe for this test. However, build_pnacl_newlib: 1 - # will also translate that pexe into a nexe, so add the shim - # just in case of a race condition. - 'dependencies': [ - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', - ], }, { 'target_name': 'exit_status_test', @@ -85,9 +79,6 @@ 'exit_status/pm_exit_status_test.html', ], }, - 'dependencies': [ - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', - ], }, { 'target_name': 'extension_validation_cache', @@ -95,10 +86,11 @@ 'variables': { 'nexe_target': 'extension_validation_cache', # The test currently only has the test expectations for the - # newlib case (# validation queries/settings), and has also - # hardcoded the newlib variant's directory path for the unpacked ext. + # newlib and glibc cases (# validation queries/settings), and has also + # hardcoded the newlib and glibc variants' directory path for the + # unpacked ext. 'build_newlib': 1, - 'build_glibc': 0, + 'build_glibc': 1, 'build_pnacl_newlib': 0, # Need a new directory to not clash with with other extension # tests's files (e.g., manifest.json). @@ -139,9 +131,6 @@ 'sysconf_nprocessors_onln/sysconf_nprocessors_onln_test.html', ], }, - 'dependencies': [ - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', - ], }, { 'target_name': 'ppapi_test_lib', @@ -214,7 +203,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -393,7 +381,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -422,7 +409,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -451,7 +437,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -480,7 +465,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -509,7 +493,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -540,7 +523,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', ], }, @@ -779,7 +761,6 @@ '<(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', '<(DEPTH)/native_client/src/shared/srpc/srpc.gyp:srpc_lib', '<(DEPTH)/native_client/src/shared/platform/platform.gyp:platform_lib', '<(DEPTH)/native_client/src/shared/gio/gio.gyp:gio_lib', @@ -832,10 +813,6 @@ 'nexe_target': 'pnacl_debug_url', 'build_pnacl_newlib': 1, 'nexe_destination_dir': 'nacl_test_data', - # No need to translate these AOT, when we just need the pexe. - 'enable_x86_32': 0, - 'enable_x86_64': 0, - 'enable_arm': 0, 'generate_nmf': 0, 'sources': [ 'simple.cc', @@ -857,10 +834,6 @@ 'variables': { 'build_pnacl_newlib': 1, 'nexe_destination_dir': 'nacl_test_data', - # No need to translate AOT. - 'enable_x86_32': 0, - 'enable_x86_64': 0, - 'enable_arm': 0, # Use prebuilt NMF files. 'generate_nmf': 0, 'test_files': [ @@ -883,10 +856,6 @@ 'build_glibc': 1, 'build_pnacl_newlib': 1, 'nexe_destination_dir': 'nacl_test_data', - # No need to translate AOT. - 'enable_x86_32': 0, - 'enable_x86_64': 0, - 'enable_arm': 0, 'test_files': [ 'pnacl_mime_type/pnacl_mime_type.html', ], @@ -899,10 +868,6 @@ 'nexe_target': 'pnacl_options', 'build_pnacl_newlib': 1, 'nexe_destination_dir': 'nacl_test_data', - # No need to translate these AOT, when we just need the pexe. - 'enable_x86_32': 0, - 'enable_x86_64': 0, - 'enable_arm': 0, 'generate_nmf': 0, 'sources': [ 'simple.cc', @@ -922,12 +887,6 @@ 'target_name': 'pnacl_dyncode_syscall_disabled_test', 'type': 'none', 'variables': { - # This tests that nexes produced by translation in the browser are not - # able to use the dyncode syscalls. Pre-translated nexes are not - # subject to this constraint, so we do not test them. - 'enable_x86_32': 0, - 'enable_x86_64': 0, - 'enable_arm': 0, 'nexe_target': 'pnacl_dyncode_syscall_disabled', 'build_pnacl_newlib': 1, 'nexe_destination_dir': 'nacl_test_data', @@ -963,12 +922,6 @@ 'target_name': 'pnacl_hw_eh_disabled_test', 'type': 'none', 'variables': { - # This tests that nexes produced by translation in the browser are not - # able to use hardware exception handling. Pre-translated nexes are - # not subject to this constraint, so we do not test them. - 'enable_x86_32': 0, - 'enable_x86_64': 0, - 'enable_arm': 0, 'nexe_target': 'pnacl_hw_eh_disabled', 'build_pnacl_newlib': 1, 'nexe_destination_dir': 'nacl_test_data', @@ -1028,7 +981,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -1060,7 +1012,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], @@ -1093,7 +1044,6 @@ '<(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', - '<(DEPTH)/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_irt_shim.gyp:aot', '<(DEPTH)/ppapi/ppapi_nacl.gyp:ppapi_cpp_lib', 'ppapi_test_lib', ], diff --git a/chrome/test/data/policy/policy_test_cases.json b/chrome/test/data/policy/policy_test_cases.json index 592c826017f5b..ebe203559a431 100644 --- a/chrome/test/data/policy/policy_test_cases.json +++ b/chrome/test/data/policy/policy_test_cases.json @@ -113,7 +113,8 @@ "can_be_recommended": true, "test_policy": { "DnsPrefetchingEnabled": false }, "pref_mappings": [ - { "pref": "dns_prefetching.enabled", + { "pref": "dns_prefetching.enabled" }, + { "pref": "net.network_prediction_options", "indicator_tests": [ { "policy": { "DnsPrefetchingEnabled": false } } ] @@ -126,7 +127,8 @@ "can_be_recommended": true, "test_policy": { "NetworkPredictionOptions": 2 }, "pref_mappings": [ - { "pref": "dns_prefetching.enabled", + { "pref": "dns_prefetching.enabled" }, + { "pref": "net.network_prediction_options", "indicator_tests": [ { "policy": { "NetworkPredictionOptions": 2 } } ] @@ -1649,6 +1651,16 @@ "indicator_selector": "#wallpaper-indicator" }, + "BrowserGuestModeEnabled": { + "os": ["win", "linux", "mac"], + "test_policy": { "BrowserGuestModeEnabled": true }, + "pref_mappings": [ + { "pref": "profile.browser_guest_enabled", + "local_state": true + } + ] + }, + "----- Chrome OS policies ------------------------------------------------": {}, "ChromeOsLockOnIdleSuspend": { diff --git a/chrome/test/data/printing/layout_tests/TestExpectations b/chrome/test/data/printing/layout_tests/TestExpectations index 1acad6ab207b1..ffef2ed10973f 100644 --- a/chrome/test/data/printing/layout_tests/TestExpectations +++ b/chrome/test/data/printing/layout_tests/TestExpectations @@ -1,8 +1,12 @@ # Test Expectations +# Tests should be kept in order of crbug issue ID. + +# Table is cut off. +crbug.com/124517 source_html/table/cell-table.html [ Pass Failure ] + +# Logo should appear on every page, but doesn't. +crbug.com/326834 source_html/css/repeating-item.html [ Pass Failure ] + # These two tests have the same problem where the image that is being rendered is incomplete when printing to PDF. crbug.com/399756 [ Win Release ] source_html/colors/color-gradient.html [ ImageOnlyFailure ] -# The previous one is flaky due to seemingly random output. -# However, this one passes because it consistently produces output that we don't want it to. -crbug.com/399756 [ Win Release ] source_html/shapes/lines.html [ Pass Failure ] -# This tests passes because its consistently wrong -crbug.com/326834 source_html/css/repeating-item.html [ Pass Failure ] \ No newline at end of file +crbug.com/399756 [ Win Release ] source_html/shapes/lines.html [ ImageOnlyFailure ] diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/colors/color-gradient-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/colors/color-gradient-expected.png new file mode 100644 index 0000000000000..7be215b710ed7 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/colors/color-gradient-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/css/repeating-item-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/css/repeating-item-expected.png new file mode 100644 index 0000000000000..72a183b458910 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/css/repeating-item-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/forms/radio-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/forms/radio-expected.png new file mode 100644 index 0000000000000..151c6e66301a3 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/forms/radio-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/forms/text-field-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/forms/text-field-expected.png new file mode 100644 index 0000000000000..6730ece8947e3 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/forms/text-field-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/images/test-image-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/images/test-image-expected.png new file mode 100644 index 0000000000000..686561305db18 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/images/test-image-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/shapes/empty-box-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/shapes/empty-box-expected.png new file mode 100644 index 0000000000000..1d3ad0521a9e0 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/shapes/empty-box-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/shapes/lines-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/shapes/lines-expected.png new file mode 100644 index 0000000000000..f1afb99c5332a Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/shapes/lines-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/table/cell-table-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/table/cell-table-expected.png new file mode 100644 index 0000000000000..d9d5e2eafc8d4 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/table/cell-table-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/text/bullet-list-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/text/bullet-list-expected.png new file mode 100644 index 0000000000000..ada6802760cdd Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/text/bullet-list-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_html/text/simple-text-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_html/text/simple-text-expected.png new file mode 100644 index 0000000000000..ef8274cda912a Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_html/text/simple-text-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/linux/source_pdf/portrait_landscape-expected.png b/chrome/test/data/printing/layout_tests/platform/linux/source_pdf/portrait_landscape-expected.png new file mode 100644 index 0000000000000..6d0064e8f3e6d Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/linux/source_pdf/portrait_landscape-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/colors/color-gradient-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/colors/color-gradient-expected.png new file mode 100644 index 0000000000000..2297846030575 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/colors/color-gradient-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/css/repeating-item-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/css/repeating-item-expected.png new file mode 100644 index 0000000000000..083bf09deb7b6 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/css/repeating-item-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/forms/radio-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/forms/radio-expected.png new file mode 100644 index 0000000000000..aaf40dbbfb935 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/forms/radio-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/forms/text-field-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/forms/text-field-expected.png new file mode 100644 index 0000000000000..2f3c663c9b31c Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/forms/text-field-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/images/test-image-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/images/test-image-expected.png new file mode 100644 index 0000000000000..6c96f70ad72d3 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/images/test-image-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/shapes/empty-box-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/shapes/empty-box-expected.png new file mode 100644 index 0000000000000..b799e08eea079 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/shapes/empty-box-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/shapes/lines-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/shapes/lines-expected.png new file mode 100644 index 0000000000000..5913a4f78b3fb Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/shapes/lines-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/table/cell-table-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/table/cell-table-expected.png new file mode 100644 index 0000000000000..a259eccf82513 Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/table/cell-table-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/text/bullet-list-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/text/bullet-list-expected.png new file mode 100644 index 0000000000000..dc7f59881510f Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/text/bullet-list-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_html/text/simple-text-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_html/text/simple-text-expected.png new file mode 100644 index 0000000000000..5e2a70093b9ef Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_html/text/simple-text-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/platform/mac/source_pdf/portrait_landscape-expected.png b/chrome/test/data/printing/layout_tests/platform/mac/source_pdf/portrait_landscape-expected.png new file mode 100644 index 0000000000000..6d0064e8f3e6d Binary files /dev/null and b/chrome/test/data/printing/layout_tests/platform/mac/source_pdf/portrait_landscape-expected.png differ diff --git a/chrome/test/data/printing/layout_tests/source_html/table/cell-table.html b/chrome/test/data/printing/layout_tests/source_html/table/cell-table.html new file mode 100644 index 0000000000000..91bd8bda5c777 --- /dev/null +++ b/chrome/test/data/printing/layout_tests/source_html/table/cell-table.html @@ -0,0 +1,16 @@ + + +
    +
    + + + + + + +
    +
    Foo
    +
    +
    +
    + diff --git a/chrome/test/data/safe_browsing/README b/chrome/test/data/safe_browsing/README new file mode 100644 index 0000000000000..17360d11ac2b0 --- /dev/null +++ b/chrome/test/data/safe_browsing/README @@ -0,0 +1,2 @@ +signed_binary.dll is a signed version of chrome_elf.dll used by +binary_integrity_service_win_unittest.cc. diff --git a/chrome/test/data/safe_browsing/signed_binary.dll b/chrome/test/data/safe_browsing/signed_binary.dll new file mode 100755 index 0000000000000..673edb60d4042 Binary files /dev/null and b/chrome/test/data/safe_browsing/signed_binary.dll differ diff --git a/chrome/test/data/webui/history_browsertest.js b/chrome/test/data/webui/history_browsertest.js index 96891840fe658..267e5a3c0d163 100644 --- a/chrome/test/data/webui/history_browsertest.js +++ b/chrome/test/data/webui/history_browsertest.js @@ -778,7 +778,26 @@ TEST_F('HistoryWebUIRealBackendTest', 'basic', function() { }); TEST_F('HistoryWebUIRealBackendTest', 'atLeastOneFocusable', function() { - assertEquals(1, document.querySelectorAll('[tabindex="0"]').length); + expectEquals(1, document.querySelectorAll('[tabindex="0"]').length); + testDone(); +}); + +TEST_F('HistoryWebUIRealBackendTest', 'deleteRemovesEntry', function() { + assertTrue(historyModel.deletingHistoryAllowed); + + var visit = document.querySelector('.entry').visit; + visit.titleLink.focus(); + assertEquals(visit.titleLink, document.activeElement); + + var deleteKey = document.createEvent('KeyboardEvent'); + deleteKey.initKeyboardEvent('keydown', true, true, window, 'U+007F'); + assertEquals('U+007F', deleteKey.keyIdentifier); + + assertFalse(historyModel.isDeletingVisits()); + expectFalse(visit.titleLink.dispatchEvent(deleteKey)); + expectTrue(historyModel.isDeletingVisits()); + + expectNotEquals(visit.dropDown, document.activeElement); testDone(); }); @@ -827,6 +846,27 @@ TEST_F('HistoryWebUIRealBackendTest', 'singleDeletion', function() { }); }); +TEST_F('HistoryWebUIRealBackendTest', 'leftRightChangeFocus', function() { + var visit = document.querySelector('.entry').visit; + visit.titleLink.focus(); + assertEquals(visit.titleLink, document.activeElement); + + var right = document.createEvent('KeyboardEvent'); + right.initKeyboardEvent('keydown', true, true, window, 'Right'); + assertEquals('Right', right.keyIdentifier); + expectFalse(visit.titleLink.dispatchEvent(right)); + + assertEquals(visit.dropDown, document.activeElement); + + var left = document.createEvent('KeyboardEvent'); + left.initKeyboardEvent('keydown', true, true, window, 'Left'); + assertEquals('Left', left.keyIdentifier); + expectFalse(visit.dropDown.dispatchEvent(left)); + + expectEquals(visit.titleLink, document.activeElement); + testDone(); +}); + /** * Fixture for History WebUI testing when deletions are prohibited. * @extends {HistoryWebUIRealBackendTest} @@ -859,16 +899,51 @@ TEST_F('HistoryWebUIDeleteProhibitedTest', 'deleteProhibited', function() { var removeVisit = $('remove-visit'); expectTrue(removeVisit.disabled); - // Attempting to remove items anyway should fail. - historyModel.removeVisitsFromHistory(historyModel.visits_, function () { - // The callback is only called on success. - testDone([false, 'Delete succeeded even though it was prohibited.']); - }); - waitForCallback('deleteFailed', testDone); + testDone(); }); TEST_F('HistoryWebUIDeleteProhibitedTest', 'atLeastOneFocusable', function() { - assertEquals(1, document.querySelectorAll('[tabindex="0"]').length); + expectEquals(1, document.querySelectorAll('[tabindex="0"]').length); + testDone(); +}); + +TEST_F('HistoryWebUIDeleteProhibitedTest', 'leftRightChangeFocus', function() { + var visit = document.querySelector('.entry').visit; + visit.titleLink.focus(); + assertEquals(visit.titleLink, document.activeElement); + + var right = document.createEvent('KeyboardEvent'); + right.initKeyboardEvent('keydown', true, true, window, 'Right'); + assertEquals('Right', right.keyIdentifier); + expectFalse(visit.titleLink.dispatchEvent(right)); + + assertEquals(visit.dropDown, document.activeElement); + + var left = document.createEvent('KeyboardEvent'); + left.initKeyboardEvent('keydown', true, true, window, 'Left'); + assertEquals('Left', left.keyIdentifier); + expectFalse(visit.dropDown.dispatchEvent(left)); + + expectEquals(visit.titleLink, document.activeElement); + testDone(); +}); + +TEST_F('HistoryWebUIDeleteProhibitedTest', 'deleteIgnored', function() { + assertFalse(historyModel.deletingHistoryAllowed); + + var visit = document.querySelector('.entry').visit; + visit.titleLink.focus(); + assertEquals(visit.titleLink, document.activeElement); + + var deleteKey = document.createEvent('KeyboardEvent'); + deleteKey.initKeyboardEvent('keydown', true, true, window, 'U+007F'); + assertEquals('U+007F', deleteKey.keyIdentifier); + + assertFalse(historyModel.isDeletingVisits()); + expectTrue(visit.titleLink.dispatchEvent(deleteKey)); + expectFalse(historyModel.isDeletingVisits()); + + expectEquals(visit.titleLink, document.activeElement); testDone(); }); @@ -909,6 +984,7 @@ TEST_F('HistoryWebUIIDNTest', 'basic', function() { /** * Fixture for a test that uses the real backend and tests how the history * page deals with odd schemes in URLs. + * @extends {HistoryWebUIRealBackendTest} */ function HistoryWebUIWithSchemesTest() {} diff --git a/chrome/test/data/webui/net_internals/log_view_painter.js b/chrome/test/data/webui/net_internals/log_view_painter.js index e0165aad1cfe2..d9621ef3da241 100644 --- a/chrome/test/data/webui/net_internals/log_view_painter.js +++ b/chrome/test/data/webui/net_internals/log_view_painter.js @@ -202,7 +202,7 @@ function painterTestURLRequest() { }, { 'params': { - 'load_flags': 136446208, + 'load_flags': 68222976, 'method': 'GET', 'priority': 4, 'url': 'http://www.google.com/' @@ -226,7 +226,7 @@ function painterTestURLRequest() { }, { 'params': { - 'load_flags': 136446208, + 'load_flags': 68222976, 'method': 'GET', 'priority': 4, 'url': 'http://www.google.com/' @@ -733,15 +733,15 @@ function painterTestURLRequest() { testCase.expectedText = 't=1338864633224 [st= 0] +REQUEST_ALIVE [dt=789]\n' + 't=1338864633238 [st= 14] URL_REQUEST_START_JOB [dt=8]\n' + -' --> load_flags = 136446208 ' + - '(ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + +' --> load_flags = 68222976 ' + + '(MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.google.com/"\n' + 't=1338864633248 [st= 24] +URL_REQUEST_START_JOB [dt=279]\n' + -' --> load_flags = 136446208 ' + - '(ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + +' --> load_flags = 68222976 ' + + '(MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + @@ -819,7 +819,7 @@ function painterTestURLRequestIncomplete() { }, { 'params': { - 'load_flags': 256, + 'load_flags': 0, 'method': 'GET', 'priority': 4, 'url': 'http://www.google.com/' @@ -846,7 +846,7 @@ function painterTestURLRequestIncomplete() { testCase.expectedText = 't=1338864633224 [st= 0] +REQUEST_ALIVE [dt=?]\n' + 't=1338864633356 [st=132] URL_REQUEST_START_JOB [dt=60]\n' + -' --> load_flags = 256 (ENABLE_LOAD_TIMING)\n' + +' --> load_flags = 0 (NORMAL)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.google.com/"'; @@ -864,7 +864,7 @@ function painterTestURLRequestIncompleteFromLoadedLog() { testCase.expectedText = 't=1338864633224 [st= 0] +REQUEST_ALIVE [dt=789+]\n' + 't=1338864633356 [st=132] URL_REQUEST_START_JOB [dt=60]\n' + -' --> load_flags = 256 (ENABLE_LOAD_TIMING)\n' + +' --> load_flags = 0 (NORMAL)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.google.com/"\n' + @@ -906,7 +906,7 @@ function painterTestNetError() { }, { 'params': { - 'load_flags': 136446208, + 'load_flags': 68222976, 'method': 'GET', 'priority': 4, 'url': 'http://www.doesnotexistdomain.com/' @@ -930,7 +930,7 @@ function painterTestNetError() { }, { 'params': { - 'load_flags': 136446208, + 'load_flags': 68222976, 'method': 'GET', 'priority': 4, 'url': 'http://www.doesnotexistdomain.com/' @@ -1065,15 +1065,15 @@ function painterTestNetError() { testCase.expectedText = 't=1338864773894 [st= 0] +REQUEST_ALIVE [dt=475]\n' + 't=1338864773901 [st= 7] URL_REQUEST_START_JOB [dt=5]\n' + -' --> load_flags = 136446208 (' + - 'ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + +' --> load_flags = 68222976 (' + + 'MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + ' --> url = "http://www.doesnotexistdomain.com/"\n' + 't=1338864773906 [st= 12] +URL_REQUEST_START_JOB [dt=245]\n' + -' --> load_flags = 136446208 (' + - 'ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + +' --> load_flags = 68222976 (' + + 'MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> method = "GET"\n' + ' --> priority = 4\n' + @@ -2051,7 +2051,7 @@ function painterTestInProgressURLRequest() { testCase.logEntries = [ { 'params': { - 'load_flags': 136446208, + 'load_flags': 68222976, 'load_state': LoadState.READING_RESPONSE, 'method': 'GET', 'url': 'http://www.MagicPonyShopper.com' @@ -2095,8 +2095,8 @@ function painterTestInProgressURLRequest() { testCase.expectedText = 't=1338864773994 [st= 0] +REQUEST_ALIVE [dt=375]\n' + -' --> load_flags = 136446208 ' + - '(ENABLE_LOAD_TIMING | MAIN_FRAME | MAYBE_USER_GESTURE ' + +' --> load_flags = 68222976 ' + + '(MAIN_FRAME | MAYBE_USER_GESTURE ' + '| VERIFY_EV_CERT)\n' + ' --> load_state = ' + LoadState.READING_RESPONSE + ' (READING_RESPONSE)\n' + diff --git a/chrome/test/gpu/webgl_infobar_browsertest.cc b/chrome/test/gpu/webgl_infobar_browsertest.cc index 300a73f01ec32..e9e1712c60cee 100644 --- a/chrome/test/gpu/webgl_infobar_browsertest.cc +++ b/chrome/test/gpu/webgl_infobar_browsertest.cc @@ -14,6 +14,7 @@ #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/url_constants.h" +#include "chrome/grit/theme_resources.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/test_launcher_utils.h" #include "chrome/test/base/test_switches.h" @@ -27,7 +28,6 @@ #include "content/public/common/page_transition_types.h" #include "content/public/test/browser_test_utils.h" #include "gpu/config/gpu_test_config.h" -#include "grit/theme_resources.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gl/gl_implementation.h" diff --git a/chrome/test/nacl/nacl_browsertest_uma.cc b/chrome/test/nacl/nacl_browsertest_uma.cc index fb7a463f7dd84..f7ee54beef788 100644 --- a/chrome/test/nacl/nacl_browsertest_uma.cc +++ b/chrome/test/nacl/nacl_browsertest_uma.cc @@ -78,7 +78,7 @@ NACL_BROWSER_TEST_F(NaClBrowserTest, SuccessfulLoadUMA, { } }) -class NaClBrowserTestVcacheExtension: +class NaClBrowserTestNewlibVcacheExtension: public NaClBrowserTestNewlibExtension { public: virtual base::FilePath::StringType Variant() OVERRIDE { @@ -86,7 +86,7 @@ class NaClBrowserTestVcacheExtension: } }; -IN_PROC_BROWSER_TEST_F(NaClBrowserTestVcacheExtension, +IN_PROC_BROWSER_TEST_F(NaClBrowserTestNewlibVcacheExtension, ValidationCacheOfMainNexe) { // Hardcoded extension AppID that corresponds to the hardcoded // public key in the manifest.json file. We need to load the extension @@ -98,7 +98,7 @@ IN_PROC_BROWSER_TEST_F(NaClBrowserTestVcacheExtension, RunNaClIntegrationTest(full_url, true); // Make sure histograms from child processes have been accumulated in the - // browser brocess. + // browser process. UMAHistogramHelper histograms; histograms.Fetch(); // Should have received 2 validation queries (one for IRT and one for NEXE), @@ -124,6 +124,57 @@ IN_PROC_BROWSER_TEST_F(NaClBrowserTestVcacheExtension, histograms.ExpectTotalCount("NaCl.ValidationCache.Set", 2); } +class NaClBrowserTestGLibcVcacheExtension: + public NaClBrowserTestGLibcExtension { + public: + virtual base::FilePath::StringType Variant() OVERRIDE { + return FILE_PATH_LITERAL("extension_vcache_test/glibc"); + } +}; + +IN_PROC_BROWSER_TEST_F(NaClBrowserTestGLibcVcacheExtension, + ValidationCacheOfMainNexe) { + // Hardcoded extension AppID that corresponds to the hardcoded + // public key in the manifest.json file. We need to load the extension + // nexe from the same origin, so we can't just try to load the extension + // nexe as a mime-type handler from a non-extension URL. + base::FilePath::StringType full_url = + FILE_PATH_LITERAL("chrome-extension://cbcdidchbppangcjoddlpdjlenngjldk/") + FILE_PATH_LITERAL("extension_validation_cache.html"); + RunNaClIntegrationTest(full_url, true); + + // Make sure histograms from child processes have been accumulated in the + // browser process. + UMAHistogramHelper histograms; + histograms.Fetch(); + // Should have received 9 validation queries, which respond with misses: + // - the IRT + // - ld.so (the initial nexe) + // - main.nexe + // - libppapi_cpp.so + // - libpthread.so.9b15f6a6 + // - libstdc++.so.6 + // - libgcc_s.so.1 + // - libc.so.9b15f6a6 + // - libm.so.9b15f6a6 + histograms.ExpectBucketCount("NaCl.ValidationCache.Query", + nacl::NaClBrowser::CACHE_MISS, 9); + // TOTAL should then be 9 queries so far. + histograms.ExpectTotalCount("NaCl.ValidationCache.Query", 9); + // Should have received a cache setting afterwards for IRT and nexe. + histograms.ExpectBucketCount("NaCl.ValidationCache.Set", + nacl::NaClBrowser::CACHE_HIT, 9); + + // Load it again to hit the cache. + RunNaClIntegrationTest(full_url, true); + histograms.Fetch(); + // Should have received 9 more validation queries and responded with hits. + histograms.ExpectBucketCount("NaCl.ValidationCache.Query", + nacl::NaClBrowser::CACHE_HIT, 9); + histograms.ExpectTotalCount("NaCl.ValidationCache.Query", 18); + histograms.ExpectTotalCount("NaCl.ValidationCache.Set", 9); +} + // Test that validation for the 2 PNaCl translator nexes can be cached. IN_PROC_BROWSER_TEST_F(NaClBrowserTestPnacl, ValidationCacheOfTranslatorNexes) { diff --git a/chrome/test/nacl/nacl_browsertest_util.cc b/chrome/test/nacl/nacl_browsertest_util.cc index 2112893003c98..461d9f03a912e 100644 --- a/chrome/test/nacl/nacl_browsertest_util.cc +++ b/chrome/test/nacl/nacl_browsertest_util.cc @@ -330,3 +330,21 @@ void NaClBrowserTestNewlibExtension::SetUpCommandLine( command_line->AppendSwitchPath(switches::kLoadExtension, src_root.Append(document_root)); } + +void NaClBrowserTestGLibcExtension::SetUpCommandLine( + CommandLine* command_line) { + NaClBrowserTestBase::SetUpCommandLine(command_line); + base::FilePath src_root; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &src_root)); + + // Extension-based tests should specialize the GetDocumentRoot() / Variant() + // to point at the isolated the test extension directory. + // Otherwise, multiple NaCl extensions tests will end up sharing the + // same directory when loading the extension files. + base::FilePath document_root; + ASSERT_TRUE(GetDocumentRoot(&document_root)); + + // Document root is relative to source root, and source root may not be CWD. + command_line->AppendSwitchPath(switches::kLoadExtension, + src_root.Append(document_root)); +} diff --git a/chrome/test/nacl/nacl_browsertest_util.h b/chrome/test/nacl/nacl_browsertest_util.h index c972c2f72c71c..643d56e471100 100644 --- a/chrome/test/nacl/nacl_browsertest_util.h +++ b/chrome/test/nacl/nacl_browsertest_util.h @@ -151,6 +151,11 @@ class NaClBrowserTestNewlibExtension : public NaClBrowserTestNewlib { virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE; }; +class NaClBrowserTestGLibcExtension : public NaClBrowserTestGLibc { + public: + virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE; +}; + // PNaCl tests take a long time on windows debug builds // and sometimes time out. Disable until it is made faster: // https://code.google.com/p/chromium/issues/detail?id=177555 diff --git a/chrome/test/nacl/pnacl_header_test.cc b/chrome/test/nacl/pnacl_header_test.cc index 0ebb121139d08..316103b829aeb 100644 --- a/chrome/test/nacl/pnacl_header_test.cc +++ b/chrome/test/nacl/pnacl_header_test.cc @@ -6,21 +6,44 @@ #include "base/bind.h" #include "base/path_service.h" +#include "base/test/scoped_path_override.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/base/ui_test_utils.h" #include "chrome/test/nacl/nacl_browsertest_util.h" +#include "content/public/browser/resource_dispatcher_host.h" #include "content/public/browser/web_contents.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" +#include "net/url_request/url_request.h" using net::test_server::BasicHttpResponse; using net::test_server::EmbeddedTestServer; using net::test_server::HttpRequest; using net::test_server::HttpResponse; +void TestDispatcherHostDelegate::RequestBeginning( + net::URLRequest* request, + content::ResourceContext* resource_context, + content::AppCacheService* appcache_service, + content::ResourceType resource_type, + int child_id, + int route_id, + ScopedVector* throttles) { + // This checks the same condition as the one for PNaCl in + // AppendComponentUpdaterThrottles. + if (resource_type == content::RESOURCE_TYPE_OBJECT) { + const net::HttpRequestHeaders& headers = request->extra_request_headers(); + std::string accept_headers; + if (headers.GetHeader("Accept", &accept_headers)) { + if (accept_headers.find("application/x-pnacl") != std::string::npos) + found_pnacl_header_ = true; + } + } +} + PnaclHeaderTest::PnaclHeaderTest() : noncors_loads_(0), cors_loads_(0) {} PnaclHeaderTest::~PnaclHeaderTest() {} @@ -40,18 +63,34 @@ void PnaclHeaderTest::StartServer() { void PnaclHeaderTest::RunLoadTest(const std::string& url, int expected_noncors, int expected_cors) { + content::ResourceDispatcherHost::Get()->SetDelegate(&test_delegate_); StartServer(); LoadTestMessageHandler handler; content::JavascriptTestObserver observer( browser()->tab_strip_model()->GetActiveWebContents(), &handler); + + // Make sure this is able to do a pexe fetch, even without access + // to the PNaCl component files (make DIR_PNACL_COMPONENT empty). + // The pexe fetch that is done with special headers must be able to + // start before the component files are on disk. This is because it + // is the pexe fetch that helps trigger an on-demand installation + // which installs the files to disk (if that hasn't already happened + // in the background). + base::ScopedPathOverride component_dir(chrome::DIR_PNACL_COMPONENT); + ui_test_utils::NavigateToURL(browser(), embedded_test_server()->GetURL(url)); + // Wait until the NMF and pexe are also loaded, not just the HTML. // Do this by waiting till the LoadTestMessageHandler responds. EXPECT_TRUE(observer.Run()) << handler.error_message(); + + // Now check the expectations. EXPECT_TRUE(handler.test_passed()) << "Test failed."; EXPECT_EQ(expected_noncors, noncors_loads_); EXPECT_EQ(expected_cors, cors_loads_); + + content::ResourceDispatcherHost::Get()->SetDelegate(NULL); } scoped_ptr PnaclHeaderTest::WatchForPexeFetch( @@ -70,14 +109,14 @@ scoped_ptr PnaclHeaderTest::WatchForPexeFetch( if (absolute_url.path().find(".pexe") == std::string::npos) return scoped_ptr(); - // For pexe files, check for the special Accept header. - // This must match whatever is in: - // ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc + // For pexe files, check for the special Accept header, + // along with the expected ResourceType of the URL request. EXPECT_NE(0U, request.headers.count("Accept")); std::map::const_iterator it = request.headers.find("Accept"); EXPECT_NE(std::string::npos, it->second.find("application/x-pnacl")); EXPECT_NE(std::string::npos, it->second.find("*/*")); + EXPECT_TRUE(test_delegate_.found_pnacl_header()); // Also make sure that other headers like CORS-related headers // are preserved when injecting the special Accept header. diff --git a/chrome/test/nacl/pnacl_header_test.h b/chrome/test/nacl/pnacl_header_test.h index 9134925edd087..e698b46bef868 100644 --- a/chrome/test/nacl/pnacl_header_test.h +++ b/chrome/test/nacl/pnacl_header_test.h @@ -8,6 +8,8 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "chrome/test/base/in_process_browser_test.h" +#include "content/public/browser/resource_dispatcher_host_delegate.h" +#include "content/public/common/resource_type.h" namespace base { class FilePath; @@ -20,6 +22,32 @@ class HttpResponse; } } +using content::ResourceDispatcherHostDelegate; + +class TestDispatcherHostDelegate : public ResourceDispatcherHostDelegate { + public: + explicit TestDispatcherHostDelegate() + : ResourceDispatcherHostDelegate(), found_pnacl_header_(false) {} + + virtual ~TestDispatcherHostDelegate() {} + + virtual void RequestBeginning( + net::URLRequest* request, + content::ResourceContext* resource_context, + content::AppCacheService* appcache_service, + content::ResourceType resource_type, + int child_id, + int route_id, + ScopedVector* throttles) OVERRIDE; + + bool found_pnacl_header() const { return found_pnacl_header_; } + + private: + bool found_pnacl_header_; + + DISALLOW_COPY_AND_ASSIGN(TestDispatcherHostDelegate); +}; + class PnaclHeaderTest : public InProcessBrowserTest { public: PnaclHeaderTest(); @@ -40,6 +68,7 @@ class PnaclHeaderTest : public InProcessBrowserTest { int noncors_loads_; int cors_loads_; + TestDispatcherHostDelegate test_delegate_; DISALLOW_COPY_AND_ASSIGN(PnaclHeaderTest); }; diff --git a/chrome/test/ppapi/ppapi_browsertest.cc b/chrome/test/ppapi/ppapi_browsertest.cc index bece9c7a06875..b590bfc98f5f6 100644 --- a/chrome/test/ppapi/ppapi_browsertest.cc +++ b/chrome/test/ppapi/ppapi_browsertest.cc @@ -1067,8 +1067,14 @@ IN_PROC_BROWSER_TEST_F(PPAPINaClPNaClNonSfiTest, LIST_TEST(Audio_AudioCallback4) \ ) +#if defined(OS_LINUX) +// http://crbug.com/396464 +#define MAYBE_Audio DISABLED_Audio +#else +#define MAYBE_Audio Audio +#endif // PPB_Audio is not supported in-process. -IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, Audio) { +IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, MAYBE_Audio) { RUN_AUDIO_SUBTESTS; } IN_PROC_BROWSER_TEST_F(PPAPINaClNewlibTest, Audio) { @@ -1247,8 +1253,9 @@ IN_PROC_BROWSER_TEST_F(OutOfProcessPPAPITest, MAYBE_FlashMessageLoop) { #define MAYBE_Compositor0 DISABLED_Compositor0 #define MAYBE_Compositor1 DISABLED_Compositor1 #else -#define MAYBE_Compositor0 Compositor0 -#define MAYBE_Compositor1 Compositor1 +// flaky on Linux: http://crbug.com/396482 +#define MAYBE_Compositor0 DISABLED_Compositor0 +#define MAYBE_Compositor1 DISABLED_Compositor1 #endif TEST_PPAPI_NACL_SUBTESTS(MAYBE_Compositor0, RUN_COMPOSITOR_SUBTESTS_0) diff --git a/chrome/third_party/mozilla_security_manager/DEPS b/chrome/third_party/mozilla_security_manager/DEPS index fac79a66f6188..d51a9baf80901 100644 --- a/chrome/third_party/mozilla_security_manager/DEPS +++ b/chrome/third_party/mozilla_security_manager/DEPS @@ -1,3 +1,3 @@ include_rules = [ - "+grit", # For generated headers + "+chrome/grit", # For generated headers ] diff --git a/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp b/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp index 58f700885d41e..d04aca0a2a1fa 100644 --- a/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp +++ b/chrome/third_party/mozilla_security_manager/nsNSSCertHelper.cpp @@ -52,7 +52,7 @@ #include "base/strings/utf_string_conversions.h" #include "chrome/common/net/x509_certificate_model.h" #include "crypto/scoped_nss_types.h" -#include "grit/generated_resources.h" +#include "chrome/grit/generated_resources.h" #include "net/base/ip_endpoint.h" #include "net/base/net_util.h" #include "ui/base/l10n/l10n_util.h" diff --git a/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp b/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp index d4a58abd1ccdd..f02a88a0cb02f 100644 --- a/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp +++ b/chrome/third_party/mozilla_security_manager/nsNSSCertificate.cpp @@ -42,7 +42,7 @@ #include -#include "grit/generated_resources.h" +#include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" namespace mozilla_security_manager { diff --git a/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp b/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp index 01044685fe314..1dbcb338df189 100644 --- a/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp +++ b/chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.cpp @@ -37,7 +37,7 @@ #include "chrome/third_party/mozilla_security_manager/nsUsageArrayHelper.h" -#include "grit/generated_resources.h" +#include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" namespace mozilla_security_manager { diff --git a/chrome/tools/build/repack_locales.py b/chrome/tools/build/repack_locales.py index 4637572fe3795..6073458712ebd 100755 --- a/chrome/tools/build/repack_locales.py +++ b/chrome/tools/build/repack_locales.py @@ -34,6 +34,7 @@ USE_ASH = False ENABLE_AUTOFILL_DIALOG = False +ENABLE_EXTENSIONS = False WHITELIST = None @@ -92,23 +93,31 @@ def calc_inputs(locale): 'ui_chromeos_strings_%s.pak' % locale)) if OS != 'ios': - #e.g. '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_da.pak' - inputs.append(os.path.join(SHARE_INT_DIR, 'webkit', - 'webkit_strings_%s.pak' % locale)) + #e.g. + # '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_da.pak' + inputs.append(os.path.join(SHARE_INT_DIR, 'content', 'app', 'strings', + 'content_strings_%s.pak' % locale)) - #e.g. '<(SHARED_INTERMEDIATE_DIR)/ui/strings_da.pak', + #e.g. '<(SHARED_INTERMEDIATE_DIR)/ui/strings/ui_strings_da.pak', inputs.append(os.path.join(SHARE_INT_DIR, 'ui', 'strings', 'ui_strings_%s.pak' % locale)) + #e.g. '<(SHARED_INTERMEDIATE_DIR)/ui/strings/app_locale_settings_da.pak', + inputs.append(os.path.join(SHARE_INT_DIR, 'ui', 'strings', + 'app_locale_settings_%s.pak' % locale)) + + if ENABLE_AUTOFILL_DIALOG and OS != 'ios' and OS != 'android': + #e.g. '<(SHARED_INTERMEDIATE_DIR)/third_party/libaddressinput/ + # address_input_strings_da.pak', + inputs.append(os.path.join(SHARE_INT_DIR, 'third_party', 'libaddressinput', + 'address_input_strings_%s.pak' % locale)) + + if ENABLE_EXTENSIONS: #e.g. '<(SHARED_INTERMEDIATE_DIR)/device/bluetooth/strings/ # device_bluetooth_strings_da.pak', inputs.append(os.path.join(SHARE_INT_DIR, 'device', 'bluetooth', 'strings', 'device_bluetooth_strings_%s.pak' % locale)) - #e.g. '<(SHARED_INTERMEDIATE_DIR)/ui/strings/app_locale_settings_da.pak', - inputs.append(os.path.join(SHARE_INT_DIR, 'ui', 'strings', - 'app_locale_settings_%s.pak' % locale)) - # For example: # '<(SHARED_INTERMEDIATE_DIR)/extensions/strings/extensions_strings_da.pak # TODO(jamescook): When Android stops building extensions code move this @@ -116,12 +125,6 @@ def calc_inputs(locale): inputs.append(os.path.join(SHARE_INT_DIR, 'extensions', 'strings', 'extensions_strings_%s.pak' % locale)) - if ENABLE_AUTOFILL_DIALOG and OS != 'ios' and OS != 'android': - #e.g. '<(SHARED_INTERMEDIATE_DIR)/third_party/libaddressinput/ - # address_input_strings_da.pak', - inputs.append(os.path.join(SHARE_INT_DIR, 'third_party', 'libaddressinput', - 'address_input_strings_%s.pak' % locale)) - #e.g. '<(grit_out_dir)/google_chrome_strings_da.pak' # or # '<(grit_out_dir)/chromium_strings_da.pak' @@ -182,6 +185,7 @@ def DoMain(argv): global USE_ASH global WHITELIST global ENABLE_AUTOFILL_DIALOG + global ENABLE_EXTENSIONS global EXTRA_INPUT_FILES parser = optparse.OptionParser("usage: %prog [options] locales") @@ -211,6 +215,9 @@ def DoMain(argv): parser.add_option("--enable-autofill-dialog", action="store", dest="enable_autofill_dialog", help="Whether to include strings for autofill dialog") + parser.add_option("--enable-extensions", action="store", + dest="enable_extensions", + help="Whether to include strings for extensions") options, locales = parser.parse_args(argv) if not locales: @@ -228,6 +235,7 @@ def DoMain(argv): USE_ASH = options.use_ash == '1' WHITELIST = options.whitelist ENABLE_AUTOFILL_DIALOG = options.enable_autofill_dialog == '1' + ENABLE_EXTENSIONS = options.enable_extensions == '1' if not OS: if sys.platform == 'darwin': diff --git a/chrome/tools/build/win/FILES.cfg b/chrome/tools/build/win/FILES.cfg index 866d93b799094..26081c4161bec 100644 --- a/chrome/tools/build/win/FILES.cfg +++ b/chrome/tools/build/win/FILES.cfg @@ -353,6 +353,7 @@ FILES = [ { 'filename': 'pdf.dll', 'buildtype': ['dev', 'official'], + 'filegroup': ['default', 'symsrc'], }, # ANGLE files: { diff --git a/chrome/tools/mac_helpers/DEPS b/chrome/tools/mac_helpers/DEPS index fac79a66f6188..d51a9baf80901 100644 --- a/chrome/tools/mac_helpers/DEPS +++ b/chrome/tools/mac_helpers/DEPS @@ -1,3 +1,3 @@ include_rules = [ - "+grit", # For generated headers + "+chrome/grit", # For generated headers ] diff --git a/chrome/tools/mac_helpers/infoplist_strings_util.mm b/chrome/tools/mac_helpers/infoplist_strings_util.mm index 1655e0ddab597..4b09208fe0d08 100644 --- a/chrome/tools/mac_helpers/infoplist_strings_util.mm +++ b/chrome/tools/mac_helpers/infoplist_strings_util.mm @@ -20,7 +20,7 @@ #include "base/strings/string_util.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" -#include "grit/chromium_strings.h" +#include "chrome/grit/chromium_strings.h" #include "ui/base/resource/data_pack.h" namespace { diff --git a/chrome/unit_tests.isolate b/chrome/unit_tests.isolate index 55ef3be9b8163..f030ef8bfbd47 100644 --- a/chrome/unit_tests.isolate +++ b/chrome/unit_tests.isolate @@ -14,6 +14,7 @@ 'variables': { 'isolate_dependency_tracked': [ '../third_party/accessibility-audit/axs_testing.js', + '../tools/metrics/histograms/histograms.xml', '<(PRODUCT_DIR)/resources.pak', ], 'isolate_dependency_untracked': [ @@ -106,6 +107,9 @@ '<(PRODUCT_DIR)/ffmpegsumo.dll', '<(PRODUCT_DIR)/libexif.dll', '<(PRODUCT_DIR)/osmesa.dll', + '<(PRODUCT_DIR)/verifier_test_dll_1.dll', + '<(PRODUCT_DIR)/verifier_test_dll_2.dll', + 'test/data/safe_browsing/signed_binary.dll', ], 'isolate_dependency_untracked': [ '../ppapi/lib/gl/include/KHR/', diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS index 17ebb358f9676..14e3a28ab0a69 100644 --- a/chrome/utility/DEPS +++ b/chrome/utility/DEPS @@ -1,13 +1,10 @@ include_rules = [ + "+chrome/grit", # For generated headers. "+content/public/child", "+content/public/utility", "+components/wifi", "+courgette", "+extensions/common", - - # For generated headers. - "+grit", - "+skia/ext", "+media", "+third_party/zlib/google", diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc index 62251438092ad..e2f9c45a1a729 100644 --- a/chrome/utility/chrome_content_utility_client.cc +++ b/chrome/utility/chrome_content_utility_client.cc @@ -27,6 +27,10 @@ #include "chrome/utility/profile_import_handler.h" #endif +#if defined(OS_WIN) +#include "chrome/utility/shell_handler_win.h" +#endif + #if defined(ENABLE_EXTENSIONS) #include "chrome/common/extensions/chrome_utility_extensions_messages.h" #include "chrome/utility/extensions/extensions_handler.h" @@ -87,6 +91,10 @@ ChromeContentUtilityClient::ChromeContentUtilityClient() handlers_.push_back(new local_discovery::ServiceDiscoveryMessageHandler()); } #endif + +#if defined(OS_WIN) + handlers_.push_back(new ShellHandler()); +#endif } ChromeContentUtilityClient::~ChromeContentUtilityClient() { diff --git a/chrome/utility/cloud_print/pwg_encoder.cc b/chrome/utility/cloud_print/pwg_encoder.cc index 03eac5cf3a9d2..5246bd92a3882 100644 --- a/chrome/utility/cloud_print/pwg_encoder.cc +++ b/chrome/utility/cloud_print/pwg_encoder.cc @@ -21,7 +21,7 @@ const uint32 kColorOrder = 0; // chunky. // Coefficients used to convert from RGB to monochrome. const uint32 kRedCoefficient = 2125; const uint32 kGreenCoefficient = 7154; -const uint32 kBlueCoefficient = 0721; +const uint32 kBlueCoefficient = 721; const uint32 kColorCoefficientDenominator = 10000; const char* kPwgKeyword = "RaS2"; diff --git a/chrome/utility/extensions/extensions_handler.cc b/chrome/utility/extensions/extensions_handler.cc index 0786b9ec380c9..25f65a77707df 100644 --- a/chrome/utility/extensions/extensions_handler.cc +++ b/chrome/utility/extensions/extensions_handler.cc @@ -9,7 +9,6 @@ #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/extensions/chrome_extensions_client.h" #include "chrome/common/extensions/chrome_utility_extensions_messages.h" -#include "chrome/common/extensions/update_manifest.h" #include "chrome/common/media_galleries/metadata_types.h" #include "chrome/utility/chrome_content_utility_client.h" #include "chrome/utility/extensions/unpacker.h" @@ -19,8 +18,10 @@ #include "extensions/common/extension.h" #include "extensions/common/extension_l10n_util.h" #include "extensions/common/manifest.h" +#include "extensions/common/update_manifest.h" #include "media/base/media.h" #include "media/base/media_file_checker.h" +#include "third_party/zlib/google/zip.h" #include "ui/base/ui_base_switches.h" #if defined(OS_WIN) @@ -52,6 +53,9 @@ void ReleaseProcessIfNeeded() { content::UtilityThread::Get()->ReleaseProcessIfNeeded(); } +const char kExtensionHandlerUnzipError[] = + "Could not unzip extension for install."; + } // namespace ExtensionsHandler::ExtensionsHandler() {} @@ -81,6 +85,7 @@ bool ExtensionsHandler::OnMessageReceived(const IPC::Message& message) { bool handled = true; IPC_BEGIN_MESSAGE_MAP(ExtensionsHandler, message) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnpackExtension, OnUnpackExtension) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_UnzipToDir, OnUnzipToDir) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_ParseUpdateManifest, OnParseUpdateManifest) IPC_MESSAGE_HANDLER(ChromeUtilityMsg_DecodeImageBase64, OnDecodeImageBase64) @@ -139,6 +144,18 @@ void ExtensionsHandler::OnUnpackExtension( ReleaseProcessIfNeeded(); } +void ExtensionsHandler::OnUnzipToDir(const base::FilePath& zip_path, + const base::FilePath& dir) { + if (!zip::Unzip(zip_path, dir)) { + Send(new ChromeUtilityHostMsg_UnzipToDir_Failed( + std::string(kExtensionHandlerUnzipError))); + } else { + Send(new ChromeUtilityHostMsg_UnzipToDir_Succeeded(dir)); + } + + ReleaseProcessIfNeeded(); +} + void ExtensionsHandler::OnParseUpdateManifest(const std::string& xml) { UpdateManifest manifest; if (!manifest.Parse(xml)) { @@ -278,8 +295,8 @@ void ExtensionsHandler::OnGetAndEncryptWiFiCredentials( std::vector ciphertext; bool success = error.empty() && !key_data.empty(); if (success) { - NetworkingPrivateCrypto crypto; - success = crypto.EncryptByteString(public_key, key_data, &ciphertext); + success = networking_private_crypto::EncryptByteString( + public_key, key_data, &ciphertext); } Send(new ChromeUtilityHostMsg_GotEncryptedWiFiCredentials(ciphertext, diff --git a/chrome/utility/extensions/extensions_handler.h b/chrome/utility/extensions/extensions_handler.h index 3fc732b8f46df..bc06a10096b73 100644 --- a/chrome/utility/extensions/extensions_handler.h +++ b/chrome/utility/extensions/extensions_handler.h @@ -39,6 +39,7 @@ class ExtensionsHandler : public UtilityMessageHandler { void OnUnpackExtension(const base::FilePath& extension_path, const std::string& extension_id, int location, int creation_flags); + void OnUnzipToDir(const base::FilePath& zip_path, const base::FilePath& dir); void OnParseUpdateManifest(const std::string& xml); void OnDecodeImageBase64(const std::string& encoded_data); void OnParseJSON(const std::string& json); diff --git a/chrome/utility/extensions/unpacker.cc b/chrome/utility/extensions/unpacker.cc index 2e7ad9ee74b34..523bec3c3dd4c 100644 --- a/chrome/utility/extensions/unpacker.cc +++ b/chrome/utility/extensions/unpacker.cc @@ -19,6 +19,7 @@ #include "chrome/common/chrome_utility_messages.h" #include "chrome/common/extensions/api/i18n/default_locale_handler.h" #include "chrome/common/extensions/extension_file_util.h" +#include "chrome/grit/generated_resources.h" #include "content/public/child/image_decoder_utils.h" #include "content/public/common/common_param_traits.h" #include "extensions/common/constants.h" @@ -27,7 +28,6 @@ #include "extensions/common/file_util.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" -#include "grit/generated_resources.h" #include "ipc/ipc_message_utils.h" #include "net/base/file_stream.h" #include "third_party/skia/include/core/SkBitmap.h" diff --git a/chrome/utility/importer/DEPS b/chrome/utility/importer/DEPS index 4b971483d9d06..29165e3905fb3 100644 --- a/chrome/utility/importer/DEPS +++ b/chrome/utility/importer/DEPS @@ -1,4 +1,4 @@ include_rules = [ - # For PasswordForm. - "+components/autofill/core/common", + "+components/autofill/core/common", # For PasswordForm. + "+components/strings/grit", ] diff --git a/chrome/utility/importer/bookmarks_file_importer.cc b/chrome/utility/importer/bookmarks_file_importer.cc index f69c9b68da82d..387257de20d20 100644 --- a/chrome/utility/importer/bookmarks_file_importer.cc +++ b/chrome/utility/importer/bookmarks_file_importer.cc @@ -10,10 +10,10 @@ #include "chrome/common/importer/importer_bridge.h" #include "chrome/common/importer/importer_data_types.h" #include "chrome/common/url_constants.h" +#include "chrome/grit/generated_resources.h" #include "chrome/utility/importer/bookmark_html_reader.h" #include "components/url_fixer/url_fixer.h" #include "content/public/common/url_constants.h" -#include "grit/generated_resources.h" namespace { diff --git a/chrome/utility/importer/firefox_importer.cc b/chrome/utility/importer/firefox_importer.cc index a682dbdf70d1b..8b216bdc1ca6c 100644 --- a/chrome/utility/importer/firefox_importer.cc +++ b/chrome/utility/importer/firefox_importer.cc @@ -20,11 +20,11 @@ #include "chrome/common/importer/imported_favicon_usage.h" #include "chrome/common/importer/importer_bridge.h" #include "chrome/common/importer/importer_url_row.h" +#include "chrome/grit/generated_resources.h" #include "chrome/utility/importer/bookmark_html_reader.h" #include "chrome/utility/importer/favicon_reencode.h" #include "chrome/utility/importer/nss_decryptor.h" #include "components/autofill/core/common/password_form.h" -#include "grit/generated_resources.h" #include "sql/connection.h" #include "sql/statement.h" #include "url/gurl.h" diff --git a/chrome/utility/importer/ie_importer_win.cc b/chrome/utility/importer/ie_importer_win.cc index 1376ac95f8bee..ccdcb1a16be61 100644 --- a/chrome/utility/importer/ie_importer_win.cc +++ b/chrome/utility/importer/ie_importer_win.cc @@ -36,9 +36,9 @@ #include "chrome/common/importer/importer_data_types.h" #include "chrome/common/importer/importer_url_row.h" #include "chrome/common/importer/pstore_declarations.h" +#include "chrome/grit/generated_resources.h" #include "chrome/utility/importer/favicon_reencode.h" #include "components/autofill/core/common/password_form.h" -#include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" #include "url/gurl.h" #include "url/url_constants.h" diff --git a/chrome/utility/importer/safari_importer.mm b/chrome/utility/importer/safari_importer.mm index d9c045566cc7f..c56b33f29e2f8 100644 --- a/chrome/utility/importer/safari_importer.mm +++ b/chrome/utility/importer/safari_importer.mm @@ -20,8 +20,8 @@ #include "chrome/common/importer/importer_bridge.h" #include "chrome/common/url_constants.h" #include "chrome/utility/importer/favicon_reencode.h" -#include "grit/components_strings.h" -#include "grit/generated_resources.h" +#include "components/strings/grit/components_strings.h" +#include "chrome/grit/generated_resources.h" #include "net/base/data_url.h" #include "sql/statement.h" #include "url/gurl.h" diff --git a/chrome/utility/media_galleries/picasa_albums_indexer.cc b/chrome/utility/media_galleries/picasa_albums_indexer.cc index abaeb3d6d4c6c..410a9f0bde65b 100644 --- a/chrome/utility/media_galleries/picasa_albums_indexer.cc +++ b/chrome/utility/media_galleries/picasa_albums_indexer.cc @@ -8,10 +8,10 @@ #include #include -#include "base/ini_parser.h" #include "base/logging.h" #include "base/strings/string_split.h" #include "base/strings/stringprintf.h" +#include "chrome/common/ini_parser.h" namespace picasa { @@ -21,7 +21,7 @@ const char kAlbumSectionHeader[] = ".album:"; const char kAlbumsKey[] = "albums"; const int kMaxDedupeNumber = 1000; // Chosen arbitrarily. -class PicasaINIParser : public base::INIParser { +class PicasaINIParser : public INIParser { public: PicasaINIParser( const base::FilePath& folder_path, AlbumImagesMap* albums_images) diff --git a/chrome/utility/shell_handler_win.cc b/chrome/utility/shell_handler_win.cc new file mode 100644 index 0000000000000..7337f9aa6ed4d --- /dev/null +++ b/chrome/utility/shell_handler_win.cc @@ -0,0 +1,51 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/utility/shell_handler_win.h" + +#include + +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "chrome/common/chrome_utility_messages.h" +#include "content/public/utility/utility_thread.h" +#include "ui/base/win/open_file_name_win.h" + +ShellHandler::ShellHandler() {} +ShellHandler::~ShellHandler() {} + +bool ShellHandler::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ShellHandler, message) + IPC_MESSAGE_HANDLER(ChromeUtilityMsg_GetOpenFileName, + OnGetOpenFileName) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ShellHandler::OnGetOpenFileName( + HWND owner, + DWORD flags, + const GetOpenFileNameFilter& filter, + const base::FilePath& initial_directory, + const base::FilePath& filename) { + ui::win::OpenFileName open_file_name(owner, flags); + open_file_name.SetInitialSelection(initial_directory, filename); + open_file_name.SetFilters(filter); + + base::FilePath directory; + std::vector filenames; + + if (::GetOpenFileName(open_file_name.GetOPENFILENAME())) + open_file_name.GetResult(&directory, &filenames); + + if (filenames.size()) { + content::UtilityThread::Get()->Send( + new ChromeUtilityHostMsg_GetOpenFileName_Result(directory, filenames)); + } else { + content::UtilityThread::Get()->Send( + new ChromeUtilityHostMsg_GetOpenFileName_Failed()); + } +} diff --git a/chrome/utility/shell_handler_win.h b/chrome/utility/shell_handler_win.h new file mode 100644 index 0000000000000..f91124c11c84a --- /dev/null +++ b/chrome/utility/shell_handler_win.h @@ -0,0 +1,47 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_UTILITY_SHELL_HANDLER_WIN_H_ +#define CHROME_UTILITY_SHELL_HANDLER_WIN_H_ + +#include + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "base/tuple.h" +#include "chrome/utility/utility_message_handler.h" + +namespace base { +class FilePath; +} // namespace base + +typedef std::vector > + GetOpenFileNameFilter; + +// Handles requests to execute shell operations. Used to protect the browser +// process from instability due to 3rd-party shell extensions. Must be invoked +// in a non-sandboxed utility process. +class ShellHandler : public UtilityMessageHandler { + public: + ShellHandler(); + virtual ~ShellHandler(); + + // IPC::Listener implementation + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + private: + void OnGetOpenFileName( + HWND owner, + DWORD flags, + const GetOpenFileNameFilter& filter, + const base::FilePath& initial_directory, + const base::FilePath& filename); + + DISALLOW_COPY_AND_ASSIGN(ShellHandler); +}; + +#endif // CHROME_UTILITY_SHELL_HANDLER_WIN_H_ diff --git a/chrome_elf/blacklist/blacklist.cc b/chrome_elf/blacklist/blacklist.cc index 824a9e95a87d5..bf1b1a2441c94 100644 --- a/chrome_elf/blacklist/blacklist.cc +++ b/chrome_elf/blacklist/blacklist.cc @@ -36,7 +36,6 @@ const wchar_t* g_troublesome_dlls[kTroublesomeDllsMaxCount] = { // See crbug.com/379218. L"activedetect64.dll", // Lenovo One Key Theater. L"bitguard.dll", // Unknown (suspected malware). - L"cespy.dll", // CovenantEyes. L"chrmxtn.dll", // Unknown (keystroke logger). L"cplushook.dll", // Unknown (suspected malware). L"datamngr.dll", // Unknown (suspected adware). diff --git a/chromecast/DEPS b/chromecast/DEPS index 1a7a778956d7c..438dbc008d708 100644 --- a/chromecast/DEPS +++ b/chromecast/DEPS @@ -3,6 +3,8 @@ include_rules = [ "+content/public/common", "+crypto", + "+grit/chromecast_settings.h", + "+grit/shell_resources.h", "+net", "+ui", ] diff --git a/chromecast/chromecast.gyp b/chromecast/chromecast.gyp index cfdac1ce010e9..fa2ef649ec2c6 100644 --- a/chromecast/chromecast.gyp +++ b/chromecast/chromecast.gyp @@ -1,4 +1,4 @@ -# Copyright 2014 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. @@ -22,12 +22,22 @@ 'sources': [ 'common/cast_paths.cc', 'common/cast_paths.h', + 'common/cast_resource_delegate.cc', + 'common/cast_resource_delegate.h', + 'common/chromecast_config.cc', + 'common/chromecast_config.h', + 'common/pref_names.cc', + 'common/pref_names.h', ], 'conditions': [ ['chromecast_branding=="Chrome"', { 'dependencies': [ 'internal/chromecast_internal.gyp:cast_common_internal', ], + }, { + 'sources': [ + 'common/chromecast_config_simple.cc', + ], }], ], }, @@ -61,21 +71,34 @@ { 'target_name': 'cast_shell_resources', 'type': 'none', - # Place holder for cast_shell specific resources. + 'variables': { + 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/chromecast', + }, + 'actions': [ + { + 'action_name': 'cast_shell_resources', + 'variables': { + 'grit_grd_file': 'shell/browser/resources/shell_resources.grd', + 'grit_resource_ids': 'shell/browser/resources/resource_ids', + }, + 'includes': [ '../build/grit_action.gypi' ], + }, + ], + 'includes': [ '../build/grit_target.gypi' ], }, { 'target_name': 'cast_shell_pak', 'type': 'none', 'dependencies': [ 'cast_shell_resources', + '../content/app/strings/content_strings.gyp:content_strings', '../content/browser/devtools/devtools_resources.gyp:devtools_resources', '../content/content_resources.gyp:content_resources', '../net/net.gyp:net_resources', '../third_party/WebKit/public/blink_resources.gyp:blink_resources', '../ui/resources/ui_resources.gyp:ui_resources', '../ui/strings/ui_strings.gyp:ui_strings', - '../webkit/webkit_resources.gyp:webkit_resources', - '../webkit/webkit_resources.gyp:webkit_strings', + '../webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], 'actions': [ { @@ -83,7 +106,9 @@ 'variables': { 'pak_inputs': [ '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak', + '<(SHARED_INTERMEDIATE_DIR)/chromecast/shell_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak', + '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/ui/resources/ui_resources_100_percent.pak', '<(SHARED_INTERMEDIATE_DIR)/ui/resources/webui_resources.pak', @@ -91,7 +116,6 @@ '<(SHARED_INTERMEDIATE_DIR)/ui/strings/ui_strings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/webkit/devtools_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources_100_percent.pak', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.pak', ], 'pak_output': '<(PRODUCT_DIR)/cast_shell.pak', }, @@ -106,7 +130,10 @@ 'cast_common', 'cast_service', 'cast_shell_pak', + 'cast_shell_resources', 'cast_version_header', + 'chromecast_locales.gyp:chromecast_locales_pak', + 'chromecast_locales.gyp:chromecast_settings', '../ui/aura/aura.gyp:aura_test_support', '../content/content.gyp:content', '../content/content.gyp:content_app_browser', @@ -128,10 +155,15 @@ 'shell/browser/cast_content_browser_client.h', 'shell/browser/cast_http_user_agent_settings.cc', 'shell/browser/cast_http_user_agent_settings.h', + 'shell/browser/devtools/cast_dev_tools_delegate.cc', + 'shell/browser/devtools/cast_dev_tools_delegate.h', + 'shell/browser/devtools/remote_debugging_server.cc', + 'shell/browser/devtools/remote_debugging_server.h', 'shell/browser/geolocation/cast_access_token_store.cc', 'shell/browser/geolocation/cast_access_token_store.h', 'shell/browser/url_request_context_factory.cc', 'shell/browser/url_request_context_factory.h', + 'shell/browser/webui/webui_cast.h', 'shell/common/cast_content_client.cc', 'shell/common/cast_content_client.h', 'shell/renderer/cast_content_renderer_client.cc', @@ -141,11 +173,16 @@ ['chromecast_branding=="Chrome"', { 'dependencies': [ 'internal/chromecast_internal.gyp:cast_gfx_internal', + 'internal/chromecast_internal.gyp:cast_shell_internal', ], }, { 'dependencies': [ '../ui/ozone/ozone.gyp:eglplatform_shim_x11', ], + 'sources': [ + 'shell/browser/devtools/remote_debugging_server_simple.cc', + 'shell/browser/webui/webui_cast_simple.cc', + ], }], ], }, diff --git a/chromecast/chromecast_locales.gyp b/chromecast/chromecast_locales.gyp new file mode 100644 index 0000000000000..ebcd4d225a228 --- /dev/null +++ b/chromecast/chromecast_locales.gyp @@ -0,0 +1,69 @@ +# 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. + +{ + 'variables': { + 'chromecast_branding%': 'Chromium', + 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/chromecast_strings', + }, + 'targets': [ + { + 'target_name': 'chromecast_settings', + 'type': 'none', + 'actions': [ + { + 'action_name': 'chromecast_settings', + 'variables': { + 'grit_grd_file': 'shell/settings/chromecast_settings.grd', + 'grit_resource_ids': 'shell/browser/resources/resource_ids', + }, + 'includes': [ '../build/grit_action.gypi' ], + }, + ], + 'includes': [ '../build/grit_target.gypi' ], + }, + { + 'target_name': 'chromecast_locales_pak', + 'type': 'none', + 'dependencies': [ + 'chromecast_settings', + ], + 'conditions': [ + ['chromecast_branding=="Chrome"', { + 'dependencies': [ + 'internal/chromecast_locales.gyp:chromecast_app_strings', + ], + }], + ], + 'actions': [ + { + 'action_name': 'repack_locales', + 'message': 'Packing locale-specific resources', + 'variables': { + 'repack_python_cmd': '<(DEPTH)/chromecast/tools/build/chromecast_repack_locales.py', + 'repack_output_dir': '<(PRODUCT_DIR)/chromecast_locales', + 'repack_locales_cmd': [ + 'python', + '<(repack_python_cmd)' + ], + }, + 'inputs': [ + '<(repack_python_cmd)', + 'second; + return true; + } + return false; +}; + +void CastResourceDelegate::AddExtraLocalizedString( + int resource_id, + const base::string16& localized) { + RemoveExtraLocalizedString(resource_id); + extra_localized_strings_.insert(std::make_pair(resource_id, localized)); +} + +void CastResourceDelegate::RemoveExtraLocalizedString(int resource_id) { + extra_localized_strings_.erase(resource_id); +} + +void CastResourceDelegate::ClearAllExtraLocalizedStrings() { + extra_localized_strings_.clear(); +} + +scoped_ptr CastResourceDelegate::GetFont( + ui::ResourceBundle::FontStyle style) { + return scoped_ptr(); +}; + +} // namespace chromecast diff --git a/chromecast/common/cast_resource_delegate.h b/chromecast/common/cast_resource_delegate.h new file mode 100644 index 0000000000000..b3130bc1668ab --- /dev/null +++ b/chromecast/common/cast_resource_delegate.h @@ -0,0 +1,73 @@ +// 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 CHROMECAST_COMMON_CAST_RESOURCE_DELEGATE_H_ +#define CHROMECAST_COMMON_CAST_RESOURCE_DELEGATE_H_ + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_ptr.h" +#include "ui/base/resource/resource_bundle.h" + +namespace base { +class FilePath; +} + +namespace gfx { +class Image; +} + +namespace chromecast { + +// A singleton resource bundle delegate. Primary purpose is to indicate the +// correct locale pack file to load. +class CastResourceDelegate : public ui::ResourceBundle::Delegate { + public: + // Returns the singleton of delegate. It doesn't create an instance. + static CastResourceDelegate* GetInstance(); + + CastResourceDelegate(); + virtual ~CastResourceDelegate(); + + // ui:ResourceBundle::Delegate implementation: + virtual base::FilePath GetPathForResourcePack( + const base::FilePath& pack_path, + ui::ScaleFactor scale_factor) OVERRIDE; + virtual base::FilePath GetPathForLocalePack( + const base::FilePath& pack_path, + const std::string& locale) OVERRIDE; + virtual gfx::Image GetImageNamed(int resource_id) OVERRIDE; + virtual gfx::Image GetNativeImageNamed( + int resource_id, + ui::ResourceBundle::ImageRTL rtl) OVERRIDE; + virtual base::RefCountedStaticMemory* LoadDataResourceBytes( + int resource_id, + ui::ScaleFactor scale_factor) OVERRIDE; + virtual bool GetRawDataResource(int resource_id, + ui::ScaleFactor scale_factor, + base::StringPiece* value) OVERRIDE; + virtual bool GetLocalizedString(int message_id, + base::string16* value) OVERRIDE; + virtual scoped_ptr GetFont( + ui::ResourceBundle::FontStyle style) OVERRIDE; + + // Adds/removes/clears extra localized strings. + void AddExtraLocalizedString(int resource_id, + const base::string16& localized); + void RemoveExtraLocalizedString(int resource_id); + void ClearAllExtraLocalizedStrings(); + + private: + typedef base::hash_map ExtraLocaledStringMap; + + ExtraLocaledStringMap extra_localized_strings_; + + DISALLOW_COPY_AND_ASSIGN(CastResourceDelegate); +}; + +} // namespace chromecast + +#endif // CHROMECAST_COMMON_CAST_RESOURCE_DELEGATE_H_ diff --git a/chromecast/common/chromecast_config.cc b/chromecast/common/chromecast_config.cc new file mode 100644 index 0000000000000..dfb3866966654 --- /dev/null +++ b/chromecast/common/chromecast_config.cc @@ -0,0 +1,138 @@ +// 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 "chromecast/common/chromecast_config.h" + +#include + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/prefs/json_pref_store.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service_factory.h" +#include "base/prefs/pref_store.h" +#include "base/strings/string_number_conversions.h" +#include "chromecast/common/cast_paths.h" +#include "chromecast/common/pref_names.h" + +namespace chromecast { + +namespace { + +// Config file IO worker constants. +const int kNumOfConfigFileIOWorkers = 1; +const char kNameOfConfigFileIOWorkers[] = "ConfigFileIO"; + +void UserPrefsLoadError( + PersistentPrefStore::PrefReadError* error_val, + PersistentPrefStore::PrefReadError error) { + DCHECK(error_val); + *error_val = error; +} + +base::FilePath GetConfigPath() { + base::FilePath config_path; + CHECK(PathService::Get(FILE_CAST_CONFIG, &config_path)); + return config_path; +} + +} // namespace + +// static +ChromecastConfig* ChromecastConfig::g_instance_ = NULL; + +// static +void ChromecastConfig::Create(PrefRegistrySimple* registry) { + DCHECK(g_instance_ == NULL); + g_instance_ = new ChromecastConfig(); + g_instance_->Load(registry); +} + +// static +ChromecastConfig* ChromecastConfig::GetInstance() { + DCHECK(g_instance_ != NULL); + return g_instance_; +} + +ChromecastConfig::ChromecastConfig() + : config_path_(GetConfigPath()), + worker_pool_(new base::SequencedWorkerPool(kNumOfConfigFileIOWorkers, + kNameOfConfigFileIOWorkers)) { +} + +ChromecastConfig::~ChromecastConfig() { + // Explict writing before worker_pool shutdown. + pref_service_->CommitPendingWrite(); + worker_pool_->Shutdown(); +} + +bool ChromecastConfig::Load(PrefRegistrySimple* registry) { + DCHECK(thread_checker_.CalledOnValidThread()); + VLOG(1) << "Loading config from " << config_path_.value(); + registry->RegisterIntegerPref(prefs::kRemoteDebuggingPort, 0); + + RegisterPlatformPrefs(registry); + + PersistentPrefStore::PrefReadError prefs_read_error = + PersistentPrefStore::PREF_READ_ERROR_NONE; + base::PrefServiceFactory prefServiceFactory; + prefServiceFactory.SetUserPrefsFile(config_path_, + JsonPrefStore::GetTaskRunnerForFile(config_path_, worker_pool_)); + prefServiceFactory.set_async(false); + prefServiceFactory.set_read_error_callback( + base::Bind(&UserPrefsLoadError, &prefs_read_error)); + pref_service_ = prefServiceFactory.Create(registry); + + if (prefs_read_error == PersistentPrefStore::PREF_READ_ERROR_NONE) { + return true; + } else { + LOG(ERROR) << "Cannot initialize chromecast config: " + << config_path_.value() + << ", pref_error=" << prefs_read_error; + return false; + } +} + +void ChromecastConfig::Save() const { + DCHECK(thread_checker_.CalledOnValidThread()); + VLOG(1) << "Saving config to: " << config_path_.value(); + pref_service_->CommitPendingWrite(); +} + +const std::string ChromecastConfig::GetValue(const std::string& key) const { + DCHECK(thread_checker_.CalledOnValidThread()); + return pref_service_->GetString(key.c_str()); +} + +const int ChromecastConfig::GetIntValue(const std::string& key) const { + return pref_service_->GetInteger(key.c_str()); +} + +void ChromecastConfig::SetValue( + const std::string& key, + const std::string& value) const { + DCHECK(thread_checker_.CalledOnValidThread()); + if (pref_service_->IsUserModifiablePreference(key.c_str())) { + VLOG(1) << "Set config: key=" << key << ", value=" << value; + pref_service_->SetString(key.c_str(), value); + } else { + LOG(ERROR) << "Cannot set read-only config: key=" << key + << ", value=" << value; + } +} + +void ChromecastConfig::SetIntValue(const std::string& key, int value) const { + DCHECK(thread_checker_.CalledOnValidThread()); + if (pref_service_->IsUserModifiablePreference(key.c_str())) { + VLOG(1) << "Set config: key=" << key << ", value=" << value; + pref_service_->SetInteger(key.c_str(), value); + } else { + LOG(ERROR) << "Cannot set read-only config: key=" << key + << ", value=" << value; + } +} + +} // namespace chromecast diff --git a/chromecast/common/chromecast_config.h b/chromecast/common/chromecast_config.h new file mode 100644 index 0000000000000..5cd6a1ae06fcc --- /dev/null +++ b/chromecast/common/chromecast_config.h @@ -0,0 +1,82 @@ +// 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. +// +// Chromecast-specific configurations retrieved from and stored into a given +// configuration file. + +#ifndef CHROMECAST_COMMON_CHROMECAST_CONFIG_H_ +#define CHROMECAST_COMMON_CHROMECAST_CONFIG_H_ + +#include + +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/prefs/pref_service.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/threading/thread_checker.h" + +class PrefRegistrySimple; + +namespace chromecast { + +// Manages configuration for Chromecast via PrefService. +// It uses JsonPrefStore internally and/so the format of config file is same to +// that of JsonPrefStore. +// It is NOT thread-safe; all functions must be run on the same thread as +// the object is created. +class ChromecastConfig { + public: + // Creates new singleton instance of ChromecastConfig. + static void Create(PrefRegistrySimple* registry); + + // Returns the singleton instance of ChromecastConfig. + static ChromecastConfig* GetInstance(); + + // Saves configs into configuration file. + void Save() const; + + // Returns string value for key, if present. + const std::string GetValue(const std::string& key) const; + + // Returns integer value for key, if present. + const int GetIntValue(const std::string& key) const; + + // Sets new string value for key. + void SetValue(const std::string& key, const std::string& value) const; + + // Sets new int value for key. + void SetIntValue(const std::string& key, int value) const; + + scoped_refptr worker_pool() const { + return worker_pool_; + } + + PrefService* pref_service() const { return pref_service_.get(); } + + private: + ChromecastConfig(); + ~ChromecastConfig(); + + // Loads configs from config file. Returns true if successful. + bool Load(PrefRegistrySimple* registry); + + // Registers any needed preferences for the current platform. + void RegisterPlatformPrefs(PrefRegistrySimple* registry); + + // Global singleton instance. + static ChromecastConfig* g_instance_; + + const base::FilePath config_path_; + const scoped_refptr worker_pool_; + scoped_ptr pref_service_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(ChromecastConfig); +}; + +} // namespace chromecast + +#endif // CHROMECAST_COMMON_CHROMECAST_CONFIG_H_ diff --git a/chromecast/common/chromecast_config_simple.cc b/chromecast/common/chromecast_config_simple.cc new file mode 100644 index 0000000000000..f62efe524b848 --- /dev/null +++ b/chromecast/common/chromecast_config_simple.cc @@ -0,0 +1,12 @@ +// 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 "chromecast/common/chromecast_config.h" + +namespace chromecast { + +void ChromecastConfig::RegisterPlatformPrefs(PrefRegistrySimple* registry) { +} + +} // namespace chromecast diff --git a/chromecast/common/pref_names.cc b/chromecast/common/pref_names.cc new file mode 100644 index 0000000000000..0fed7c8e8b606 --- /dev/null +++ b/chromecast/common/pref_names.cc @@ -0,0 +1,13 @@ +// 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 "chromecast/common/pref_names.h" + +namespace prefs { + +// Port on which to host the remote debugging server. A value of 0 indicates +// that remote debugging is disabled. +const char kRemoteDebuggingPort[] = "remote_debugging_port"; + +} // namespace prefs diff --git a/chromecast/common/pref_names.h b/chromecast/common/pref_names.h new file mode 100644 index 0000000000000..669f19d10e39a --- /dev/null +++ b/chromecast/common/pref_names.h @@ -0,0 +1,14 @@ +// 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 CHROMECAST_COMMON_PREF_NAMES_H_ +#define CHROMECAST_COMMON_PREF_NAMES_H_ + +namespace prefs { + +extern const char kRemoteDebuggingPort[]; + +} // namespace prefs + +#endif // CHROMECAST_COMMON_PREF_NAMES_H_ diff --git a/chromecast/shell/app/cast_main_delegate.cc b/chromecast/shell/app/cast_main_delegate.cc index 87c5fbf69536c..4d3284d8bac95 100644 --- a/chromecast/shell/app/cast_main_delegate.cc +++ b/chromecast/shell/app/cast_main_delegate.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/path_service.h" #include "chromecast/common/cast_paths.h" +#include "chromecast/common/cast_resource_delegate.h" #include "chromecast/shell/browser/cast_content_browser_client.h" #include "chromecast/shell/renderer/cast_content_renderer_client.h" #include "content/public/common/content_switches.h" @@ -42,15 +43,20 @@ void CastMainDelegate::PreSandboxStartup() { void CastMainDelegate::ZygoteForked() { } -// static void CastMainDelegate::InitializeResourceBundle() { - base::FilePath pak_file; - base::FilePath pak_dir; + resource_delegate_.reset(new CastResourceDelegate()); + // TODO(gunsch): Use LOAD_COMMON_RESOURCES once ResourceBundle no longer + // hardcodes resource file names. + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", + resource_delegate_.get(), + ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); + base::FilePath pak_dir; PathService::Get(base::DIR_MODULE, &pak_dir); - - pak_file = pak_dir.Append(FILE_PATH_LITERAL("cast_shell.pak")); - ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); + ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( + pak_dir.Append(FILE_PATH_LITERAL("cast_shell.pak")), + ui::SCALE_FACTOR_NONE); } content::ContentBrowserClient* CastMainDelegate::CreateContentBrowserClient() { diff --git a/chromecast/shell/app/cast_main_delegate.h b/chromecast/shell/app/cast_main_delegate.h index a9abc90ed4a80..8c9ed620ddd78 100644 --- a/chromecast/shell/app/cast_main_delegate.h +++ b/chromecast/shell/app/cast_main_delegate.h @@ -11,6 +11,9 @@ #include "content/public/app/content_main_delegate.h" namespace chromecast { + +class CastResourceDelegate; + namespace shell { class CastContentBrowserClient; @@ -30,10 +33,11 @@ class CastMainDelegate : public content::ContentMainDelegate { CreateContentRendererClient() OVERRIDE; private: - static void InitializeResourceBundle(); + void InitializeResourceBundle(); scoped_ptr browser_client_; scoped_ptr renderer_client_; + scoped_ptr resource_delegate_; CastContentClient content_client_; DISALLOW_COPY_AND_ASSIGN(CastMainDelegate); diff --git a/chromecast/shell/browser/cast_browser_context.cc b/chromecast/shell/browser/cast_browser_context.cc index 34658ccb9d4cc..ac1f22fe7d0c9 100644 --- a/chromecast/shell/browser/cast_browser_context.cc +++ b/chromecast/shell/browser/cast_browser_context.cc @@ -127,5 +127,10 @@ content::PushMessagingService* CastBrowserContext::GetPushMessagingService() { return NULL; } +content::SSLHostStateDelegate* CastBrowserContext::GetSSLHostStateDelegate() { + NOTIMPLEMENTED(); + return NULL; +} + } // namespace shell } // namespace chromecast diff --git a/chromecast/shell/browser/cast_browser_context.h b/chromecast/shell/browser/cast_browser_context.h index 93584e957fde8..5981e2938923f 100644 --- a/chromecast/shell/browser/cast_browser_context.h +++ b/chromecast/shell/browser/cast_browser_context.h @@ -43,6 +43,7 @@ class CastBrowserContext : public content::BrowserContext { virtual content::BrowserPluginGuestManager* GetGuestManager() OVERRIDE; virtual quota::SpecialStoragePolicy* GetSpecialStoragePolicy() OVERRIDE; virtual content::PushMessagingService* GetPushMessagingService() OVERRIDE; + virtual content::SSLHostStateDelegate* GetSSLHostStateDelegate() OVERRIDE; private: class CastResourceContext; diff --git a/chromecast/shell/browser/cast_browser_main_parts.cc b/chromecast/shell/browser/cast_browser_main_parts.cc index 1da09377be0fb..72c75440b4f43 100644 --- a/chromecast/shell/browser/cast_browser_main_parts.cc +++ b/chromecast/shell/browser/cast_browser_main_parts.cc @@ -5,11 +5,16 @@ #include "chromecast/shell/browser/cast_browser_main_parts.h" #include "base/command_line.h" +#include "base/prefs/pref_registry_simple.h" +#include "chromecast/common/chromecast_config.h" #include "chromecast/net/network_change_notifier_cast.h" #include "chromecast/net/network_change_notifier_factory_cast.h" #include "chromecast/service/cast_service.h" #include "chromecast/shell/browser/cast_browser_context.h" +#include "chromecast/shell/browser/devtools/remote_debugging_server.h" #include "chromecast/shell/browser/url_request_context_factory.h" +#include "chromecast/shell/browser/webui/webui_cast.h" +#include "content/public/common/content_switches.h" namespace chromecast { namespace shell { @@ -22,9 +27,8 @@ struct DefaultCommandLineSwitch { }; DefaultCommandLineSwitch g_default_switches[] = { - { "enable-webrtc-hw-decoding", "" }, - { "disable-plugins", "" }, - { "enable-threaded-compositing", "" }, + { switches::kDisableApplicationCache, "" }, + { switches::kDisablePlugins, "" }, { NULL, NULL }, // Termination }; @@ -62,6 +66,7 @@ void CastBrowserMainParts::PostMainMessageLoopStart() { } int CastBrowserMainParts::PreCreateThreads() { + ChromecastConfig::Create(new PrefRegistrySimple()); return 0; } @@ -69,6 +74,9 @@ void CastBrowserMainParts::PreMainMessageLoopRun() { url_request_context_factory_->InitializeOnUIThread(); browser_context_.reset(new CastBrowserContext(url_request_context_factory_)); + dev_tools_.reset(new RemoteDebuggingServer()); + + InitializeWebUI(); cast_service_.reset(CastService::Create(browser_context_.get())); cast_service_->Start(); @@ -81,6 +89,9 @@ bool CastBrowserMainParts::MainMessageLoopRun(int* result_code) { void CastBrowserMainParts::PostMainMessageLoopRun() { cast_service_->Stop(); + + cast_service_.reset(); + dev_tools_.reset(); browser_context_.reset(); } diff --git a/chromecast/shell/browser/cast_browser_main_parts.h b/chromecast/shell/browser/cast_browser_main_parts.h index 50e4ee25bc592..988ceea92f3e9 100644 --- a/chromecast/shell/browser/cast_browser_main_parts.h +++ b/chromecast/shell/browser/cast_browser_main_parts.h @@ -21,6 +21,7 @@ class CastService; namespace shell { class CastBrowserContext; +class RemoteDebuggingServer; class URLRequestContextFactory; class CastBrowserMainParts : public content::BrowserMainParts { @@ -45,9 +46,9 @@ class CastBrowserMainParts : public content::BrowserMainParts { private: scoped_ptr browser_context_; scoped_ptr cast_service_; + scoped_ptr dev_tools_; URLRequestContextFactory* const url_request_context_factory_; - DISALLOW_COPY_AND_ASSIGN(CastBrowserMainParts); }; diff --git a/chromecast/shell/browser/cast_content_browser_client.cc b/chromecast/shell/browser/cast_content_browser_client.cc index fc1dac87b7675..720bbb82de744 100644 --- a/chromecast/shell/browser/cast_content_browser_client.cc +++ b/chromecast/shell/browser/cast_content_browser_client.cc @@ -28,9 +28,9 @@ CastContentBrowserClient::~CastContentBrowserClient() { content::BrowserMainParts* CastContentBrowserClient::CreateBrowserMainParts( const content::MainFunctionParams& parameters) { - shell_browser_main_parts_.reset( - new CastBrowserMainParts(parameters, url_request_context_factory_.get())); - return shell_browser_main_parts_.get(); + shell_browser_main_parts_ = + new CastBrowserMainParts(parameters, url_request_context_factory_.get()); + return shell_browser_main_parts_; } void CastContentBrowserClient::RenderProcessWillLaunch( @@ -109,6 +109,7 @@ void CastContentBrowserClient::AllowCertificateError( content::ResourceType resource_type, bool overridable, bool strict_enforcement, + bool expired_previous_decision, const base::Callback& callback, content::CertificateRequestResultType* result) { // Allow developers to override certificate errors. diff --git a/chromecast/shell/browser/cast_content_browser_client.h b/chromecast/shell/browser/cast_content_browser_client.h index 8647cd2a5f81b..98504e3581a7e 100644 --- a/chromecast/shell/browser/cast_content_browser_client.h +++ b/chromecast/shell/browser/cast_content_browser_client.h @@ -6,6 +6,7 @@ #define CHROMECAST_SHELL_BROWSER_CAST_CONTENT_BROWSER_CLIENT_H_ #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "content/public/browser/content_browser_client.h" namespace chromecast { @@ -46,6 +47,7 @@ class CastContentBrowserClient: public content::ContentBrowserClient { content::ResourceType resource_type, bool overridable, bool strict_enforcement, + bool expired_previous_decision, const base::Callback& callback, content::CertificateRequestResultType* result) OVERRIDE; virtual bool CanCreateWindow( @@ -69,8 +71,9 @@ class CastContentBrowserClient: public content::ContentBrowserClient { std::vector* mappings) OVERRIDE; private: - scoped_ptr shell_browser_main_parts_; - + // Note: BrowserMainLoop holds ownership of CastBrowserMainParts after it is + // created. + CastBrowserMainParts* shell_browser_main_parts_; scoped_ptr url_request_context_factory_; DISALLOW_COPY_AND_ASSIGN(CastContentBrowserClient); diff --git a/chromecast/shell/browser/cast_http_user_agent_settings.cc b/chromecast/shell/browser/cast_http_user_agent_settings.cc index 32957a6247d0b..fbc43b3916f2d 100644 --- a/chromecast/shell/browser/cast_http_user_agent_settings.cc +++ b/chromecast/shell/browser/cast_http_user_agent_settings.cc @@ -7,7 +7,9 @@ #include "base/logging.h" #include "chromecast/shell/common/cast_content_client.h" #include "content/public/browser/browser_thread.h" +#include "grit/chromecast_settings.h" #include "net/http/http_util.h" +#include "ui/base/l10n/l10n_util.h" namespace chromecast { namespace shell { @@ -21,7 +23,11 @@ CastHttpUserAgentSettings::~CastHttpUserAgentSettings() { } std::string CastHttpUserAgentSettings::GetAcceptLanguage() const { - accept_language_ = net::HttpUtil::GenerateAcceptLanguageHeader("en-US"); + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + if (accept_language_.empty()) { + accept_language_ = net::HttpUtil::GenerateAcceptLanguageHeader( + l10n_util::GetStringUTF8(IDS_CHROMECAST_SETTINGS_ACCEPT_LANGUAGES)); + } return accept_language_; } diff --git a/chromecast/shell/browser/devtools/cast_dev_tools_delegate.cc b/chromecast/shell/browser/devtools/cast_dev_tools_delegate.cc new file mode 100644 index 0000000000000..77cfba1f3d1a4 --- /dev/null +++ b/chromecast/shell/browser/devtools/cast_dev_tools_delegate.cc @@ -0,0 +1,150 @@ +// 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 "chromecast/shell/browser/devtools/cast_dev_tools_delegate.h" + +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "content/public/browser/devtools_agent_host.h" +#include "content/public/browser/devtools_target.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_delegate.h" +#include "grit/shell_resources.h" +#include "ui/base/resource/resource_bundle.h" + +namespace chromecast { +namespace shell { + +namespace { + +const char kTargetTypePage[] = "page"; + +class Target : public content::DevToolsTarget { + public: + explicit Target(content::WebContents* web_contents); + + virtual std::string GetId() const OVERRIDE { return id_; } + virtual std::string GetParentId() const OVERRIDE { return std::string(); } + virtual std::string GetType() const OVERRIDE { return kTargetTypePage; } + virtual std::string GetTitle() const OVERRIDE { return title_; } + virtual std::string GetDescription() const OVERRIDE { return std::string(); } + virtual GURL GetURL() const OVERRIDE { return url_; } + virtual GURL GetFaviconURL() const OVERRIDE { return favicon_url_; } + virtual base::TimeTicks GetLastActivityTime() const OVERRIDE { + return last_activity_time_; + } + virtual bool IsAttached() const OVERRIDE { + return agent_host_->IsAttached(); + } + virtual scoped_refptr GetAgentHost() + const OVERRIDE { + return agent_host_; + } + virtual bool Activate() const OVERRIDE; + virtual bool Close() const OVERRIDE; + + private: + scoped_refptr agent_host_; + std::string id_; + std::string title_; + GURL url_; + GURL favicon_url_; + base::TimeTicks last_activity_time_; + + DISALLOW_COPY_AND_ASSIGN(Target); +}; + +Target::Target(content::WebContents* web_contents) { + agent_host_ = content::DevToolsAgentHost::GetOrCreateFor(web_contents); + id_ = agent_host_->GetId(); + title_ = base::UTF16ToUTF8(web_contents->GetTitle()); + url_ = web_contents->GetURL(); + 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(); +} + +bool Target::Activate() const { + content::WebContents* web_contents = agent_host_->GetWebContents(); + if (!web_contents) + return false; + web_contents->GetDelegate()->ActivateContents(web_contents); + return true; +} + +bool Target::Close() const { + content::WebContents* web_contents = agent_host_->GetWebContents(); + if (!web_contents) + return false; + web_contents->GetRenderViewHost()->ClosePage(); + return true; +} + +} // namespace + +CastDevToolsDelegate::CastDevToolsDelegate() { +} + +CastDevToolsDelegate::~CastDevToolsDelegate() { +} + +std::string CastDevToolsDelegate::GetDiscoveryPageHTML() { +#if defined(OS_ANDROID) + return std::string(); +#else + return ResourceBundle::GetSharedInstance().GetRawDataResource( + IDR_CAST_SHELL_DEVTOOLS_DISCOVERY_PAGE).as_string(); +#endif // defined(OS_ANDROID) +} + +bool CastDevToolsDelegate::BundlesFrontendResources() { +#if defined(OS_ANDROID) + // Since Android remote debugging connects over a Unix domain socket, Chrome + // will not load the same homepage. + return false; +#else + return true; +#endif // defined(OS_ANDROID) +} + +base::FilePath CastDevToolsDelegate::GetDebugFrontendDir() { + return base::FilePath(); +} + +std::string CastDevToolsDelegate::GetPageThumbnailData(const GURL& url) { + return ""; +} + +scoped_ptr CastDevToolsDelegate::CreateNewTarget( + const GURL& url) { + return scoped_ptr(); +} + +void CastDevToolsDelegate::EnumerateTargets(TargetCallback callback) { + TargetList targets; + std::vector wc_list = + content::DevToolsAgentHost::GetInspectableWebContents(); + for (std::vector::iterator it = wc_list.begin(); + it != wc_list.end(); + ++it) { + targets.push_back(new Target(*it)); + } + callback.Run(targets); +} + +scoped_ptr +CastDevToolsDelegate::CreateSocketForTethering( + net::StreamListenSocket::Delegate* delegate, + std::string* name) { + return scoped_ptr(); +} + +} // namespace shell +} // namespace chromecast diff --git a/chromecast/shell/browser/devtools/cast_dev_tools_delegate.h b/chromecast/shell/browser/devtools/cast_dev_tools_delegate.h new file mode 100644 index 0000000000000..e41e1a85bd081 --- /dev/null +++ b/chromecast/shell/browser/devtools/cast_dev_tools_delegate.h @@ -0,0 +1,42 @@ +// 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 CHROMECAST_SHELL_BROWSER_DEVTOOLS_CAST_DEV_TOOLS_DELEGATE_H_ +#define CHROMECAST_SHELL_BROWSER_DEVTOOLS_CAST_DEV_TOOLS_DELEGATE_H_ + +#include "content/public/browser/devtools_http_handler_delegate.h" +#include "net/socket/stream_listen_socket.h" + +namespace base { +class FilePath; +} + +namespace chromecast { +namespace shell { + +class CastDevToolsDelegate : public content::DevToolsHttpHandlerDelegate { + public: + CastDevToolsDelegate(); + virtual ~CastDevToolsDelegate(); + + // DevToolsHttpHandlerDelegate implementation. + virtual std::string GetDiscoveryPageHTML() OVERRIDE; + virtual bool BundlesFrontendResources() OVERRIDE; + virtual base::FilePath GetDebugFrontendDir() OVERRIDE; + virtual std::string GetPageThumbnailData(const GURL& url) OVERRIDE; + virtual scoped_ptr CreateNewTarget( + const GURL& url) OVERRIDE; + virtual void EnumerateTargets(TargetCallback callback) OVERRIDE; + virtual scoped_ptr CreateSocketForTethering( + net::StreamListenSocket::Delegate* delegate, + std::string* name) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(CastDevToolsDelegate); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_SHELL_BROWSER_DEVTOOLS_CAST_DEV_TOOLS_DELEGATE_H_ diff --git a/chromecast/shell/browser/devtools/remote_debugging_server.cc b/chromecast/shell/browser/devtools/remote_debugging_server.cc new file mode 100644 index 0000000000000..076b06654ecca --- /dev/null +++ b/chromecast/shell/browser/devtools/remote_debugging_server.cc @@ -0,0 +1,116 @@ +// 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 "chromecast/shell/browser/devtools/remote_debugging_server.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/strings/stringprintf.h" +#include "chromecast/common/chromecast_config.h" +#include "chromecast/common/pref_names.h" +#include "chromecast/shell/browser/devtools/cast_dev_tools_delegate.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/devtools_http_handler.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/user_agent.h" +#include "net/socket/tcp_listen_socket.h" + +#if defined(OS_ANDROID) +#include "content/public/browser/android/devtools_auth.h" +#include "net/socket/unix_domain_socket_posix.h" +#endif // defined(OS_ANDROID) + +namespace chromecast { +namespace shell { + +namespace { + +#if defined(OS_ANDROID) +const char kFrontEndURL[] = + "http://chrome-devtools-frontend.appspot.com/serve_rev/%s/devtools.html"; +#endif // defined(OS_ANDROID) +const int kDefaultRemoteDebuggingPort = 9222; + +net::StreamListenSocketFactory* CreateSocketFactory(int port) { +#if defined(OS_ANDROID) + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + std::string socket_name = "content_shell_devtools_remote"; + if (command_line->HasSwitch(switches::kRemoteDebuggingSocketName)) { + socket_name = command_line->GetSwitchValueASCII( + switches::kRemoteDebuggingSocketName); + } + return new net::UnixDomainSocketWithAbstractNamespaceFactory( + socket_name, "", base::Bind(&content::CanUserConnectToDevTools)); +#else + return new net::TCPListenSocketFactory("0.0.0.0", port); +#endif // defined(OS_ANDROID) +} + +std::string GetFrontendUrl() { +#if defined(OS_ANDROID) + return base::StringPrintf(kFrontEndURL, content::GetWebKitRevision().c_str()); +#else + return std::string(); +#endif // defined(OS_ANDROID) +} + +} // namespace + +RemoteDebuggingServer::RemoteDebuggingServer() + : devtools_http_handler_(NULL), + port_(0) { + DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + pref_port_.Init(prefs::kRemoteDebuggingPort, + ChromecastConfig::GetInstance()->pref_service(), + base::Bind(&RemoteDebuggingServer::OnPortChanged, + base::Unretained(this))); + + // Starts new dev tools, clearing port number saved in config. + // Remote debugging in production must be triggered only by config server. + pref_port_.SetValue(ShouldStartImmediately() ? + kDefaultRemoteDebuggingPort : 0); + OnPortChanged(); +} + +RemoteDebuggingServer::~RemoteDebuggingServer() { + pref_port_.SetValue(0); + OnPortChanged(); +} + +void RemoteDebuggingServer::OnPortChanged() { + int new_port = *pref_port_; + if (new_port < 0) { + new_port = 0; + } + VLOG(1) << "OnPortChanged called: old_port=" << port_ + << ", new_port=" << new_port; + + if (new_port == port_) { + VLOG(1) << "Port has not been changed. Ignore silently."; + return; + } + + if (devtools_http_handler_) { + LOG(INFO) << "Stop old devtools: port=" << port_; + // Note: Stop destroys devtools_http_handler_. + devtools_http_handler_->Stop(); + devtools_http_handler_ = NULL; + } + + port_ = new_port; + if (port_ > 0) { + devtools_http_handler_ = content::DevToolsHttpHandler::Start( + CreateSocketFactory(port_), + GetFrontendUrl(), + new CastDevToolsDelegate(), + base::FilePath()); + LOG(INFO) << "Devtools started: port=" << port_; + } +} + +} // namespace shell +} // namespace chromecast diff --git a/chromecast/shell/browser/devtools/remote_debugging_server.h b/chromecast/shell/browser/devtools/remote_debugging_server.h new file mode 100644 index 0000000000000..f4351fa998f77 --- /dev/null +++ b/chromecast/shell/browser/devtools/remote_debugging_server.h @@ -0,0 +1,41 @@ +// 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 CHROMECAST_SHELL_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_ +#define CHROMECAST_SHELL_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_ + +#include "base/prefs/pref_member.h" + +namespace content { +class DevToolsHttpHandler; +} // namespace content + +namespace chromecast { +namespace shell { + +class RemoteDebuggingServer { + public: + RemoteDebuggingServer(); + ~RemoteDebuggingServer(); + + private: + // Called on port number changed. + void OnPortChanged(); + + // Returns whether or not the remote debugging server should be available + // on device startup. + bool ShouldStartImmediately(); + + content::DevToolsHttpHandler* devtools_http_handler_; + + IntegerPrefMember pref_port_; + int port_; + + DISALLOW_COPY_AND_ASSIGN(RemoteDebuggingServer); +}; + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_SHELL_BROWSER_DEVTOOLS_REMOTE_DEBUGGING_SERVER_H_ diff --git a/chromecast/shell/browser/devtools/remote_debugging_server_simple.cc b/chromecast/shell/browser/devtools/remote_debugging_server_simple.cc new file mode 100644 index 0000000000000..40d7a2c6bcc47 --- /dev/null +++ b/chromecast/shell/browser/devtools/remote_debugging_server_simple.cc @@ -0,0 +1,15 @@ +// 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 "chromecast/shell/browser/devtools/remote_debugging_server.h" + +namespace chromecast { +namespace shell { + +bool RemoteDebuggingServer::ShouldStartImmediately() { + return true; +} + +} // namespace shell +} // namespace chromecast diff --git a/chromecast/shell/browser/resources/resource_ids b/chromecast/shell/browser/resources/resource_ids new file mode 100644 index 0000000000000..882fd8fac2154 --- /dev/null +++ b/chromecast/shell/browser/resources/resource_ids @@ -0,0 +1,25 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +# +# This file is used to assign starting resource ids for resources and strings +# used by Chromium. This is done to ensure that resource ids are unique +# across all the grd files. If you are adding a new grd file, please add +# a new entry to this file. +# +# The first entry in the file, SRCDIR, is special: It is a relative path from +# this file to the base of your checkout. +# +# http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx says that the +# range for IDR_ is 1 to 28,671 and the range for IDS_ is 1 to 32,767 and +# common convention starts practical use of IDs at 100 or 101. +{ + "SRCDIR": "../../../..", + + "chromecast/shell/browser/resources/shell_resources.grd": { + "includes": [31000], + }, + "chromecast/shell/settings/chromecast_settings.grd": { + "messages": [31500], + }, +} diff --git a/chromecast/shell/browser/resources/shell_devtools_discovery_page.html b/chromecast/shell/browser/resources/shell_devtools_discovery_page.html new file mode 100644 index 0000000000000..00c937473ba1a --- /dev/null +++ b/chromecast/shell/browser/resources/shell_devtools_discovery_page.html @@ -0,0 +1,56 @@ + + +Cast shell remote debugging + + + + + +
    Inspectable WebContents
    +
    + + diff --git a/chromecast/shell/browser/resources/shell_resources.grd b/chromecast/shell/browser/resources/shell_resources.grd new file mode 100644 index 0000000000000..9d4c3222edacb --- /dev/null +++ b/chromecast/shell/browser/resources/shell_resources.grd @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/chromecast/shell/browser/webui/webui_cast.h b/chromecast/shell/browser/webui/webui_cast.h new file mode 100644 index 0000000000000..f0588cc79c249 --- /dev/null +++ b/chromecast/shell/browser/webui/webui_cast.h @@ -0,0 +1,18 @@ +// 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 CHROMECAST_SHELL_BROWSER_WEBUI_WEBUI_CAST_H_ +#define CHROMECAST_SHELL_BROWSER_WEBUI_WEBUI_CAST_H_ + +namespace chromecast { +namespace shell { + +// Initializes all WebUIs needed for the Chromecast shell. This should be +// implemented on a per-product basis. +void InitializeWebUI(); + +} // namespace shell +} // namespace chromecast + +#endif // CHROMECAST_SHELL_BROWSER_UI_WEBUI_CAST_H_ diff --git a/chromecast/shell/browser/webui/webui_cast_simple.cc b/chromecast/shell/browser/webui/webui_cast_simple.cc new file mode 100644 index 0000000000000..76c2a647e47b1 --- /dev/null +++ b/chromecast/shell/browser/webui/webui_cast_simple.cc @@ -0,0 +1,15 @@ +// 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 "chromecast/shell/browser/webui/webui_cast.h" + +namespace chromecast { +namespace shell { + +void InitializeWebUI() { + // Intentional no-op for public cast_shell build. +} + +} // namespace shell +} // namespace chromecast diff --git a/chromecast/shell/renderer/DEPS b/chromecast/shell/renderer/DEPS index 725e151a7a515..ad0391cf78ba8 100644 --- a/chromecast/shell/renderer/DEPS +++ b/chromecast/shell/renderer/DEPS @@ -1,3 +1,5 @@ include_rules = [ "+content/public/renderer", + "+third_party/WebKit/public/platform", + "+third_party/WebKit/public/web", ] diff --git a/chromecast/shell/renderer/cast_content_renderer_client.cc b/chromecast/shell/renderer/cast_content_renderer_client.cc index c3855a7fc90c5..e07e227929529 100644 --- a/chromecast/shell/renderer/cast_content_renderer_client.cc +++ b/chromecast/shell/renderer/cast_content_renderer_client.cc @@ -9,11 +9,21 @@ #include "base/command_line.h" #include "base/memory/memory_pressure_listener.h" #include "content/public/common/content_switches.h" +#include "content/public/renderer/render_view.h" #include "crypto/nss_util.h" +#include "third_party/WebKit/public/platform/WebColor.h" +#include "third_party/WebKit/public/web/WebView.h" namespace chromecast { namespace shell { +namespace { + +// Default background color to set for WebViews +const blink::WebColor kColorBlack = 0x000000FF; + +} // namespace + void CastContentRendererClient::RenderThreadStarted() { #if defined(USE_NSS) // Note: Copied from chrome_render_process_observer.cc to fix b/8676652. @@ -26,6 +36,14 @@ void CastContentRendererClient::RenderThreadStarted() { #endif } +void CastContentRendererClient::RenderViewCreated( + content::RenderView* render_view) { + blink::WebView* webview = render_view->GetWebView(); + if (webview) { + webview->setBaseBackgroundColor(kColorBlack); + } +} + void CastContentRendererClient::AddKeySystems( std::vector* key_systems) { } diff --git a/chromecast/shell/renderer/cast_content_renderer_client.h b/chromecast/shell/renderer/cast_content_renderer_client.h index b12814f72c406..e3fc055ebe885 100644 --- a/chromecast/shell/renderer/cast_content_renderer_client.h +++ b/chromecast/shell/renderer/cast_content_renderer_client.h @@ -17,6 +17,7 @@ class CastContentRendererClient : public content::ContentRendererClient { // ContentRendererClient implementation: virtual void RenderThreadStarted() OVERRIDE; + virtual void RenderViewCreated(content::RenderView* render_view) OVERRIDE; virtual void AddKeySystems( std::vector* key_systems) OVERRIDE; }; diff --git a/chromecast/shell/settings/chromecast_settings.grd b/chromecast/shell/settings/chromecast_settings.grd new file mode 100644 index 0000000000000..3233be3fe9986 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings.grd @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + en-US,en + + + + diff --git a/chromecast/shell/settings/chromecast_settings_am.xtb b/chromecast/shell/settings/chromecast_settings_am.xtb new file mode 100644 index 0000000000000..2eb7f9cc9fdb2 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_am.xtb @@ -0,0 +1,5 @@ + + + +am,en-GB,en + diff --git a/chromecast/shell/settings/chromecast_settings_ar.xtb b/chromecast/shell/settings/chromecast_settings_ar.xtb new file mode 100644 index 0000000000000..c747cfcd6cbd6 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ar.xtb @@ -0,0 +1,5 @@ + + + +ar,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_bg.xtb b/chromecast/shell/settings/chromecast_settings_bg.xtb new file mode 100644 index 0000000000000..9869f278375b0 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_bg.xtb @@ -0,0 +1,5 @@ + + + +bg-BG,bg + diff --git a/chromecast/shell/settings/chromecast_settings_bn.xtb b/chromecast/shell/settings/chromecast_settings_bn.xtb new file mode 100644 index 0000000000000..817c97b32835c --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_bn.xtb @@ -0,0 +1,5 @@ + + + +bn-IN,bn,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ca.xtb b/chromecast/shell/settings/chromecast_settings_ca.xtb new file mode 100644 index 0000000000000..621a7a29f7ed4 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ca.xtb @@ -0,0 +1,5 @@ + + + +ca-ES,ca + diff --git a/chromecast/shell/settings/chromecast_settings_cs.xtb b/chromecast/shell/settings/chromecast_settings_cs.xtb new file mode 100644 index 0000000000000..b9e2fa70c800c --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_cs.xtb @@ -0,0 +1,5 @@ + + + +cs-CZ,cs + diff --git a/chromecast/shell/settings/chromecast_settings_da.xtb b/chromecast/shell/settings/chromecast_settings_da.xtb new file mode 100644 index 0000000000000..edc061cb37752 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_da.xtb @@ -0,0 +1,5 @@ + + + +da-DK,da,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_de.xtb b/chromecast/shell/settings/chromecast_settings_de.xtb new file mode 100644 index 0000000000000..32950948cbb18 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_de.xtb @@ -0,0 +1,5 @@ + + + +de-DE,de,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_el.xtb b/chromecast/shell/settings/chromecast_settings_el.xtb new file mode 100644 index 0000000000000..a9c08ab481a77 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_el.xtb @@ -0,0 +1,5 @@ + + + +el-GR,el + diff --git a/chromecast/shell/settings/chromecast_settings_en-GB.xtb b/chromecast/shell/settings/chromecast_settings_en-GB.xtb new file mode 100644 index 0000000000000..339f9c6801ca7 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_en-GB.xtb @@ -0,0 +1,5 @@ + + + +en-GB,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_es-419.xtb b/chromecast/shell/settings/chromecast_settings_es-419.xtb new file mode 100644 index 0000000000000..42c5de58b38ba --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_es-419.xtb @@ -0,0 +1,5 @@ + + + +es-419,es + diff --git a/chromecast/shell/settings/chromecast_settings_es.xtb b/chromecast/shell/settings/chromecast_settings_es.xtb new file mode 100644 index 0000000000000..b31331bf86c48 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_es.xtb @@ -0,0 +1,5 @@ + + + +es-ES,es + diff --git a/chromecast/shell/settings/chromecast_settings_et.xtb b/chromecast/shell/settings/chromecast_settings_et.xtb new file mode 100644 index 0000000000000..f5af1f4b40455 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_et.xtb @@ -0,0 +1,5 @@ + + + +et-EE,et,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_fa.xtb b/chromecast/shell/settings/chromecast_settings_fa.xtb new file mode 100644 index 0000000000000..2da41aebb6d63 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_fa.xtb @@ -0,0 +1,5 @@ + + + +fa,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_fi.xtb b/chromecast/shell/settings/chromecast_settings_fi.xtb new file mode 100644 index 0000000000000..2fa3b8f3cfa05 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_fi.xtb @@ -0,0 +1,5 @@ + + + +fi-FI,fi,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_fil.xtb b/chromecast/shell/settings/chromecast_settings_fil.xtb new file mode 100644 index 0000000000000..66f3f6292df93 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_fil.xtb @@ -0,0 +1,5 @@ + + + +fil,fil-PH,tl,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_fr.xtb b/chromecast/shell/settings/chromecast_settings_fr.xtb new file mode 100644 index 0000000000000..6c6d6bd09ee5b --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_fr.xtb @@ -0,0 +1,5 @@ + + + +fr-FR,fr,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_gu.xtb b/chromecast/shell/settings/chromecast_settings_gu.xtb new file mode 100644 index 0000000000000..5d94c0f1689d5 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_gu.xtb @@ -0,0 +1,5 @@ + + + +gu-IN,gu,hi-IN,hi,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_he.xtb b/chromecast/shell/settings/chromecast_settings_he.xtb new file mode 100644 index 0000000000000..50c852cce0a9a --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_he.xtb @@ -0,0 +1,5 @@ + + + +he-IL,he,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_hi.xtb b/chromecast/shell/settings/chromecast_settings_hi.xtb new file mode 100644 index 0000000000000..68a78399e9c2e --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_hi.xtb @@ -0,0 +1,5 @@ + + + +hi-IN,hi,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_hr.xtb b/chromecast/shell/settings/chromecast_settings_hr.xtb new file mode 100644 index 0000000000000..acd6d752d362c --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_hr.xtb @@ -0,0 +1,5 @@ + + + +hr-HR,hr,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_hu.xtb b/chromecast/shell/settings/chromecast_settings_hu.xtb new file mode 100644 index 0000000000000..c0ee3dcc6357a --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_hu.xtb @@ -0,0 +1,5 @@ + + + +hu-HU,hu,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_id.xtb b/chromecast/shell/settings/chromecast_settings_id.xtb new file mode 100644 index 0000000000000..f4e482eaa4145 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_id.xtb @@ -0,0 +1,5 @@ + + + +id-ID,id,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_it.xtb b/chromecast/shell/settings/chromecast_settings_it.xtb new file mode 100644 index 0000000000000..f5409dba8dcb2 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_it.xtb @@ -0,0 +1,5 @@ + + + +it-IT,it,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ja.xtb b/chromecast/shell/settings/chromecast_settings_ja.xtb new file mode 100644 index 0000000000000..b018c168a645e --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ja.xtb @@ -0,0 +1,5 @@ + + + +ja,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_kn.xtb b/chromecast/shell/settings/chromecast_settings_kn.xtb new file mode 100644 index 0000000000000..1b8b2a94089e1 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_kn.xtb @@ -0,0 +1,5 @@ + + + +kn-IN,kn,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ko.xtb b/chromecast/shell/settings/chromecast_settings_ko.xtb new file mode 100644 index 0000000000000..df283e279d38a --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ko.xtb @@ -0,0 +1,5 @@ + + + +ko-KR,ko,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_lt.xtb b/chromecast/shell/settings/chromecast_settings_lt.xtb new file mode 100644 index 0000000000000..39aa2397f83b8 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_lt.xtb @@ -0,0 +1,5 @@ + + + +lt,en-US,en,ru,pl + diff --git a/chromecast/shell/settings/chromecast_settings_lv.xtb b/chromecast/shell/settings/chromecast_settings_lv.xtb new file mode 100644 index 0000000000000..c40c878381b71 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_lv.xtb @@ -0,0 +1,5 @@ + + + +lv-LV,lv,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ml.xtb b/chromecast/shell/settings/chromecast_settings_ml.xtb new file mode 100644 index 0000000000000..ee4aab9493f03 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ml.xtb @@ -0,0 +1,5 @@ + + + +ml-IN,ml,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_mr.xtb b/chromecast/shell/settings/chromecast_settings_mr.xtb new file mode 100644 index 0000000000000..e20441d3cfce1 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_mr.xtb @@ -0,0 +1,5 @@ + + + +mr-IN,mr,hi-IN,hi,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ms.xtb b/chromecast/shell/settings/chromecast_settings_ms.xtb new file mode 100644 index 0000000000000..86fe7eb546132 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ms.xtb @@ -0,0 +1,5 @@ + + + +ms-MY,ms,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_nb.xtb b/chromecast/shell/settings/chromecast_settings_nb.xtb new file mode 100644 index 0000000000000..cc4afe8ec8f66 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_nb.xtb @@ -0,0 +1,5 @@ + + + +nb-NO,nb,no,nn,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_nl.xtb b/chromecast/shell/settings/chromecast_settings_nl.xtb new file mode 100644 index 0000000000000..dc7b59620e546 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_nl.xtb @@ -0,0 +1,5 @@ + + + +nl-NL,nl,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_pl.xtb b/chromecast/shell/settings/chromecast_settings_pl.xtb new file mode 100644 index 0000000000000..51726b736f002 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_pl.xtb @@ -0,0 +1,5 @@ + + + +pl-PL,pl,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_pt-BR.xtb b/chromecast/shell/settings/chromecast_settings_pt-BR.xtb new file mode 100644 index 0000000000000..62b4e97ecaac1 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_pt-BR.xtb @@ -0,0 +1,5 @@ + + + +pt-BR,pt,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_pt-PT.xtb b/chromecast/shell/settings/chromecast_settings_pt-PT.xtb new file mode 100644 index 0000000000000..6554e55aaf89b --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_pt-PT.xtb @@ -0,0 +1,5 @@ + + + +pt-PT,pt,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ro.xtb b/chromecast/shell/settings/chromecast_settings_ro.xtb new file mode 100644 index 0000000000000..0bb0d5a56ff15 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ro.xtb @@ -0,0 +1,5 @@ + + + +ro-RO,ro,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_ru.xtb b/chromecast/shell/settings/chromecast_settings_ru.xtb new file mode 100644 index 0000000000000..d5daf5e6cba3e --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ru.xtb @@ -0,0 +1,5 @@ + + + +ru-RU,ru,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_sk.xtb b/chromecast/shell/settings/chromecast_settings_sk.xtb new file mode 100644 index 0000000000000..4e530fc8efa11 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_sk.xtb @@ -0,0 +1,5 @@ + + + +sk-SK,sk,cs,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_sl.xtb b/chromecast/shell/settings/chromecast_settings_sl.xtb new file mode 100644 index 0000000000000..51c81ab3681fd --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_sl.xtb @@ -0,0 +1,5 @@ + + + +sl-SI,sl,en-GB,en + diff --git a/chromecast/shell/settings/chromecast_settings_sr.xtb b/chromecast/shell/settings/chromecast_settings_sr.xtb new file mode 100644 index 0000000000000..c20a7f6e85371 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_sr.xtb @@ -0,0 +1,5 @@ + + + +sr-RS,sr,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_sv.xtb b/chromecast/shell/settings/chromecast_settings_sv.xtb new file mode 100644 index 0000000000000..58df97fb50528 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_sv.xtb @@ -0,0 +1,5 @@ + + + +sv-SE,sv,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_sw.xtb b/chromecast/shell/settings/chromecast_settings_sw.xtb new file mode 100644 index 0000000000000..dd1a9d8f5081f --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_sw.xtb @@ -0,0 +1,5 @@ + + + +sw,en-GB,en + diff --git a/chromecast/shell/settings/chromecast_settings_ta.xtb b/chromecast/shell/settings/chromecast_settings_ta.xtb new file mode 100644 index 0000000000000..23ca317b70d41 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_ta.xtb @@ -0,0 +1,5 @@ + + + +ta-IN,ta,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_te.xtb b/chromecast/shell/settings/chromecast_settings_te.xtb new file mode 100644 index 0000000000000..819de4ec168e3 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_te.xtb @@ -0,0 +1,5 @@ + + + +te-IN,te,hi-IN,hi,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_th.xtb b/chromecast/shell/settings/chromecast_settings_th.xtb new file mode 100644 index 0000000000000..8cfc81a8155a1 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_th.xtb @@ -0,0 +1,5 @@ + + + +th-TH,th + diff --git a/chromecast/shell/settings/chromecast_settings_tr.xtb b/chromecast/shell/settings/chromecast_settings_tr.xtb new file mode 100644 index 0000000000000..b15cca3f201b9 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_tr.xtb @@ -0,0 +1,5 @@ + + + +tr-TR,tr,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_uk.xtb b/chromecast/shell/settings/chromecast_settings_uk.xtb new file mode 100644 index 0000000000000..17f0db62cf29c --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_uk.xtb @@ -0,0 +1,5 @@ + + + +uk-UA,uk,ru,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_vi.xtb b/chromecast/shell/settings/chromecast_settings_vi.xtb new file mode 100644 index 0000000000000..af25ed9b73c27 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_vi.xtb @@ -0,0 +1,5 @@ + + + +vi-VN,vi,fr-FR,fr,en-US,en + diff --git a/chromecast/shell/settings/chromecast_settings_zh-CN.xtb b/chromecast/shell/settings/chromecast_settings_zh-CN.xtb new file mode 100644 index 0000000000000..173ce581a9d5a --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_zh-CN.xtb @@ -0,0 +1,5 @@ + + + +zh-CN,zh + diff --git a/chromecast/shell/settings/chromecast_settings_zh-TW.xtb b/chromecast/shell/settings/chromecast_settings_zh-TW.xtb new file mode 100644 index 0000000000000..b7b0c07313671 --- /dev/null +++ b/chromecast/shell/settings/chromecast_settings_zh-TW.xtb @@ -0,0 +1,5 @@ + + + +zh-TW,zh,en-US,en + diff --git a/chromecast/tools/build/chromecast_repack_locales.py b/chromecast/tools/build/chromecast_repack_locales.py new file mode 100755 index 0000000000000..607930a7c2173 --- /dev/null +++ b/chromecast/tools/build/chromecast_repack_locales.py @@ -0,0 +1,132 @@ +#!/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. + +"""Helper script to repack paks for a list of locales. + +Gyp doesn't have any built-in looping capability, so this just provides a way to +loop over a list of locales when repacking pak files, thus avoiding a +proliferation of mostly duplicate, cut-n-paste gyp actions. +""" + +import optparse +import os +import sys + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..', + 'tools', 'grit')) +from grit.format import data_pack + +# Some build paths defined by gyp. +GRIT_DIR = None +INT_DIR = None +CHROMECAST_BRANDING = None + +class Usage(Exception): + def __init__(self, msg): + self.msg = msg + + +def calc_output(locale): + """Determine the file that will be generated for the given locale.""" + #e.g. '<(INTERMEDIATE_DIR)/repack/da.pak', + # For Fake Bidi, generate it at a fixed path so that tests can safely + # reference it. + if locale == 'fake-bidi': + return '%s/%s.pak' % (INT_DIR, locale) + return os.path.join(INT_DIR, locale + '.pak') + + +def calc_inputs(locale): + """Determine the files that need processing for the given locale.""" + inputs = [] + if CHROMECAST_BRANDING == 'Chrome': + inputs.append(os.path.join(GRIT_DIR, 'app_strings_%s.pak' % locale)) + inputs.append(os.path.join(GRIT_DIR, 'chromecast_settings_%s.pak' % locale)) + return inputs + + +def list_outputs(locales): + """Returns the names of files that will be generated for the given locales. + + This is to provide gyp the list of output files, so build targets can + properly track what needs to be built. + """ + outputs = [] + for locale in locales: + outputs.append(calc_output(locale)) + # Quote each element so filename spaces don't mess up gyp's attempt to parse + # it into a list. + return " ".join(['"%s"' % x for x in outputs]) + + +def list_inputs(locales): + """Returns the names of files that will be processed for the given locales. + + This is to provide gyp the list of input files, so build targets can properly + track their prerequisites. + """ + inputs = [] + for locale in locales: + inputs += calc_inputs(locale) + # Quote each element so filename spaces don't mess up gyp's attempt to parse + # it into a list. + return " ".join(['"%s"' % x for x in inputs]) + + +def repack_locales(locales): + """ Loop over and repack the given locales.""" + for locale in locales: + inputs = [] + inputs += calc_inputs(locale) + output = calc_output(locale) + data_pack.DataPack.RePack(output, inputs) + + +def DoMain(argv): + global CHROMECAST_BRANDING + global GRIT_DIR + global INT_DIR + + parser = optparse.OptionParser("usage: %prog [options] locales") + parser.add_option("-i", action="store_true", dest="inputs", default=False, + help="Print the expected input file list, then exit.") + parser.add_option("-o", action="store_true", dest="outputs", default=False, + help="Print the expected output file list, then exit.") + parser.add_option("-g", action="store", dest="grit_dir", + help="GRIT build files output directory.") + parser.add_option("-x", action="store", dest="int_dir", + help="Intermediate build files output directory.") + parser.add_option("-b", action="store", dest="chromecast_branding", + help="Chromecast branding ('Chrome' or 'Chromium').") + options, locales = parser.parse_args(argv) + + if not locales: + parser.error('Please specificy at least one locale to process.\n') + + print_inputs = options.inputs + print_outputs = options.outputs + GRIT_DIR = options.grit_dir + INT_DIR = options.int_dir + CHROMECAST_BRANDING = options.chromecast_branding + + if CHROMECAST_BRANDING != "Chrome" and CHROMECAST_BRANDING != "Chromium": + parser.error('Chromecast branding (-b) must be "Chrome" or "Chromium".\n') + if not (GRIT_DIR and INT_DIR): + parser.error('Please specify all of "-g" and "-x".\n') + if print_inputs and print_outputs: + parser.error('Please specify only one of "-i" or "-o".\n') + + if print_inputs: + return list_inputs(locales) + + if print_outputs: + return list_outputs(locales) + + return repack_locales(locales) + +if __name__ == '__main__': + results = DoMain(sys.argv[1:]) + if results: + print results diff --git a/chromeos/CHROMEOS_LKGM b/chromeos/CHROMEOS_LKGM index eca4d9c5c665d..eb39921382884 100644 --- a/chromeos/CHROMEOS_LKGM +++ b/chromeos/CHROMEOS_LKGM @@ -1 +1 @@ -6129.0.0 \ No newline at end of file +6149.0.0 \ No newline at end of file diff --git a/chromeos/audio/chromeos_sounds.h b/chromeos/audio/chromeos_sounds.h index e159985f00726..81788e6fd1bfb 100644 --- a/chromeos/audio/chromeos_sounds.h +++ b/chromeos/audio/chromeos_sounds.h @@ -19,7 +19,10 @@ enum { SOUND_SPOKEN_FEEDBACK_ENABLED, SOUND_SPOKEN_FEEDBACK_DISABLED, SOUND_VOLUME_ADJUST, - SOUND_COUNT + SOUND_PASSTHROUGH, + SOUND_EXIT_SCREEN, + SOUND_ENTER_SCREEN, + SOUND_COUNT, }; } // namespace chromeos diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index 872a7821d8ef4..d038ec51a8061 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -114,6 +114,8 @@ 'dbus/cros_disks_client.h', 'dbus/cryptohome_client.cc', 'dbus/cryptohome_client.h', + 'dbus/dbus_client_bundle.h', + 'dbus/dbus_client_bundle.cc', 'dbus/dbus_client_implementation_type.h', 'dbus/dbus_method_call_status.cc', 'dbus/dbus_method_call_status.h', @@ -534,6 +536,7 @@ 'cryptohome/system_salt_getter_unittest.cc', 'dbus/blocking_method_caller_unittest.cc', 'dbus/cros_disks_client_unittest.cc', + 'dbus/dbus_client_bundle_unittest.cc', 'dbus/gsm_sms_client_unittest.cc', 'dbus/introspectable_client_unittest.cc', 'dbus/modem_messaging_client_unittest.cc', diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc index 471c2507febf1..ac0e50c5bdb0e 100644 --- a/chromeos/chromeos_switches.cc +++ b/chromeos/chromeos_switches.cc @@ -21,6 +21,10 @@ const char kConsumerDeviceManagementUrl[] = "consumer-device-management-url"; // Forces the stub implementation of dbus clients. const char kDbusStub[] = "dbus-stub"; +// Comma-spearated list of dbus clients that should be unstubbed. +// See chromeos/dbus/dbus_client_bundle.cc for the names of the dbus clients. +const char kDbusUnstubClients[] = "dbus-unstub-clients"; + // Time before a machine at OOBE is considered derelict. const char kDerelictDetectionTimeout[] = "derelict-detection-timeout"; @@ -58,9 +62,6 @@ const char kDisableNewKioskUI[] = "disable-new-kiosk-ui"; const char kDisableOfficeEditingComponentApp[] = "disable-office-editing-component-extension"; -// Disables rollback option on reset screen. -const char kDisableRollbackOption[] = "disable-rollback-option"; - // Disables volume adjust sound. const char kDisableVolumeAdjustSound[] = "disable-volume-adjust-sound"; @@ -93,8 +94,8 @@ const char kEnableFileManagerMTP[] = "enable-filemanager-mtp"; const char kEnableNetworkPortalNotification[] = "enable-network-portal-notification"; -// Enables activation of voice search by saying 'Ok Google'. -const char kEnableOkGoogleVoiceSearch[] = "enable-ok-google-voice-search"; +// Enables rollback option on reset screen. +const char kEnableRollbackOption[] = "enable-rollback-option"; // Enables touchpad three-finger-click as middle button. const char kEnableTouchpadThreeFingerClick[] diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h index 1c3a9ed5cc34f..4dc5a2c4f98e5 100644 --- a/chromeos/chromeos_switches.h +++ b/chromeos/chromeos_switches.h @@ -24,6 +24,7 @@ CHROMEOS_EXPORT extern const char kAppOemManifestFile[]; CHROMEOS_EXPORT extern const char kAshWebUIInit[]; CHROMEOS_EXPORT extern const char kConsumerDeviceManagementUrl[]; CHROMEOS_EXPORT extern const char kDbusStub[]; +CHROMEOS_EXPORT extern const char kDbusUnstubClients[]; CHROMEOS_EXPORT extern const char kDerelictDetectionTimeout[]; CHROMEOS_EXPORT extern const char kDerelictIdleTimeout[]; CHROMEOS_EXPORT extern const char kDisableBootAnimation[]; @@ -37,7 +38,6 @@ CHROMEOS_EXPORT extern const char kDisableNetworkPortalNotification[]; CHROMEOS_EXPORT extern const char kDisableNewChannelSwitcherUI[]; CHROMEOS_EXPORT extern const char kDisableNewKioskUI[]; CHROMEOS_EXPORT extern const char kDisableOfficeEditingComponentApp[]; -CHROMEOS_EXPORT extern const char kDisableRollbackOption[]; CHROMEOS_EXPORT extern const char kDisableVolumeAdjustSound[]; CHROMEOS_EXPORT extern const char kEnableCarrierSwitching[]; CHROMEOS_EXPORT extern const char kEnableChromeVoxNext[]; @@ -48,8 +48,8 @@ CHROMEOS_EXPORT extern const char kEnableFileManagerMTP[]; CHROMEOS_EXPORT extern const char kEnableFirstRunUITransitions[]; CHROMEOS_EXPORT extern const char kEnableKioskMode[]; CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[]; -CHROMEOS_EXPORT extern const char kEnableOkGoogleVoiceSearch[]; CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[]; +CHROMEOS_EXPORT extern const char kEnableRollbackOption[]; CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[]; CHROMEOS_EXPORT extern const char kEnableVideoPlayerChromecastSupport[]; CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[]; diff --git a/chromeos/dbus/OWNERS b/chromeos/dbus/OWNERS index 9a25ad6f8ceab..8321aa688ac68 100644 --- a/chromeos/dbus/OWNERS +++ b/chromeos/dbus/OWNERS @@ -3,3 +3,6 @@ per-file *bluetooth*=armansito@chromium.org per-file *nfc*=keybuk@chromium.org per-file *nfc*=armansito@chromium.org per-file *cryptohome*=dkrahn@chromium.org +per-file *audio*=rkc@chromium.org +per-file *audio*=jennyz@chromium.org +per-file *audio*=hychao@chromium.org diff --git a/chromeos/dbus/bluetooth_agent_service_provider.cc b/chromeos/dbus/bluetooth_agent_service_provider.cc index 63e6d77b01306..46117d180af9a 100644 --- a/chromeos/dbus/bluetooth_agent_service_provider.cc +++ b/chromeos/dbus/bluetooth_agent_service_provider.cc @@ -9,8 +9,8 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "base/sys_info.h" #include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_bluetooth_agent_service_provider.h" #include "dbus/bus.h" #include "dbus/exported_object.h" @@ -469,7 +469,7 @@ BluetoothAgentServiceProvider* BluetoothAgentServiceProvider::Create( dbus::Bus* bus, const dbus::ObjectPath& object_path, Delegate* delegate) { - if (base::SysInfo::IsRunningOnChromeOS()) { + if (!DBusThreadManager::IsUsingStub(DBusClientBundle::BLUETOOTH)) { return new BluetoothAgentServiceProviderImpl(bus, object_path, delegate); } else { return new FakeBluetoothAgentServiceProvider(object_path, delegate); diff --git a/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc b/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc index 6270854af73a1..c54610c6afd33 100644 --- a/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc +++ b/chromeos/dbus/bluetooth_gatt_characteristic_service_provider.cc @@ -8,8 +8,8 @@ #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_util.h" -#include "base/sys_info.h" #include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_bluetooth_gatt_characteristic_service_provider.h" #include "dbus/exported_object.h" #include "dbus/message.h" @@ -464,7 +464,7 @@ BluetoothGattCharacteristicServiceProvider::Create( const std::vector& flags, const std::vector& permissions, const dbus::ObjectPath& service_path) { - if (base::SysInfo::IsRunningOnChromeOS()) { + if (!DBusThreadManager::IsUsingStub(DBusClientBundle::BLUETOOTH)) { return new BluetoothGattCharacteristicServiceProviderImpl( bus, object_path, delegate, uuid, flags, permissions, service_path); } diff --git a/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc b/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc index 2dfe7a1f4a80b..5040066f7e3aa 100644 --- a/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc +++ b/chromeos/dbus/bluetooth_gatt_descriptor_service_provider.cc @@ -8,8 +8,8 @@ #include "base/logging.h" #include "base/memory/weak_ptr.h" #include "base/strings/string_util.h" -#include "base/sys_info.h" #include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_bluetooth_gatt_descriptor_service_provider.h" #include "dbus/exported_object.h" #include "dbus/message.h" @@ -460,7 +460,7 @@ BluetoothGattDescriptorServiceProvider::Create( const std::string& uuid, const std::vector& permissions, const dbus::ObjectPath& characteristic_path) { - if (base::SysInfo::IsRunningOnChromeOS()) { + if (!DBusThreadManager::IsUsingStub(DBusClientBundle::BLUETOOTH)) { return new BluetoothGattDescriptorServiceProviderImpl( bus, object_path, delegate, uuid, permissions, characteristic_path); } diff --git a/chromeos/dbus/bluetooth_gatt_service_service_provider.cc b/chromeos/dbus/bluetooth_gatt_service_service_provider.cc index 545ccfc9239ed..88fd581a67aed 100644 --- a/chromeos/dbus/bluetooth_gatt_service_service_provider.cc +++ b/chromeos/dbus/bluetooth_gatt_service_service_provider.cc @@ -8,8 +8,8 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" -#include "base/sys_info.h" #include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_bluetooth_gatt_service_service_provider.h" #include "dbus/exported_object.h" #include "dbus/message.h" @@ -269,7 +269,7 @@ BluetoothGattServiceServiceProvider::Create( const dbus::ObjectPath& object_path, const std::string& uuid, const std::vector& includes) { - if (base::SysInfo::IsRunningOnChromeOS()) { + if (!DBusThreadManager::IsUsingStub(DBusClientBundle::BLUETOOTH)) { return new BluetoothGattServiceServiceProviderImpl( bus, object_path, uuid, includes); } diff --git a/chromeos/dbus/bluetooth_profile_service_provider.cc b/chromeos/dbus/bluetooth_profile_service_provider.cc index e30fadaaf5f99..1d9f2b5400242 100644 --- a/chromeos/dbus/bluetooth_profile_service_provider.cc +++ b/chromeos/dbus/bluetooth_profile_service_provider.cc @@ -9,8 +9,8 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "base/sys_info.h" #include "base/threading/platform_thread.h" +#include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_bluetooth_profile_service_provider.h" #include "dbus/bus.h" #include "dbus/exported_object.h" @@ -252,7 +252,7 @@ BluetoothProfileServiceProvider* BluetoothProfileServiceProvider::Create( dbus::Bus* bus, const dbus::ObjectPath& object_path, Delegate* delegate) { - if (base::SysInfo::IsRunningOnChromeOS()) { + if (!DBusThreadManager::IsUsingStub(DBusClientBundle::BLUETOOTH)) { return new BluetoothProfileServiceProviderImpl(bus, object_path, delegate); } else { return new FakeBluetoothProfileServiceProvider(object_path, delegate); diff --git a/chromeos/dbus/cras_audio_client.cc b/chromeos/dbus/cras_audio_client.cc index 779a3de415815..aa22c96b0e6a8 100644 --- a/chromeos/dbus/cras_audio_client.cc +++ b/chromeos/dbus/cras_audio_client.cc @@ -154,6 +154,26 @@ class CrasAudioClientImpl : public CrasAudioClient { dbus::ObjectProxy::EmptyResponseCallback()); } + virtual void AddActiveOutputNode(uint64 node_id) OVERRIDE { + dbus::MethodCall method_call(cras::kCrasControlInterface, + cras::kAddActiveOutputNode); + dbus::MessageWriter writer(&method_call); + writer.AppendUint64(node_id); + cras_proxy_->CallMethod(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + dbus::ObjectProxy::EmptyResponseCallback()); + } + + virtual void RemoveActiveOutputNode(uint64 node_id) OVERRIDE { + dbus::MethodCall method_call(cras::kCrasControlInterface, + cras::kRemoveActiveOutputNode); + dbus::MessageWriter writer(&method_call); + writer.AppendUint64(node_id); + cras_proxy_->CallMethod(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + dbus::ObjectProxy::EmptyResponseCallback()); + } + protected: virtual void Init(dbus::Bus* bus) OVERRIDE { cras_proxy_ = bus->GetObjectProxy(cras::kCrasServiceName, diff --git a/chromeos/dbus/cras_audio_client.h b/chromeos/dbus/cras_audio_client.h index c3d670897675d..230b868ce9420 100644 --- a/chromeos/dbus/cras_audio_client.h +++ b/chromeos/dbus/cras_audio_client.h @@ -105,6 +105,16 @@ class CHROMEOS_EXPORT CrasAudioClient : public DBusClient { // removing an active input node added by AddActiveInputNode. virtual void RemoveActiveInputNode(uint64 node_id) = 0; + // Adds input node |node_id| to the active outputs list. This is used to add + // an additional active output node besides the one set by SetActiveInputNode. + // Note that this action will not trigger an ActiveOutputNodeChanged event + // and nothing will happen if the |node_id| has already been set as active. + virtual void AddActiveOutputNode(uint64 node_id) = 0; + + // Removes output node |node_id| from the active output list. This is used for + // removing an active output node added by AddActiveOutputNode. + virtual void RemoveActiveOutputNode(uint64 node_id) = 0; + // Creates the instance. static CrasAudioClient* Create(); diff --git a/chromeos/dbus/cras_audio_client_stub_impl.cc b/chromeos/dbus/cras_audio_client_stub_impl.cc index c48d743c9fbd4..633db7b2f6899 100644 --- a/chromeos/dbus/cras_audio_client_stub_impl.cc +++ b/chromeos/dbus/cras_audio_client_stub_impl.cc @@ -137,9 +137,31 @@ void CrasAudioClientStubImpl::SetActiveInputNode(uint64 node_id) { } void CrasAudioClientStubImpl::AddActiveInputNode(uint64 node_id) { + for (size_t i = 0; i < node_list_.size(); ++i) { + if (node_list_[i].id == node_id) + node_list_[i].active = true; + } } void CrasAudioClientStubImpl::RemoveActiveInputNode(uint64 node_id) { + for (size_t i = 0; i < node_list_.size(); ++i) { + if (node_list_[i].id == node_id) + node_list_[i].active = false; + } +} + +void CrasAudioClientStubImpl::AddActiveOutputNode(uint64 node_id) { + for (size_t i = 0; i < node_list_.size(); ++i) { + if (node_list_[i].id == node_id) + node_list_[i].active = true; + } +} + +void CrasAudioClientStubImpl::RemoveActiveOutputNode(uint64 node_id) { + for (size_t i = 0; i < node_list_.size(); ++i) { + if (node_list_[i].id == node_id) + node_list_[i].active = false; + } } void CrasAudioClientStubImpl::SetAudioDevices( diff --git a/chromeos/dbus/cras_audio_client_stub_impl.h b/chromeos/dbus/cras_audio_client_stub_impl.h index 5589192f1d31f..e363e933a026f 100644 --- a/chromeos/dbus/cras_audio_client_stub_impl.h +++ b/chromeos/dbus/cras_audio_client_stub_impl.h @@ -33,6 +33,8 @@ class CrasAudioClientStubImpl : public CrasAudioClient { virtual void SetActiveInputNode(uint64 node_id) OVERRIDE; virtual void AddActiveInputNode(uint64 node_id) OVERRIDE; virtual void RemoveActiveInputNode(uint64 node_id) OVERRIDE; + virtual void AddActiveOutputNode(uint64 node_id) OVERRIDE; + virtual void RemoveActiveOutputNode(uint64 node_id) OVERRIDE; protected: // Helper functions for testing diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc index 78660c7902d28..c3af9d8609314 100644 --- a/chromeos/dbus/cryptohome_client.cc +++ b/chromeos/dbus/cryptohome_client.cc @@ -26,6 +26,11 @@ namespace { // stub_hash = "[user_id]-hash"; static const char kUserIdStubHashSuffix[] = "-hash"; +// Timeout for TPM operations. On slow machines it should be larger, than +// default DBus timeout. TPM operations can take up to 80 seconds, so limit +// is 2 minutes. +const int kTpmDBusTimeoutMs = 2 * 60 * 1000; + // The CryptohomeClient implementation. class CryptohomeClientImpl : public CryptohomeClient { public: @@ -74,7 +79,7 @@ class CryptohomeClientImpl : public CryptohomeClient { dbus::MessageWriter writer(&method_call); writer.AppendString(username); writer.AppendString(key); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -91,7 +96,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendString(username); writer.AppendString(from_key); writer.AppendString(to_key); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -104,7 +109,7 @@ class CryptohomeClientImpl : public CryptohomeClient { cryptohome::kCryptohomeAsyncRemove); dbus::MessageWriter writer(&method_call); writer.AppendString(username); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -114,7 +119,7 @@ class CryptohomeClientImpl : public CryptohomeClient { virtual void GetSystemSalt(const GetSystemSaltCallback& callback) OVERRIDE { dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, cryptohome::kCryptohomeGetSystemSalt); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnGetSystemSalt, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -128,7 +133,7 @@ class CryptohomeClientImpl : public CryptohomeClient { cryptohome::kCryptohomeGetSanitizedUsername); dbus::MessageWriter writer(&method_call); writer.AppendString(username); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnStringMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -168,7 +173,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(flags & cryptohome::ENSURE_EPHEMERAL); // deprecated_tracked_subdirectories writer.AppendArrayOfStrings(std::vector()); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -185,7 +190,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendString(username); writer.AppendString(key); writer.AppendString(new_key); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -195,7 +200,7 @@ class CryptohomeClientImpl : public CryptohomeClient { virtual void AsyncMountGuest(const AsyncMethodCallback& callback) OVERRIDE { dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, cryptohome::kCryptohomeAsyncMountGuest); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -211,7 +216,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendString(public_mount_id); writer.AppendBool(flags & cryptohome::CREATE_IF_MISSING); writer.AppendBool(flags & cryptohome::ENSURE_EPHEMERAL); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -245,7 +250,7 @@ class CryptohomeClientImpl : public CryptohomeClient { dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, cryptohome::kCryptohomeTpmGetPassword); proxy_->CallMethod( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + &method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnStringMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -322,7 +327,7 @@ class CryptohomeClientImpl : public CryptohomeClient { dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, cryptohome::kCryptohomePkcs11GetTpmTokenInfo); proxy_->CallMethod( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + &method_call, kTpmDBusTimeoutMs , base::Bind( &CryptohomeClientImpl::OnPkcs11GetTpmTokenInfo, weak_ptr_factory_.GetWeakPtr(), @@ -339,7 +344,7 @@ class CryptohomeClientImpl : public CryptohomeClient { dbus::MessageWriter writer(&method_call); writer.AppendString(user_email); proxy_->CallMethod( - &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + &method_call, kTpmDBusTimeoutMs , base::Bind( &CryptohomeClientImpl::OnPkcs11GetTpmTokenInfoForUser, weak_ptr_factory_.GetWeakPtr(), @@ -441,7 +446,7 @@ class CryptohomeClientImpl : public CryptohomeClient { cryptohome::kCryptohomeAsyncTpmAttestationCreateEnrollRequest); dbus::MessageWriter writer(&method_call); writer.AppendInt32(pca_type); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -460,7 +465,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendArrayOfBytes( reinterpret_cast(pca_response.data()), pca_response.size()); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -481,7 +486,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendInt32(certificate_profile); writer.AppendString(user_id); writer.AppendString(request_origin); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -505,7 +510,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(is_user_specific); writer.AppendString(user_id); writer.AppendString(key_name); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -542,7 +547,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(is_user_specific); writer.AppendString(user_id); writer.AppendString(key_name); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnDataMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -562,7 +567,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(is_user_specific); writer.AppendString(user_id); writer.AppendString(key_name); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnDataMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -582,7 +587,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(is_user_specific); writer.AppendString(user_id); writer.AppendString(key_name); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -614,7 +619,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(include_signed_public_key); writer.AppendArrayOfBytes(reinterpret_cast(challenge.data()), challenge.size()); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -637,7 +642,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendString(key_name); writer.AppendArrayOfBytes(reinterpret_cast(challenge.data()), challenge.size()); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnAsyncMethodCall, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -657,7 +662,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendBool(is_user_specific); writer.AppendString(user_id); writer.AppendString(key_name); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnDataMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -714,7 +719,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(auth); writer.AppendProtoAsArrayOfBytes(request); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -734,7 +739,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(auth); writer.AppendProtoAsArrayOfBytes(request); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -754,7 +759,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(auth); writer.AppendProtoAsArrayOfBytes(request); - proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs, base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -775,7 +780,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(request); proxy_->CallMethod(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -794,7 +799,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(request); proxy_->CallMethod(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -810,7 +815,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(request); proxy_->CallMethod(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -826,7 +831,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(request); proxy_->CallMethod(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -842,7 +847,7 @@ class CryptohomeClientImpl : public CryptohomeClient { writer.AppendProtoAsArrayOfBytes(request); proxy_->CallMethod(&method_call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -907,7 +912,7 @@ class CryptohomeClientImpl : public CryptohomeClient { // Calls a method without result values. void CallVoidMethod(dbus::MethodCall* method_call, const VoidDBusMethodCallback& callback) { - proxy_->CallMethod(method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(method_call, kTpmDBusTimeoutMs , base::Bind(&CryptohomeClientImpl::OnVoidMethod, weak_ptr_factory_.GetWeakPtr(), callback)); @@ -936,7 +941,7 @@ class CryptohomeClientImpl : public CryptohomeClient { // Calls a method with a bool value result. void CallBoolMethod(dbus::MethodCall* method_call, const BoolDBusMethodCallback& callback) { - proxy_->CallMethod(method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + proxy_->CallMethod(method_call, kTpmDBusTimeoutMs , base::Bind( &CryptohomeClientImpl::OnBoolMethod, weak_ptr_factory_.GetWeakPtr(), diff --git a/chromeos/dbus/dbus_client_bundle.cc b/chromeos/dbus/dbus_client_bundle.cc new file mode 100644 index 0000000000000..4ab8f3ed763bc --- /dev/null +++ b/chromeos/dbus/dbus_client_bundle.cc @@ -0,0 +1,305 @@ +// 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 "chromeos/dbus/dbus_client_bundle.h" + +#include "base/command_line.h" +#include "base/strings/string_split.h" +#include "base/strings/string_util.h" +#include "chromeos/chromeos_switches.h" +#include "chromeos/dbus/bluetooth_adapter_client.h" +#include "chromeos/dbus/bluetooth_agent_manager_client.h" +#include "chromeos/dbus/bluetooth_device_client.h" +#include "chromeos/dbus/bluetooth_gatt_characteristic_client.h" +#include "chromeos/dbus/bluetooth_gatt_descriptor_client.h" +#include "chromeos/dbus/bluetooth_gatt_manager_client.h" +#include "chromeos/dbus/bluetooth_gatt_service_client.h" +#include "chromeos/dbus/bluetooth_input_client.h" +#include "chromeos/dbus/bluetooth_profile_manager_client.h" +#include "chromeos/dbus/cras_audio_client.h" +#include "chromeos/dbus/cras_audio_client_stub_impl.h" +#include "chromeos/dbus/cros_disks_client.h" +#include "chromeos/dbus/cryptohome_client.h" +#include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/debug_daemon_client.h" +#include "chromeos/dbus/easy_unlock_client.h" +#include "chromeos/dbus/fake_bluetooth_adapter_client.h" +#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_device_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_characteristic_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_descriptor_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_manager_client.h" +#include "chromeos/dbus/fake_bluetooth_gatt_service_client.h" +#include "chromeos/dbus/fake_bluetooth_input_client.h" +#include "chromeos/dbus/fake_bluetooth_profile_manager_client.h" +#include "chromeos/dbus/fake_cryptohome_client.h" +#include "chromeos/dbus/fake_debug_daemon_client.h" +#include "chromeos/dbus/fake_easy_unlock_client.h" +#include "chromeos/dbus/fake_gsm_sms_client.h" +#include "chromeos/dbus/fake_image_burner_client.h" +#include "chromeos/dbus/fake_introspectable_client.h" +#include "chromeos/dbus/fake_lorgnette_manager_client.h" +#include "chromeos/dbus/fake_modem_messaging_client.h" +#include "chromeos/dbus/fake_nfc_adapter_client.h" +#include "chromeos/dbus/fake_nfc_device_client.h" +#include "chromeos/dbus/fake_nfc_manager_client.h" +#include "chromeos/dbus/fake_nfc_record_client.h" +#include "chromeos/dbus/fake_nfc_tag_client.h" +#include "chromeos/dbus/fake_permission_broker_client.h" +#include "chromeos/dbus/fake_shill_device_client.h" +#include "chromeos/dbus/fake_shill_ipconfig_client.h" +#include "chromeos/dbus/fake_shill_manager_client.h" +#include "chromeos/dbus/fake_shill_profile_client.h" +#include "chromeos/dbus/fake_shill_service_client.h" +#include "chromeos/dbus/fake_sms_client.h" +#include "chromeos/dbus/fake_system_clock_client.h" +#include "chromeos/dbus/gsm_sms_client.h" +#include "chromeos/dbus/image_burner_client.h" +#include "chromeos/dbus/introspectable_client.h" +#include "chromeos/dbus/lorgnette_manager_client.h" +#include "chromeos/dbus/modem_messaging_client.h" +#include "chromeos/dbus/nfc_adapter_client.h" +#include "chromeos/dbus/nfc_device_client.h" +#include "chromeos/dbus/nfc_manager_client.h" +#include "chromeos/dbus/nfc_record_client.h" +#include "chromeos/dbus/nfc_tag_client.h" +#include "chromeos/dbus/permission_broker_client.h" +#include "chromeos/dbus/power_manager_client.h" +#include "chromeos/dbus/power_policy_controller.h" +#include "chromeos/dbus/session_manager_client.h" +#include "chromeos/dbus/shill_device_client.h" +#include "chromeos/dbus/shill_ipconfig_client.h" +#include "chromeos/dbus/shill_manager_client.h" +#include "chromeos/dbus/shill_profile_client.h" +#include "chromeos/dbus/shill_service_client.h" +#include "chromeos/dbus/sms_client.h" +#include "chromeos/dbus/system_clock_client.h" +#include "chromeos/dbus/update_engine_client.h" + +namespace chromeos { + +namespace { + +// Command line switch mapping for --dbus-unstub-clients. +const struct { + const char* param_name; + DBusClientBundle::DBusClientType client_type; +} client_type_map[] = { + { "bluetooth", DBusClientBundle::BLUETOOTH }, + { "cras", DBusClientBundle::CRAS }, + { "cros_disks", DBusClientBundle::CROS_DISKS }, + { "cryptohome", DBusClientBundle::CRYPTOHOME }, + { "debug_daemon", DBusClientBundle::DEBUG_DAEMON }, + { "easy_unlock", DBusClientBundle::EASY_UNLOCK }, + { "lorgnette_manager", DBusClientBundle::LORGNETTE_MANAGER }, + { "shill", DBusClientBundle::SHILL }, + { "gsm_sms", DBusClientBundle::GSM_SMS }, + { "image_burner", DBusClientBundle::IMAGE_BURNER }, + { "introspectable", DBusClientBundle::INTROSPECTABLE }, + { "modem_messaging", DBusClientBundle::MODEM_MESSAGING }, + { "nfc", DBusClientBundle::NFC }, + { "permission_broker", DBusClientBundle::PERMISSION_BROKER }, + { "power_manager", DBusClientBundle::POWER_MANAGER }, + { "session_manager", DBusClientBundle::SESSION_MANAGER }, + { "sms", DBusClientBundle::SMS }, + { "system_clock", DBusClientBundle::SYSTEM_CLOCK }, + { "update_engine", DBusClientBundle::UPDATE_ENGINE }, +}; + +// Parses single command line param value for dbus subsystem and returns its +// enum representation. DBusClientType::UNKWNOWN is returned if |client_type| +// does not match any known dbus client. +DBusClientBundle::DBusClientType GetDBusClientType( + const std::string& client_type) { + for (size_t i = 0; i < arraysize(client_type_map); i++) { + if (LowerCaseEqualsASCII(client_type, client_type_map[i].param_name)) + return client_type_map[i].client_type; + } + return DBusClientBundle::NO_CLIENTS; +} + +} // namespace + +DBusClientBundle::DBusClientBundle() { + if (!DBusThreadManager::IsUsingStub(BLUETOOTH)) { + bluetooth_adapter_client_.reset(BluetoothAdapterClient::Create()); + bluetooth_agent_manager_client_.reset( + BluetoothAgentManagerClient::Create()); + bluetooth_device_client_.reset(BluetoothDeviceClient::Create()); + bluetooth_input_client_.reset(BluetoothInputClient::Create()); + bluetooth_profile_manager_client_.reset( + BluetoothProfileManagerClient::Create()); + bluetooth_gatt_characteristic_client_.reset( + BluetoothGattCharacteristicClient::Create()); + bluetooth_gatt_descriptor_client_.reset( + BluetoothGattDescriptorClient::Create()); + bluetooth_gatt_manager_client_.reset( + BluetoothGattManagerClient::Create()); + bluetooth_gatt_service_client_.reset( + BluetoothGattServiceClient::Create()); + } else { + bluetooth_adapter_client_.reset(new FakeBluetoothAdapterClient); + bluetooth_agent_manager_client_.reset(new FakeBluetoothAgentManagerClient); + bluetooth_device_client_.reset(new FakeBluetoothDeviceClient); + bluetooth_input_client_.reset(new FakeBluetoothInputClient); + bluetooth_profile_manager_client_.reset( + new FakeBluetoothProfileManagerClient); + bluetooth_gatt_characteristic_client_.reset( + new FakeBluetoothGattCharacteristicClient); + bluetooth_gatt_descriptor_client_.reset( + new FakeBluetoothGattDescriptorClient); + bluetooth_gatt_manager_client_.reset(new FakeBluetoothGattManagerClient); + bluetooth_gatt_service_client_.reset(new FakeBluetoothGattServiceClient); + } + + if (!DBusThreadManager::IsUsingStub(CRAS)) + cras_audio_client_.reset(CrasAudioClient::Create()); + else + cras_audio_client_.reset(new CrasAudioClientStubImpl); + + cros_disks_client_.reset(CrosDisksClient::Create( + DBusThreadManager::IsUsingStub(CROS_DISKS) ? + STUB_DBUS_CLIENT_IMPLEMENTATION : + REAL_DBUS_CLIENT_IMPLEMENTATION)); + + if (!DBusThreadManager::IsUsingStub(CRYPTOHOME)) + cryptohome_client_.reset(CryptohomeClient::Create()); + else + cryptohome_client_.reset(new FakeCryptohomeClient); + + if (!DBusThreadManager::IsUsingStub(DEBUG_DAEMON)) + debug_daemon_client_.reset(DebugDaemonClient::Create()); + else + debug_daemon_client_.reset(new FakeDebugDaemonClient); + + if (!DBusThreadManager::IsUsingStub(EASY_UNLOCK)) + easy_unlock_client_.reset(EasyUnlockClient::Create()); + else + easy_unlock_client_.reset(new FakeEasyUnlockClient); + + if (!DBusThreadManager::IsUsingStub(LORGNETTE_MANAGER)) + lorgnette_manager_client_.reset(LorgnetteManagerClient::Create()); + else + lorgnette_manager_client_.reset(new FakeLorgnetteManagerClient); + + if (!DBusThreadManager::IsUsingStub(SHILL)) { + shill_manager_client_.reset(ShillManagerClient::Create()); + shill_device_client_.reset(ShillDeviceClient::Create()); + shill_ipconfig_client_.reset(ShillIPConfigClient::Create()); + shill_service_client_.reset(ShillServiceClient::Create()); + shill_profile_client_.reset(ShillProfileClient::Create()); + } else { + shill_manager_client_.reset(new FakeShillManagerClient); + shill_device_client_.reset(new FakeShillDeviceClient); + shill_ipconfig_client_.reset(new FakeShillIPConfigClient); + shill_service_client_.reset(new FakeShillServiceClient); + shill_profile_client_.reset(new FakeShillProfileClient); + } + + if (!DBusThreadManager::IsUsingStub(GSM_SMS)) { + gsm_sms_client_.reset(GsmSMSClient::Create()); + } else { + FakeGsmSMSClient* gsm_sms_client = new FakeGsmSMSClient(); + gsm_sms_client->set_sms_test_message_switch_present( + CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kSmsTestMessages)); + gsm_sms_client_.reset(gsm_sms_client); + } + + if (!DBusThreadManager::IsUsingStub(IMAGE_BURNER)) + image_burner_client_.reset(ImageBurnerClient::Create()); + else + image_burner_client_.reset(new FakeImageBurnerClient); + + if (!DBusThreadManager::IsUsingStub(INTROSPECTABLE)) + introspectable_client_.reset(IntrospectableClient::Create()); + else + introspectable_client_.reset(new FakeIntrospectableClient); + + if (!DBusThreadManager::IsUsingStub(MODEM_MESSAGING)) + modem_messaging_client_.reset(ModemMessagingClient::Create()); + else + modem_messaging_client_.reset(ModemMessagingClient::Create()); + + // Create the NFC clients in the correct order based on their dependencies. + if (!DBusThreadManager::IsUsingStub(NFC)) { + nfc_manager_client_.reset(NfcManagerClient::Create()); + nfc_adapter_client_.reset( + NfcAdapterClient::Create(nfc_manager_client_.get())); + nfc_device_client_.reset( + NfcDeviceClient::Create(nfc_adapter_client_.get())); + nfc_tag_client_.reset(NfcTagClient::Create(nfc_adapter_client_.get())); + nfc_record_client_.reset(NfcRecordClient::Create(nfc_device_client_.get(), + nfc_tag_client_.get())); + } else { + nfc_manager_client_.reset(new FakeNfcManagerClient); + nfc_adapter_client_.reset(new FakeNfcAdapterClient); + nfc_device_client_.reset(new FakeNfcDeviceClient); + nfc_tag_client_.reset(new FakeNfcTagClient); + nfc_record_client_.reset(new FakeNfcRecordClient); + } + + if (!DBusThreadManager::IsUsingStub(PERMISSION_BROKER)) + permission_broker_client_.reset(PermissionBrokerClient::Create()); + else + permission_broker_client_.reset(new FakePermissionBrokerClient); + + power_manager_client_.reset(PowerManagerClient::Create( + DBusThreadManager::IsUsingStub(CROS_DISKS) ? + STUB_DBUS_CLIENT_IMPLEMENTATION : + REAL_DBUS_CLIENT_IMPLEMENTATION)); + + session_manager_client_.reset(SessionManagerClient::Create( + DBusThreadManager::IsUsingStub(CROS_DISKS) ? + STUB_DBUS_CLIENT_IMPLEMENTATION : + REAL_DBUS_CLIENT_IMPLEMENTATION)); + + if (!DBusThreadManager::IsUsingStub(SMS)) + sms_client_.reset(SMSClient::Create()); + else + sms_client_.reset(new FakeSMSClient); + + if (!DBusThreadManager::IsUsingStub(SYSTEM_CLOCK)) + system_clock_client_.reset(SystemClockClient::Create()); + else + system_clock_client_.reset(new FakeSystemClockClient); + + update_engine_client_.reset(UpdateEngineClient::Create( + DBusThreadManager::IsUsingStub(CROS_DISKS) ? + STUB_DBUS_CLIENT_IMPLEMENTATION : + REAL_DBUS_CLIENT_IMPLEMENTATION)); +} + +DBusClientBundle::~DBusClientBundle() { +} + +void DBusClientBundle::SetupDefaultEnvironment() { + ShillManagerClient::TestInterface* manager = + shill_manager_client_->GetTestInterface(); + if (manager) + manager->SetupDefaultEnvironment(); +} + +// static +DBusClientBundle::DBusClientTypeMask DBusClientBundle::ParseUnstubList( + const std::string& unstub_list) { + DBusClientTypeMask unstub_mask = 0; + std::vector unstub_components; + base::SplitString(unstub_list, ',', &unstub_components); + for (std::vector::const_iterator iter = + unstub_components.begin(); + iter != unstub_components.end(); ++iter) { + DBusClientBundle::DBusClientType client = GetDBusClientType(*iter); + if (client != DBusClientBundle::NO_CLIENTS) { + LOG(WARNING) << "Unstubbing dbus client for " << *iter; + unstub_mask |= client; + } else { + LOG(ERROR) << "Unknown dbus client: " << *iter; + } + } + + return unstub_mask; +} + +} // namespace chromeos diff --git a/chromeos/dbus/dbus_client_bundle.h b/chromeos/dbus/dbus_client_bundle.h new file mode 100644 index 0000000000000..9d5d22d855db6 --- /dev/null +++ b/chromeos/dbus/dbus_client_bundle.h @@ -0,0 +1,277 @@ +// 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 CHROMEOS_DBUS_DBUS_CLIENT_BUNDLE_H_ +#define CHROMEOS_DBUS_DBUS_CLIENT_BUNDLE_H_ + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" + +namespace chromeos { + +class BluetoothAdapterClient; +class BluetoothAgentManagerClient; +class BluetoothDeviceClient; +class BluetoothGattCharacteristicClient; +class BluetoothGattDescriptorClient; +class BluetoothGattManagerClient; +class BluetoothGattServiceClient; +class BluetoothInputClient; +class BluetoothProfileManagerClient; +class CrasAudioClient; +class CrosDisksClient; +class CryptohomeClient; +class DebugDaemonClient; +class EasyUnlockClient; +class LorgnetteManagerClient; +class ShillDeviceClient; +class ShillIPConfigClient; +class ShillManagerClient; +class ShillServiceClient; +class ShillProfileClient; +class GsmSMSClient; +class ImageBurnerClient; +class IntrospectableClient; +class ModemMessagingClient; +class NfcManagerClient; +class NfcAdapterClient; +class NfcDeviceClient; +class NfcTagClient; +class NfcRecordClient; +class PermissionBrokerClient; +class SystemClockClient; +class PowerManagerClient; +class SessionManagerClient; +class SMSClient; +class UpdateEngineClient; + +// The bundle of all D-Bus clients used in DBusThreadManagerImpl. The bundle +// is used to delete them at once in the right order before shutting down the +// system bus. See also the comment in the destructor of DBusThreadManagerImpl. +class CHROMEOS_EXPORT DBusClientBundle { + public: + typedef unsigned int DBusClientTypeMask; + + // TODO(zelidrag): We might want to collapse few more of these subsystems if + // their dbus interfaced correspond to the same daemon. + enum DBusClientType { + NO_CLIENTS = 0, + BLUETOOTH = 1 << 0, + CRAS = 1 << 1, + CROS_DISKS = 1 << 2, + CRYPTOHOME = 1 << 3, + DEBUG_DAEMON = 1 << 4, + EASY_UNLOCK = 1 << 5, + LORGNETTE_MANAGER = 1 << 6, + SHILL = 1 << 7, + GSM_SMS = 1 << 8, + IMAGE_BURNER = 1 << 9, + INTROSPECTABLE = 1 << 10, + MODEM_MESSAGING = 1 << 11, + NFC = 1 << 12, + PERMISSION_BROKER = 1 << 13, + POWER_MANAGER = 1 << 14, + SESSION_MANAGER = 1 << 15, + SMS = 1 << 16, + SYSTEM_CLOCK = 1 << 17, + UPDATE_ENGINE = 1 << 18, + ALL_CLIENTS = ~static_cast(0), + }; + + DBusClientBundle(); + virtual ~DBusClientBundle(); + + // Initialize proper runtime environment for its dbus clients. + void SetupDefaultEnvironment(); + + // Parses command line param values for dbus subsystem that should be + // un-stubbed. + static DBusClientTypeMask ParseUnstubList(const std::string& unstub_list); + + BluetoothAdapterClient* bluetooth_adapter_client() { + return bluetooth_adapter_client_.get(); + } + + BluetoothAgentManagerClient* bluetooth_agent_manager_client() { + return bluetooth_agent_manager_client_.get(); + } + + BluetoothDeviceClient* bluetooth_device_client() { + return bluetooth_device_client_.get(); + } + + BluetoothGattCharacteristicClient* bluetooth_gatt_characteristic_client() { + return bluetooth_gatt_characteristic_client_.get(); + } + + BluetoothGattDescriptorClient* bluetooth_gatt_descriptor_client() { + return bluetooth_gatt_descriptor_client_.get(); + + } + BluetoothGattManagerClient* bluetooth_gatt_manager_client() { + return bluetooth_gatt_manager_client_.get(); + } + + BluetoothGattServiceClient* bluetooth_gatt_service_client() { + return bluetooth_gatt_service_client_.get(); + } + + BluetoothInputClient* bluetooth_input_client() { + return bluetooth_input_client_.get(); + } + + BluetoothProfileManagerClient* bluetooth_profile_manager_client() { + return bluetooth_profile_manager_client_.get(); + } + + CrasAudioClient* cras_audio_client() { + return cras_audio_client_.get(); + } + + CrosDisksClient* cros_disks_client() { + return cros_disks_client_.get(); + } + + CryptohomeClient* cryptohome_client() { + return cryptohome_client_.get(); + } + + DebugDaemonClient* debug_daemon_client() { + return debug_daemon_client_.get(); + } + + EasyUnlockClient* easy_unlock_client() { + return easy_unlock_client_.get(); + } + + LorgnetteManagerClient* lorgnette_manager_client() { + return lorgnette_manager_client_.get(); + } + + ShillDeviceClient* shill_device_client() { + return shill_device_client_.get(); + } + + ShillIPConfigClient* shill_ipconfig_client() { + return shill_ipconfig_client_.get(); + } + + ShillManagerClient* shill_manager_client() { + return shill_manager_client_.get(); + } + + ShillServiceClient* shill_service_client() { + return shill_service_client_.get(); + } + + ShillProfileClient* shill_profile_client() { + return shill_profile_client_.get(); + } + + GsmSMSClient* gsm_sms_client() { + return gsm_sms_client_.get(); + } + + ImageBurnerClient* image_burner_client() { + return image_burner_client_.get(); + } + + IntrospectableClient* introspectable_client() { + return introspectable_client_.get(); + } + + ModemMessagingClient* modem_messaging_client() { + return modem_messaging_client_.get(); + } + + NfcManagerClient* nfc_manager_client() { + return nfc_manager_client_.get(); + } + + NfcAdapterClient* nfc_adapter_client() { + return nfc_adapter_client_.get(); + } + + NfcDeviceClient* nfc_device_client() { + return nfc_device_client_.get(); + } + + NfcTagClient* nfc_tag_client() { + return nfc_tag_client_.get(); + } + + NfcRecordClient* nfc_record_client() { + return nfc_record_client_.get(); + } + + PermissionBrokerClient* permission_broker_client() { + return permission_broker_client_.get(); + } + + SystemClockClient* system_clock_client() { + return system_clock_client_.get(); + } + + PowerManagerClient* power_manager_client() { + return power_manager_client_.get(); + } + + SessionManagerClient* session_manager_client() { + return session_manager_client_.get(); + } + + SMSClient* sms_client() { + return sms_client_.get(); + } + + UpdateEngineClient* update_engine_client() { + return update_engine_client_.get(); + } + + private: + scoped_ptr bluetooth_adapter_client_; + scoped_ptr bluetooth_agent_manager_client_; + scoped_ptr bluetooth_device_client_; + scoped_ptr + bluetooth_gatt_characteristic_client_; + scoped_ptr bluetooth_gatt_descriptor_client_; + scoped_ptr bluetooth_gatt_manager_client_; + scoped_ptr bluetooth_gatt_service_client_; + scoped_ptr bluetooth_input_client_; + scoped_ptr bluetooth_profile_manager_client_; + scoped_ptr cras_audio_client_; + scoped_ptr cros_disks_client_; + scoped_ptr cryptohome_client_; + scoped_ptr debug_daemon_client_; + scoped_ptr easy_unlock_client_; + scoped_ptr lorgnette_manager_client_; + scoped_ptr shill_device_client_; + scoped_ptr shill_ipconfig_client_; + scoped_ptr shill_manager_client_; + scoped_ptr shill_service_client_; + scoped_ptr shill_profile_client_; + scoped_ptr gsm_sms_client_; + scoped_ptr image_burner_client_; + scoped_ptr introspectable_client_; + scoped_ptr modem_messaging_client_; + // The declaration order for NFC client objects is important. See + // DBusThreadManager::CreateDefaultClients for the dependencies. + scoped_ptr nfc_manager_client_; + scoped_ptr nfc_adapter_client_; + scoped_ptr nfc_device_client_; + scoped_ptr nfc_tag_client_; + scoped_ptr nfc_record_client_; + scoped_ptr permission_broker_client_; + scoped_ptr system_clock_client_; + scoped_ptr power_manager_client_; + scoped_ptr session_manager_client_; + scoped_ptr sms_client_; + scoped_ptr update_engine_client_; + + DISALLOW_COPY_AND_ASSIGN(DBusClientBundle); +}; + +} // namespace chromeos + +#endif // CHROMEOS_DBUS_DBUS_CLIENT_BUNDLE_H_ diff --git a/chromeos/dbus/dbus_client_bundle_unittest.cc b/chromeos/dbus/dbus_client_bundle_unittest.cc new file mode 100644 index 0000000000000..4c05dfdd23eb3 --- /dev/null +++ b/chromeos/dbus/dbus_client_bundle_unittest.cc @@ -0,0 +1,40 @@ +// 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 "chromeos/dbus/dbus_client_bundle.h" + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { + +TEST(DBusClientBundleTest, UnstubFlagParser) { + EXPECT_EQ(DBusClientBundle::NO_CLIENTS, + DBusClientBundle::ParseUnstubList("foo")); + + EXPECT_EQ(DBusClientBundle::BLUETOOTH, + DBusClientBundle::ParseUnstubList("BLUETOOTH")); + + EXPECT_EQ(DBusClientBundle::BLUETOOTH, + DBusClientBundle::ParseUnstubList("bluetooth")); + + EXPECT_EQ( + DBusClientBundle::CRAS | + DBusClientBundle::CROS_DISKS | + DBusClientBundle::DEBUG_DAEMON | + DBusClientBundle::SHILL, + DBusClientBundle::ParseUnstubList( + "Cras,Cros_Disks,debug_daemon,Shill")); + + EXPECT_EQ( + DBusClientBundle::CRAS | + DBusClientBundle::CROS_DISKS | + DBusClientBundle::DEBUG_DAEMON | + DBusClientBundle::SHILL, + DBusClientBundle::ParseUnstubList( + "foo,Cras,Cros_Disks,debug_daemon,Shill")); +} + +} // namespace chromeos diff --git a/chromeos/dbus/dbus_thread_manager.cc b/chromeos/dbus/dbus_thread_manager.cc index e372005dc5266..f2edfad992480 100644 --- a/chromeos/dbus/dbus_thread_manager.cc +++ b/chromeos/dbus/dbus_thread_manager.cc @@ -5,7 +5,6 @@ #include "chromeos/dbus/dbus_thread_manager.h" #include "base/command_line.h" -#include "base/observer_list.h" #include "base/sys_info.h" #include "base/threading/thread.h" #include "chromeos/chromeos_switches.h" @@ -22,7 +21,7 @@ #include "chromeos/dbus/cros_disks_client.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_client.h" -#include "chromeos/dbus/dbus_thread_manager_observer.h" +#include "chromeos/dbus/dbus_client_bundle.h" #include "chromeos/dbus/debug_daemon_client.h" #include "chromeos/dbus/easy_unlock_client.h" #include "chromeos/dbus/fake_dbus_thread_manager.h" @@ -56,207 +55,8 @@ namespace chromeos { static DBusThreadManager* g_dbus_thread_manager = NULL; static DBusThreadManager* g_dbus_thread_manager_for_testing = NULL; -// The bundle of all D-Bus clients used in DBusThreadManagerImpl. The bundle -// is used to delete them at once in the right order before shutting down the -// system bus. See also the comment in the destructor of DBusThreadManagerImpl. -class DBusClientBundle { - public: - DBusClientBundle() { - const DBusClientImplementationType type = REAL_DBUS_CLIENT_IMPLEMENTATION; - - bluetooth_adapter_client_.reset(BluetoothAdapterClient::Create()); - bluetooth_agent_manager_client_.reset( - BluetoothAgentManagerClient::Create()); - bluetooth_device_client_.reset(BluetoothDeviceClient::Create()); - bluetooth_gatt_characteristic_client_.reset( - BluetoothGattCharacteristicClient::Create()); - bluetooth_gatt_descriptor_client_.reset( - BluetoothGattDescriptorClient::Create()); - bluetooth_gatt_manager_client_.reset(BluetoothGattManagerClient::Create()); - bluetooth_gatt_service_client_.reset(BluetoothGattServiceClient::Create()); - bluetooth_input_client_.reset(BluetoothInputClient::Create()); - bluetooth_profile_manager_client_.reset( - BluetoothProfileManagerClient::Create()); - cras_audio_client_.reset(CrasAudioClient::Create()); - cros_disks_client_.reset(CrosDisksClient::Create(type)); - cryptohome_client_.reset(CryptohomeClient::Create()); - debug_daemon_client_.reset(DebugDaemonClient::Create()); - easy_unlock_client_.reset(EasyUnlockClient::Create()); - lorgnette_manager_client_.reset(LorgnetteManagerClient::Create()); - shill_manager_client_.reset(ShillManagerClient::Create()); - shill_device_client_.reset(ShillDeviceClient::Create()); - shill_ipconfig_client_.reset(ShillIPConfigClient::Create()); - shill_service_client_.reset(ShillServiceClient::Create()); - shill_profile_client_.reset(ShillProfileClient::Create()); - gsm_sms_client_.reset(GsmSMSClient::Create()); - image_burner_client_.reset(ImageBurnerClient::Create()); - introspectable_client_.reset(IntrospectableClient::Create()); - modem_messaging_client_.reset(ModemMessagingClient::Create()); - // Create the NFC clients in the correct order based on their dependencies. - nfc_manager_client_.reset(NfcManagerClient::Create()); - nfc_adapter_client_.reset( - NfcAdapterClient::Create(nfc_manager_client_.get())); - nfc_device_client_.reset( - NfcDeviceClient::Create(nfc_adapter_client_.get())); - nfc_tag_client_.reset(NfcTagClient::Create(nfc_adapter_client_.get())); - nfc_record_client_.reset(NfcRecordClient::Create(nfc_device_client_.get(), - nfc_tag_client_.get())); - permission_broker_client_.reset(PermissionBrokerClient::Create()); - power_manager_client_.reset(PowerManagerClient::Create(type)); - session_manager_client_.reset(SessionManagerClient::Create(type)); - sms_client_.reset(SMSClient::Create()); - system_clock_client_.reset(SystemClockClient::Create()); - update_engine_client_.reset(UpdateEngineClient::Create(type)); - } - - BluetoothAdapterClient* bluetooth_adapter_client() { - return bluetooth_adapter_client_.get(); - } - BluetoothAgentManagerClient* bluetooth_agent_manager_client() { - return bluetooth_agent_manager_client_.get(); - } - BluetoothDeviceClient* bluetooth_device_client() { - return bluetooth_device_client_.get(); - } - BluetoothGattCharacteristicClient* bluetooth_gatt_characteristic_client() { - return bluetooth_gatt_characteristic_client_.get(); - } - BluetoothGattDescriptorClient* bluetooth_gatt_descriptor_client() { - return bluetooth_gatt_descriptor_client_.get(); - } - BluetoothGattManagerClient* bluetooth_gatt_manager_client() { - return bluetooth_gatt_manager_client_.get(); - } - BluetoothGattServiceClient* bluetooth_gatt_service_client() { - return bluetooth_gatt_service_client_.get(); - } - BluetoothInputClient* bluetooth_input_client() { - return bluetooth_input_client_.get(); - } - BluetoothProfileManagerClient* bluetooth_profile_manager_client() { - return bluetooth_profile_manager_client_.get(); - } - CrasAudioClient* cras_audio_client() { - return cras_audio_client_.get(); - } - CrosDisksClient* cros_disks_client() { - return cros_disks_client_.get(); - } - CryptohomeClient* cryptohome_client() { - return cryptohome_client_.get(); - } - DebugDaemonClient* debug_daemon_client() { - return debug_daemon_client_.get(); - } - EasyUnlockClient* easy_unlock_client() { - return easy_unlock_client_.get(); - } - LorgnetteManagerClient* lorgnette_manager_client() { - return lorgnette_manager_client_.get(); - } - ShillDeviceClient* shill_device_client() { - return shill_device_client_.get(); - } - ShillIPConfigClient* shill_ipconfig_client() { - return shill_ipconfig_client_.get(); - } - ShillManagerClient* shill_manager_client() { - return shill_manager_client_.get(); - } - ShillServiceClient* shill_service_client() { - return shill_service_client_.get(); - } - ShillProfileClient* shill_profile_client() { - return shill_profile_client_.get(); - } - GsmSMSClient* gsm_sms_client() { - return gsm_sms_client_.get(); - } - ImageBurnerClient* image_burner_client() { - return image_burner_client_.get(); - } - IntrospectableClient* introspectable_client() { - return introspectable_client_.get(); - } - ModemMessagingClient* modem_messaging_client() { - return modem_messaging_client_.get(); - } - NfcManagerClient* nfc_manager_client() { - return nfc_manager_client_.get(); - } - NfcAdapterClient* nfc_adapter_client() { - return nfc_adapter_client_.get(); - } - NfcDeviceClient* nfc_device_client() { - return nfc_device_client_.get(); - } - NfcTagClient* nfc_tag_client() { - return nfc_tag_client_.get(); - } - NfcRecordClient* nfc_record_client() { - return nfc_record_client_.get(); - } - PermissionBrokerClient* permission_broker_client() { - return permission_broker_client_.get(); - } - SystemClockClient* system_clock_client() { - return system_clock_client_.get(); - } - PowerManagerClient* power_manager_client() { - return power_manager_client_.get(); - } - SessionManagerClient* session_manager_client() { - return session_manager_client_.get(); - } - SMSClient* sms_client() { - return sms_client_.get(); - } - UpdateEngineClient* update_engine_client() { - return update_engine_client_.get(); - } - - private: - scoped_ptr bluetooth_adapter_client_; - scoped_ptr bluetooth_agent_manager_client_; - scoped_ptr bluetooth_device_client_; - scoped_ptr - bluetooth_gatt_characteristic_client_; - scoped_ptr bluetooth_gatt_descriptor_client_; - scoped_ptr bluetooth_gatt_manager_client_; - scoped_ptr bluetooth_gatt_service_client_; - scoped_ptr bluetooth_input_client_; - scoped_ptr bluetooth_profile_manager_client_; - scoped_ptr cras_audio_client_; - scoped_ptr cros_disks_client_; - scoped_ptr cryptohome_client_; - scoped_ptr debug_daemon_client_; - scoped_ptr easy_unlock_client_; - scoped_ptr lorgnette_manager_client_; - scoped_ptr shill_device_client_; - scoped_ptr shill_ipconfig_client_; - scoped_ptr shill_manager_client_; - scoped_ptr shill_service_client_; - scoped_ptr shill_profile_client_; - scoped_ptr gsm_sms_client_; - scoped_ptr image_burner_client_; - scoped_ptr introspectable_client_; - scoped_ptr modem_messaging_client_; - // The declaration order for NFC client objects is important. See - // DBusThreadManager::CreateDefaultClients for the dependencies. - scoped_ptr nfc_manager_client_; - scoped_ptr nfc_adapter_client_; - scoped_ptr nfc_device_client_; - scoped_ptr nfc_tag_client_; - scoped_ptr nfc_record_client_; - scoped_ptr permission_broker_client_; - scoped_ptr system_clock_client_; - scoped_ptr power_manager_client_; - scoped_ptr session_manager_client_; - scoped_ptr sms_client_; - scoped_ptr update_engine_client_; - - DISALLOW_COPY_AND_ASSIGN(DBusClientBundle); -}; +DBusClientBundle::DBusClientTypeMask + DBusThreadManager::unstub_client_mask_ = DBusClientBundle::NO_CLIENTS; // The DBusThreadManager implementation used in production. class DBusThreadManagerImpl : public DBusThreadManager { @@ -279,9 +79,6 @@ class DBusThreadManagerImpl : public DBusThreadManager { } virtual ~DBusThreadManagerImpl() { - FOR_EACH_OBSERVER(DBusThreadManagerObserver, observers_, - OnDBusThreadManagerDestroying(this)); - // PowerPolicyController's destructor depends on PowerManagerClient. power_policy_controller_.reset(); @@ -296,15 +93,8 @@ class DBusThreadManagerImpl : public DBusThreadManager { dbus_thread_->Stop(); } - // DBusThreadManager overrides: - virtual void AddObserver(DBusThreadManagerObserver* observer) OVERRIDE { - DCHECK(observer); - observers_.AddObserver(observer); - } - - virtual void RemoveObserver(DBusThreadManagerObserver* observer) OVERRIDE { - DCHECK(observer); - observers_.RemoveObserver(observer); + void SetupDefaultEnvironment() { + return client_bundle_->SetupDefaultEnvironment(); } virtual dbus::Bus* GetSystemBus() OVERRIDE { @@ -462,14 +252,12 @@ class DBusThreadManagerImpl : public DBusThreadManager { // Constructs all clients and stores them in the respective *_client_ member // variable. void CreateDefaultClients() { - client_bundle_.reset(new DBusClientBundle); + client_bundle_.reset(new DBusClientBundle()); + // TODO(crbug.com/345586): Move PowerPolicyController out of + // DBusThreadManagerImpl. power_policy_controller_.reset(new PowerPolicyController); } - // Note: Keep this before other members so they can call AddObserver() in - // their c'tors. - ObserverList observers_; - scoped_ptr dbus_thread_; scoped_refptr system_bus_; scoped_ptr client_bundle_; @@ -478,6 +266,11 @@ class DBusThreadManagerImpl : public DBusThreadManager { DISALLOW_COPY_AND_ASSIGN(DBusThreadManagerImpl); }; +// static +bool DBusThreadManager::IsUsingStub(DBusClientBundle::DBusClientType client) { + return !(unstub_client_mask_ & client); +} + // static void DBusThreadManager::Initialize() { // If we initialize DBusThreadManager twice we may also be shutting it down @@ -491,15 +284,20 @@ void DBusThreadManager::Initialize() { return; } - // Determine whether we use stub or real client implementations. - if (!base::SysInfo::IsRunningOnChromeOS() || + bool use_dbus_stub = !base::SysInfo::IsRunningOnChromeOS() || CommandLine::ForCurrentProcess()->HasSwitch( - chromeos::switches::kDbusStub)) { + chromeos::switches::kDbusStub); + bool force_unstub_clients = CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kDbusUnstubClients); + // Determine whether we use stub or real client implementations. + if (force_unstub_clients) { + InitializeWithPartialStub( + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + chromeos::switches::kDbusUnstubClients)); + } else if (use_dbus_stub) { InitializeWithStub(); } else { - g_dbus_thread_manager = new DBusThreadManagerImpl; - InitializeClients(); - VLOG(1) << "DBusThreadManager initialized for Chrome OS"; + InitializeRegular(); } } @@ -514,12 +312,44 @@ void DBusThreadManager::SetInstanceForTesting( // static void DBusThreadManager::InitializeForTesting( DBusThreadManager* dbus_thread_manager) { + unstub_client_mask_ = DBusClientBundle::NO_CLIENTS; SetInstanceForTesting(dbus_thread_manager); Initialize(); } +// static +void DBusThreadManager::InitializeRegular() { + unstub_client_mask_ = DBusClientBundle::ALL_CLIENTS; + g_dbus_thread_manager = new DBusThreadManagerImpl(); + InitializeClients(); + VLOG(1) << "DBusThreadManager initialized for Chrome OS"; +} + +// static +void DBusThreadManager::InitializeWithPartialStub( + const std::string& unstub_clients) { + // If we initialize DBusThreadManager twice we may also be shutting it down + // early; do not allow that. + CHECK(g_dbus_thread_manager == NULL); + + unstub_client_mask_ = DBusClientBundle::ParseUnstubList(unstub_clients); + // We should have something parsed correctly here. + if (unstub_client_mask_ == 0) { + LOG(FATAL) << "Switch values for --" + << chromeos::switches::kDbusUnstubClients + << " cannot be parsed: " + << unstub_clients; + } + DBusThreadManagerImpl* dbus_thread_manager = new DBusThreadManagerImpl(); + VLOG(1) << "DBusThreadManager initialized for mixed runtime environment"; + g_dbus_thread_manager = dbus_thread_manager; + InitializeClients(); + dbus_thread_manager->SetupDefaultEnvironment(); +} + // static void DBusThreadManager::InitializeWithStub() { + unstub_client_mask_ = DBusClientBundle::NO_CLIENTS; // If we initialize DBusThreadManager twice we may also be shutting it down // early; do not allow that. CHECK(g_dbus_thread_manager == NULL); diff --git a/chromeos/dbus/dbus_thread_manager.h b/chromeos/dbus/dbus_thread_manager.h index f225a5e14ca74..053763c984d08 100644 --- a/chromeos/dbus/dbus_thread_manager.h +++ b/chromeos/dbus/dbus_thread_manager.h @@ -9,6 +9,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "chromeos/chromeos_export.h" +#include "chromeos/dbus/dbus_client_bundle.h" namespace base { class Thread; @@ -21,8 +22,6 @@ class ObjectPath; namespace chromeos { -class DBusThreadManagerObserver; - // Style Note: Clients are sorted by names. class BluetoothAdapterClient; class BluetoothAgentManagerClient; @@ -116,9 +115,8 @@ class CHROMEOS_EXPORT DBusThreadManager { // Gets the global instance. Initialize() must be called first. static DBusThreadManager* Get(); - // Adds or removes an observer. - virtual void AddObserver(DBusThreadManagerObserver* observer) = 0; - virtual void RemoveObserver(DBusThreadManagerObserver* observer) = 0; + // Returns true if |client| is stubbed. + static bool IsUsingStub(DBusClientBundle::DBusClientType client); // Returns various D-Bus bus instances, owned by DBusThreadManager. virtual dbus::Bus* GetSystemBus() = 0; @@ -169,6 +167,13 @@ class CHROMEOS_EXPORT DBusThreadManager { DBusThreadManager(); private: + // Initialize global thread manager instance. + static void InitializeRegular(); + + // Initialize with stub implementations for only certain clients that are + // not included in comma-separated |unstub_clients| list. + static void InitializeWithPartialStub(const std::string& unstub_clients); + // InitializeClients is called after g_dbus_thread_manager is set. // NOTE: Clients that access other clients in their Init() must be // initialized in the correct order. @@ -177,6 +182,10 @@ class CHROMEOS_EXPORT DBusThreadManager { // Initializes |client| with the |system_bus_|. static void InitClient(DBusClient* client); + // Bitmask that defines which dbus clients are not stubbed out. Bitmap flags + // are defined within DBusClientBundle::DBusClientType enum. + static DBusClientBundle::DBusClientTypeMask unstub_client_mask_; + DISALLOW_COPY_AND_ASSIGN(DBusThreadManager); }; diff --git a/chromeos/dbus/dbus_thread_manager_observer.h b/chromeos/dbus/dbus_thread_manager_observer.h deleted file mode 100644 index 77ce14f849f12..0000000000000 --- a/chromeos/dbus/dbus_thread_manager_observer.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROMEOS_DBUS_DBUS_THREAD_MANAGER_OBSERVER_H_ -#define CHROMEOS_DBUS_DBUS_THREAD_MANAGER_OBSERVER_H_ - -namespace chromeos { - -class DBusThreadManager; - -// Interface for classes that are interested in observing changes to the -// DBusThreadManager. -class DBusThreadManagerObserver { - public: - // Called when |manager| is about to be destroyed. |manager| is still usable - // at this point. - virtual void OnDBusThreadManagerDestroying(DBusThreadManager* manager) = 0; - - protected: - virtual ~DBusThreadManagerObserver() {} -}; - -} // namespace chromeos - -#endif // CHROMEOS_DBUS_DBUS_THREAD_MANAGER_OBSERVER_H_ diff --git a/chromeos/dbus/fake_dbus_thread_manager.cc b/chromeos/dbus/fake_dbus_thread_manager.cc index 38e67e3d7b1a6..abf26e96ac6d6 100644 --- a/chromeos/dbus/fake_dbus_thread_manager.cc +++ b/chromeos/dbus/fake_dbus_thread_manager.cc @@ -10,7 +10,6 @@ #include "chromeos/dbus/cros_disks_client.h" #include "chromeos/dbus/dbus_client.h" #include "chromeos/dbus/dbus_thread_manager.h" -#include "chromeos/dbus/dbus_thread_manager_observer.h" #include "chromeos/dbus/fake_bluetooth_adapter_client.h" #include "chromeos/dbus/fake_bluetooth_agent_manager_client.h" #include "chromeos/dbus/fake_bluetooth_device_client.h" @@ -52,8 +51,6 @@ FakeDBusThreadManager::FakeDBusThreadManager() { } FakeDBusThreadManager::~FakeDBusThreadManager() { - FOR_EACH_OBSERVER(DBusThreadManagerObserver, observers_, - OnDBusThreadManagerDestroying(this)); } void FakeDBusThreadManager::SetFakeClients() { @@ -319,17 +316,6 @@ void FakeDBusThreadManager::SetUpdateEngineClient( update_engine_client_ = client.Pass(); } -void FakeDBusThreadManager::AddObserver(DBusThreadManagerObserver* observer) { - DCHECK(observer); - observers_.AddObserver(observer); -} - -void FakeDBusThreadManager::RemoveObserver( - DBusThreadManagerObserver* observer) { - DCHECK(observer); - observers_.RemoveObserver(observer); -} - dbus::Bus* FakeDBusThreadManager::GetSystemBus() { return NULL; } diff --git a/chromeos/dbus/fake_dbus_thread_manager.h b/chromeos/dbus/fake_dbus_thread_manager.h index 075b906b81aa1..d70f59900531b 100644 --- a/chromeos/dbus/fake_dbus_thread_manager.h +++ b/chromeos/dbus/fake_dbus_thread_manager.h @@ -6,7 +6,6 @@ #define CHROMEOS_DBUS_FAKE_DBUS_THREAD_MANAGER_H_ #include "base/logging.h" -#include "base/observer_list.h" #include "chromeos/chromeos_export.h" #include "chromeos/dbus/dbus_thread_manager.h" @@ -17,8 +16,6 @@ class ObjectPath; namespace chromeos { -class DBusThreadManagerObserver; - // This class provides a fake implementation of DBusThreadManager, which // hosts fake D-Bus clients. class CHROMEOS_EXPORT FakeDBusThreadManager : public DBusThreadManager { @@ -78,8 +75,6 @@ class CHROMEOS_EXPORT FakeDBusThreadManager : public DBusThreadManager { void SetSystemClockClient(scoped_ptr client); void SetUpdateEngineClient(scoped_ptr client); - virtual void AddObserver(DBusThreadManagerObserver* observer) OVERRIDE; - virtual void RemoveObserver(DBusThreadManagerObserver* observer) OVERRIDE; virtual dbus::Bus* GetSystemBus() OVERRIDE; virtual BluetoothAdapterClient* GetBluetoothAdapterClient() OVERRIDE; @@ -124,10 +119,6 @@ class CHROMEOS_EXPORT FakeDBusThreadManager : public DBusThreadManager { virtual UpdateEngineClient* GetUpdateEngineClient() OVERRIDE; private: - // Note: Keep this before other members so they can call AddObserver() in - // their c'tors. - ObserverList observers_; - scoped_ptr bluetooth_adapter_client_; scoped_ptr bluetooth_agent_manager_client_; scoped_ptr bluetooth_device_client_; diff --git a/chromeos/dbus/fake_session_manager_client.cc b/chromeos/dbus/fake_session_manager_client.cc index c5fe549335d4a..b3aabae179f9e 100644 --- a/chromeos/dbus/fake_session_manager_client.cc +++ b/chromeos/dbus/fake_session_manager_client.cc @@ -13,7 +13,8 @@ namespace chromeos { FakeSessionManagerClient::FakeSessionManagerClient() - : start_device_wipe_call_count_(0), + : first_boot_(false), + start_device_wipe_call_count_(0), notify_lock_screen_shown_call_count_(0), notify_lock_screen_dismissed_call_count_(0) { } @@ -135,7 +136,7 @@ void FakeSessionManagerClient::SetFlagsForUser( void FakeSessionManagerClient::GetServerBackedStateKeys( const StateKeysCallback& callback) { base::MessageLoop::current()->PostTask( - FROM_HERE, base::Bind(callback, server_backed_state_keys_)); + FROM_HERE, base::Bind(callback, server_backed_state_keys_, first_boot_)); } const std::string& FakeSessionManagerClient::device_policy() const { diff --git a/chromeos/dbus/fake_session_manager_client.h b/chromeos/dbus/fake_session_manager_client.h index 2f4bb9a0955fb..f408b510810bf 100644 --- a/chromeos/dbus/fake_session_manager_client.h +++ b/chromeos/dbus/fake_session_manager_client.h @@ -85,6 +85,8 @@ class FakeSessionManagerClient : public SessionManagerClient { server_backed_state_keys_ = state_keys; } + void set_first_boot(bool first_boot) { first_boot_ = first_boot; } + int start_device_wipe_call_count() const { return start_device_wipe_call_count_; } @@ -106,6 +108,7 @@ class FakeSessionManagerClient : public SessionManagerClient { ObserverList observers_; SessionManagerClient::ActiveSessionsMap user_sessions_; std::vector server_backed_state_keys_; + bool first_boot_; int start_device_wipe_call_count_; int notify_lock_screen_shown_call_count_; diff --git a/chromeos/dbus/fake_shill_manager_client.cc b/chromeos/dbus/fake_shill_manager_client.cc index d21419f4e69df..f524c1f7c0f62 100644 --- a/chromeos/dbus/fake_shill_manager_client.cc +++ b/chromeos/dbus/fake_shill_manager_client.cc @@ -497,6 +497,7 @@ void FakeShillManagerClient::ServiceStateChanged( void FakeShillManagerClient::SortManagerServices(bool notify) { DVLOG(1) << "SortManagerServices"; static const char* ordered_types[] = {shill::kTypeEthernet, + shill::kTypeEthernetEap, shill::kTypeWifi, shill::kTypeCellular, shill::kTypeWimax, @@ -876,6 +877,8 @@ base::ListValue* FakeShillManagerClient::GetListProperty( bool FakeShillManagerClient::TechnologyEnabled(const std::string& type) const { if (type == shill::kTypeVPN) return true; // VPN is always "enabled" since there is no associated device + if (type == shill::kTypeEthernetEap) + return true; bool enabled = false; const base::ListValue* technologies; if (stub_properties_.GetListWithoutPathExpansion( diff --git a/chromeos/dbus/permission_broker_client.h b/chromeos/dbus/permission_broker_client.h index 17e215a41ac8b..9ee953a4cea35 100644 --- a/chromeos/dbus/permission_broker_client.h +++ b/chromeos/dbus/permission_broker_client.h @@ -24,8 +24,8 @@ namespace chromeos { class CHROMEOS_EXPORT PermissionBrokerClient : public DBusClient { public: // The ResultCallback is used for both the RequestPathAccess and - // RequestUsbAcess methods. Its boolean parameter represents the result of the - // operation that it was submitted alongside. + // RequestUsbAccess methods. Its boolean parameter represents the result of + // the operation that it was submitted alongside. typedef base::Callback ResultCallback; virtual ~PermissionBrokerClient(); diff --git a/chromeos/dbus/session_manager_client.cc b/chromeos/dbus/session_manager_client.cc index b3ddad7b872d8..56dc850811116 100644 --- a/chromeos/dbus/session_manager_client.cc +++ b/chromeos/dbus/session_manager_client.cc @@ -525,6 +525,7 @@ class SessionManagerClientImpl : public SessionManagerClient { void OnGetServerBackedStateKeys(const StateKeysCallback& callback, dbus::Response* response) { std::vector state_keys; + bool first_run = false; if (!response) { LOG(ERROR) << "Failed to call " << login_manager::kSessionManagerStartSession; @@ -547,10 +548,14 @@ class SessionManagerClientImpl : public SessionManagerClient { std::string(reinterpret_cast(data), size)); } } + if (!reader.PopBool(&first_run)) { + // TODO(tnagel): After 2014-11-19 turn this warning into an error. + LOG(WARNING) << "Chrome OS is too old. Defaulting to first_run=false."; + } } if (!callback.is_null()) - callback.Run(state_keys); + callback.Run(state_keys, first_run); } @@ -713,7 +718,7 @@ class SessionManagerClientStubImpl : public SessionManagerClient { state_keys.push_back(crypto::SHA256HashString(base::IntToString(i))); if (!callback.is_null()) - callback.Run(state_keys); + callback.Run(state_keys, false); } private: diff --git a/chromeos/dbus/session_manager_client.h b/chromeos/dbus/session_manager_client.h index d4c07066e2deb..b007bee96c1cb 100644 --- a/chromeos/dbus/session_manager_client.h +++ b/chromeos/dbus/session_manager_client.h @@ -168,13 +168,16 @@ class CHROMEOS_EXPORT SessionManagerClient : public DBusClient { virtual void SetFlagsForUser(const std::string& username, const std::vector& flags) = 0; - typedef base::Callback& state_keys)> - StateKeysCallback; + typedef base::Callback& state_keys, + bool first_boot)> StateKeysCallback; // Get the currently valid server-backed state keys for the device. // Server-backed state keys are opaque, device-unique, time-dependent, // client-determined identifiers that are used for keying state in the cloud - // for the device to retrieve after a device factory reset. + // for the device to retrieve after a device factory reset. The |first_boot| + // parameter indicates if this is the very first boot of the device after + // being assembled (even a "factory reset" will not trigger this again) in + // which case doing the enrollment check makes no sense. // // The state keys are returned asynchronously via |callback|. The callback // will be invoked with an empty state key vector in case of errors. diff --git a/chromeos/ime/component_extension_ime_manager.cc b/chromeos/ime/component_extension_ime_manager.cc index f77421918c29b..74a2e09cf4c1d 100644 --- a/chromeos/ime/component_extension_ime_manager.cc +++ b/chromeos/ime/component_extension_ime_manager.cc @@ -87,13 +87,27 @@ ComponentExtensionIMEManager::~ComponentExtensionIMEManager() { void ComponentExtensionIMEManager::Initialize( scoped_ptr delegate) { delegate_ = delegate.Pass(); - component_extension_imes_ = delegate_->ListIME(); + std::vector ext_list = delegate_->ListIME(); + for (size_t i = 0; i < ext_list.size(); ++i) { + ComponentExtensionIME& ext = ext_list[i]; + bool extension_exists = IsWhitelistedExtension(ext.id); + if (!extension_exists) + component_extension_imes_[ext.id] = ext; + for (size_t j = 0; j < ext.engines.size(); ++j) { + ComponentExtensionEngine& ime = ext.engines[j]; + const std::string input_method_id = + extension_ime_util::GetComponentInputMethodID(ext.id, ime.engine_id); + if (extension_exists && !IsWhitelisted(input_method_id)) + component_extension_imes_[ext.id].engines.push_back(ime); + input_method_id_set_.insert(input_method_id); + } + } } bool ComponentExtensionIMEManager::LoadComponentExtensionIME( const std::string& input_method_id) { ComponentExtensionIME ime; - if (FindEngineEntry(input_method_id, &ime, NULL)) + if (FindEngineEntry(input_method_id, &ime)) return delegate_->Load(ime.id, ime.manifest, ime.path); else return false; @@ -102,7 +116,7 @@ bool ComponentExtensionIMEManager::LoadComponentExtensionIME( bool ComponentExtensionIMEManager::UnloadComponentExtensionIME( const std::string& input_method_id) { ComponentExtensionIME ime; - if (!FindEngineEntry(input_method_id, &ime, NULL)) + if (!FindEngineEntry(input_method_id, &ime)) return false; delegate_->Unload(ime.id, ime.path); return true; @@ -110,87 +124,41 @@ bool ComponentExtensionIMEManager::UnloadComponentExtensionIME( bool ComponentExtensionIMEManager::IsWhitelisted( const std::string& input_method_id) { - return extension_ime_util::IsComponentExtensionIME(input_method_id) && - FindEngineEntry(input_method_id, NULL, NULL); + return input_method_id_set_.find(input_method_id) != + input_method_id_set_.end(); } bool ComponentExtensionIMEManager::IsWhitelistedExtension( const std::string& extension_id) { - for (size_t i = 0; i < component_extension_imes_.size(); ++i) { - if (component_extension_imes_[i].id == extension_id) - return true; - } - return false; -} - -std::string ComponentExtensionIMEManager::GetId( - const std::string& extension_id, - const std::string& engine_id) { - ComponentExtensionEngine engine; - const std::string& input_method_id = - extension_ime_util::GetComponentInputMethodID(extension_id, engine_id); - if (!FindEngineEntry(input_method_id, NULL, &engine)) - return ""; - return input_method_id; -} - -std::string ComponentExtensionIMEManager::GetName( - const std::string& input_method_id) { - ComponentExtensionEngine engine; - if (!FindEngineEntry(input_method_id, NULL, &engine)) - return ""; - return engine.display_name; -} - -std::string ComponentExtensionIMEManager::GetDescription( - const std::string& input_method_id) { - ComponentExtensionEngine engine; - if (!FindEngineEntry(input_method_id, NULL, &engine)) - return ""; - return engine.description; -} - -std::vector ComponentExtensionIMEManager::ListIMEByLanguage( - const std::string& language) { - std::vector result; - for (size_t i = 0; i < component_extension_imes_.size(); ++i) { - for (size_t j = 0; j < component_extension_imes_[i].engines.size(); ++j) { - const ComponentExtensionIME& ime = component_extension_imes_[i]; - if (std::find(ime.engines[j].language_codes.begin(), - ime.engines[j].language_codes.end(), - language) != ime.engines[j].language_codes.end()) { - result.push_back(extension_ime_util::GetComponentInputMethodID( - ime.id, - ime.engines[j].engine_id)); - } - } - } - return result; + return component_extension_imes_.find(extension_id) != + component_extension_imes_.end(); } input_method::InputMethodDescriptors ComponentExtensionIMEManager::GetAllIMEAsInputMethodDescriptor() { input_method::InputMethodDescriptors result; - for (size_t i = 0; i < component_extension_imes_.size(); ++i) { - for (size_t j = 0; j < component_extension_imes_[i].engines.size(); ++j) { + for (std::map::const_iterator it = + component_extension_imes_.begin(); + it != component_extension_imes_.end(); ++it) { + const ComponentExtensionIME& ext = it->second; + for (size_t j = 0; j < ext.engines.size(); ++j) { + const ComponentExtensionEngine& ime = ext.engines[j]; const std::string input_method_id = extension_ime_util::GetComponentInputMethodID( - component_extension_imes_[i].id, - component_extension_imes_[i].engines[j].engine_id); - const std::vector& layouts = - component_extension_imes_[i].engines[j].layouts; + ext.id, ime.engine_id); + const std::vector& layouts = ime.layouts; result.push_back( input_method::InputMethodDescriptor( input_method_id, - component_extension_imes_[i].engines[j].display_name, + ime.display_name, std::string(), // TODO(uekawa): Set short name. layouts, - component_extension_imes_[i].engines[j].language_codes, + ime.language_codes, // Enables extension based xkb keyboards on login screen. extension_ime_util::IsKeyboardLayoutExtension( input_method_id) && IsInLoginLayoutWhitelist(layouts), - component_extension_imes_[i].engines[j].options_page_url, - component_extension_imes_[i].engines[j].input_view_url)); + ime.options_page_url, + ime.input_view_url)); } } return result; @@ -210,30 +178,20 @@ ComponentExtensionIMEManager::GetXkbIMEAsInputMethodDescriptor() { bool ComponentExtensionIMEManager::FindEngineEntry( const std::string& input_method_id, - ComponentExtensionIME* out_extension, - ComponentExtensionEngine* out_engine) { - if (!extension_ime_util::IsComponentExtensionIME(input_method_id)) + ComponentExtensionIME* out_extension) { + if (!IsWhitelisted(input_method_id)) return false; - for (size_t i = 0; i < component_extension_imes_.size(); ++i) { - const std::string extension_id = component_extension_imes_[i].id; - const std::vector& engines = - component_extension_imes_[i].engines; - for (size_t j = 0; j < engines.size(); ++j) { - const std::string trial_ime_id = - extension_ime_util::GetComponentInputMethodID( - extension_id, engines[j].engine_id); - if (trial_ime_id != input_method_id) - continue; - - if (out_extension) - *out_extension = component_extension_imes_[i]; - if (out_engine) - *out_engine = component_extension_imes_[i].engines[j]; - return true; - } - } - return false; + std::string extension_id = + extension_ime_util::GetExtensionIDFromInputMethodID(input_method_id); + std::map::iterator it = + component_extension_imes_.find(extension_id); + if (it == component_extension_imes_.end()) + return false; + + if (out_extension) + *out_extension = it->second; + return true; } bool ComponentExtensionIMEManager::IsInLoginLayoutWhitelist( diff --git a/chromeos/ime/component_extension_ime_manager.h b/chromeos/ime/component_extension_ime_manager.h index c455712d8f32a..ee00fb2ce2a5e 100644 --- a/chromeos/ime/component_extension_ime_manager.h +++ b/chromeos/ime/component_extension_ime_manager.h @@ -5,6 +5,7 @@ #ifndef CHROMEOS_IME_COMPONENT_EXTENSION_IME_MANAGER_H_ #define CHROMEOS_IME_COMPONENT_EXTENSION_IME_MANAGER_H_ +#include #include #include "base/files/file_path.h" @@ -88,20 +89,6 @@ class CHROMEOS_EXPORT ComponentExtensionIMEManager { // Returns true if |extension_id| is whitelisted component extension. bool IsWhitelistedExtension(const std::string& extension_id); - // Returns InputMethodId. This function returns empty string if |extension_id| - // and |engine_id| is not a whitelisted component extention IME. - std::string GetId(const std::string& extension_id, - const std::string& engine_id); - - // Returns localized name of |input_method_id|. - std::string GetName(const std::string& input_method_id); - - // Returns localized description of |input_method_id|. - std::string GetDescription(const std::string& input_method_id); - - // Returns list of input method id associated with |language|. - std::vector ListIMEByLanguage(const std::string& language); - // Returns all IME as InputMethodDescriptors. input_method::InputMethodDescriptors GetAllIMEAsInputMethodDescriptor(); @@ -113,14 +100,19 @@ class CHROMEOS_EXPORT ComponentExtensionIMEManager { // |input_method_id|. This function retruns true if it is found, otherwise // returns false. |out_extension| and |out_engine| can be NULL. bool FindEngineEntry(const std::string& input_method_id, - ComponentExtensionIME* out_extension, - ComponentExtensionEngine* out_engine); + ComponentExtensionIME* out_extension); bool IsInLoginLayoutWhitelist(const std::vector& layouts); scoped_ptr delegate_; - std::vector component_extension_imes_; + // The map of extension_id to ComponentExtensionIME instance. + // It's filled by Initialize() method and never changed during runtime. + std::map component_extension_imes_; + + // For quick check the validity of a given input method id. + // It's filled by Initialize() method and never changed during runtime. + std::set input_method_id_set_; std::set login_layout_set_; diff --git a/chromeos/ime/component_extension_ime_manager_unittest.cc b/chromeos/ime/component_extension_ime_manager_unittest.cc index 7822026efb3d3..4da6fa32d8d53 100644 --- a/chromeos/ime/component_extension_ime_manager_unittest.cc +++ b/chromeos/ime/component_extension_ime_manager_unittest.cc @@ -21,7 +21,7 @@ class ComponentExtensionIMEManagerTest : public testing::Test { ime_list_.clear(); ComponentExtensionIME ext1; - ext1.id = "ext1_id"; + ext1.id = "ext1_id_xxxxxxxxxxxxxxxxxxxxxxxx"; ext1.description = "ext1_description"; ext1.options_page_url = GURL("chrome-extension://" + ext1.id + "/options.html"); @@ -51,7 +51,7 @@ class ComponentExtensionIMEManagerTest : public testing::Test { ime_list_.push_back(ext1); ComponentExtensionIME ext2; - ext2.id = "ext2_id"; + ext2.id = "ext2_id_xxxxxxxxxxxxxxxxxxxxxxxx"; ext2.description = "ext2_description"; ext2.path = base::FilePath("ext2_file_path"); @@ -79,7 +79,7 @@ class ComponentExtensionIMEManagerTest : public testing::Test { ime_list_.push_back(ext2); ComponentExtensionIME ext3; - ext3.id = "ext3_id"; + ext3.id = "ext3_id_xxxxxxxxxxxxxxxxxxxxxxxx"; ext3.description = "ext3_description"; ext3.options_page_url = GURL("chrome-extension://" + ext3.id + "/options.html"); @@ -181,48 +181,6 @@ TEST_F(ComponentExtensionIMEManagerTest, IsWhitelistedExtensionTest) { EXPECT_FALSE(component_ext_mgr_->IsWhitelistedExtension("")); } -TEST_F(ComponentExtensionIMEManagerTest, GetNameDescriptionTest) { - for (size_t i = 0; i < ime_list_.size(); ++i) { - for (size_t j = 0; j < ime_list_[i].engines.size(); ++j) { - const ComponentExtensionEngine& engine - = ime_list_[i].engines[j]; - - const std::string input_method_id = - extension_ime_util::GetComponentInputMethodID( - ime_list_[i].id, - engine.engine_id); - - EXPECT_EQ(input_method_id, - component_ext_mgr_->GetId(ime_list_[i].id, engine.engine_id)); - EXPECT_EQ(engine.display_name, - component_ext_mgr_->GetName(input_method_id)); - EXPECT_EQ(engine.description, - component_ext_mgr_->GetDescription(input_method_id)); - } - } -} - -TEST_F(ComponentExtensionIMEManagerTest, ListIMEByLanguageTest) { - const std::string hindi_layout1 = - extension_ime_util::GetComponentInputMethodID( - ime_list_[1].id, ime_list_[1].engines[1].engine_id); - const std::string hindi_layout2 = - extension_ime_util::GetComponentInputMethodID( - ime_list_[2].id, ime_list_[2].engines[0].engine_id); - - std::vector hindi_list - = component_ext_mgr_->ListIMEByLanguage("hi"); - ASSERT_EQ(2UL, hindi_list.size()); - EXPECT_TRUE(hindi_list[0] == hindi_layout1 || hindi_list[0] == hindi_layout2); - EXPECT_TRUE(hindi_list[1] == hindi_layout1 || hindi_list[1] == hindi_layout2); - - EXPECT_EQ(0UL, component_ext_mgr_->ListIMEByLanguage("ru").size()); - EXPECT_EQ(0UL, component_ext_mgr_->ListIMEByLanguage("").size()); - EXPECT_EQ(0UL, component_ext_mgr_->ListIMEByLanguage("invalid").size()); - EXPECT_EQ(5UL, component_ext_mgr_->ListIMEByLanguage("en").size()); - EXPECT_EQ(2UL, component_ext_mgr_->ListIMEByLanguage("ja").size()); -} - TEST_F(ComponentExtensionIMEManagerTest, GetAllIMEAsInputMethodDescriptor) { input_method::InputMethodDescriptors descriptors = component_ext_mgr_->GetAllIMEAsInputMethodDescriptor(); diff --git a/chromeos/ime/extension_ime_util.cc b/chromeos/ime/extension_ime_util.cc index 322635f5e478c..ee37cae714aad 100644 --- a/chromeos/ime/extension_ime_util.cc +++ b/chromeos/ime/extension_ime_util.cc @@ -39,15 +39,11 @@ std::string GetComponentInputMethodID(const std::string& extension_id, std::string GetExtensionIDFromInputMethodID( const std::string& input_method_id) { - if (IsExtensionIME(input_method_id) && - input_method_id.size() >= kExtensionIMEPrefixLength + - kExtensionIdLength) { + if (IsExtensionIME(input_method_id)) { return input_method_id.substr(kExtensionIMEPrefixLength, kExtensionIdLength); } - if (IsComponentExtensionIME(input_method_id) && - input_method_id.size() >= kComponentExtensionIMEPrefixLength + - kExtensionIdLength) { + if (IsComponentExtensionIME(input_method_id)) { return input_method_id.substr(kComponentExtensionIMEPrefixLength, kExtensionIdLength); } @@ -99,20 +95,24 @@ std::string GetInputMethodIDByEngineID(const std::string& engine_id) { bool IsExtensionIME(const std::string& input_method_id) { return StartsWithASCII(input_method_id, kExtensionIMEPrefix, - true); // Case sensitive. + true /* Case sensitive */) && + input_method_id.size() > kExtensionIMEPrefixLength + + kExtensionIdLength; } bool IsComponentExtensionIME(const std::string& input_method_id) { return StartsWithASCII(input_method_id, kComponentExtensionIMEPrefix, - true); // Case sensitive. + true /* Case sensitive */) && + input_method_id.size() > kComponentExtensionIMEPrefixLength + + kExtensionIdLength; } bool IsMemberOfExtension(const std::string& input_method_id, const std::string& extension_id) { return StartsWithASCII(input_method_id, kExtensionIMEPrefix + extension_id, - true); // Case sensitive. + true /* Case sensitive */); } bool IsKeyboardLayoutExtension(const std::string& input_method_id) { diff --git a/chromeos/ime/extension_ime_util_unittest.cc b/chromeos/ime/extension_ime_util_unittest.cc index e499e8b73c222..bc86f17baeb6f 100644 --- a/chromeos/ime/extension_ime_util_unittest.cc +++ b/chromeos/ime/extension_ime_util_unittest.cc @@ -37,18 +37,22 @@ TEST(ExtensionIMEUtilTest, GetExtensionIDFromInputMethodIDTest) { TEST(ExtensionIMEUtilTest, IsExtensionIMETest) { EXPECT_TRUE(extension_ime_util::IsExtensionIME( - extension_ime_util::GetInputMethodID("abcde", "12345"))); + extension_ime_util::GetInputMethodID( + "abcde_xxxxxxxxxxxxxxxxxxxxxxxxxx", "12345"))); EXPECT_FALSE(extension_ime_util::IsExtensionIME( - extension_ime_util::GetComponentInputMethodID("abcde", "12345"))); + extension_ime_util::GetComponentInputMethodID( + "abcde_xxxxxxxxxxxxxxxxxxxxxxxxxx", "12345"))); EXPECT_FALSE(extension_ime_util::IsExtensionIME("")); EXPECT_FALSE(extension_ime_util::IsExtensionIME("mozc")); } TEST(ExtensionIMEUtilTest, IsComponentExtensionIMETest) { EXPECT_TRUE(extension_ime_util::IsComponentExtensionIME( - extension_ime_util::GetComponentInputMethodID("abcde", "12345"))); + extension_ime_util::GetComponentInputMethodID( + "abcde_xxxxxxxxxxxxxxxxxxxxxxxxxx", "12345"))); EXPECT_FALSE(extension_ime_util::IsComponentExtensionIME( - extension_ime_util::GetInputMethodID("abcde", "12345"))); + extension_ime_util::GetInputMethodID( + "abcde_xxxxxxxxxxxxxxxxxxxxxxxxxx", "12345"))); EXPECT_FALSE(extension_ime_util::IsComponentExtensionIME("")); EXPECT_FALSE(extension_ime_util::IsComponentExtensionIME("mozc")); } diff --git a/chromeos/ime/input_method_manager.h b/chromeos/ime/input_method_manager.h index 1ef694e7018a5..fefa0e1897214 100644 --- a/chromeos/ime/input_method_manager.h +++ b/chromeos/ime/input_method_manager.h @@ -76,8 +76,8 @@ class CHROMEOS_EXPORT InputMethodManager { // Destroy the global instance. static CHROMEOS_EXPORT void Shutdown(); - // Initialize component extensions. - virtual void InitializeComponentExtension() = 0; + // Get the current UI session state (e.g. login screen, lock screen, etc.). + virtual State GetState() = 0; // Adds an observer to receive notifications of input method related // changes as desribed in the Observer class above. diff --git a/chromeos/login/auth/extended_authenticator.cc b/chromeos/login/auth/extended_authenticator.cc index 549888ddef4c8..7a99d72dbc9d3 100644 --- a/chromeos/login/auth/extended_authenticator.cc +++ b/chromeos/login/auth/extended_authenticator.cc @@ -336,6 +336,8 @@ void ExtendedAuthenticator::OnOperationComplete( return; } + LOG(ERROR) << "Supervised user cryptohome error, code: " << return_code; + AuthState state = FAILED_MOUNT; if (return_code == cryptohome::MOUNT_ERROR_TPM_COMM_ERROR || diff --git a/chromeos/login/login_state.cc b/chromeos/login/login_state.cc index eba009a91d29d..b65b0fdf548f5 100644 --- a/chromeos/login/login_state.cc +++ b/chromeos/login/login_state.cc @@ -67,8 +67,8 @@ void LoginState::SetLoggedInStateAndPrimaryUser( SetLoggedInState(state, type); } -void LoginState::SetLoggedInState(LoggedInState state, - LoggedInUserType type) { +void LoginState::SetLoggedInState(LoggedInState state, LoggedInUserType type) { + CHECK_NE(LOGGED_IN_USER_RETAIL_MODE, type); if (state == logged_in_state_ && type == logged_in_user_type_) return; VLOG(1) << "LoggedInState: " << state << " UserType: " << type; @@ -92,34 +92,22 @@ bool LoginState::IsInSafeMode() const { return logged_in_state_ == LOGGED_IN_SAFE_MODE; } -bool LoginState::IsGuestUser() const { - if (!IsUserLoggedIn()) - return false; - switch (logged_in_user_type_) { - case LOGGED_IN_USER_NONE: - case LOGGED_IN_USER_REGULAR: - case LOGGED_IN_USER_OWNER: - case LOGGED_IN_USER_SUPERVISED: - case LOGGED_IN_USER_KIOSK_APP: - return false; - case LOGGED_IN_USER_GUEST: - case LOGGED_IN_USER_RETAIL_MODE: - case LOGGED_IN_USER_PUBLIC_ACCOUNT: - return true; - } - NOTREACHED(); - return false; +bool LoginState::IsGuestSessionUser() const { + return logged_in_user_type_ == LOGGED_IN_USER_GUEST; +} + +bool LoginState::IsPublicSessionUser() const { + return logged_in_user_type_ == LOGGED_IN_USER_PUBLIC_ACCOUNT; } bool LoginState::IsKioskApp() const { - return logged_in_user_type_ == LoginState::LOGGED_IN_USER_KIOSK_APP; + return logged_in_user_type_ == LOGGED_IN_USER_KIOSK_APP; } bool LoginState::UserHasNetworkProfile() const { if (!IsUserLoggedIn()) return false; - return logged_in_user_type_ != LOGGED_IN_USER_RETAIL_MODE && - logged_in_user_type_ != LOGGED_IN_USER_PUBLIC_ACCOUNT; + return logged_in_user_type_ != LOGGED_IN_USER_PUBLIC_ACCOUNT; } bool LoginState::IsUserAuthenticated() const { diff --git a/chromeos/login/login_state.h b/chromeos/login/login_state.h index 7ab76663ccbb6..1c30a3373fe88 100644 --- a/chromeos/login/login_state.h +++ b/chromeos/login/login_state.h @@ -26,7 +26,7 @@ class CHROMEOS_EXPORT LoginState { LOGGED_IN_USER_OWNER, // The owner of the device is logged in LOGGED_IN_USER_GUEST, // A guest is logged in (i.e. incognito) LOGGED_IN_USER_RETAIL_MODE, // Is in retail mode - LOGGED_IN_USER_PUBLIC_ACCOUNT, // A public account is logged in + LOGGED_IN_USER_PUBLIC_ACCOUNT, // A user is logged in to a public session. LOGGED_IN_USER_SUPERVISED, // A supervised user is logged in LOGGED_IN_USER_KIOSK_APP // Is in kiosk app mode }; @@ -72,8 +72,11 @@ class CHROMEOS_EXPORT LoginState { // logged in, and only the owner will be allowed to log in). bool IsInSafeMode() const; - // Returns true if logged in and is a guest, retail, or public user. - bool IsGuestUser() const; + // Returns true if logged in to a guest session. + bool IsGuestSessionUser() const; + + // Returns true if logged in to a public session. + bool IsPublicSessionUser() const; // Returns true if logged in as a kiosk app. bool IsKioskApp() const; @@ -81,11 +84,12 @@ class CHROMEOS_EXPORT LoginState { // Whether a network profile is created for the user. bool UserHasNetworkProfile() const; - // Returns true if the user is an authenticated user (i.e. non public account) + // Returns true if the user is an authenticated user (i.e. the user is not + // using an anonymous session like public or guest session) bool IsUserAuthenticated() const; // Returns true if the user is authenticated by logging into Google account - // (i.e., non public nor supervised account). + // (i.e. not using an anonymous nor supervised session). bool IsUserGaiaAuthenticated() const; void set_always_logged_in(bool always_logged_in) { diff --git a/chromeos/network/client_cert_util.cc b/chromeos/network/client_cert_util.cc index b831beaf53b99..8f00865e6b1a2 100644 --- a/chromeos/network/client_cert_util.cc +++ b/chromeos/network/client_cert_util.cc @@ -97,7 +97,9 @@ bool CertPrincipalMatches(const IssuerSubjectPattern& pattern, return true; } -std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) { +std::string GetPkcs11AndSlotIdFromEapCertId(const std::string& cert_id, + int* slot_id) { + *slot_id = -1; if (cert_id.empty()) return std::string(); @@ -110,9 +112,73 @@ std::string GetPkcs11IdFromEapCertId(const std::string& cert_id) { LOG(ERROR) << "Empty PKCS11 id in cert id."; return std::string(); } + int parsed_slot_id; + if (base::StringToInt(cert_id.substr(0, delimiter_pos), &parsed_slot_id)) + *slot_id = parsed_slot_id; + else + LOG(ERROR) << "Slot ID is not an integer. Cert ID is: " << cert_id << "."; return cert_id.substr(delimiter_pos + 1); } +void GetClientCertFromShillProperties( + const base::DictionaryValue& shill_properties, + ConfigType* cert_config_type, + int* tpm_slot, + std::string* pkcs11_id) { + *cert_config_type = CONFIG_TYPE_NONE; + *tpm_slot = -1; + pkcs11_id->clear(); + + // Look for VPN specific client certificate properties. + // + // VPN Provider values are read from the "Provider" dictionary, not the + // "Provider.Type", etc keys (which are used only to set the values). + const base::DictionaryValue* provider_properties = NULL; + if (shill_properties.GetDictionaryWithoutPathExpansion( + shill::kProviderProperty, &provider_properties)) { + // Look for OpenVPN specific properties. + if (provider_properties->GetStringWithoutPathExpansion( + shill::kOpenVPNClientCertIdProperty, pkcs11_id)) { + *cert_config_type = CONFIG_TYPE_OPENVPN; + return; + } + // Look for L2TP-IPsec specific properties. + if (provider_properties->GetStringWithoutPathExpansion( + shill::kL2tpIpsecClientCertIdProperty, pkcs11_id)) { + std::string cert_slot; + provider_properties->GetStringWithoutPathExpansion( + shill::kL2tpIpsecClientCertSlotProperty, &cert_slot); + if (!cert_slot.empty() && !base::StringToInt(cert_slot, tpm_slot)) { + LOG(ERROR) << "Cert slot is not an integer: " << cert_slot << "."; + return; + } + + *cert_config_type = CONFIG_TYPE_IPSEC; + } + return; + } + + // Look for EAP specific client certificate properties, which can either be + // part of a WiFi or EthernetEAP configuration. + std::string cert_id; + if (shill_properties.GetStringWithoutPathExpansion(shill::kEapCertIdProperty, + &cert_id)) { + // Shill requires both CertID and KeyID for TLS connections, despite the + // fact that by convention they are the same ID, because one identifies + // the certificate and the other the private key. + std::string key_id; + shill_properties.GetStringWithoutPathExpansion(shill::kEapKeyIdProperty, + &key_id); + // Assume the configuration to be invalid, if the two IDs are not identical. + if (cert_id != key_id) { + LOG(ERROR) << "EAP CertID differs from KeyID"; + return; + } + *pkcs11_id = GetPkcs11AndSlotIdFromEapCertId(cert_id, tpm_slot); + *cert_config_type = CONFIG_TYPE_EAP; + } +} + void SetShillProperties(const ConfigType cert_config_type, const int tpm_slot, const std::string& pkcs11_id, diff --git a/chromeos/network/client_cert_util.h b/chromeos/network/client_cert_util.h index 68f07a711732f..c1fd42bb599cf 100644 --- a/chromeos/network/client_cert_util.h +++ b/chromeos/network/client_cert_util.h @@ -55,10 +55,26 @@ struct CHROMEOS_EXPORT ClientCertConfig { bool CertPrincipalMatches(const IssuerSubjectPattern& pattern, const net::CertPrincipal& principal); -// Returns the PKCS11 id part of |cert_id|, which is expected to be the value of -// the Shill property kEapCertIdProperty or kEapKeyIdProperty. -CHROMEOS_EXPORT std::string GetPkcs11IdFromEapCertId( - const std::string& cert_id); +// Returns the PKCS11 and slot ID of |cert_id|, which is expected to be a +// value of the Shill property kEapCertIdProperty or kEapKeyIdProperty, either +// of format "" or ":". +CHROMEOS_EXPORT std::string GetPkcs11AndSlotIdFromEapCertId( + const std::string& cert_id, + int* slot_id); + +// Reads the client certificate configuration from the Shill Service properties +// |shill_properties|. +// If such a configuration is found, the values |cert_config_type|, |tpm_slot| +// and |pkcs11_id| are filled accordingly. In case of OpenVPN or because the +// property was not set, |tpm_slot| will be set to -1. +// If an error occurred or no client configuration is found, |cert_config_type| +// will be set to CONFIG_TYPE_NONE, |tpm_slot| to -1 and |pkcs11_id| to the +// empty string. +CHROMEOS_EXPORT void GetClientCertFromShillProperties( + const base::DictionaryValue& shill_properties, + ConfigType* cert_config_type, + int* tpm_slot, + std::string* pkcs11_id); // Sets the properties of a client cert and the TPM slot that it's contained in. // |cert_config_type| determines which dictionary entries to set. diff --git a/chromeos/network/network_cert_migrator.cc b/chromeos/network/network_cert_migrator.cc index 29a34102af96e..31f476aef6f85 100644 --- a/chromeos/network/network_cert_migrator.cc +++ b/chromeos/network/network_cert_migrator.cc @@ -12,6 +12,7 @@ #include "base/metrics/histogram.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/shill_service_client.h" +#include "chromeos/network/client_cert_util.h" #include "chromeos/network/network_handler_callbacks.h" #include "chromeos/network/network_state.h" #include "chromeos/network/network_state_handler.h" @@ -44,12 +45,23 @@ std::string GetNickname(const net::X509Certificate& cert) { } // namespace -// Checks which of the given |networks| has one of the deprecated -// CaCertNssProperties set. If such a network already has a CaCertPEM property, -// then the NssProperty is cleared. Otherwise, the NssProperty is compared with -// the nickname of each certificate of |certs|. If a match is found, then the -// CaCertPemProperty is set and the NssProperty is cleared. Otherwise, the -// network is not modified. +// Migrates each network of |networks| with a deprecated CaCertNss property to +// the respective CaCertPEM property and fixes an invalid or missing slot ID of +// a client certificate configuration. +// +// If a network already has a CaCertPEM property, then the NssProperty is +// cleared. Otherwise, the NssProperty is compared with +// the nickname of each certificate of |certs|. If a match is found, the +// CaCertPemProperty is set and the NssProperty is cleared. +// +// If a network with a client certificate configuration (i.e. a PKCS11 ID) is +// found, the configured client certificate is looked up. +// If the certificate is found, the currently configured slot ID (if any) is +// compared with the actual slot ID of the certificate and if required updated. +// If the certificate is not found, the client certificate configuration is +// removed. +// +// Only if necessary, a network will be notified. class NetworkCertMigrator::MigrationTask : public base::RefCounted { public: @@ -61,11 +73,15 @@ class NetworkCertMigrator::MigrationTask void Run(const NetworkStateHandler::NetworkStateList& networks) { // Request properties for each network that has a CaCertNssProperty set - // according to the NetworkStateHandler. + // or which could be configured with a client certificate. for (NetworkStateHandler::NetworkStateList::const_iterator it = networks.begin(); it != networks.end(); ++it) { - if (!(*it)->HasCACertNSS()) + if (!(*it)->HasCACertNSS() && + (*it)->security() != shill::kSecurity8021x && + (*it)->type() != shill::kTypeVPN && + (*it)->type() != shill::kTypeEthernetEap) { continue; + } const std::string& service_path = (*it)->path(); DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( dbus::ObjectPath(service_path), @@ -83,6 +99,59 @@ class NetworkCertMigrator::MigrationTask return; } + base::DictionaryValue new_properties; + MigrateClientCertProperties(service_path, properties, &new_properties); + MigrateNssProperties(service_path, properties, &new_properties); + + if (new_properties.empty()) + return; + SendPropertiesToShill(service_path, new_properties); + } + + void MigrateClientCertProperties(const std::string& service_path, + const base::DictionaryValue& properties, + base::DictionaryValue* new_properties) { + int configured_slot_id = -1; + std::string pkcs11_id; + chromeos::client_cert::ConfigType config_type = + chromeos::client_cert::CONFIG_TYPE_NONE; + chromeos::client_cert::GetClientCertFromShillProperties( + properties, &config_type, &configured_slot_id, &pkcs11_id); + if (config_type == chromeos::client_cert::CONFIG_TYPE_NONE || + pkcs11_id.empty()) { + return; + } + + // OpenVPN configuration doesn't have a slot id to migrate. + if (config_type == chromeos::client_cert::CONFIG_TYPE_OPENVPN) + return; + + int real_slot_id = -1; + scoped_refptr cert = + FindCertificateWithPkcs11Id(pkcs11_id, &real_slot_id); + if (!cert) { + LOG(WARNING) << "No matching cert found, removing the certificate " + "configuration from network " << service_path; + chromeos::client_cert::SetEmptyShillProperties(config_type, + new_properties); + return; + } + if (real_slot_id == -1) { + LOG(WARNING) << "Found a certificate without slot id."; + return; + } + + if (cert && real_slot_id != configured_slot_id) { + VLOG(1) << "Network " << service_path + << " is configured with no or an incorrect slot id."; + chromeos::client_cert::SetShillProperties( + config_type, real_slot_id, pkcs11_id, new_properties); + } + } + + void MigrateNssProperties(const std::string& service_path, + const base::DictionaryValue& properties, + base::DictionaryValue* new_properties) { std::string nss_key, pem_key, nickname; const base::ListValue* pem_property = NULL; UMANetworkType uma_type = UMA_NETWORK_TYPE_SIZE; @@ -99,7 +168,7 @@ class NetworkCertMigrator::MigrationTask if (pem_property && !pem_property->empty()) { VLOG(2) << "PEM already exists, clearing NSS property."; - ClearNssProperty(service_path, nss_key); + ClearNssProperty(nss_key, new_properties); return; } @@ -117,7 +186,8 @@ class NetworkCertMigrator::MigrationTask return; } - SetNssAndPemProperties(service_path, nss_key, pem_key, pem_encoded); + ClearNssProperty(nss_key, new_properties); + SetPemProperty(pem_key, pem_encoded, new_properties); } void GetNssAndPemProperties(const base::DictionaryValue& shill_properties, @@ -159,18 +229,25 @@ class NetworkCertMigrator::MigrationTask } } - void ClearNssProperty(const std::string& service_path, - const std::string& nss_key) { - DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( - dbus::ObjectPath(service_path), - nss_key, - base::StringValue(std::string()), - base::Bind( - &MigrationTask::NotifyNetworkStateHandler, this, service_path), - base::Bind(&network_handler::ShillErrorCallbackFunction, - "MigrationTask.SetProperty failed", - service_path, - network_handler::ErrorCallback())); + void ClearNssProperty(const std::string& nss_key, + base::DictionaryValue* new_properties) { + new_properties->SetStringWithoutPathExpansion(nss_key, std::string()); + } + + scoped_refptr FindCertificateWithPkcs11Id( + const std::string& pkcs11_id, int* slot_id) { + *slot_id = -1; + for (net::CertificateList::iterator it = certs_.begin(); it != certs_.end(); + ++it) { + int current_slot_id = -1; + std::string current_pkcs11_id = + CertLoader::GetPkcs11IdAndSlotForCert(**it, ¤t_slot_id); + if (current_pkcs11_id == pkcs11_id) { + *slot_id = current_slot_id; + return *it; + } + } + return NULL; } scoped_refptr FindCertificateWithNickname( @@ -183,19 +260,19 @@ class NetworkCertMigrator::MigrationTask return NULL; } - void SetNssAndPemProperties(const std::string& service_path, - const std::string& nss_key, - const std::string& pem_key, - const std::string& pem_encoded_cert) { - base::DictionaryValue new_properties; - new_properties.SetStringWithoutPathExpansion(nss_key, std::string()); + void SetPemProperty(const std::string& pem_key, + const std::string& pem_encoded_cert, + base::DictionaryValue* new_properties) { scoped_ptr ca_cert_pems(new base::ListValue); ca_cert_pems->AppendString(pem_encoded_cert); - new_properties.SetWithoutPathExpansion(pem_key, ca_cert_pems.release()); + new_properties->SetWithoutPathExpansion(pem_key, ca_cert_pems.release()); + } + void SendPropertiesToShill(const std::string& service_path, + const base::DictionaryValue& properties) { DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( dbus::ObjectPath(service_path), - new_properties, + properties, base::Bind( &MigrationTask::NotifyNetworkStateHandler, this, service_path), base::Bind(&MigrationTask::LogErrorAndNotifyNetworkStateHandler, @@ -258,20 +335,26 @@ void NetworkCertMigrator::NetworkListChanged() { VLOG(2) << "Certs not loaded yet."; return; } - // Run the migration process from deprecated CaCertNssProperties to CaCertPem. - VLOG(2) << "Start NSS nickname to PEM migration."; + // Run the migration process from deprecated CaCertNssProperties to CaCertPem + // and to fix missing or incorrect slot ids of client certificates. + VLOG(2) << "Start certificate migration of network configurations."; scoped_refptr helper(new MigrationTask( CertLoader::Get()->cert_list(), weak_ptr_factory_.GetWeakPtr())); NetworkStateHandler::NetworkStateList networks; - network_state_handler_->GetVisibleNetworkList(&networks); + network_state_handler_->GetNetworkListByType( + NetworkTypePattern::Default(), + true, // only configured networks + false, // visible and not visible networks + 0, // no count limit + &networks); helper->Run(networks); } void NetworkCertMigrator::OnCertificatesLoaded( const net::CertificateList& cert_list, bool initial_load) { - // Maybe there are networks referring to certs (by NSS nickname) that were not - // loaded before but are now. + // Maybe there are networks referring to certs that were not loaded before but + // are now. NetworkListChanged(); } diff --git a/chromeos/network/network_cert_migrator.h b/chromeos/network/network_cert_migrator.h index 05b69e6d251a4..1acc816a9caa3 100644 --- a/chromeos/network/network_cert_migrator.h +++ b/chromeos/network/network_cert_migrator.h @@ -16,7 +16,7 @@ namespace chromeos { class NetworkStateHandler; // Migrates network configurations from deprecated CaCertNSS properties to -// CaCertPEM. +// CaCertPEM and incorrect or missing slot IDs of client certificates. class CHROMEOS_EXPORT NetworkCertMigrator : public NetworkStateHandlerObserver, public CertLoader::Observer { public: diff --git a/chromeos/network/network_cert_migrator_unittest.cc b/chromeos/network/network_cert_migrator_unittest.cc index 2f72cfdfd800d..32798c88f7d5c 100644 --- a/chromeos/network/network_cert_migrator_unittest.cc +++ b/chromeos/network/network_cert_migrator_unittest.cc @@ -9,8 +9,10 @@ #include "base/file_util.h" #include "base/files/file_path.h" #include "base/run_loop.h" +#include "base/strings/string_number_conversions.h" #include "chromeos/cert_loader.h" #include "chromeos/dbus/dbus_thread_manager.h" +#include "chromeos/dbus/shill_profile_client.h" #include "chromeos/dbus/shill_service_client.h" #include "chromeos/network/network_state_handler.h" #include "chromeos/tpm_token_loader.h" @@ -30,9 +32,11 @@ namespace chromeos { namespace { const char* kWifiStub = "wifi_stub"; +const char* kEthernetEapStub = "ethernet_eap_stub"; const char* kVPNStub = "vpn_stub"; const char* kNSSNickname = "nss_nickname"; const char* kFakePEM = "pem"; +const char* kProfile = "/profile/profile1"; } // namespace @@ -57,6 +61,10 @@ class NetworkCertMigratorTest : public testing::Test { DBusThreadManager::InitializeWithStub(); service_test_ = DBusThreadManager::Get()->GetShillServiceClient()->GetTestInterface(); + DBusThreadManager::Get() + ->GetShillProfileClient() + ->GetTestInterface() + ->AddProfile(kProfile, "" /* userhash */); base::RunLoop().RunUntilIdle(); service_test_->ClearServices(); base::RunLoop().RunUntilIdle(); @@ -98,6 +106,30 @@ class NetworkCertMigratorTest : public testing::Test { ASSERT_TRUE(failures.empty()) << net::ErrorToString(failures[0].net_error); } + void SetupTestClientCert() { + std::string pkcs12_data; + ASSERT_TRUE(base::ReadFileToString( + net::GetTestCertsDirectory().Append("websocket_client_cert.p12"), + &pkcs12_data)); + + net::CertificateList client_cert_list; + scoped_refptr module(net::CryptoModule::CreateFromHandle( + test_nssdb_->GetPrivateSlot().get())); + ASSERT_EQ( + net::OK, + test_nssdb_->ImportFromPKCS12( + module, pkcs12_data, base::string16(), false, &client_cert_list)); + ASSERT_TRUE(!client_cert_list.empty()); + test_client_cert_ = client_cert_list[0]; + + int slot_id = -1; + test_client_cert_pkcs11_id_ = CertLoader::GetPkcs11IdAndSlotForCert( + *test_client_cert_, &slot_id); + ASSERT_FALSE(test_client_cert_pkcs11_id_.empty()); + ASSERT_NE(-1, slot_id); + test_client_cert_slot_id_ = base::IntToString(slot_id); + } + void SetupNetworkHandlers() { network_state_handler_.reset(NetworkStateHandler::InitializeForTest()); network_cert_migrator_.reset(new NetworkCertMigrator); @@ -113,6 +145,11 @@ class NetworkCertMigratorTest : public testing::Test { type, state, true /* add_to_visible */); + + // Ensure that the service appears as 'configured', i.e. is associated to a + // Shill profile. + service_test_->SetServiceProperty( + network_id, shill::kProfileProperty, base::StringValue(kProfile)); } void SetupWifiWithNss() { @@ -122,6 +159,80 @@ class NetworkCertMigratorTest : public testing::Test { base::StringValue(kNSSNickname)); } + void SetupNetworkWithEapCertId(bool wifi, const std::string& cert_id) { + std::string type = wifi ? shill::kTypeWifi: shill::kTypeEthernetEap; + std::string name = wifi ? kWifiStub : kEthernetEapStub; + AddService(name, type, shill::kStateOnline); + service_test_->SetServiceProperty( + name, shill::kEapCertIdProperty, base::StringValue(cert_id)); + service_test_->SetServiceProperty( + name, shill::kEapKeyIdProperty, base::StringValue(cert_id)); + + if (wifi) { + service_test_->SetServiceProperty( + name, + shill::kSecurityProperty, + base::StringValue(shill::kSecurity8021x)); + } + } + + void GetEapCertId(bool wifi, std::string* cert_id) { + cert_id->clear(); + + std::string name = wifi ? kWifiStub : kEthernetEapStub; + const base::DictionaryValue* properties = + service_test_->GetServiceProperties(name); + properties->GetStringWithoutPathExpansion(shill::kEapCertIdProperty, + cert_id); + } + + void SetupVpnWithCertId(bool open_vpn, + const std::string& slot_id, + const std::string& pkcs11_id) { + AddService(kVPNStub, shill::kTypeVPN, shill::kStateIdle); + base::DictionaryValue provider; + if (open_vpn) { + provider.SetStringWithoutPathExpansion(shill::kTypeProperty, + shill::kProviderOpenVpn); + provider.SetStringWithoutPathExpansion( + shill::kOpenVPNClientCertIdProperty, pkcs11_id); + } else { + provider.SetStringWithoutPathExpansion(shill::kTypeProperty, + shill::kProviderL2tpIpsec); + provider.SetStringWithoutPathExpansion( + shill::kL2tpIpsecClientCertSlotProperty, slot_id); + provider.SetStringWithoutPathExpansion( + shill::kL2tpIpsecClientCertIdProperty, pkcs11_id); + } + service_test_->SetServiceProperty( + kVPNStub, shill::kProviderProperty, provider); + } + + void GetVpnCertId(bool open_vpn, + std::string* slot_id, + std::string* pkcs11_id) { + slot_id->clear(); + pkcs11_id->clear(); + + const base::DictionaryValue* properties = + service_test_->GetServiceProperties(kVPNStub); + ASSERT_TRUE(properties); + const base::DictionaryValue* provider = NULL; + properties->GetDictionaryWithoutPathExpansion(shill::kProviderProperty, + &provider); + if (!provider) + return; + if (open_vpn) { + provider->GetStringWithoutPathExpansion( + shill::kOpenVPNClientCertIdProperty, pkcs11_id); + } else { + provider->GetStringWithoutPathExpansion( + shill::kL2tpIpsecClientCertSlotProperty, slot_id); + provider->GetStringWithoutPathExpansion( + shill::kL2tpIpsecClientCertIdProperty, pkcs11_id); + } + } + void GetEapCACertProperties(std::string* nss_nickname, std::string* ca_pem) { nss_nickname->clear(); ca_pem->clear(); @@ -171,12 +282,19 @@ class NetworkCertMigratorTest : public testing::Test { ShillServiceClient::TestInterface* service_test_; scoped_refptr test_ca_cert_; + scoped_refptr test_client_cert_; + std::string test_client_cert_pkcs11_id_; + std::string test_client_cert_slot_id_; std::string test_ca_cert_pem_; base::MessageLoop message_loop_; private: void CleanupTestCert() { - ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_ca_cert_.get())); + if (test_ca_cert_) + ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_ca_cert_.get())); + + if (test_client_cert_) + ASSERT_TRUE(test_nssdb_->DeleteCertAndKey(test_client_cert_.get())); } scoped_ptr network_state_handler_; @@ -233,7 +351,7 @@ TEST_F(NetworkCertMigratorTest, DoNotMigrateNssIfPemSet) { EXPECT_EQ(kFakePEM, ca_pem); } -TEST_F(NetworkCertMigratorTest, MigrateOpenVpn) { +TEST_F(NetworkCertMigratorTest, MigrateNssOpenVpn) { // Add a new network for migration before the handlers are initialized. SetupVpnWithNss(true /* OpenVPN */); @@ -247,7 +365,7 @@ TEST_F(NetworkCertMigratorTest, MigrateOpenVpn) { EXPECT_EQ(test_ca_cert_pem_, ca_pem); } -TEST_F(NetworkCertMigratorTest, MigrateIpsecVpn) { +TEST_F(NetworkCertMigratorTest, MigrateNssIpsecVpn) { // Add a new network for migration before the handlers are initialized. SetupVpnWithNss(false /* not OpenVPN */); @@ -261,4 +379,127 @@ TEST_F(NetworkCertMigratorTest, MigrateIpsecVpn) { EXPECT_EQ(test_ca_cert_pem_, ca_pem); } +TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoMatchingCert) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + // Add a new network for migration after the handlers are initialized. + SetupNetworkWithEapCertId(true /* wifi */, "unknown pkcs11 id"); + + base::RunLoop().RunUntilIdle(); + // Since the PKCS11 ID is unknown, the certificate configuration will be + // cleared. + std::string cert_id; + GetEapCertId(true /* wifi */, &cert_id); + EXPECT_EQ(std::string(), cert_id); +} + +TEST_F(NetworkCertMigratorTest, MigrateEapCertIdNoSlotId) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + // Add a new network for migration after the handlers are initialized. + SetupNetworkWithEapCertId(true /* wifi */, test_client_cert_pkcs11_id_); + + base::RunLoop().RunUntilIdle(); + + std::string cert_id; + GetEapCertId(true /* wifi */, &cert_id); + std::string expected_cert_id = + test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; + EXPECT_EQ(expected_cert_id, cert_id); +} + +TEST_F(NetworkCertMigratorTest, MigrateWifiEapCertIdWrongSlotId) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + // Add a new network for migration after the handlers are initialized. + SetupNetworkWithEapCertId(true /* wifi */, + "123:" + test_client_cert_pkcs11_id_); + + base::RunLoop().RunUntilIdle(); + + std::string cert_id; + GetEapCertId(true /* wifi */, &cert_id); + std::string expected_cert_id = + test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; + EXPECT_EQ(expected_cert_id, cert_id); +} + +TEST_F(NetworkCertMigratorTest, DoNotChangeEapCertIdWithCorrectSlotId) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + std::string expected_cert_id = + test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; + + // Add a new network for migration after the handlers are initialized. + SetupNetworkWithEapCertId(true /* wifi */, expected_cert_id); + + base::RunLoop().RunUntilIdle(); + + std::string cert_id; + GetEapCertId(true /* wifi */, &cert_id); + EXPECT_EQ(expected_cert_id, cert_id); +} + +TEST_F(NetworkCertMigratorTest, IgnoreOpenVPNCertId) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + const char kPkcs11Id[] = "any slot id"; + + // Add a new network for migration after the handlers are initialized. + SetupVpnWithCertId( + true /* OpenVPN */, std::string() /* no slot id */, kPkcs11Id); + + base::RunLoop().RunUntilIdle(); + + std::string pkcs11_id; + std::string unused_slot_id; + GetVpnCertId(true /* OpenVPN */, &unused_slot_id, &pkcs11_id); + EXPECT_EQ(kPkcs11Id, pkcs11_id); +} + +TEST_F(NetworkCertMigratorTest, MigrateEthernetEapCertIdWrongSlotId) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + // Add a new network for migration after the handlers are initialized. + SetupNetworkWithEapCertId( + false /* ethernet */, "123:" + test_client_cert_pkcs11_id_); + + base::RunLoop().RunUntilIdle(); + + std::string cert_id; + GetEapCertId(false /* ethernet */, &cert_id); + std::string expected_cert_id = + test_client_cert_slot_id_ + ":" + test_client_cert_pkcs11_id_; + EXPECT_EQ(expected_cert_id, cert_id); +} + +TEST_F(NetworkCertMigratorTest, MigrateIpsecCertIdWrongSlotId) { + SetupTestClientCert(); + SetupNetworkHandlers(); + base::RunLoop().RunUntilIdle(); + + // Add a new network for migration after the handlers are initialized. + SetupVpnWithCertId(false /* IPsec */, "123", test_client_cert_pkcs11_id_); + + base::RunLoop().RunUntilIdle(); + + std::string pkcs11_id; + std::string slot_id; + GetVpnCertId(false /* IPsec */, &slot_id, &pkcs11_id); + EXPECT_EQ(test_client_cert_pkcs11_id_, pkcs11_id); + EXPECT_EQ(test_client_cert_slot_id_, slot_id); +} + } // namespace chromeos diff --git a/chromeos/network/network_connection_handler.cc b/chromeos/network/network_connection_handler.cc index 99c3051e920d1..332319aff77d2 100644 --- a/chromeos/network/network_connection_handler.cc +++ b/chromeos/network/network_connection_handler.cc @@ -268,11 +268,6 @@ void NetworkConnectionHandler::ConnectToNetwork( InvokeErrorCallback(service_path, error_callback, kErrorConnecting); return; } - if (network->RequiresActivation()) { - InvokeErrorCallback(service_path, error_callback, - kErrorActivationRequired); - return; - } if (check_error_state) { const std::string& error = network->last_error(); diff --git a/chromeos/network/network_connection_handler_unittest.cc b/chromeos/network/network_connection_handler_unittest.cc index ef69213390423..b22c07d2b045c 100644 --- a/chromeos/network/network_connection_handler_unittest.cc +++ b/chromeos/network/network_connection_handler_unittest.cc @@ -303,9 +303,6 @@ const char* kConfigConnecting = const char* kConfigRequiresPassphrase = "{ \"GUID\": \"wifi3\", \"Type\": \"wifi\", " " \"PassphraseRequired\": true }"; -const char* kConfigRequiresActivation = - "{ \"GUID\": \"cellular1\", \"Type\": \"cellular\"," - " \"Cellular.ActivationState\": \"not-activated\" }"; } // namespace @@ -335,11 +332,6 @@ TEST_F(NetworkConnectionHandlerTest, NetworkConnectionHandlerConnectFailure) { Connect("wifi3"); EXPECT_EQ(NetworkConnectionHandler::kErrorPassphraseRequired, GetResultAndReset()); - - EXPECT_TRUE(Configure(kConfigRequiresActivation)); - Connect("cellular1"); - EXPECT_EQ(NetworkConnectionHandler::kErrorActivationRequired, - GetResultAndReset()); } namespace { diff --git a/chromeos/network/network_state.cc b/chromeos/network/network_state.cc index c528c431adf6a..cb451e86090f7 100644 --- a/chromeos/network/network_state.cc +++ b/chromeos/network/network_state.cc @@ -65,7 +65,6 @@ NetworkState::NetworkState(const std::string& path) connectable_(false), prefix_length_(0), signal_strength_(0), - activate_over_non_cellular_networks_(false), cellular_out_of_credits_(false), has_ca_cert_nss_(false) { } @@ -94,6 +93,8 @@ bool NetworkState::PropertyChanged(const std::string& key, else error_.clear(); return true; + } else if (key == shill::kActivationTypeProperty) { + return GetStringValue(key, value, &activation_type_); } else if (key == shill::kActivationStateProperty) { return GetStringValue(key, value, &activation_state_); } else if (key == shill::kRoamingStateProperty) { @@ -110,8 +111,6 @@ bool NetworkState::PropertyChanged(const std::string& key, return GetStringValue(key, value, &guid_); } else if (key == shill::kProfileProperty) { return GetStringValue(key, value, &profile_path_); - } else if (key == shill::kActivateOverNonCellularNetworkProperty) { - return GetBooleanValue(key, value, &activate_over_non_cellular_networks_); } else if (key == shill::kOutOfCreditsProperty) { return GetBooleanValue(key, value, &cellular_out_of_credits_); } else if (key == shill::kProxyConfigProperty) { diff --git a/chromeos/network/network_state.h b/chromeos/network/network_state.h index 90b78cd954921..b1a491bdd96d8 100644 --- a/chromeos/network/network_state.h +++ b/chromeos/network/network_state.h @@ -84,11 +84,9 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState { const std::string& network_technology() const { return network_technology_; } + const std::string& activation_type() const { return activation_type_; } const std::string& activation_state() const { return activation_state_; } const std::string& roaming() const { return roaming_; } - bool activate_over_non_cellular_networks() const { - return activate_over_non_cellular_networks_; - } bool cellular_out_of_credits() const { return cellular_out_of_credits_; } // Whether this network has a CACertNSS nickname set. @@ -168,9 +166,9 @@ class CHROMEOS_EXPORT NetworkState : public ManagedState { // Cellular properties, used for icons, Connect, and Activation. std::string network_technology_; + std::string activation_type_; std::string activation_state_; std::string roaming_; - bool activate_over_non_cellular_networks_; bool cellular_out_of_credits_; // Whether a deprecated CaCertNSS property of this network is set. Required diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc index 1b1daa75a7d46..4e1bdebece15e 100644 --- a/chromeos/network/onc/onc_signature.cc +++ b/chromeos/network/onc/onc_signature.cc @@ -251,7 +251,7 @@ const OncFieldSignature cellular_fields[] = { {NULL}}; const OncFieldSignature cellular_with_state_fields[] = { - { ::onc::cellular::kActivateOverNonCellularNetwork, &kBoolSignature}, + { ::onc::cellular::kActivationType, &kStringSignature}, { ::onc::cellular::kActivationState, &kStringSignature}, { ::onc::cellular::kAllowRoaming, &kBoolSignature}, { ::onc::cellular::kCarrier, &kStringSignature}, diff --git a/chromeos/network/onc/onc_translation_tables.cc b/chromeos/network/onc/onc_translation_tables.cc index 6aba3c1cf8927..119b62bf09507 100644 --- a/chromeos/network/onc/onc_translation_tables.cc +++ b/chromeos/network/onc/onc_translation_tables.cc @@ -149,8 +149,7 @@ const FieldTranslationEntry sim_lock_status_fields[] = { // This must only contain Service properties and not Device properties. // For Device properties see kCellularDeviceTable. const FieldTranslationEntry cellular_fields[] = { - { ::onc::cellular::kActivateOverNonCellularNetwork, - shill::kActivateOverNonCellularNetworkProperty}, + { ::onc::cellular::kActivationType, shill::kActivationTypeProperty}, { ::onc::cellular::kActivationState, shill::kActivationStateProperty}, { ::onc::cellular::kNetworkTechnology, shill::kNetworkTechnologyProperty}, { ::onc::cellular::kRoamingState, shill::kRoamingStateProperty}, diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc index dc01b2a53dd51..19e7eff0b853e 100644 --- a/chromeos/network/onc/onc_translator_shill_to_onc.cc +++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc @@ -398,8 +398,10 @@ void ShillToONCTranslator::TranslateAndAddNestedObject( const base::DictionaryValue& dictionary) { const OncFieldSignature* field_signature = GetFieldSignature(*onc_signature_, onc_field_name); - DCHECK(field_signature) << "Unable to find signature for field " - << onc_field_name << "."; + if (!field_signature) { + NOTREACHED() << "Unable to find signature for field: " << onc_field_name; + return; + } ShillToONCTranslator nested_translator(dictionary, *field_signature->value_signature); scoped_ptr nested_object = diff --git a/chromeos/system/statistics_provider.cc b/chromeos/system/statistics_provider.cc index a829c35513983..60b3c4b394205 100644 --- a/chromeos/system/statistics_provider.cc +++ b/chromeos/system/statistics_provider.cc @@ -267,7 +267,6 @@ void StatisticsProviderImpl::LoadMachineStatistics(bool load_oem_manifest) { } else if (base::SysInfo::IsRunningOnChromeOS()) { LoadOemManifestFromFile(base::FilePath(kOemManifestFilePath)); } - oem_manifest_loaded_ = true; } if (!base::SysInfo::IsRunningOnChromeOS() && @@ -306,6 +305,7 @@ void StatisticsProviderImpl::LoadOemManifestFromFile( machine_flags_[kOemKeyboardDrivenOobeKey] = oem_manifest.keyboard_driven_oobe; + oem_manifest_loaded_ = true; VLOG(1) << "Loaded OEM Manifest statistics from " << file.value(); } diff --git a/chromeos/test/data/network/shill_cellular_with_state.json b/chromeos/test/data/network/shill_cellular_with_state.json index b26aaae186f67..a3af38f65c9e3 100644 --- a/chromeos/test/data/network/shill_cellular_with_state.json +++ b/chromeos/test/data/network/shill_cellular_with_state.json @@ -1,8 +1,8 @@ { "Type": "cellular", "Name": "Test Network", - "Cellular.ActivateOverNonCellularNetwork": false, "Cellular.ActivationState": "activated", + "Cellular.ActivationType": "OTASP", "Cellular.ServingOperator": { "code": "test-code", "country": "test-country", diff --git a/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc b/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc index ee290f566de88..d7d6b337dea45 100644 --- a/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc +++ b/chromeos/test/data/network/translation_of_shill_cellular_with_state.onc @@ -2,8 +2,8 @@ "Type": "Cellular", "Name": "Test Network", "Cellular": { - "ActivateOverNonCellularNetwork": false, "ActivationState": "activated", + "ActivationType": "OTASP", "AllowRoaming": true, "HomeProvider": { "country": "us", diff --git a/chromeos/tpm_token_loader.cc b/chromeos/tpm_token_loader.cc index 44a438a3c6875..a469b845c9c44 100644 --- a/chromeos/tpm_token_loader.cc +++ b/chromeos/tpm_token_loader.cc @@ -140,7 +140,7 @@ void TPMTokenLoader::MaybeStartTokenInitialization() { tpm_token_state_ = TPM_DISABLED; // Treat TPM as disabled for guest users since they do not store certs. - if (LoginState::Get()->IsGuestUser()) + if (LoginState::Get()->IsGuestSessionUser()) tpm_token_state_ = TPM_DISABLED; ContinueTokenInitialization(); diff --git a/codereview.settings b/codereview.settings index 76514e97e3496..9789920c94198 100644 --- a/codereview.settings +++ b/codereview.settings @@ -1,7 +1,7 @@ # This file is used by gcl to get repository specific information. CODE_REVIEW_SERVER: codereview.chromium.org CC_LIST: chromium-reviews@chromium.org -VIEW_VC: https://src.chromium.org/viewvc/chrome?view=rev&revision= +VIEW_VC: https://chromium.googlesource.com/chromium/src/+/ STATUS: http://chromium-status.appspot.com/status TRY_ON_UPLOAD: True TRYSERVER_SVN_URL: svn://svn.chromium.org/chrome-try/try @@ -9,3 +9,4 @@ GITCL_PREUPLOAD: http://src.chromium.org/viewvc/trunk/tools/depot_tools/git-cl-u GITCL_PREDCOMMIT: http://src.chromium.org/viewvc/trunk/tools/depot_tools/git-cl-upload-hook?revision=HEAD&root=chrome LINT_IGNORE_REGEX: webkit/api/.* PROJECT: chromium +PENDING_REF_PREFIX: refs/pending/ diff --git a/components/BUILD.gn b/components/BUILD.gn index 3f1efe4854e52..2d9bc68073bda 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn @@ -57,6 +57,7 @@ group("all_components") { "//components/password_manager/core/common", "//components/plugins/renderer", "//components/policy", + "//components/power", "//components/precache/core", "//components/precache/content", "//components/pref_registry", @@ -137,6 +138,7 @@ group("all_components") { "//components/password_manager/core/browser", # Should work, needs checking. "//components/password_manager/core/common", # Should work, needs checking. "//components/plugins/renderer", # Blocked on blink. + "//components/power", # Blocked on content. "//components/policy", # Blocked on content (indirectly via autofill). "//components/precache/content", # Blocked on content. "//components/precache/core", # Should work, needs checking. diff --git a/components/OWNERS b/components/OWNERS index 1a4365453dc86..193f9dc165569 100644 --- a/components/OWNERS +++ b/components/OWNERS @@ -1,4 +1,5 @@ blundell@chromium.org +erikwright@chromium.org jochen@chromium.org per-file autofill*=dhollowa@chromium.org @@ -12,13 +13,6 @@ per-file breakpad.gypi=thestig@chromium.org per-file bookmarks.gypi=brettw@chromium.org per-file bookmarks.gypi=sky@chromium.org -per-file data_reduction_proxy*=bengr@chromium.org -per-file data_reduction_proxy*=marq@chromium.org -per-file data_reduction_proxy*=bolian@chromium.org - -per-file enhanced_bookmarks*=noyau@chromium.org -per-file enhanced_bookmarks*=sky@chromium.org - per-file cloud_devices*=gene@chromium.org per-file cloud_devices*=noamsml@chromium.org per-file cloud_devices*=vitalybuka@chromium.org @@ -31,6 +25,10 @@ per-file copresence.gypi=derat@chromium.org per-file cronet*=mef@chromium.org per-file cronet*=mmenke@chromium.org +per-file data_reduction_proxy*=bengr@chromium.org +per-file data_reduction_proxy*=marq@chromium.org +per-file data_reduction_proxy*=bolian@chromium.org + per-file dom_distiller*=bengr@chromium.org per-file dom_distiller*=cjhopman@chromium.org per-file dom_distiller*=nyquist@chromium.org @@ -40,6 +38,10 @@ per-file domain_reliability.gypi=mmenke@chromium.org per-file domain_reliability.gypi=szym@chromium.org per-file domain_reliability.gypi=ttuttle@chromium.org +per-file enhanced_bookmarks*=noyau@chromium.org +per-file enhanced_bookmarks*=sky@chromium.org +per-file enhanced_bookmarks*=yfriedman@chromium.org + per-file favicon*=sky@chromium.org per-file favicon*=stevenjb@chromium.org # Temporary for the duration of the favicon componentization. diff --git a/components/autofill.gypi b/components/autofill.gypi index a20b011c37c87..bac6893cd54cc 100644 --- a/components/autofill.gypi +++ b/components/autofill.gypi @@ -34,24 +34,10 @@ '../ui/gfx/gfx.gyp:gfx', '../url/url.gyp:url_lib', ], - 'conditions': [ - ['OS == "android"', { - 'dependencies': [ - 'autofill_jni_headers', - ], - }], - ], 'include_dirs': [ '..', ], 'sources': [ - 'autofill/core/browser/android/auxiliary_profile_loader_android.cc', - 'autofill/core/browser/android/auxiliary_profile_loader_android.h', - 'autofill/core/browser/android/auxiliary_profiles_android.cc', - 'autofill/core/browser/android/auxiliary_profiles_android.h', - 'autofill/core/browser/android/component_jni_registrar.cc', - 'autofill/core/browser/android/component_jni_registrar.h', - 'autofill/core/browser/android/personal_data_manager_android.cc', 'autofill/core/common/autofill_constants.cc', 'autofill/core/common/autofill_constants.h', 'autofill/core/common/autofill_data_validation.cc', @@ -258,8 +244,6 @@ '../testing/gtest.gyp:gtest', ], 'sources': [ - 'autofill/core/browser/android/test_auxiliary_profile_loader_android.cc', - 'autofill/core/browser/android/test_auxiliary_profile_loader_android.h', 'autofill/core/browser/autofill_test_utils.cc', 'autofill/core/browser/autofill_test_utils.h', 'autofill/core/browser/data_driven_test.cc', @@ -452,32 +436,5 @@ }, ], }], - ['OS == "android"', { - 'targets': [ - { - 'target_name': 'autofill_java', - 'type': 'none', - 'dependencies': [ - '../base/base.gyp:base', - '../content/content.gyp:content_java', - ], - 'variables': { - 'java_in_dir': 'autofill/core/browser/android/java', - }, - 'includes': [ '../build/java.gypi' ], - }, - { - 'target_name': 'autofill_jni_headers', - 'type': 'none', - 'sources': [ - 'autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java', - ], - 'variables': { - 'jni_gen_package': 'autofill', - }, - 'includes': [ '../build/jni_generator.gypi' ], - }, - ], - }], ], } diff --git a/components/autofill/content/renderer/password_generation_agent.cc b/components/autofill/content/renderer/password_generation_agent.cc index e90f1c9c9b79e..b777c9c420815 100644 --- a/components/autofill/content/renderer/password_generation_agent.cc +++ b/components/autofill/content/renderer/password_generation_agent.cc @@ -288,10 +288,12 @@ bool PasswordGenerationAgent::FocusedNodeHasChanged( return true; } - // Only trigger if the password field is empty. + // Assume that if the password field has less than kMaximumOfferSize + // characters then the user is not finished typing their password and display + // the password suggestion. if (!element->isReadOnly() && element->isEnabled() && - element->value().isEmpty()) { + element->value().length() <= kMaximumOfferSize) { ShowGenerationPopup(); return true; } @@ -318,10 +320,7 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( // Offer generation again. ShowGenerationPopup(); - } else if (!password_is_generated_) { - // User has rejected the feature and has started typing a password. - HidePopup(); - } else { + } else if (password_is_generated_) { password_edited_ = true; // Mirror edits to any confirmation password fields. for (std::vector::iterator it = @@ -329,6 +328,14 @@ bool PasswordGenerationAgent::TextDidChangeInTextField( it != password_elements_.end(); ++it) { it->setValue(element.value()); } + } else if (element.value().length() > kMaximumOfferSize) { + // User has rejected the feature and has started typing a password. + HidePopup(); + } else { + // Password isn't generated and there are fewer than kMaximumOfferSize + // characters typed, so keep offering the password. Note this function + // will just keep the previous popup if one is already showing. + ShowGenerationPopup(); } return true; diff --git a/components/autofill/content/renderer/password_generation_agent.h b/components/autofill/content/renderer/password_generation_agent.h index bf632aef9f6c5..2d75af0376d36 100644 --- a/components/autofill/content/renderer/password_generation_agent.h +++ b/components/autofill/content/renderer/password_generation_agent.h @@ -39,6 +39,9 @@ class PasswordGenerationAgent : public content::RenderViewObserver { // Returns true if the newly focused node caused the generation UI to show. bool FocusedNodeHasChanged(const blink::WebNode& node); + // The length that a password can be before the UI is hidden. + static const size_t kMaximumOfferSize = 5; + protected: // Returns true if this document is one that we should consider analyzing. // Virtual so that it can be overriden during testing. diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 9f3713c4398a3..acda944f2eb23 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn @@ -177,8 +177,6 @@ static_library("browser") { static_library("test_support") { sources = [ - "android/test_auxiliary_profile_loader_android.cc", - "android/test_auxiliary_profile_loader_android.h", "autofill_test_utils.cc", "autofill_test_utils.h", "data_driven_test.cc", @@ -206,7 +204,6 @@ source_set("unit_tests") { sources = [ "address_field_unittest.cc", "address_unittest.cc", - "android/auxiliary_profile_unittest_android.cc", "autocomplete_history_manager_unittest.cc", "autofill_country_unittest.cc", "autofill_data_model_unittest.cc", diff --git a/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc b/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc deleted file mode 100644 index 4361791f66c13..0000000000000 --- a/components/autofill/core/browser/android/auxiliary_profile_loader_android.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" - -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_array.h" -#include "base/android/jni_string.h" -#include "jni/PersonalAutofillPopulator_jni.h" - -#define JAVA_METHOD(__jmethod__) Java_PersonalAutofillPopulator_##__jmethod__( \ - env_, \ - populator_.obj()) - -namespace { - -base::string16 SafeJavaStringToUTF16( - const ScopedJavaLocalRef& jstring) { - if (jstring.is_null()) - return base::string16(); - - return ConvertJavaStringToUTF16(jstring); -} - -void SafeJavaStringArrayToStringVector( - const ScopedJavaLocalRef& jarray, - JNIEnv* env, - std::vector* string_vector) { - if (!jarray.is_null()) { - base::android::AppendJavaStringArrayToStringVector(env, - jarray.obj(), - string_vector); - } -} - -} // namespace - -namespace autofill { - -bool RegisterAuxiliaryProfileLoader(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -AuxiliaryProfileLoaderAndroid::AuxiliaryProfileLoaderAndroid() {} - -AuxiliaryProfileLoaderAndroid::~AuxiliaryProfileLoaderAndroid() {} - -void AuxiliaryProfileLoaderAndroid::Init(JNIEnv* env, const jobject& context) { - env_ = env; - populator_ = Java_PersonalAutofillPopulator_create(env_, context); -} - -bool AuxiliaryProfileLoaderAndroid::GetHasPermissions() const { - return (bool)JAVA_METHOD(getHasPermissions); -} - -// Address info -base::string16 AuxiliaryProfileLoaderAndroid::GetStreet() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getStreet)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetPostOfficeBox() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getPobox)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetNeighborhood() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getNeighborhood)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetRegion() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getRegion)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetCity() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getCity)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetPostalCode() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getPostalCode)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetCountry() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getCountry)); -} - -// Name info -base::string16 AuxiliaryProfileLoaderAndroid::GetFirstName() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getFirstName)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetMiddleName() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getMiddleName)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetLastName() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getLastName)); -} - -base::string16 AuxiliaryProfileLoaderAndroid::GetSuffix() const { - return SafeJavaStringToUTF16(JAVA_METHOD(getSuffix)); -} - -// Email info -void AuxiliaryProfileLoaderAndroid::GetEmailAddresses( - std::vector* email_addresses) const { - SafeJavaStringArrayToStringVector(JAVA_METHOD(getEmailAddresses), - env_, - email_addresses); -} - -// Phone info -void AuxiliaryProfileLoaderAndroid::GetPhoneNumbers( - std::vector* phone_numbers) const { - SafeJavaStringArrayToStringVector(JAVA_METHOD(getPhoneNumbers), - env_, - phone_numbers); -} - -} // namespace diff --git a/components/autofill/core/browser/android/auxiliary_profile_loader_android.h b/components/autofill/core/browser/android/auxiliary_profile_loader_android.h deleted file mode 100644 index 2185e83e3776f..0000000000000 --- a/components/autofill/core/browser/android/auxiliary_profile_loader_android.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILE_LOADER_ANDROID_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILE_LOADER_ANDROID_H_ - -#include - -#include "base/android/jni_android.h" -#include "base/strings/string16.h" - -namespace autofill { - -bool RegisterAuxiliaryProfileLoader(JNIEnv* env); - -// This class loads user's contact information from their device. -// The populated data corresponds to the user's 'Me' profile in their -// contact list. -class AuxiliaryProfileLoaderAndroid { - public: - AuxiliaryProfileLoaderAndroid(); - virtual ~AuxiliaryProfileLoaderAndroid(); - - // Init method should be called after object initialization. - // context parameter is an Android context. - void Init(JNIEnv* env, const jobject& context); - - // Returns true IFF the application has priviledges to access the user's - // contact information. - virtual bool GetHasPermissions() const; - // Returns address street. - virtual base::string16 GetStreet() const; - // Returns address post office box. - virtual base::string16 GetPostOfficeBox() const; - // Returns address neighborhood (e.g. Noe Valley, Nob Hill, Twin Peaks, ...). - virtual base::string16 GetNeighborhood() const; - // Returns address region such as state or province information - // (e.g. Ontario, California, Hubei). - virtual base::string16 GetRegion() const; - // Returns address city. - virtual base::string16 GetCity() const; - // Returns address postal code or zip code. - virtual base::string16 GetPostalCode() const; - // Returns address country. - virtual base::string16 GetCountry() const; - - // Returns contact's first name. - virtual base::string16 GetFirstName() const; - // Returns contact's middle name. - virtual base::string16 GetMiddleName() const; - // Returns contact's last name. - virtual base::string16 GetLastName() const; - // Returns contact's suffix (e.g. Ph.D, M.D., ...). - virtual base::string16 GetSuffix() const; - - // Populates string vector parameter with contact's email addresses. - virtual void GetEmailAddresses( - std::vector* email_addresses) const; - - // Populates string vector parameter with contact's phones numbers. - virtual void GetPhoneNumbers( - std::vector* phone_numbers) const; - - private: - JNIEnv* env_; - // The reference to java |PersonalAutofillPopulator| which - // actually extracts users contact information from the physical device - base::android::ScopedJavaLocalRef populator_; - DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfileLoaderAndroid); -}; - -} // namespace - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILE_LOADER_ANDROID_H_ diff --git a/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc b/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc deleted file mode 100644 index daee564a16b88..0000000000000 --- a/components/autofill/core/browser/android/auxiliary_profile_unittest_android.cc +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/memory/scoped_vector.h" -#include "base/strings/string16.h" -#include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" -#include "components/autofill/core/browser/android/auxiliary_profiles_android.h" -#include "components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h" -#include "components/autofill/core/browser/autofill_profile.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::ASCIIToUTF16; - -namespace autofill { - -class AuxiliaryProfileAndroidTest : public testing::Test { - public: - AuxiliaryProfileAndroidTest() {} - - AutofillProfile* GetAndLoadProfile() { - autofill::AuxiliaryProfilesAndroid impl(profile_loader_, "en-US"); - profile_ = impl.LoadContactsProfile(); - return profile_.get(); - } - - TestAuxiliaryProfileLoader& profile_loader() { - return profile_loader_; - } - - private: - TestAuxiliaryProfileLoader profile_loader_; - scoped_ptr profile_; -}; - -TEST_F(AuxiliaryProfileAndroidTest, SetNameInfo) { - base::string16 first_name = ASCIIToUTF16("John"); - base::string16 middle_name = ASCIIToUTF16("H."); - base::string16 last_name = ASCIIToUTF16("Waston"); - - profile_loader().SetFirstName(first_name); - profile_loader().SetMiddleName(middle_name); - profile_loader().SetLastName(last_name); - - AutofillProfile* profile = GetAndLoadProfile(); - - EXPECT_EQ(profile->GetRawInfo(NAME_FIRST), first_name); - EXPECT_EQ(profile->GetRawInfo(NAME_MIDDLE), middle_name); - EXPECT_EQ(profile->GetRawInfo(NAME_LAST), last_name); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetNameInfoEmpty) { - AutofillProfile* profile = GetAndLoadProfile(); - - EXPECT_EQ(profile->GetRawInfo(NAME_FIRST), base::string16()); - EXPECT_EQ(profile->GetRawInfo(NAME_MIDDLE), base::string16()); - EXPECT_EQ(profile->GetRawInfo(NAME_LAST), base::string16()); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetEmailInfo) { - std::vector email_addresses; - email_addresses.push_back(ASCIIToUTF16("sherlock@holmes.com")); - email_addresses.push_back(ASCIIToUTF16("watson@holmes.com")); - profile_loader().SetEmailAddresses(email_addresses); - - AutofillProfile* profile = GetAndLoadProfile(); - std::vector loaded_email_addresses; - profile->GetRawMultiInfo(EMAIL_ADDRESS, &loaded_email_addresses); - EXPECT_EQ(loaded_email_addresses, email_addresses); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetEmailInfoEmpty) { - std::vector expected_email_addresses; - expected_email_addresses.push_back(base::string16()); - std::vector loaded_email_addresses; - AutofillProfile* profile = GetAndLoadProfile(); - profile->GetRawMultiInfo(EMAIL_ADDRESS, &loaded_email_addresses); - EXPECT_EQ(loaded_email_addresses, expected_email_addresses); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetPhoneInfo) { - std::vector phone_numbers; - phone_numbers.push_back(ASCIIToUTF16("6502530000")); - profile_loader().SetPhoneNumbers(phone_numbers); - - std::vector loaded_phone_numbers; - AutofillProfile* profile = GetAndLoadProfile(); - profile->GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &loaded_phone_numbers); - EXPECT_EQ(loaded_phone_numbers, phone_numbers); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetPhoneInfoEmpty) { - std::vector expected_phone_numbers; - expected_phone_numbers.push_back(base::string16()); - - std::vector loaded_phone_numbers; - AutofillProfile* profile = GetAndLoadProfile(); - profile->GetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, &loaded_phone_numbers); - EXPECT_EQ(loaded_phone_numbers, expected_phone_numbers); -} - -// -// Android user's profile contact does not parse its address -// into constituent parts. Instead we just Get a long string blob. -// Disable address population tests until we implement a way to parse the -// data. -// - -#if 0 -TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfo) { - base::string16 street = ASCIIToUTF16("221 B Baker Street"); - base::string16 city = ASCIIToUTF16("London"); - base::string16 postal_code = ASCIIToUTF16("123456"); - base::string16 region = ASCIIToUTF16("Georgian Terrace"); - base::string16 country = ASCIIToUTF16("England"); - - profile_loader().SetStreet(street); - profile_loader().SetCity(city); - profile_loader().SetPostalCode(postal_code); - profile_loader().SetRegion(region); - profile_loader().SetCountry(country); - - AutofillProfile* profile = GetAndLoadProfile(); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE1), street); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_CITY), city); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_ZIP), postal_code); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_STATE), region); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_COUNTRY), country); -} - -base::string16 post_office_box= ASCIIToUTF16("222"); -base::string16 neighborhood = ASCIIToUTF16("Doyle"); -TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoCompoundFields1) { - profile_loader().SetPostOfficeBox(post_office_box); - profile_loader().SetNeighborhood(neighborhood); - base::string16 expectedLine2= ASCIIToUTF16("222, Doyle"); - AutofillProfile* profile = GetAndLoadProfile(); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), expectedLine2); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoCompoundFields2) { - profile_loader().SetPostOfficeBox(post_office_box); - AutofillProfile* profile = GetAndLoadProfile(); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), post_office_box); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoCompoundFields3) { - profile_loader().SetNeighborhood(neighborhood); - AutofillProfile* profile = GetAndLoadProfile(); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), neighborhood); -} - -TEST_F(AuxiliaryProfileAndroidTest, SetAddressInfoEmpty) { - AutofillProfile* profile = GetAndLoadProfile(); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE1), base::string16()); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_LINE2), base::string16()); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_CITY), base::string16()); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_ZIP), base::string16()); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_STATE), base::string16()); - EXPECT_EQ(profile->GetRawInfo(ADDRESS_HOME_COUNTRY), base::string16()); -} -#endif - -} // namespace autofill diff --git a/components/autofill/core/browser/android/auxiliary_profiles_android.cc b/components/autofill/core/browser/android/auxiliary_profiles_android.cc deleted file mode 100644 index e215d98d72561..0000000000000 --- a/components/autofill/core/browser/android/auxiliary_profiles_android.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Populates default autofill profile from user's own Android contact. -#include "components/autofill/core/browser/android/auxiliary_profiles_android.h" - -#include - -#include "base/guid.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" -#include "base/strings/string16.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" -#include "components/autofill/core/browser/autofill_profile.h" -#include "components/autofill/core/browser/autofill_type.h" -#include "components/autofill/core/browser/phone_number.h" - -// Generates the autofill profile by accessing the Android -// ContactsContract.Profile API through PersonalAutofillPopulator via JNI. -// The generated profile corresponds to the user's "ME" contact in the -// "People" app. The caller passes a vector of profiles into the constructor -// then initiates a fetch using |GetContactsProfile()| method. This clears -// any existing addresses. - -// Randomly generated guid. The Autofillprofile class requires a consistent -// unique guid or else things break. -const char kAndroidMeContactA[] = "9A9E1C06-7A3B-48FA-AA4F-135CA6FC25D9"; - -// The origin string for all profiles loaded from the Android contacts list. -const char kAndroidContactsOrigin[] = "Android Contacts"; - -namespace { - -// Takes misc. address information strings from Android API and collapses -// into single string for "address line 2" -base::string16 CollapseAddress(const base::string16& post_office_box, - const base::string16& neighborhood) { - std::vector accumulator; - if (!post_office_box.empty()) - accumulator.push_back(post_office_box); - if (!neighborhood.empty()) - accumulator.push_back(neighborhood); - - return JoinString(accumulator, base::ASCIIToUTF16(", ")); -} - -} // namespace - -namespace autofill { - -AuxiliaryProfilesAndroid::AuxiliaryProfilesAndroid( - const AuxiliaryProfileLoaderAndroid& profileLoader, - const std::string& app_locale) - : profile_loader_(profileLoader), - app_locale_(app_locale) {} - -AuxiliaryProfilesAndroid::~AuxiliaryProfilesAndroid() { -} - -scoped_ptr AuxiliaryProfilesAndroid::LoadContactsProfile() { - scoped_ptr profile( - new AutofillProfile(kAndroidMeContactA, kAndroidContactsOrigin)); - LoadName(profile.get()); - LoadEmailAddress(profile.get()); - LoadPhoneNumbers(profile.get()); - - // Android user's profile contact does not parse its address - // into constituent parts. Instead we just get a long string blob. - // Disable address population until we implement a way to parse the - // data. - // http://crbug.com/178838 - // LoadAddress(profile.get()); - - return profile.Pass(); -} - -void AuxiliaryProfilesAndroid::LoadAddress(AutofillProfile* profile) { - base::string16 street = profile_loader_.GetStreet(); - base::string16 post_office_box = profile_loader_.GetPostOfficeBox(); - base::string16 neighborhood = profile_loader_.GetNeighborhood(); - base::string16 city = profile_loader_.GetCity(); - base::string16 postal_code = profile_loader_.GetPostalCode(); - base::string16 region = profile_loader_.GetRegion(); - base::string16 country = profile_loader_.GetCountry(); - - base::string16 street2 = CollapseAddress(post_office_box, neighborhood); - - profile->SetRawInfo(ADDRESS_HOME_LINE1, street); - profile->SetRawInfo(ADDRESS_HOME_LINE2, street2); - profile->SetRawInfo(ADDRESS_HOME_CITY, city); - profile->SetRawInfo(ADDRESS_HOME_STATE, region); - profile->SetRawInfo(ADDRESS_HOME_ZIP, postal_code); - profile->SetInfo(AutofillType(ADDRESS_HOME_COUNTRY), country, app_locale_); -} - -void AuxiliaryProfilesAndroid::LoadName(AutofillProfile* profile) { - base::string16 first_name = profile_loader_.GetFirstName(); - base::string16 middle_name = profile_loader_.GetMiddleName(); - base::string16 last_name = profile_loader_.GetLastName(); - - profile->SetRawInfo(NAME_FIRST, first_name); - profile->SetRawInfo(NAME_MIDDLE, middle_name); - profile->SetRawInfo(NAME_LAST, last_name); -} - -void AuxiliaryProfilesAndroid::LoadEmailAddress(AutofillProfile* profile) { - std::vector emails; - profile_loader_.GetEmailAddresses(&emails); - profile->SetRawMultiInfo(EMAIL_ADDRESS, emails); -} - -void AuxiliaryProfilesAndroid::LoadPhoneNumbers(AutofillProfile* profile) { - std::vector phone_numbers; - profile_loader_.GetPhoneNumbers(&phone_numbers); - profile->SetRawMultiInfo(PHONE_HOME_WHOLE_NUMBER, phone_numbers); -} - -} // namespace autofill diff --git a/components/autofill/core/browser/android/auxiliary_profiles_android.h b/components/autofill/core/browser/android/auxiliary_profiles_android.h deleted file mode 100644 index 5f81fd1f6dfb3..0000000000000 --- a/components/autofill/core/browser/android/auxiliary_profiles_android.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILES_ANDROID_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILES_ANDROID_H_ - -#include -#include - -#include "base/memory/scoped_ptr.h" -#include "base/strings/string16.h" -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" - -namespace autofill { - -class AutofillProfile; -class AuxiliaryProfileLoaderAndroid; - - // This class is used to populate an AutofillProfile vector with - // a 'default' auxiliary profile. It depends on an - // AuxiliaryProfileLoaderAndroid object to provide contact information - // that is re-organized into an Autofill profile. -class AuxiliaryProfilesAndroid { - public: - // Takes in an AuxiliaryProfileLoader object which provides contact - // information methods. - AuxiliaryProfilesAndroid( - const autofill::AuxiliaryProfileLoaderAndroid& profile_loader, - const std::string& app_locale); - ~AuxiliaryProfilesAndroid(); - - // Returns autofill profile constructed from profile_loader_. - scoped_ptr LoadContactsProfile(); - - private: - // Inserts contact's address data into profile. - void LoadAddress(AutofillProfile* profile); - // Inserts contact's name data into profile. - void LoadName(AutofillProfile* profile); - // Inserts contact's email address data into profile. - void LoadEmailAddress(AutofillProfile* profile); - // Inserts contact's phone number data into profile. - void LoadPhoneNumbers(AutofillProfile* profile); - - const AuxiliaryProfileLoaderAndroid& profile_loader_; - std::string app_locale_; - - DISALLOW_COPY_AND_ASSIGN(AuxiliaryProfilesAndroid); -}; - -} // namespace - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_AUXILIARY_PROFILES_ANDROID_H_ diff --git a/components/autofill/core/browser/android/component_jni_registrar.cc b/components/autofill/core/browser/android/component_jni_registrar.cc deleted file mode 100644 index f3aac1f02ad03..0000000000000 --- a/components/autofill/core/browser/android/component_jni_registrar.cc +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/core/browser/android/component_jni_registrar.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_registrar.h" -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" - -namespace autofill { - -static base::android::RegistrationMethod kComponentRegisteredMethods[] = { - { "RegisterAuxiliaryProfileLoader", - autofill::RegisterAuxiliaryProfileLoader }, -}; - -bool RegisterAutofillAndroidJni(JNIEnv* env) { - return RegisterNativeMethods(env, - kComponentRegisteredMethods, arraysize(kComponentRegisteredMethods)); -} - -} // namespace autofill diff --git a/components/autofill/core/browser/android/component_jni_registrar.h b/components/autofill/core/browser/android/component_jni_registrar.h deleted file mode 100644 index 6cc0900cfdd51..0000000000000 --- a/components/autofill/core/browser/android/component_jni_registrar.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_COMPONENT_JNI_REGISTRAR_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_COMPONENT_JNI_REGISTRAR_H_ - -#include - -namespace autofill { - -// Register all JNI bindings necessary for the autofill -// component. -bool RegisterAutofillAndroidJni(JNIEnv* env); - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_COMPONENT_JNI_REGISTRAR_H_ diff --git a/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java b/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java deleted file mode 100644 index e83b867b2fee4..0000000000000 --- a/components/autofill/core/browser/android/java/src/org/chromium/components/browser/autofill/PersonalAutofillPopulator.java +++ /dev/null @@ -1,304 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Populates data fields from Android contacts profile API (i.e. "me" contact). - -package org.chromium.components.browser.autofill; - -import android.content.ContentResolver; -import android.content.Context; -import android.content.pm.PackageManager; -import android.database.Cursor; -import android.net.Uri; -import android.provider.ContactsContract; - -import org.chromium.base.CalledByNative; -import org.chromium.base.JNINamespace; - -/** - * Loads user profile information stored under the "Me" contact. - * Requires permissions: READ_CONTACTS and READ_PROFILE. - */ -@JNINamespace("autofill") -public class PersonalAutofillPopulator { - /** - * SQL query definitions for obtaining specific profile information. - */ - private abstract static class ProfileQuery { - Uri profileDataUri = Uri.withAppendedPath( - ContactsContract.Profile.CONTENT_URI, - ContactsContract.Contacts.Data.CONTENT_DIRECTORY - ); - public abstract String[] projection(); - public abstract String mimeType(); - } - - private static class EmailProfileQuery extends ProfileQuery { - private static final int EMAIL_ADDRESS = 0; - - @Override - public String[] projection() { - return new String[] { - ContactsContract.CommonDataKinds.Email.ADDRESS, - }; - } - - @Override - public String mimeType() { - return ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE; - } - } - - private static class PhoneProfileQuery extends ProfileQuery { - private static final int NUMBER = 0; - - @Override - public String[] projection() { - return new String[] { - ContactsContract.CommonDataKinds.Phone.NUMBER, - }; - } - - @Override - public String mimeType() { - return ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE; - } - } - - private static class AddressProfileQuery extends ProfileQuery { - private static final int STREET = 0; - private static final int POBOX = 1; - private static final int NEIGHBORHOOD = 2; - private static final int CITY = 3; - private static final int REGION = 4; - private static final int POSTALCODE = 5; - private static final int COUNTRY = 6; - - @Override - public String[] projection() { - return new String[] { - ContactsContract.CommonDataKinds.StructuredPostal.STREET, - ContactsContract.CommonDataKinds.StructuredPostal.POBOX, - ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD, - ContactsContract.CommonDataKinds.StructuredPostal.CITY, - ContactsContract.CommonDataKinds.StructuredPostal.REGION, - ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, - ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, - }; - } - - @Override - public String mimeType() { - return ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE; - } - } - - private static class NameProfileQuery extends ProfileQuery { - private static final int GIVEN_NAME = 0; - private static final int MIDDLE_NAME = 1; - private static final int FAMILY_NAME = 2; - private static final int SUFFIX = 3; - - @Override - public String[] projection() { - return new String[] { - ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, - ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, - ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, - ContactsContract.CommonDataKinds.StructuredName.SUFFIX - }; - } - - @Override - public String mimeType() { - return ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE; - } - } - - /** - * Takes a query object, transforms into actual query and returns cursor. - * Primary contact values will be first. - */ - private Cursor cursorFromProfileQuery(ProfileQuery query, ContentResolver contentResolver) { - String sortDescriptor = ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"; - return contentResolver.query( - query.profileDataUri, - query.projection(), - ContactsContract.Contacts.Data.MIMETYPE + " = ?", - new String[]{query.mimeType()}, - sortDescriptor - ); - } - // Extracted data variables. - private String[] mEmailAddresses; - private String mGivenName; - private String mMiddleName; - private String mFamilyName; - private String mSuffix; - private String mPobox; - private String mStreet; - private String mNeighborhood; - private String mCity; - private String mRegion; - private String mCountry; - private String mPostalCode; - private String[] mPhoneNumbers; - private boolean mHasPermissions; - - /** - * Constructor - * @param context a valid android context reference - */ - PersonalAutofillPopulator(Context context) { - mHasPermissions = hasPermissions(context); - if (mHasPermissions) { - ContentResolver contentResolver = context.getContentResolver(); - populateName(contentResolver); - populateEmail(contentResolver); - populateAddress(contentResolver); - populatePhone(contentResolver); - } - } - - // Check if the user has granted permissions. - private boolean hasPermissions(Context context) { - String [] permissions = { - "android.permission.READ_CONTACTS", - "android.permission.READ_PROFILE" - }; - for (String permission : permissions) { - int res = context.checkCallingOrSelfPermission(permission); - if (res != PackageManager.PERMISSION_GRANTED) return false; - } - return true; - } - - // Populating data fields. - private void populateName(ContentResolver contentResolver) { - NameProfileQuery nameProfileQuery = new NameProfileQuery(); - Cursor nameCursor = cursorFromProfileQuery(nameProfileQuery, contentResolver); - if (nameCursor.moveToNext()) { - mGivenName = nameCursor.getString(nameProfileQuery.GIVEN_NAME); - mMiddleName = nameCursor.getString(nameProfileQuery.MIDDLE_NAME); - mFamilyName = nameCursor.getString(nameProfileQuery.FAMILY_NAME); - mSuffix = nameCursor.getString(nameProfileQuery.SUFFIX); - } - nameCursor.close(); - } - - private void populateEmail(ContentResolver contentResolver) { - EmailProfileQuery emailProfileQuery = new EmailProfileQuery(); - Cursor emailCursor = cursorFromProfileQuery(emailProfileQuery, contentResolver); - mEmailAddresses = new String[emailCursor.getCount()]; - for (int i = 0; emailCursor.moveToNext(); i++) { - mEmailAddresses[i] = emailCursor.getString(emailProfileQuery.EMAIL_ADDRESS); - } - emailCursor.close(); - } - - private void populateAddress(ContentResolver contentResolver) { - AddressProfileQuery addressProfileQuery = new AddressProfileQuery(); - Cursor addressCursor = cursorFromProfileQuery(addressProfileQuery, contentResolver); - if(addressCursor.moveToNext()) { - mPobox = addressCursor.getString(addressProfileQuery.POBOX); - mStreet = addressCursor.getString(addressProfileQuery.STREET); - mNeighborhood = addressCursor.getString(addressProfileQuery.NEIGHBORHOOD); - mCity = addressCursor.getString(addressProfileQuery.CITY); - mRegion = addressCursor.getString(addressProfileQuery.REGION); - mPostalCode = addressCursor.getString(addressProfileQuery.POSTALCODE); - mCountry = addressCursor.getString(addressProfileQuery.COUNTRY); - } - addressCursor.close(); - } - - private void populatePhone(ContentResolver contentResolver) { - PhoneProfileQuery phoneProfileQuery = new PhoneProfileQuery(); - Cursor phoneCursor = cursorFromProfileQuery(phoneProfileQuery, contentResolver); - mPhoneNumbers = new String[phoneCursor.getCount()]; - for (int i = 0; phoneCursor.moveToNext(); i++) { - mPhoneNumbers[i] = phoneCursor.getString(phoneProfileQuery.NUMBER); - } - phoneCursor.close(); - } - - /** - * Static factory method for instance creation. - * @param context valid Android context. - * @return PersonalAutofillPopulator new instance of PersonalAutofillPopulator. - */ - @CalledByNative - static PersonalAutofillPopulator create(Context context) { - return new PersonalAutofillPopulator(context); - } - - @CalledByNative - private String getFirstName() { - return mGivenName; - } - - @CalledByNative - private String getLastName() { - return mFamilyName; - } - - @CalledByNative - private String getMiddleName() { - return mMiddleName; - } - - @CalledByNative - private String getSuffix() { - return mSuffix; - } - - @CalledByNative - private String[] getEmailAddresses() { - return mEmailAddresses; - } - - @CalledByNative - private String getStreet() { - return mStreet; - } - - @CalledByNative - private String getPobox() { - return mPobox; - } - - @CalledByNative - private String getNeighborhood() { - return mNeighborhood; - } - - @CalledByNative - private String getCity() { - return mCity; - } - - @CalledByNative - private String getRegion() { - return mRegion; - } - - @CalledByNative - private String getPostalCode() { - return mPostalCode; - } - - @CalledByNative - private String getCountry() { - return mCountry; - } - - @CalledByNative - private String[] getPhoneNumbers() { - return mPhoneNumbers; - } - - @CalledByNative - private boolean getHasPermissions() { - return mHasPermissions; - } -} diff --git a/components/autofill/core/browser/android/personal_data_manager_android.cc b/components/autofill/core/browser/android/personal_data_manager_android.cc deleted file mode 100644 index 55de240fb6b38..0000000000000 --- a/components/autofill/core/browser/android/personal_data_manager_android.cc +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Populates default autofill profile from user's own Android contact. - -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" -#include "components/autofill/core/browser/android/auxiliary_profiles_android.h" -#include "components/autofill/core/browser/personal_data_manager.h" - -namespace autofill { - -void PersonalDataManager::LoadAuxiliaryProfiles(bool record_metrics) const { - auxiliary_profiles_.clear(); - autofill::AuxiliaryProfileLoaderAndroid profile_loader; - profile_loader.Init( - base::android::AttachCurrentThread(), - base::android::GetApplicationContext()); - if (profile_loader.GetHasPermissions()) { - autofill::AuxiliaryProfilesAndroid impl(profile_loader, app_locale_); - auxiliary_profiles_.push_back(impl.LoadContactsProfile().release()); - } -} - -} // namespace autofill diff --git a/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc b/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc deleted file mode 100644 index ced72c8a60320..0000000000000 --- a/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.cc +++ /dev/null @@ -1,132 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h" - -namespace autofill { - -TestAuxiliaryProfileLoader::TestAuxiliaryProfileLoader() { -} - -TestAuxiliaryProfileLoader::~TestAuxiliaryProfileLoader() { -} - -bool TestAuxiliaryProfileLoader::GetHasPermissions() const { - return true; -} - -base::string16 TestAuxiliaryProfileLoader::GetFirstName() const { - return first_name_; -} - -base::string16 TestAuxiliaryProfileLoader::GetMiddleName() const { - return middle_name_; -} - -base::string16 TestAuxiliaryProfileLoader::GetLastName() const { - return last_name_; -} - -base::string16 TestAuxiliaryProfileLoader::GetSuffix() const { - return suffix_; -} - -base::string16 TestAuxiliaryProfileLoader::GetStreet() const { - return street_; -} - -base::string16 TestAuxiliaryProfileLoader::GetPostOfficeBox() const { - return post_office_box_; -} - -base::string16 TestAuxiliaryProfileLoader::GetCity() const { - return city_; -} - -base::string16 TestAuxiliaryProfileLoader::GetNeighborhood() const { - return neighborhood_; -} - -base::string16 TestAuxiliaryProfileLoader::GetRegion() const { - return region_; -} - -base::string16 TestAuxiliaryProfileLoader::GetPostalCode() const { - return postal_code_; -} - -base::string16 TestAuxiliaryProfileLoader::GetCountry() const { - return country_; -} - -void TestAuxiliaryProfileLoader::GetEmailAddresses( - std::vector* email_addresses) const { - *email_addresses = email_addresses_; -} - -void TestAuxiliaryProfileLoader::GetPhoneNumbers( - std::vector* phone_numbers) const { - *phone_numbers = phone_numbers_; -} - -void TestAuxiliaryProfileLoader::SetFirstName( - const base::string16& first_name) { - first_name_ = first_name; -} - -void TestAuxiliaryProfileLoader::SetMiddleName( - const base::string16& middle_name) { - middle_name_ = middle_name; -} - -void TestAuxiliaryProfileLoader::SetLastName(const base::string16& last_name) { - last_name_ = last_name; -} - -void TestAuxiliaryProfileLoader::SetSuffix(const base::string16& suffix) { - suffix_ = suffix; -} - -void TestAuxiliaryProfileLoader::SetStreet(const base::string16& street) { - street_ = street; -} - -void TestAuxiliaryProfileLoader::SetPostOfficeBox( - const base::string16& post_office_box) { - post_office_box_ = post_office_box; -} - -void TestAuxiliaryProfileLoader::SetNeighborhood( - const base::string16& neighborhood) { - neighborhood_ = neighborhood; -} - -void TestAuxiliaryProfileLoader::SetRegion(const base::string16& region) { - region_ = region; -} - -void TestAuxiliaryProfileLoader::SetCity(const base::string16& city) { - city_ = city; -} - -void TestAuxiliaryProfileLoader::SetPostalCode( - const base::string16& postal_code) { - postal_code_ = postal_code; -} - -void TestAuxiliaryProfileLoader::SetCountry(const base::string16& country) { - country_ = country; -} - -void TestAuxiliaryProfileLoader::SetEmailAddresses( - const std::vector& addresses) { - email_addresses_ = addresses; -} - -void TestAuxiliaryProfileLoader::SetPhoneNumbers( - const std::vector& phone_numbers) { - phone_numbers_ = phone_numbers; -} - -} // namespace autofill diff --git a/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h b/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h deleted file mode 100644 index 34d424c9adfd4..0000000000000 --- a/components/autofill/core/browser/android/test_auxiliary_profile_loader_android.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_TEST_AUXILIARY_PROFILE_LOADER_ANDROID_H_ -#define COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_TEST_AUXILIARY_PROFILE_LOADER_ANDROID_H_ - -#include "base/compiler_specific.h" -#include "components/autofill/core/browser/android/auxiliary_profile_loader_android.h" - -namespace autofill { - -class TestAuxiliaryProfileLoader - : public autofill::AuxiliaryProfileLoaderAndroid { - // Mock object for unit testing |AuxiliaryProfilesAndroid| - public: - TestAuxiliaryProfileLoader(); - virtual ~TestAuxiliaryProfileLoader(); - - virtual bool GetHasPermissions() const OVERRIDE; - - virtual base::string16 GetFirstName() const OVERRIDE; - virtual base::string16 GetMiddleName() const OVERRIDE; - virtual base::string16 GetLastName() const OVERRIDE; - virtual base::string16 GetSuffix() const OVERRIDE; - - virtual base::string16 GetStreet() const OVERRIDE; - virtual base::string16 GetCity() const OVERRIDE; - virtual base::string16 GetNeighborhood() const OVERRIDE; - virtual base::string16 GetPostalCode() const OVERRIDE; - virtual base::string16 GetRegion() const OVERRIDE; - virtual base::string16 GetPostOfficeBox() const OVERRIDE; - virtual base::string16 GetCountry() const OVERRIDE; - - virtual void GetEmailAddresses( - std::vector* email_addresses) const OVERRIDE; - virtual void GetPhoneNumbers( - std::vector* phone_numbers) const OVERRIDE; - - void SetFirstName(const base::string16& first_name); - void SetMiddleName(const base::string16& middle_name); - void SetLastName(const base::string16& last_name); - void SetSuffix(const base::string16& suffix); - - void SetStreet(const base::string16& street); - void SetPostOfficeBox(const base::string16& post_office_box); - void SetNeighborhood(const base::string16& neighborhood); - void SetRegion(const base::string16& region); - void SetCity(const base::string16& city); - void SetPostalCode(const base::string16& postal_code); - void SetCountry(const base::string16& country); - - void SetEmailAddresses(const std::vector& email_addresses); - void SetPhoneNumbers(const std::vector& phone_numbers); - - private: - base::string16 street_; - base::string16 post_office_box_; - base::string16 neighborhood_; - base::string16 region_; - base::string16 city_; - base::string16 postal_code_; - base::string16 country_; - base::string16 first_name_; - base::string16 middle_name_; - base::string16 last_name_; - base::string16 suffix_; - std::vector email_addresses_; - std::vector phone_numbers_; -}; - -} // namespace autofill - -#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_ANDROID_TEST_AUXILIARY_PROFILE_LOADER_ANDROID_H_ diff --git a/components/autofill/core/browser/autofill_external_delegate.cc b/components/autofill/core/browser/autofill_external_delegate.cc index 57777c8e37c8e..80ad335a44a0e 100644 --- a/components/autofill/core/browser/autofill_external_delegate.cc +++ b/components/autofill/core/browser/autofill_external_delegate.cc @@ -117,6 +117,8 @@ void AutofillExternalDelegate::OnSuggestionsReturned( // updated to match. InsertDataListValues(&values, &labels, &icons, &ids); +// Temporarily disabled. See http://crbug.com/408695 +#if 0 #if defined(OS_MACOSX) && !defined(OS_IOS) if (values.empty() && manager_->ShouldShowAccessAddressBookSuggestion(query_form_, @@ -130,6 +132,7 @@ void AutofillExternalDelegate::OnSuggestionsReturned( EmitHistogram(SHOWED_ACCESS_ADDRESS_BOOK_ENTRY); } #endif // defined(OS_MACOSX) && !defined(OS_IOS) +#endif if (values.empty()) { // No suggestions, any popup currently showing is obsolete. diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index 77fc9463a03c6..b8d824b71a5e2 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc @@ -138,20 +138,15 @@ void DeterminePossibleFieldTypesForUpload( AutofillField* field = submitted_form->field(i); ServerFieldTypeSet matching_types; - // If it's a password field, set the type directly. - if (field->form_control_type == "password") { - matching_types.insert(PASSWORD); - } else { - base::string16 value; - base::TrimWhitespace(field->value, base::TRIM_ALL, &value); - for (std::vector::const_iterator it = profiles.begin(); - it != profiles.end(); ++it) { - it->GetMatchingTypes(value, app_locale, &matching_types); - } - for (std::vector::const_iterator it = credit_cards.begin(); - it != credit_cards.end(); ++it) { - it->GetMatchingTypes(value, app_locale, &matching_types); - } + base::string16 value; + base::TrimWhitespace(field->value, base::TRIM_ALL, &value); + for (std::vector::const_iterator it = profiles.begin(); + it != profiles.end(); ++it) { + it->GetMatchingTypes(value, app_locale, &matching_types); + } + for (std::vector::const_iterator it = credit_cards.begin(); + it != credit_cards.end(); ++it) { + it->GetMatchingTypes(value, app_locale, &matching_types); } if (matching_types.empty()) @@ -199,17 +194,17 @@ void AutofillManager::RegisterProfilePrefs( prefs::kAutofillEnabled, true, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); -#if defined(OS_MACOSX) || defined(OS_ANDROID) +#if defined(OS_MACOSX) registry->RegisterBooleanPref( prefs::kAutofillAuxiliaryProfilesEnabled, true, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); -#else // defined(OS_MACOSX) || defined(OS_ANDROID) +#else // defined(OS_MACOSX) registry->RegisterBooleanPref( prefs::kAutofillAuxiliaryProfilesEnabled, false, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -#endif // defined(OS_MACOSX) || defined(OS_ANDROID) +#endif // defined(OS_MACOSX) #if defined(OS_MACOSX) registry->RegisterBooleanPref( prefs::kAutofillMacAddressBookQueried, @@ -813,17 +808,14 @@ void AutofillManager::UploadFormData(const FormStructure& submitted_form) { ServerFieldTypeSet non_empty_types; personal_data_->GetNonEmptyTypes(&non_empty_types); - // Always add PASSWORD to |non_empty_types| so that if |submitted_form| - // contains a password field it will be uploaded to the server. If - // |submitted_form| doesn't contain a password field, there is no side - // effect from adding PASSWORD to |non_empty_types|. - non_empty_types.insert(PASSWORD); download_manager_->StartUploadRequest(submitted_form, was_autofilled, non_empty_types); } -bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) { +bool AutofillManager::UploadPasswordForm( + const FormData& form, + const ServerFieldType& password_type) { FormStructure form_structure(form); if (!ShouldUploadForm(form_structure)) @@ -832,8 +824,6 @@ bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) { if (!form_structure.ShouldBeCrowdsourced()) return false; - // TODO(gcasto): Check that PasswordGeneration is enabled? - // Find the first password field to label. We don't try to label anything // else. bool found_password_field = false; @@ -842,7 +832,7 @@ bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) { ServerFieldTypeSet types; if (!found_password_field && field->form_control_type == "password") { - types.insert(ACCOUNT_CREATION_PASSWORD); + types.insert(password_type); found_password_field = true; } else { types.insert(UNKNOWN_TYPE); @@ -853,7 +843,7 @@ bool AutofillManager::UploadPasswordGenerationForm(const FormData& form) { // Only one field type should be present. ServerFieldTypeSet available_field_types; - available_field_types.insert(ACCOUNT_CREATION_PASSWORD); + available_field_types.insert(password_type); // Force uploading as these events are relatively rare and we want to make // sure to receive them. It also makes testing easier if these requests diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index 72454ed22bb4a..d2ff66068feef 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h @@ -156,15 +156,17 @@ class AutofillManager : public AutofillDownloadManager::Observer { void OnSetDataList(const std::vector& values, const std::vector& labels); - // Try and upload |form|. This differs from OnFormSubmitted() in a few ways. + // Try to label password fields and upload |form|. This differs from + // OnFormSubmitted() in a few ways. // - This function will only label the first field - // as ACCOUNT_CREATION_PASSWORD. Other fields will stay unlabeled, as they + // as |password_type|. Other fields will stay unlabeled, as they // should have been labeled during the upload for OnFormSubmitted(). // - This function does not assume that |form| is being uploaded during // the same browsing session as it was originally submitted (as we may // not have the necessary information to classify the form at that time) // so it bypasses the cache and doesn't log the same quality UMA metrics. - bool UploadPasswordGenerationForm(const FormData& form); + bool UploadPasswordForm(const FormData& form, + const ServerFieldType& pasword_type); // Resets cache. virtual void Reset(); diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 31a70df5e52b6..861ca23e65597 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc @@ -865,8 +865,8 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestionsEmptyValue) { ASCIIToUTF16("************3456"), ASCIIToUTF16("************8765") }; - base::string16 expected_labels[] = { ASCIIToUTF16("*3456"), - ASCIIToUTF16("*8765")}; + base::string16 expected_labels[] = { ASCIIToUTF16("04/12"), + ASCIIToUTF16("10/14")}; base::string16 expected_icons[] = { ASCIIToUTF16(kVisaCard), ASCIIToUTF16(kMasterCard) @@ -899,7 +899,7 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestionsMatchCharacter) { // Test that we sent the right values to the external delegate. base::string16 expected_values[] = {ASCIIToUTF16("************3456")}; - base::string16 expected_labels[] = {ASCIIToUTF16("*3456")}; + base::string16 expected_labels[] = {ASCIIToUTF16("04/12")}; base::string16 expected_icons[] = {ASCIIToUTF16(kVisaCard)}; int expected_unique_ids[] = {autofill_manager_->GetPackedCreditCardID(4)}; external_delegate_->CheckSuggestions( @@ -1008,7 +1008,7 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestionsRepeatedObfuscatedNumber) { CreditCard* credit_card = new CreditCard; test::SetCreditCardInfo(credit_card, "Elvis Presley", "5231567890123456", // Mastercard - "04", "2012"); + "05", "2012"); credit_card->set_guid("00000000-0000-0000-0000-000000000007"); autofill_manager_->AddCreditCard(credit_card); @@ -1032,9 +1032,9 @@ TEST_F(AutofillManagerTest, GetCreditCardSuggestionsRepeatedObfuscatedNumber) { ASCIIToUTF16("************3456") }; base::string16 expected_labels[] = { - ASCIIToUTF16("*3456"), - ASCIIToUTF16("*8765"), - ASCIIToUTF16("*3456"), + ASCIIToUTF16("04/12"), + ASCIIToUTF16("10/14"), + ASCIIToUTF16("05/12"), }; base::string16 expected_icons[] = { ASCIIToUTF16(kVisaCard), @@ -1095,8 +1095,8 @@ TEST_F(AutofillManagerTest, GetAddressAndCreditCardSuggestions) { ASCIIToUTF16("************3456"), ASCIIToUTF16("************8765") }; - base::string16 expected_labels2[] = { ASCIIToUTF16("*3456"), - ASCIIToUTF16("*8765")}; + base::string16 expected_labels2[] = { ASCIIToUTF16("04/12"), + ASCIIToUTF16("10/14")}; base::string16 expected_icons2[] = { ASCIIToUTF16(kVisaCard), ASCIIToUTF16(kMasterCard) @@ -2425,10 +2425,9 @@ TEST_F(AutofillManagerTest, FormSubmittedWithDefaultValues) { // thing on all platforms. TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) { PrefService* prefs = autofill_client_.GetPrefs(); -#if defined(OS_MACOSX) || defined(OS_ANDROID) - // Auxiliary profiles is implemented on Mac and Android only. +#if defined(OS_MACOSX) + // Auxiliary profiles is implemented on Mac only. // OSX: This preference exists for legacy reasons. It is no longer used. - // Android: enables integration with user's contact profile. ASSERT_TRUE( prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled)); prefs->SetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled, @@ -2443,7 +2442,7 @@ TEST_F(AutofillManagerTest, AuxiliaryProfilesReset) { prefs->ClearPref(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled); ASSERT_FALSE( prefs->GetBoolean(::autofill::prefs::kAutofillAuxiliaryProfilesEnabled)); -#endif // defined(OS_MACOSX) || defined(OS_ANDROID) +#endif // defined(OS_MACOSX) } TEST_F(AutofillManagerTest, DeterminePossibleFieldTypesForUpload) { diff --git a/components/autofill/core/browser/personal_data_manager.cc b/components/autofill/core/browser/personal_data_manager.cc index 5aa966e539546..dd5baecd6b0ae 100644 --- a/components/autofill/core/browser/personal_data_manager.cc +++ b/components/autofill/core/browser/personal_data_manager.cc @@ -671,11 +671,18 @@ void PersonalDataManager::GetCreditCardSuggestions( if (type.GetStorableType() == CREDIT_CARD_NUMBER) creditcard_field_value = credit_card->ObfuscatedNumber(); + // If the value is the card number, the label is the expiration date. + // Otherwise the label is the card number, or if that is empty the + // cardholder name. The label should never repeat the value. base::string16 label; - if (credit_card->number().empty()) { - // If there is no CC number, return name to show something. - label = - credit_card->GetInfo(AutofillType(CREDIT_CARD_NAME), app_locale_); + if (type.GetStorableType() == CREDIT_CARD_NUMBER) { + label = credit_card->GetInfo( + AutofillType(CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR), app_locale_); + } else if (credit_card->number().empty()) { + if (type.GetStorableType() != CREDIT_CARD_NAME) { + label = + credit_card->GetInfo(AutofillType(CREDIT_CARD_NAME), app_locale_); + } } else { label = kCreditCardPrefix; label.append(credit_card->LastFourDigits()); @@ -919,9 +926,9 @@ void PersonalDataManager::LoadProfiles() { pending_profiles_query_ = database_->GetAutofillProfiles(this); } -// Win, Linux, and iOS implementations do nothing. Mac and Android -// implementations fill in the contents of |auxiliary_profiles_|. -#if defined(OS_IOS) || (!defined(OS_MACOSX) && !defined(OS_ANDROID)) +// Win, Linux, Android and iOS implementations do nothing. Mac implementation +// fills in the contents of |auxiliary_profiles_|. +#if defined(OS_IOS) || !defined(OS_MACOSX) void PersonalDataManager::LoadAuxiliaryProfiles(bool record_metrics) const { } #endif diff --git a/components/autofill/core/browser/personal_data_manager_unittest.cc b/components/autofill/core/browser/personal_data_manager_unittest.cc index 9568cb52453d2..4cbf034a14800 100644 --- a/components/autofill/core/browser/personal_data_manager_unittest.cc +++ b/components/autofill/core/browser/personal_data_manager_unittest.cc @@ -2615,4 +2615,64 @@ TEST_F(PersonalDataManagerTest, GetProfileSuggestions) { base::UTF8ToUTF16("123 Zoo St., Second Line, Third line, unit 5")); } +TEST_F(PersonalDataManagerTest, GetCreditCardSuggestions) { + CreditCard credit_card0(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card0, + "Clyde Barrow", "347666888555" /* American Express */, "04", "2015"); + personal_data_->AddCreditCard(credit_card0); + + CreditCard credit_card1(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card1, "John Dillinger", "", "01", "2010"); + personal_data_->AddCreditCard(credit_card1); + + CreditCard credit_card2(base::GenerateGUID(), "https://www.example.com"); + test::SetCreditCardInfo(&credit_card2, + "Bonnie Parker", "518765432109" /* Mastercard */, "", ""); + personal_data_->AddCreditCard(credit_card2); + + EXPECT_CALL(personal_data_observer_, OnPersonalDataChanged()) + .WillOnce(QuitMainMessageLoop()); + base::MessageLoop::current()->Run(); + + // Sublabel is card number when filling name. + std::vector values; + std::vector labels; + std::vector icons; + std::vector guid_pairs; + personal_data_->GetCreditCardSuggestions( + AutofillType(CREDIT_CARD_NAME), + base::string16(), + &values, + &labels, + &icons, + &guid_pairs); + ASSERT_EQ(3U, values.size()); + ASSERT_EQ(values.size(), labels.size()); + EXPECT_EQ(ASCIIToUTF16("Clyde Barrow"), values[0]); + EXPECT_EQ(ASCIIToUTF16("*8555"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("John Dillinger"), values[1]); + EXPECT_EQ(base::string16(), labels[1]); + EXPECT_EQ(ASCIIToUTF16("Bonnie Parker"), values[2]); + EXPECT_EQ(ASCIIToUTF16("*2109"), labels[2]); + + // Sublabel is expiration date when filling card number. + values.clear(); + labels.clear(); + icons.clear(); + guid_pairs.clear(); + personal_data_->GetCreditCardSuggestions( + AutofillType(CREDIT_CARD_NUMBER), + base::string16(), + &values, + &labels, + &icons, + &guid_pairs); + ASSERT_EQ(2U, values.size()); + ASSERT_EQ(values.size(), labels.size()); + EXPECT_EQ(ASCIIToUTF16("********8555"), values[0]); + EXPECT_EQ(ASCIIToUTF16("04/15"), labels[0]); + EXPECT_EQ(ASCIIToUTF16("********2109"), values[1]); + EXPECT_EQ(base::string16(), labels[1]); +} + } // namespace autofill diff --git a/components/autofill/core/common/form_data.cc b/components/autofill/core/common/form_data.cc index 83d8173ace00f..b720ae402ea4b 100644 --- a/components/autofill/core/common/form_data.cc +++ b/components/autofill/core/common/form_data.cc @@ -13,7 +13,7 @@ namespace autofill { namespace { -const int kPickleVersion = 1; +const int kPickleVersion = 2; bool ReadGURL(PickleIterator* iter, GURL* url) { std::string spec; @@ -48,6 +48,11 @@ bool DeserializeFormFieldDataVector(PickleIterator* iter, return true; } +void LogDeserializationError(int version) { + DVLOG(1) << "Could not deserialize version " << version + << " FormData from pickle."; +} + } // namespace FormData::FormData() @@ -113,24 +118,36 @@ void SerializeFormData(const FormData& form_data, Pickle* pickle) { bool DeserializeFormData(PickleIterator* iter, FormData* form_data) { int version; if (!iter->ReadInt(&version)) { - LOG(ERROR) << "Bad pickle of FormData, no version present"; + DVLOG(1) << "Bad pickle of FormData, no version present"; return false; } switch (version) { case 1: { + base::string16 method; if (!iter->ReadString16(&form_data->name) || + !iter->ReadString16(&method) || !ReadGURL(iter, &form_data->origin) || !ReadGURL(iter, &form_data->action) || !iter->ReadBool(&form_data->user_submitted) || !DeserializeFormFieldDataVector(iter, &form_data->fields)) { - LOG(ERROR) << "Could not deserialize FormData from pickle"; + LogDeserializationError(version); return false; } break; } + case 2: + if (!iter->ReadString16(&form_data->name) || + !ReadGURL(iter, &form_data->origin) || + !ReadGURL(iter, &form_data->action) || + !iter->ReadBool(&form_data->user_submitted) || + !DeserializeFormFieldDataVector(iter, &form_data->fields)) { + LogDeserializationError(version); + return false; + } + break; default: { - LOG(ERROR) << "Unknown FormData pickle version " << version; + DVLOG(1) << "Unknown FormData pickle version " << version; return false; } } diff --git a/components/autofill/core/common/form_data_unittest.cc b/components/autofill/core/common/form_data_unittest.cc index 73d825696dd18..4f57129e682f6 100644 --- a/components/autofill/core/common/form_data_unittest.cc +++ b/components/autofill/core/common/form_data_unittest.cc @@ -6,8 +6,45 @@ #include "base/pickle.h" #include "base/strings/utf_string_conversions.h" +#include "components/autofill/core/common/form_field_data.h" #include "testing/gtest/include/gtest/gtest.h" +namespace { + +// This function serializes the form data into the pickle in version one format. +// It should always be possible to deserialize it using DeserializeFormData(), +// even when version changes. See kPickleVersion in form_data.cc. +void SerializeInVersion1Format(const autofill::FormData& form_data, + Pickle* pickle) { + pickle->WriteInt(1); + pickle->WriteString16(form_data.name); + base::string16 method(base::ASCIIToUTF16("POST")); + pickle->WriteString16(method); + pickle->WriteString(form_data.origin.spec()); + pickle->WriteString(form_data.action.spec()); + pickle->WriteBool(form_data.user_submitted); + pickle->WriteInt(static_cast(form_data.fields.size())); + for (size_t i = 0; i < form_data.fields.size(); ++i) { + SerializeFormFieldData(form_data.fields[i], pickle); + } +} + +// This function serializes the form data into the pickle in incorrect format +// (no version number). +void SerializeIncorrectFormat(const autofill::FormData& form_data, + Pickle* pickle) { + pickle->WriteString16(form_data.name); + pickle->WriteString(form_data.origin.spec()); + pickle->WriteString(form_data.action.spec()); + pickle->WriteBool(form_data.user_submitted); + pickle->WriteInt(static_cast(form_data.fields.size())); + for (size_t i = 0; i < form_data.fields.size(); ++i) { + SerializeFormFieldData(form_data.fields[i], pickle); + } +} + +} + namespace autofill { TEST(FormDataTest, SerializeAndDeserialize) { @@ -53,4 +90,88 @@ TEST(FormDataTest, SerializeAndDeserialize) { EXPECT_EQ(actual, data); } +TEST(FormDataTest, Serialize_v1_Deserialize_vCurrent) { + FormData data; + data.name = base::ASCIIToUTF16("name"); + data.origin = GURL("origin"); + data.action = GURL("action"); + data.user_submitted = true; + + FormFieldData field_data; + field_data.label = base::ASCIIToUTF16("label"); + field_data.name = base::ASCIIToUTF16("name"); + field_data.value = base::ASCIIToUTF16("value"); + field_data.form_control_type = "password"; + field_data.autocomplete_attribute = "off"; + field_data.max_length = 200; + field_data.is_autofilled = true; + field_data.is_checked = true; + field_data.is_checkable = true; + field_data.is_focusable = true; + field_data.should_autocomplete = false; + field_data.text_direction = base::i18n::RIGHT_TO_LEFT; + field_data.option_values.push_back(base::ASCIIToUTF16("First")); + field_data.option_values.push_back(base::ASCIIToUTF16("Second")); + field_data.option_contents.push_back(base::ASCIIToUTF16("First")); + field_data.option_contents.push_back(base::ASCIIToUTF16("Second")); + + data.fields.push_back(field_data); + + // Change a few fields. + field_data.max_length = 150; + field_data.option_values.push_back(base::ASCIIToUTF16("Third")); + field_data.option_contents.push_back(base::ASCIIToUTF16("Third")); + data.fields.push_back(field_data); + + Pickle pickle; + SerializeInVersion1Format(data, &pickle); + + PickleIterator iter(pickle); + FormData actual; + EXPECT_TRUE(DeserializeFormData(&iter, &actual)); + + EXPECT_EQ(actual, data); +} + +TEST(FormDataTest, SerializeIncorrectFormatAndDeserialize) { + FormData data; + data.name = base::ASCIIToUTF16("name"); + data.origin = GURL("origin"); + data.action = GURL("action"); + data.user_submitted = true; + + FormFieldData field_data; + field_data.label = base::ASCIIToUTF16("label"); + field_data.name = base::ASCIIToUTF16("name"); + field_data.value = base::ASCIIToUTF16("value"); + field_data.form_control_type = "password"; + field_data.autocomplete_attribute = "off"; + field_data.max_length = 200; + field_data.is_autofilled = true; + field_data.is_checked = true; + field_data.is_checkable = true; + field_data.is_focusable = true; + field_data.should_autocomplete = false; + field_data.text_direction = base::i18n::RIGHT_TO_LEFT; + field_data.option_values.push_back(base::ASCIIToUTF16("First")); + field_data.option_values.push_back(base::ASCIIToUTF16("Second")); + field_data.option_contents.push_back(base::ASCIIToUTF16("First")); + field_data.option_contents.push_back(base::ASCIIToUTF16("Second")); + + data.fields.push_back(field_data); + + // Change a few fields. + field_data.max_length = 150; + field_data.option_values.push_back(base::ASCIIToUTF16("Third")); + field_data.option_contents.push_back(base::ASCIIToUTF16("Third")); + data.fields.push_back(field_data); + + Pickle pickle; + SerializeIncorrectFormat(data, &pickle); + + PickleIterator iter(pickle); + FormData actual; + EXPECT_FALSE(DeserializeFormData(&iter, &actual)); +} + } // namespace autofill diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp index 9e1db3f2910ad..5a3fd5ed13d91 100644 --- a/components/autofill_strings.grdp +++ b/components/autofill_strings.grdp @@ -99,9 +99,16 @@ Privacy Policy
    - - Chrome Autofill settings - + + + Chrome Autofill settings... + + + + + Chromium Autofill settings... + + This type of card is not supported by Google Wallet. Please select a different card. diff --git a/components/bookmarks.gypi b/components/bookmarks.gypi index c88c26e504324..9d70d3350c9a9 100644 --- a/components/bookmarks.gypi +++ b/components/bookmarks.gypi @@ -57,6 +57,20 @@ 'bookmarks/browser/scoped_group_bookmark_actions.cc', 'bookmarks/browser/scoped_group_bookmark_actions.h', ], + 'conditions': [ + ['OS == "android"', { + 'dependencies': [ + 'bookmarks_jni_headers', + ], + 'sources' : [ + 'bookmarks/common/android/bookmark_id.cc', + 'bookmarks/common/android/bookmark_id.h', + 'bookmarks/common/android/bookmark_type_list.h', + 'bookmarks/common/android/component_jni_registrar.cc', + 'bookmarks/common/android/component_jni_registrar.h', + ], + }], + ], }, { 'target_name': 'bookmarks_common', @@ -104,4 +118,45 @@ ], }, ], + 'conditions' : [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'bookmarks_java', + 'type': 'none', + 'dependencies': [ + '../base/base.gyp:base_java', + 'bookmark_type_java', + ], + 'variables': { + 'java_in_dir': 'bookmarks/common/android/java', + }, + 'includes': [ '../build/java.gypi' ], + }, + { + 'target_name': 'bookmarks_jni_headers', + 'type': 'none', + 'sources': [ + 'bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkId.java', + ], + 'variables': { + 'jni_gen_package': 'components/bookmarks', + }, + 'includes': [ '../build/jni_generator.gypi' ], + }, + { + 'target_name': 'bookmark_type_java', + 'type': 'none', + 'sources': [ + 'bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkType.template', + ], + 'variables': { + 'package_name': 'org/chromium/components/bookmarks', + 'template_deps': ['bookmarks/common/android/bookmark_type_list.h'], + }, + 'includes': [ '../build/android/java_cpp_template.gypi' ], + }, + ], + }] + ], } diff --git a/components/bookmarks/DEPS b/components/bookmarks/DEPS index f5adbc8a301be..4aefd683a36b0 100644 --- a/components/bookmarks/DEPS +++ b/components/bookmarks/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+components/query_parser", "+components/startup_metric_utils", "+grit/components_strings.h", + "+jni", "+net/base", "+ui", ] diff --git a/components/bookmarks/browser/bookmark_utils_unittest.cc b/components/bookmarks/browser/bookmark_utils_unittest.cc index a5009d7c993c1..68e084ff072bb 100644 --- a/components/bookmarks/browser/bookmark_utils_unittest.cc +++ b/components/bookmarks/browser/bookmark_utils_unittest.cc @@ -322,7 +322,13 @@ TEST_F(BookmarkUtilsTest, CopyPasteMetaInfo) { EXPECT_EQ("someothervalue", value); } -TEST_F(BookmarkUtilsTest, CutToClipboard) { +#if defined(OS_LINUX) || defined(OS_MACOSX) +// http://crbug.com/396472 +#define MAYBE_CutToClipboard DISABLED_CutToClipboard +#else +#define MAYBE_CutToClipboard CutToClipboard +#endif +TEST_F(BookmarkUtilsTest, MAYBE_CutToClipboard) { test::TestBookmarkClient client; scoped_ptr model(client.CreateModel(false)); model->AddObserver(this); diff --git a/components/bookmarks/common/android/OWNERS b/components/bookmarks/common/android/OWNERS new file mode 100644 index 0000000000000..3df886e30ca65 --- /dev/null +++ b/components/bookmarks/common/android/OWNERS @@ -0,0 +1,3 @@ +yfriedman@chromium.org +tedchoc@chromium.org +kkimlabs@chromium.org diff --git a/components/bookmarks/common/android/bookmark_id.cc b/components/bookmarks/common/android/bookmark_id.cc new file mode 100644 index 0000000000000..b3c5a9b4261b2 --- /dev/null +++ b/components/bookmarks/common/android/bookmark_id.cc @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/bookmarks/common/android/bookmark_id.h" + +#include "jni/BookmarkId_jni.h" + +namespace bookmarks { +namespace android { + +long JavaBookmarkIdGetId(JNIEnv* env, jobject obj) { + return Java_BookmarkId_getId(env, obj); +} + +int JavaBookmarkIdGetType(JNIEnv* env, jobject obj) { + return Java_BookmarkId_getType(env, obj); +} + +bool RegisterBookmarkId(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace bookmarks diff --git a/components/bookmarks/common/android/bookmark_id.h b/components/bookmarks/common/android/bookmark_id.h new file mode 100644 index 0000000000000..61fd7a8c42429 --- /dev/null +++ b/components/bookmarks/common/android/bookmark_id.h @@ -0,0 +1,24 @@ +// 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_BOOKMARKS_COMMON_ANDROID_BOOKMARK_ID_H_ +#define COMPONENTS_BOOKMARKS_COMMON_ANDROID_BOOKMARK_ID_H_ + +#include + +namespace bookmarks { +namespace android { + +// See BookmarkId#getId +long JavaBookmarkIdGetId(JNIEnv* env, jobject obj); + +// See BookmarkId#getType +int JavaBookmarkIdGetType(JNIEnv* env, jobject obj); + +bool RegisterBookmarkId(JNIEnv* env); + +} // namespace android +} // namespace bookmarks + +#endif // COMPONENTS_BOOKMARKS_COMMON_ANDROID_BOOKMARK_ID_H_ diff --git a/components/bookmarks/common/android/bookmark_type.h b/components/bookmarks/common/android/bookmark_type.h new file mode 100644 index 0000000000000..0c4c3e2af6823 --- /dev/null +++ b/components/bookmarks/common/android/bookmark_type.h @@ -0,0 +1,18 @@ +// 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_BOOKMARKS_COMMON_ANDROID_BOOKMARK_TYPE_H_ +#define COMPONENTS_BOOKMARKS_COMMON_ANDROID_BOOKMARK_TYPE_H_ + +namespace bookmarks { + +enum BookmarkType { +#define DEFINE_BOOKMARK_TYPE(name, value) name = value, +#include "components/bookmarks/common/android/bookmark_type_list.h" +#undef DEFINE_BOOKMARK_TYPE +}; + +} + +#endif // COMPONENTS_BOOKMARKS_COMMON_ANDROID_BOOKMARK_TYPE_H_ diff --git a/components/bookmarks/common/android/bookmark_type_list.h b/components/bookmarks/common/android/bookmark_type_list.h new file mode 100644 index 0000000000000..e0a65948d0258 --- /dev/null +++ b/components/bookmarks/common/android/bookmark_type_list.h @@ -0,0 +1,13 @@ +// 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. + +// This file intentionally does not have header guards, it's included +// inside a macro to generate enum values. + +#ifndef DEFINE_BOOKMARK_TYPE +#error "DEFINE_BOOKMARK_TYPE should be defined before including this file" +#endif + +DEFINE_BOOKMARK_TYPE(NORMAL, 0) +DEFINE_BOOKMARK_TYPE(PARTNER, 1) diff --git a/components/bookmarks/common/android/component_jni_registrar.cc b/components/bookmarks/common/android/component_jni_registrar.cc new file mode 100644 index 0000000000000..56a924aa6ced3 --- /dev/null +++ b/components/bookmarks/common/android/component_jni_registrar.cc @@ -0,0 +1,26 @@ +// 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/bookmarks/common/android/component_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "components/bookmarks/common/android/bookmark_id.h" + +namespace bookmarks { +namespace android { + +static base::android::RegistrationMethod kBookmarksRegisteredMethods[] = { + {"BookmarkId", RegisterBookmarkId}, +}; + +bool RegisterBookmarks(JNIEnv* env) { + return base::android::RegisterNativeMethods( + env, + kBookmarksRegisteredMethods, + arraysize(kBookmarksRegisteredMethods)); +} + +} // namespace android +} // namespace bookmarks diff --git a/components/bookmarks/common/android/component_jni_registrar.h b/components/bookmarks/common/android/component_jni_registrar.h new file mode 100644 index 0000000000000..09b80600c32e7 --- /dev/null +++ b/components/bookmarks/common/android/component_jni_registrar.h @@ -0,0 +1,18 @@ +// 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_BOOKMARKS_COMMON_ANDROID_COMPONENT_JNI_REGISTRAR_H_ +#define COMPONENTS_BOOKMARKS_COMMON_ANDROID_COMPONENT_JNI_REGISTRAR_H_ + +#include + +namespace bookmarks { +namespace android { + +bool RegisterBookmarks(JNIEnv* env); + +} // namespace android +} // namespace bookmarks + +#endif // COMPONENTS_BOOKMARKS_COMMON_ANDROID_COMPONENT_JNI_REGISTRAR_H_ diff --git a/components/bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkId.java b/components/bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkId.java new file mode 100644 index 0000000000000..88764febbbe5b --- /dev/null +++ b/components/bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkId.java @@ -0,0 +1,117 @@ +// 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.components.bookmarks; + +import android.text.TextUtils; +import android.util.Log; + +import org.chromium.base.CalledByNative; + +/** + * Simple object representing the bookmark id. + */ +public class BookmarkId { + public static final int INVALID_FOLDER_ID = -2; + public static final int ROOT_FOLDER_ID = -1; + + private static final String LOG_TAG = "BookmarkId"; + private static final char TYPE_PARTNER = 'p'; + + private final long mId; + private final int mType; + + public BookmarkId(long id, int type) { + mId = id; + mType = type; + } + + /** + * @param c The char representing the type. + * @return The Bookmark type from a char representing the type. + */ + private static int getBookmarkTypeFromChar(char c) { + switch (c) { + case TYPE_PARTNER: + return BookmarkType.BOOKMARK_TYPE_PARTNER; + default: + return BookmarkType.BOOKMARK_TYPE_NORMAL; + } + } + + /** + * @param c The char representing the bookmark type. + * @return Whether the char representing the bookmark type is a valid type. + */ + private static boolean isValidBookmarkTypeFromChar(char c) { + return c == TYPE_PARTNER; + } + + /** + * @param s The bookmark id string (Eg: p1 for partner bookmark id 1). + * @return the Bookmark id from the string which is a concatenation of + * bookmark type and the bookmark id. + */ + public static BookmarkId getBookmarkIdFromString(String s) { + long id = ROOT_FOLDER_ID; + int type = BookmarkType.BOOKMARK_TYPE_NORMAL; + if (TextUtils.isEmpty(s)) + return new BookmarkId(id, type); + char folderTypeChar = s.charAt(0); + if (isValidBookmarkTypeFromChar(folderTypeChar)) { + type = getBookmarkTypeFromChar(folderTypeChar); + s = s.substring(1); + } + try { + id = Long.parseLong(s); + } catch (NumberFormatException exception) { + Log.e(LOG_TAG, "Error parsing url to extract the bookmark folder id.", exception); + } + return new BookmarkId(id, type); + } + + /** + * @return The id of the bookmark. + */ + @CalledByNative + public long getId() { + return mId; + } + + /** + * @return The bookmark type. + */ + @CalledByNative + public int getType() { + return mType; + } + + private String getBookmarkTypeString() { + switch (mType) { + case BookmarkType.BOOKMARK_TYPE_PARTNER: + return String.valueOf(TYPE_PARTNER); + case BookmarkType.BOOKMARK_TYPE_NORMAL: + default: + return ""; + } + } + + @Override + public String toString() { + return getBookmarkTypeString() + mId; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BookmarkId)) + return false; + BookmarkId item = (BookmarkId) o; + return (item.mId == mId && item.mType == mType); + } + + @Override + public int hashCode() { + return toString().hashCode(); + } +} diff --git a/components/bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkType.template b/components/bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkType.template new file mode 100644 index 0000000000000..fd8915417aa95 --- /dev/null +++ b/components/bookmarks/common/android/java/src/org/chromium/components/bookmarks/BookmarkType.template @@ -0,0 +1,13 @@ +// 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.components.bookmarks; + +public class BookmarkType { + +#define DEFINE_BOOKMARK_TYPE(name, value) public static final int BOOKMARK_TYPE_##name = value; +#include "components/bookmarks/common/android/bookmark_type_list.h" +#undef DEFINE_BOOKMARK_TYPE + +} diff --git a/components/component_updater/component_updater_paths.cc b/components/component_updater/component_updater_paths.cc index bf9f4d39afb3f..7a3fbbf49bad9 100644 --- a/components/component_updater/component_updater_paths.cc +++ b/components/component_updater/component_updater_paths.cc @@ -11,14 +11,23 @@ namespace component_updater { namespace { -static base::LazyInstance g_components_root = - LAZY_INSTANCE_INITIALIZER; +// This key gives the root directory of all the component installations. +static int g_components_root_key = -1; } // namespace bool PathProvider(int key, base::FilePath* result) { - DCHECK(!g_components_root.Get().empty()); - base::FilePath cur = g_components_root.Get(); + DCHECK_GT(g_components_root_key, 0); + + // Early exit here to prevent a potential infinite loop when we retrieve + // the path for g_components_root_key. + if (key < PATH_START || key > PATH_END) + return false; + + base::FilePath cur; + if (!PathService::Get(g_components_root_key, &cur)) + return false; + switch (key) { case DIR_COMPONENT_CLD2: cur = cur.Append(FILE_PATH_LITERAL("CLD")); @@ -32,6 +41,9 @@ bool PathProvider(int key, base::FilePath* result) { case DIR_SW_REPORTER: cur = cur.Append(FILE_PATH_LITERAL("SwReporter")); break; + case DIR_COMPONENT_EV_WHITELIST: + cur = cur.Append(FILE_PATH_LITERAL("EVWhitelist")); + break; default: return false; } @@ -42,11 +54,12 @@ bool PathProvider(int key, base::FilePath* result) { // This cannot be done as a static initializer sadly since Visual Studio will // eliminate this object file if there is no direct entry point into it. -void RegisterPathProvider(const base::FilePath& components_root) { - DCHECK(g_components_root.Get().empty()); - DCHECK(!components_root.empty()); - g_components_root.Get() = components_root; +void RegisterPathProvider(int components_root_key) { + DCHECK_EQ(g_components_root_key, -1); + DCHECK_GT(components_root_key, 0); + DCHECK(components_root_key < PATH_START || components_root_key > PATH_END); + g_components_root_key = components_root_key; PathService::RegisterProvider(PathProvider, PATH_START, PATH_END); } diff --git a/components/component_updater/component_updater_paths.h b/components/component_updater/component_updater_paths.h index c69aec08d7090..33dd3d43ab4f6 100644 --- a/components/component_updater/component_updater_paths.h +++ b/components/component_updater/component_updater_paths.h @@ -17,13 +17,14 @@ enum { // component. DIR_SWIFT_SHADER, // Path to the SwiftShader component. DIR_SW_REPORTER, // Path to the SwReporter component. + DIR_COMPONENT_EV_WHITELIST, // EV whitelist for CT files. PATH_END }; // Call once to register the provider for the path keys defined above. // |components_root_key| is the path provider key defining where the // components should be installed. -void RegisterPathProvider(const base::FilePath& components_root_key); +void RegisterPathProvider(int components_root_key); } // namespace component_updater diff --git a/components/components.gyp b/components/components.gyp index 62e189a10b295..6d864c3a2be9e 100644 --- a/components/components.gyp +++ b/components/components.gyp @@ -19,6 +19,7 @@ 'component_updater.gypi', 'content_settings.gypi', 'cronet.gypi', + 'crx_file.gypi', 'data_reduction_proxy.gypi', 'dom_distiller.gypi', 'domain_reliability.gypi', @@ -62,6 +63,7 @@ 'cdm.gypi', 'navigation_interception.gypi', 'plugins.gypi', + 'power.gypi', 'sessions.gypi', 'visitedlink.gypi', 'web_contents_delegate_android.gypi', diff --git a/components/components_tests.gyp b/components/components_tests.gyp index e8d7f9dfecc01..b9178f0330b2a 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -33,7 +33,6 @@ 'autofill/content/browser/wallet/wallet_signin_helper_unittest.cc', 'autofill/core/browser/address_field_unittest.cc', 'autofill/core/browser/address_unittest.cc', - 'autofill/core/browser/android/auxiliary_profile_unittest_android.cc', 'autofill/core/browser/autocomplete_history_manager_unittest.cc', 'autofill/core/browser/autofill_country_unittest.cc', 'autofill/core/browser/autofill_data_model_unittest.cc', @@ -82,6 +81,7 @@ 'data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc', + 'data_reduction_proxy/browser/data_reduction_proxy_tamper_detection_unittest.cc', 'data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc', 'data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc', 'dom_distiller/core/article_entry_unittest.cc', @@ -105,6 +105,7 @@ 'domain_reliability/uploader_unittest.cc', 'domain_reliability/util_unittest.cc', # Note: GN tests converted to here, need to do the rest. + 'enhanced_bookmarks/enhanced_bookmark_utils_unittest.cc', 'enhanced_bookmarks/image_store_ios_unittest.mm', 'enhanced_bookmarks/image_store_unittest.cc', 'enhanced_bookmarks/metadata_accessor_unittest.cc', @@ -138,6 +139,8 @@ 'omaha_query_params/omaha_query_params_unittest.cc', 'omnibox/autocomplete_input_unittest.cc', 'omnibox/autocomplete_match_unittest.cc', + 'omnibox/autocomplete_result_unittest.cc', + 'omnibox/omnibox_field_trial_unittest.cc', 'os_crypt/ie7_password_win_unittest.cc', 'os_crypt/keychain_password_mac_unittest.mm', 'os_crypt/os_crypt_unittest.cc', @@ -400,7 +403,9 @@ 'sources': [ 'autofill/content/renderer/renderer_save_password_progress_logger_unittest.cc', 'dom_distiller/content/dom_distiller_viewer_source_unittest.cc', + 'power/origin_power_map_unittest.cc', 'usb_service/usb_context_unittest.cc', + 'usb_service/usb_device_filter_unittest.cc', ], 'dependencies': [ # Dependencies of autofill @@ -422,6 +427,9 @@ # Dependencies of precache/content 'components.gyp:precache_content', + # Dependencies of power + 'components.gyp:power', + # Dependencies of sessions '../third_party/protobuf/protobuf.gyp:protobuf_lite', 'components.gyp:sessions', @@ -550,6 +558,7 @@ 'storage_monitor/storage_info_unittest.cc', 'storage_monitor/storage_monitor_unittest.cc', 'usb_service/usb_context_unittest.cc', + 'usb_service/usb_device_filter_unittest.cc', 'web_modal/web_contents_modal_dialog_manager_unittest.cc', ], 'dependencies': [ @@ -589,6 +598,8 @@ 'copresence/handlers/audio/audio_directive_list_unittest.cc', 'copresence/mediums/audio/audio_player_unittest.cc', 'copresence/mediums/audio/audio_recorder_unittest.cc', + 'copresence/rpc/http_post_unittest.cc', + 'copresence/rpc/rpc_handler_unittest.cc', 'copresence/timed_map_unittest.cc', ], 'dependencies': [ @@ -598,10 +609,14 @@ ], }], ['chromeos==1', { + 'sources': [ + 'pairing/message_buffer_unittest.cc', + ], 'sources!': [ 'storage_monitor/storage_monitor_linux_unittest.cc', ], 'dependencies': [ + 'components.gyp:pairing', '../chromeos/chromeos.gyp:chromeos_test_support', ], }], @@ -853,15 +868,16 @@ # but that causes errors in other targets when # resulting .res files get referenced multiple times. '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.rc', + '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.rc', '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.rc', ], 'dependencies': [ + '<(DEPTH)/content/app/strings/content_strings.gyp:content_strings', '<(DEPTH)/net/net.gyp:net_resources', '<(DEPTH)/third_party/WebKit/public/blink_resources.gyp:blink_resources', '<(DEPTH)/third_party/iaccessible2/iaccessible2.gyp:iaccessible2', '<(DEPTH)/third_party/isimpledom/isimpledom.gyp:isimpledom', - '<(DEPTH)/webkit/webkit_resources.gyp:webkit_resources', + '<(DEPTH)/webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], 'configurations': { 'Debug_Base': { diff --git a/components/copresence.gypi b/components/copresence.gypi index baac59b265749..fded0bf1cc912 100644 --- a/components/copresence.gypi +++ b/components/copresence.gypi @@ -21,6 +21,8 @@ 'sources': [ 'copresence/copresence_client.cc', 'copresence/copresence_constants.cc', + 'copresence/copresence_switches.cc', + 'copresence/copresence_switches.h', 'copresence/handlers/audio/audio_directive_handler.cc', 'copresence/handlers/audio/audio_directive_handler.h', 'copresence/handlers/audio/audio_directive_list.cc', @@ -35,8 +37,10 @@ 'copresence/public/copresence_client.h', 'copresence/public/copresence_constants.h', 'copresence/public/whispernet_client.h', - 'copresence/rpc/rpc_handler.cc' - 'copresence/rpc/rpc_handler.h' + 'copresence/rpc/http_post.cc', + 'copresence/rpc/http_post.h', + 'copresence/rpc/rpc_handler.cc', + 'copresence/rpc/rpc_handler.h', 'copresence/timed_map.h', ], 'export_dependent_settings': [ diff --git a/components/copresence/BUILD.gn b/components/copresence/BUILD.gn index 7260b9dfb5f62..9d7d6852404b8 100644 --- a/components/copresence/BUILD.gn +++ b/components/copresence/BUILD.gn @@ -20,6 +20,8 @@ static_library("copresence") { "public/copresence_client.h", "public/copresence_constants.h", "public/whispernet_client.h", + "rpc/http_post.cc", + "rpc/http_post.h", "rpc/rpc_handler.cc", "rpc/rpc_handler.h", "timed_map.h", diff --git a/components/copresence/DEPS b/components/copresence/DEPS index 602f425d90bbd..dec5262052ce7 100644 --- a/components/copresence/DEPS +++ b/components/copresence/DEPS @@ -2,5 +2,8 @@ include_rules = [ "+base", "+content/public/browser", "+content/public/test", + "+google_apis", "+media", + "+net", + "+url", ] diff --git a/components/copresence/copresence_client.cc b/components/copresence/copresence_client.cc index 60e45e15d2156..cc027b67f1f1a 100644 --- a/components/copresence/copresence_client.cc +++ b/components/copresence/copresence_client.cc @@ -26,27 +26,22 @@ CopresenceClient::CopresenceClient(CopresenceClientDelegate* delegate) : delegate_(delegate), init_failed_(false), pending_init_operations_(0) { DVLOG(3) << "Initializing client."; pending_init_operations_++; - rpc_handler_.reset( - new RpcHandler(delegate, - base::Bind(&CopresenceClient::InitStepComplete, - AsWeakPtr(), - "Copresence device registration"))); + rpc_handler_.reset(new RpcHandler(delegate)); + // We own the RpcHandler, so it won't outlive us. + rpc_handler_->Initialize(base::Bind(&CopresenceClient::InitStepComplete, + base::Unretained(this), + "Copresence device registration")); pending_init_operations_++; delegate_->GetWhispernetClient()->Initialize( base::Bind(&CopresenceClient::InitStepComplete, + // We cannot cancel WhispernetClient initialization. + // TODO(ckehoe): Get rid of this. AsWeakPtr(), "Whispernet proxy initialization")); } -CopresenceClient::~CopresenceClient() { -} - -void CopresenceClient::Shutdown() { - DVLOG(3) << "Shutting down client."; - delegate_->GetWhispernetClient()->Shutdown(); - rpc_handler_->DisconnectFromWhispernet(); -} +CopresenceClient::~CopresenceClient() {} // Returns false if any operations were malformed. void CopresenceClient::ExecuteReportRequest(copresence::ReportRequest request, @@ -76,7 +71,7 @@ void CopresenceClient::CompleteInitialization() { return; if (!init_failed_) - rpc_handler_->ConnectToWhispernet(delegate_->GetWhispernetClient()); + rpc_handler_->ConnectToWhispernet(); for (std::vector::iterator request = pending_requests_queue_.begin(); diff --git a/components/copresence/copresence_switches.cc b/components/copresence/copresence_switches.cc new file mode 100644 index 0000000000000..f7a3bc7af4f05 --- /dev/null +++ b/components/copresence/copresence_switches.cc @@ -0,0 +1,16 @@ +// 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/copresence/copresence_switches.h" + +namespace switches { + +// Address for calls to the Copresence server (via Apiary). +// Defaults to https://www.googleapis.com/copresence/v2/copresence. +const char kCopresenceServer[] = "copresence-server"; + +// Apiary tracing token for calls to the Copresence server. +const char kCopresenceTracingToken[] = "copresence-tracing-token"; + +} // namespace switches diff --git a/components/copresence/copresence_switches.h b/components/copresence/copresence_switches.h new file mode 100644 index 0000000000000..d60a589ffabc6 --- /dev/null +++ b/components/copresence/copresence_switches.h @@ -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. + +#ifndef COMPONENTS_COPRESENCE_COPRESENCE_SWITCHES_H_ +#define COMPONENTS_COPRESENCE_COPRESENCE_SWITCHES_H_ + +namespace switches { + +// All switches in alphabetical order. The switches should be documented +// alongside the definition of their values in the .cc file. +extern const char kCopresenceServer[]; +extern const char kCopresenceTracingToken[]; + +} // namespace switches + +#endif // COMPONENTS_COPRESENCE_COPRESENCE_SWITCHES_H_ diff --git a/components/copresence/handlers/audio/audio_directive_handler.cc b/components/copresence/handlers/audio/audio_directive_handler.cc index c17a53a0d3750..fcf1d8247c66c 100644 --- a/components/copresence/handlers/audio/audio_directive_handler.cc +++ b/components/copresence/handlers/audio/audio_directive_handler.cc @@ -7,107 +7,215 @@ #include "base/bind.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "base/strings/string_util.h" #include "base/time/time.h" #include "components/copresence/mediums/audio/audio_player.h" #include "components/copresence/mediums/audio/audio_recorder.h" #include "components/copresence/proto/data.pb.h" #include "media/base/audio_bus.h" +namespace { + +// UrlSafe is defined as: +// '/' represented by a '_' and '+' represented by a '-' +// TODO(rkc): Move this processing to the whispernet wrapper. +std::string FromUrlSafe(std::string token) { + base::ReplaceChars(token, "-", "+", &token); + base::ReplaceChars(token, "_", "/", &token); + return token; +} + +const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes. +const int kMaxSamples = 10000; + +} // namespace + namespace copresence { // Public methods. AudioDirectiveHandler::AudioDirectiveHandler( const AudioRecorder::DecodeSamplesCallback& decode_cb, - const AudioDirectiveList::EncodeTokenCallback& encode_cb) - : directive_list_(encode_cb, - base::Bind(&AudioDirectiveHandler::ExecuteNextTransmit, - base::Unretained(this))), - player_(NULL), + const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) + : player_audible_(NULL), + player_inaudible_(NULL), recorder_(NULL), - decode_cb_(decode_cb) { + decode_cb_(decode_cb), + encode_cb_(encode_cb), + samples_cache_audible_( + base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), + kMaxSamples), + samples_cache_inaudible_( + base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), + kMaxSamples) { } AudioDirectiveHandler::~AudioDirectiveHandler() { - if (player_) - player_->Finalize(); + if (player_audible_) + player_audible_->Finalize(); + if (player_inaudible_) + player_inaudible_->Finalize(); if (recorder_) recorder_->Finalize(); } void AudioDirectiveHandler::Initialize() { - player_ = new AudioPlayer(); - player_->Initialize(); + player_audible_ = new AudioPlayer(); + player_audible_->Initialize(); + + player_inaudible_ = new AudioPlayer(); + player_inaudible_->Initialize(); recorder_ = new AudioRecorder(decode_cb_); recorder_->Initialize(); } void AudioDirectiveHandler::AddInstruction(const TokenInstruction& instruction, + const std::string& op_id, base::TimeDelta ttl) { switch (instruction.token_instruction_type()) { case TRANSMIT: DVLOG(2) << "Audio Transmit Directive received. Token: " << instruction.token_id() << " with TTL=" << ttl.InMilliseconds(); - // TODO(rkc): Fill in the op_id once we get it from the directive. - directive_list_.AddTransmitDirective( - instruction.token_id(), std::string(), ttl); + switch (instruction.medium()) { + case AUDIO_ULTRASOUND_PASSBAND: + transmits_list_inaudible_.AddDirective(op_id, ttl); + HandleToken(instruction.token_id(), false); + break; + case AUDIO_AUDIBLE_DTMF: + transmits_list_audible_.AddDirective(op_id, ttl); + HandleToken(instruction.token_id(), true); + break; + default: + NOTREACHED(); + } break; case RECEIVE: DVLOG(2) << "Audio Receive Directive received. TTL=" << ttl.InMilliseconds(); - // TODO(rkc): Fill in the op_id once we get it from the directive. - directive_list_.AddReceiveDirective(std::string(), ttl); + receives_list_.AddDirective(op_id, ttl); break; case UNKNOWN_TOKEN_INSTRUCTION_TYPE: default: LOG(WARNING) << "Unknown Audio Transmit Directive received."; } // ExecuteNextTransmit will be called by directive_list_ when Add is done. - ExecuteNextReceive(); + ProcessNextReceive(); } -// Protected methods. - -void AudioDirectiveHandler::PlayAudio( - const scoped_refptr& samples, - base::TimeDelta duration) { - player_->Play(samples); - stop_playback_timer_.Start( - FROM_HERE, duration, this, &AudioDirectiveHandler::StopPlayback); -} +void AudioDirectiveHandler::RemoveInstructions(const std::string& op_id) { + transmits_list_audible_.RemoveDirective(op_id); + transmits_list_inaudible_.RemoveDirective(op_id); + receives_list_.RemoveDirective(op_id); -void AudioDirectiveHandler::RecordAudio(base::TimeDelta duration) { - recorder_->Record(); - stop_recording_timer_.Start( - FROM_HERE, duration, this, &AudioDirectiveHandler::StopRecording); + ProcessNextTransmit(); + ProcessNextReceive(); } // Private methods. -void AudioDirectiveHandler::StopPlayback() { - player_->Stop(); - DVLOG(2) << "Done playing audio."; - ExecuteNextTransmit(); +void AudioDirectiveHandler::ProcessNextTransmit() { + // If we have an active directive for audible or inaudible audio, ensure that + // we are playing our respective token; if we do not have a directive, then + // make sure we aren't playing. This is duplicate code, but for just two + // elements, it has hard to make a case for processing a loop instead. + + scoped_ptr audible_transmit( + transmits_list_audible_.GetActiveDirective()); + if (audible_transmit && !player_audible_->IsPlaying()) { + DVLOG(3) << "Playing audible for op_id: " << audible_transmit->op_id; + player_audible_->Play( + samples_cache_audible_.GetValue(current_token_audible_)); + stop_audible_playback_timer_.Start( + FROM_HERE, + audible_transmit->end_time - base::Time::Now(), + this, + &AudioDirectiveHandler::ProcessNextTransmit); + } else if (!audible_transmit && player_audible_->IsPlaying()) { + DVLOG(3) << "Stopping audible playback."; + current_token_audible_.clear(); + stop_audible_playback_timer_.Stop(); + player_audible_->Stop(); + } + + scoped_ptr inaudible_transmit( + transmits_list_inaudible_.GetActiveDirective()); + if (inaudible_transmit && !player_inaudible_->IsPlaying()) { + DVLOG(3) << "Playing inaudible for op_id: " << inaudible_transmit->op_id; + player_inaudible_->Play( + samples_cache_inaudible_.GetValue(current_token_inaudible_)); + stop_inaudible_playback_timer_.Start( + FROM_HERE, + inaudible_transmit->end_time - base::Time::Now(), + this, + &AudioDirectiveHandler::ProcessNextTransmit); + } else if (!inaudible_transmit && player_inaudible_->IsPlaying()) { + DVLOG(3) << "Stopping inaudible playback."; + current_token_inaudible_.clear(); + stop_inaudible_playback_timer_.Stop(); + player_inaudible_->Stop(); + } } -void AudioDirectiveHandler::StopRecording() { - recorder_->Stop(); - DVLOG(2) << "Done recording audio."; - ExecuteNextReceive(); +void AudioDirectiveHandler::ProcessNextReceive() { + scoped_ptr receive(receives_list_.GetActiveDirective()); + + if (receive && !recorder_->IsRecording()) { + DVLOG(3) << "Recording for op_id: " << receive->op_id; + recorder_->Record(); + stop_recording_timer_.Start(FROM_HERE, + receive->end_time - base::Time::Now(), + this, + &AudioDirectiveHandler::ProcessNextReceive); + } else if (!receive && recorder_->IsRecording()) { + DVLOG(3) << "Stopping Recording"; + stop_recording_timer_.Stop(); + recorder_->Stop(); + } } -void AudioDirectiveHandler::ExecuteNextTransmit() { - scoped_ptr transmit(directive_list_.GetNextTransmit()); - if (transmit) - PlayAudio(transmit->samples, transmit->end_time - base::Time::Now()); +void AudioDirectiveHandler::HandleToken(const std::string token, bool audible) { + std::string valid_token = FromUrlSafe(token); + + if (audible && samples_cache_audible_.HasKey(valid_token)) { + current_token_audible_ = token; + ProcessNextTransmit(); + return; + } + + if (!audible && samples_cache_inaudible_.HasKey(valid_token)) { + current_token_inaudible_ = token; + ProcessNextTransmit(); + return; + } + + encode_cb_.Run(valid_token, + audible, + base::Bind(&AudioDirectiveHandler::OnTokenEncoded, + base::Unretained(this))); } -void AudioDirectiveHandler::ExecuteNextReceive() { - scoped_ptr receive(directive_list_.GetNextReceive()); - if (receive) - RecordAudio(receive->end_time - base::Time::Now()); +void AudioDirectiveHandler::OnTokenEncoded( + const std::string& token, + bool audible, + const scoped_refptr& samples) { + DVLOG(3) << "Token: " << token << "[audible:" << audible << "] encoded."; + if (audible) { + samples_cache_audible_.Add(token, samples); + current_token_audible_ = token; + // Force process transmits to pick up the new token. + if (player_audible_->IsPlaying()) + player_audible_->Stop(); + } else { + samples_cache_inaudible_.Add(token, samples); + current_token_inaudible_ = token; + // Force process transmits to pick up the new token. + if (player_inaudible_->IsPlaying()) + player_inaudible_->Stop(); + } + + ProcessNextTransmit(); } } // namespace copresence diff --git a/components/copresence/handlers/audio/audio_directive_handler.h b/components/copresence/handlers/audio/audio_directive_handler.h index a6ffa39bbf7bd..feff3378962f0 100644 --- a/components/copresence/handlers/audio/audio_directive_handler.h +++ b/components/copresence/handlers/audio/audio_directive_handler.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 COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_HANDLER_ -#define COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_HANDLER_ +#ifndef COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_HANDLER_H_ +#define COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_HANDLER_H_ -#include +#include #include "base/basictypes.h" #include "base/macros.h" @@ -15,6 +15,7 @@ #include "components/copresence/handlers/audio/audio_directive_list.h" #include "components/copresence/mediums/audio/audio_recorder.h" #include "components/copresence/proto/data.pb.h" +#include "components/copresence/timed_map.h" namespace media { class AudioBusRefCounted; @@ -25,11 +26,24 @@ namespace copresence { class AudioPlayer; // The AudioDirectiveHandler handles audio transmit and receive instructions. +// TODO(rkc): Currently since WhispernetClient can only have one token encoded +// callback at a time, we need to have both the audible and inaudible in this +// class. Investigate a better way to do this; a few options are abstracting +// out token encoding to a separate class, or allowing whispernet to have +// multiple callbacks for encoded tokens being sent back and have two versions +// of this class. class AudioDirectiveHandler { public: + typedef base::Callback&)> + SamplesCallback; + typedef base::Callback + EncodeTokenCallback; + AudioDirectiveHandler( const AudioRecorder::DecodeSamplesCallback& decode_cb, - const AudioDirectiveList::EncodeTokenCallback& encode_cb); + const AudioDirectiveHandler::EncodeTokenCallback& encode_cb); virtual ~AudioDirectiveHandler(); // Do not use this class before calling this. @@ -38,39 +52,82 @@ class AudioDirectiveHandler { // Adds an instruction to our handler. The instruction will execute and be // removed after the ttl expires. void AddInstruction(const copresence::TokenInstruction& instruction, + const std::string& op_id, base::TimeDelta ttl_ms); - protected: - // Protected and virtual since we want to be able to mock these out. - virtual void PlayAudio( - const scoped_refptr& samples, - base::TimeDelta duration); - virtual void RecordAudio(base::TimeDelta duration); + // Removes all instructions associated with this operation id. + void RemoveInstructions(const std::string& op_id); + + // Returns the currently playing DTMF token. + const std::string& PlayingAudibleToken() const { + return current_token_audible_; + } + + // Returns the currently playing DSSS token. + const std::string& PlayingInaudibleToken() const { + return current_token_inaudible_; + } + + void set_player_audible_for_testing(AudioPlayer* player) { + player_audible_ = player; + } + + void set_player_inaudible_for_testing(AudioPlayer* player) { + player_inaudible_ = player; + } + + void set_recorder_for_testing(AudioRecorder* recorder) { + recorder_ = recorder; + } private: - void StopPlayback(); - void StopRecording(); + FRIEND_TEST_ALL_PREFIXES(AudioDirectiveHandlerTest, Basic); - // Execute the next active transmit instruction. - void ExecuteNextTransmit(); - // Execute the next active receive instruction. - void ExecuteNextReceive(); + typedef TimedMap > + SamplesMap; - AudioDirectiveList directive_list_; + // Processes the next active transmit instruction. + void ProcessNextTransmit(); + // Processes the next active receive instruction. + void ProcessNextReceive(); - // The next two pointers are self-deleting. When we call Finalize on them, - // they clean themselves up on the Audio thread. - AudioPlayer* player_; + void HandleToken(const std::string token, bool audible); + + // This is the method that the whispernet client needs to call to return + // samples to us. + void OnTokenEncoded(const std::string& token, + bool audible, + const scoped_refptr& samples); + + AudioDirectiveList transmits_list_audible_; + AudioDirectiveList transmits_list_inaudible_; + AudioDirectiveList receives_list_; + + // Currently playing tokens. + std::string current_token_audible_; + std::string current_token_inaudible_; + + // AudioPlayer and AudioRecorder objects are self-deleting. When we call + // Finalize on them, they clean themselves up on the Audio thread. + AudioPlayer* player_audible_; + AudioPlayer* player_inaudible_; AudioRecorder* recorder_; AudioRecorder::DecodeSamplesCallback decode_cb_; + EncodeTokenCallback encode_cb_; - base::OneShotTimer stop_playback_timer_; + base::OneShotTimer stop_audible_playback_timer_; + base::OneShotTimer stop_inaudible_playback_timer_; base::OneShotTimer stop_recording_timer_; + // Cache that holds the encoded samples. After reaching its limit, the cache + // expires the oldest samples first. + SamplesMap samples_cache_audible_; + SamplesMap samples_cache_inaudible_; + DISALLOW_COPY_AND_ASSIGN(AudioDirectiveHandler); }; } // namespace copresence -#endif // COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_HANDLER_ +#endif // COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_HANDLER_H_ diff --git a/components/copresence/handlers/audio/audio_directive_handler_unittest.cc b/components/copresence/handlers/audio/audio_directive_handler_unittest.cc index d637c900720e6..c2482ddd298a2 100644 --- a/components/copresence/handlers/audio/audio_directive_handler_unittest.cc +++ b/components/copresence/handlers/audio/audio_directive_handler_unittest.cc @@ -6,6 +6,8 @@ #include "base/bind.h" #include "base/message_loop/message_loop.h" +#include "components/copresence/mediums/audio/audio_player.h" +#include "components/copresence/mediums/audio/audio_recorder.h" #include "components/copresence/test/audio_test_support.h" #include "media/base/audio_bus.h" #include "testing/gmock/include/gmock/gmock.h" @@ -16,46 +18,70 @@ using ::testing::Le; namespace copresence { -class MockAudioDirectiveHandler : public AudioDirectiveHandler { +class TestAudioPlayer : public AudioPlayer { public: - MockAudioDirectiveHandler( - const AudioDirectiveList::EncodeTokenCallback& encode_cb) - : AudioDirectiveHandler(AudioRecorder::DecodeSamplesCallback(), - encode_cb) {} - virtual ~MockAudioDirectiveHandler() {} - - // Mock out the play/record methods. - MOCK_METHOD2(PlayAudio, - void(const scoped_refptr&, - base::TimeDelta)); - MOCK_METHOD1(RecordAudio, void(base::TimeDelta)); + TestAudioPlayer() {} + virtual ~TestAudioPlayer() {} + + // AudioPlayer overrides: + virtual void Initialize() OVERRIDE {} + virtual void Play( + const scoped_refptr& /* samples */) OVERRIDE { + set_is_playing(true); + } + virtual void Stop() OVERRIDE { set_is_playing(false); } + virtual void Finalize() OVERRIDE { delete this; } private: - DISALLOW_COPY_AND_ASSIGN(MockAudioDirectiveHandler); + DISALLOW_COPY_AND_ASSIGN(TestAudioPlayer); +}; + +class TestAudioRecorder : public AudioRecorder { + public: + TestAudioRecorder() : AudioRecorder(AudioRecorder::DecodeSamplesCallback()) {} + virtual ~TestAudioRecorder() {} + + // AudioRecorder overrides: + virtual void Initialize() OVERRIDE {} + virtual void Record() OVERRIDE { set_is_recording(true); } + virtual void Stop() OVERRIDE { set_is_recording(false); } + virtual void Finalize() OVERRIDE { delete this; } + + private: + DISALLOW_COPY_AND_ASSIGN(TestAudioRecorder); }; class AudioDirectiveHandlerTest : public testing::Test { public: AudioDirectiveHandlerTest() - : directive_handler_(new MockAudioDirectiveHandler( + : directive_handler_(new AudioDirectiveHandler( + AudioRecorder::DecodeSamplesCallback(), base::Bind(&AudioDirectiveHandlerTest::EncodeToken, - base::Unretained(this)))) {} - + base::Unretained(this)))) { + directive_handler_->set_player_audible_for_testing(new TestAudioPlayer()); + directive_handler_->set_player_inaudible_for_testing(new TestAudioPlayer()); + directive_handler_->set_recorder_for_testing(new TestAudioRecorder()); + } virtual ~AudioDirectiveHandlerTest() {} void DirectiveAdded() {} protected: void EncodeToken(const std::string& token, - const AudioDirectiveList::SamplesCallback& callback) { - callback.Run(token, CreateRandomAudioRefCounted(0x1337, 1, 0x7331)); + bool audible, + const AudioDirectiveHandler::SamplesCallback& callback) { + callback.Run( + token, audible, CreateRandomAudioRefCounted(0x1337, 1, 0x7331)); } copresence::TokenInstruction CreateTransmitInstruction( - const std::string& token) { + const std::string& token, + bool audible) { copresence::TokenInstruction instruction; instruction.set_token_instruction_type(copresence::TRANSMIT); instruction.set_token_id(token); + instruction.set_medium(audible ? AUDIO_AUDIBLE_DTMF + : AUDIO_ULTRASOUND_PASSBAND); return instruction; } @@ -69,39 +95,45 @@ class AudioDirectiveHandlerTest : public testing::Test { // our the audio directive handler since the directive list ctor (invoked // from the directive handler ctor) will post tasks. base::MessageLoop message_loop_; - scoped_ptr directive_handler_; + scoped_ptr directive_handler_; private: DISALLOW_COPY_AND_ASSIGN(AudioDirectiveHandlerTest); }; -// TODO(rkc): Find and fix the memory leak here. -#define MAYBE_Basic DISABLED_Basic - -TEST_F(AudioDirectiveHandlerTest, MAYBE_Basic) { - const base::TimeDelta kSmallTtl = base::TimeDelta::FromMilliseconds(0x1337); - const base::TimeDelta kLargeTtl = base::TimeDelta::FromSeconds(0x7331); - - // Expect to play and record instructions for 'less' than the TTL specified, - // since by the time that the token would have gotten encoded, we would - // have (TTL - time_to_encode) left to play on that instruction. - EXPECT_CALL(*directive_handler_, PlayAudio(_, testing::Le(kLargeTtl))) - .Times(3); - directive_handler_->AddInstruction(CreateTransmitInstruction("token1"), - kLargeTtl); - directive_handler_->AddInstruction(CreateTransmitInstruction("token2"), - kLargeTtl); - directive_handler_->AddInstruction(CreateTransmitInstruction("token3"), - kSmallTtl); - - EXPECT_CALL(*directive_handler_, RecordAudio(Le(kLargeTtl))).Times(3); - directive_handler_->AddInstruction(CreateReceiveInstruction(), kLargeTtl); - directive_handler_->AddInstruction(CreateReceiveInstruction(), kSmallTtl); - directive_handler_->AddInstruction(CreateReceiveInstruction(), kLargeTtl); +TEST_F(AudioDirectiveHandlerTest, Basic) { + const base::TimeDelta kTtl = base::TimeDelta::FromMilliseconds(9999); + directive_handler_->AddInstruction( + CreateTransmitInstruction("token", true), "op_id1", kTtl); + directive_handler_->AddInstruction( + CreateTransmitInstruction("token", false), "op_id1", kTtl); + directive_handler_->AddInstruction( + CreateTransmitInstruction("token", false), "op_id2", kTtl); + directive_handler_->AddInstruction( + CreateReceiveInstruction(), "op_id1", kTtl); + directive_handler_->AddInstruction( + CreateReceiveInstruction(), "op_id2", kTtl); + directive_handler_->AddInstruction( + CreateReceiveInstruction(), "op_id3", kTtl); + + EXPECT_EQ(true, directive_handler_->player_audible_->IsPlaying()); + EXPECT_EQ(true, directive_handler_->player_inaudible_->IsPlaying()); + EXPECT_EQ(true, directive_handler_->recorder_->IsRecording()); + + directive_handler_->RemoveInstructions("op_id1"); + EXPECT_FALSE(directive_handler_->player_audible_->IsPlaying()); + EXPECT_EQ(true, directive_handler_->player_inaudible_->IsPlaying()); + EXPECT_EQ(true, directive_handler_->recorder_->IsRecording()); + + directive_handler_->RemoveInstructions("op_id2"); + EXPECT_FALSE(directive_handler_->player_inaudible_->IsPlaying()); + EXPECT_EQ(true, directive_handler_->recorder_->IsRecording()); + + directive_handler_->RemoveInstructions("op_id3"); + EXPECT_FALSE(directive_handler_->recorder_->IsRecording()); } -// TODO(rkc): When we are keeping track of which token we're currently playing, -// add tests to make sure we don't replay if we get a token with a lower ttl -// than the current active. +// TODO(rkc): Write more tests that check more convoluted sequences of +// transmits/receives. } // namespace copresence diff --git a/components/copresence/handlers/audio/audio_directive_list.cc b/components/copresence/handlers/audio/audio_directive_list.cc index 1954508ebe0d1..2dc505bb8af50 100644 --- a/components/copresence/handlers/audio/audio_directive_list.cc +++ b/components/copresence/handlers/audio/audio_directive_list.cc @@ -6,24 +6,8 @@ #include "base/bind.h" #include "base/logging.h" -#include "base/strings/string_util.h" -#include "media/base/audio_bus.h" - -namespace { - -// UrlSafe is defined as: -// '/' represented by a '_' and '+' represented by a '-' -// TODO(rkc): Move this processing to the whispernet wrapper. -std::string FromUrlSafe(std::string token) { - base::ReplaceChars(token, "-", "+", &token); - base::ReplaceChars(token, "_", "/", &token); - return token; -} - -const int kSampleExpiryTimeMs = 60 * 60 * 1000; // 60 minutes. -const int kMaxSamples = 10000; - -} // namespace +#include "base/memory/scoped_ptr.h" +#include "base/time/time.h" namespace copresence { @@ -32,113 +16,71 @@ namespace copresence { AudioDirective::AudioDirective() { } -AudioDirective::AudioDirective(const std::string& token, - const std::string& op_id, - base::Time end_time) - : token(token), op_id(op_id), end_time(end_time) { -} - -AudioDirective::AudioDirective( - const std::string& token, - const std::string& op_id, - base::Time end_time, - const scoped_refptr& samples) - : token(token), op_id(op_id), end_time(end_time), samples(samples) { -} - -AudioDirective::~AudioDirective() { +AudioDirective::AudioDirective(const std::string& op_id, base::Time end_time) + : op_id(op_id), end_time(end_time) { } -AudioDirectiveList::AudioDirectiveList( - const EncodeTokenCallback& encode_token_callback, - const base::Closure& token_added_callback) - : encode_token_callback_(encode_token_callback), - token_added_callback_(token_added_callback), - samples_cache_(base::TimeDelta::FromMilliseconds(kSampleExpiryTimeMs), - kMaxSamples) { +AudioDirectiveList::AudioDirectiveList() { } AudioDirectiveList::~AudioDirectiveList() { } -void AudioDirectiveList::AddTransmitDirective(const std::string& token, - const std::string& op_id, - base::TimeDelta ttl) { - std::string valid_token = FromUrlSafe(token); +void AudioDirectiveList::AddDirective(const std::string& op_id, + base::TimeDelta ttl) { base::Time end_time = base::Time::Now() + ttl; - if (samples_cache_.HasKey(valid_token)) { - active_transmit_tokens_.push(AudioDirective( - valid_token, op_id, end_time, samples_cache_.GetValue(valid_token))); + // In case this op is already in the list, update it instead of adding + // it again. + std::vector::iterator it = FindDirectiveByOpId(op_id); + if (it != active_directives_.end()) { + it->end_time = end_time; + std::make_heap(active_directives_.begin(), + active_directives_.end(), + LatestFirstComparator()); return; } - // If an encode request for this token has been sent, don't send it again. - if (pending_transmit_tokens_.find(valid_token) != - pending_transmit_tokens_.end()) { - return; - } - - pending_transmit_tokens_[valid_token] = - AudioDirective(valid_token, op_id, end_time); - // All whispernet callbacks will be cleared before we are destructed, so - // unretained is safe to use here. - encode_token_callback_.Run( - valid_token, - base::Bind(&AudioDirectiveList::OnTokenEncoded, base::Unretained(this))); + active_directives_.push_back(AudioDirective(op_id, end_time)); + std::push_heap(active_directives_.begin(), + active_directives_.end(), + LatestFirstComparator()); } -void AudioDirectiveList::AddReceiveDirective(const std::string& op_id, - base::TimeDelta ttl) { - active_receive_tokens_.push( - AudioDirective(std::string(), op_id, base::Time::Now() + ttl)); -} +void AudioDirectiveList::RemoveDirective(const std::string& op_id) { + std::vector::iterator it = FindDirectiveByOpId(op_id); + if (it != active_directives_.end()) + active_directives_.erase(it); -scoped_ptr AudioDirectiveList::GetNextTransmit() { - return GetNextFromList(&active_transmit_tokens_); + std::make_heap(active_directives_.begin(), + active_directives_.end(), + LatestFirstComparator()); } -scoped_ptr AudioDirectiveList::GetNextReceive() { - return GetNextFromList(&active_receive_tokens_); -} - -scoped_ptr AudioDirectiveList::GetNextFromList( - AudioDirectiveQueue* list) { - CHECK(list); - - // Checks if we have any valid tokens at all (since the top of the list is - // always pointing to the token with the latest expiry time). If we don't - // have any valid tokens left, clear the list. - if (!list->empty() && list->top().end_time < base::Time::Now()) { - while (!list->empty()) - list->pop(); +scoped_ptr AudioDirectiveList::GetActiveDirective() { + // The top is always the instruction that is ending the latest. If that time + // has passed, means all our previous instructions have expired too, hence + // clear the list. + if (!active_directives_.empty() && + active_directives_.front().end_time < base::Time::Now()) { + active_directives_.clear(); } - if (list->empty()) + if (active_directives_.empty()) return make_scoped_ptr(NULL); - return make_scoped_ptr(new AudioDirective(list->top())); + return make_scoped_ptr(new AudioDirective(active_directives_.front())); } -void AudioDirectiveList::OnTokenEncoded( - const std::string& token, - const scoped_refptr& samples) { - // We shouldn't re-encode a token if it's already in the cache. - DCHECK(!samples_cache_.HasKey(token)); - DVLOG(3) << "Token: " << token << " encoded."; - samples_cache_.Add(token, samples); - - // Copy the samples into their corresponding directive object and move - // that object into the active queue. - std::map::iterator it = - pending_transmit_tokens_.find(token); - - it->second.samples = samples; - active_transmit_tokens_.push(it->second); - pending_transmit_tokens_.erase(it); - - if (!token_added_callback_.is_null()) - token_added_callback_.Run(); +std::vector::iterator AudioDirectiveList::FindDirectiveByOpId( + const std::string& op_id) { + for (std::vector::iterator it = active_directives_.begin(); + it != active_directives_.end(); + ++it) { + if (it->op_id == op_id) + return it; + } + return active_directives_.end(); } } // namespace copresence diff --git a/components/copresence/handlers/audio/audio_directive_list.h b/components/copresence/handlers/audio/audio_directive_list.h index b190ea7f14370..9edd89f49b07c 100644 --- a/components/copresence/handlers/audio/audio_directive_list.h +++ b/components/copresence/handlers/audio/audio_directive_list.h @@ -2,21 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_LIST_ -#define COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_LIST_ +#ifndef COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_LIST_H_ +#define COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_LIST_H_ -#include -#include #include #include -#include "base/basictypes.h" #include "base/callback.h" #include "base/macros.h" -#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" -#include "components/copresence/timed_map.h" namespace media { class AudioBusRefCounted; @@ -27,21 +22,10 @@ namespace copresence { struct AudioDirective { // Default ctor, required by the priority queue. AudioDirective(); - // ctor used to store transmit directives that are awaiting samples. - AudioDirective(const std::string& token, - const std::string& op_id, - base::Time end_time); - // ctor used to construct a complete transmit directive. - AudioDirective(const std::string& token, - const std::string& op_id, - base::Time end_time, - const scoped_refptr& samples); - ~AudioDirective(); - - std::string token; + AudioDirective(const std::string& op_id, base::Time end_time); + std::string op_id; base::Time end_time; - scoped_refptr samples; }; // This class maintains a list of active audio directives. It fetches the audio @@ -52,32 +36,13 @@ struct AudioDirective { // classes from it. class AudioDirectiveList { public: - typedef base::Callback< - void(const std::string&, const scoped_refptr&)> - SamplesCallback; - typedef base::Callback - EncodeTokenCallback; - - AudioDirectiveList(const EncodeTokenCallback& encode_token_callback, - const base::Closure& token_added_callback); + AudioDirectiveList(); virtual ~AudioDirectiveList(); - // Adds a token to the token queue, after getting its corresponding samples - // from whispernet. - void AddTransmitDirective(const std::string& token, - const std::string& op_id, - base::TimeDelta ttl); - - void AddReceiveDirective(const std::string& op_id, base::TimeDelta ttl); + void AddDirective(const std::string& op_id, base::TimeDelta ttl); + void RemoveDirective(const std::string& op_id); - // Returns the next audio token to play. This also cleans up expired tokens. - scoped_ptr GetNextTransmit(); - scoped_ptr GetNextReceive(); - - // This is the method that the whispernet client needs to call to return - // samples to us. - void OnTokenEncoded(const std::string& token, - const scoped_refptr& samples); + scoped_ptr GetActiveDirective(); private: // Comparator for comparing end_times on audio tokens. @@ -90,32 +55,16 @@ class AudioDirectiveList { } }; - typedef std::priority_queue, - LatestFirstComparator> AudioDirectiveQueue; - typedef TimedMap > - SamplesMap; - - scoped_ptr GetNextFromList(AudioDirectiveQueue* list); - - // A map of tokens that are awaiting their samples before we can - // add them to the active transmit tokens list. - std::map pending_transmit_tokens_; - - AudioDirectiveQueue active_transmit_tokens_; - AudioDirectiveQueue active_receive_tokens_; - - EncodeTokenCallback encode_token_callback_; - - base::Closure token_added_callback_; + std::vector::iterator FindDirectiveByOpId( + const std::string& op_id); - // Cache that holds the encoded samples. After reaching its limit, the cache - // expires the oldest samples first. - SamplesMap samples_cache_; + // This vector will be organized as a heap with the latest time as the first + // element. Only currently active directives will exist in this list. + std::vector active_directives_; DISALLOW_COPY_AND_ASSIGN(AudioDirectiveList); }; } // namespace copresence -#endif // COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_LIST_ +#endif // COMPONENTS_COPRESENCE_HANDLERS_AUDIO_AUDIO_DIRECTIVE_LIST_H_ diff --git a/components/copresence/handlers/audio/audio_directive_list_unittest.cc b/components/copresence/handlers/audio/audio_directive_list_unittest.cc index f237561b1bc9a..5fda54255d379 100644 --- a/components/copresence/handlers/audio/audio_directive_list_unittest.cc +++ b/components/copresence/handlers/audio/audio_directive_list_unittest.cc @@ -7,82 +7,74 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/message_loop/message_loop.h" -#include "components/copresence/test/audio_test_support.h" -#include "media/base/audio_bus.h" #include "testing/gtest/include/gtest/gtest.h" namespace copresence { class AudioDirectiveListTest : public testing::Test { public: - AudioDirectiveListTest() - : directive_list_(new AudioDirectiveList( - base::Bind(&AudioDirectiveListTest::EncodeToken, - base::Unretained(this)), - base::Bind(&base::DoNothing))) {} + AudioDirectiveListTest() : directive_list_(new AudioDirectiveList()) {} virtual ~AudioDirectiveListTest() {} protected: - void EncodeToken(const std::string& token, - const AudioDirectiveList::SamplesCallback& callback) { - callback.Run(token, CreateRandomAudioRefCounted(0x1337, 1, 0x7331)); - } - base::MessageLoop message_loop_; scoped_ptr directive_list_; }; -// TODO(rkc): Find and fix the memory leak here. -#define MAYBE_Basic DISABLED_Basic - -TEST_F(AudioDirectiveListTest, MAYBE_Basic) { - const base::TimeDelta kZeroTtl = base::TimeDelta::FromMilliseconds(0); - const base::TimeDelta kLargeTtl = base::TimeDelta::FromSeconds(0x7331); - - directive_list_->AddTransmitDirective("token1", "op_id1", kZeroTtl); - directive_list_->AddTransmitDirective("token2", "op_id2", kLargeTtl); - directive_list_->AddTransmitDirective("token3", "op_id1", kZeroTtl); +TEST_F(AudioDirectiveListTest, Basic) { + const base::TimeDelta kTtl = base::TimeDelta::FromSeconds(9999); - EXPECT_EQ("token2", directive_list_->GetNextTransmit()->token); + EXPECT_EQ(NULL, directive_list_->GetActiveDirective().get()); - directive_list_->AddReceiveDirective("op_id1", kZeroTtl); - directive_list_->AddReceiveDirective("op_id3", kZeroTtl); - directive_list_->AddReceiveDirective("op_id3", kLargeTtl); - directive_list_->AddReceiveDirective("op_id7", kZeroTtl); + directive_list_->AddDirective("op_id1", kTtl); + directive_list_->AddDirective("op_id2", kTtl * 3); + directive_list_->AddDirective("op_id3", kTtl * 2); + EXPECT_EQ("op_id2", directive_list_->GetActiveDirective()->op_id); - EXPECT_EQ("op_id3", directive_list_->GetNextReceive()->op_id); + directive_list_->RemoveDirective("op_id2"); + EXPECT_EQ("op_id3", directive_list_->GetActiveDirective()->op_id); } -// TODO(rkc): Find out why this is breaking on bots and fix it. -#define MAYBE_OutOfOrderAndMultiple DISABLED_OutOfOrderAndMultiple - -TEST_F(AudioDirectiveListTest, MAYBE_OutOfOrderAndMultiple) { - const base::TimeDelta kZeroTtl = base::TimeDelta::FromMilliseconds(0); - const base::TimeDelta kLargeTtl = base::TimeDelta::FromSeconds(0x7331); - - EXPECT_EQ(NULL, directive_list_->GetNextTransmit().get()); - EXPECT_EQ(NULL, directive_list_->GetNextReceive().get()); - - directive_list_->AddTransmitDirective("token1", "op_id1", kZeroTtl); - directive_list_->AddTransmitDirective("token2", "op_id2", kLargeTtl); - directive_list_->AddTransmitDirective("token3", "op_id1", kLargeTtl); - - // Should keep getting the directive till it expires or we add a newer one. - EXPECT_EQ("token3", directive_list_->GetNextTransmit()->token); - EXPECT_EQ("token3", directive_list_->GetNextTransmit()->token); - EXPECT_EQ("token3", directive_list_->GetNextTransmit()->token); - EXPECT_EQ(NULL, directive_list_->GetNextReceive().get()); - - directive_list_->AddReceiveDirective("op_id1", kLargeTtl); - directive_list_->AddReceiveDirective("op_id3", kZeroTtl); - directive_list_->AddReceiveDirective("op_id3", kLargeTtl); - directive_list_->AddReceiveDirective("op_id7", kLargeTtl); +TEST_F(AudioDirectiveListTest, AddDirectiveMultiple) { + const base::TimeDelta kTtl = base::TimeDelta::FromSeconds(9999); + + directive_list_->AddDirective("op_id1", kTtl); + directive_list_->AddDirective("op_id2", kTtl * 2); + directive_list_->AddDirective("op_id3", kTtl * 3 * 2); + directive_list_->AddDirective("op_id3", kTtl * 3 * 3); + directive_list_->AddDirective("op_id4", kTtl * 4); + + EXPECT_EQ("op_id3", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id3"); + EXPECT_EQ("op_id4", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id4"); + EXPECT_EQ("op_id2", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id2"); + EXPECT_EQ("op_id1", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id1"); + EXPECT_EQ(NULL, directive_list_->GetActiveDirective().get()); +} - // Should keep getting the directive till it expires or we add a newer one. - EXPECT_EQ("op_id7", directive_list_->GetNextReceive()->op_id); - EXPECT_EQ("op_id7", directive_list_->GetNextReceive()->op_id); - EXPECT_EQ("op_id7", directive_list_->GetNextReceive()->op_id); +TEST_F(AudioDirectiveListTest, RemoveDirectiveMultiple) { + const base::TimeDelta kTtl = base::TimeDelta::FromSeconds(9999); + + directive_list_->AddDirective("op_id1", kTtl); + directive_list_->AddDirective("op_id2", kTtl * 2); + directive_list_->AddDirective("op_id3", kTtl * 3); + directive_list_->AddDirective("op_id4", kTtl * 4); + + EXPECT_EQ("op_id4", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id4"); + EXPECT_EQ("op_id3", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id3"); + directive_list_->RemoveDirective("op_id3"); + directive_list_->RemoveDirective("op_id3"); + EXPECT_EQ("op_id2", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id2"); + EXPECT_EQ("op_id1", directive_list_->GetActiveDirective()->op_id); + directive_list_->RemoveDirective("op_id1"); + EXPECT_EQ(NULL, directive_list_->GetActiveDirective().get()); } } // namespace copresence diff --git a/components/copresence/handlers/directive_handler.cc b/components/copresence/handlers/directive_handler.cc index 79ddf8fbe243a..e76dde6fed261 100644 --- a/components/copresence/handlers/directive_handler.cc +++ b/components/copresence/handlers/directive_handler.cc @@ -10,10 +10,12 @@ namespace copresence { -DirectiveHandler::DirectiveHandler( +DirectiveHandler::DirectiveHandler() {} + +void DirectiveHandler::Initialize( const AudioRecorder::DecodeSamplesCallback& decode_cb, - const AudioDirectiveList::EncodeTokenCallback& encode_cb) - : audio_handler_(new AudioDirectiveHandler(decode_cb, encode_cb)) { + const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) { + audio_handler_.reset(new AudioDirectiveHandler(decode_cb, encode_cb)); audio_handler_->Initialize(); } @@ -24,15 +26,39 @@ void DirectiveHandler::AddDirective(const Directive& directive) { // We only handle Token directives; wifi/ble requests aren't implemented. DCHECK_EQ(directive.instruction_type(), TOKEN); + std::string op_id; + if (directive.has_published_message_id()) { + op_id = directive.published_message_id(); + } else if (directive.has_subscription_id()) { + op_id = directive.subscription_id(); + } else { + NOTREACHED() << "No operation associated with directive!"; + return; + } + const TokenInstruction& ti = directive.token_instruction(); + DCHECK(audio_handler_.get()) << "Clients must call Initialize() before " + << "any other DirectiveHandler methods."; // We currently only support audio. - DCHECK_EQ(ti.medium(), AUDIO_ULTRASOUND_PASSBAND); - audio_handler_->AddInstruction( - ti, base::TimeDelta::FromMilliseconds(directive.ttl_millis())); + if (ti.medium() == AUDIO_ULTRASOUND_PASSBAND || + ti.medium() == AUDIO_AUDIBLE_DTMF) { + audio_handler_->AddInstruction( + ti, op_id, base::TimeDelta::FromMilliseconds(directive.ttl_millis())); + } +} + +void DirectiveHandler::RemoveDirectives(const std::string& op_id) { + DCHECK(audio_handler_.get()) << "Clients must call Initialize() before " + << "any other DirectiveHandler methods."; + audio_handler_->RemoveInstructions(op_id); +} + +const std::string& DirectiveHandler::CurrentAudibleToken() const { + return audio_handler_->PlayingAudibleToken(); } -void DirectiveHandler::RemoveDirectives(const std::string& /* op_id */) { - // TODO(rkc): Forward the remove directive call to all the directive handlers. +const std::string& DirectiveHandler::CurrentInaudibleToken() const { + return audio_handler_->PlayingInaudibleToken(); } } // namespace copresence diff --git a/components/copresence/handlers/directive_handler.h b/components/copresence/handlers/directive_handler.h index 8a415f1dbb77c..081b62efd253d 100644 --- a/components/copresence/handlers/directive_handler.h +++ b/components/copresence/handlers/directive_handler.h @@ -2,34 +2,43 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_COPRESENCE_HANDLERS_DIRECTIVE_HANDLER_ -#define COMPONENTS_COPRESENCE_HANDLERS_DIRECTIVE_HANDLER_ +#ifndef COMPONENTS_COPRESENCE_HANDLERS_DIRECTIVE_HANDLER_H_ +#define COMPONENTS_COPRESENCE_HANDLERS_DIRECTIVE_HANDLER_H_ #include #include "base/callback.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "components/copresence/handlers/audio/audio_directive_list.h" +#include "components/copresence/handlers/audio/audio_directive_handler.h" #include "components/copresence/mediums/audio/audio_recorder.h" namespace copresence { -class AudioDirectiveHandler; class Directive; -// The directive handler manages transmit and receive directives given to it -// by the client. +// The directive handler manages transmit and receive directives +// given to it by the client. class DirectiveHandler { public: - DirectiveHandler(const AudioRecorder::DecodeSamplesCallback& decode_cb, - const AudioDirectiveList::EncodeTokenCallback& encode_cb); - ~DirectiveHandler(); + DirectiveHandler(); + virtual ~DirectiveHandler(); + + // Initialize the |audio_handler_| with the appropriate callbacks. + // This function must be called before any others. + // TODO(ckehoe): Instead of this, use a static Create() method + // and make the constructor private. + virtual void Initialize( + const AudioRecorder::DecodeSamplesCallback& decode_cb, + const AudioDirectiveHandler::EncodeTokenCallback& encode_cb); // Adds a directive to handle. - void AddDirective(const copresence::Directive& directive); + virtual void AddDirective(const copresence::Directive& directive); // Removes any directives associated with the given operation id. - void RemoveDirectives(const std::string& op_id); + virtual void RemoveDirectives(const std::string& op_id); + + const std::string& CurrentAudibleToken() const; + const std::string& CurrentInaudibleToken() const; private: scoped_ptr audio_handler_; @@ -39,4 +48,4 @@ class DirectiveHandler { } // namespace copresence -#endif // COMPONENTS_COPRESENCE_HANDLERS_DIRECTIVE_HANDLER_ +#endif // COMPONENTS_COPRESENCE_HANDLERS_DIRECTIVE_HANDLER_H_ diff --git a/components/copresence/mediums/audio/audio_player.cc b/components/copresence/mediums/audio/audio_player.cc index 4a734a1ed04ea..ac9bf78344a9e 100644 --- a/components/copresence/mediums/audio/audio_player.cc +++ b/components/copresence/mediums/audio/audio_player.cc @@ -5,7 +5,7 @@ #include "components/copresence/mediums/audio/audio_player.h" #include -#include +#include #include "base/bind.h" #include "base/bind_helpers.h" @@ -29,7 +29,7 @@ namespace copresence { // Public methods. AudioPlayer::AudioPlayer() - : stream_(NULL), is_playing_(false), frame_index_(0) { + : is_playing_(false), stream_(NULL), frame_index_(0) { } AudioPlayer::~AudioPlayer() { @@ -56,6 +56,10 @@ void AudioPlayer::Stop() { base::Bind(&AudioPlayer::StopOnAudioThread, base::Unretained(this))); } +bool AudioPlayer::IsPlaying() { + return is_playing_; +} + void AudioPlayer::Finalize() { media::AudioManager::Get()->GetTaskRunner()->PostTask( FROM_HERE, @@ -104,7 +108,6 @@ void AudioPlayer::PlayOnAudioThread( return; } - DVLOG(2) << "Playing Audio."; is_playing_ = true; stream_->Start(this); } diff --git a/components/copresence/mediums/audio/audio_player.h b/components/copresence/mediums/audio/audio_player.h index a6bebceae083b..aecd6601fb88d 100644 --- a/components/copresence/mediums/audio/audio_player.h +++ b/components/copresence/mediums/audio/audio_player.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 COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_PLAYER_ -#define COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_PLAYER_ +#ifndef COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_PLAYER_H_ +#define COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_PLAYER_H_ #include @@ -26,17 +26,19 @@ class AudioPlayer : public media::AudioOutputStream::AudioSourceCallback { AudioPlayer(); // Initializes the object. Do not use this object before calling this method. - void Initialize(); + virtual void Initialize(); // Play the given samples. These samples will keep on being played in a loop // till we explicitly tell the player to stop playing. - void Play(const scoped_refptr& samples); + virtual void Play(const scoped_refptr& samples); // Stop playing. - void Stop(); + virtual void Stop(); // Cleans up and deletes this object. Do not use object after this call. - void Finalize(); + virtual void Finalize(); + + bool IsPlaying(); // Takes ownership of the stream. void set_output_stream_for_testing( @@ -44,13 +46,15 @@ class AudioPlayer : public media::AudioOutputStream::AudioSourceCallback { output_stream_for_testing_.reset(output_stream_for_testing); } + protected: + virtual ~AudioPlayer(); + void set_is_playing(bool is_playing) { is_playing_ = is_playing; } + private: friend class AudioPlayerTest; FRIEND_TEST_ALL_PREFIXES(AudioPlayerTest, BasicPlayAndStop); FRIEND_TEST_ALL_PREFIXES(AudioPlayerTest, OutOfOrderPlayAndStopMultiple); - virtual ~AudioPlayer(); - // Methods to do our various operations; all of these need to be run on the // audio thread. void InitializeOnAudioThread(); @@ -70,13 +74,13 @@ class AudioPlayer : public media::AudioOutputStream::AudioSourceCallback { // performed. void FlushAudioLoopForTesting(); + bool is_playing_; + // Self-deleting object. media::AudioOutputStream* stream_; scoped_ptr output_stream_for_testing_; - bool is_playing_; - // All fields below here are protected by this lock. base::Lock state_lock_; @@ -90,4 +94,4 @@ class AudioPlayer : public media::AudioOutputStream::AudioSourceCallback { } // namespace copresence -#endif // COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_PLAYER_ +#endif // COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_PLAYER_H_ diff --git a/components/copresence/mediums/audio/audio_recorder.cc b/components/copresence/mediums/audio/audio_recorder.cc index e19e3543dd111..7430b27d17c41 100644 --- a/components/copresence/mediums/audio/audio_recorder.cc +++ b/components/copresence/mediums/audio/audio_recorder.cc @@ -51,8 +51,8 @@ void ProcessSamples(scoped_ptr bus, // Public methods. AudioRecorder::AudioRecorder(const DecodeSamplesCallback& decode_callback) - : stream_(NULL), - is_recording_(false), + : is_recording_(false), + stream_(NULL), decode_callback_(decode_callback), total_buffer_frames_(0), buffer_frame_index_(0) { @@ -80,6 +80,10 @@ void AudioRecorder::Stop() { base::Bind(&AudioRecorder::StopOnAudioThread, base::Unretained(this))); } +bool AudioRecorder::IsRecording() { + return is_recording_; +} + void AudioRecorder::Finalize() { media::AudioManager::Get()->GetTaskRunner()->PostTask( FROM_HERE, @@ -137,7 +141,6 @@ void AudioRecorder::RecordOnAudioThread() { if (!stream_ || is_recording_) return; - DVLOG(2) << "Recording Audio."; converter_->Reset(); stream_->Start(this); is_recording_ = true; diff --git a/components/copresence/mediums/audio/audio_recorder.h b/components/copresence/mediums/audio/audio_recorder.h index 69e95a1db2d35..eec982d4e040c 100644 --- a/components/copresence/mediums/audio/audio_recorder.h +++ b/components/copresence/mediums/audio/audio_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 COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_RECORDER_ -#define COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_RECORDER_ +#ifndef COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_RECORDER_H_ +#define COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_RECORDER_H_ #include @@ -33,13 +33,15 @@ class AudioRecorder : public media::AudioInputStream::AudioInputCallback, explicit AudioRecorder(const DecodeSamplesCallback& decode_callback); // Initializes the object. Do not use this object before calling this method. - void Initialize(); + virtual void Initialize(); - void Record(); - void Stop(); + virtual void Record(); + virtual void Stop(); // Cleans up and deletes this object. Do not use object after this call. - void Finalize(); + virtual void Finalize(); + + bool IsRecording(); // Takes ownership of the stream. void set_input_stream_for_testing( @@ -52,13 +54,15 @@ class AudioRecorder : public media::AudioInputStream::AudioInputCallback, params_for_testing_.reset(params_for_testing); } + protected: + virtual ~AudioRecorder(); + void set_is_recording(bool is_recording) { is_recording_ = is_recording; } + private: friend class AudioRecorderTest; FRIEND_TEST_ALL_PREFIXES(AudioRecorderTest, BasicRecordAndStop); FRIEND_TEST_ALL_PREFIXES(AudioRecorderTest, OutOfOrderRecordAndStopMultiple); - virtual ~AudioRecorder(); - // Methods to do our various operations; all of these need to be run on the // audio thread. void InitializeOnAudioThread(); @@ -85,8 +89,9 @@ class AudioRecorder : public media::AudioInputStream::AudioInputCallback, // performed. void FlushAudioLoopForTesting(); - media::AudioInputStream* stream_; bool is_recording_; + + media::AudioInputStream* stream_; DecodeSamplesCallback decode_callback_; // ProvideInput will use this buffer as its source. @@ -108,4 +113,4 @@ class AudioRecorder : public media::AudioInputStream::AudioInputCallback, } // namespace copresence -#endif // COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_RECORDER_ +#endif // COMPONENTS_COPRESENCE_MEDIUMS_AUDIO_AUDIO_RECORDER_H_ diff --git a/components/copresence/mediums/audio/audio_recorder_unittest.cc b/components/copresence/mediums/audio/audio_recorder_unittest.cc index 900dffbc8a16f..69c856aa717ae 100644 --- a/components/copresence/mediums/audio/audio_recorder_unittest.cc +++ b/components/copresence/mediums/audio/audio_recorder_unittest.cc @@ -189,18 +189,14 @@ class AudioRecorderTest : public testing::Test { content::TestBrowserThreadBundle thread_bundle_; }; -#if defined(OS_WIN) || defined(OS_MACOSX) -// Windows does not let us use non-OS params. The tests need to be rewritten to -// use the params provided to us by the audio manager rather than setting our -// own params. +// TODO(rkc): These tests are broken on all platforms. +// On Windows and Mac, we cannot use non-OS params. The tests need to be +// rewritten to use the params provided to us by the audio manager +// rather than setting our own params. +// On Linux, there is a memory leak in the audio code during initialization. #define MAYBE_BasicRecordAndStop DISABLED_BasicRecordAndStop #define MAYBE_OutOfOrderRecordAndStopMultiple DISABLED_OutOfOrderRecordAndStopMultiple #define MAYBE_RecordingEndToEnd DISABLED_RecordingEndToEnd -#else -#define MAYBE_BasicRecordAndStop BasicRecordAndStop -#define MAYBE_OutOfOrderRecordAndStopMultiple OutOfOrderRecordAndStopMultiple -#define MAYBE_RecordingEndToEnd RecordingEndToEnd -#endif TEST_F(AudioRecorderTest, MAYBE_BasicRecordAndStop) { CreateSimpleRecorder(); diff --git a/components/copresence/proto/data.proto b/components/copresence/proto/data.proto index a03df3c7d910c..b4bbb9da6491f 100644 --- a/components/copresence/proto/data.proto +++ b/components/copresence/proto/data.proto @@ -13,16 +13,6 @@ message Status { optional StatusCode code = 1; optional string message = 2; } -message DeviceRegistration { - optional PushService service = 1 [deprecated = true]; - optional string device_token = 2 [deprecated = true]; - optional DeviceIdentity device_identity = 3 [deprecated = true]; - optional string app_name = 4 [deprecated = true]; - optional bytes device_token_binary = 5 [deprecated = true]; - optional string registered_device_id = 6 [deprecated = true]; - optional int32 ulr_device_id = 7 [deprecated = true]; - optional Identity registrant = 8 [deprecated = true]; -} message PushServiceRegistration { optional PushService service = 1; } @@ -34,13 +24,17 @@ message DeviceIdentifiers { message Token { message Debug { repeated string email = 2; - extensions 1 to 1; } optional string id = 1; - optional uint32 ttl_millis_deprecated = 2 [deprecated = true]; optional TokenStatus status = 3; optional Debug debug = 4; } +message DeviceFingerprint { + optional string manufacturer = 1; + optional string model = 2; + optional PlatformType type = 3; + optional string platform_version = 4; +} message TokenTechnology { optional TokenMedium medium = 1; repeated TokenInstructionType instruction_type = 2; @@ -67,7 +61,6 @@ message DeviceState { } message DebugInfo { optional string served_by_task = 1; - optional bool listen_initiated_by_location_deprecated = 2 [deprecated = true]; repeated string token_id = 3; optional int64 request_time_millis = 4; } @@ -83,7 +76,6 @@ message TokenSignals { message AccessPolicy { optional int64 ttl_millis = 1; optional Acl acl = 2; - extensions 4 to 4; } message Acl { optional AclType acl_type = 1; @@ -94,13 +86,16 @@ message PublishedMessage { optional string id = 1; optional AccessPolicy access_policy = 2; optional Message message = 3; - optional TransmissionStrategy strategy = 4; optional TokenExchangeStrategy token_exchange_strategy = 5; optional OptInStateFilter opt_in_state_filter = 6; } message TokenExchangeStrategy { optional AudioConfiguration audio_configuration = 1; optional BroadcastScanConfiguration broadcast_scan_configuration = 2; + // TODO(rkc): Horrible hack. Get rid of this once the server tells us whether + // or not to use audible for a given publish or subscribe. + // http://b/16849245/ + optional bool use_audible = 99; } message SubscribedMessage { message Debug { @@ -109,7 +104,6 @@ message SubscribedMessage { optional string publisher_device_id = 3; optional int64 creation_timestamp_millis = 4; optional int64 ttl_millis = 5; - optional TransmissionStrategy strategy = 6; optional TokenExchangeStrategy token_exchange_strategy = 7; } repeated string subscription_id = 1; @@ -121,21 +115,15 @@ message Message { optional bytes payload = 3; } message MessageType { - optional string namespace_deprecated = 1 [deprecated = true]; optional string type = 2; } message Subscription { optional string id = 1; - optional TransmissionStrategy strategy = 2; optional int64 ttl_millis = 3; optional MessageType message_type = 4; optional TokenExchangeStrategy token_exchange_strategy = 7; optional OptInStateFilter opt_in_state_filter = 8; } -message DeleteAll { - optional DeleteAllType type = 1; - repeated string namespace = 2; -} message MessageResult { optional string published_message_id = 1; } diff --git a/components/copresence/proto/enums.proto b/components/copresence/proto/enums.proto index 9356cab0439d3..d44616d4ddf65 100644 --- a/components/copresence/proto/enums.proto +++ b/components/copresence/proto/enums.proto @@ -52,25 +52,19 @@ enum TokenInstructionType { TRANSMIT = 1; RECEIVE = 2; } +enum PlatformType { + UNKNOWN_PLATFORM_TYPE = 0; + CHROMECAST_PLATFORM_TYPE = 5; + ANDROID_PLATFORM_TYPE = 6; + IOS_PLATFORM_TYPE = 7; + CHROME_PLATFORM_TYPE = 8; +} enum InstructionType { UNKNOWN_INSTRUCTION_TYPE = 0; TOKEN = 1; SCAN_WIFI = 2; SCAN_BLE = 3; } -enum TransmissionStrategy { - UNKNOWN_STRATEGY = 0; - AGGRESSIVE = 1; - OPPORTUNISTIC = 2; - PASSIVE = 3; -} -enum DeleteAllType { - UNKNOWN_DELETE_ALL_TYPE = 0; - DELETE_NONE = 1; - DELETE_ALL_FOR_IDENTITY_DEVICE = 2; - DELETE_ALL_FOR_IDENTITY = 3; - DELETE_ALL_FOR_DEVICE_APP = 4; -} enum StatusCode { STATUS_CODE_UNKNOWN = -1; OK = 0; diff --git a/components/copresence/proto/identity.proto b/components/copresence/proto/identity.proto index c89f6619f8303..6c104edd2e086 100644 --- a/components/copresence/proto/identity.proto +++ b/components/copresence/proto/identity.proto @@ -4,7 +4,6 @@ option optimize_for = LITE_RUNTIME; import "enums.proto"; message Identity { optional IdentityType type = 1; - optional string obfuscated_gaia_id = 2 [deprecated = true]; optional string chromecast_id = 3; optional string android_id = 4; optional string chrome_id = 5; diff --git a/components/copresence/proto/rpcs.proto b/components/copresence/proto/rpcs.proto index bdbed2ec9cb26..c138f9a87fdb1 100644 --- a/components/copresence/proto/rpcs.proto +++ b/components/copresence/proto/rpcs.proto @@ -5,24 +5,20 @@ import "codes.proto"; import "enums.proto"; import "data.proto"; message RequestHeader { - optional DeviceRegistration device_id = 3 [deprecated = true]; optional ClientVersion client_version = 4; optional ClientVersion framework_version = 5; optional int64 current_time_millis = 6; optional string registered_device_id = 7; repeated string experiment_override = 8; + optional DeviceFingerprint device_fingerprint = 10; optional string configuration_etag = 11; - extensions 9 to 9; } message ResponseHeader { - optional ErrorType error_type = 1 [deprecated = true]; optional DebugInfo debug_info = 2; optional Status status = 3; } message RegisterDeviceRequest { optional RequestHeader header = 1; - optional DeviceRegistration old_registration = 2 [deprecated = true]; - optional DeviceRegistration new_registration = 3 [deprecated = true]; optional PushServiceRegistration push_service = 5; optional DeviceIdentifiers device_identifiers = 6; } @@ -43,12 +39,10 @@ message UpdateSignalsRequest { message ManageMessagesRequest { repeated PublishedMessage message_to_publish = 1; repeated string id_to_unpublish = 2; - optional DeleteAll delete_all = 3; } message ManageSubscriptionsRequest { repeated Subscription subscription = 1; repeated string id_to_unsubscribe = 2; - optional DeleteAll delete_all = 3; } message ReportResponse { optional ResponseHeader header = 1; @@ -65,10 +59,8 @@ message UpdateSignalsResponse { message ManageMessagesResponse { optional util.error.Code status = 1; repeated MessageResult published_message_result = 3; - extensions 2 to 2; } message ManageSubscriptionsResponse { optional util.error.Code status = 1; repeated SubscriptionResult subscription_result = 3; - extensions 2 to 2; } diff --git a/components/copresence/public/copresence_client.h b/components/copresence/public/copresence_client.h index 40b8bf97bfe47..219a0ebc835be 100644 --- a/components/copresence/public/copresence_client.h +++ b/components/copresence/public/copresence_client.h @@ -36,9 +36,9 @@ struct PendingRequest { }; // The CopresenceClient class is the central interface for Copresence -// functionality. This class handles all the initialization and delegation of -// copresence tasks. Any user of copresence only needs to interact with this -// client. +// functionality. This class handles all the initialization and delegation +// of copresence tasks. Any user of copresence only needs to interact +// with this client. class CopresenceClient : public base::SupportsWeakPtr { public: // The delegate must outlive us. @@ -54,9 +54,6 @@ class CopresenceClient : public base::SupportsWeakPtr { const std::string& app_id, const StatusCallback& callback); - // Called before the API (and thus the Client) is destructed. - void Shutdown(); - private: void CompleteInitialization(); void InitStepComplete(const std::string& step, bool success); diff --git a/components/copresence/public/copresence_client_delegate.h b/components/copresence/public/copresence_client_delegate.h index 374eb96561a7f..ef7634cb81a17 100644 --- a/components/copresence/public/copresence_client_delegate.h +++ b/components/copresence/public/copresence_client_delegate.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 COMPONENTS_COPRESENCE_PUBLIC_COPRESENCE_DELEGATE_H_ -#define COMPONENTS_COPRESENCE_PUBLIC_COPRESENCE_DELEGATE_H_ +#ifndef COMPONENTS_COPRESENCE_PUBLIC_COPRESENCE_CLIENT_DELEGATE_H_ +#define COMPONENTS_COPRESENCE_PUBLIC_COPRESENCE_CLIENT_DELEGATE_H_ #include +#include #include "base/callback_forward.h" -#include "components/copresence/proto/rpcs.pb.h" namespace net { class URLRequestContextGetter; @@ -16,6 +16,7 @@ class URLRequestContextGetter; namespace copresence { +class Message; class WhispernetClient; enum CopresenceStatus { SUCCESS, FAIL }; @@ -31,21 +32,17 @@ class CopresenceClientDelegate { virtual void HandleMessages( const std::string& app_id, const std::string& subscription_id, - const std::vector& message) = 0; + const std::vector& message) = 0; virtual net::URLRequestContextGetter* GetRequestContext() const = 0; virtual const std::string GetPlatformVersionString() const = 0; - // Methods to set and and get our device ID. This needs to be saved to - // persistent storage to avoid flooding the Copresence server with generate - // id requests. - virtual const std::string GetDeviceId() const = 0; - virtual void SaveDeviceId(const std::string& device_id) = 0; + virtual const std::string GetAPIKey() const = 0; virtual WhispernetClient* GetWhispernetClient() = 0; }; } // namespace copresence -#endif // COMPONENTS_COPRESENCE_PUBLIC_COPRESENCE_DELEGATE_H_ +#endif // COMPONENTS_COPRESENCE_PUBLIC_COPRESENCE_CLIENT_DELEGATE_H_ diff --git a/components/copresence/public/whispernet_client.h b/components/copresence/public/whispernet_client.h index 61eeed0d15a66..a4e530c28fa67 100644 --- a/components/copresence/public/whispernet_client.h +++ b/components/copresence/public/whispernet_client.h @@ -18,6 +18,13 @@ class AudioBusRefCounted; namespace copresence { +struct AudioToken { + AudioToken(const std::string& token, bool audible) + : token(token), audible(audible) {} + std::string token; + bool audible; +}; + // The interface that the whispernet client needs to implement. These methods // provide us the ability to use the audio medium in copresence. Currently since // the only medium that copresence uses is audio, the implementation of this @@ -27,10 +34,11 @@ class WhispernetClient { // Generic callback to indicate a boolean success or failure. typedef base::Callback SuccessCallback; // Callback that returns detected tokens. - typedef base::Callback&)> TokensCallback; + typedef base::Callback&)> TokensCallback; // Callback that returns encoded samples for a given token. - typedef base::Callback< - void(const std::string&, const scoped_refptr&)> + typedef base::Callback&)> SamplesCallback; // Initialize the whispernet client and call the callback when done. The @@ -40,7 +48,7 @@ class WhispernetClient { virtual void Shutdown() = 0; // Fires an event to request a token encode. - virtual void EncodeToken(const std::string& token) = 0; + virtual void EncodeToken(const std::string& token, bool audible) = 0; // Fires an event to request a decode for the given samples. virtual void DecodeSamples(const std::string& samples) = 0; // Fires an event to request detection of a whispernet broadcast. @@ -62,7 +70,7 @@ class WhispernetClient { virtual SuccessCallback GetDetectBroadcastCallback() = 0; virtual SuccessCallback GetInitializedCallback() = 0; - virtual ~WhispernetClient() {}; + virtual ~WhispernetClient() {} }; } // namespace copresence diff --git a/components/copresence/rpc/http_post.cc b/components/copresence/rpc/http_post.cc new file mode 100644 index 0000000000000..0e2b40f17ece8 --- /dev/null +++ b/components/copresence/rpc/http_post.cc @@ -0,0 +1,95 @@ +// 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/copresence/rpc/http_post.h" + +// TODO(ckehoe): Support third-party protobufs too. +#include + +#include "base/bind.h" +#include "google_apis/google_api_keys.h" +#include "net/base/load_flags.h" +#include "net/base/url_util.h" +#include "net/http/http_status_code.h" +#include "net/url_request/url_fetcher.h" +#include "net/url_request/url_request_context_getter.h" +#include "url/gurl.h" + +namespace copresence { + +const char HttpPost::kApiKeyField[] = "key"; +const char HttpPost::kTracingTokenField[] = "trace"; + +HttpPost::HttpPost(net::URLRequestContextGetter* url_context_getter, + const std::string& server_host, + const std::string& rpc_name, + const std::string& tracing_token, + std::string api_key, + const google::protobuf::MessageLite& request_proto) { + // Create the base URL to call. + GURL url(server_host + "/" + rpc_name); + + // Add the tracing token, if specified. + if (!tracing_token.empty()) { + url = net::AppendQueryParameter( + url, kTracingTokenField, "token:" + tracing_token); + } + + // If no API key is specified, use the Chrome API key. + if (api_key.empty()) { +#ifdef GOOGLE_CHROME_BUILD + DCHECK(google_apis::HasKeysConfigured()); + api_key = google_apis::GetAPIKey(); +#else + LOG(ERROR) << "No Copresence API key provided"; +#endif + } + url = net::AppendQueryParameter(url, kApiKeyField, api_key); + + // Serialize the proto for transmission. + std::string request_data; + bool serialize_success = request_proto.SerializeToString(&request_data); + DCHECK(serialize_success); + + // Configure and send the request. + url_fetcher_.reset(net::URLFetcher::Create( + kUrlFetcherId, url, net::URLFetcher::POST, this)); + url_fetcher_->SetRequestContext(url_context_getter); + url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | + net::LOAD_DISABLE_CACHE | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SEND_AUTH_DATA); + url_fetcher_->SetUploadData("application/x-protobuf", request_data); +} + +HttpPost::~HttpPost() {} + +void HttpPost::Start(const ResponseCallback& response_callback) { + response_callback_ = response_callback; + url_fetcher_->Start(); +} + +void HttpPost::OnURLFetchComplete(const net::URLFetcher* source) { + DCHECK_EQ(url_fetcher_.get(), source); + + // Gather response info. + std::string response; + source->GetResponseAsString(&response); + int response_code = source->GetResponseCode(); + + // Log any errors. + if (response_code < 0) { + LOG(WARNING) << "Couldn't contact the Copresence server at " + << source->GetURL(); + } else if (response_code != net::HTTP_OK) { + LOG(WARNING) << "Copresence request got HTTP response code " + << response_code << ". Response:\n" << response; + } + + // Return the response. + response_callback_.Run(response_code, response); +} + +} // namespace copresence diff --git a/components/copresence/rpc/http_post.h b/components/copresence/rpc/http_post.h new file mode 100644 index 0000000000000..53a3d1c37f4a5 --- /dev/null +++ b/components/copresence/rpc/http_post.h @@ -0,0 +1,74 @@ +// 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_COPRESENCE_RPC_HTTP_POST_H_ +#define COMPONENTS_COPRESENCE_RPC_HTTP_POST_H_ + +#include + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "net/url_request/url_fetcher_delegate.h" + +namespace google { +namespace protobuf { +class MessageLite; +} +} + +namespace net { +class URLRequestContextGetter; +} + +namespace copresence { + +// This class handles all Apiary calls to the Copresence server. +// It configures the HTTP request appropriately and reports any errors. +// If deleted, the HTTP request is cancelled. +// +// TODO(ckehoe): Add retry logic. +class HttpPost : public net::URLFetcherDelegate { + public: + // Callback to receive the HTTP status code and body of the response + // (if any). A pointer to this HttpPost object is also passed along. + typedef base::Callback + ResponseCallback; + + // Create a request to the Copresence server. + // |url_context_getter| is owned by the caller, + // and the context it provides must be available until the request completes. + HttpPost(net::URLRequestContextGetter* url_context_getter, + const std::string& server_host, + const std::string& rpc_name, + const std::string& tracing_token, + std::string api_key, // If blank, we overwrite with a default. + const google::protobuf::MessageLite& request_proto); + + // HTTP requests are cancelled on delete. + virtual ~HttpPost(); + + // Send an HttpPost request. + void Start(const ResponseCallback& response_callback); + + private: + static const int kUrlFetcherId = 1; + static const char kApiKeyField[]; + static const char kTracingTokenField[]; + + friend class HttpPostTest; + + // Overridden from net::URLFetcherDelegate. + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + + ResponseCallback response_callback_; + + scoped_ptr url_fetcher_; + + DISALLOW_COPY_AND_ASSIGN(HttpPost); +}; + +} // namespace copresence + +#endif // COMPONENTS_COPRESENCE_RPC_HTTP_POST_H_ diff --git a/components/copresence/rpc/http_post_unittest.cc b/components/copresence/rpc/http_post_unittest.cc new file mode 100644 index 0000000000000..46a85b1a69e00 --- /dev/null +++ b/components/copresence/rpc/http_post_unittest.cc @@ -0,0 +1,141 @@ +// 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/copresence/rpc/http_post.h" + +#include "base/test/test_simple_task_runner.h" +#include "components/copresence/proto/data.pb.h" +#include "net/base/url_util.h" +#include "net/http/http_status_code.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_request_test_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "url/gurl.h" + +namespace { + +const char kFakeServerHost[] = "test.server.google.com"; +const char kRPCName[] = "testRpc"; +const char kTracingToken[] = "trace me!"; +const char kApiKey[] = "unlock ALL the APIz"; + +} // namespace + +using google::protobuf::MessageLite; + +namespace copresence { + +class HttpPostTest : public testing::Test { + public: + HttpPostTest() + : received_response_code_(0) { + context_getter_ = new net::TestURLRequestContextGetter( + make_scoped_refptr(new base::TestSimpleTaskRunner)); + proto_.set_client("test_client"); + proto_.set_version_code(123); + } + virtual ~HttpPostTest() {} + + // Record the response sent back to the client for verification. + void TestResponseCallback(int response_code, + const std::string& response) { + received_response_code_ = response_code; + received_response_ = response; + } + + protected: + bool ResponsePassedThrough(int response_code, const std::string& response) { + pending_post_ = new HttpPost(context_getter_.get(), + std::string("http://") + kFakeServerHost, + kRPCName, + "", + kApiKey, + proto_); + pending_post_->Start(base::Bind(&HttpPostTest::TestResponseCallback, + base::Unretained(this))); + net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID( + HttpPost::kUrlFetcherId); + fetcher->set_response_code(response_code); + fetcher->SetResponseString(response); + fetcher->delegate()->OnURLFetchComplete(fetcher); + delete pending_post_; + return received_response_code_ == response_code && + received_response_ == response; + } + + net::TestURLFetcher* GetFetcher() { + return fetcher_factory_.GetFetcherByID(HttpPost::kUrlFetcherId); + } + + const std::string GetApiKeySent() { + std::string api_key_sent; + net::GetValueForKeyInQuery(GetFetcher()->GetOriginalURL(), + HttpPost::kApiKeyField, + &api_key_sent); + return api_key_sent; + } + + const std::string GetTracingTokenSent() { + std::string tracing_token_sent; + net::GetValueForKeyInQuery(GetFetcher()->GetOriginalURL(), + HttpPost::kTracingTokenField, + &tracing_token_sent); + return tracing_token_sent; + } + + net::TestURLFetcherFactory fetcher_factory_; + scoped_refptr context_getter_; + + ClientVersion proto_; + + int received_response_code_; + std::string received_response_; + + private: + HttpPost* pending_post_; +}; + +TEST_F(HttpPostTest, OKResponse) { + // "Send" the proto to the "server". + HttpPost* post = new HttpPost(context_getter_.get(), + std::string("http://") + kFakeServerHost, + kRPCName, + kTracingToken, + kApiKey, + proto_); + post->Start(base::Bind(&HttpPostTest::TestResponseCallback, + base::Unretained(this))); + + // Verify that the data was sent to the right place. + GURL requested_url = GetFetcher()->GetOriginalURL(); + EXPECT_EQ(kFakeServerHost, requested_url.host()); + EXPECT_EQ(std::string("/") + kRPCName, requested_url.path()); + + // Check query parameters. + EXPECT_EQ(kApiKey, GetApiKeySent()); + EXPECT_EQ(std::string("token:") + kTracingToken, GetTracingTokenSent()); + + // Verify that the right data was sent. + std::string upload_data; + ASSERT_TRUE(proto_.SerializeToString(&upload_data)); + EXPECT_EQ(upload_data, GetFetcher()->upload_data()); + + // Send a response and check that it's passed along correctly. + GetFetcher()->set_response_code(net::HTTP_OK); + GetFetcher()->SetResponseString("Hello World!"); + GetFetcher()->delegate()->OnURLFetchComplete(GetFetcher()); + EXPECT_EQ(net::HTTP_OK, received_response_code_); + EXPECT_EQ("Hello World!", received_response_); + delete post; +} + +TEST_F(HttpPostTest, ErrorResponse) { + EXPECT_TRUE(ResponsePassedThrough( + net::HTTP_BAD_REQUEST, "Bad client. Shame on you.")); + EXPECT_TRUE(ResponsePassedThrough( + net::HTTP_INTERNAL_SERVER_ERROR, "I'm dying. Forgive me.")); + EXPECT_TRUE(ResponsePassedThrough(-1, "")); +} + +} // namespace copresence diff --git a/components/copresence/rpc/rpc_handler.cc b/components/copresence/rpc/rpc_handler.cc index e5f12d0c4d969..0b60a77fa92fb 100644 --- a/components/copresence/rpc/rpc_handler.cc +++ b/components/copresence/rpc/rpc_handler.cc @@ -4,39 +4,574 @@ #include "components/copresence/rpc/rpc_handler.h" +#include + #include "base/bind.h" +#include "base/command_line.h" +#include "base/guid.h" +#include "base/logging.h" +#include "base/strings/string_util.h" +#include "base/time/time.h" +#include "components/copresence/copresence_switches.h" +#include "components/copresence/handlers/directive_handler.h" +#include "components/copresence/proto/codes.pb.h" #include "components/copresence/proto/data.pb.h" #include "components/copresence/proto/rpcs.pb.h" #include "components/copresence/public/copresence_client_delegate.h" -#include "components/copresence/public/whispernet_client.h" +#include "net/http/http_status_code.h" + +// TODO(ckehoe): Return error messages for bad requests. namespace copresence { -RpcHandler::RpcHandler(CopresenceClientDelegate* delegate, - SuccessCallback init_done_callback) { +using google::protobuf::MessageLite; +using google::protobuf::RepeatedPtrField; + +const char RpcHandler::kReportRequestRpcName[] = "report"; + +namespace { + +// UrlSafe is defined as: +// '/' represented by a '_' and '+' represented by a '-' +// TODO(rkc): Move this to the wrapper. +std::string ToUrlSafe(std::string token) { + base::ReplaceChars(token, "+", "-", &token); + base::ReplaceChars(token, "/", "_", &token); + return token; +} + +const int kInvalidTokenExpiryTimeMs = 10 * 60 * 1000; // 10 minutes. +const int kMaxInvalidTokens = 10000; +const char kRegisterDeviceRpcName[] = "registerdevice"; +const char kDefaultCopresenceServer[] = + "https://www.googleapis.com/copresence/v2/copresence"; + +// Logging + +// Checks for a copresence error. If there is one, logs it and returns true. +bool CopresenceErrorLogged(const Status& status) { + if (status.code() != OK) { + LOG(ERROR) << "Copresence error code " << status.code() + << (status.message().empty() ? std::string() : + ": " + status.message()); + } + return status.code() != OK; +} + +void LogIfErrorStatus(const util::error::Code& code, + const std::string& context) { + LOG_IF(ERROR, code != util::error::OK) + << context << " error " << code << ". See " + << "cs/google3/util/task/codes.proto for more info."; +} + +// If any errors occurred, logs them and returns true. +bool ReportErrorLogged(const ReportResponse& response) { + bool result = CopresenceErrorLogged(response.header().status()); + + // The Report fails or succeeds as a unit. If any responses had errors, + // the header will too. Thus we don't need to propagate individual errors. + if (response.has_update_signals_response()) + LogIfErrorStatus(response.update_signals_response().status(), "Update"); + if (response.has_manage_messages_response()) + LogIfErrorStatus(response.manage_messages_response().status(), "Publish"); + if (response.has_manage_subscriptions_response()) { + LogIfErrorStatus(response.manage_subscriptions_response().status(), + "Subscribe"); + } + + return result; +} + +// Request construction +// TODO(ckehoe): Move these into a separate file? + +template +BroadcastScanConfiguration GetBroadcastScanConfig(const T& msg) { + if (msg.has_token_exchange_strategy() && + msg.token_exchange_strategy().has_broadcast_scan_configuration()) { + return msg.token_exchange_strategy().broadcast_scan_configuration(); + } + return BROADCAST_SCAN_CONFIGURATION_UNKNOWN; +} + +// This method will extract token exchange strategies +// from the publishes and subscribes in a report request. +// TODO(ckehoe): Delete this when the server supports +// BroadcastScanConfiguration. +BroadcastScanConfiguration ExtractTokenExchangeStrategy( + const ReportRequest& request) { + bool broadcast_only = false; + bool scan_only = false; + + // Strategies for publishes. + if (request.has_manage_messages_request()) { + const RepeatedPtrField& messages = + request.manage_messages_request().message_to_publish(); + for (int i = 0; i < messages.size(); ++i) { + BroadcastScanConfiguration config = + GetBroadcastScanConfig(messages.Get(i)); + broadcast_only = broadcast_only || config == BROADCAST_ONLY; + scan_only = scan_only || config == SCAN_ONLY; + if (config == BROADCAST_AND_SCAN || (broadcast_only && scan_only)) + return BROADCAST_AND_SCAN; + } + } + + // Strategies for subscriptions. + if (request.has_manage_subscriptions_request()) { + const RepeatedPtrField subscriptions = + request.manage_subscriptions_request().subscription(); + for (int i = 0; i < subscriptions.size(); ++i) { + BroadcastScanConfiguration config = + GetBroadcastScanConfig(subscriptions.Get(i)); + broadcast_only = broadcast_only || config == BROADCAST_ONLY; + scan_only = scan_only || config == SCAN_ONLY; + if (config == BROADCAST_AND_SCAN || (broadcast_only && scan_only)) + return BROADCAST_AND_SCAN; + } + } + + if (broadcast_only) + return BROADCAST_ONLY; + if (scan_only) + return SCAN_ONLY; + + // If nothing else is specified, default to both broadcast and scan. + return BROADCAST_AND_SCAN; } +// TODO(rkc): Fix this hack once the server supports setting strategies per +// operation. +bool ExtractIsAudibleStrategy(const ReportRequest& request) { + if (request.has_manage_messages_request()) { + const RepeatedPtrField messages = + request.manage_messages_request().message_to_publish(); + for (int i = 0; i < messages.size(); ++i) { + const PublishedMessage& msg = messages.Get(i); + if (msg.has_token_exchange_strategy() && + msg.token_exchange_strategy().has_use_audible() && + msg.token_exchange_strategy().use_audible()) { + return true; + } + } + } + return false; +} + +scoped_ptr GetDeviceCapabilities(const ReportRequest& request) { + scoped_ptr state(new DeviceState); + +// TODO(ckehoe): Currently this code causes a linker error on Windows. +#ifndef OS_WIN + TokenTechnology* token_technology = + state->mutable_capabilities()->add_token_technology(); + token_technology->set_medium(AUDIO_ULTRASOUND_PASSBAND); + if (ExtractIsAudibleStrategy(request)) + token_technology->set_medium(AUDIO_AUDIBLE_DTMF); + + BroadcastScanConfiguration config = + ExtractTokenExchangeStrategy(request); + if (config == BROADCAST_ONLY || config == BROADCAST_AND_SCAN) + token_technology->add_instruction_type(TRANSMIT); + if (config == SCAN_ONLY || config == BROADCAST_AND_SCAN) + token_technology->add_instruction_type(RECEIVE); +#endif + + return state.Pass(); +} + +// TODO(ckehoe): We're keeping this code in a separate function for now +// because we get a version string from Chrome, but the proto expects +// an int64 version. We should probably change the version proto +// to handle a more detailed version. +ClientVersion* CreateVersion(const std::string& client, + const std::string& version_name) { + ClientVersion* version = new ClientVersion; + + version->set_client(client); + version->set_version_name(version_name); + + return version; +} + +void AddTokenToRequest(ReportRequest* request, const AudioToken& token) { + TokenObservation* token_observation = + request->mutable_update_signals_request()->add_token_observation(); + token_observation->set_token_id(ToUrlSafe(token.token)); + + TokenSignals* signals = token_observation->add_signals(); + signals->set_medium(token.audible ? AUDIO_AUDIBLE_DTMF + : AUDIO_ULTRASOUND_PASSBAND); + signals->set_observed_time_millis(base::Time::Now().ToJsTime()); +} + +} // namespace + +// Public methods + +RpcHandler::RpcHandler(CopresenceClientDelegate* delegate) + : delegate_(delegate), + invalid_audio_token_cache_( + base::TimeDelta::FromMilliseconds(kInvalidTokenExpiryTimeMs), + kMaxInvalidTokens), + server_post_callback_(base::Bind(&RpcHandler::SendHttpPost, + base::Unretained(this))) {} + RpcHandler::~RpcHandler() { + for (std::set::iterator post = pending_posts_.begin(); + post != pending_posts_.end(); ++post) { + delete *post; + } + + if (delegate_ && delegate_->GetWhispernetClient()) { + delegate_->GetWhispernetClient()->RegisterTokensCallback( + WhispernetClient::TokensCallback()); + } } -void RpcHandler::SendReportRequest( - scoped_ptr request) { +void RpcHandler::Initialize(const SuccessCallback& init_done_callback) { + scoped_ptr request(new RegisterDeviceRequest); + DCHECK(device_id_.empty()); + + request->mutable_push_service()->set_service(PUSH_SERVICE_NONE); + Identity* identity = + request->mutable_device_identifiers()->mutable_registrant(); + identity->set_type(CHROME); + identity->set_chrome_id(base::GenerateGUID()); + SendServerRequest( + kRegisterDeviceRpcName, + std::string(), + request.Pass(), + base::Bind(&RpcHandler::RegisterResponseHandler, + // On destruction, this request will be cancelled. + base::Unretained(this), + init_done_callback)); } -void RpcHandler::SendReportRequest( - scoped_ptr request, - const std::string& app_id, - const StatusCallback& status_callback) { +void RpcHandler::SendReportRequest(scoped_ptr request) { + SendReportRequest(request.Pass(), std::string(), StatusCallback()); } -void RpcHandler::ReportTokens(copresence::TokenMedium medium, - const std::vector& tokens) { +void RpcHandler::SendReportRequest(scoped_ptr request, + const std::string& app_id, + const StatusCallback& status_callback) { + DCHECK(request.get()); + DCHECK(!device_id_.empty()) + << "RpcHandler::Initialize() must complete successfully " + << "before other RpcHandler methods are called."; + + DVLOG(3) << "Sending report request to server."; + + // If we are unpublishing or unsubscribing, we need to stop those publish or + // subscribes right away, we don't need to wait for the server to tell us. + ProcessRemovedOperations(*request); + + request->mutable_update_signals_request()->set_allocated_state( + GetDeviceCapabilities(*request).release()); + + AddPlayingTokens(request.get()); + + // TODO(ckehoe): Currently the server supports only BROADCAST_AND_SCAN. + // Remove this once b/16715253 is fixed. + if (request->has_manage_messages_request()) { + RepeatedPtrField* messages = request + ->mutable_manage_messages_request()->mutable_message_to_publish(); + for (int i = 0; i < messages->size(); ++i) { + messages->Mutable(i)->mutable_token_exchange_strategy() + ->set_broadcast_scan_configuration(BROADCAST_AND_SCAN); + } + } + if (request->has_manage_subscriptions_request()) { + RepeatedPtrField* subscriptions = + request->mutable_manage_subscriptions_request()->mutable_subscription(); + for (int i = 0; i < subscriptions->size(); ++i) { + subscriptions->Mutable(i)->mutable_token_exchange_strategy() + ->set_broadcast_scan_configuration(BROADCAST_AND_SCAN); + } + } + + SendServerRequest(kReportRequestRpcName, + app_id, + request.Pass(), + // On destruction, this request will be cancelled. + base::Bind(&RpcHandler::ReportResponseHandler, + base::Unretained(this), + status_callback)); +} + +void RpcHandler::ReportTokens(const std::vector& tokens) { + DCHECK(!tokens.empty()); + + scoped_ptr request(new ReportRequest); + for (size_t i = 0; i < tokens.size(); ++i) { + if (invalid_audio_token_cache_.HasKey(ToUrlSafe(tokens[i].token))) + continue; + DVLOG(3) << "Sending token " << tokens[i].token << " to server."; + AddTokenToRequest(request.get(), tokens[i]); + } + SendReportRequest(request.Pass()); +} + +void RpcHandler::ConnectToWhispernet() { + WhispernetClient* whispernet_client = delegate_->GetWhispernetClient(); + + // |directive_handler_| will be destructed with us, so unretained is safe. + directive_handler_.reset(new DirectiveHandler); + directive_handler_->Initialize( + base::Bind(&WhispernetClient::DecodeSamples, + base::Unretained(whispernet_client)), + base::Bind(&RpcHandler::AudioDirectiveListToWhispernetConnector, + base::Unretained(this))); + + whispernet_client->RegisterTokensCallback( + base::Bind(&RpcHandler::ReportTokens, + // On destruction, this callback will be disconnected. + base::Unretained(this))); +} + +// Private methods + +void RpcHandler::RegisterResponseHandler( + const SuccessCallback& init_done_callback, + HttpPost* completed_post, + int http_status_code, + const std::string& response_data) { + if (completed_post) { + int elements_erased = pending_posts_.erase(completed_post); + DCHECK(elements_erased); + delete completed_post; + } + + if (http_status_code != net::HTTP_OK) { + init_done_callback.Run(false); + return; + } + + RegisterDeviceResponse response; + if (!response.ParseFromString(response_data)) { + LOG(ERROR) << "Invalid RegisterDeviceResponse:\n" << response_data; + init_done_callback.Run(false); + return; + } + + if (CopresenceErrorLogged(response.header().status())) + return; + device_id_ = response.registered_device_id(); + DCHECK(!device_id_.empty()); + DVLOG(2) << "Device registration successful: id " << device_id_; + init_done_callback.Run(true); } -void RpcHandler::ConnectToWhispernet(WhispernetClient* whispernet_client) { +void RpcHandler::ReportResponseHandler(const StatusCallback& status_callback, + HttpPost* completed_post, + int http_status_code, + const std::string& response_data) { + if (completed_post) { + int elements_erased = pending_posts_.erase(completed_post); + DCHECK(elements_erased); + delete completed_post; + } + + if (http_status_code != net::HTTP_OK) { + if (!status_callback.is_null()) + status_callback.Run(FAIL); + return; + } + + DVLOG(3) << "Received ReportResponse."; + ReportResponse response; + if (!response.ParseFromString(response_data)) { + LOG(ERROR) << "Invalid ReportResponse"; + if (!status_callback.is_null()) + status_callback.Run(FAIL); + return; + } + + if (ReportErrorLogged(response)) { + if (!status_callback.is_null()) + status_callback.Run(FAIL); + return; + } + + const RepeatedPtrField& message_results = + response.manage_messages_response().published_message_result(); + for (int i = 0; i < message_results.size(); ++i) { + DVLOG(2) << "Published message with id " + << message_results.Get(i).published_message_id(); + } + + const RepeatedPtrField& subscription_results = + response.manage_subscriptions_response().subscription_result(); + for (int i = 0; i < subscription_results.size(); ++i) { + DVLOG(2) << "Created subscription with id " + << subscription_results.Get(i).subscription_id(); + } + + if (response.has_update_signals_response()) { + const UpdateSignalsResponse& update_response = + response.update_signals_response(); + DispatchMessages(update_response.message()); + + if (directive_handler_.get()) { + for (int i = 0; i < update_response.directive_size(); ++i) + directive_handler_->AddDirective(update_response.directive(i)); + } else { + DVLOG(1) << "No directive handler."; + } + + const RepeatedPtrField& tokens = update_response.token(); + for (int i = 0; i < tokens.size(); ++i) { + switch (tokens.Get(i).status()) { + case VALID: + // TODO(rkc/ckehoe): Store the token in a |valid_token_cache_| with a + // short TTL (like 10s) and send it up with every report request. + // Then we'll still get messages while we're waiting to hear it again. + VLOG(1) << "Got valid token " << tokens.Get(i).id(); + break; + case INVALID: + DVLOG(3) << "Discarding invalid token " << tokens.Get(i).id(); + invalid_audio_token_cache_.Add(tokens.Get(i).id(), true); + break; + default: + DVLOG(2) << "Token " << tokens.Get(i).id() << " has status code " + << tokens.Get(i).status(); + } + } + } + + // TODO(ckehoe): Return a more detailed status response. + if (!status_callback.is_null()) + status_callback.Run(SUCCESS); +} + +void RpcHandler::ProcessRemovedOperations(const ReportRequest& request) { + // Remove unpublishes. + if (request.has_manage_messages_request()) { + const RepeatedPtrField& unpublishes = + request.manage_messages_request().id_to_unpublish(); + for (int i = 0; i < unpublishes.size(); ++i) + directive_handler_->RemoveDirectives(unpublishes.Get(i)); + } + + // Remove unsubscribes. + if (request.has_manage_subscriptions_request()) { + const RepeatedPtrField& unsubscribes = + request.manage_subscriptions_request().id_to_unsubscribe(); + for (int i = 0; i < unsubscribes.size(); ++i) + directive_handler_->RemoveDirectives(unsubscribes.Get(i)); + } +} + +void RpcHandler::AddPlayingTokens(ReportRequest* request) { + if (!directive_handler_) + return; + + const std::string& audible_token = directive_handler_->CurrentAudibleToken(); + const std::string& inaudible_token = + directive_handler_->CurrentInaudibleToken(); + + if (!audible_token.empty()) + AddTokenToRequest(request, AudioToken(audible_token, true)); + if (!inaudible_token.empty()) + AddTokenToRequest(request, AudioToken(inaudible_token, false)); +} + +void RpcHandler::DispatchMessages( + const RepeatedPtrField& messages) { + if (messages.size() == 0) + return; + + // Index the messages by subscription id. + std::map > messages_by_subscription; + DVLOG(3) << "Dispatching " << messages.size() << " messages"; + for (int m = 0; m < messages.size(); ++m) { + const RepeatedPtrField& subscription_ids = + messages.Get(m).subscription_id(); + for (int s = 0; s < subscription_ids.size(); ++s) { + messages_by_subscription[subscription_ids.Get(s)].push_back( + messages.Get(m).published_message()); + } + } + + // Send the messages for each subscription. + for (std::map >::const_iterator + subscription = messages_by_subscription.begin(); + subscription != messages_by_subscription.end(); + ++subscription) { + // TODO(ckehoe): Once we have the app ID from the server, we need to pass + // it in here and get rid of the app id registry from the main API class. + delegate_->HandleMessages("", subscription->first, subscription->second); + } +} + +RequestHeader* RpcHandler::CreateRequestHeader( + const std::string& client_name) const { + RequestHeader* header = new RequestHeader; + + header->set_allocated_framework_version(CreateVersion( + "Chrome", delegate_->GetPlatformVersionString())); + if (!client_name.empty()) { + header->set_allocated_client_version( + CreateVersion(client_name, std::string())); + } + header->set_current_time_millis(base::Time::Now().ToJsTime()); + header->set_registered_device_id(device_id_); + + DeviceFingerprint* fingerprint = new DeviceFingerprint; + fingerprint->set_platform_version(delegate_->GetPlatformVersionString()); + fingerprint->set_type(CHROME_PLATFORM_TYPE); + header->set_allocated_device_fingerprint(fingerprint); + + return header; +} + +template +void RpcHandler::SendServerRequest( + const std::string& rpc_name, + const std::string& app_id, + scoped_ptr request, + const PostCleanupCallback& response_handler) { + request->set_allocated_header(CreateRequestHeader(app_id)); + server_post_callback_.Run(delegate_->GetRequestContext(), + rpc_name, + make_scoped_ptr(request.release()), + response_handler); +} + +void RpcHandler::SendHttpPost(net::URLRequestContextGetter* url_context_getter, + const std::string& rpc_name, + scoped_ptr request_proto, + const PostCleanupCallback& callback) { + // Create the base URL to call. + CommandLine* command_line = CommandLine::ForCurrentProcess(); + const std::string copresence_server_host = + command_line->HasSwitch(switches::kCopresenceServer) ? + command_line->GetSwitchValueASCII(switches::kCopresenceServer) : + kDefaultCopresenceServer; + + // Create the request and keep a pointer until it completes. + HttpPost* http_post = new HttpPost( + url_context_getter, + copresence_server_host, + rpc_name, + command_line->GetSwitchValueASCII(switches::kCopresenceTracingToken), + delegate_->GetAPIKey(), + *request_proto); + + http_post->Start(base::Bind(callback, http_post)); + pending_posts_.insert(http_post); } -void RpcHandler::DisconnectFromWhispernet() { +void RpcHandler::AudioDirectiveListToWhispernetConnector( + const std::string& token, + bool audible, + const WhispernetClient::SamplesCallback& samples_callback) { + WhispernetClient* whispernet_client = delegate_->GetWhispernetClient(); + if (whispernet_client) { + whispernet_client->RegisterSamplesCallback(samples_callback); + whispernet_client->EncodeToken(token, audible); + } } } // namespace copresence diff --git a/components/copresence/rpc/rpc_handler.h b/components/copresence/rpc/rpc_handler.h index dee7220ac7804..bfd14cd973050 100644 --- a/components/copresence/rpc/rpc_handler.h +++ b/components/copresence/rpc/rpc_handler.h @@ -5,50 +5,129 @@ #ifndef COMPONENTS_COPRESENCE_RPC_RPC_HANDLER_H_ #define COMPONENTS_COPRESENCE_RPC_RPC_HANDLER_H_ +#include #include #include #include "base/callback.h" -#include "base/macros.h" #include "base/memory/scoped_ptr.h" +#include "components/copresence/proto/enums.pb.h" #include "components/copresence/public/copresence_client_delegate.h" +#include "components/copresence/public/whispernet_client.h" +#include "components/copresence/rpc/http_post.h" +#include "components/copresence/timed_map.h" namespace copresence { +class DirectiveHandler; class ReportRequest; -class WhispernetClient; +class RequestHeader; +class SubscribedMessage; // This class currently handles all communication with the copresence server. -// NOTE: This class is a stub. class RpcHandler { public: // A callback to indicate whether handler initialization succeeded. typedef base::Callback SuccessCallback; - // Constructor. May call the server to register a device, - // so completion status is indicated by the SuccessCallback. - RpcHandler(CopresenceClientDelegate* delegate, - SuccessCallback init_done_callback); + // Report rpc name to send to Apiary. + static const char kReportRequestRpcName[]; + + // Constructor. |delegate| is owned by the caller, + // and must be valid as long as the RpcHandler exists. + explicit RpcHandler(CopresenceClientDelegate* delegate); virtual ~RpcHandler(); + // Clients must call this and wait for |init_done_callback| + // to be called before invoking any other methods. + void Initialize(const SuccessCallback& init_done_callback); + // Send a report request - void SendReportRequest(scoped_ptr request); - void SendReportRequest(scoped_ptr request, + void SendReportRequest(scoped_ptr request); + void SendReportRequest(scoped_ptr request, const std::string& app_id, const StatusCallback& callback); // Report a set of tokens to the server for a given medium. - void ReportTokens(copresence::TokenMedium medium, - const std::vector& tokens); - - // Create the directive handler and connect it to the whispernet client. - void ConnectToWhispernet(WhispernetClient* whispernet_client); + void ReportTokens(const std::vector& tokens); - // Disconnect the directive handler from the whispernet client. - void DisconnectFromWhispernet(); + // Create the directive handler and connect it to + // the whispernet client specified by the delegate. + void ConnectToWhispernet(); private: + // An HttpPost::ResponseCallback prepended with an HttpPost object + // that needs to be deleted. + typedef base::Callback + PostCleanupCallback; + + // Callback to allow tests to stub out HTTP POST behavior. + // Arguments: + // URLRequestContextGetter: Context for the HTTP POST request. + // string: Name of the rpc to invoke. URL format: server.google.com/rpc_name + // MessageLite: Contents of POST request to be sent. This needs to be + // a (scoped) pointer to ease handling of the abstract MessageLite class. + // ResponseCallback: Receives the response to the request. + typedef base::Callback, + const PostCleanupCallback&)> PostCallback; + + friend class RpcHandlerTest; + + void RegisterResponseHandler(const SuccessCallback& init_done_callback, + HttpPost* completed_post, + int http_status_code, + const std::string& response_data); + void ReportResponseHandler(const StatusCallback& status_callback, + HttpPost* completed_post, + int http_status_code, + const std::string& response_data); + + // If the request has any unpublish or unsubscribe operations, it removes + // them from our directive handlers. + void ProcessRemovedOperations(const ReportRequest& request); + + // Add all currently playing tokens to the update signals in this report + // request. This ensures that the server doesn't keep issueing new tokens to + // us when we're already playing valid tokens. + void AddPlayingTokens(ReportRequest* request); + + void DispatchMessages( + const google::protobuf::RepeatedPtrField& + subscribed_messages); + + RequestHeader* CreateRequestHeader(const std::string& client_name) const; + + template + void SendServerRequest(const std::string& rpc_name, + const std::string& app_id, + scoped_ptr request, + const PostCleanupCallback& response_handler); + + // Wrapper for the http post constructor. This is the default way + // to contact the server, but it can be overridden for testing. + void SendHttpPost(net::URLRequestContextGetter* url_context_getter, + const std::string& rpc_name, + scoped_ptr request_proto, + const PostCleanupCallback& callback); + + // This method receives the request to encode a token and forwards it to + // whispernet, setting the samples return callback to samples_callback. + void AudioDirectiveListToWhispernetConnector( + const std::string& token, + bool audible, + const WhispernetClient::SamplesCallback& samples_callback); + + CopresenceClientDelegate* delegate_; // Belongs to the caller. + TimedMap invalid_audio_token_cache_; + PostCallback server_post_callback_; + + std::string device_id_; + scoped_ptr directive_handler_; + std::set pending_posts_; + DISALLOW_COPY_AND_ASSIGN(RpcHandler); }; diff --git a/components/copresence/rpc/rpc_handler_unittest.cc b/components/copresence/rpc/rpc_handler_unittest.cc new file mode 100644 index 0000000000000..b8d6b9bf89184 --- /dev/null +++ b/components/copresence/rpc/rpc_handler_unittest.cc @@ -0,0 +1,354 @@ +// 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/copresence/rpc/rpc_handler.h" + +#include +#include +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/message_loop/message_loop.h" +#include "components/copresence/handlers/directive_handler.h" +#include "components/copresence/proto/data.pb.h" +#include "components/copresence/proto/enums.pb.h" +#include "components/copresence/proto/rpcs.pb.h" +#include "net/http/http_status_code.h" +#include "testing/gtest/include/gtest/gtest.h" + +using google::protobuf::MessageLite; +using google::protobuf::RepeatedPtrField; + +namespace copresence { + +namespace { + +const char kChromeVersion[] = "Chrome Version String"; + +void AddMessageWithStrategy(ReportRequest* report, + BroadcastScanConfiguration strategy) { + report->mutable_manage_messages_request()->add_message_to_publish() + ->mutable_token_exchange_strategy()->set_broadcast_scan_configuration( + strategy); +} + +void AddSubscriptionWithStrategy(ReportRequest* report, + BroadcastScanConfiguration strategy) { + report->mutable_manage_subscriptions_request()->add_subscription() + ->mutable_token_exchange_strategy()->set_broadcast_scan_configuration( + strategy); +} + +void CreateSubscribedMessage(const std::vector& subscription_ids, + const std::string& message_string, + SubscribedMessage* message_proto) { + message_proto->mutable_published_message()->set_payload(message_string); + for (std::vector::const_iterator subscription_id = + subscription_ids.begin(); + subscription_id != subscription_ids.end(); + ++subscription_id) { + message_proto->add_subscription_id(*subscription_id); + } +} + +// TODO(ckehoe): Make DirectiveHandler an interface. +class FakeDirectiveHandler : public DirectiveHandler { + public: + FakeDirectiveHandler() {} + virtual ~FakeDirectiveHandler() {} + + const std::vector& added_directives() const { + return added_directives_; + } + + virtual void Initialize( + const AudioRecorder::DecodeSamplesCallback& decode_cb, + const AudioDirectiveHandler::EncodeTokenCallback& encode_cb) OVERRIDE {} + + virtual void AddDirective(const Directive& directive) OVERRIDE { + added_directives_.push_back(directive); + } + + virtual void RemoveDirectives(const std::string& op_id) OVERRIDE { + // TODO(ckehoe): Add a parallel implementation when prod has one. + } + + private: + std::vector added_directives_; + + DISALLOW_COPY_AND_ASSIGN(FakeDirectiveHandler); +}; + +} // namespace + +class RpcHandlerTest : public testing::Test, public CopresenceClientDelegate { + public: + RpcHandlerTest() : rpc_handler_(this), status_(SUCCESS), api_key_("API key") { + rpc_handler_.server_post_callback_ = + base::Bind(&RpcHandlerTest::CaptureHttpPost, base::Unretained(this)); + rpc_handler_.device_id_ = "Device ID"; + } + + void CaptureHttpPost( + net::URLRequestContextGetter* url_context_getter, + const std::string& rpc_name, + scoped_ptr request_proto, + const RpcHandler::PostCleanupCallback& response_callback) { + rpc_name_ = rpc_name; + request_proto_ = request_proto.Pass(); + } + + void CaptureStatus(CopresenceStatus status) { + status_ = status; + } + + inline const ReportRequest* GetReportSent() { + return static_cast(request_proto_.get()); + } + +// TODO(ckehoe): Fix this on Windows. See rpc_handler.cc. +#ifndef OS_WIN + const TokenTechnology& GetTokenTechnologyFromReport() { + return GetReportSent()->update_signals_request().state().capabilities() + .token_technology(0); + } +#endif + + const RepeatedPtrField& GetMessagesPublished() { + return GetReportSent()->manage_messages_request().message_to_publish(); + } + + const RepeatedPtrField& GetSubscriptionsSent() { + return GetReportSent()->manage_subscriptions_request().subscription(); + } + + void SetDeviceId(const std::string& device_id) { + rpc_handler_.device_id_ = device_id; + } + + const std::string& GetDeviceId() { + return rpc_handler_.device_id_; + } + + void AddInvalidToken(const std::string& token) { + rpc_handler_.invalid_audio_token_cache_.Add(token, true); + } + + bool TokenIsInvalid(const std::string& token) { + return rpc_handler_.invalid_audio_token_cache_.HasKey(token); + } + + FakeDirectiveHandler* InstallFakeDirectiveHandler() { + FakeDirectiveHandler* handler = new FakeDirectiveHandler; + rpc_handler_.directive_handler_.reset(handler); + return handler; + } + + void InvokeReportResponseHandler(int status_code, + const std::string& response) { + rpc_handler_.ReportResponseHandler( + base::Bind(&RpcHandlerTest::CaptureStatus, base::Unretained(this)), + NULL, + status_code, + response); + } + + // CopresenceClientDelegate implementation + + virtual void HandleMessages( + const std::string& app_id, + const std::string& subscription_id, + const std::vector& messages) OVERRIDE { + // app_id is unused for now, pending a server fix. + messages_by_subscription_[subscription_id] = messages; + } + + virtual net::URLRequestContextGetter* GetRequestContext() const OVERRIDE { + return NULL; + } + + virtual const std::string GetPlatformVersionString() const OVERRIDE { + return kChromeVersion; + } + + virtual const std::string GetAPIKey() const OVERRIDE { + return api_key_; + } + + virtual WhispernetClient* GetWhispernetClient() OVERRIDE { + return NULL; + } + + protected: + // For rpc_handler_.invalid_audio_token_cache_ + base::MessageLoop message_loop_; + + RpcHandler rpc_handler_; + CopresenceStatus status_; + std::string api_key_; + + std::string rpc_name_; + scoped_ptr request_proto_; + std::map > messages_by_subscription_; +}; + +TEST_F(RpcHandlerTest, Initialize) { + SetDeviceId(""); + rpc_handler_.Initialize(RpcHandler::SuccessCallback()); + RegisterDeviceRequest* registration = + static_cast(request_proto_.get()); + Identity identity = registration->device_identifiers().registrant(); + EXPECT_EQ(CHROME, identity.type()); + EXPECT_FALSE(identity.chrome_id().empty()); +} + +// TODO(ckehoe): Fix this on Windows. See rpc_handler.cc. +#ifndef OS_WIN + +TEST_F(RpcHandlerTest, GetDeviceCapabilities) { + // Empty request. + rpc_handler_.SendReportRequest(make_scoped_ptr(new ReportRequest)); + EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); + const TokenTechnology* token_technology = &GetTokenTechnologyFromReport(); + EXPECT_EQ(AUDIO_ULTRASOUND_PASSBAND, token_technology->medium()); + EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); + EXPECT_EQ(RECEIVE, token_technology->instruction_type(1)); + + // Request with broadcast only. + scoped_ptr report(new ReportRequest); + AddMessageWithStrategy(report.get(), BROADCAST_ONLY); + rpc_handler_.SendReportRequest(report.Pass()); + token_technology = &GetTokenTechnologyFromReport(); + EXPECT_EQ(1, token_technology->instruction_type_size()); + EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); + EXPECT_FALSE(GetReportSent()->has_manage_subscriptions_request()); + + // Request with scan only. + report.reset(new ReportRequest); + AddSubscriptionWithStrategy(report.get(), SCAN_ONLY); + AddSubscriptionWithStrategy(report.get(), SCAN_ONLY); + rpc_handler_.SendReportRequest(report.Pass()); + token_technology = &GetTokenTechnologyFromReport(); + EXPECT_EQ(1, token_technology->instruction_type_size()); + EXPECT_EQ(RECEIVE, token_technology->instruction_type(0)); + EXPECT_FALSE(GetReportSent()->has_manage_messages_request()); + + // Request with both scan and broadcast only (conflict). + report.reset(new ReportRequest); + AddMessageWithStrategy(report.get(), SCAN_ONLY); + AddMessageWithStrategy(report.get(), BROADCAST_ONLY); + AddSubscriptionWithStrategy(report.get(), BROADCAST_ONLY); + rpc_handler_.SendReportRequest(report.Pass()); + token_technology = &GetTokenTechnologyFromReport(); + EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); + EXPECT_EQ(RECEIVE, token_technology->instruction_type(1)); + + // Request with broadcast and scan. + report.reset(new ReportRequest); + AddMessageWithStrategy(report.get(), SCAN_ONLY); + AddSubscriptionWithStrategy(report.get(), BROADCAST_AND_SCAN); + rpc_handler_.SendReportRequest(report.Pass()); + token_technology = &GetTokenTechnologyFromReport(); + EXPECT_EQ(TRANSMIT, token_technology->instruction_type(0)); + EXPECT_EQ(RECEIVE, token_technology->instruction_type(1)); +} +#endif + +TEST_F(RpcHandlerTest, CreateRequestHeader) { + SetDeviceId("CreateRequestHeader Device ID"); + rpc_handler_.SendReportRequest(make_scoped_ptr(new ReportRequest), + "CreateRequestHeader App ID", + StatusCallback()); + EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); + ReportRequest* report = static_cast(request_proto_.get()); + EXPECT_EQ(kChromeVersion, + report->header().framework_version().version_name()); + EXPECT_EQ("CreateRequestHeader App ID", + report->header().client_version().client()); + EXPECT_EQ("CreateRequestHeader Device ID", + report->header().registered_device_id()); + EXPECT_EQ(CHROME_PLATFORM_TYPE, + report->header().device_fingerprint().type()); +} + +TEST_F(RpcHandlerTest, ReportTokens) { + std::vector test_tokens; + test_tokens.push_back(AudioToken("token 1", false)); + test_tokens.push_back(AudioToken("token 2", true)); + test_tokens.push_back(AudioToken("token 3", false)); + AddInvalidToken("token 2"); + + rpc_handler_.ReportTokens(test_tokens); + EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); + ReportRequest* report = static_cast(request_proto_.get()); + google::protobuf::RepeatedPtrField tokens_sent = + report->update_signals_request().token_observation(); + ASSERT_EQ(2, tokens_sent.size()); + EXPECT_EQ("token 1", tokens_sent.Get(0).token_id()); + EXPECT_EQ("token 3", tokens_sent.Get(1).token_id()); +} + +TEST_F(RpcHandlerTest, ReportResponseHandler) { + // Fail on HTTP status != 200. + ReportResponse empty_response; + empty_response.mutable_header()->mutable_status()->set_code(OK); + std::string serialized_empty_response; + ASSERT_TRUE(empty_response.SerializeToString(&serialized_empty_response)); + status_ = SUCCESS; + InvokeReportResponseHandler(net::HTTP_BAD_REQUEST, serialized_empty_response); + EXPECT_EQ(FAIL, status_); + + std::vector subscription_1(1, "Subscription 1"); + std::vector subscription_2(1, "Subscription 2"); + std::vector both_subscriptions; + both_subscriptions.push_back("Subscription 1"); + both_subscriptions.push_back("Subscription 2"); + + ReportResponse test_response; + test_response.mutable_header()->mutable_status()->set_code(OK); + UpdateSignalsResponse* update_response = + test_response.mutable_update_signals_response(); + update_response->set_status(util::error::OK); + Token* invalid_token = update_response->add_token(); + invalid_token->set_id("bad token"); + invalid_token->set_status(INVALID); + CreateSubscribedMessage( + subscription_1, "Message A", update_response->add_message()); + CreateSubscribedMessage( + subscription_2, "Message B", update_response->add_message()); + CreateSubscribedMessage( + both_subscriptions, "Message C", update_response->add_message()); + update_response->add_directive()->set_subscription_id("Subscription 1"); + update_response->add_directive()->set_subscription_id("Subscription 2"); + + messages_by_subscription_.clear(); + FakeDirectiveHandler* directive_handler = InstallFakeDirectiveHandler(); + std::string serialized_proto; + ASSERT_TRUE(test_response.SerializeToString(&serialized_proto)); + status_ = FAIL; + InvokeReportResponseHandler(net::HTTP_OK, serialized_proto); + + EXPECT_EQ(SUCCESS, status_); + EXPECT_TRUE(TokenIsInvalid("bad token")); + ASSERT_EQ(2U, messages_by_subscription_.size()); + ASSERT_EQ(2U, messages_by_subscription_["Subscription 1"].size()); + ASSERT_EQ(2U, messages_by_subscription_["Subscription 2"].size()); + EXPECT_EQ("Message A", + messages_by_subscription_["Subscription 1"][0].payload()); + EXPECT_EQ("Message B", + messages_by_subscription_["Subscription 2"][0].payload()); + EXPECT_EQ("Message C", + messages_by_subscription_["Subscription 1"][1].payload()); + EXPECT_EQ("Message C", + messages_by_subscription_["Subscription 2"][1].payload()); + + ASSERT_EQ(2U, directive_handler->added_directives().size()); + EXPECT_EQ("Subscription 1", + directive_handler->added_directives()[0].subscription_id()); + EXPECT_EQ("Subscription 2", + directive_handler->added_directives()[1].subscription_id()); +} + +} // namespace copresence diff --git a/components/copresence/timed_map.h b/components/copresence/timed_map.h index 909775201ce7c..4367e59b65e65 100644 --- a/components/copresence/timed_map.h +++ b/components/copresence/timed_map.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 COMPONENTS_COPRESENCE_TIMED_MAP_ -#define COMPONENTS_COPRESENCE_TIMED_MAP_ +#ifndef COMPONENTS_COPRESENCE_TIMED_MAP_H_ +#define COMPONENTS_COPRESENCE_TIMED_MAP_H_ #include #include @@ -11,6 +11,7 @@ #include #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/time/default_tick_clock.h" #include "base/time/tick_clock.h" #include "base/time/time.h" @@ -51,7 +52,9 @@ class TimedMap { return elt == map_.end() ? kEmptyValue : elt->second; } - void set_clock_for_testing(base::TickClock* clock) { clock_ = clock; } + void set_clock_for_testing(scoped_ptr clock) { + clock_ = clock.Pass(); + } private: void ClearExpiredTokens() { @@ -80,7 +83,7 @@ class TimedMap { const ValueType kEmptyValue; - base::TickClock* clock_; + scoped_ptr clock_; base::RepeatingTimer timer_; const base::TimeDelta lifetime_; const size_t max_elements_; @@ -94,4 +97,4 @@ class TimedMap { } // namespace copresence -#endif // COMPONENTS_COPRESENCE_TIMED_MAP_ +#endif // COMPONENTS_COPRESENCE_TIMED_MAP_H_ diff --git a/components/copresence/timed_map_unittest.cc b/components/copresence/timed_map_unittest.cc index 355f304ff0ec2..dcf2266add5a6 100644 --- a/components/copresence/timed_map_unittest.cc +++ b/components/copresence/timed_map_unittest.cc @@ -22,6 +22,8 @@ struct Value { class TimedMapTest : public testing::Test { public: + typedef copresence::TimedMap Map; + TimedMapTest() {} private: @@ -31,11 +33,7 @@ class TimedMapTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(TimedMapTest); }; -// TODO(rkc): Find and fix the memory leak here. -#define MAYBE_Basic DISABLED_Basic - -TEST_F(TimedMapTest, MAYBE_Basic) { - typedef copresence::TimedMap Map; +TEST_F(TimedMapTest, Basic) { Map map(base::TimeDelta::FromSeconds(9999), 3); EXPECT_FALSE(map.HasKey(0)); @@ -60,11 +58,7 @@ TEST_F(TimedMapTest, MAYBE_Basic) { EXPECT_EQ(0x7331, map.GetValue(0x1337).value); } -// TODO(rkc): Find and fix the memory leak here. -#define MAYBE_ValueReplacement DISABLED_ValueReplacement - -TEST_F(TimedMapTest, MAYBE_ValueReplacement) { - typedef copresence::TimedMap Map; +TEST_F(TimedMapTest, ValueReplacement) { Map map(base::TimeDelta::FromSeconds(9999), 10); map.Add(0x1337, Value(0x7331)); @@ -80,11 +74,7 @@ TEST_F(TimedMapTest, MAYBE_ValueReplacement) { EXPECT_EQ(0xd00d, map.GetValue(0x1337).value); } -// TODO(rkc): Find and fix the memory leak here. -#define MAYBE_SizeEvict DISABLED_SizeEvict - -TEST_F(TimedMapTest, MAYBE_SizeEvict) { - typedef copresence::TimedMap Map; +TEST_F(TimedMapTest, SizeEvict) { Map two_element_map(base::TimeDelta::FromSeconds(9999), 2); two_element_map.Add(0x1337, Value(0x7331)); @@ -103,15 +93,13 @@ TEST_F(TimedMapTest, MAYBE_SizeEvict) { EXPECT_EQ(0, two_element_map.GetValue(0x1337).value); } -// TODO(rkc): Find and fix the memory leak here. -#define MAYBE_TimedEvict DISABLED_TimedEvict - -TEST_F(TimedMapTest, MAYBE_TimedEvict) { +TEST_F(TimedMapTest, TimedEvict) { const int kLargeTimeValueSeconds = 9999; - base::SimpleTestTickClock clock; - typedef copresence::TimedMap Map; Map map(base::TimeDelta::FromSeconds(kLargeTimeValueSeconds), 2); - map.set_clock_for_testing(&clock); + + // The map takes ownership of the clock, but we retain a pointer. + base::SimpleTestTickClock* clock = new base::SimpleTestTickClock; + map.set_clock_for_testing(make_scoped_ptr(clock)); // Add value at T=0. map.Add(0x1337, Value(0x7331)); @@ -119,7 +107,7 @@ TEST_F(TimedMapTest, MAYBE_TimedEvict) { EXPECT_EQ(0x7331, map.GetValue(0x1337).value); // Add value at T=kLargeTimeValueSeconds-1. - clock.Advance(base::TimeDelta::FromSeconds(kLargeTimeValueSeconds - 1)); + clock->Advance(base::TimeDelta::FromSeconds(kLargeTimeValueSeconds - 1)); map.Add(0xbaad, Value(0xf00d)); // Check values at T=kLargeTimeValueSeconds-1. @@ -129,14 +117,14 @@ TEST_F(TimedMapTest, MAYBE_TimedEvict) { EXPECT_EQ(0x7331, map.GetValue(0x1337).value); // Check values at T=kLargeTimeValueSeconds. - clock.Advance(base::TimeDelta::FromSeconds(1)); + clock->Advance(base::TimeDelta::FromSeconds(1)); EXPECT_FALSE(map.HasKey(0x1337)); EXPECT_EQ(0, map.GetValue(0x1337).value); EXPECT_TRUE(map.HasKey(0xbaad)); EXPECT_EQ(0xf00d, map.GetValue(0xbaad).value); // Check values at T=2*kLargeTimeValueSeconds - clock.Advance(base::TimeDelta::FromSeconds(kLargeTimeValueSeconds)); + clock->Advance(base::TimeDelta::FromSeconds(kLargeTimeValueSeconds)); EXPECT_FALSE(map.HasKey(0xbaad)); EXPECT_EQ(0, map.GetValue(0xbaad).value); } diff --git a/components/cronet.gypi b/components/cronet.gypi index 2915571048214..a68819dbe0cd9 100644 --- a/components/cronet.gypi +++ b/components/cronet.gypi @@ -11,8 +11,8 @@ 'target_name': 'cronet_jni_headers', 'type': 'none', 'sources': [ - 'cronet/android/java/src/org/chromium/net/UrlRequest.java', - 'cronet/android/java/src/org/chromium/net/UrlRequestContext.java', + 'cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java', + 'cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java', ], 'variables': { 'jni_gen_package': 'cronet', @@ -23,11 +23,11 @@ 'target_name': 'cronet_url_request_error_list', 'type': 'none', 'sources': [ - 'cronet/android/java/src/org/chromium/net/UrlRequestError.template', + 'cronet/android/java/src/org/chromium/net/ChromiumUrlRequestError.template', ], 'variables': { 'package_name': 'org/chromium/cronet', - 'template_deps': ['cronet/android/org_chromium_net_UrlRequest_error_list.h'], + 'template_deps': ['cronet/android/chromium_url_request_error_list.h'], }, 'includes': [ '../build/android/java_cpp_template.gypi' ], }, @@ -35,11 +35,11 @@ 'target_name': 'cronet_url_request_priority_list', 'type': 'none', 'sources': [ - 'cronet/android/java/src/org/chromium/net/UrlRequestPriority.template', + 'cronet/android/java/src/org/chromium/net/ChromiumUrlRequestPriority.template', ], 'variables': { 'package_name': 'org/chromium/cronet', - 'template_deps': ['cronet/android/org_chromium_net_UrlRequest_priority_list.h'], + 'template_deps': ['cronet/android/chromium_url_request_priority_list.h'], }, 'includes': [ '../build/android/java_cpp_template.gypi' ], }, @@ -110,18 +110,17 @@ 'cronet/url_request_context_config.cc', 'cronet/url_request_context_config.h', 'cronet/url_request_context_config_list.h', + 'cronet/android/chromium_url_request.cc', + 'cronet/android/chromium_url_request.h', + 'cronet/android/chromium_url_request_error_list.h', + 'cronet/android/chromium_url_request_priority_list.h', + 'cronet/android/chromium_url_request_context.cc', + 'cronet/android/chromium_url_request_context.h', 'cronet/android/cronet_jni.cc', - 'cronet/android/org_chromium_net_UrlRequest.cc', - 'cronet/android/org_chromium_net_UrlRequest.h', - 'cronet/android/org_chromium_net_UrlRequest_error_list.h', - 'cronet/android/org_chromium_net_UrlRequest_priority_list.h', - 'cronet/android/org_chromium_net_UrlRequestContext.cc', - 'cronet/android/org_chromium_net_UrlRequestContext.h', - 'cronet/android/org_chromium_net_UrlRequestContext_config_list.h', - 'cronet/android/url_request_context_peer.cc', - 'cronet/android/url_request_context_peer.h', - 'cronet/android/url_request_peer.cc', - 'cronet/android/url_request_peer.h', + 'cronet/android/url_request_adapter.cc', + 'cronet/android/url_request_adapter.h', + 'cronet/android/url_request_context_adapter.cc', + 'cronet/android/url_request_context_adapter.h', 'cronet/android/wrapped_channel_upload_element_reader.cc', 'cronet/android/wrapped_channel_upload_element_reader.h', ], @@ -152,17 +151,51 @@ ], ], }, - { + { # cronet_stub.jar defines HttpUrlRequest interface and provides its + # its implementation using HttpUrlConnection (not the Chromium stack). + 'target_name': 'cronet_stub', + 'type': 'none', + 'dependencies': [ + 'cronet_url_request_context_config_list', + 'cronet_version', + ], + 'variables': { + 'java_in_dir': 'cronet/android/java', + 'javac_includes': [ + '**/ChunkedWritableByteChannel.java', + '**/HttpUrlConnection*.java', + '**/HttpUrlRequest*.java', + '**/ResponseTooLargeException.java', + '**/UserAgent.java', + # TODO(mef): Consider moving this into HttpUrlRequestConfig. + '**/UrlRequestContextConfig.java', + '**/Version.java', + ], + }, + 'includes': [ '../build/java.gypi' ], + }, + { # cronet.jar implements HttpUrlRequest interface using Chromium stack + # in native libcronet.so library. 'target_name': 'cronet', 'type': 'none', 'dependencies': [ '../base/base.gyp:base', - 'libcronet', + 'cronet_stub', 'cronet_url_request_error_list', 'cronet_url_request_priority_list', + 'libcronet', ], 'variables': { 'java_in_dir': 'cronet/android/java', + 'javac_includes': [ + '**/ChromiumUrlRequest.java', + '**/ChromiumUrlRequestContext.java', + '**/ChromiumUrlRequestError.java', + '**/ChromiumUrlRequestFactory.java', + '**/ChromiumUrlRequestPriority.java', + '**/LibraryLoader.java', + '**/UsedByReflection.java', + ], }, 'includes': [ '../build/java.gypi' ], }, @@ -172,12 +205,15 @@ 'dependencies': [ 'libcronet', 'cronet', + 'cronet_stub', ], 'variables': { 'native_lib': 'libcronet.>(android_product_extension)', 'java_lib': 'cronet.jar', + 'java_stub_lib': 'cronet_stub.jar', 'java_src_lib': 'cronet-src.jar', 'java_sample_src_lib': 'cronet-sample-src.jar', + 'lib_java_dir': '<(PRODUCT_DIR)/lib.java', 'package_dir': '<(PRODUCT_DIR)/cronet', 'intermediate_dir': '<(SHARED_INTERMEDIATE_DIR)/cronet', 'jar_extract_dir': '<(intermediate_dir)/cronet_jar_extract', @@ -203,10 +239,10 @@ { 'action_name': 'extracting from jars', 'inputs': [ - '<(PRODUCT_DIR)/lib.java/<(java_lib)', - '<(PRODUCT_DIR)/lib.java/base_java.jar', - '<(PRODUCT_DIR)/lib.java/net_java.jar', - '<(PRODUCT_DIR)/lib.java/url_java.jar', + '<(lib_java_dir)/<(java_lib)', + '<(lib_java_dir)/base_java.jar', + '<(lib_java_dir)/net_java.jar', + '<(lib_java_dir)/url_java.jar', ], 'outputs': ['<(jar_extract_stamp)', '<(jar_extract_dir)'], 'action': [ @@ -279,6 +315,13 @@ '../AUTHORS', '../chrome/VERSION', 'cronet/android/proguard.cfg', + '<(lib_java_dir)/<(java_stub_lib)' + ], + }, + { + 'destination': '<(package_dir)/symbols/<(android_app_abi)', + 'files': [ + '<(SHARED_LIB_DIR)/<(native_lib)', ], }, ], diff --git a/components/cronet/android/chromium_url_request.cc b/components/cronet/android/chromium_url_request.cc new file mode 100644 index 0000000000000..60b422f378159 --- /dev/null +++ b/components/cronet/android/chromium_url_request.cc @@ -0,0 +1,370 @@ +// 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/cronet/android/chromium_url_request.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/macros.h" +#include "components/cronet/android/url_request_adapter.h" +#include "components/cronet/android/url_request_context_adapter.h" +#include "jni/ChromiumUrlRequest_jni.h" +#include "net/base/net_errors.h" +#include "net/base/request_priority.h" +#include "net/http/http_response_headers.h" + +using base::android::ConvertUTF8ToJavaString; + +namespace cronet { +namespace { + +net::RequestPriority ConvertRequestPriority(jint request_priority) { + switch (request_priority) { + case REQUEST_PRIORITY_IDLE: + return net::IDLE; + case REQUEST_PRIORITY_LOWEST: + return net::LOWEST; + case REQUEST_PRIORITY_LOW: + return net::LOW; + case REQUEST_PRIORITY_MEDIUM: + return net::MEDIUM; + case REQUEST_PRIORITY_HIGHEST: + return net::HIGHEST; + default: + return net::LOWEST; + } +} + +void SetPostContentType(JNIEnv* env, + URLRequestAdapter* request, + jstring content_type) { + DCHECK(request != NULL); + + std::string method_post("POST"); + request->SetMethod(method_post); + + std::string content_type_header("Content-Type"); + + const char* content_type_utf8 = env->GetStringUTFChars(content_type, NULL); + std::string content_type_string(content_type_utf8); + env->ReleaseStringUTFChars(content_type, content_type_utf8); + + request->AddHeader(content_type_header, content_type_string); +} + +// A delegate of URLRequestAdapter that delivers callbacks to the Java layer. +class JniURLRequestAdapterDelegate + : public URLRequestAdapter::URLRequestAdapterDelegate { + public: + JniURLRequestAdapterDelegate(JNIEnv* env, jobject owner) { + owner_ = env->NewGlobalRef(owner); + } + + virtual void OnResponseStarted(URLRequestAdapter* request) OVERRIDE { + JNIEnv* env = base::android::AttachCurrentThread(); + cronet::Java_ChromiumUrlRequest_onResponseStarted(env, owner_); + } + + virtual void OnBytesRead(URLRequestAdapter* request) OVERRIDE { + int bytes_read = request->bytes_read(); + if (bytes_read != 0) { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::ScopedJavaLocalRef java_buffer( + env, env->NewDirectByteBuffer(request->Data(), bytes_read)); + cronet::Java_ChromiumUrlRequest_onBytesRead( + env, owner_, java_buffer.obj()); + } + } + + virtual void OnRequestFinished(URLRequestAdapter* request) OVERRIDE { + JNIEnv* env = base::android::AttachCurrentThread(); + cronet::Java_ChromiumUrlRequest_finish(env, owner_); + } + + virtual int ReadFromUploadChannel(net::IOBuffer* buf, + int buf_length) OVERRIDE { + JNIEnv* env = base::android::AttachCurrentThread(); + base::android::ScopedJavaLocalRef java_buffer( + env, env->NewDirectByteBuffer(buf->data(), buf_length)); + jint bytes_read = cronet::Java_ChromiumUrlRequest_readFromUploadChannel( + env, owner_, java_buffer.obj()); + return bytes_read; + } + + protected: + virtual ~JniURLRequestAdapterDelegate() { + JNIEnv* env = base::android::AttachCurrentThread(); + env->DeleteGlobalRef(owner_); + } + + private: + jobject owner_; + + DISALLOW_COPY_AND_ASSIGN(JniURLRequestAdapterDelegate); +}; + +} // namespace + +// Explicitly register static JNI functions. +bool ChromiumUrlRequestRegisterJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +static jlong CreateRequestAdapter(JNIEnv* env, + jobject object, + jlong urlRequestContextAdapter, + jstring url_string, + jint priority) { + URLRequestContextAdapter* context = + reinterpret_cast(urlRequestContextAdapter); + DCHECK(context != NULL); + + const char* url_utf8 = env->GetStringUTFChars(url_string, NULL); + + VLOG(1) << "New chromium network request. URL:" << url_utf8; + + GURL url(url_utf8); + + env->ReleaseStringUTFChars(url_string, url_utf8); + + URLRequestAdapter* adapter = + new URLRequestAdapter(context, + new JniURLRequestAdapterDelegate(env, object), + url, + ConvertRequestPriority(priority)); + + return reinterpret_cast(adapter); +} + +// synchronized +static void AddHeader(JNIEnv* env, + jobject object, + jlong urlRequestAdapter, + jstring name, + jstring value) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + DCHECK(request); + + std::string name_string(base::android::ConvertJavaStringToUTF8(env, name)); + std::string value_string(base::android::ConvertJavaStringToUTF8(env, value)); + + request->AddHeader(name_string, value_string); +} + +static void SetMethod(JNIEnv* env, + jobject object, + jlong urlRequestAdapter, + jstring method) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + DCHECK(request); + + std::string method_string( + base::android::ConvertJavaStringToUTF8(env, method)); + + request->SetMethod(method_string); +} + +static void SetUploadData(JNIEnv* env, + jobject object, + jlong urlRequestAdapter, + jstring content_type, + jbyteArray content) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + SetPostContentType(env, request, content_type); + + if (content != NULL) { + jsize size = env->GetArrayLength(content); + if (size > 0) { + jbyte* content_bytes = env->GetByteArrayElements(content, NULL); + request->SetUploadContent(reinterpret_cast(content_bytes), + size); + env->ReleaseByteArrayElements(content, content_bytes, 0); + } + } +} + +static void SetUploadChannel(JNIEnv* env, + jobject object, + jlong urlRequestAdapter, + jstring content_type, + jlong content_length) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + SetPostContentType(env, request, content_type); + + request->SetUploadChannel(env, content_length); +} + +/* synchronized */ +static void Start(JNIEnv* env, jobject object, jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request != NULL) { + request->Start(); + } +} + +/* synchronized */ +static void DestroyRequestAdapter(JNIEnv* env, + jobject object, + jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request != NULL) { + request->Destroy(); + } +} + +/* synchronized */ +static void Cancel(JNIEnv* env, jobject object, jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request != NULL) { + request->Cancel(); + } +} + +static jint GetErrorCode(JNIEnv* env, jobject object, jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + int error_code = request->error_code(); + switch (error_code) { + // TODO(mef): Investigate returning success on positive values, too, as + // they technically indicate success. + case net::OK: + return REQUEST_ERROR_SUCCESS; + + // TODO(mef): Investigate this. The fact is that Chrome does not do this, + // and this library is not just being used for downloads. + + // Comment from src/content/browser/download/download_resource_handler.cc: + // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are + // allowed since a number of servers in the wild close the connection too + // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - + // treat downloads as complete in both cases, so we follow their lead. + case net::ERR_CONTENT_LENGTH_MISMATCH: + case net::ERR_INCOMPLETE_CHUNKED_ENCODING: + return REQUEST_ERROR_SUCCESS; + + case net::ERR_INVALID_URL: + case net::ERR_DISALLOWED_URL_SCHEME: + case net::ERR_UNKNOWN_URL_SCHEME: + return REQUEST_ERROR_MALFORMED_URL; + + case net::ERR_CONNECTION_TIMED_OUT: + return REQUEST_ERROR_CONNECTION_TIMED_OUT; + + case net::ERR_NAME_NOT_RESOLVED: + return REQUEST_ERROR_UNKNOWN_HOST; + } + return REQUEST_ERROR_UNKNOWN; +} + +static jstring GetErrorString(JNIEnv* env, + jobject object, + jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + int error_code = request->error_code(); + char buffer[200]; + std::string error_string = net::ErrorToString(error_code); + snprintf(buffer, + sizeof(buffer), + "System error: %s(%d)", + error_string.c_str(), + error_code); + return ConvertUTF8ToJavaString(env, buffer).Release(); +} + +static jint GetHttpStatusCode(JNIEnv* env, + jobject object, + jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + return request->http_status_code(); +} + +static jstring GetContentType(JNIEnv* env, + jobject object, + jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request == NULL) { + return NULL; + } + std::string type = request->content_type(); + if (!type.empty()) { + return ConvertUTF8ToJavaString(env, type.c_str()).Release(); + } else { + return NULL; + } +} + +static jlong GetContentLength(JNIEnv* env, + jobject object, + jlong urlRequestAdapter) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request == NULL) { + return 0; + } + return request->content_length(); +} + +static jstring GetHeader(JNIEnv* env, + jobject object, + jlong urlRequestAdapter, + jstring name) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request == NULL) { + return NULL; + } + + std::string name_string = base::android::ConvertJavaStringToUTF8(env, name); + std::string value = request->GetHeader(name_string); + if (!value.empty()) { + return ConvertUTF8ToJavaString(env, value.c_str()).Release(); + } else { + return NULL; + } +} + +static void GetAllHeaders(JNIEnv* env, + jobject object, + jlong urlRequestAdapter, + jobject headersMap) { + URLRequestAdapter* request = + reinterpret_cast(urlRequestAdapter); + if (request == NULL) + return; + + net::HttpResponseHeaders* headers = request->GetResponseHeaders(); + if (headers == NULL) + return; + + void* iter = NULL; + std::string header_name; + std::string header_value; + while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) { + ScopedJavaLocalRef name = + ConvertUTF8ToJavaString(env, header_name); + ScopedJavaLocalRef value = + ConvertUTF8ToJavaString(env, header_value); + Java_ChromiumUrlRequest_onAppendResponseHeader( + env, object, headersMap, name.Release(), value.Release()); + } + + // Some implementations (notably HttpURLConnection) include a mapping for the + // null key; in HTTP's case, this maps to the HTTP status line. + ScopedJavaLocalRef status_line = + ConvertUTF8ToJavaString(env, headers->GetStatusLine()); + Java_ChromiumUrlRequest_onAppendResponseHeader( + env, object, headersMap, NULL, status_line.Release()); +} + +} // namespace cronet diff --git a/components/cronet/android/chromium_url_request.h b/components/cronet/android/chromium_url_request.h new file mode 100644 index 0000000000000..4296738358715 --- /dev/null +++ b/components/cronet/android/chromium_url_request.h @@ -0,0 +1,32 @@ +// 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_CRONET_ANDROID_CHROMIUM_URL_REQUEST_H_ +#define COMPONENTS_CRONET_ANDROID_CHROMIUM_URL_REQUEST_H_ + +#include + +namespace cronet { + +// Define request priority values like REQUEST_PRIORITY_IDLE in a +// way that ensures they're always the same than their Java counterpart. +enum UrlRequestPriority { +#define DEFINE_REQUEST_PRIORITY(x, y) REQUEST_PRIORITY_##x = y, +#include "components/cronet/android/chromium_url_request_priority_list.h" +#undef DEFINE_REQUEST_PRIORITY +}; + +// Define request priority values like REQUEST_ERROR_SUCCESS in a +// way that ensures they're always the same than their Java counterpart. +enum UrlRequestError { +#define DEFINE_REQUEST_ERROR(x, y) REQUEST_ERROR_##x = y, +#include "components/cronet/android/chromium_url_request_error_list.h" +#undef DEFINE_REQUEST_ERROR +}; + +bool ChromiumUrlRequestRegisterJni(JNIEnv* env); + +} // namespace cronet + +#endif // COMPONENTS_CRONET_ANDROID_CHROMIUM_URL_REQUEST_H_ diff --git a/components/cronet/android/chromium_url_request_context.cc b/components/cronet/android/chromium_url_request_context.cc new file mode 100644 index 0000000000000..f870adfe242d0 --- /dev/null +++ b/components/cronet/android/chromium_url_request_context.cc @@ -0,0 +1,150 @@ +// 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/cronet/android/chromium_url_request_context.h" + +#include + +#include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/metrics/statistics_recorder.h" +#include "base/values.h" +#include "components/cronet/android/chromium_url_request.h" +#include "components/cronet/android/url_request_adapter.h" +#include "components/cronet/android/url_request_context_adapter.h" +#include "components/cronet/url_request_context_config.h" +#include "jni/ChromiumUrlRequestContext_jni.h" + +namespace { + +// Delegate of URLRequestContextAdapter that delivers callbacks to the Java +// layer. +class JniURLRequestContextAdapterDelegate + : public cronet::URLRequestContextAdapter:: + URLRequestContextAdapterDelegate { + public: + JniURLRequestContextAdapterDelegate(JNIEnv* env, jobject owner) + : owner_(env->NewGlobalRef(owner)) {} + + virtual void OnContextInitialized( + cronet::URLRequestContextAdapter* context) OVERRIDE { + JNIEnv* env = base::android::AttachCurrentThread(); + cronet::Java_ChromiumUrlRequestContext_initNetworkThread(env, owner_); + // TODO(dplotnikov): figure out if we need to detach from the thread. + // The documentation says we should detach just before the thread exits. + } + + protected: + virtual ~JniURLRequestContextAdapterDelegate() { + JNIEnv* env = base::android::AttachCurrentThread(); + env->DeleteGlobalRef(owner_); + } + + private: + jobject owner_; +}; + +} // namespace + +namespace cronet { + +// Explicitly register static JNI functions. +bool ChromiumUrlRequestContextRegisterJni(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// Sets global user-agent to be used for all subsequent requests. +static jlong CreateRequestContextAdapter(JNIEnv* env, + jobject object, + jobject context, + jstring user_agent, + jint log_level, + jstring config) { + std::string user_agent_string = + base::android::ConvertJavaStringToUTF8(env, user_agent); + + std::string config_string = + base::android::ConvertJavaStringToUTF8(env, config); + + scoped_ptr config_value(base::JSONReader::Read(config_string)); + if (!config_value || !config_value->IsType(base::Value::TYPE_DICTIONARY)) { + DLOG(ERROR) << "Bad JSON: " << config_string; + return 0; + } + + scoped_ptr context_config( + new URLRequestContextConfig()); + base::JSONValueConverter converter; + if (!converter.Convert(*config_value, context_config.get())) { + DLOG(ERROR) << "Bad Config: " << config_value; + return 0; + } + + // Set application context. + base::android::ScopedJavaLocalRef scoped_context(env, context); + base::android::InitApplicationContext(env, scoped_context); + + // TODO(mef): MinLogLevel is global, shared by all URLRequestContexts. + // Revisit this if each URLRequestContext would need an individual log level. + logging::SetMinLogLevel(static_cast(log_level)); + + // TODO(dplotnikov): set application context. + URLRequestContextAdapter* adapter = new URLRequestContextAdapter( + new JniURLRequestContextAdapterDelegate(env, object), user_agent_string); + adapter->AddRef(); // Hold onto this ref-counted object. + adapter->Initialize(context_config.Pass()); + return reinterpret_cast(adapter); +} + +// Releases native objects. +static void ReleaseRequestContextAdapter(JNIEnv* env, + jobject object, + jlong urlRequestContextAdapter) { + URLRequestContextAdapter* adapter = + reinterpret_cast(urlRequestContextAdapter); + // TODO(mef): Revisit this from thread safety point of view: Can we delete a + // thread while running on that thread? + // URLRequestContextAdapter is a ref-counted object, and may have pending + // tasks, + // so we need to release it instead of deleting here. + adapter->Release(); +} + +// Starts recording statistics. +static void InitializeStatistics(JNIEnv* env, jobject jcaller) { + base::StatisticsRecorder::Initialize(); +} + +// Gets current statistics with |filter| as a substring as JSON text (an empty +// |filter| will include all registered histograms). +static jstring GetStatisticsJSON(JNIEnv* env, jobject jcaller, jstring filter) { + std::string query = base::android::ConvertJavaStringToUTF8(env, filter); + std::string json = base::StatisticsRecorder::ToJSON(query); + return base::android::ConvertUTF8ToJavaString(env, json).Release(); +} + +// Starts recording NetLog into file with |fileName|. +static void StartNetLogToFile(JNIEnv* env, + jobject jcaller, + jlong urlRequestContextAdapter, + jstring fileName) { + URLRequestContextAdapter* adapter = + reinterpret_cast(urlRequestContextAdapter); + std::string file_name = base::android::ConvertJavaStringToUTF8(env, fileName); + adapter->StartNetLogToFile(file_name); +} + +// Stops recording NetLog. +static void StopNetLog(JNIEnv* env, + jobject jcaller, + jlong urlRequestContextAdapter) { + URLRequestContextAdapter* adapter = + reinterpret_cast(urlRequestContextAdapter); + adapter->StopNetLog(); +} + +} // namespace cronet diff --git a/components/cronet/android/chromium_url_request_context.h b/components/cronet/android/chromium_url_request_context.h new file mode 100644 index 0000000000000..3d5a1f14cc7d5 --- /dev/null +++ b/components/cronet/android/chromium_url_request_context.h @@ -0,0 +1,16 @@ +// 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_CRONET_ANDROID_URLREQUEST_CONTEXT_H_ +#define COMPONENTS_CRONET_ANDROID_URLREQUEST_CONTEXT_H_ + +#include + +namespace cronet { + +bool ChromiumUrlRequestContextRegisterJni(JNIEnv* env); + +} // namespace cronet + +#endif // COMPONENTS_CRONET_ANDROID_URLREQUEST_CONTEXT_H_ diff --git a/components/cronet/android/org_chromium_net_UrlRequest_error_list.h b/components/cronet/android/chromium_url_request_error_list.h similarity index 100% rename from components/cronet/android/org_chromium_net_UrlRequest_error_list.h rename to components/cronet/android/chromium_url_request_error_list.h diff --git a/components/cronet/android/org_chromium_net_UrlRequest_priority_list.h b/components/cronet/android/chromium_url_request_priority_list.h similarity index 100% rename from components/cronet/android/org_chromium_net_UrlRequest_priority_list.h rename to components/cronet/android/chromium_url_request_priority_list.h diff --git a/components/cronet/android/cronet_jni.cc b/components/cronet/android/cronet_jni.cc index f67425bc95654..890649a183a74 100644 --- a/components/cronet/android/cronet_jni.cc +++ b/components/cronet/android/cronet_jni.cc @@ -6,8 +6,8 @@ #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" #include "base/at_exit.h" -#include "components/cronet/android/org_chromium_net_UrlRequest.h" -#include "components/cronet/android/org_chromium_net_UrlRequestContext.h" +#include "components/cronet/android/chromium_url_request.h" +#include "components/cronet/android/chromium_url_request_context.h" #include "net/android/net_jni_registrar.h" #include "url/android/url_jni_registrar.h" @@ -18,11 +18,11 @@ namespace { const base::android::RegistrationMethod kCronetRegisteredMethods[] = { - {"BaseAndroid", base::android::RegisterJni}, - {"NetAndroid", net::android::RegisterJni}, - {"UrlAndroid", url::android::RegisterJni}, - {"UrlRequest", cronet::UrlRequestRegisterJni}, - {"UrlRequestContext", cronet::UrlRequestContextRegisterJni}, + {"BaseAndroid", base::android::RegisterJni}, + {"ChromiumUrlRequest", cronet::ChromiumUrlRequestRegisterJni}, + {"ChromiumUrlRequestContext", cronet::ChromiumUrlRequestContextRegisterJni}, + {"NetAndroid", net::android::RegisterJni}, + {"UrlAndroid", url::android::RegisterJni}, }; base::AtExitManager* g_at_exit_manager = NULL; diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java index a643a1456230d..593993bc380eb 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequest.java @@ -4,35 +4,65 @@ package org.chromium.net; +import android.util.Log; + +import org.apache.http.conn.ConnectTimeoutException; +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.UnknownHostException; import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Map.Entry; /** * Network request using the native http stack implementation. */ -public class ChromiumUrlRequest extends UrlRequest implements HttpUrlRequest { - +@JNINamespace("cronet") +public class ChromiumUrlRequest implements HttpUrlRequest { + /** + * Native adapter object, owned by UrlRequest. + */ + private long mUrlRequestAdapter; + private final ChromiumUrlRequestContext mRequestContext; + private final String mUrl; + private final int mPriority; + private final Map mHeaders; + private final WritableByteChannel mSink; + private Map mAdditionalHeaders; + private String mUploadContentType; + private String mMethod; + private byte[] mUploadData; + private ReadableByteChannel mUploadChannel; + private WritableByteChannel mOutputChannel; + private IOException mSinkException; + private volatile boolean mStarted; + private volatile boolean mCanceled; + private volatile boolean mRecycled; + private volatile boolean mFinished; + private boolean mHeadersAvailable; + private String mContentType; + private long mUploadContentLength; private final HttpUrlRequestListener mListener; - private boolean mBufferFullResponse; - private long mOffset; - private long mContentLength; - private long mContentLengthLimit; - private boolean mCancelIfContentLengthOverLimit; - private boolean mContentLengthOverLimit; - private boolean mSkippingToOffset; - private long mSize; + private final Object mLock = new Object(); - public ChromiumUrlRequest(UrlRequestContext requestContext, + public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, String url, int priority, Map headers, HttpUrlRequestListener listener) { this(requestContext, url, priority, headers, @@ -40,29 +70,35 @@ public ChromiumUrlRequest(UrlRequestContext requestContext, mBufferFullResponse = true; } - public ChromiumUrlRequest(UrlRequestContext requestContext, + /** + * Constructor. + * + * @param requestContext The context. + * @param url The URL. + * @param priority Request priority, e.g. {@link #REQUEST_PRIORITY_MEDIUM}. + * @param headers HTTP headers. + * @param sink The output channel into which downloaded content will be + * written. + */ + public ChromiumUrlRequest(ChromiumUrlRequestContext requestContext, String url, int priority, Map headers, WritableByteChannel sink, HttpUrlRequestListener listener) { - super(requestContext, url, convertRequestPriority(priority), headers, - sink); - mListener = listener; - } - - private static int convertRequestPriority(int priority) { - switch (priority) { - case HttpUrlRequest.REQUEST_PRIORITY_IDLE: - return UrlRequestPriority.IDLE; - case HttpUrlRequest.REQUEST_PRIORITY_LOWEST: - return UrlRequestPriority.LOWEST; - case HttpUrlRequest.REQUEST_PRIORITY_LOW: - return UrlRequestPriority.LOW; - case HttpUrlRequest.REQUEST_PRIORITY_MEDIUM: - return UrlRequestPriority.MEDIUM; - case HttpUrlRequest.REQUEST_PRIORITY_HIGHEST: - return UrlRequestPriority.HIGHEST; - default: - return UrlRequestPriority.MEDIUM; + if (requestContext == null) { + throw new NullPointerException("Context is required"); } + if (url == null) { + throw new NullPointerException("URL is required"); + } + mRequestContext = requestContext; + mUrl = url; + mPriority = convertRequestPriority(priority); + mHeaders = headers; + mSink = sink; + mUrlRequestAdapter = nativeCreateRequestAdapter( + mRequestContext.getChromiumUrlRequestContextAdapter(), + mUrl, + mPriority); + mListener = listener; } @Override @@ -73,6 +109,13 @@ public void setOffset(long offset) { } } + /** + * The compressed content length as reported by the server. May be -1 if + * the server did not provide a length. Some servers may also report the + * wrong number. Since this is the compressed content length, and only + * uncompressed content is returned by the consumer, the consumer should + * not rely on this value. + */ @Override public long getContentLength() { return mContentLength; @@ -85,61 +128,246 @@ public void setContentLengthLimit(long limit, boolean cancelEarly) { } @Override - protected void onResponseStarted() { - super.onResponseStarted(); + public int getHttpStatusCode() { + int httpStatusCode = nativeGetHttpStatusCode(mUrlRequestAdapter); - mContentLength = super.getContentLength(); - if (mContentLengthLimit > 0 && mContentLength > mContentLengthLimit - && mCancelIfContentLengthOverLimit) { - onContentLengthOverLimit(); - return; + // TODO(mef): Investigate the following: + // If we have been able to successfully resume a previously interrupted + // download, the status code will be 206, not 200. Since the rest of the + // application is expecting 200 to indicate success, we need to fake it. + if (httpStatusCode == 206) { + httpStatusCode = 200; } + return httpStatusCode; + } - if (mBufferFullResponse && mContentLength != -1 - && !mContentLengthOverLimit) { - ((ChunkedWritableByteChannel)getSink()).setCapacity( - (int)mContentLength); + /** + * Returns an exception if any, or null if the request was completed + * successfully. + */ + @Override + public IOException getException() { + if (mSinkException != null) { + return mSinkException; } - if (mOffset != 0) { - // The server may ignore the request for a byte range. - if (super.getHttpStatusCode() == 200) { - if (mContentLength != -1) { - mContentLength -= mOffset; + validateNotRecycled(); + + int errorCode = nativeGetErrorCode(mUrlRequestAdapter); + switch (errorCode) { + case ChromiumUrlRequestError.SUCCESS: + if (mContentLengthOverLimit) { + return new ResponseTooLargeException(); } - mSkippingToOffset = true; - } else { - mSize = mOffset; - } + return null; + case ChromiumUrlRequestError.UNKNOWN: + return new IOException( + nativeGetErrorString(mUrlRequestAdapter)); + case ChromiumUrlRequestError.MALFORMED_URL: + return new MalformedURLException("Malformed URL: " + mUrl); + case ChromiumUrlRequestError.CONNECTION_TIMED_OUT: + return new ConnectTimeoutException("Connection timed out"); + case ChromiumUrlRequestError.UNKNOWN_HOST: + String host; + try { + host = new URL(mUrl).getHost(); + } catch (MalformedURLException e) { + host = mUrl; + } + return new UnknownHostException("Unknown host: " + host); + default: + throw new IllegalStateException( + "Unrecognized error code: " + errorCode); } - mListener.onResponseStarted(this); } @Override - protected void onBytesRead(ByteBuffer buffer) { - if (mContentLengthOverLimit) { - return; + public ByteBuffer getByteBuffer() { + return ((ChunkedWritableByteChannel)getSink()).getByteBuffer(); + } + + @Override + public byte[] getResponseAsBytes() { + return ((ChunkedWritableByteChannel)getSink()).getBytes(); + } + + /** + * Adds a request header. Must be done before request has started. + */ + public void addHeader(String header, String value) { + synchronized (mLock) { + validateNotStarted(); + if (mAdditionalHeaders == null) { + mAdditionalHeaders = new HashMap(); + } + mAdditionalHeaders.put(header, value); + } + } + + /** + * Sets data to upload as part of a POST or PUT request. + * + * @param contentType MIME type of the upload content or null if this is not + * an upload. + * @param data The content that needs to be uploaded. + */ + public void setUploadData(String contentType, byte[] data) { + synchronized (mLock) { + validateNotStarted(); + mUploadContentType = contentType; + mUploadData = data; + mUploadChannel = null; + } + } + + /** + * Sets a readable byte channel to upload as part of a POST or PUT request. + * + * @param contentType MIME type of the upload content or null if this is not + * an upload request. + * @param channel The channel to read to read upload data from if this is an + * upload request. + * @param contentLength The length of data to upload. + */ + public void setUploadChannel(String contentType, + ReadableByteChannel channel, long contentLength) { + synchronized (mLock) { + validateNotStarted(); + mUploadContentType = contentType; + mUploadChannel = channel; + mUploadContentLength = contentLength; + mUploadData = null; + } + } + + /** + * Sets HTTP method for upload request. Only PUT or POST are allowed. + */ + public void setHttpMethod(String method) { + validateNotStarted(); + if (!("PUT".equals(method) || "POST".equals(method))) { + throw new IllegalArgumentException("Only PUT or POST are allowed."); } + mMethod = method; + } + + public WritableByteChannel getSink() { + return mSink; + } + + public void start() { + synchronized (mLock) { + if (mCanceled) { + return; + } + + validateNotStarted(); + validateNotRecycled(); + + mStarted = true; + + String method = mMethod; + if (method == null && + ((mUploadData != null && mUploadData.length > 0) || + mUploadChannel != null)) { + // Default to POST if there is data to upload but no method was + // specified. + method = "POST"; + } - int size = buffer.remaining(); - mSize += size; - if (mSkippingToOffset) { - if (mSize <= mOffset) { + if (method != null) { + nativeSetMethod(mUrlRequestAdapter, method); + } + + if (mHeaders != null && !mHeaders.isEmpty()) { + for (Entry entry : mHeaders.entrySet()) { + nativeAddHeader(mUrlRequestAdapter, entry.getKey(), + entry.getValue()); + } + } + + if (mAdditionalHeaders != null) { + for (Entry entry : + mAdditionalHeaders.entrySet()) { + nativeAddHeader(mUrlRequestAdapter, entry.getKey(), + entry.getValue()); + } + } + + if (mUploadData != null && mUploadData.length > 0) { + nativeSetUploadData(mUrlRequestAdapter, mUploadContentType, + mUploadData); + } else if (mUploadChannel != null) { + nativeSetUploadChannel(mUrlRequestAdapter, mUploadContentType, + mUploadContentLength); + } + + nativeStart(mUrlRequestAdapter); + } + } + + public void cancel() { + synchronized (mLock) { + if (mCanceled) { return; - } else { - mSkippingToOffset = false; - buffer.position((int)(mOffset - (mSize - size))); + } + + mCanceled = true; + + if (!mRecycled) { + nativeCancel(mUrlRequestAdapter); } } + } - if (mContentLengthLimit != 0 && mSize > mContentLengthLimit) { - buffer.limit(size - (int)(mSize - mContentLengthLimit)); - super.onBytesRead(buffer); - onContentLengthOverLimit(); - return; + public boolean isCanceled() { + synchronized (mLock) { + return mCanceled; } + } + + public boolean isRecycled() { + synchronized (mLock) { + return mRecycled; + } + } + + public String getContentType() { + return mContentType; + } - super.onBytesRead(buffer); + public String getHeader(String name) { + validateHeadersAvailable(); + return nativeGetHeader(mUrlRequestAdapter, name); + } + + // All response headers. + public Map> getAllHeaders() { + validateHeadersAvailable(); + ResponseHeadersMap result = new ResponseHeadersMap(); + nativeGetAllHeaders(mUrlRequestAdapter, result); + return result; + } + + public String getUrl() { + return mUrl; + } + + private static int convertRequestPriority(int priority) { + switch (priority) { + case HttpUrlRequest.REQUEST_PRIORITY_IDLE: + return ChromiumUrlRequestPriority.IDLE; + case HttpUrlRequest.REQUEST_PRIORITY_LOWEST: + return ChromiumUrlRequestPriority.LOWEST; + case HttpUrlRequest.REQUEST_PRIORITY_LOW: + return ChromiumUrlRequestPriority.LOW; + case HttpUrlRequest.REQUEST_PRIORITY_MEDIUM: + return ChromiumUrlRequestPriority.MEDIUM; + case HttpUrlRequest.REQUEST_PRIORITY_HIGHEST: + return ChromiumUrlRequestPriority.HIGHEST; + default: + return ChromiumUrlRequestPriority.MEDIUM; + } } private void onContentLengthOverLimit() { @@ -147,43 +375,249 @@ private void onContentLengthOverLimit() { cancel(); } - @Override - protected void onRequestComplete() { + /** + * A callback invoked when the response has been fully consumed. + */ + private void onRequestComplete() { mListener.onRequestComplete(this); } - @Override - public int getHttpStatusCode() { - int httpStatusCode = super.getHttpStatusCode(); + private void validateNotRecycled() { + if (mRecycled) { + throw new IllegalStateException("Accessing recycled request"); + } + } - // TODO(mef): Investigate the following: - // If we have been able to successfully resume a previously interrupted - // download, - // the status code will be 206, not 200. Since the rest of the - // application is - // expecting 200 to indicate success, we need to fake it. - if (httpStatusCode == 206) { - httpStatusCode = 200; + private void validateNotStarted() { + if (mStarted) { + throw new IllegalStateException("Request already started"); } - return httpStatusCode; } - @Override - public IOException getException() { - IOException ex = super.getException(); - if (ex == null && mContentLengthOverLimit) { - ex = new ResponseTooLargeException(); + private void validateHeadersAvailable() { + if (!mHeadersAvailable) { + throw new IllegalStateException("Response headers not available"); } - return ex; } - @Override - public ByteBuffer getByteBuffer() { - return ((ChunkedWritableByteChannel)getSink()).getByteBuffer(); + // Private methods called by native library. + + /** + * If @CalledByNative method throws an exception, request gets cancelled + * and exception could be retrieved from request using getException(). + */ + private void onCalledByNativeException(Exception e) { + mSinkException = new IOException( + "CalledByNative method has thrown an exception", e); + Log.e(ChromiumUrlRequestContext.LOG_TAG, + "Exception in CalledByNative method", e); + try { + cancel(); + } catch (Exception cancel_exception) { + Log.e(ChromiumUrlRequestContext.LOG_TAG, + "Exception trying to cancel request", cancel_exception); + } } - @Override - public byte[] getResponseAsBytes() { - return ((ChunkedWritableByteChannel)getSink()).getBytes(); + /** + * A callback invoked when the first chunk of the response has arrived. + */ + @CalledByNative + private void onResponseStarted() { + try { + mContentType = nativeGetContentType(mUrlRequestAdapter); + mContentLength = nativeGetContentLength(mUrlRequestAdapter); + mHeadersAvailable = true; + + if (mContentLengthLimit > 0 && + mContentLength > mContentLengthLimit && + mCancelIfContentLengthOverLimit) { + onContentLengthOverLimit(); + return; + } + + if (mBufferFullResponse && mContentLength != -1 && + !mContentLengthOverLimit) { + ((ChunkedWritableByteChannel)getSink()).setCapacity( + (int)mContentLength); + } + + if (mOffset != 0) { + // The server may ignore the request for a byte range, in which + // case status code will be 200, instead of 206. Note that we + // cannot call getHttpStatusCode as it rewrites 206 into 200. + if (nativeGetHttpStatusCode(mUrlRequestAdapter) == 200) { + // TODO(mef): Revisit this logic. + if (mContentLength != -1) { + mContentLength -= mOffset; + } + mSkippingToOffset = true; + } else { + mSize = mOffset; + } + } + mListener.onResponseStarted(this); + } catch (Exception e) { + onCalledByNativeException(e); + } + } + + /** + * Consumes a portion of the response. + * + * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and + * no references to it may be retained after the method ends, as + * it wraps code managed on the native heap. + */ + @CalledByNative + private void onBytesRead(ByteBuffer buffer) { + try { + if (mContentLengthOverLimit) { + return; + } + + int size = buffer.remaining(); + mSize += size; + if (mSkippingToOffset) { + if (mSize <= mOffset) { + return; + } else { + mSkippingToOffset = false; + buffer.position((int)(mOffset - (mSize - size))); + } + } + + boolean contentLengthOverLimit = + (mContentLengthLimit != 0 && mSize > mContentLengthLimit); + if (contentLengthOverLimit) { + buffer.limit(size - (int)(mSize - mContentLengthLimit)); + } + + while (buffer.hasRemaining()) { + mSink.write(buffer); + } + if (contentLengthOverLimit) { + onContentLengthOverLimit(); + } + } catch (Exception e) { + onCalledByNativeException(e); + } + } + + /** + * Notifies the listener, releases native data structures. + */ + @SuppressWarnings("unused") + @CalledByNative + private void finish() { + try { + synchronized (mLock) { + mFinished = true; + + if (mRecycled) { + return; + } + try { + mSink.close(); + } catch (IOException e) { + // Ignore + } + try { + if (mUploadChannel != null && mUploadChannel.isOpen()) { + mUploadChannel.close(); + } + } catch (IOException e) { + // Ignore + } + onRequestComplete(); + nativeDestroyRequestAdapter(mUrlRequestAdapter); + mUrlRequestAdapter = 0; + mRecycled = true; + } + } catch (Exception e) { + mSinkException = new IOException("Exception in finish", e); + } + } + + /** + * Appends header |name| with value |value| to |headersMap|. + */ + @SuppressWarnings("unused") + @CalledByNative + private void onAppendResponseHeader(ResponseHeadersMap headersMap, + String name, String value) { + try { + if (!headersMap.containsKey(name)) { + headersMap.put(name, new ArrayList()); + } + headersMap.get(name).add(value); + } catch (Exception e) { + onCalledByNativeException(e); + } + } + + /** + * Reads a sequence of bytes from upload channel into the given buffer. + * @param dest The buffer into which bytes are to be transferred. + * @return Returns number of bytes read (could be 0) or -1 and closes + * the channel if error occured. + */ + @SuppressWarnings("unused") + @CalledByNative + private int readFromUploadChannel(ByteBuffer dest) { + try { + if (mUploadChannel == null || !mUploadChannel.isOpen()) + return -1; + int result = mUploadChannel.read(dest); + if (result < 0) { + mUploadChannel.close(); + return 0; + } + return result; + } catch (Exception e) { + onCalledByNativeException(e); + } + return -1; + } + + // Native methods are implemented in chromium_url_request.cc. + + private native long nativeCreateRequestAdapter( + long ChromiumUrlRequestContextAdapter, String url, int priority); + + private native void nativeAddHeader(long urlRequestAdapter, String name, + String value); + + private native void nativeSetMethod(long urlRequestAdapter, String method); + + private native void nativeSetUploadData(long urlRequestAdapter, + String contentType, byte[] content); + + private native void nativeSetUploadChannel(long urlRequestAdapter, + String contentType, long contentLength); + + private native void nativeStart(long urlRequestAdapter); + + private native void nativeCancel(long urlRequestAdapter); + + private native void nativeDestroyRequestAdapter(long urlRequestAdapter); + + private native int nativeGetErrorCode(long urlRequestAdapter); + + private native int nativeGetHttpStatusCode(long urlRequestAdapter); + + private native String nativeGetErrorString(long urlRequestAdapter); + + private native String nativeGetContentType(long urlRequestAdapter); + + private native long nativeGetContentLength(long urlRequestAdapter); + + private native String nativeGetHeader(long urlRequestAdapter, String name); + + private native void nativeGetAllHeaders(long urlRequestAdapter, + ResponseHeadersMap headers); + + // Explicit class to work around JNI-generator generics confusion. + private class ResponseHeadersMap extends HashMap> { } } diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java new file mode 100644 index 0000000000000..d5a5c9b4e8c7d --- /dev/null +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestContext.java @@ -0,0 +1,138 @@ +// 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.net; + +import android.content.Context; +import android.os.ConditionVariable; +import android.os.Process; +import android.util.Log; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * Provides context for the native HTTP operations. + */ +@JNINamespace("cronet") +public class ChromiumUrlRequestContext { + private static final int LOG_NONE = 3; // LOG(FATAL), no VLOG. + private static final int LOG_DEBUG = -1; // LOG(FATAL...INFO), VLOG(1) + private static final int LOG_VERBOSE = -2; // LOG(FATAL...INFO), VLOG(2) + static final String LOG_TAG = "ChromiumNetwork"; + + /** + * Native adapter object, owned by ChromiumUrlRequestContext. + */ + private long mChromiumUrlRequestContextAdapter; + + private final ConditionVariable mStarted = new ConditionVariable(); + + /** + * Constructor. + * + */ + protected ChromiumUrlRequestContext(Context context, String userAgent, + String config) { + mChromiumUrlRequestContextAdapter = nativeCreateRequestContextAdapter( + context, userAgent, getLoggingLevel(), config); + if (mChromiumUrlRequestContextAdapter == 0) + throw new NullPointerException("Context Adapter creation failed"); + + // TODO(mef): Revisit the need of block here. + mStarted.block(2000); + } + + /** + * Returns the version of this network stack formatted as N.N.N.N/X where + * N.N.N.N is the version of Chromium and X is the revision number. + */ + public static String getVersion() { + return Version.getVersion(); + } + + /** + * Initializes statistics recorder. + */ + public void initializeStatistics() { + nativeInitializeStatistics(); + } + + /** + * Gets current statistics recorded since |initializeStatistics| with + * |filter| as a substring as JSON text (an empty |filter| will include all + * registered histograms). + */ + public String getStatisticsJSON(String filter) { + return nativeGetStatisticsJSON(filter); + } + + /** + * Starts NetLog logging to a file named |fileName| in the + * application temporary directory. |fileName| must not be empty. Log level + * is LOG_ALL_BUT_BYTES. If the file exists it is truncated before starting. + * If actively logging the call is ignored. + */ + public void startNetLogToFile(String fileName) { + nativeStartNetLogToFile(mChromiumUrlRequestContextAdapter, fileName); + } + + /** + * Stops NetLog logging and flushes file to disk. If a logging session is + * not in progress this call is ignored. + */ + public void stopNetLog() { + nativeStopNetLog(mChromiumUrlRequestContextAdapter); + } + + @CalledByNative + private void initNetworkThread() { + Thread.currentThread().setName("ChromiumNet"); + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mStarted.open(); + } + + @Override + protected void finalize() throws Throwable { + nativeReleaseRequestContextAdapter(mChromiumUrlRequestContextAdapter); + super.finalize(); + } + + protected long getChromiumUrlRequestContextAdapter() { + return mChromiumUrlRequestContextAdapter; + } + + /** + * @return loggingLevel see {@link #LOG_NONE}, {@link #LOG_DEBUG} and + * {@link #LOG_VERBOSE}. + */ + private int getLoggingLevel() { + int loggingLevel; + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + loggingLevel = LOG_VERBOSE; + } else if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { + loggingLevel = LOG_DEBUG; + } else { + loggingLevel = LOG_NONE; + } + return loggingLevel; + } + + // Returns an instance ChromiumUrlRequestContextAdapter to be stored in + // mChromiumUrlRequestContextAdapter. + private native long nativeCreateRequestContextAdapter(Context context, + String userAgent, int loggingLevel, String config); + + private native void nativeReleaseRequestContextAdapter( + long ChromiumUrlRequestContextAdapter); + + private native void nativeInitializeStatistics(); + + private native String nativeGetStatisticsJSON(String filter); + + private native void nativeStartNetLogToFile( + long ChromiumUrlRequestContextAdapter, String fileName); + + private native void nativeStopNetLog(long ChromiumUrlRequestContextAdapter); +} diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestError.template b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestError.template new file mode 100644 index 0000000000000..24e51102b6bf6 --- /dev/null +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestError.template @@ -0,0 +1,14 @@ +// 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.net; + +// A simple auto-generated interface used to list request errors as used by +// both org.chromium.net.ChromiumUrlRequest and +// net/cronet/android/chromium_url_request_error_list.h +public interface ChromiumUrlRequestError { +#define DEFINE_REQUEST_ERROR(x,y) public static final int x = y; +#include "components/cronet/android/chromium_url_request_error_list.h" +#undef DEFINE_REQUEST_ERROR +} diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java index 8efb82befa790..dabed9f6452bd 100644 --- a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestFactory.java @@ -15,14 +15,14 @@ */ @UsedByReflection("HttpUrlRequestFactory.java") public class ChromiumUrlRequestFactory extends HttpUrlRequestFactory { - private UrlRequestContext mRequestContext; + private ChromiumUrlRequestContext mRequestContext; @UsedByReflection("HttpUrlRequestFactory.java") public ChromiumUrlRequestFactory( Context context, HttpUrlRequestFactoryConfig config) { if (isEnabled()) { System.loadLibrary("cronet"); - mRequestContext = new UrlRequestContext( + mRequestContext = new ChromiumUrlRequestContext( context.getApplicationContext(), UserAgent.from(context), config.toString()); } @@ -35,7 +35,7 @@ public boolean isEnabled() { @Override public String getName() { - return "Chromium/" + UrlRequestContext.getVersion(); + return "Chromium/" + ChromiumUrlRequestContext.getVersion(); } @Override @@ -52,4 +52,8 @@ public HttpUrlRequest createRequest(String url, int requestPriority, return new ChromiumUrlRequest(mRequestContext, url, requestPriority, headers, channel, listener); } + + public ChromiumUrlRequestContext getRequestContext() { + return mRequestContext; + } } diff --git a/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestPriority.template b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestPriority.template new file mode 100644 index 0000000000000..604849d9aecc3 --- /dev/null +++ b/components/cronet/android/java/src/org/chromium/net/ChromiumUrlRequestPriority.template @@ -0,0 +1,14 @@ +// 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.net; + +// A simple auto-generated interface used to list request priorities as used by +// both org.chromium.net.ChromiumUrlRequest and +// net/cronet/android/chromium_url_request_priority_list.h +public interface ChromiumUrlRequestPriority { +#define DEFINE_REQUEST_PRIORITY(x,y) public static final int x = y; +#include "components/cronet/android/chromium_url_request_priority_list.h" +#undef DEFINE_REQUEST_PRIORITY +} diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java b/components/cronet/android/java/src/org/chromium/net/UrlRequest.java deleted file mode 100644 index 7cf9ef44b0572..0000000000000 --- a/components/cronet/android/java/src/org/chromium/net/UrlRequest.java +++ /dev/null @@ -1,451 +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.net; - -import org.apache.http.conn.ConnectTimeoutException; -import org.chromium.base.CalledByNative; -import org.chromium.base.JNINamespace; - -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * Network request using the native http stack implementation. - */ -@JNINamespace("cronet") -public class UrlRequest { - private static final class ContextLock { - } - - private final UrlRequestContext mRequestContext; - private final String mUrl; - private final int mPriority; - private final Map mHeaders; - private final WritableByteChannel mSink; - private Map mAdditionalHeaders; - private String mUploadContentType; - private String mMethod; - private byte[] mUploadData; - private ReadableByteChannel mUploadChannel; - private WritableByteChannel mOutputChannel; - private IOException mSinkException; - private volatile boolean mStarted; - private volatile boolean mCanceled; - private volatile boolean mRecycled; - private volatile boolean mFinished; - private boolean mHeadersAvailable; - private String mContentType; - private long mContentLength; - private long mUploadContentLength; - private final ContextLock mLock; - - /** - * Native peer object, owned by UrlRequest. - */ - private long mUrlRequestPeer; - - /** - * Constructor. - * - * @param requestContext The context. - * @param url The URL. - * @param priority Request priority, e.g. {@link #REQUEST_PRIORITY_MEDIUM}. - * @param headers HTTP headers. - * @param sink The output channel into which downloaded content will be - * written. - */ - public UrlRequest(UrlRequestContext requestContext, String url, - int priority, Map headers, - WritableByteChannel sink) { - if (requestContext == null) { - throw new NullPointerException("Context is required"); - } - if (url == null) { - throw new NullPointerException("URL is required"); - } - mRequestContext = requestContext; - mUrl = url; - mPriority = priority; - mHeaders = headers; - mSink = sink; - mLock = new ContextLock(); - mUrlRequestPeer = nativeCreateRequestPeer( - mRequestContext.getUrlRequestContextPeer(), mUrl, mPriority); - } - - /** - * Adds a request header. - */ - public void addHeader(String header, String value) { - validateNotStarted(); - if (mAdditionalHeaders == null) { - mAdditionalHeaders = new HashMap(); - } - mAdditionalHeaders.put(header, value); - } - - /** - * Sets data to upload as part of a POST request. - * - * @param contentType MIME type of the post content or null if this is not a - * POST. - * @param data The content that needs to be uploaded if this is a POST - * request. - */ - public void setUploadData(String contentType, byte[] data) { - synchronized (mLock) { - validateNotStarted(); - mUploadContentType = contentType; - mUploadData = data; - mUploadChannel = null; - } - } - - /** - * Sets a readable byte channel to upload as part of a POST request. - * - * @param contentType MIME type of the post content or null if this is not a - * POST request. - * @param channel The channel to read to read upload data from if this is a - * POST request. - * @param contentLength The length of data to upload. - */ - public void setUploadChannel(String contentType, - ReadableByteChannel channel, long contentLength) { - synchronized (mLock) { - validateNotStarted(); - mUploadContentType = contentType; - mUploadChannel = channel; - mUploadContentLength = contentLength; - mUploadData = null; - } - } - - public void setHttpMethod(String method) { - validateNotStarted(); - if (!("PUT".equals(method) || "POST".equals(method))) { - throw new IllegalArgumentException("Only PUT and POST are allowed."); - } - mMethod = method; - } - - public WritableByteChannel getSink() { - return mSink; - } - - public void start() { - synchronized (mLock) { - if (mCanceled) { - return; - } - - validateNotStarted(); - validateNotRecycled(); - - mStarted = true; - - String method = mMethod; - if (method == null && - ((mUploadData != null && mUploadData.length > 0) || - mUploadChannel != null)) { - // Default to POST if there is data to upload but no method was - // specified. - method = "POST"; - } - - if (method != null) { - nativeSetMethod(mUrlRequestPeer, method); - } - - if (mHeaders != null && !mHeaders.isEmpty()) { - for (Entry entry : mHeaders.entrySet()) { - nativeAddHeader(mUrlRequestPeer, entry.getKey(), - entry.getValue()); - } - } - - if (mAdditionalHeaders != null) { - for (Entry entry : - mAdditionalHeaders.entrySet()) { - nativeAddHeader(mUrlRequestPeer, entry.getKey(), - entry.getValue()); - } - } - - if (mUploadData != null && mUploadData.length > 0) { - nativeSetUploadData(mUrlRequestPeer, mUploadContentType, - mUploadData); - } else if (mUploadChannel != null) { - nativeSetUploadChannel(mUrlRequestPeer, mUploadContentType, - mUploadContentLength); - } - - nativeStart(mUrlRequestPeer); - } - } - - public void cancel() { - synchronized (mLock) { - if (mCanceled) { - return; - } - - mCanceled = true; - - if (!mRecycled) { - nativeCancel(mUrlRequestPeer); - } - } - } - - public boolean isCanceled() { - synchronized (mLock) { - return mCanceled; - } - } - - public boolean isRecycled() { - synchronized (mLock) { - return mRecycled; - } - } - - /** - * Returns an exception if any, or null if the request was completed - * successfully. - */ - public IOException getException() { - if (mSinkException != null) { - return mSinkException; - } - - validateNotRecycled(); - - int errorCode = nativeGetErrorCode(mUrlRequestPeer); - switch (errorCode) { - case UrlRequestError.SUCCESS: - return null; - case UrlRequestError.UNKNOWN: - return new IOException(nativeGetErrorString(mUrlRequestPeer)); - case UrlRequestError.MALFORMED_URL: - return new MalformedURLException("Malformed URL: " + mUrl); - case UrlRequestError.CONNECTION_TIMED_OUT: - return new ConnectTimeoutException("Connection timed out"); - case UrlRequestError.UNKNOWN_HOST: - String host; - try { - host = new URL(mUrl).getHost(); - } catch (MalformedURLException e) { - host = mUrl; - } - return new UnknownHostException("Unknown host: " + host); - default: - throw new IllegalStateException( - "Unrecognized error code: " + errorCode); - } - } - - public int getHttpStatusCode() { - return nativeGetHttpStatusCode(mUrlRequestPeer); - } - - /** - * Content length as reported by the server. May be -1 or incorrect if the - * server returns the wrong number, which happens even with Google servers. - */ - public long getContentLength() { - return mContentLength; - } - - public String getContentType() { - return mContentType; - } - - public String getHeader(String name) { - validateHeadersAvailable(); - return nativeGetHeader(mUrlRequestPeer, name); - } - - // All response headers. - public Map> getAllHeaders() { - validateHeadersAvailable(); - ResponseHeadersMap result = new ResponseHeadersMap(); - nativeGetAllHeaders(mUrlRequestPeer, result); - return result; - } - - /** - * A callback invoked when the first chunk of the response has arrived. - */ - @CalledByNative - protected void onResponseStarted() { - mContentType = nativeGetContentType(mUrlRequestPeer); - mContentLength = nativeGetContentLength(mUrlRequestPeer); - mHeadersAvailable = true; - } - - /** - * A callback invoked when the response has been fully consumed. - */ - protected void onRequestComplete() { - } - - /** - * Consumes a portion of the response. - * - * @param byteBuffer The ByteBuffer to append. Must be a direct buffer, and - * no references to it may be retained after the method ends, as - * it wraps code managed on the native heap. - */ - @CalledByNative - protected void onBytesRead(ByteBuffer byteBuffer) { - try { - while (byteBuffer.hasRemaining()) { - mSink.write(byteBuffer); - } - } catch (IOException e) { - mSinkException = e; - cancel(); - } - } - - /** - * Notifies the listener, releases native data structures. - */ - @SuppressWarnings("unused") - @CalledByNative - private void finish() { - synchronized (mLock) { - mFinished = true; - - if (mRecycled) { - return; - } - try { - mSink.close(); - } catch (IOException e) { - // Ignore - } - onRequestComplete(); - nativeDestroyRequestPeer(mUrlRequestPeer); - mUrlRequestPeer = 0; - mRecycled = true; - } - } - - /** - * Appends header |name| with value |value| to |headersMap|. - */ - @SuppressWarnings("unused") - @CalledByNative - private void onAppendResponseHeader(ResponseHeadersMap headersMap, - String name, String value) { - if (!headersMap.containsKey(name)) { - headersMap.put(name, new ArrayList()); - } - headersMap.get(name).add(value); - } - - /** - * Reads a sequence of bytes from upload channel into the given buffer. - * @param dest The buffer into which bytes are to be transferred. - * @return Returns number of bytes read (could be 0) or -1 and closes - * the channel if error occured. - */ - @SuppressWarnings("unused") - @CalledByNative - private int readFromUploadChannel(ByteBuffer dest) { - if (mUploadChannel == null || !mUploadChannel.isOpen()) - return -1; - try { - int result = mUploadChannel.read(dest); - if (result < 0) { - mUploadChannel.close(); - return 0; - } - return result; - } catch (IOException e) { - mSinkException = e; - try { - mUploadChannel.close(); - } catch (IOException ignored) { - // Ignore this exception. - } - cancel(); - return -1; - } - } - - private void validateNotRecycled() { - if (mRecycled) { - throw new IllegalStateException("Accessing recycled request"); - } - } - - private void validateNotStarted() { - if (mStarted) { - throw new IllegalStateException("Request already started"); - } - } - - private void validateHeadersAvailable() { - if (!mHeadersAvailable) { - throw new IllegalStateException("Response headers not available"); - } - } - - public String getUrl() { - return mUrl; - } - - private native long nativeCreateRequestPeer(long urlRequestContextPeer, - String url, int priority); - - private native void nativeAddHeader(long urlRequestPeer, String name, - String value); - - private native void nativeSetMethod(long urlRequestPeer, String method); - - private native void nativeSetUploadData(long urlRequestPeer, - String contentType, byte[] content); - - private native void nativeSetUploadChannel(long urlRequestPeer, - String contentType, long contentLength); - - private native void nativeStart(long urlRequestPeer); - - private native void nativeCancel(long urlRequestPeer); - - private native void nativeDestroyRequestPeer(long urlRequestPeer); - - private native int nativeGetErrorCode(long urlRequestPeer); - - private native int nativeGetHttpStatusCode(long urlRequestPeer); - - private native String nativeGetErrorString(long urlRequestPeer); - - private native String nativeGetContentType(long urlRequestPeer); - - private native long nativeGetContentLength(long urlRequestPeer); - - private native String nativeGetHeader(long urlRequestPeer, String name); - - private native void nativeGetAllHeaders(long urlRequestPeer, - ResponseHeadersMap headers); - - // Explicit class to work around JNI-generator generics confusion. - private class ResponseHeadersMap extends HashMap> { - } -} diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java deleted file mode 100644 index d20f0a33daee7..0000000000000 --- a/components/cronet/android/java/src/org/chromium/net/UrlRequestContext.java +++ /dev/null @@ -1,138 +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.net; - -import android.content.Context; -import android.os.ConditionVariable; -import android.os.Process; -import android.util.Log; - -import org.chromium.base.CalledByNative; -import org.chromium.base.JNINamespace; - -/** - * Provides context for the native HTTP operations. - */ -@JNINamespace("cronet") -public class UrlRequestContext { - private static final int LOG_NONE = 3; // LOG(FATAL), no VLOG. - private static final int LOG_DEBUG = -1; // LOG(FATAL...INFO), VLOG(1) - private static final int LOG_VERBOSE = -2; // LOG(FATAL...INFO), VLOG(2) - private static final String LOG_TAG = "ChromiumNetwork"; - - /** - * Native peer object, owned by UrlRequestContext. - */ - private long mUrlRequestContextPeer; - - private final ConditionVariable mStarted = new ConditionVariable(); - - /** - * Constructor. - * - */ - protected UrlRequestContext(Context context, String userAgent, - String config) { - mUrlRequestContextPeer = nativeCreateRequestContextPeer(context, - userAgent, getLoggingLevel(), config); - if (mUrlRequestContextPeer == 0) - throw new NullPointerException("Context Peer creation failed"); - - // TODO(mef): Revisit the need of block here. - mStarted.block(2000); - } - - /** - * Returns the version of this network stack formatted as N.N.N.N/X where - * N.N.N.N is the version of Chromium and X is the revision number. - */ - public static String getVersion() { - return Version.getVersion(); - } - - /** - * Initializes statistics recorder. - */ - public void initializeStatistics() { - nativeInitializeStatistics(); - } - - /** - * Gets current statistics recorded since |initializeStatistics| with - * |filter| as a substring as JSON text (an empty |filter| will include all - * registered histograms). - */ - public String getStatisticsJSON(String filter) { - return nativeGetStatisticsJSON(filter); - } - - /** - * Starts NetLog logging to a file named |fileName| in the - * application temporary directory. |fileName| must not be empty. Log level - * is LOG_ALL_BUT_BYTES. If the file exists it is truncated before starting. - * If actively logging the call is ignored. - */ - public void startNetLogToFile(String fileName) { - nativeStartNetLogToFile(mUrlRequestContextPeer, fileName); - } - - /** - * Stops NetLog logging and flushes file to disk. If a logging session is - * not in progress this call is ignored. - */ - public void stopNetLog() { - nativeStopNetLog(mUrlRequestContextPeer); - } - - @CalledByNative - private void initNetworkThread() { - Thread.currentThread().setName("ChromiumNet"); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - mStarted.open(); - } - - @Override - protected void finalize() throws Throwable { - nativeReleaseRequestContextPeer(mUrlRequestContextPeer); - super.finalize(); - } - - protected long getUrlRequestContextPeer() { - return mUrlRequestContextPeer; - } - - /** - * @return loggingLevel see {@link #LOG_NONE}, {@link #LOG_DEBUG} and - * {@link #LOG_VERBOSE}. - */ - private int getLoggingLevel() { - int loggingLevel; - if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { - loggingLevel = LOG_VERBOSE; - } else if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { - loggingLevel = LOG_DEBUG; - } else { - loggingLevel = LOG_NONE; - } - return loggingLevel; - } - - // Returns an instance URLRequestContextPeer to be stored in - // mUrlRequestContextPeer. - private native long nativeCreateRequestContextPeer(Context context, - String userAgent, int loggingLevel, String config); - - private native void nativeReleaseRequestContextPeer( - long urlRequestContextPeer); - - private native void nativeInitializeStatistics(); - - private native String nativeGetStatisticsJSON(String filter); - - private native void nativeStartNetLogToFile(long urlRequestContextPeer, - String fileName); - - private native void nativeStopNetLog(long urlRequestContextPeer); -} diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestError.template b/components/cronet/android/java/src/org/chromium/net/UrlRequestError.template deleted file mode 100644 index 85ce7bf035409..0000000000000 --- a/components/cronet/android/java/src/org/chromium/net/UrlRequestError.template +++ /dev/null @@ -1,14 +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.net; - -// A simple auto-generated interface used to list request errors as used by -// both org.chromium.net.UrlRequest and -// net/cronet/android/org_chromium_net_UrlRequest.h -public interface UrlRequestError { -#define DEFINE_REQUEST_ERROR(x,y) public static final int x = y; -#include "components/cronet/android/org_chromium_net_UrlRequest_error_list.h" -#undef DEFINE_REQUEST_ERROR -} diff --git a/components/cronet/android/java/src/org/chromium/net/UrlRequestPriority.template b/components/cronet/android/java/src/org/chromium/net/UrlRequestPriority.template deleted file mode 100644 index 92d2df2d3e9c9..0000000000000 --- a/components/cronet/android/java/src/org/chromium/net/UrlRequestPriority.template +++ /dev/null @@ -1,14 +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.net; - -// A simple auto-generated interface used to list request priorities as used by -// both org.chromium.net.UrlRequest and -// net/cronet/android/org_chromium_net_UrlRequest.h -public interface UrlRequestPriority { -#define DEFINE_REQUEST_PRIORITY(x,y) public static final int x = y; -#include "components/cronet/android/org_chromium_net_UrlRequest_priority_list.h" -#undef DEFINE_REQUEST_PRIORITY -} diff --git a/components/cronet/android/org_chromium_net_UrlRequest.cc b/components/cronet/android/org_chromium_net_UrlRequest.cc deleted file mode 100644 index 0f9ea6433bc45..0000000000000 --- a/components/cronet/android/org_chromium_net_UrlRequest.cc +++ /dev/null @@ -1,352 +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/cronet/android/org_chromium_net_UrlRequest.h" - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/macros.h" -#include "components/cronet/android/url_request_context_peer.h" -#include "components/cronet/android/url_request_peer.h" -#include "jni/UrlRequest_jni.h" -#include "net/base/net_errors.h" -#include "net/base/request_priority.h" -#include "net/http/http_response_headers.h" - -using base::android::ConvertUTF8ToJavaString; - -namespace cronet { -namespace { - -net::RequestPriority ConvertRequestPriority(jint request_priority) { - switch (request_priority) { - case REQUEST_PRIORITY_IDLE: - return net::IDLE; - case REQUEST_PRIORITY_LOWEST: - return net::LOWEST; - case REQUEST_PRIORITY_LOW: - return net::LOW; - case REQUEST_PRIORITY_MEDIUM: - return net::MEDIUM; - case REQUEST_PRIORITY_HIGHEST: - return net::HIGHEST; - default: - return net::LOWEST; - } -} - -void SetPostContentType(JNIEnv* env, - URLRequestPeer* request, - jstring content_type) { - DCHECK(request != NULL); - - std::string method_post("POST"); - request->SetMethod(method_post); - - std::string content_type_header("Content-Type"); - - const char* content_type_utf8 = env->GetStringUTFChars(content_type, NULL); - std::string content_type_string(content_type_utf8); - env->ReleaseStringUTFChars(content_type, content_type_utf8); - - request->AddHeader(content_type_header, content_type_string); -} - -// A delegate of URLRequestPeer that delivers callbacks to the Java layer. -class JniURLRequestPeerDelegate - : public URLRequestPeer::URLRequestPeerDelegate { - public: - JniURLRequestPeerDelegate(JNIEnv* env, jobject owner) { - owner_ = env->NewGlobalRef(owner); - } - - virtual void OnResponseStarted(URLRequestPeer* request) OVERRIDE { - JNIEnv* env = base::android::AttachCurrentThread(); - cronet::Java_UrlRequest_onResponseStarted(env, owner_); - } - - virtual void OnBytesRead(URLRequestPeer* request) OVERRIDE { - int bytes_read = request->bytes_read(); - if (bytes_read != 0) { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef java_buffer( - env, env->NewDirectByteBuffer(request->Data(), bytes_read)); - cronet::Java_UrlRequest_onBytesRead(env, owner_, java_buffer.obj()); - } - } - - virtual void OnRequestFinished(URLRequestPeer* request) OVERRIDE { - JNIEnv* env = base::android::AttachCurrentThread(); - cronet::Java_UrlRequest_finish(env, owner_); - } - - virtual int ReadFromUploadChannel(net::IOBuffer* buf, - int buf_length) OVERRIDE { - JNIEnv* env = base::android::AttachCurrentThread(); - base::android::ScopedJavaLocalRef java_buffer( - env, env->NewDirectByteBuffer(buf->data(), buf_length)); - jint bytes_read = cronet::Java_UrlRequest_readFromUploadChannel( - env, owner_, java_buffer.obj()); - return bytes_read; - } - - protected: - virtual ~JniURLRequestPeerDelegate() { - JNIEnv* env = base::android::AttachCurrentThread(); - env->DeleteGlobalRef(owner_); - } - - private: - jobject owner_; - - DISALLOW_COPY_AND_ASSIGN(JniURLRequestPeerDelegate); -}; - -} // namespace - -// Explicitly register static JNI functions. -bool UrlRequestRegisterJni(JNIEnv* env) { return RegisterNativesImpl(env); } - -static jlong CreateRequestPeer(JNIEnv* env, - jobject object, - jlong urlRequestContextPeer, - jstring url_string, - jint priority) { - URLRequestContextPeer* context = - reinterpret_cast(urlRequestContextPeer); - DCHECK(context != NULL); - - const char* url_utf8 = env->GetStringUTFChars(url_string, NULL); - - VLOG(1) << "New chromium network request. URL:" << url_utf8; - - GURL url(url_utf8); - - env->ReleaseStringUTFChars(url_string, url_utf8); - - URLRequestPeer* peer = - new URLRequestPeer(context, - new JniURLRequestPeerDelegate(env, object), - url, - ConvertRequestPriority(priority)); - - return reinterpret_cast(peer); -} - -// synchronized -static void AddHeader(JNIEnv* env, - jobject object, - jlong urlRequestPeer, - jstring name, - jstring value) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - DCHECK(request); - - std::string name_string(base::android::ConvertJavaStringToUTF8(env, name)); - std::string value_string(base::android::ConvertJavaStringToUTF8(env, value)); - - request->AddHeader(name_string, value_string); -} - -static void SetMethod(JNIEnv* env, - jobject object, - jlong urlRequestPeer, - jstring method) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - DCHECK(request); - - std::string method_string( - base::android::ConvertJavaStringToUTF8(env, method)); - - request->SetMethod(method_string); -} - -static void SetUploadData(JNIEnv* env, - jobject object, - jlong urlRequestPeer, - jstring content_type, - jbyteArray content) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - SetPostContentType(env, request, content_type); - - if (content != NULL) { - jsize size = env->GetArrayLength(content); - if (size > 0) { - jbyte* content_bytes = env->GetByteArrayElements(content, NULL); - request->SetUploadContent(reinterpret_cast(content_bytes), - size); - env->ReleaseByteArrayElements(content, content_bytes, 0); - } - } -} - -static void SetUploadChannel(JNIEnv* env, - jobject object, - jlong urlRequestPeer, - jstring content_type, - jlong content_length) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - SetPostContentType(env, request, content_type); - - request->SetUploadChannel(env, content_length); -} - - -/* synchronized */ -static void Start(JNIEnv* env, jobject object, jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request != NULL) { - request->Start(); - } -} - -/* synchronized */ -static void DestroyRequestPeer(JNIEnv* env, - jobject object, - jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request != NULL) { - request->Destroy(); - } -} - -/* synchronized */ -static void Cancel(JNIEnv* env, jobject object, jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request != NULL) { - request->Cancel(); - } -} - -static jint GetErrorCode(JNIEnv* env, jobject object, jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - int error_code = request->error_code(); - switch (error_code) { - // TODO(mef): Investigate returning success on positive values, too, as - // they technically indicate success. - case net::OK: - return REQUEST_ERROR_SUCCESS; - - // TODO(mef): Investigate this. The fact is that Chrome does not do this, - // and this library is not just being used for downloads. - - // Comment from src/content/browser/download/download_resource_handler.cc: - // ERR_CONTENT_LENGTH_MISMATCH and ERR_INCOMPLETE_CHUNKED_ENCODING are - // allowed since a number of servers in the wild close the connection too - // early by mistake. Other browsers - IE9, Firefox 11.0, and Safari 5.1.4 - - // treat downloads as complete in both cases, so we follow their lead. - case net::ERR_CONTENT_LENGTH_MISMATCH: - case net::ERR_INCOMPLETE_CHUNKED_ENCODING: - return REQUEST_ERROR_SUCCESS; - - case net::ERR_INVALID_URL: - case net::ERR_DISALLOWED_URL_SCHEME: - case net::ERR_UNKNOWN_URL_SCHEME: - return REQUEST_ERROR_MALFORMED_URL; - - case net::ERR_CONNECTION_TIMED_OUT: - return REQUEST_ERROR_CONNECTION_TIMED_OUT; - - case net::ERR_NAME_NOT_RESOLVED: - return REQUEST_ERROR_UNKNOWN_HOST; - } - return REQUEST_ERROR_UNKNOWN; -} - -static jstring GetErrorString(JNIEnv* env, - jobject object, - jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - int error_code = request->error_code(); - char buffer[200]; - std::string error_string = net::ErrorToString(error_code); - snprintf(buffer, - sizeof(buffer), - "System error: %s(%d)", - error_string.c_str(), - error_code); - return ConvertUTF8ToJavaString(env, buffer).Release(); -} - -static jint GetHttpStatusCode(JNIEnv* env, - jobject object, - jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - return request->http_status_code(); -} - -static jstring GetContentType(JNIEnv* env, - jobject object, - jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request == NULL) { - return NULL; - } - std::string type = request->content_type(); - if (!type.empty()) { - return ConvertUTF8ToJavaString(env, type.c_str()).Release(); - } else { - return NULL; - } -} - -static jlong GetContentLength(JNIEnv* env, - jobject object, - jlong urlRequestPeer) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request == NULL) { - return 0; - } - return request->content_length(); -} - -static jstring GetHeader( - JNIEnv* env, jobject object, jlong urlRequestPeer, jstring name) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request == NULL) { - return NULL; - } - - std::string name_string = base::android::ConvertJavaStringToUTF8(env, name); - std::string value = request->GetHeader(name_string); - if (!value.empty()) { - return ConvertUTF8ToJavaString(env, value.c_str()).Release(); - } else { - return NULL; - } -} - -static void GetAllHeaders(JNIEnv* env, - jobject object, - jlong urlRequestPeer, - jobject headersMap) { - URLRequestPeer* request = reinterpret_cast(urlRequestPeer); - if (request == NULL) - return; - - net::HttpResponseHeaders* headers = request->GetResponseHeaders(); - if (headers == NULL) - return; - - void* iter = NULL; - std::string header_name; - std::string header_value; - while (headers->EnumerateHeaderLines(&iter, &header_name, &header_value)) { - ScopedJavaLocalRef name = - ConvertUTF8ToJavaString(env, header_name); - ScopedJavaLocalRef value = - ConvertUTF8ToJavaString(env, header_value); - Java_UrlRequest_onAppendResponseHeader( - env, object, headersMap, name.Release(), value.Release()); - } - - // Some implementations (notably HttpURLConnection) include a mapping for the - // null key; in HTTP's case, this maps to the HTTP status line. - ScopedJavaLocalRef status_line = - ConvertUTF8ToJavaString(env, headers->GetStatusLine()); - Java_UrlRequest_onAppendResponseHeader( - env, object, headersMap, NULL, status_line.Release()); -} - -} // namespace cronet diff --git a/components/cronet/android/org_chromium_net_UrlRequest.h b/components/cronet/android/org_chromium_net_UrlRequest.h deleted file mode 100644 index 9a8f3d97cd627..0000000000000 --- a/components/cronet/android/org_chromium_net_UrlRequest.h +++ /dev/null @@ -1,32 +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_CRONET_ANDROID_URLREQUEST_H_ -#define COMPONENTS_CRONET_ANDROID_URLREQUEST_H_ - -#include - -namespace cronet { - -// Define request priority values like REQUEST_PRIORITY_IDLE in a -// way that ensures they're always the same than their Java counterpart. -enum UrlRequestPriority { -#define DEFINE_REQUEST_PRIORITY(x, y) REQUEST_PRIORITY_##x = y, -#include "components/cronet/android/org_chromium_net_UrlRequest_priority_list.h" -#undef DEFINE_REQUEST_PRIORITY -}; - -// Define request priority values like REQUEST_ERROR_SUCCESS in a -// way that ensures they're always the same than their Java counterpart. -enum UrlRequestError { -#define DEFINE_REQUEST_ERROR(x, y) REQUEST_ERROR_##x = y, -#include "components/cronet/android/org_chromium_net_UrlRequest_error_list.h" -#undef DEFINE_REQUEST_ERROR -}; - -bool UrlRequestRegisterJni(JNIEnv* env); - -} // namespace cronet - -#endif // COMPONENTS_CRONET_ANDROID_URLREQUEST_H_ diff --git a/components/cronet/android/org_chromium_net_UrlRequestContext.cc b/components/cronet/android/org_chromium_net_UrlRequestContext.cc deleted file mode 100644 index b378c2850cabd..0000000000000 --- a/components/cronet/android/org_chromium_net_UrlRequestContext.cc +++ /dev/null @@ -1,148 +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/cronet/android/org_chromium_net_UrlRequestContext.h" - -#include - -#include "base/android/jni_android.h" -#include "base/android/jni_string.h" -#include "base/json/json_reader.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/metrics/statistics_recorder.h" -#include "base/values.h" -#include "components/cronet/android/org_chromium_net_UrlRequest.h" -#include "components/cronet/android/url_request_context_peer.h" -#include "components/cronet/android/url_request_peer.h" -#include "components/cronet/url_request_context_config.h" -#include "jni/UrlRequestContext_jni.h" - -namespace { - -// Delegate of URLRequestContextPeer that delivers callbacks to the Java layer. -class JniURLRequestContextPeerDelegate - : public cronet::URLRequestContextPeer::URLRequestContextPeerDelegate { - public: - JniURLRequestContextPeerDelegate(JNIEnv* env, jobject owner) - : owner_(env->NewGlobalRef(owner)) { - } - - virtual void OnContextInitialized( - cronet::URLRequestContextPeer* context) OVERRIDE { - JNIEnv* env = base::android::AttachCurrentThread(); - cronet::Java_UrlRequestContext_initNetworkThread(env, owner_); - // TODO(dplotnikov): figure out if we need to detach from the thread. - // The documentation says we should detach just before the thread exits. - } - - protected: - virtual ~JniURLRequestContextPeerDelegate() { - JNIEnv* env = base::android::AttachCurrentThread(); - env->DeleteGlobalRef(owner_); - } - - private: - jobject owner_; -}; - -} // namespace - -namespace cronet { - -// Explicitly register static JNI functions. -bool UrlRequestContextRegisterJni(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -// Sets global user-agent to be used for all subsequent requests. -static jlong CreateRequestContextPeer(JNIEnv* env, - jobject object, - jobject context, - jstring user_agent, - jint log_level, - jstring config) { - std::string user_agent_string = - base::android::ConvertJavaStringToUTF8(env, user_agent); - - std::string config_string = - base::android::ConvertJavaStringToUTF8(env, config); - - scoped_ptr config_value(base::JSONReader::Read(config_string)); - if (!config_value || !config_value->IsType(base::Value::TYPE_DICTIONARY)) { - DLOG(ERROR) << "Bad JSON: " << config_string; - return 0; - } - - scoped_ptr context_config( - new URLRequestContextConfig()); - base::JSONValueConverter converter; - if (!converter.Convert(*config_value, context_config.get())) { - DLOG(ERROR) << "Bad Config: " << config_value; - return 0; - } - - // Set application context. - base::android::ScopedJavaLocalRef scoped_context(env, context); - base::android::InitApplicationContext(env, scoped_context); - - // TODO(mef): MinLogLevel is global, shared by all URLRequestContexts. - // Revisit this if each URLRequestContext would need an individual log level. - logging::SetMinLogLevel(static_cast(log_level)); - - // TODO(dplotnikov): set application context. - URLRequestContextPeer* peer = new URLRequestContextPeer( - new JniURLRequestContextPeerDelegate(env, object), user_agent_string); - peer->AddRef(); // Hold onto this ref-counted object. - peer->Initialize(context_config.Pass()); - return reinterpret_cast(peer); -} - -// Releases native objects. -static void ReleaseRequestContextPeer(JNIEnv* env, - jobject object, - jlong urlRequestContextPeer) { - URLRequestContextPeer* peer = - reinterpret_cast(urlRequestContextPeer); - // TODO(mef): Revisit this from thread safety point of view: Can we delete a - // thread while running on that thread? - // URLRequestContextPeer is a ref-counted object, and may have pending tasks, - // so we need to release it instead of deleting here. - peer->Release(); -} - -// Starts recording statistics. -static void InitializeStatistics(JNIEnv* env, jobject jcaller) { - base::StatisticsRecorder::Initialize(); -} - -// Gets current statistics with |filter| as a substring as JSON text (an empty -// |filter| will include all registered histograms). -static jstring GetStatisticsJSON(JNIEnv* env, jobject jcaller, jstring filter) { - std::string query = base::android::ConvertJavaStringToUTF8(env, filter); - std::string json = base::StatisticsRecorder::ToJSON(query); - return base::android::ConvertUTF8ToJavaString(env, json).Release(); -} - -// Starts recording NetLog into file with |fileName|. -static void StartNetLogToFile(JNIEnv* env, - jobject jcaller, - jlong urlRequestContextPeer, - jstring fileName) { - URLRequestContextPeer* peer = - reinterpret_cast(urlRequestContextPeer); - std::string file_name = base::android::ConvertJavaStringToUTF8(env, fileName); - peer->StartNetLogToFile(file_name); -} - -// Stops recording NetLog. -static void StopNetLog(JNIEnv* env, - jobject jcaller, - jlong urlRequestContextPeer) { - URLRequestContextPeer* peer = - reinterpret_cast(urlRequestContextPeer); - peer->StopNetLog(); -} - -} // namespace cronet diff --git a/components/cronet/android/org_chromium_net_UrlRequestContext.h b/components/cronet/android/org_chromium_net_UrlRequestContext.h deleted file mode 100644 index 4052e5ff0fdd8..0000000000000 --- a/components/cronet/android/org_chromium_net_UrlRequestContext.h +++ /dev/null @@ -1,16 +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_CRONET_ANDROID_URLREQUEST_CONTEXT_H_ -#define COMPONENTS_CRONET_ANDROID_URLREQUEST_CONTEXT_H_ - -#include - -namespace cronet { - -bool UrlRequestContextRegisterJni(JNIEnv* env); - -} // namespace cronet - -#endif // COMPONENTS_CRONET_ANDROID_URLREQUEST_CONTEXT_H_ diff --git a/components/cronet/android/proguard.cfg b/components/cronet/android/proguard.cfg index 7d1fe1a435ee5..938960ae9922a 100644 --- a/components/cronet/android/proguard.cfg +++ b/components/cronet/android/proguard.cfg @@ -7,8 +7,21 @@ -keepclasseswithmembers class org.chromium.** { @org.chromium.base.*Native* ; } --keep @org.chromium.base.JNINamespace class org.chromium.** { + +# TODO(mef) remove unnecessary classes from base, so we don't have to preserve +# their methods +-keepclasseswithmembers class org.chromium.** { native ; } + -dontnote org.chromium.net.AndroidKeyStore +# Needed so that multiple optimization passes will detect annotations +-keepattributes *Annotation* + +# Keep methods used by reflection and native code +-keep class org.chromium.net.UsedBy* +-keep @org.chromium.net.UsedBy* class * +-keepclassmembers class * { + @org.chromium.net.UsedBy* *; +} \ No newline at end of file diff --git a/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleUrlTest.java b/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleUrlTest.java index 860237612df1c..3136ca72646bb 100644 --- a/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleUrlTest.java +++ b/components/cronet/android/sample/javatests/src/org/chromium/cronet_sample_apk/CronetSampleUrlTest.java @@ -4,12 +4,17 @@ package org.chromium.cronet_sample_apk; +import android.os.ConditionVariable; + import android.test.suitebuilder.annotation.SmallTest; import org.chromium.base.test.util.Feature; +import org.chromium.net.HttpUrlRequest; import org.chromium.net.HttpUrlRequestFactoryConfig; +import org.chromium.net.HttpUrlRequestListener; import java.io.File; +import java.util.HashMap; /** * Example test that just starts the cronet sample. @@ -78,16 +83,64 @@ public void testNetLog() throws Exception { waitForActiveShellToBeDoneLoading(); File file = File.createTempFile("cronet", "json"); - activity.mRequestContext.startNetLogToFile(file.getPath()); - activity.startWithURL_UrlRequest(URL); + activity.mChromiumRequestFactory.getRequestContext().startNetLogToFile( + file.getPath()); + activity.startWithURL(URL); Thread.sleep(5000); - activity.mRequestContext.stopNetLog(); + activity.mChromiumRequestFactory.getRequestContext().stopNetLog(); assertTrue(file.exists()); assertTrue(file.length() != 0); assertTrue(file.delete()); assertTrue(!file.exists()); } + class BadHttpUrlRequestListener implements HttpUrlRequestListener { + static final String THROW_TAG = "BadListener"; + ConditionVariable mComplete = new ConditionVariable(); + + public BadHttpUrlRequestListener() { + } + + @Override + public void onResponseStarted(HttpUrlRequest request) { + throw new NullPointerException(THROW_TAG); + } + + @Override + public void onRequestComplete(HttpUrlRequest request) { + mComplete.open(); + throw new NullPointerException(THROW_TAG); + } + + public void blockForComplete() { + mComplete.block(); + } + } + + @SmallTest + @Feature({"Cronet"}) + public void testCalledByNativeException() throws Exception { + CronetSampleActivity activity = launchCronetSampleWithUrl(URL); + + // Make sure the activity was created as expected. + assertNotNull(activity); + + waitForActiveShellToBeDoneLoading(); + + HashMap headers = new HashMap(); + BadHttpUrlRequestListener listener = new BadHttpUrlRequestListener(); + + // Create request with null listener to trigger an exception. + HttpUrlRequest request = activity.mChromiumRequestFactory.createRequest( + URL, HttpUrlRequest.REQUEST_PRIORITY_MEDIUM, headers, listener); + request.start(); + listener.blockForComplete(); + assertTrue(request.isCanceled()); + assertNotNull(request.getException()); + assertEquals(listener.THROW_TAG, request.getException().getCause().getMessage()); + + } + @SmallTest @Feature({"Cronet"}) public void testLegacyLoadUrl() throws Exception { diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java index 96762787cc173..84ee97e2fdbe6 100644 --- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java +++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleActivity.java @@ -14,23 +14,19 @@ import android.widget.EditText; import android.widget.Toast; +import org.chromium.net.ChromiumUrlRequestFactory; import org.chromium.net.HttpUrlRequest; import org.chromium.net.HttpUrlRequestFactory; import org.chromium.net.HttpUrlRequestFactoryConfig; import org.chromium.net.HttpUrlRequestListener; import org.chromium.net.LibraryLoader; -import org.chromium.net.UrlRequest; -import org.chromium.net.UrlRequestContext; -import org.chromium.net.UrlRequestPriority; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; import java.util.HashMap; -import java.util.Map; /** * Activity for managing the Cronet Sample. @@ -43,8 +39,7 @@ public class CronetSampleActivity extends Activity { public static final String POST_DATA_KEY = "postData"; public static final String CONFIG_KEY = "config"; - UrlRequestContext mRequestContext; - + ChromiumUrlRequestFactory mChromiumRequestFactory; HttpUrlRequestFactory mRequestFactory; String mUrl; @@ -53,43 +48,6 @@ public class CronetSampleActivity extends Activity { int mHttpStatusCode = 0; - class SampleRequestContext extends UrlRequestContext { - public SampleRequestContext() { - super(getApplicationContext(), "Cronet Sample", - new HttpUrlRequestFactoryConfig().toString()); - } - } - - class SampleRequest extends UrlRequest { - public SampleRequest(UrlRequestContext requestContext, String url, - int priority, Map headers, - WritableByteChannel sink) { - super(requestContext, url, priority, headers, sink); - } - - @Override - protected void onRequestComplete() { - mHttpStatusCode = super.getHttpStatusCode(); - Log.i(TAG, "****** Request Complete, status code is " - + mHttpStatusCode); - Intent intent = new Intent(getApplicationContext(), - CronetSampleActivity.class); - startActivity(intent); - final String url = super.getUrl(); - final CharSequence text = "Completed " + url + " (" - + mHttpStatusCode + ")"; - CronetSampleActivity.this.runOnUiThread(new Runnable() { - public void run() { - mLoading = false; - Toast toast = Toast.makeText(getApplicationContext(), text, - Toast.LENGTH_SHORT); - toast.show(); - promptForURL(url); - } - }); - } - } - class SampleHttpUrlRequestListener implements HttpUrlRequestListener { public SampleHttpUrlRequestListener() { } @@ -136,7 +94,6 @@ protected void onCreate(final Bundle savedInstanceState) { return; } - mRequestContext = new SampleRequestContext(); HttpUrlRequestFactoryConfig config = new HttpUrlRequestFactoryConfig(); config.enableHttpCache(HttpUrlRequestFactoryConfig.HttpCache.IN_MEMORY, 100 * 1024) @@ -159,6 +116,9 @@ protected void onCreate(final Bundle savedInstanceState) { mRequestFactory = HttpUrlRequestFactory.createFactory( getApplicationContext(), config); + mChromiumRequestFactory = new ChromiumUrlRequestFactory( + getApplicationContext(), config); + String appUrl = getUrlFromIntent(getIntent()); if (appUrl == null) { promptForURL("https://"); @@ -219,7 +179,7 @@ private void applyCommandLineToHttpUrlRequest(HttpUrlRequest request) { } } - private void startWithURL(String url) { + public void startWithURL(String url) { Log.i(TAG, "Cronet started: " + url); mUrl = url; mLoading = true; @@ -227,36 +187,11 @@ private void startWithURL(String url) { HashMap headers = new HashMap(); HttpUrlRequestListener listener = new SampleHttpUrlRequestListener(); HttpUrlRequest request = mRequestFactory.createRequest( - url, UrlRequestPriority.MEDIUM, headers, listener); + url, HttpUrlRequest.REQUEST_PRIORITY_MEDIUM, headers, listener); applyCommandLineToHttpUrlRequest(request); request.start(); } - private void applyCommandLineToUrlRequest(UrlRequest request) { - String postData = getCommandLineArg(POST_DATA_KEY); - if (postData != null) { - InputStream dataStream = new ByteArrayInputStream( - postData.getBytes()); - ReadableByteChannel dataChannel = Channels.newChannel(dataStream); - request.setUploadChannel("text/plain", dataChannel, - postData.length()); - request.setHttpMethod("POST"); - } - } - - public void startWithURL_UrlRequest(String url) { - Log.i(TAG, "Cronet started: " + url); - mUrl = url; - mLoading = true; - - HashMap headers = new HashMap(); - WritableByteChannel sink = Channels.newChannel(System.out); - UrlRequest request = new SampleRequest(mRequestContext, url, - UrlRequestPriority.MEDIUM, headers, sink); - applyCommandLineToUrlRequest(request); - request.start(); - } - public String getUrl() { return mUrl; } @@ -270,11 +205,12 @@ public int getHttpStatusCode() { } public void startNetLog() { - mRequestContext.startNetLogToFile( - Environment.getExternalStorageDirectory().getPath() + "/cronet_sample_netlog.json"); + mChromiumRequestFactory.getRequestContext().startNetLogToFile( + Environment.getExternalStorageDirectory().getPath() + + "/cronet_sample_netlog.json"); } public void stopNetLog() { - mRequestContext.stopNetLog(); + mChromiumRequestFactory.getRequestContext().stopNetLog(); } } diff --git a/components/cronet/android/url_request_adapter.cc b/components/cronet/android/url_request_adapter.cc new file mode 100644 index 0000000000000..8ab88a2f3cd76 --- /dev/null +++ b/components/cronet/android/url_request_adapter.cc @@ -0,0 +1,265 @@ +// 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 "url_request_adapter.h" + +#include "base/strings/string_number_conversions.h" +#include "components/cronet/android/url_request_context_adapter.h" +#include "components/cronet/android/wrapped_channel_upload_element_reader.h" +#include "net/base/load_flags.h" +#include "net/base/upload_bytes_element_reader.h" +#include "net/http/http_status_code.h" + +namespace cronet { + +static const size_t kBufferSizeIncrement = 8192; + +URLRequestAdapter::URLRequestAdapter(URLRequestContextAdapter* context, + URLRequestAdapterDelegate* delegate, + GURL url, + net::RequestPriority priority) + : method_("GET"), + url_request_(NULL), + read_buffer_(new net::GrowableIOBuffer()), + bytes_read_(0), + total_bytes_read_(0), + error_code_(0), + http_status_code_(0), + canceled_(false), + expected_size_(0) { + context_ = context; + delegate_ = delegate; + url_ = url; + priority_ = priority; +} + +URLRequestAdapter::~URLRequestAdapter() { + CHECK(url_request_ == NULL); +} + +void URLRequestAdapter::SetMethod(const std::string& method) { + method_ = method; +} + +void URLRequestAdapter::AddHeader(const std::string& name, + const std::string& value) { + headers_.SetHeader(name, value); +} + +void URLRequestAdapter::SetUploadContent(const char* bytes, int bytes_len) { + std::vector data(bytes, bytes + bytes_len); + scoped_ptr reader( + new net::UploadOwnedBytesElementReader(&data)); + upload_data_stream_.reset( + net::UploadDataStream::CreateWithReader(reader.Pass(), 0)); +} + +void URLRequestAdapter::SetUploadChannel(JNIEnv* env, int64 content_length) { + scoped_ptr reader( + new WrappedChannelElementReader(delegate_, content_length)); + upload_data_stream_.reset( + net::UploadDataStream::CreateWithReader(reader.Pass(), 0)); +} + +std::string URLRequestAdapter::GetHeader(const std::string& name) const { + std::string value; + if (url_request_ != NULL) { + url_request_->GetResponseHeaderByName(name, &value); + } + return value; +} + +net::HttpResponseHeaders* URLRequestAdapter::GetResponseHeaders() const { + if (url_request_ == NULL) { + return NULL; + } + return url_request_->response_headers(); +} + +void URLRequestAdapter::Start() { + context_->GetNetworkTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&URLRequestAdapter::OnInitiateConnection, + base::Unretained(this))); +} + +void URLRequestAdapter::OnInitiateConnection() { + if (canceled_) { + return; + } + + VLOG(1) << "Starting chromium request: " + << url_.possibly_invalid_spec().c_str() + << " priority: " << RequestPriorityToString(priority_); + url_request_ = new net::URLRequest( + url_, net::DEFAULT_PRIORITY, this, context_->GetURLRequestContext()); + url_request_->SetLoadFlags(net::LOAD_DISABLE_CACHE | + net::LOAD_DO_NOT_SAVE_COOKIES | + net::LOAD_DO_NOT_SEND_COOKIES); + url_request_->set_method(method_); + url_request_->SetExtraRequestHeaders(headers_); + if (!headers_.HasHeader(net::HttpRequestHeaders::kUserAgent)) { + std::string user_agent; + user_agent = context_->GetUserAgent(url_); + url_request_->SetExtraRequestHeaderByName( + net::HttpRequestHeaders::kUserAgent, user_agent, true /* override */); + } + + if (upload_data_stream_) + url_request_->set_upload(upload_data_stream_.Pass()); + + url_request_->SetPriority(priority_); + + url_request_->Start(); +} + +void URLRequestAdapter::Cancel() { + if (canceled_) { + return; + } + + canceled_ = true; + + context_->GetNetworkTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&URLRequestAdapter::OnCancelRequest, base::Unretained(this))); +} + +void URLRequestAdapter::OnCancelRequest() { + VLOG(1) << "Canceling chromium request: " << url_.possibly_invalid_spec(); + + if (url_request_ != NULL) { + url_request_->Cancel(); + } + + OnRequestCanceled(); +} + +void URLRequestAdapter::Destroy() { + context_->GetNetworkTaskRunner()->PostTask( + FROM_HERE, base::Bind(&URLRequestAdapter::OnDestroyRequest, this)); +} + +// static +void URLRequestAdapter::OnDestroyRequest(URLRequestAdapter* self) { + VLOG(1) << "Destroying chromium request: " + << self->url_.possibly_invalid_spec(); + delete self; +} + +void URLRequestAdapter::OnResponseStarted(net::URLRequest* request) { + if (request->status().status() != net::URLRequestStatus::SUCCESS) { + OnRequestFailed(); + return; + } + + http_status_code_ = request->GetResponseCode(); + VLOG(1) << "Response started with status: " << http_status_code_; + + request->GetResponseHeaderByName("Content-Type", &content_type_); + expected_size_ = request->GetExpectedContentSize(); + delegate_->OnResponseStarted(this); + + Read(); +} + +// Reads all available data or starts an asynchronous read. +void URLRequestAdapter::Read() { + while (true) { + if (read_buffer_->RemainingCapacity() == 0) { + int new_capacity = read_buffer_->capacity() + kBufferSizeIncrement; + read_buffer_->SetCapacity(new_capacity); + } + + int bytes_read; + if (url_request_->Read( + read_buffer_, read_buffer_->RemainingCapacity(), &bytes_read)) { + if (bytes_read == 0) { + OnRequestSucceeded(); + break; + } + + VLOG(1) << "Synchronously read: " << bytes_read << " bytes"; + OnBytesRead(bytes_read); + } else if (url_request_->status().status() == + net::URLRequestStatus::IO_PENDING) { + if (bytes_read_ != 0) { + VLOG(1) << "Flushing buffer: " << bytes_read_ << " bytes"; + + delegate_->OnBytesRead(this); + read_buffer_->set_offset(0); + bytes_read_ = 0; + } + VLOG(1) << "Started async read"; + break; + } else { + OnRequestFailed(); + break; + } + } +} + +void URLRequestAdapter::OnReadCompleted(net::URLRequest* request, + int bytes_read) { + VLOG(1) << "Asynchronously read: " << bytes_read << " bytes"; + if (bytes_read < 0) { + OnRequestFailed(); + return; + } else if (bytes_read == 0) { + OnRequestSucceeded(); + return; + } + + OnBytesRead(bytes_read); + Read(); +} + +void URLRequestAdapter::OnBytesRead(int bytes_read) { + read_buffer_->set_offset(read_buffer_->offset() + bytes_read); + bytes_read_ += bytes_read; + total_bytes_read_ += bytes_read; +} + +void URLRequestAdapter::OnRequestSucceeded() { + if (canceled_) { + return; + } + + VLOG(1) << "Request completed with HTTP status: " << http_status_code_ + << ". Total bytes read: " << total_bytes_read_; + + OnRequestCompleted(); +} + +void URLRequestAdapter::OnRequestFailed() { + if (canceled_) { + return; + } + + error_code_ = url_request_->status().error(); + VLOG(1) << "Request failed with status: " << url_request_->status().status() + << " and error: " << net::ErrorToString(error_code_); + OnRequestCompleted(); +} + +void URLRequestAdapter::OnRequestCanceled() { + OnRequestCompleted(); +} + +void URLRequestAdapter::OnRequestCompleted() { + VLOG(1) << "Completed: " << url_.possibly_invalid_spec(); + if (url_request_ != NULL) { + delete url_request_; + url_request_ = NULL; + } + + delegate_->OnBytesRead(this); + delegate_->OnRequestFinished(this); +} + +unsigned char* URLRequestAdapter::Data() const { + return reinterpret_cast(read_buffer_->StartOfBuffer()); +} + +} // namespace cronet diff --git a/components/cronet/android/url_request_adapter.h b/components/cronet/android/url_request_adapter.h new file mode 100644 index 0000000000000..482f175f62652 --- /dev/null +++ b/components/cronet/android/url_request_adapter.h @@ -0,0 +1,145 @@ +// 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_CRONET_ANDROID_URL_REQUEST_ADAPTER_H_ +#define COMPONENTS_CRONET_ANDROID_URL_REQUEST_ADAPTER_H_ + +#include + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "net/base/request_priority.h" +#include "net/http/http_request_headers.h" +#include "net/url_request/url_request.h" + +namespace net { +class GrowableIOBuffer; +class HttpResponseHeaders; +class UploadDataStream; +} // namespace net + +namespace cronet { + +class URLRequestContextAdapter; + +// An adapter from the JNI |UrlRequest| object and the Chromium |URLRequest| +// object. +class URLRequestAdapter : public net::URLRequest::Delegate { + public: + // The delegate which is called when the request finishes. + class URLRequestAdapterDelegate + : public base::RefCountedThreadSafe { + public: + virtual void OnResponseStarted(URLRequestAdapter* request) = 0; + virtual void OnBytesRead(URLRequestAdapter* request) = 0; + virtual void OnRequestFinished(URLRequestAdapter* request) = 0; + virtual int ReadFromUploadChannel(net::IOBuffer* buf, int buf_length) = 0; + + protected: + friend class base::RefCountedThreadSafe; + virtual ~URLRequestAdapterDelegate() {} + }; + + URLRequestAdapter(URLRequestContextAdapter* context, + URLRequestAdapterDelegate* delegate, + GURL url, + net::RequestPriority priority); + virtual ~URLRequestAdapter(); + + // Sets the request method GET, POST etc + void SetMethod(const std::string& method); + + // Adds a header to the request + void AddHeader(const std::string& name, const std::string& value); + + // Sets the contents of the POST or PUT request + void SetUploadContent(const char* bytes, int bytes_len); + + // Sets the request to streaming upload. + void SetUploadChannel(JNIEnv* env, int64 content_length); + + // Starts the request. + void Start(); + + // Cancels the request. + void Cancel(); + + // Releases all resources for the request and deletes the object itself. + void Destroy(); + + // Returns the URL of the request. + GURL url() const { return url_; } + + // Returns the error code after the request is complete. + // Negative codes indicate system errors. + int error_code() const { return error_code_; } + + // Returns the HTTP status code. + int http_status_code() const { + return http_status_code_; + }; + + // Returns the value of the content-length response header. + int64 content_length() const { return expected_size_; } + + // Returns the value of the content-type response header. + std::string content_type() const { return content_type_; } + + // Returns the value of the specified response header. + std::string GetHeader(const std::string& name) const; + + // Get all response headers, as a HttpResponseHeaders object. + net::HttpResponseHeaders* GetResponseHeaders() const; + + // Returns the overall number of bytes read. + size_t bytes_read() const { return bytes_read_; } + + // Returns a pointer to the downloaded data. + unsigned char* Data() const; + + virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; + + virtual void OnReadCompleted(net::URLRequest* request, + int bytes_read) OVERRIDE; + + private: + static void OnDestroyRequest(URLRequestAdapter* self); + + void OnInitiateConnection(); + void OnCancelRequest(); + void OnRequestSucceeded(); + void OnRequestFailed(); + void OnRequestCompleted(); + void OnRequestCanceled(); + void OnBytesRead(int bytes_read); + void OnAppendChunk(const char* bytes, int bytes_len, bool is_last_chunk); + + void Read(); + + URLRequestContextAdapter* context_; + scoped_refptr delegate_; + GURL url_; + net::RequestPriority priority_; + std::string method_; + net::HttpRequestHeaders headers_; + net::URLRequest* url_request_; + scoped_ptr upload_data_stream_; + scoped_refptr read_buffer_; + int bytes_read_; + int total_bytes_read_; + int error_code_; + int http_status_code_; + std::string content_type_; + bool canceled_; + int64 expected_size_; + + DISALLOW_COPY_AND_ASSIGN(URLRequestAdapter); +}; + +} // namespace cronet + +#endif // COMPONENTS_CRONET_ANDROID_URL_REQUEST_ADAPTER_H_ diff --git a/components/cronet/android/url_request_context_adapter.cc b/components/cronet/android/url_request_context_adapter.cc new file mode 100644 index 0000000000000..0feb0f13e4ad8 --- /dev/null +++ b/components/cronet/android/url_request_context_adapter.cc @@ -0,0 +1,209 @@ +// 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/cronet/android/url_request_context_adapter.h" + +#include "base/bind.h" +#include "base/file_util.h" +#include "base/single_thread_task_runner.h" +#include "components/cronet/url_request_context_config.h" +#include "net/base/net_errors.h" +#include "net/base/net_log_logger.h" +#include "net/cert/cert_verifier.h" +#include "net/http/http_auth_handler_factory.h" +#include "net/http/http_network_layer.h" +#include "net/http/http_server_properties_impl.h" +#include "net/proxy/proxy_config_service_fixed.h" +#include "net/proxy/proxy_service.h" +#include "net/ssl/ssl_config_service_defaults.h" +#include "net/url_request/static_http_user_agent_settings.h" +#include "net/url_request/url_request_context_builder.h" +#include "net/url_request/url_request_context_storage.h" +#include "net/url_request/url_request_job_factory_impl.h" + +namespace { + +class BasicNetworkDelegate : public net::NetworkDelegate { + public: + BasicNetworkDelegate() {} + virtual ~BasicNetworkDelegate() {} + + private: + // net::NetworkDelegate implementation. + virtual int OnBeforeURLRequest(net::URLRequest* request, + const net::CompletionCallback& callback, + GURL* new_url) OVERRIDE { + return net::OK; + } + + virtual int OnBeforeSendHeaders(net::URLRequest* request, + const net::CompletionCallback& callback, + net::HttpRequestHeaders* headers) OVERRIDE { + return net::OK; + } + + virtual void OnSendHeaders(net::URLRequest* request, + const net::HttpRequestHeaders& headers) OVERRIDE {} + + virtual int OnHeadersReceived( + net::URLRequest* request, + const net::CompletionCallback& callback, + const net::HttpResponseHeaders* original_response_headers, + scoped_refptr* _response_headers, + GURL* allowed_unsafe_redirect_url) OVERRIDE { + return net::OK; + } + + virtual void OnBeforeRedirect(net::URLRequest* request, + const GURL& new_location) OVERRIDE {} + + virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {} + + virtual void OnRawBytesRead(const net::URLRequest& request, + int bytes_read) OVERRIDE {} + + virtual void OnCompleted(net::URLRequest* request, bool started) OVERRIDE {} + + virtual void OnURLRequestDestroyed(net::URLRequest* request) OVERRIDE {} + + virtual void OnPACScriptError(int line_number, + const base::string16& error) OVERRIDE {} + + virtual NetworkDelegate::AuthRequiredResponse OnAuthRequired( + net::URLRequest* request, + const net::AuthChallengeInfo& auth_info, + const AuthCallback& callback, + net::AuthCredentials* credentials) OVERRIDE { + return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; + } + + virtual bool OnCanGetCookies(const net::URLRequest& request, + const net::CookieList& cookie_list) OVERRIDE { + return false; + } + + virtual bool OnCanSetCookie(const net::URLRequest& request, + const std::string& cookie_line, + net::CookieOptions* options) OVERRIDE { + return false; + } + + virtual bool OnCanAccessFile(const net::URLRequest& request, + const base::FilePath& path) const OVERRIDE { + return false; + } + + virtual bool OnCanThrottleRequest( + const net::URLRequest& request) const OVERRIDE { + return false; + } + + virtual int OnBeforeSocketStreamConnect( + net::SocketStream* stream, + const net::CompletionCallback& callback) OVERRIDE { + return net::OK; + } + + DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate); +}; + +} // namespace + +namespace cronet { + +URLRequestContextAdapter::URLRequestContextAdapter( + URLRequestContextAdapterDelegate* delegate, + std::string user_agent) { + delegate_ = delegate; + user_agent_ = user_agent; +} + +void URLRequestContextAdapter::Initialize( + scoped_ptr config) { + network_thread_ = new base::Thread("network"); + base::Thread::Options options; + options.message_loop_type = base::MessageLoop::TYPE_IO; + network_thread_->StartWithOptions(options); + + GetNetworkTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&URLRequestContextAdapter::InitializeURLRequestContext, + this, + Passed(&config))); +} + +void URLRequestContextAdapter::InitializeURLRequestContext( + scoped_ptr config) { + // TODO(mmenke): Add method to have the builder enable SPDY. + net::URLRequestContextBuilder context_builder; + context_builder.set_network_delegate(new BasicNetworkDelegate()); + context_builder.set_proxy_config_service( + new net::ProxyConfigServiceFixed(net::ProxyConfig())); + config->ConfigureURLRequestContextBuilder(&context_builder); + + context_.reset(context_builder.Build()); + + if (VLOG_IS_ON(2)) { + net_log_observer_.reset(new NetLogObserver()); + context_->net_log()->AddThreadSafeObserver(net_log_observer_.get(), + net::NetLog::LOG_ALL_BUT_BYTES); + } + + delegate_->OnContextInitialized(this); +} + +URLRequestContextAdapter::~URLRequestContextAdapter() { + if (net_log_observer_) { + context_->net_log()->RemoveThreadSafeObserver(net_log_observer_.get()); + net_log_observer_.reset(); + } + StopNetLog(); + // TODO(mef): Ensure that |network_thread_| is destroyed properly. +} + +const std::string& URLRequestContextAdapter::GetUserAgent( + const GURL& url) const { + return user_agent_; +} + +net::URLRequestContext* URLRequestContextAdapter::GetURLRequestContext() { + if (!context_) { + LOG(ERROR) << "URLRequestContext is not set up"; + } + return context_.get(); +} + +scoped_refptr +URLRequestContextAdapter::GetNetworkTaskRunner() const { + return network_thread_->message_loop_proxy(); +} + +void URLRequestContextAdapter::StartNetLogToFile(const std::string& file_name) { + // Do nothing if already logging to a file. + if (net_log_logger_) + return; + + base::FilePath file_path(file_name); + FILE* file = base::OpenFile(file_path, "w"); + if (!file) + return; + + scoped_ptr constants(net::NetLogLogger::GetConstants()); + net_log_logger_.reset(new net::NetLogLogger(file, *constants)); + net_log_logger_->StartObserving(context_->net_log()); +} + +void URLRequestContextAdapter::StopNetLog() { + if (net_log_logger_) { + net_log_logger_->StopObserving(); + net_log_logger_.reset(); + } +} + +void NetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) { + VLOG(2) << "Net log entry: type=" << entry.type() + << ", source=" << entry.source().type << ", phase=" << entry.phase(); +} + +} // namespace cronet diff --git a/components/cronet/android/url_request_context_adapter.h b/components/cronet/android/url_request_context_adapter.h new file mode 100644 index 0000000000000..ef4fcd6deacd8 --- /dev/null +++ b/components/cronet/android/url_request_context_adapter.h @@ -0,0 +1,88 @@ +// 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_CRONET_ANDROID_URL_REQUEST_CONTEXT_ADAPTER_H_ +#define COMPONENTS_CRONET_ANDROID_URL_REQUEST_CONTEXT_ADAPTER_H_ + +#include + +#include "base/compiler_specific.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" +#include "net/base/net_log.h" +#include "net/base/network_change_notifier.h" +#include "net/url_request/url_request_context.h" +#include "net/url_request/url_request_context_getter.h" + +namespace net { +class NetLogLogger; +} // namespace net + +namespace cronet { + +struct URLRequestContextConfig; + +// Implementation of the Chromium NetLog observer interface. +class NetLogObserver : public net::NetLog::ThreadSafeObserver { + public: + explicit NetLogObserver() {} + + virtual ~NetLogObserver() {} + + virtual void OnAddEntry(const net::NetLog::Entry& entry) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(NetLogObserver); +}; + +// Fully configured |URLRequestContext|. +class URLRequestContextAdapter : public net::URLRequestContextGetter { + public: + class URLRequestContextAdapterDelegate + : public base::RefCountedThreadSafe { + public: + virtual void OnContextInitialized(URLRequestContextAdapter* context) = 0; + + protected: + friend class base::RefCountedThreadSafe; + + virtual ~URLRequestContextAdapterDelegate() {} + }; + + URLRequestContextAdapter(URLRequestContextAdapterDelegate* delegate, + std::string user_agent); + void Initialize(scoped_ptr config); + + const std::string& GetUserAgent(const GURL& url) const; + + // net::URLRequestContextGetter implementation: + virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; + virtual scoped_refptr GetNetworkTaskRunner() + const OVERRIDE; + + void StartNetLogToFile(const std::string& file_name); + void StopNetLog(); + + private: + scoped_refptr delegate_; + scoped_ptr context_; + std::string user_agent_; + base::Thread* network_thread_; + scoped_ptr network_change_notifier_; + scoped_ptr net_log_observer_; + scoped_ptr net_log_logger_; + + virtual ~URLRequestContextAdapter(); + + // Initializes |context_| on the IO thread. + void InitializeURLRequestContext(scoped_ptr config); + + DISALLOW_COPY_AND_ASSIGN(URLRequestContextAdapter); +}; + +} // namespace cronet + +#endif // COMPONENTS_CRONET_ANDROID_URL_REQUEST_CONTEXT_ADAPTER_H_ diff --git a/components/cronet/android/url_request_context_peer.cc b/components/cronet/android/url_request_context_peer.cc deleted file mode 100644 index 84b369e4b401a..0000000000000 --- a/components/cronet/android/url_request_context_peer.cc +++ /dev/null @@ -1,209 +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/cronet/android/url_request_context_peer.h" - -#include "base/bind.h" -#include "base/file_util.h" -#include "base/single_thread_task_runner.h" -#include "components/cronet/url_request_context_config.h" -#include "net/base/net_errors.h" -#include "net/base/net_log_logger.h" -#include "net/cert/cert_verifier.h" -#include "net/http/http_auth_handler_factory.h" -#include "net/http/http_network_layer.h" -#include "net/http/http_server_properties_impl.h" -#include "net/proxy/proxy_config_service_fixed.h" -#include "net/proxy/proxy_service.h" -#include "net/ssl/ssl_config_service_defaults.h" -#include "net/url_request/static_http_user_agent_settings.h" -#include "net/url_request/url_request_context_builder.h" -#include "net/url_request/url_request_context_storage.h" -#include "net/url_request/url_request_job_factory_impl.h" - -namespace { - -class BasicNetworkDelegate : public net::NetworkDelegate { - public: - BasicNetworkDelegate() {} - virtual ~BasicNetworkDelegate() {} - - private: - // net::NetworkDelegate implementation. - virtual int OnBeforeURLRequest(net::URLRequest* request, - const net::CompletionCallback& callback, - GURL* new_url) OVERRIDE { - return net::OK; - } - - virtual int OnBeforeSendHeaders(net::URLRequest* request, - const net::CompletionCallback& callback, - net::HttpRequestHeaders* headers) OVERRIDE { - return net::OK; - } - - virtual void OnSendHeaders(net::URLRequest* request, - const net::HttpRequestHeaders& headers) OVERRIDE {} - - virtual int OnHeadersReceived( - net::URLRequest* request, - const net::CompletionCallback& callback, - const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* _response_headers, - GURL* allowed_unsafe_redirect_url) OVERRIDE { - return net::OK; - } - - virtual void OnBeforeRedirect(net::URLRequest* request, - const GURL& new_location) OVERRIDE {} - - virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {} - - virtual void OnRawBytesRead(const net::URLRequest& request, - int bytes_read) OVERRIDE {} - - virtual void OnCompleted(net::URLRequest* request, bool started) OVERRIDE {} - - virtual void OnURLRequestDestroyed(net::URLRequest* request) OVERRIDE {} - - virtual void OnPACScriptError(int line_number, - const base::string16& error) OVERRIDE {} - - virtual NetworkDelegate::AuthRequiredResponse OnAuthRequired( - net::URLRequest* request, - const net::AuthChallengeInfo& auth_info, - const AuthCallback& callback, - net::AuthCredentials* credentials) OVERRIDE { - return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION; - } - - virtual bool OnCanGetCookies(const net::URLRequest& request, - const net::CookieList& cookie_list) OVERRIDE { - return false; - } - - virtual bool OnCanSetCookie(const net::URLRequest& request, - const std::string& cookie_line, - net::CookieOptions* options) OVERRIDE { - return false; - } - - virtual bool OnCanAccessFile(const net::URLRequest& request, - const base::FilePath& path) const OVERRIDE { - return false; - } - - virtual bool OnCanThrottleRequest(const net::URLRequest& request) - const OVERRIDE { - return false; - } - - virtual int OnBeforeSocketStreamConnect( - net::SocketStream* stream, - const net::CompletionCallback& callback) OVERRIDE { - return net::OK; - } - - DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate); -}; - -} // namespace - -namespace cronet { - -URLRequestContextPeer::URLRequestContextPeer( - URLRequestContextPeerDelegate* delegate, - std::string user_agent) { - delegate_ = delegate; - user_agent_ = user_agent; -} - -void URLRequestContextPeer::Initialize( - scoped_ptr config) { - network_thread_ = new base::Thread("network"); - base::Thread::Options options; - options.message_loop_type = base::MessageLoop::TYPE_IO; - network_thread_->StartWithOptions(options); - - GetNetworkTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&URLRequestContextPeer::InitializeURLRequestContext, - this, - Passed(&config))); -} - -void URLRequestContextPeer::InitializeURLRequestContext( - scoped_ptr config) { - // TODO(mmenke): Add method to have the builder enable SPDY. - net::URLRequestContextBuilder context_builder; - context_builder.set_network_delegate(new BasicNetworkDelegate()); - context_builder.set_proxy_config_service( - new net::ProxyConfigServiceFixed(net::ProxyConfig())); - config->ConfigureURLRequestContextBuilder(&context_builder); - - context_.reset(context_builder.Build()); - - if (VLOG_IS_ON(2)) { - net_log_observer_.reset(new NetLogObserver()); - context_->net_log()->AddThreadSafeObserver(net_log_observer_.get(), - net::NetLog::LOG_ALL_BUT_BYTES); - } - - delegate_->OnContextInitialized(this); -} - -URLRequestContextPeer::~URLRequestContextPeer() { - if (net_log_observer_) { - context_->net_log()->RemoveThreadSafeObserver(net_log_observer_.get()); - net_log_observer_.reset(); - } - StopNetLog(); - // TODO(mef): Ensure that |network_thread_| is destroyed properly. -} - -const std::string& URLRequestContextPeer::GetUserAgent(const GURL& url) const { - return user_agent_; -} - -net::URLRequestContext* URLRequestContextPeer::GetURLRequestContext() { - if (!context_) { - LOG(ERROR) << "URLRequestContext is not set up"; - } - return context_.get(); -} - -scoped_refptr -URLRequestContextPeer::GetNetworkTaskRunner() const { - return network_thread_->message_loop_proxy(); -} - -void URLRequestContextPeer::StartNetLogToFile(const std::string& file_name) { - // Do nothing if already logging to a file. - if (net_log_logger_) - return; - - base::FilePath file_path(file_name); - FILE* file = base::OpenFile(file_path, "w"); - if (!file) - return; - - scoped_ptr constants(net::NetLogLogger::GetConstants()); - net_log_logger_.reset(new net::NetLogLogger(file, *constants)); - net_log_logger_->StartObserving(context_->net_log()); -} - -void URLRequestContextPeer::StopNetLog() { - if (net_log_logger_) { - net_log_logger_->StopObserving(); - net_log_logger_.reset(); - } -} - -void NetLogObserver::OnAddEntry(const net::NetLog::Entry& entry) { - VLOG(2) << "Net log entry: type=" << entry.type() - << ", source=" << entry.source().type - << ", phase=" << entry.phase(); -} - -} // namespace cronet diff --git a/components/cronet/android/url_request_context_peer.h b/components/cronet/android/url_request_context_peer.h deleted file mode 100644 index 572e65089a1c8..0000000000000 --- a/components/cronet/android/url_request_context_peer.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef COMPONENTS_CRONET_ANDROID_URL_REQUEST_CONTEXT_PEER_H_ -#define COMPONENTS_CRONET_ANDROID_URL_REQUEST_CONTEXT_PEER_H_ - -#include - -#include "base/compiler_specific.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/threading/thread.h" -#include "net/base/net_log.h" -#include "net/base/network_change_notifier.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" - -namespace net { -class NetLogLogger; -} // namespace net - -namespace cronet { - -struct URLRequestContextConfig; - -// Implementation of the Chromium NetLog observer interface. -class NetLogObserver : public net::NetLog::ThreadSafeObserver { - public: - explicit NetLogObserver() {} - - virtual ~NetLogObserver() {} - - virtual void OnAddEntry(const net::NetLog::Entry& entry) OVERRIDE; - - private: - DISALLOW_COPY_AND_ASSIGN(NetLogObserver); -}; - -// Fully configured |URLRequestContext|. -class URLRequestContextPeer : public net::URLRequestContextGetter { - public: - class URLRequestContextPeerDelegate - : public base::RefCountedThreadSafe { - public: - virtual void OnContextInitialized(URLRequestContextPeer* context) = 0; - - protected: - friend class base::RefCountedThreadSafe; - - virtual ~URLRequestContextPeerDelegate() {} - }; - - URLRequestContextPeer(URLRequestContextPeerDelegate* delegate, - std::string user_agent); - void Initialize(scoped_ptr config); - - const std::string& GetUserAgent(const GURL& url) const; - - // net::URLRequestContextGetter implementation: - virtual net::URLRequestContext* GetURLRequestContext() OVERRIDE; - virtual scoped_refptr GetNetworkTaskRunner() - const OVERRIDE; - - void StartNetLogToFile(const std::string& file_name); - void StopNetLog(); - - private: - scoped_refptr delegate_; - scoped_ptr context_; - std::string user_agent_; - base::Thread* network_thread_; - scoped_ptr network_change_notifier_; - scoped_ptr net_log_observer_; - scoped_ptr net_log_logger_; - - virtual ~URLRequestContextPeer(); - - // Initializes |context_| on the IO thread. - void InitializeURLRequestContext(scoped_ptr config); - - DISALLOW_COPY_AND_ASSIGN(URLRequestContextPeer); -}; - -} // namespace cronet - -#endif // COMPONENTS_CRONET_ANDROID_URL_REQUEST_CONTEXT_PEER_H_ diff --git a/components/cronet/android/url_request_peer.cc b/components/cronet/android/url_request_peer.cc deleted file mode 100644 index 5f413e60281ef..0000000000000 --- a/components/cronet/android/url_request_peer.cc +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "url_request_peer.h" - -#include "base/strings/string_number_conversions.h" -#include "components/cronet/android/url_request_context_peer.h" -#include "components/cronet/android/wrapped_channel_upload_element_reader.h" -#include "net/base/load_flags.h" -#include "net/base/upload_bytes_element_reader.h" -#include "net/http/http_status_code.h" - -namespace cronet { - -static const size_t kBufferSizeIncrement = 8192; - -URLRequestPeer::URLRequestPeer(URLRequestContextPeer* context, - URLRequestPeerDelegate* delegate, - GURL url, - net::RequestPriority priority) - : method_("GET"), - url_request_(NULL), - read_buffer_(new net::GrowableIOBuffer()), - bytes_read_(0), - total_bytes_read_(0), - error_code_(0), - http_status_code_(0), - canceled_(false), - expected_size_(0) { - context_ = context; - delegate_ = delegate; - url_ = url; - priority_ = priority; -} - -URLRequestPeer::~URLRequestPeer() { CHECK(url_request_ == NULL); } - -void URLRequestPeer::SetMethod(const std::string& method) { method_ = method; } - -void URLRequestPeer::AddHeader(const std::string& name, - const std::string& value) { - headers_.SetHeader(name, value); -} - -void URLRequestPeer::SetUploadContent(const char* bytes, int bytes_len) { - std::vector data(bytes, bytes + bytes_len); - scoped_ptr reader( - new net::UploadOwnedBytesElementReader(&data)); - upload_data_stream_.reset(net::UploadDataStream::CreateWithReader( - reader.Pass(), 0)); -} - -void URLRequestPeer::SetUploadChannel(JNIEnv* env, int64 content_length) { - scoped_ptr reader( - new WrappedChannelElementReader(delegate_, content_length)); - upload_data_stream_.reset(net::UploadDataStream::CreateWithReader( - reader.Pass(), 0)); -} - -std::string URLRequestPeer::GetHeader(const std::string &name) const { - std::string value; - if (url_request_ != NULL) { - url_request_->GetResponseHeaderByName(name, &value); - } - return value; -} - -net::HttpResponseHeaders* URLRequestPeer::GetResponseHeaders() const { - if (url_request_ == NULL) { - return NULL; - } - return url_request_->response_headers(); -} - -void URLRequestPeer::Start() { - context_->GetNetworkTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&URLRequestPeer::OnInitiateConnection, - base::Unretained(this))); -} - -void URLRequestPeer::OnInitiateConnection() { - if (canceled_) { - return; - } - - VLOG(1) << "Starting chromium request: " - << url_.possibly_invalid_spec().c_str() - << " priority: " << RequestPriorityToString(priority_); - url_request_ = new net::URLRequest( - url_, net::DEFAULT_PRIORITY, this, context_->GetURLRequestContext()); - url_request_->SetLoadFlags(net::LOAD_DISABLE_CACHE | - net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DO_NOT_SEND_COOKIES); - url_request_->set_method(method_); - url_request_->SetExtraRequestHeaders(headers_); - if (!headers_.HasHeader(net::HttpRequestHeaders::kUserAgent)) { - std::string user_agent; - user_agent = context_->GetUserAgent(url_); - url_request_->SetExtraRequestHeaderByName( - net::HttpRequestHeaders::kUserAgent, user_agent, true /* override */); - } - - if (upload_data_stream_) - url_request_->set_upload(upload_data_stream_.Pass()); - - url_request_->SetPriority(priority_); - - url_request_->Start(); -} - -void URLRequestPeer::Cancel() { - if (canceled_) { - return; - } - - canceled_ = true; - - context_->GetNetworkTaskRunner()->PostTask( - FROM_HERE, - base::Bind(&URLRequestPeer::OnCancelRequest, base::Unretained(this))); -} - -void URLRequestPeer::OnCancelRequest() { - VLOG(1) << "Canceling chromium request: " << url_.possibly_invalid_spec(); - - if (url_request_ != NULL) { - url_request_->Cancel(); - } - - OnRequestCanceled(); -} - -void URLRequestPeer::Destroy() { - context_->GetNetworkTaskRunner()->PostTask( - FROM_HERE, base::Bind(&URLRequestPeer::OnDestroyRequest, this)); -} - -// static -void URLRequestPeer::OnDestroyRequest(URLRequestPeer* self) { - VLOG(1) << "Destroying chromium request: " - << self->url_.possibly_invalid_spec(); - delete self; -} - -void URLRequestPeer::OnResponseStarted(net::URLRequest* request) { - if (request->status().status() != net::URLRequestStatus::SUCCESS) { - OnRequestFailed(); - return; - } - - http_status_code_ = request->GetResponseCode(); - VLOG(1) << "Response started with status: " << http_status_code_; - - request->GetResponseHeaderByName("Content-Type", &content_type_); - expected_size_ = request->GetExpectedContentSize(); - delegate_->OnResponseStarted(this); - - Read(); -} - -// Reads all available data or starts an asynchronous read. -void URLRequestPeer::Read() { - while (true) { - if (read_buffer_->RemainingCapacity() == 0) { - int new_capacity = read_buffer_->capacity() + kBufferSizeIncrement; - read_buffer_->SetCapacity(new_capacity); - } - - int bytes_read; - if (url_request_->Read( - read_buffer_, read_buffer_->RemainingCapacity(), &bytes_read)) { - if (bytes_read == 0) { - OnRequestSucceeded(); - break; - } - - VLOG(1) << "Synchronously read: " << bytes_read << " bytes"; - OnBytesRead(bytes_read); - } else if (url_request_->status().status() == - net::URLRequestStatus::IO_PENDING) { - if (bytes_read_ != 0) { - VLOG(1) << "Flushing buffer: " << bytes_read_ << " bytes"; - - delegate_->OnBytesRead(this); - read_buffer_->set_offset(0); - bytes_read_ = 0; - } - VLOG(1) << "Started async read"; - break; - } else { - OnRequestFailed(); - break; - } - } -} - -void URLRequestPeer::OnReadCompleted(net::URLRequest* request, int bytes_read) { - VLOG(1) << "Asynchronously read: " << bytes_read << " bytes"; - if (bytes_read < 0) { - OnRequestFailed(); - return; - } else if (bytes_read == 0) { - OnRequestSucceeded(); - return; - } - - OnBytesRead(bytes_read); - Read(); -} - -void URLRequestPeer::OnBytesRead(int bytes_read) { - read_buffer_->set_offset(read_buffer_->offset() + bytes_read); - bytes_read_ += bytes_read; - total_bytes_read_ += bytes_read; -} - -void URLRequestPeer::OnRequestSucceeded() { - if (canceled_) { - return; - } - - VLOG(1) << "Request completed with HTTP status: " << http_status_code_ - << ". Total bytes read: " << total_bytes_read_; - - OnRequestCompleted(); -} - -void URLRequestPeer::OnRequestFailed() { - if (canceled_) { - return; - } - - error_code_ = url_request_->status().error(); - VLOG(1) << "Request failed with status: " << url_request_->status().status() - << " and error: " << net::ErrorToString(error_code_); - OnRequestCompleted(); -} - -void URLRequestPeer::OnRequestCanceled() { OnRequestCompleted(); } - -void URLRequestPeer::OnRequestCompleted() { - VLOG(1) << "Completed: " << url_.possibly_invalid_spec(); - if (url_request_ != NULL) { - delete url_request_; - url_request_ = NULL; - } - - delegate_->OnBytesRead(this); - delegate_->OnRequestFinished(this); -} - -unsigned char* URLRequestPeer::Data() const { - return reinterpret_cast(read_buffer_->StartOfBuffer()); -} - -} // namespace cronet diff --git a/components/cronet/android/url_request_peer.h b/components/cronet/android/url_request_peer.h deleted file mode 100644 index a6e4f9e177b65..0000000000000 --- a/components/cronet/android/url_request_peer.h +++ /dev/null @@ -1,145 +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_CRONET_ANDROID_URL_REQUEST_PEER_H_ -#define COMPONENTS_CRONET_ANDROID_URL_REQUEST_PEER_H_ - -#include - -#include - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "net/base/request_priority.h" -#include "net/http/http_request_headers.h" -#include "net/url_request/url_request.h" - -namespace net { -class GrowableIOBuffer; -class HttpResponseHeaders; -class UploadDataStream; -} // namespace net - -namespace cronet { - -class URLRequestContextPeer; - -// An adapter from the JNI |UrlRequest| object and the Chromium |URLRequest| -// object. -class URLRequestPeer : public net::URLRequest::Delegate { - public: - // The delegate which is called when the request finishes. - class URLRequestPeerDelegate - : public base::RefCountedThreadSafe { - public: - virtual void OnResponseStarted(URLRequestPeer* request) = 0; - virtual void OnBytesRead(URLRequestPeer* request) = 0; - virtual void OnRequestFinished(URLRequestPeer* request) = 0; - virtual int ReadFromUploadChannel(net::IOBuffer* buf, int buf_length) = 0; - - protected: - friend class base::RefCountedThreadSafe; - virtual ~URLRequestPeerDelegate() {} - }; - - URLRequestPeer(URLRequestContextPeer* context, - URLRequestPeerDelegate* delegate, - GURL url, - net::RequestPriority priority); - virtual ~URLRequestPeer(); - - // Sets the request method GET, POST etc - void SetMethod(const std::string& method); - - // Adds a header to the request - void AddHeader(const std::string& name, const std::string& value); - - // Sets the contents of the POST or PUT request - void SetUploadContent(const char* bytes, int bytes_len); - - // Sets the request to streaming upload. - void SetUploadChannel(JNIEnv* env, int64 content_length); - - // Starts the request. - void Start(); - - // Cancels the request. - void Cancel(); - - // Releases all resources for the request and deletes the object itself. - void Destroy(); - - // Returns the URL of the request. - GURL url() const { return url_; } - - // Returns the error code after the request is complete. - // Negative codes indicate system errors. - int error_code() const { return error_code_; } - - // Returns the HTTP status code. - int http_status_code() const { - return http_status_code_; - }; - - // Returns the value of the content-length response header. - int64 content_length() const { return expected_size_; } - - // Returns the value of the content-type response header. - std::string content_type() const { return content_type_; } - - // Returns the value of the specified response header. - std::string GetHeader(const std::string& name) const; - - // Get all response headers, as a HttpResponseHeaders object. - net::HttpResponseHeaders* GetResponseHeaders() const; - - // Returns the overall number of bytes read. - size_t bytes_read() const { return bytes_read_; } - - // Returns a pointer to the downloaded data. - unsigned char* Data() const; - - virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; - - virtual void OnReadCompleted(net::URLRequest* request, - int bytes_read) OVERRIDE; - - private: - static void OnDestroyRequest(URLRequestPeer* self); - - void OnInitiateConnection(); - void OnCancelRequest(); - void OnRequestSucceeded(); - void OnRequestFailed(); - void OnRequestCompleted(); - void OnRequestCanceled(); - void OnBytesRead(int bytes_read); - void OnAppendChunk(const char* bytes, int bytes_len, bool is_last_chunk); - - void Read(); - - URLRequestContextPeer* context_; - scoped_refptr delegate_; - GURL url_; - net::RequestPriority priority_; - std::string method_; - net::HttpRequestHeaders headers_; - net::URLRequest* url_request_; - scoped_ptr upload_data_stream_; - scoped_refptr read_buffer_; - int bytes_read_; - int total_bytes_read_; - int error_code_; - int http_status_code_; - std::string content_type_; - bool canceled_; - int64 expected_size_; - - DISALLOW_COPY_AND_ASSIGN(URLRequestPeer); -}; - -} // namespace cronet - -#endif // COMPONENTS_CRONET_ANDROID_URL_REQUEST_PEER_H_ diff --git a/components/cronet/android/wrapped_channel_upload_element_reader.cc b/components/cronet/android/wrapped_channel_upload_element_reader.cc index db6fc78a77a54..3027cbadab146 100644 --- a/components/cronet/android/wrapped_channel_upload_element_reader.cc +++ b/components/cronet/android/wrapped_channel_upload_element_reader.cc @@ -12,7 +12,7 @@ namespace cronet { WrappedChannelElementReader::WrappedChannelElementReader( - scoped_refptr delegate, + scoped_refptr delegate, uint64 length) : length_(length), offset_(0), delegate_(delegate) { } diff --git a/components/cronet/android/wrapped_channel_upload_element_reader.h b/components/cronet/android/wrapped_channel_upload_element_reader.h index c438f6fb0912d..251a3ec370e15 100644 --- a/components/cronet/android/wrapped_channel_upload_element_reader.h +++ b/components/cronet/android/wrapped_channel_upload_element_reader.h @@ -7,7 +7,7 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "components/cronet/android/url_request_peer.h" +#include "components/cronet/android/url_request_adapter.h" #include "net/base/completion_callback.h" #include "net/base/upload_element_reader.h" @@ -24,7 +24,7 @@ namespace cronet { class WrappedChannelElementReader : public net::UploadElementReader { public: WrappedChannelElementReader( - scoped_refptr delegate, + scoped_refptr delegate, uint64 length); virtual ~WrappedChannelElementReader(); @@ -40,7 +40,7 @@ class WrappedChannelElementReader : public net::UploadElementReader { private: const uint64 length_; uint64 offset_; - scoped_refptr delegate_; + scoped_refptr delegate_; DISALLOW_COPY_AND_ASSIGN(WrappedChannelElementReader); }; diff --git a/components/crx_file.gypi b/components/crx_file.gypi new file mode 100644 index 0000000000000..07a0487c0c77b --- /dev/null +++ b/components/crx_file.gypi @@ -0,0 +1,26 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'crx_file', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + ], + 'include_dirs': [ + '..', + ], + 'defines': [ + 'CRX_FILE_IMPLEMENTATION', + ], + 'sources': [ + 'crx_file/constants.h', + 'crx_file/crx_file.cc', + 'crx_file/crx_file.h', + ], + }, + ], +} diff --git a/components/crx_file/constants.h b/components/crx_file/constants.h new file mode 100644 index 0000000000000..cae0bc3be4703 --- /dev/null +++ b/components/crx_file/constants.h @@ -0,0 +1,21 @@ +// 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_CRX_FILE_CONSTANTS_H_ +#define COMPONENTS_CRX_FILE_CONSTANTS_H_ + +#include "base/basictypes.h" + +namespace crx_file { + +// Note: this structure is an ASN.1 which encodes the algorithm used +// with its parameters. This is defined in PKCS #1 v2.1 (RFC 3447). +// It is encoding: { OID sha1WithRSAEncryption PARAMETERS NULL } +const uint8 kSignatureAlgorithm[15] = {0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00}; + +} // namespace crx_file + +#endif // COMPONENTS_CRX_FILE_CONSTANTS_H_ diff --git a/extensions/common/crx_file.cc b/components/crx_file/crx_file.cc similarity index 94% rename from extensions/common/crx_file.cc rename to components/crx_file/crx_file.cc index 73e7f7b73d6b7..391d5492daa21 100644 --- a/extensions/common/crx_file.cc +++ b/components/crx_file/crx_file.cc @@ -1,10 +1,10 @@ -// Copyright (c) 2012 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. -#include "extensions/common/crx_file.h" +#include "components/crx_file/crx_file.h" -namespace extensions { +namespace crx_file { namespace { @@ -78,4 +78,4 @@ bool CrxFile::HeaderIsValid(const CrxFile::Header& header, return valid; } -} // namespace extensions +} // namespace crx_file diff --git a/extensions/common/crx_file.h b/components/crx_file/crx_file.h similarity index 92% rename from extensions/common/crx_file.h rename to components/crx_file/crx_file.h index d7c33ab2c2fca..55e4c39e26c6c 100644 --- a/extensions/common/crx_file.h +++ b/components/crx_file/crx_file.h @@ -1,15 +1,15 @@ -// Copyright (c) 2012 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. -#ifndef EXTENSIONS_COMMON_CRX_FILE_H_ -#define EXTENSIONS_COMMON_CRX_FILE_H_ +#ifndef COMPONENTS_CRX_FILE_CRX_FILE_H_ +#define COMPONENTS_CRX_FILE_CRX_FILE_H_ #include #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -namespace extensions { +namespace crx_file { // CRX files have a header that includes a magic key, version number, and // some signature sizing information. Use CrxFile object to validate whether @@ -74,6 +74,6 @@ class CrxFile { static bool HeaderIsValid(const Header& header, Error* error); }; -} // namespace extensions +} // namespace crx_file -#endif // EXTENSIONS_COMMON_CRX_FILE_H_ +#endif // COMPONENTS_CRX_FILE_CRX_FILE_H_ diff --git a/components/data_reduction_proxy.gypi b/components/data_reduction_proxy.gypi index 77f9f69de8925..9be6ed7fd4541 100644 --- a/components/data_reduction_proxy.gypi +++ b/components/data_reduction_proxy.gypi @@ -36,6 +36,8 @@ 'data_reduction_proxy/browser/data_reduction_proxy_protocol.h', 'data_reduction_proxy/browser/data_reduction_proxy_settings.cc', 'data_reduction_proxy/browser/data_reduction_proxy_settings.h', + 'data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.cc', + 'data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h', 'data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc', 'data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h', ], diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.cc index 36a796f93da31..3f1565684a729 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.cc @@ -4,7 +4,9 @@ #include "components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h" +#include "base/bind.h" #include "base/command_line.h" +#include "base/single_thread_task_runner.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -41,15 +43,17 @@ bool DataReductionProxyAuthRequestHandler::IsKeySetOnCommandLine() { DataReductionProxyAuthRequestHandler::DataReductionProxyAuthRequestHandler( const std::string& client, const std::string& version, - DataReductionProxyParams* params) - : data_reduction_proxy_params_(params) { + DataReductionProxyParams* params, + scoped_refptr network_task_runner) + : data_reduction_proxy_params_(params), + network_task_runner_(network_task_runner) { client_ = client; version_ = version; Init(); } void DataReductionProxyAuthRequestHandler::Init() { - InitAuthentication(GetDefaultKey()); + InitAuthenticationOnUI(GetDefaultKey()); } @@ -83,6 +87,7 @@ void DataReductionProxyAuthRequestHandler::MaybeAddRequestHeader( net::URLRequest* request, const net::ProxyServer& proxy_server, net::HttpRequestHeaders* request_headers) { + DCHECK(network_task_runner_->BelongsToCurrentThread()); if (!proxy_server.is_valid()) return; if (data_reduction_proxy_params_ && @@ -94,6 +99,11 @@ void DataReductionProxyAuthRequestHandler::MaybeAddRequestHeader( void DataReductionProxyAuthRequestHandler::AddAuthorizationHeader( net::HttpRequestHeaders* headers) { + base::Time now = Now(); + if (now - last_update_time_ > base::TimeDelta::FromHours(24)) { + last_update_time_ = now; + ComputeCredentials(last_update_time_, &session_, &credentials_); + } const char kChromeProxyHeader[] = "Chrome-Proxy"; std::string header_value; if (headers->HasHeader(kChromeProxyHeader)) { @@ -108,28 +118,47 @@ void DataReductionProxyAuthRequestHandler::AddAuthorizationHeader( headers->SetHeader(kChromeProxyHeader, header_value); } -void DataReductionProxyAuthRequestHandler::InitAuthentication( +void DataReductionProxyAuthRequestHandler::InitAuthenticationOnUI( const std::string& key) { - key_ = key; + network_task_runner_->PostTask(FROM_HERE, base::Bind( + &DataReductionProxyAuthRequestHandler::InitAuthentication, + base::Unretained(this), + key)); +} + +void DataReductionProxyAuthRequestHandler::ComputeCredentials( + const base::Time& now, + std::string* session, + std::string* credentials) { + DCHECK(session); + DCHECK(credentials); int64 timestamp = - (Now() - base::Time::UnixEpoch()).InMilliseconds() / 1000; + (now - base::Time::UnixEpoch()).InMilliseconds() / 1000; int32 rand[3]; RandBytes(rand, 3 * sizeof(rand[0])); - session_ = base::StringPrintf("%lld-%u-%u-%u", - static_cast(timestamp), - rand[0], - rand[1], - rand[2]); - credentials_ = base::UTF16ToUTF8(AuthHashForSalt(timestamp, key_)); - - DVLOG(1) << "session: [" << session_ << "] " - << "password: [" << credentials_ << "]"; + *session = base::StringPrintf("%lld-%u-%u-%u", + static_cast(timestamp), + rand[0], + rand[1], + rand[2]); + *credentials = base::UTF16ToUTF8(AuthHashForSalt(timestamp, key_)); + + DVLOG(1) << "session: [" << *session << "] " + << "password: [" << *credentials << "]"; +} + +void DataReductionProxyAuthRequestHandler::InitAuthentication( + const std::string& key) { + DCHECK(network_task_runner_->BelongsToCurrentThread()); + key_ = key; + last_update_time_ = Now(); + ComputeCredentials(last_update_time_, &session_, &credentials_); } -void DataReductionProxyAuthRequestHandler::SetKey(const std::string& key) { +void DataReductionProxyAuthRequestHandler::SetKeyOnUI(const std::string& key) { if (!key.empty()) - InitAuthentication(key); + InitAuthenticationOnUI(key); } diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h b/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h index 17ffb37856fb9..992b8b66e5e5d 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler.h @@ -11,6 +11,10 @@ #include "base/time/time.h" #include "url/gurl.h" +namespace base { +class SingleThreadTaskRunner; +} + namespace net { class HttpRequestHeaders; class HttpResponseHeaders; @@ -34,33 +38,29 @@ class DataReductionProxyAuthRequestHandler { public: static bool IsKeySetOnCommandLine(); - // Constructs an authentication request handler. Client is the canonical name - // for the client. Client names should be defined in this file as one of - // |kClient...|. Version is the authentication protocol version that the - // client uses, which should be |kProtocolVersion| unless the client expects - // to be handled differently from the standard behavior. DataReductionProxyAuthRequestHandler( const std::string& client, const std::string& version, - DataReductionProxyParams* params); + DataReductionProxyParams* params, + scoped_refptr network_task_runner); virtual ~DataReductionProxyAuthRequestHandler(); // Adds a 'Chrome-Proxy' header to |request_headers| with the data reduction // proxy authentication credentials. Only adds this header if the provided - // |proxy_server| is a data reduction proxy. + // |proxy_server| is a data reduction proxy. Must be called on the IO thread. void MaybeAddRequestHeader(net::URLRequest* request, const net::ProxyServer& proxy_server, net::HttpRequestHeaders* request_headers); // Sets a new authentication key. This must be called for platforms that do // not have a default key defined. See the constructor implementation for - // those platforms. - void SetKey(const std::string& key); + // those platforms. Must be called on the UI thread. + void SetKeyOnUI(const std::string& key); protected: void Init(); - void InitAuthentication(const std::string& key); + void InitAuthenticationOnUI(const std::string& key); void AddAuthorizationHeader(net::HttpRequestHeaders* headers); @@ -82,17 +82,36 @@ class DataReductionProxyAuthRequestHandler { FRIEND_TEST_ALL_PREFIXES(DataReductionProxyAuthRequestHandlerTest, AuthHashForSalt); + // Stores the supplied key and sets up credentials suitable for authenticating + // with the data reduction proxy. + void InitAuthentication(const std::string& key); + + // Generates a session ID and credentials suitable for authenticating with + // the data reduction proxy. + void ComputeCredentials(const base::Time& now, + std::string* session, + std::string* credentials); + // Authentication state. std::string key_; + + // Lives on the IO thread. std::string session_; std::string credentials_; // Name of the client and version of the data reduction proxy protocol to use. + // Both live on the IO thread. std::string client_; std::string version_; + // The last time the session was updated. Used to ensure that a session is + // never used for more than twenty-four hours. + base::Time last_update_time_; + DataReductionProxyParams* data_reduction_proxy_params_; + scoped_refptr network_task_runner_; + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyAuthRequestHandler); }; diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler_unittest.cc index b5bc70941c7f4..17430d87cae36 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler_unittest.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_auth_request_handler_unittest.cc @@ -7,6 +7,7 @@ #include "base/md5.h" #include "base/memory/scoped_ptr.h" +#include "base/run_loop.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/time/time.h" @@ -42,14 +43,23 @@ const char kExpectedSession2[] = "0-1633771873-1633771873-1633771873"; const char kExpectedHeader2[] = "ps=0-1633771873-1633771873-1633771873, " "sid=c911fdb402f578787562cf7f00eda972, v=0, c=android"; +const char kExpectedHeader3[] = + "ps=86401-1633771873-1633771873-1633771873, " + "sid=d7c1c34ef6b90303b01c48a6c1db6419, v=0, c=android"; #elif defined(OS_IOS) const char kExpectedHeader2[] = "ps=0-1633771873-1633771873-1633771873, " "sid=c911fdb402f578787562cf7f00eda972, v=0, c=ios"; +const char kExpectedHeader3[] = + "ps=86401-1633771873-1633771873-1633771873, " + "sid=d7c1c34ef6b90303b01c48a6c1db6419, v=0, c=ios"; #else const char kExpectedHeader2[] = "ps=0-1633771873-1633771873-1633771873, " "sid=c911fdb402f578787562cf7f00eda972, v=0"; +const char kExpectedHeader3[] = + "ps=86401-1633771873-1633771873-1633771873, " + "sid=d7c1c34ef6b90303b01c48a6c1db6419, v=0"; #endif const char kDataReductionProxyKey[] = "12345"; @@ -64,15 +74,17 @@ class TestDataReductionProxyAuthRequestHandler TestDataReductionProxyAuthRequestHandler( const std::string& client, const std::string& version, - DataReductionProxyParams* params) - : DataReductionProxyAuthRequestHandler(client,version, params) {} + DataReductionProxyParams* params, + base::MessageLoopProxy* loop_proxy) + : DataReductionProxyAuthRequestHandler( + client, version, params, loop_proxy) {} virtual std::string GetDefaultKey() const OVERRIDE { return kTestKey; } virtual base::Time Now() const OVERRIDE { - return base::Time::UnixEpoch(); + return base::Time::UnixEpoch() + now_offset_; } virtual void RandBytes(void* output, size_t length) OVERRIDE { @@ -81,11 +93,26 @@ class TestDataReductionProxyAuthRequestHandler c[i] = 'a'; } } + + // Time after the unix epoch that Now() reports. + void set_offset(const base::TimeDelta& now_offset) { + now_offset_ = now_offset; + } + + private: + base::TimeDelta now_offset_; }; } // namespace class DataReductionProxyAuthRequestHandlerTest : public testing::Test { + public: + DataReductionProxyAuthRequestHandlerTest() + : loop_proxy_(base::MessageLoopProxy::current().get()) { + } + // Required for MessageLoopProxy::current(). + base::MessageLoopForUI loop_; + base::MessageLoopProxy* loop_proxy_; }; TEST_F(DataReductionProxyAuthRequestHandlerTest, Authorization) { @@ -99,8 +126,10 @@ TEST_F(DataReductionProxyAuthRequestHandlerTest, Authorization) { ~TestDataReductionProxyParams::HAS_DEV_ORIGIN)); TestDataReductionProxyAuthRequestHandler auth_handler(kClient, kVersion, - params.get()); + params.get(), + loop_proxy_); auth_handler.Init(); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(auth_handler.client_, kClient); EXPECT_EQ(kVersion, auth_handler.version_); EXPECT_EQ(auth_handler.key_, kTestKey); @@ -108,7 +137,8 @@ TEST_F(DataReductionProxyAuthRequestHandlerTest, Authorization) { EXPECT_EQ(kExpectedSession, auth_handler.session_); // Now set a key. - auth_handler.SetKey(kTestKey2); + auth_handler.SetKeyOnUI(kTestKey2); + base::RunLoop().RunUntilIdle(); EXPECT_EQ(kTestKey2, auth_handler.key_); EXPECT_EQ(kExpectedCredentials2, auth_handler.credentials_); EXPECT_EQ(kExpectedSession2, auth_handler.session_); @@ -121,22 +151,52 @@ TEST_F(DataReductionProxyAuthRequestHandlerTest, Authorization) { // Don't write headers with a valid proxy, that's not a data reduction proxy. auth_handler.MaybeAddRequestHeader( - NULL, - net::ProxyServer::FromURI(kOtherProxy, net::ProxyServer::SCHEME_HTTP), - &headers); + NULL, + net::ProxyServer::FromURI(kOtherProxy, net::ProxyServer::SCHEME_HTTP), + &headers); EXPECT_FALSE(headers.HasHeader(kChromeProxyHeader)); // Write headers with a valid data reduction proxy; auth_handler.MaybeAddRequestHeader( - NULL, - net::ProxyServer::FromURI( - net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(), - net::ProxyServer::SCHEME_HTTP), - &headers); + NULL, + net::ProxyServer::FromURI( + net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(), + net::ProxyServer::SCHEME_HTTP), + &headers); EXPECT_TRUE(headers.HasHeader(kChromeProxyHeader)); std::string header_value; headers.GetHeader(kChromeProxyHeader, &header_value); EXPECT_EQ(kExpectedHeader2, header_value); + + // Fast forward 24 hours. The header should be the same. + auth_handler.set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60)); + net::HttpRequestHeaders headers2; + // Write headers with a valid data reduction proxy; + auth_handler.MaybeAddRequestHeader( + NULL, + net::ProxyServer::FromURI( + net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(), + net::ProxyServer::SCHEME_HTTP), + &headers2); + EXPECT_TRUE(headers2.HasHeader(kChromeProxyHeader)); + std::string header_value2; + headers2.GetHeader(kChromeProxyHeader, &header_value2); + EXPECT_EQ(kExpectedHeader2, header_value2); + + // Fast forward one more second. The header should be new. + auth_handler.set_offset(base::TimeDelta::FromSeconds(24 * 60 * 60 + 1)); + net::HttpRequestHeaders headers3; + // Write headers with a valid data reduction proxy; + auth_handler.MaybeAddRequestHeader( + NULL, + net::ProxyServer::FromURI( + net::HostPortPair::FromURL(GURL(params->DefaultOrigin())).ToString(), + net::ProxyServer::SCHEME_HTTP), + &headers3); + EXPECT_TRUE(headers3.HasHeader(kChromeProxyHeader)); + std::string header_value3; + headers3.GetHeader(kChromeProxyHeader, &header_value3); + EXPECT_EQ(kExpectedHeader3, header_value3); } TEST_F(DataReductionProxyAuthRequestHandlerTest, AuthHashForSalt) { diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.cc index 921a2e4b47e5b..d160578dc7a1a 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.cc @@ -116,11 +116,10 @@ void DataReductionProxyConfigService::RegisterObserver() { } DataReductionProxyConfigTracker::DataReductionProxyConfigTracker( - DataReductionProxyConfigService* config_service, + base::Callback update_proxy_config, base::TaskRunner* task_runner) - : config_service_(config_service), + : update_proxy_config_(update_proxy_config), task_runner_(task_runner) { - DCHECK(config_service); } DataReductionProxyConfigTracker::~DataReductionProxyConfigTracker() { @@ -194,11 +193,8 @@ void DataReductionProxyConfigTracker::AddURLPatternToBypass( void DataReductionProxyConfigTracker::UpdateProxyConfigOnIOThread( bool enabled, const net::ProxyConfig& config) { - task_runner_->PostTask(FROM_HERE, - base::Bind( - &DataReductionProxyConfigService::UpdateProxyConfig, - base::Unretained(config_service_), - enabled, config)); + task_runner_->PostTask( + FROM_HERE, base::Bind(update_proxy_config_, enabled, config)); } } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.h b/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.h index 75addad6e273c..7c92938681d21 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_config_service.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/callback.h" #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -91,7 +92,7 @@ class DataReductionProxyConfigService class DataReductionProxyConfigTracker : public DataReductionProxyConfigurator { public: DataReductionProxyConfigTracker( - DataReductionProxyConfigService* config_service, + base::Callback update_proxy_config, base::TaskRunner* task_runner); virtual ~DataReductionProxyConfigTracker(); @@ -115,7 +116,7 @@ class DataReductionProxyConfigTracker : public DataReductionProxyConfigurator { void UpdateProxyConfigOnIOThread(bool enabled, const net::ProxyConfig& config); - DataReductionProxyConfigService* config_service_; + base::Callback update_proxy_config_; std::vector bypass_rules_; scoped_refptr task_runner_; diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_config_service_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_config_service_unittest.cc index d5624f0960871..ad46684a843ae 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_config_service_unittest.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_config_service_unittest.cc @@ -6,6 +6,7 @@ #include +#include "base/bind.h" #include "base/memory/ref_counted.h" #include "base/message_loop/message_loop.h" #include "base/test/test_simple_task_runner.h" @@ -197,8 +198,11 @@ TEST_F(DataReductionProxyConfigServiceTest, TrackerEnable) { config_service_->AddObserver(&observer); scoped_refptr task_runner_( new base::TestSimpleTaskRunner()); - DataReductionProxyConfigTracker tracker(config_service_.get(), - task_runner_.get()); + DataReductionProxyConfigTracker tracker( + base::Bind(&data_reduction_proxy::DataReductionProxyConfigService:: + UpdateProxyConfig, + base::Unretained(config_service_.get())), + task_runner_.get()); net::ProxyConfig expected_config; expected_config.proxy_rules().ParseFromString(kDataReductionProxyRules); EXPECT_CALL(observer, OnProxyConfigChanged( @@ -221,8 +225,11 @@ TEST_F(DataReductionProxyConfigServiceTest, TrackerEnableRestricted) { config_service_->AddObserver(&observer); scoped_refptr task_runner_( new base::TestSimpleTaskRunner()); - DataReductionProxyConfigTracker tracker(config_service_.get(), - task_runner_.get()); + DataReductionProxyConfigTracker tracker( + base::Bind(&data_reduction_proxy::DataReductionProxyConfigService:: + UpdateProxyConfig, + base::Unretained(config_service_.get())), + task_runner_.get()); net::ProxyConfig expected_config; expected_config.proxy_rules().ParseFromString( kDataReductionProxyRestrictedRules); @@ -246,8 +253,11 @@ TEST_F(DataReductionProxyConfigServiceTest, TrackerDisable) { config_service_->AddObserver(&observer); scoped_refptr task_runner_( new base::TestSimpleTaskRunner()); - DataReductionProxyConfigTracker tracker(config_service_.get(), - task_runner_.get()); + DataReductionProxyConfigTracker tracker( + base::Bind(&data_reduction_proxy::DataReductionProxyConfigService:: + UpdateProxyConfig, + base::Unretained(config_service_.get())), + task_runner_.get()); net::ProxyConfig expected_config; expected_config.proxy_rules().ParseFromString(kSystemProxyRules); EXPECT_CALL(observer, OnProxyConfigChanged( @@ -266,8 +276,11 @@ TEST_F(DataReductionProxyConfigServiceTest, TrackerBypassList) { base::MessageLoopForUI loop; scoped_refptr task_runner_( new base::TestSimpleTaskRunner()); - DataReductionProxyConfigTracker tracker(config_service_.get(), - task_runner_.get()); + DataReductionProxyConfigTracker tracker( + base::Bind(&data_reduction_proxy::DataReductionProxyConfigService:: + UpdateProxyConfig, + base::Unretained(config_service_.get())), + task_runner_.get()); tracker.AddHostPatternToBypass("http://www.google.com"); tracker.AddHostPatternToBypass("fefe:13::abc/33"); tracker.AddURLPatternToBypass("foo.org/images/*"); diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc index 2093d841ad810..795dbea03dc53 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_metrics.cc @@ -315,7 +315,7 @@ DataReductionProxyRequestType GetDataReductionProxyRequestType( } #endif if (request->response_info().headers && - HasDataReductionProxyViaHeader(request->response_info().headers)) { + HasDataReductionProxyViaHeader(request->response_info().headers, NULL)) { return VIA_DATA_REDUCTION_PROXY; } return UNKNOWN_TYPE; diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_params.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_params.cc index fa75c6b6cb7a5..3088e083eabe4 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_params.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_params.cc @@ -4,11 +4,15 @@ #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" +#include + #include "base/command_line.h" +#include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" #include "base/time/time.h" #include "components/data_reduction_proxy/common/data_reduction_proxy_switches.h" #include "net/base/host_port_pair.h" +#include "net/proxy/proxy_config.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_retry_info.h" #include "net/proxy/proxy_server.h" @@ -57,6 +61,16 @@ bool DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() { "DataCompressionProxyCriticalBypass") == kEnabled; } +DataReductionProxyTypeInfo::DataReductionProxyTypeInfo() + : proxy_servers(), + is_fallback(false), + is_alternative(false), + is_ssl(false) { +} + +DataReductionProxyTypeInfo::~DataReductionProxyTypeInfo(){ +} + bool DataReductionProxyParams::IsIncludedInHoldbackFieldTrial() { return FieldTrialList::FindFullName( "DataCompressionProxyHoldback") == kEnabled; @@ -73,6 +87,28 @@ DataReductionProxyParams::DataReductionProxyParams(int flags) DCHECK(result); } +scoped_ptr DataReductionProxyParams::Clone() { + return scoped_ptr( + new DataReductionProxyParams(*this)); +} + +DataReductionProxyParams::DataReductionProxyParams( + const DataReductionProxyParams& other) + : origin_(other.origin_), + fallback_origin_(other.fallback_origin_), + ssl_origin_(other.ssl_origin_), + alt_origin_(other.alt_origin_), + alt_fallback_origin_(other.alt_fallback_origin_), + probe_url_(other.probe_url_), + warmup_url_(other.warmup_url_), + allowed_(other.allowed_), + fallback_allowed_(other.fallback_allowed_), + alt_allowed_(other.alt_allowed_), + promo_allowed_(other.promo_allowed_), + holdback_(other.holdback_), + configured_on_command_line_(other.configured_on_command_line_) { +} + DataReductionProxyParams::~DataReductionProxyParams() { } @@ -241,19 +277,19 @@ void DataReductionProxyParams::InitWithoutChecks() { bool DataReductionProxyParams::WasDataReductionProxyUsed( const net::URLRequest* request, - std::pair* proxy_servers) const { + DataReductionProxyTypeInfo* proxy_info) const { DCHECK(request); - return IsDataReductionProxy(request->proxy_server(), proxy_servers); + return IsDataReductionProxy(request->proxy_server(), proxy_info); } bool DataReductionProxyParams::IsDataReductionProxy( const net::HostPortPair& host_port_pair, - std::pair* proxy_servers) const { + DataReductionProxyTypeInfo* proxy_info) const { if (net::HostPortPair::FromURL(origin()).Equals(host_port_pair)) { - if (proxy_servers) { - (*proxy_servers).first = origin(); + if (proxy_info) { + proxy_info->proxy_servers.first = origin(); if (fallback_allowed()) - (*proxy_servers).second = fallback_origin(); + proxy_info->proxy_servers.second = fallback_origin(); } return true; } @@ -265,10 +301,10 @@ bool DataReductionProxyParams::IsDataReductionProxy( if (GURL(GetDefaultDevOrigin()) == origin()) { const GURL& default_origin = GURL(GetDefaultOrigin()); if (net::HostPortPair::FromURL(default_origin).Equals(host_port_pair)) { - if (proxy_servers) { - (*proxy_servers).first = default_origin; + if (proxy_info) { + proxy_info->proxy_servers.first = default_origin; if (fallback_allowed()) - (*proxy_servers).second = fallback_origin(); + proxy_info->proxy_servers.second = fallback_origin(); } return true; } @@ -276,33 +312,38 @@ bool DataReductionProxyParams::IsDataReductionProxy( if (fallback_allowed() && net::HostPortPair::FromURL(fallback_origin()).Equals(host_port_pair)) { - if (proxy_servers) { - (*proxy_servers).first = fallback_origin(); - (*proxy_servers).second = GURL(); + if (proxy_info) { + proxy_info->proxy_servers.first = fallback_origin(); + proxy_info->proxy_servers.second = GURL(); + proxy_info->is_fallback = true; } return true; } if (net::HostPortPair::FromURL(alt_origin()).Equals(host_port_pair)) { - if (proxy_servers) { - (*proxy_servers).first = alt_origin(); + if (proxy_info) { + proxy_info->proxy_servers.first = alt_origin(); + proxy_info->is_alternative = true; if (fallback_allowed()) - (*proxy_servers).second = alt_fallback_origin(); + proxy_info->proxy_servers.second = alt_fallback_origin(); } return true; } if (fallback_allowed() && net::HostPortPair::FromURL(alt_fallback_origin()).Equals( host_port_pair)) { - if (proxy_servers) { - (*proxy_servers).first = alt_fallback_origin(); - (*proxy_servers).second = GURL(); + if (proxy_info) { + proxy_info->proxy_servers.first = alt_fallback_origin(); + proxy_info->proxy_servers.second = GURL(); + proxy_info->is_fallback = true; + proxy_info->is_alternative = true; } return true; } if (net::HostPortPair::FromURL(ssl_origin()).Equals(host_port_pair)) { - if (proxy_servers) { - (*proxy_servers).first = ssl_origin(); - (*proxy_servers).second = GURL(); + if (proxy_info) { + proxy_info->proxy_servers.first = ssl_origin(); + proxy_info->proxy_servers.second = GURL(); + proxy_info->is_ssl = true; } return true; } @@ -326,6 +367,21 @@ bool DataReductionProxyParams::IsDataReductionProxyEligible( return IsDataReductionProxy(result.proxy_server().host_port_pair(), NULL); } +bool DataReductionProxyParams::IsBypassedByDataReductionProxyLocalRules( + const net::URLRequest& request, + const net::ProxyConfig& data_reduction_proxy_config) const { + DCHECK(request.context()); + DCHECK(request.context()->proxy_service()); + net::ProxyInfo result; + data_reduction_proxy_config.proxy_rules().Apply( + request.url(), &result); + if (!result.proxy_server().is_valid()) + return true; + if (result.proxy_server().is_direct()) + return true; + return !IsDataReductionProxy(result.proxy_server().host_port_pair(), NULL); +} + std::string DataReductionProxyParams::GetDefaultDevOrigin() const { #if defined(DATA_REDUCTION_DEV_HOST) const CommandLine& command_line = *CommandLine::ForCurrentProcess(); @@ -360,9 +416,14 @@ bool DataReductionProxyParams::AreProxiesBypassed( if (retry_map.size() == 0) return false; - if (is_https && alt_allowed_) { - return ArePrimaryAndFallbackBypassed( - retry_map, ssl_origin_, GURL(), min_retry_delay); + // If the request is https, consider only the ssl proxy. + if (is_https) { + if (alt_allowed_) { + return ArePrimaryAndFallbackBypassed( + retry_map, ssl_origin_, GURL(), min_retry_delay); + } + NOTREACHED(); + return false; } if (allowed_ && ArePrimaryAndFallbackBypassed( @@ -383,34 +444,41 @@ bool DataReductionProxyParams::ArePrimaryAndFallbackBypassed( const GURL& primary, const GURL& fallback, base::TimeDelta* min_retry_delay) const { - net::ProxyRetryInfoMap::const_iterator found = retry_map.find( - net::ProxyServer(primary.SchemeIs(url::kHttpsScheme) ? - net::ProxyServer::SCHEME_HTTPS : net::ProxyServer::SCHEME_HTTP, - net::HostPortPair::FromURL(primary)).ToURI()); - - if (found == retry_map.end()) - return false; - - base::TimeDelta min_delay = found->second.current_delay; - if (!fallback_allowed_ || !fallback.is_valid()) { - if (min_retry_delay != NULL) - *min_retry_delay = min_delay; - return true; + net::ProxyRetryInfoMap::const_iterator found = retry_map.end(); + if (min_retry_delay) + *min_retry_delay = base::TimeDelta::Max(); + + // Look for the primary proxy in the retry map. This must be done before + // looking for the fallback in order to assign |min_retry_delay| if the + // primary proxy has a shorter delay. + if (!fallback_allowed_ || !fallback.is_valid() || min_retry_delay) { + found = retry_map.find( + net::ProxyServer(primary.SchemeIs(url::kHttpsScheme) ? + net::ProxyServer::SCHEME_HTTPS : + net::ProxyServer::SCHEME_HTTP, + net::HostPortPair::FromURL(primary)).ToURI()); + if (found != retry_map.end() && min_retry_delay) { + *min_retry_delay = found->second.current_delay; + } } - found = retry_map.find( - net::ProxyServer(fallback.SchemeIs(url::kHttpsScheme) ? - net::ProxyServer::SCHEME_HTTPS : net::ProxyServer::SCHEME_HTTP, - net::HostPortPair::FromURL(fallback)).ToURI()); - - if (found == retry_map.end()) - return false; + if (fallback_allowed_ && fallback.is_valid()) { + // If fallback is allowed, only the fallback proxy needs to be on the retry + // map to know if there was a bypass. We can reset found and forget if the + // primary was on the retry map. + found = retry_map.find( + net::ProxyServer(fallback.SchemeIs(url::kHttpsScheme) ? + net::ProxyServer::SCHEME_HTTPS : + net::ProxyServer::SCHEME_HTTP, + net::HostPortPair::FromURL(fallback)).ToURI()); + if (found != retry_map.end() && + min_retry_delay && + *min_retry_delay > found->second.current_delay) { + *min_retry_delay = found->second.current_delay; + } + } - if (min_delay > found->second.current_delay) - min_delay = found->second.current_delay; - if (min_retry_delay != NULL) - *min_retry_delay = min_delay; - return true; + return found != retry_map.end(); } std::string DataReductionProxyParams::GetDefaultOrigin() const { diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_params.h b/components/data_reduction_proxy/browser/data_reduction_proxy_params.h index ae024b8063e2d..905904792de95 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_params.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_params.h @@ -9,7 +9,7 @@ #include #include -#include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "net/base/host_port_pair.h" #include "net/proxy/proxy_retry_info.h" #include "url/gurl.h" @@ -19,10 +19,25 @@ class TimeDelta; } namespace net { +class ProxyConfig; class URLRequest; } namespace data_reduction_proxy { + +// Contains information about a given proxy server. |proxy_servers| contains +// the configured data reduction proxy servers. |is_fallback|, |is_alternative| +// and |is_ssl| note whether the given proxy is a fallback, an alternative, +// or a proxy for ssl; these are not mutually exclusive. +struct DataReductionProxyTypeInfo { + DataReductionProxyTypeInfo(); + ~DataReductionProxyTypeInfo(); + std::pair proxy_servers; + bool is_fallback; + bool is_alternative; + bool is_ssl; +}; + // Provides initialization parameters. Proxy origins, and the probe url are // are taken from flags if available and from preprocessor constants otherwise. // The DataReductionProxySettings class and others use this class to determine @@ -80,35 +95,49 @@ class DataReductionProxyParams { // A standard configuration has a primary proxy, and a fallback proxy for // HTTP traffic. The alternative configuration has a different primary and // fallback proxy for HTTP traffic, and an SSL proxy. + explicit DataReductionProxyParams(int flags); - DataReductionProxyParams(int flags); + // Creates a copy of the configuration parameters. + scoped_ptr Clone(); virtual ~DataReductionProxyParams(); // Returns true if a data reduction proxy was used for the given |request|. - // If true, |proxy_servers.first| will contain the name of the proxy that was - // used. |proxy_servers.second| will contain the name of the data reduction - // proxy server that would be used if |proxy_server.first| is bypassed, if one - // exists. |proxy_servers| can be NULL if the caller isn't interested in its - // values. + // If true, |proxy_info.proxy_servers.first| will contain the name of the + // proxy that was used. |proxy_info.proxy_servers.second| will contain the + // name of the data reduction proxy server that would be used if + // |proxy_info.proxy_server.first| is bypassed, if one exists. In addition, + // |proxy_info| will note if the proxy used was a fallback, an alternative, + // or a proxy for ssl; these are not mutually exclusive. |proxy_info| can be + // NULL if the caller isn't interested in its values. virtual bool WasDataReductionProxyUsed( const net::URLRequest* request, - std::pair* proxy_servers) const; + DataReductionProxyTypeInfo* proxy_info) const; // Returns true if the specified |host_port_pair| matches a data reduction - // proxy. If true, |proxy_servers.first| will contain the name of the proxy - // that matches. |proxy_servers.second| will contain the name of the - // data reduction proxy server that would be used if |proxy_server.first| is - // bypassed, if one exists. |proxy_servers| can be NULL if the caller isn't - // interested in its values. Virtual for testing. - virtual bool IsDataReductionProxy(const net::HostPortPair& host_port_pair, - std::pair* proxy_servers) const; + // proxy. If true, |proxy_info.proxy_servers.first| will contain the name of + // the proxy that matches. |proxy_info.proxy_servers.second| will contain the + // name of the data reduction proxy server that would be used if + // |proxy_info.proxy_server.first| is bypassed, if one exists. In addition, + // |proxy_info| will note if the proxy was a fallback, an alternative, or a + // proxy for ssl; these are not mutually exclusive. |proxy_info| can be NULL + // if the caller isn't interested in its values. Virtual for testing. + virtual bool IsDataReductionProxy( + const net::HostPortPair& host_port_pair, + DataReductionProxyTypeInfo* proxy_info) const; // Returns true if this request will be sent through the data request proxy // based on applying the param rules to the URL. We do not check bad proxy // list. virtual bool IsDataReductionProxyEligible(const net::URLRequest* request); + // Returns true if this request would be bypassed by the data request proxy + // based on applying the |data_reduction_proxy_config| param rules to the + // request URL. + bool IsBypassedByDataReductionProxyLocalRules( + const net::URLRequest& request, + const net::ProxyConfig& data_reduction_proxy_config) const; + // Checks if all configured data reduction proxies are in the retry map. // Returns true if the request is bypassed by all configured data reduction // proxies and returns the bypass delay in delay_seconds (if not NULL). If @@ -207,6 +236,8 @@ class DataReductionProxyParams { DataReductionProxyParams(int flags, bool should_call_init); + DataReductionProxyParams(const DataReductionProxyParams& params); + // Initialize the values of the proxies, and probe URL, from command // line flags and preprocessor constants, and check that there are // corresponding definitions for the allowed configurations. @@ -237,6 +268,9 @@ class DataReductionProxyParams { const GURL& primary, const GURL& fallback, base::TimeDelta* min_retry_delay) const; + + DataReductionProxyParams& operator=(const DataReductionProxyParams& params); + GURL origin_; GURL fallback_origin_; GURL ssl_origin_; @@ -246,14 +280,12 @@ class DataReductionProxyParams { GURL warmup_url_; bool allowed_; - const bool fallback_allowed_; + bool fallback_allowed_; bool alt_allowed_; - const bool promo_allowed_; + bool promo_allowed_; bool holdback_; bool configured_on_command_line_; - - DISALLOW_COPY_AND_ASSIGN(DataReductionProxyParams); }; } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc index 4ab9e2d2bfba2..9a5681d159426 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_params_unittest.cc @@ -439,6 +439,9 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { bool expected_result; net::HostPortPair expected_first; net::HostPortPair expected_second; + bool expected_is_fallback; + bool expected_is_alternative; + bool expected_is_ssl; } tests[] = { { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultOrigin())), @@ -448,7 +451,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultOrigin())), net::HostPortPair::FromURL(GURL( - TestDataReductionProxyParams::DefaultFallbackOrigin())) + TestDataReductionProxyParams::DefaultFallbackOrigin())), + false, + false, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultOrigin())), @@ -457,7 +463,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { true, net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultOrigin())), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + false, + false, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultFallbackOrigin())), @@ -466,7 +475,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { true, net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultFallbackOrigin())), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + true, + false, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultFallbackOrigin())), @@ -474,7 +486,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { false, false, net::HostPortPair::FromURL(GURL()), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + false, + false, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultAltOrigin())), @@ -484,7 +499,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultAltOrigin())), net::HostPortPair::FromURL(GURL( - TestDataReductionProxyParams::DefaultAltFallbackOrigin())) + TestDataReductionProxyParams::DefaultAltFallbackOrigin())), + false, + true, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultAltOrigin())), @@ -493,7 +511,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { true, net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultAltOrigin())), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + false, + true, + false }, { net::HostPortPair::FromURL( GURL(TestDataReductionProxyParams::DefaultAltFallbackOrigin())), @@ -502,7 +523,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { true, net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultAltFallbackOrigin())), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + true, + true, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultAltFallbackOrigin())), @@ -510,7 +534,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { false, false, net::HostPortPair::FromURL(GURL()), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + false, + false, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultSSLOrigin())), @@ -519,7 +546,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { true, net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultSSLOrigin())), - net::HostPortPair::FromURL(GURL()) + net::HostPortPair::FromURL(GURL()), + false, + false, + true }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultDevOrigin())), @@ -529,7 +559,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultDevOrigin())), net::HostPortPair::FromURL(GURL( - TestDataReductionProxyParams::DefaultFallbackOrigin())) + TestDataReductionProxyParams::DefaultFallbackOrigin())), + false, + false, + false }, { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultOrigin())), @@ -539,7 +572,10 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { net::HostPortPair::FromURL(GURL( TestDataReductionProxyParams::DefaultOrigin())), net::HostPortPair::FromURL(GURL( - TestDataReductionProxyParams::DefaultFallbackOrigin())) + TestDataReductionProxyParams::DefaultFallbackOrigin())), + false, + false, + false }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { @@ -552,14 +588,17 @@ TEST_F(DataReductionProxyParamsTest, IsDataReductionProxy) { has_definitions &= ~TestDataReductionProxyParams::HAS_DEV_ORIGIN; } TestDataReductionProxyParams params(flags, has_definitions); - std::pair proxy_servers; + DataReductionProxyTypeInfo proxy_type_info; EXPECT_EQ(tests[i].expected_result, params.IsDataReductionProxy( - tests[i].host_port_pair, &proxy_servers)); + tests[i].host_port_pair, &proxy_type_info)); EXPECT_TRUE(tests[i].expected_first.Equals( - net::HostPortPair::FromURL(proxy_servers.first))); + net::HostPortPair::FromURL(proxy_type_info.proxy_servers.first))); EXPECT_TRUE(tests[i].expected_second.Equals( - net::HostPortPair::FromURL(proxy_servers.second))); + net::HostPortPair::FromURL(proxy_type_info.proxy_servers.second))); + EXPECT_EQ(tests[i].expected_is_fallback, proxy_type_info.is_fallback); + EXPECT_EQ(tests[i].expected_is_alternative, proxy_type_info.is_alternative); + EXPECT_EQ(tests[i].expected_is_ssl, proxy_type_info.is_ssl); } } @@ -665,7 +704,7 @@ TEST_F(DataReductionProxyParamsTest, AreProxiesBypassed) { { // proxy flags false, false, - false, + true, // is https request true, // proxies in retry map @@ -675,11 +714,11 @@ TEST_F(DataReductionProxyParamsTest, AreProxiesBypassed) { false, true, // expected result - false, + true, }, { // proxy flags - false, - false, + true, + true, true, // is https request true, @@ -694,30 +733,45 @@ TEST_F(DataReductionProxyParamsTest, AreProxiesBypassed) { }, { // proxy flags true, - true, - true, + false, + false, // is https request - true, + false, // proxies in retry map + true, false, false, false, false, - true, // expected result true, }, { // proxy flags true, - false, + true, false, // is https request false, // proxies in retry map + false, + true, + false, + false, + false, + // expected result + true, + }, + { // proxy flags + false, + true, true, + // is https request + false, + // proxies in retry map false, false, false, + true, false, // expected result true, diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc index f6b785dca91ab..aae5ee30ac24a 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.cc @@ -7,11 +7,15 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h" +#include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h" #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" +#include "net/proxy/proxy_config.h" #include "net/proxy/proxy_info.h" #include "net/proxy/proxy_list.h" +#include "net/proxy/proxy_retry_info.h" #include "net/proxy/proxy_server.h" #include "net/proxy/proxy_service.h" #include "net/url_request/url_request.h" @@ -41,12 +45,12 @@ bool MaybeBypassProxyAndPrepareToRetry( net::URLRequest* request, const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, - net::ProxyService::DataReductionProxyBypassType* proxy_bypass_type) { + DataReductionProxyBypassType* proxy_bypass_type) { if (!data_reduction_proxy_params) return false; - std::pair data_reduction_proxies; + DataReductionProxyTypeInfo data_reduction_proxy_type_info; if (!data_reduction_proxy_params->WasDataReductionProxyUsed( - request, &data_reduction_proxies)) { + request, &data_reduction_proxy_type_info)) { return false; } @@ -55,32 +59,45 @@ bool MaybeBypassProxyAndPrepareToRetry( if (request->proxy_server().IsEmpty()) return false; - if (data_reduction_proxies.first.is_empty()) + if (data_reduction_proxy_type_info.proxy_servers.first.is_empty()) return false; + DataReductionProxyTamperDetection::DetectAndReport( + original_response_headers, + data_reduction_proxy_type_info.proxy_servers.first.SchemeIsSecure()); + DataReductionProxyInfo data_reduction_proxy_info; - net::ProxyService::DataReductionProxyBypassType bypass_type = + DataReductionProxyBypassType bypass_type = GetDataReductionProxyBypassType(original_response_headers, &data_reduction_proxy_info); if (proxy_bypass_type) *proxy_bypass_type = bypass_type; - if (bypass_type == net::ProxyService::BYPASS_EVENT_TYPE_MAX) + if (bypass_type == BYPASS_EVENT_TYPE_MAX) return false; DCHECK(request->context()); DCHECK(request->context()->proxy_service()); net::ProxyServer proxy_server; - SetProxyServerFromGURL(data_reduction_proxies.first, &proxy_server); - request->context()->proxy_service()->RecordDataReductionProxyBypassInfo( - !data_reduction_proxies.second.is_empty(), - data_reduction_proxy_info.bypass_all, - proxy_server, - bypass_type); - - MarkProxiesAsBadUntil(request, - data_reduction_proxy_info.bypass_duration, - data_reduction_proxy_info.bypass_all, - data_reduction_proxies); + SetProxyServerFromGURL( + data_reduction_proxy_type_info.proxy_servers.first, &proxy_server); + + // Only record UMA if the proxy isn't already on the retry list. + const net::ProxyRetryInfoMap& proxy_retry_info = + request->context()->proxy_service()->proxy_retry_info(); + if (proxy_retry_info.find(proxy_server.ToURI()) == proxy_retry_info.end()) { + DataReductionProxyUsageStats::RecordDataReductionProxyBypassInfo( + !data_reduction_proxy_type_info.proxy_servers.second.is_empty(), + data_reduction_proxy_info.bypass_all, + proxy_server, + bypass_type); + } + + if (data_reduction_proxy_info.mark_proxies_as_bad) { + MarkProxiesAsBadUntil(request, + data_reduction_proxy_info.bypass_duration, + data_reduction_proxy_info.bypass_all, + data_reduction_proxy_type_info.proxy_servers); + } // Only retry idempotent methods. if (!IsRequestIdempotent(request)) @@ -94,8 +111,19 @@ bool MaybeBypassProxyAndPrepareToRetry( void OnResolveProxyHandler(const GURL& url, int load_flags, + const net::ProxyConfig& data_reduction_proxy_config, + const net::ProxyRetryInfoMap& proxy_retry_info, const DataReductionProxyParams* params, net::ProxyInfo* result) { + if (data_reduction_proxy_config.is_valid() && + result->proxy_server().is_direct()) { + net::ProxyInfo data_reduction_proxy_info; + data_reduction_proxy_config.proxy_rules().Apply( + url, &data_reduction_proxy_info); + data_reduction_proxy_info.DeprioritizeBadProxies(proxy_retry_info); + result->Use(data_reduction_proxy_info); + } + if ((load_flags & net::LOAD_BYPASS_DATA_REDUCTION_PROXY) && DataReductionProxyParams::IsIncludedInCriticalPathBypassFieldTrial() && !result->is_empty() && diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h index 26dca40cb6a9d..e592713f215a4 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol.h @@ -5,9 +5,9 @@ #ifndef COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_PROTOCOL_H_ #define COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_PROTOCOL_H_ -#include "base/macros.h" #include "base/memory/ref_counted.h" -#include "net/proxy/proxy_service.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" +#include "net/proxy/proxy_retry_info.h" namespace base { class TimeDelta; @@ -15,6 +15,7 @@ class TimeDelta; namespace net { class HttpResponseHeaders; +class ProxyConfig; class ProxyInfo; class ProxyServer; class URLRequest; @@ -35,7 +36,7 @@ bool MaybeBypassProxyAndPrepareToRetry( net::URLRequest* request, const net::HttpResponseHeaders* original_response_headers, scoped_refptr* override_response_headers, - net::ProxyService::DataReductionProxyBypassType* proxy_bypass_type); + DataReductionProxyBypassType* proxy_bypass_type); // Configure |result| to proceed directly to the origin if |result|'s current // proxy is the data reduction proxy, the @@ -45,6 +46,8 @@ bool MaybeBypassProxyAndPrepareToRetry( // |ChromeNetworkDelegate.NotifyResolveProxy|. void OnResolveProxyHandler(const GURL& url, int load_flags, + const net::ProxyConfig& data_reduction_proxy_config, + const net::ProxyRetryInfoMap& proxy_retry_info, const DataReductionProxyParams* params, net::ProxyInfo* result); diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc index e835c4d7f082f..60e663be76482 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_protocol_unittest.cc @@ -12,6 +12,7 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params_test_utils.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "net/base/completion_callback.h" #include "net/base/host_port_pair.h" #include "net/base/load_flags.h" @@ -58,7 +59,7 @@ class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate { public: TestDataReductionProxyNetworkDelegate( TestDataReductionProxyParams* test_params, - ProxyService::DataReductionProxyBypassType* bypass_type) + DataReductionProxyBypassType* bypass_type) : net::NetworkDelegate(), test_data_reduction_proxy_params_(test_params), bypass_type_(bypass_type) { @@ -80,7 +81,7 @@ class TestDataReductionProxyNetworkDelegate : public net::NetworkDelegate { } TestDataReductionProxyParams* test_data_reduction_proxy_params_; - ProxyService::DataReductionProxyBypassType* bypass_type_; + DataReductionProxyBypassType* bypass_type_; }; // Constructs a |TestURLRequestContext| that uses a |MockSocketFactory| to @@ -99,9 +100,8 @@ class DataReductionProxyProtocolTest : public testing::Test { // Sets up the |TestURLRequestContext| with the provided |ProxyService| and // |bypass_type| to store bypass reasons. - void ConfigureTestDependencies( - ProxyService* proxy_service, - ProxyService::DataReductionProxyBypassType* bypass_type) { + void ConfigureTestDependencies(ProxyService* proxy_service, + DataReductionProxyBypassType* bypass_type) { // Create a context with delayed initialization. context_.reset(new TestURLRequestContext(true)); @@ -323,6 +323,15 @@ TEST_F(DataReductionProxyProtocolTest, OverrideResponseAsRedirect) { "Via: 1.1 Chrome-Compression-Proxy\n" "Location: http://www.google.com/\n" }, + { "HTTP/1.1 200 0K\n" + "Chrome-Proxy: block-once\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + + "HTTP/1.1 302 Found\n" + "Chrome-Proxy: block-once\n" + "Via: 1.1 Chrome-Compression-Proxy\n" + "Location: http://www.google.com/\n" + }, { "HTTP/1.1 302 Found\n" "Location: http://foo.com/\n", @@ -367,7 +376,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { size_t expected_bad_proxy_count; bool expect_response_body; int expected_duration; - ProxyService::DataReductionProxyBypassType expected_bypass_type; + DataReductionProxyBypassType expected_bypass_type; } tests[] = { // Valid data reduction proxy response with no bypass message. { "GET", @@ -378,7 +387,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 0u, true, -1, - ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, }, // Valid data reduction proxy response with older, but still valid via // header. @@ -390,7 +399,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 0u, true, -1, - ProxyService::BYPASS_EVENT_TYPE_MAX + BYPASS_EVENT_TYPE_MAX }, // Valid data reduction proxy response with chained via header, // no bypass message. @@ -402,7 +411,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 0u, true, -1, - ProxyService::BYPASS_EVENT_TYPE_MAX + BYPASS_EVENT_TYPE_MAX }, // Valid data reduction proxy response with a bypass message. { "GET", @@ -414,7 +423,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // Valid data reduction proxy response with a bypass message. { "GET", @@ -426,7 +435,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 1, - ProxyService::SHORT_BYPASS + BYPASS_EVENT_TYPE_SHORT }, // Same as above with the OPTIONS method, which is idempotent. { "OPTIONS", @@ -438,7 +447,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // Same as above with the HEAD method, which is idempotent. { "HEAD", @@ -450,7 +459,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, false, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // Same as above with the PUT method, which is idempotent. { "PUT", @@ -462,7 +471,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // Same as above with the DELETE method, which is idempotent. { "DELETE", @@ -474,7 +483,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // Same as above with the TRACE method, which is idempotent. { "TRACE", @@ -486,7 +495,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // 500 responses should be bypassed. { "GET", @@ -497,7 +506,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::STATUS_500_HTTP_INTERNAL_SERVER_ERROR + BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR }, // 502 responses should be bypassed. { "GET", @@ -508,7 +517,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::STATUS_502_HTTP_BAD_GATEWAY + BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY }, // 503 responses should be bypassed. { "GET", @@ -519,7 +528,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::STATUS_503_HTTP_SERVICE_UNAVAILABLE + BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE }, // Invalid data reduction proxy response. Missing Via header. { "GET", @@ -529,7 +538,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MISSING_VIA_HEADER_OTHER + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER }, // Invalid data reduction proxy response. Wrong Via header. { "GET", @@ -540,7 +549,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MISSING_VIA_HEADER_OTHER + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER }, // Valid data reduction proxy response. 304 missing Via header. { "GET", @@ -550,7 +559,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 0u, false, 0, - ProxyService::BYPASS_EVENT_TYPE_MAX + BYPASS_EVENT_TYPE_MAX }, // Valid data reduction proxy response with a bypass message. It will // not be retried because the request is non-idempotent. @@ -563,7 +572,7 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 1u, true, 0, - ProxyService::MEDIUM_BYPASS + BYPASS_EVENT_TYPE_MEDIUM }, // Valid data reduction proxy response with block message. Both proxies // should be on the retry list when it completes. @@ -576,13 +585,130 @@ TEST_F(DataReductionProxyProtocolTest, BypassLogic) { 2u, true, 1, - ProxyService::SHORT_BYPASS - } + BYPASS_EVENT_TYPE_SHORT + }, + // Valid data reduction proxy response with a block-once message. It will be + // retried, and there will be no proxies on the retry list since block-once + // only affects the current request. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the OPTIONS method, which is idempotent. + { "OPTIONS", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the HEAD method, which is idempotent. + { "HEAD", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 0u, + false, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the PUT method, which is idempotent. + { "PUT", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the DELETE method, which is idempotent. + { "DELETE", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Same as above with the TRACE method, which is idempotent. + { "TRACE", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Valid data reduction proxy response with a block-once message. It will + // not be retried because the request is non-idempotent, and there will be + // no proxies on the retry list since block-once only affects the current + // request. + { "POST", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + false, + 0u, + true, + 0, + BYPASS_EVENT_TYPE_CURRENT + }, + // Valid data reduction proxy response with block and block-once messages. + // The block message will override the block-once message, so both proxies + // should be on the retry list when it completes. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: block=1, block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 2u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, + // Valid data reduction proxy response with bypass and block-once messages. + // The bypass message will override the block-once message, so one proxy + // should be on the retry list when it completes. + { "GET", + "HTTP/1.1 200 OK\r\n" + "Server: proxy\r\n" + "Chrome-Proxy: bypass=1, block-once\r\n" + "Via: 1.1 Chrome-Compression-Proxy\r\n\r\n", + true, + 1u, + true, + 1, + BYPASS_EVENT_TYPE_SHORT + }, }; std::string primary = proxy_params_->DefaultOrigin(); std::string fallback = proxy_params_->DefaultFallbackOrigin(); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { - ProxyService::DataReductionProxyBypassType bypass_type; + DataReductionProxyBypassType bypass_type; ConfigureTestDependencies(ProxyService::CreateFixedFromPacResult( "PROXY " + HostPortPair::FromURL(GURL(primary)).ToString() + "; PROXY " + @@ -667,33 +793,95 @@ TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) { TestDataReductionProxyParams::HAS_EVERYTHING & ~TestDataReductionProxyParams::HAS_DEV_ORIGIN); - // Data reduction proxy - net::ProxyInfo info1; + // Data reduction proxy info + net::ProxyInfo data_reduction_proxy_info; std::string data_reduction_proxy; base::TrimString(test_params.DefaultOrigin(), "/", &data_reduction_proxy); - info1.UseNamedProxy(data_reduction_proxy); - EXPECT_FALSE(info1.is_empty()); + data_reduction_proxy_info.UseNamedProxy(data_reduction_proxy); + EXPECT_FALSE(data_reduction_proxy_info.is_empty()); + + // Data reduction proxy config + net::ProxyConfig data_reduction_proxy_config; + data_reduction_proxy_config.proxy_rules().ParseFromString( + "http=" + data_reduction_proxy + ",direct://;"); + data_reduction_proxy_config.set_id(1); + + // Other proxy info + net::ProxyInfo other_proxy_info; + other_proxy_info.UseNamedProxy("proxy.com"); + EXPECT_FALSE(other_proxy_info.is_empty()); + + // Direct + net::ProxyInfo direct_proxy_info; + direct_proxy_info.UseDirect(); + EXPECT_TRUE(direct_proxy_info.is_direct()); + + // Empty retry info map + net::ProxyRetryInfoMap empty_proxy_retry_info; + + // Retry info map with the data reduction proxy; + net::ProxyRetryInfoMap data_reduction_proxy_retry_info; + net::ProxyRetryInfo retry_info; + retry_info.current_delay = base::TimeDelta::FromSeconds(1000); + retry_info.bad_until = base::TimeTicks().Now() + retry_info.current_delay; + retry_info.try_while_bad = false; + data_reduction_proxy_retry_info[ + data_reduction_proxy_info.proxy_server().ToURI()] = retry_info; + + net::ProxyInfo result; + + // The data reduction proxy is used. It should be used afterwards. + result.Use(data_reduction_proxy_info); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, &result); + EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server()); + + // Another proxy is used. It should be used afterwards. + result.Use(other_proxy_info); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, &result); + EXPECT_EQ(other_proxy_info.proxy_server(), result.proxy_server()); + + // A direct connection is used. The data reduction proxy should be used + // afterwards. + // Another proxy is used. It should be used afterwards. + result.Use(direct_proxy_info); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, &result); + EXPECT_EQ(data_reduction_proxy_info.proxy_server(), result.proxy_server()); + + // A direct connection is used, but the data reduction proxy is on the retry + // list. A direct connection should be used afterwards. + result.Use(direct_proxy_info); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + data_reduction_proxy_retry_info, &test_params, + &result); + EXPECT_TRUE(result.proxy_server().is_direct()); - // Other proxy - net::ProxyInfo info2; - info2.UseNamedProxy("proxy.com"); - EXPECT_FALSE(info2.is_empty()); // Without DataCompressionProxyCriticalBypass Finch trial set, should never // bypass. - OnResolveProxyHandler(url, load_flags, &test_params, &info1); - EXPECT_FALSE(info1.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &data_reduction_proxy_info); + EXPECT_FALSE(data_reduction_proxy_info.is_direct()); - OnResolveProxyHandler(url, load_flags, &test_params,&info2); - EXPECT_FALSE(info2.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &other_proxy_info); + EXPECT_FALSE(other_proxy_info.is_direct()); load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY; - OnResolveProxyHandler(url, load_flags, &test_params, &info1); - EXPECT_FALSE(info1.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &data_reduction_proxy_info); + EXPECT_FALSE(data_reduction_proxy_info.is_direct()); - OnResolveProxyHandler(url, load_flags, &test_params, &info2); - EXPECT_FALSE(info2.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &other_proxy_info); + EXPECT_FALSE(other_proxy_info.is_direct()); // With Finch trial set, should only bypass if LOAD flag is set and the // effective proxy is the data compression proxy. @@ -707,19 +895,27 @@ TEST_F(DataReductionProxyProtocolTest, OnResolveProxyHandler) { load_flags = net::LOAD_NORMAL; - OnResolveProxyHandler(url, load_flags, &test_params, &info1); - EXPECT_FALSE(info1.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &data_reduction_proxy_info); + EXPECT_FALSE(data_reduction_proxy_info.is_direct()); - OnResolveProxyHandler(url, load_flags, &test_params, &info2); - EXPECT_FALSE(info2.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &other_proxy_info); + EXPECT_FALSE(other_proxy_info.is_direct()); load_flags |= net::LOAD_BYPASS_DATA_REDUCTION_PROXY; - OnResolveProxyHandler(url, load_flags, &test_params, &info2); - EXPECT_FALSE(info2.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &other_proxy_info); + EXPECT_FALSE(other_proxy_info.is_direct()); - OnResolveProxyHandler(url, load_flags, &test_params, &info1); - EXPECT_TRUE(info1.is_direct()); + OnResolveProxyHandler(url, load_flags, data_reduction_proxy_config, + empty_proxy_retry_info, &test_params, + &data_reduction_proxy_info); + EXPECT_TRUE(data_reduction_proxy_info.is_direct()); } } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_settings.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_settings.cc index 1e20219260b24..a2ca75d1ee2d9 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_settings.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_settings.cc @@ -91,9 +91,11 @@ DataReductionProxySettings::DataReductionProxySettings( : restricted_by_carrier_(false), enabled_by_user_(false), disabled_on_vpn_(false), + unreachable_(false), prefs_(NULL), local_state_prefs_(NULL), - url_request_context_getter_(NULL) { + url_request_context_getter_(NULL), + configurator_(NULL) { DCHECK(params); params_.reset(params); } @@ -101,6 +103,7 @@ DataReductionProxySettings::DataReductionProxySettings( DataReductionProxySettings::~DataReductionProxySettings() { if (params_->allowed()) spdy_proxy_auth_enabled_.Destroy(); + net::NetworkChangeNotifier::RemoveIPAddressObserver(this); } void DataReductionProxySettings::InitPrefMembers() { @@ -147,11 +150,11 @@ void DataReductionProxySettings::InitDataReductionProxySettings( PrefService* prefs, PrefService* local_state_prefs, net::URLRequestContextGetter* url_request_context_getter, - scoped_ptr configurator) { + DataReductionProxyConfigurator* configurator) { InitDataReductionProxySettings(prefs, local_state_prefs, url_request_context_getter); - SetProxyConfigurator(configurator.Pass()); + SetProxyConfigurator(configurator); } void DataReductionProxySettings::SetOnDataReductionEnabledCallback( @@ -161,9 +164,9 @@ void DataReductionProxySettings::SetOnDataReductionEnabledCallback( } void DataReductionProxySettings::SetProxyConfigurator( - scoped_ptr configurator) { + DataReductionProxyConfigurator* configurator) { DCHECK(configurator); - configurator_ = configurator.Pass(); + configurator_ = configurator; } bool DataReductionProxySettings::IsDataReductionProxyEnabled() { @@ -218,14 +221,13 @@ DataReductionProxySettings::GetDailyOriginalContentLengths() { return GetDailyContentLengths(prefs::kDailyHttpOriginalContentLength); } -bool DataReductionProxySettings::IsDataReductionProxyUnreachable() { - DCHECK(thread_checker_.CalledOnValidThread()); - return usage_stats_ && usage_stats_->isDataReductionProxyUnreachable(); +void DataReductionProxySettings::SetUnreachable(bool unreachable) { + unreachable_ = unreachable; } -void DataReductionProxySettings::SetDataReductionProxyUsageStats( - DataReductionProxyUsageStats* usage_stats) { - usage_stats_ = usage_stats; +bool DataReductionProxySettings::IsDataReductionProxyUnreachable() { + DCHECK(thread_checker_.CalledOnValidThread()); + return unreachable_; } DataReductionProxySettings::ContentLengthList @@ -238,11 +240,6 @@ void DataReductionProxySettings::OnURLFetchComplete( const net::URLFetcher* source) { DCHECK(thread_checker_.CalledOnValidThread()); - // The purpose of sending a request for the warmup URL is to warm the - // connection to the data_reduction_proxy. The result is ignored. - if (source == warmup_fetcher_.get()) - return; - DCHECK(source == fetcher_.get()); net::URLRequestStatus status = source->GetStatus(); if (status.status() == net::URLRequestStatus::FAILED) { @@ -349,7 +346,6 @@ void DataReductionProxySettings::OnIPAddressChanged() { if (DisableIfVPN()) return; ProbeWhetherDataReductionProxyIsAvailable(); - WarmProxyConnection(); } } @@ -405,7 +401,6 @@ void DataReductionProxySettings::MaybeActivateDataReductionProxy( // Check if the proxy has been restricted explicitly by the carrier. if (enabled_by_user_ && !disabled_on_vpn_) { ProbeWhetherDataReductionProxyIsAvailable(); - WarmProxyConnection(); } } @@ -564,18 +559,6 @@ void DataReductionProxySettings::ProbeWhetherDataReductionProxyIsAvailable() { fetcher_->Start(); } -net::URLFetcher* DataReductionProxySettings::GetURLFetcherForWarmup() { - return GetBaseURLFetcher(params_->warmup_url(), net::LOAD_DISABLE_CACHE); -} - -void DataReductionProxySettings::WarmProxyConnection() { - net::URLFetcher* fetcher = GetURLFetcherForWarmup(); - if (!fetcher) - return; - warmup_fetcher_.reset(fetcher); - warmup_fetcher_->Start(); -} - bool DataReductionProxySettings::DisableIfVPN() { net::NetworkInterfaceList network_interfaces; GetNetworkList(&network_interfaces, 0); diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_settings.h b/components/data_reduction_proxy/browser/data_reduction_proxy_settings.h index a4938139ac76c..7a1348caab991 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_settings.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_settings.h @@ -16,7 +16,6 @@ #include "base/threading/thread_checker.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_configurator.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" -#include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h" #include "net/base/net_util.h" #include "net/base/network_change_notifier.h" #include "net/url_request/url_fetcher_delegate.h" @@ -98,10 +97,6 @@ class DataReductionProxySettings return params_.get(); } - DataReductionProxyUsageStats* usage_stats() const { - return usage_stats_; - } - // Initializes the data reduction proxy with profile and local state prefs, // and a |UrlRequestContextGetter| for canary probes. The caller must ensure // that all parameters remain alive for the lifetime of the @@ -120,7 +115,7 @@ class DataReductionProxySettings PrefService* prefs, PrefService* local_state_prefs, net::URLRequestContextGetter* url_request_context_getter, - scoped_ptr configurator); + DataReductionProxyConfigurator* configurator); // Sets the |on_data_reduction_proxy_enabled_| callback and runs to register // the DataReductionProxyEnabled synthetic field trial. @@ -130,7 +125,7 @@ class DataReductionProxySettings // Sets the logic the embedder uses to set the networking configuration that // causes traffic to be proxied. void SetProxyConfigurator( - scoped_ptr configurator); + DataReductionProxyConfigurator* configurator); // Returns true if the proxy is enabled. bool IsDataReductionProxyEnabled(); @@ -165,15 +160,14 @@ class DataReductionProxySettings int64* received_content_length, int64* last_update_time); + // Records that the data reduction proxy is unreachable or not. + void SetUnreachable(bool unreachable); + // Returns whether the data reduction proxy is unreachable. Returns true // if no request has successfully completed through proxy, even though atleast // some of them should have. bool IsDataReductionProxyUnreachable(); - // Set the data reduction proxy usage stats. - void SetDataReductionProxyUsageStats( - DataReductionProxyUsageStats* usage_stats); - // Returns an vector containing the aggregate received HTTP content in the // last |kNumDaysInHistory| days. ContentLengthList GetDailyReceivedContentLengths(); @@ -190,10 +184,6 @@ class DataReductionProxySettings // Virtual for testing. virtual net::URLFetcher* GetURLFetcherForAvailabilityCheck(); - // Returns a fetcher to warm up the connection to the data reduction proxy. - // Virtual for testing. - virtual net::URLFetcher* GetURLFetcherForWarmup(); - // Virtualized for unit test support. virtual PrefService* GetOriginalProfilePrefs(); virtual PrefService* GetLocalStatePrefs(); @@ -233,7 +223,7 @@ class DataReductionProxySettings int policy); DataReductionProxyConfigurator* configurator() { - return configurator_.get(); + return configurator_; } // Reset params for tests. @@ -290,9 +280,6 @@ class DataReductionProxySettings // failure. void ProbeWhetherDataReductionProxyIsAvailable(); - // Warms the connection to the data reduction proxy. - void WarmProxyConnection(); - // Disables use of the data reduction proxy on VPNs. Returns true if the // data reduction proxy has been disabled. bool DisableIfVPN(); @@ -304,9 +291,9 @@ class DataReductionProxySettings bool restricted_by_carrier_; bool enabled_by_user_; bool disabled_on_vpn_; + bool unreachable_; scoped_ptr fetcher_; - scoped_ptr warmup_fetcher_; BooleanPrefMember spdy_proxy_auth_enabled_; BooleanPrefMember data_reduction_proxy_alternative_enabled_; @@ -318,12 +305,11 @@ class DataReductionProxySettings base::Callback on_data_reduction_proxy_enabled_; - scoped_ptr configurator_; + DataReductionProxyConfigurator* configurator_; base::ThreadChecker thread_checker_; scoped_ptr params_; - DataReductionProxyUsageStats* usage_stats_; DISALLOW_COPY_AND_ASSIGN(DataReductionProxySettings); }; diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.cc index ccd3c2dd2b421..42c4ca367f10f 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.cc @@ -20,7 +20,6 @@ using testing::Return; namespace { const char kProbeURLWithOKResponse[] = "http://ok.org/"; -const char kWarmupURLWithNoContentResponse[] = "http://warm.org/"; const char kProxy[] = "proxy"; @@ -143,10 +142,10 @@ void DataReductionProxySettingsTestBase::ResetSettings(bool allowed, .Times(AnyNumber()) .WillRepeatedly(Return(&pref_service_)); EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0); - EXPECT_CALL(*settings, GetURLFetcherForWarmup()).Times(0); EXPECT_CALL(*settings, LogProxyState(_, _, _)).Times(0); settings_.reset(settings); - settings_->configurator_.reset(new TestDataReductionProxyConfig()); + configurator_.reset(new TestDataReductionProxyConfig()); + settings_->configurator_ = configurator_.get(); } // Explicitly generate required instantiations. @@ -161,7 +160,6 @@ DataReductionProxySettingsTestBase::ResetSettings( template void DataReductionProxySettingsTestBase::SetProbeResult( const std::string& test_url, - const std::string& warmup_test_url, const std::string& response, ProbeURLFetchResult result, bool success, @@ -170,7 +168,6 @@ void DataReductionProxySettingsTestBase::SetProbeResult( static_cast*>(settings_.get()); if (0 == expected_calls) { EXPECT_CALL(*settings, GetURLFetcherForAvailabilityCheck()).Times(0); - EXPECT_CALL(*settings, GetURLFetcherForWarmup()).Times(0); EXPECT_CALL(*settings, RecordProbeURLFetchResult(_)).Times(0); } else { EXPECT_CALL(*settings, RecordProbeURLFetchResult(result)).Times(1); @@ -183,15 +180,6 @@ void DataReductionProxySettingsTestBase::SetProbeResult( success ? net::HTTP_OK : net::HTTP_INTERNAL_SERVER_ERROR, success ? net::URLRequestStatus::SUCCESS : net::URLRequestStatus::FAILED))); - EXPECT_CALL(*settings, GetURLFetcherForWarmup()) - .Times(expected_calls) - .WillRepeatedly(Return(new net::FakeURLFetcher( - GURL(warmup_test_url), - settings, - "", - success ? net::HTTP_NO_CONTENT : net::HTTP_INTERNAL_SERVER_ERROR, - success ? net::URLRequestStatus::SUCCESS : - net::URLRequestStatus::FAILED))); } } @@ -199,7 +187,6 @@ void DataReductionProxySettingsTestBase::SetProbeResult( template void DataReductionProxySettingsTestBase::SetProbeResult( const std::string& test_url, - const std::string& warmup_test_url, const std::string& response, ProbeURLFetchResult result, bool success, @@ -210,8 +197,7 @@ void DataReductionProxySettingsTestBase::CheckProxyConfigs( bool expected_restricted, bool expected_fallback_restricted) { TestDataReductionProxyConfig* config = - static_cast( - settings_->configurator_.get()); + static_cast(settings_->configurator_); ASSERT_EQ(expected_restricted, config->restricted_); ASSERT_EQ(expected_fallback_restricted, config->fallback_restricted_); ASSERT_EQ(expected_enabled, config->enabled_); @@ -220,7 +206,6 @@ void DataReductionProxySettingsTestBase::CheckProxyConfigs( void DataReductionProxySettingsTestBase::CheckProbe( bool initially_enabled, const std::string& probe_url, - const std::string& warmup_url, const std::string& response, bool request_succeeded, bool expected_enabled, @@ -232,7 +217,6 @@ void DataReductionProxySettingsTestBase::CheckProbe( settings_->enabled_by_user_ = true; settings_->restricted_by_carrier_ = false; SetProbeResult(probe_url, - warmup_url, response, FetchResult(initially_enabled, request_succeeded && (response == "OK")), @@ -247,13 +231,11 @@ void DataReductionProxySettingsTestBase::CheckProbe( void DataReductionProxySettingsTestBase::CheckProbeOnIPChange( const std::string& probe_url, - const std::string& warmup_url, const std::string& response, bool request_succeeded, bool expected_restricted, bool expected_fallback_restricted) { SetProbeResult(probe_url, - warmup_url, response, FetchResult(!settings_->restricted_by_carrier_, request_succeeded && (response == "OK")), @@ -270,7 +252,6 @@ void DataReductionProxySettingsTestBase::CheckOnPrefChange( bool managed) { // Always have a sucessful probe for pref change tests. SetProbeResult(kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", FetchResult(enabled, true), true, @@ -290,14 +271,13 @@ void DataReductionProxySettingsTestBase::CheckInitDataReductionProxy( bool enabled_at_startup) { base::MessageLoopForUI loop; SetProbeResult(kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", FetchResult(enabled_at_startup, true), true, enabled_at_startup ? 1 : 0); scoped_ptr configurator( new TestDataReductionProxyConfig()); - settings_->SetProxyConfigurator(configurator.Pass()); + settings_->SetProxyConfigurator(configurator.get()); scoped_refptr request_context = new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()); diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.h b/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.h index 0ece527aee589..3cd6fdb8bdc33 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_settings_test_utils.h @@ -69,7 +69,6 @@ class MockDataReductionProxySettings : public C { TestDataReductionProxyParams::HAS_EVERYTHING & ~TestDataReductionProxyParams::HAS_DEV_ORIGIN)) {} MOCK_METHOD0(GetURLFetcherForAvailabilityCheck, net::URLFetcher*()); - MOCK_METHOD0(GetURLFetcherForWarmup, net::URLFetcher*()); MOCK_METHOD0(GetOriginalProfilePrefs, PrefService*()); MOCK_METHOD0(GetLocalStatePrefs, PrefService*()); MOCK_METHOD3(LogProxyState, void( @@ -126,13 +125,11 @@ class DataReductionProxySettingsTestBase : public testing::Test { template void SetProbeResult( const std::string& test_url, - const std::string& warmup_test_url, const std::string& response, ProbeURLFetchResult state, bool success, int expected_calls); virtual void SetProbeResult(const std::string& test_url, - const std::string& warmup_test_url, const std::string& response, ProbeURLFetchResult result, bool success, @@ -143,14 +140,12 @@ class DataReductionProxySettingsTestBase : public testing::Test { bool expected_fallback_restricted); void CheckProbe(bool initially_enabled, const std::string& probe_url, - const std::string& warmup_url, const std::string& response, bool request_success, bool expected_enabled, bool expected_restricted, bool expected_fallback_restricted); void CheckProbeOnIPChange(const std::string& probe_url, - const std::string& warmup_url, const std::string& response, bool request_success, bool expected_enabled, @@ -162,6 +157,7 @@ class DataReductionProxySettingsTestBase : public testing::Test { } TestingPrefServiceSimple pref_service_; + scoped_ptr configurator_; scoped_ptr settings_; scoped_ptr expected_params_; base::Time last_update_time_; @@ -186,14 +182,12 @@ class ConcreteDataReductionProxySettingsTest } virtual void SetProbeResult(const std::string& test_url, - const std::string& warmup_test_url, const std::string& response, ProbeURLFetchResult result, bool success, int expected_calls) OVERRIDE { return DataReductionProxySettingsTestBase::SetProbeResult( test_url, - warmup_test_url, response, result, success, diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc index f4619250945c7..a192d9a32201c 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_settings_unittest.cc @@ -23,7 +23,6 @@ namespace { const char kProbeURLWithOKResponse[] = "http://ok.org/"; const char kProbeURLWithBadResponse[] = "http://bad.org/"; const char kProbeURLWithNoResponse[] = "http://no.org/"; -const char kWarmupURLWithNoContentResponse[] = "http://warm.org/"; } // namespace @@ -239,7 +238,6 @@ TEST_F(DataReductionProxySettingsTest, TestMaybeActivateDataReductionProxy) { // Request succeeded but with bad response, expect proxy to be restricted. CheckProbe(true, kProbeURLWithBadResponse, - kWarmupURLWithNoContentResponse, "Bad", true, true, @@ -248,7 +246,6 @@ TEST_F(DataReductionProxySettingsTest, TestMaybeActivateDataReductionProxy) { // Request succeeded with valid response, expect proxy to be unrestricted. CheckProbe(true, kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", true, true, @@ -257,7 +254,6 @@ TEST_F(DataReductionProxySettingsTest, TestMaybeActivateDataReductionProxy) { // Request failed, expect proxy to be enabled but restricted. CheckProbe(true, kProbeURLWithNoResponse, - kWarmupURLWithNoContentResponse, "", false, true, @@ -267,7 +263,6 @@ TEST_F(DataReductionProxySettingsTest, TestMaybeActivateDataReductionProxy) { // state. CheckProbe(false, kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", true, false, @@ -291,28 +286,24 @@ TEST_F(DataReductionProxySettingsTest, TestOnIPAddressChanged) { // IP address change triggers a probe that succeeds. Proxy remains // unrestricted. CheckProbeOnIPChange(kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", true, false, false); // IP address change triggers a probe that fails. Proxy is restricted. CheckProbeOnIPChange(kProbeURLWithBadResponse, - kWarmupURLWithNoContentResponse, "Bad", true, true, false); // IP address change triggers a probe that fails. Proxy remains restricted. CheckProbeOnIPChange(kProbeURLWithBadResponse, - kWarmupURLWithNoContentResponse, "Bad", true, true, false); // IP address change triggers a probe that succeeds. Proxy is unrestricted. CheckProbeOnIPChange(kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", true, false, @@ -343,7 +334,6 @@ TEST_F(DataReductionProxySettingsTest, TestOnIPAddressChanged) { 0 /* network prefix */ )); CheckProbeOnIPChange(kProbeURLWithOKResponse, - kWarmupURLWithNoContentResponse, "OK", true, false, @@ -417,7 +407,7 @@ TEST_F(DataReductionProxySettingsTest, CheckInitMetricsWhenNotAllowed) { scoped_ptr configurator( new TestDataReductionProxyConfig()); - settings_->SetProxyConfigurator(configurator.Pass()); + settings_->SetProxyConfigurator(configurator.get()); scoped_refptr request_context = new net::TestURLRequestContextGetter(base::MessageLoopProxy::current()); settings_->InitDataReductionProxySettings( diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.cc new file mode 100644 index 0000000000000..3cb822ab4ceb8 --- /dev/null +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.cc @@ -0,0 +1,384 @@ +// 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/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h" + +#include +#include + +#include "base/base64.h" +#include "base/md5.h" +#include "base/metrics/histogram.h" +#include "base/metrics/sparse_histogram.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_util.h" + +#if defined(OS_ANDROID) +#include "net/android/network_library.h" +#endif + +// Macro for UMA reporting. HTTP response first reports to histogram events +// |http_histogram| by |carrier_id|; then reports the total counts to +// |http_histogram|_Total. HTTPS response reports to histograms +// |https_histogram| and |https_histogram|_Total similarly. +#define REPORT_TAMPER_DETECTION_UMA( \ + scheme_is_https, https_histogram, http_histogram, carrier_id) \ + do { \ + if (scheme_is_https) { \ + UMA_HISTOGRAM_SPARSE_SLOWLY(https_histogram, carrier_id); \ + UMA_HISTOGRAM_COUNTS(https_histogram "_Total", 1); \ + } else { \ + UMA_HISTOGRAM_SPARSE_SLOWLY(http_histogram, carrier_id); \ + UMA_HISTOGRAM_COUNTS(http_histogram "_Total", 1); \ + }\ + } while (0) + +namespace data_reduction_proxy { + +// static +bool DataReductionProxyTamperDetection::DetectAndReport( + const net::HttpResponseHeaders* headers, + const bool scheme_is_https) { + DCHECK(headers); + // Abort tamper detection, if the fingerprint of the Chrome-Proxy header is + // absent. + std::string chrome_proxy_fingerprint; + if (!GetDataReductionProxyActionFingerprintChromeProxy( + headers, &chrome_proxy_fingerprint)) { + return false; + } + + // Get carrier ID. + unsigned carrier_id = 0; +#if defined(OS_ANDROID) + base::StringToUint(net::android::GetTelephonyNetworkOperator(), &carrier_id); +#endif + + DataReductionProxyTamperDetection tamper_detection( + headers, scheme_is_https, carrier_id); + + // Checks if the Chrome-Proxy header has been tampered with. + if (tamper_detection.ValidateChromeProxyHeader(chrome_proxy_fingerprint)) { + tamper_detection.ReportUMAforChromeProxyHeaderValidation(); + return true; + } + + // 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); + + bool tampered = false; + std::string fingerprint; + + if (GetDataReductionProxyActionFingerprintVia(headers, &fingerprint)) { + bool has_chrome_proxy_via_header; + if (tamper_detection.ValidateViaHeader( + fingerprint, &has_chrome_proxy_via_header)) { + tamper_detection.ReportUMAforViaHeaderValidation( + has_chrome_proxy_via_header); + tampered = true; + } + } + + if (GetDataReductionProxyActionFingerprintOtherHeaders( + headers, &fingerprint)) { + if (tamper_detection.ValidateOtherHeaders(fingerprint)) { + tamper_detection.ReportUMAforOtherHeadersValidation(); + tampered = true; + } + } + + if (GetDataReductionProxyActionFingerprintContentLength( + headers, &fingerprint)) { + if (tamper_detection.ValidateContentLengthHeader(fingerprint)) { + tamper_detection.ReportUMAforContentLengthHeaderValidation(); + tampered = true; + } + } + + if (!tampered) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https, + "DataReductionProxy.HeaderTamperDetectionPassHTTPS", + "DataReductionProxy.HeaderTamperDetectionPassHTTP", + carrier_id); + } + + return tampered; +} + +// Constructor initializes the map of fingerprint names to codes. +DataReductionProxyTamperDetection::DataReductionProxyTamperDetection( + const net::HttpResponseHeaders* headers, + const bool is_secure, + const unsigned carrier_id) + : response_headers_(headers), + scheme_is_https_(is_secure), + carrier_id_(carrier_id) { + DCHECK(headers); +} + +DataReductionProxyTamperDetection::~DataReductionProxyTamperDetection() {}; + +// |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. +bool DataReductionProxyTamperDetection::ValidateChromeProxyHeader( + const std::string& fingerprint) const { + std::string received_fingerprint; + if (!base::Base64Decode(fingerprint, &received_fingerprint)) + return true; + + // Gets the Chrome-Proxy header values with its fingerprint removed. + std::vector chrome_proxy_header_values; + GetDataReductionProxyHeaderWithFingerprintRemoved( + response_headers_, &chrome_proxy_header_values); + + // Calculates the MD5 hash value of Chrome-Proxy. + std::string actual_fingerprint; + GetMD5(ValuesToSortedString(&chrome_proxy_header_values), + &actual_fingerprint); + + return received_fingerprint != actual_fingerprint; +} + +void DataReductionProxyTamperDetection:: + ReportUMAforChromeProxyHeaderValidation() const { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_ChromeProxy", + "DataReductionProxy.HeaderTamperedHTTP_ChromeProxy", + carrier_id_); +} + +// Checks whether there are other proxies/middleboxes' named after the data +// reduction proxy's name in Via header. |has_chrome_proxy_via_header| marks +// that whether the data reduction proxy's Via header occurs or not. +bool DataReductionProxyTamperDetection::ValidateViaHeader( + const std::string& fingerprint, + bool* has_chrome_proxy_via_header) const { + bool has_intermediary; + *has_chrome_proxy_via_header = HasDataReductionProxyViaHeader( + response_headers_, + &has_intermediary); + + if (*has_chrome_proxy_via_header) + return !has_intermediary; + return true; +} + +void DataReductionProxyTamperDetection::ReportUMAforViaHeaderValidation( + bool has_chrome_proxy) const { + // The Via header of the data reduction proxy is missing. + if (!has_chrome_proxy) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_Via_Missing", + "DataReductionProxy.HeaderTamperedHTTP_Via_Missing", + carrier_id_); + return; + } + + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_Via", + "DataReductionProxy.HeaderTamperedHTTP_Via", + carrier_id_); +} + +// The data reduction proxy constructs a canonical representation of values of +// a list of headers. The fingerprint is constructed as follows: +// 1) for each header, gets the string representation of its values (same as +// ValuesToSortedString); +// 2) concatenates all header's string representations using ";" as a delimiter; +// 3) calculates the MD5 hash value of above concatenated string; +// 4) appends the header names to the fingerprint, with a delimiter "|". +// The constructed fingerprint looks like: +// [hashed_fingerprint]|header_name1|header_namer2:... +// +// To check whether such a fingerprint matches the response that the Chromium +// client receives, the client firstly extracts the header names. For +// each header, gets its string representation (by ValuesToSortedString), +// concatenates them and calculates the MD5 hash value. Compares the hash +// value to the fingerprint received from the data reduction proxy. +bool DataReductionProxyTamperDetection::ValidateOtherHeaders( + const std::string& fingerprint) const { + DCHECK(!fingerprint.empty()); + + // According to RFC 2616, "|" is not a valid character in a header name; and + // it is not a valid base64 encoding character, so there is no ambituity in + //using it as a delimiter. + net::HttpUtil::ValuesIterator it( + fingerprint.begin(), fingerprint.end(), '|'); + + // The first value is the base64 encoded fingerprint. + std::string received_fingerprint; + if (!it.GetNext() || + !base::Base64Decode(it.value(), &received_fingerprint)) { + NOTREACHED(); + return true; + } + + std::string header_values; + // The following values are the header names included in the fingerprint + // calculation. + while (it.GetNext()) { + // Gets values of one header. + std::vector response_header_values = + GetHeaderValues(response_headers_, it.value()); + // Sorts the values and concatenate them, with delimiter ";". ";" can occur + // in a header value and thus two different sets of header values could map + // to the same string representation. This should be very rare. + // TODO(xingx): find an unambiguous representation. + header_values += ValuesToSortedString(&response_header_values) + ";"; + } + + // Calculates the MD5 hash of the concatenated string. + std::string actual_fingerprint; + GetMD5(header_values, &actual_fingerprint); + + return received_fingerprint != actual_fingerprint; +} + +void DataReductionProxyTamperDetection:: + ReportUMAforOtherHeadersValidation() const { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_OtherHeaders", + "DataReductionProxy.HeaderTamperedHTTP_OtherHeaders", + carrier_id_); +} + +// The Content-Length value will not be reported as different if at either side +// (the data reduction proxy side and the client side), the Content-Length is +// missing or it cannot be decoded as a valid integer. +bool DataReductionProxyTamperDetection::ValidateContentLengthHeader( + const std::string& fingerprint) const { + int received_content_length_fingerprint, actual_content_length; + // Abort, if Content-Length value from the data reduction proxy does not + // exist or it cannot be converted to an integer. + if (!base::StringToInt(fingerprint, &received_content_length_fingerprint)) + return false; + + std::string actual_content_length_string; + // Abort, if there is no Content-Length header received. + if (!response_headers_->GetNormalizedHeader("Content-Length", + &actual_content_length_string)) { + return false; + } + + // Abort, if the Content-Length value cannot be converted to integer. + if (!base::StringToInt(actual_content_length_string, + &actual_content_length)) { + return false; + } + + return received_content_length_fingerprint != actual_content_length; +} + +void DataReductionProxyTamperDetection:: + ReportUMAforContentLengthHeaderValidation() const { + // Gets MIME type of the response and reports to UMA histograms separately. + // Divides MIME types into 4 groups: JavaScript, CSS, Images, and others. + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_ContentLength", + "DataReductionProxy.HeaderTamperedHTTP_ContentLength", + carrier_id_); + + // Gets MIME type. + std::string mime_type; + response_headers_->GetMimeType(&mime_type); + + std::string JS1 = "text/javascript"; + std::string JS2 = "application/x-javascript"; + std::string JS3 = "application/javascript"; + std::string CSS = "text/css"; + std::string IMAGE = "image/"; + + size_t mime_type_size = mime_type.size(); + if ((mime_type_size >= JS1.size() && LowerCaseEqualsASCII(mime_type.begin(), + mime_type.begin() + JS1.size(), JS1.c_str())) || + (mime_type_size >= JS2.size() && LowerCaseEqualsASCII(mime_type.begin(), + mime_type.begin() + JS2.size(), JS2.c_str())) || + (mime_type_size >= JS3.size() && LowerCaseEqualsASCII(mime_type.begin(), + mime_type.begin() + JS3.size(), JS3.c_str()))) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_JS", + "DataReductionProxy.HeaderTamperedHTTP_ContentLength_JS", + carrier_id_); + } else if (mime_type_size >= CSS.size() && + LowerCaseEqualsASCII(mime_type.begin(), + mime_type.begin() + CSS.size(), CSS.c_str())) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_CSS", + "DataReductionProxy.HeaderTamperedHTTP_ContentLength_CSS", + carrier_id_); + } else if (mime_type_size >= IMAGE.size() && + LowerCaseEqualsASCII(mime_type.begin(), + mime_type.begin() + IMAGE.size(), IMAGE.c_str())) { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Image", + "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Image", + carrier_id_); + } else { + REPORT_TAMPER_DETECTION_UMA( + scheme_is_https_, + "DataReductionProxy.HeaderTamperedHTTPS_ContentLength_Other", + "DataReductionProxy.HeaderTamperedHTTP_ContentLength_Other", + carrier_id_); + } +} + +// We construct a canonical representation of the header so that reordered +// header values will produce the same fingerprint. The fingerprint is +// constructed as follows: +// 1) sort the values; +// 2) concatenate sorted values with a "," delimiter. +std::string DataReductionProxyTamperDetection::ValuesToSortedString( + std::vector* values) { + std::string concatenated_values; + DCHECK(values); + if (!values) return ""; + + std::sort(values->begin(), values->end()); + for (size_t i = 0; i < values->size(); ++i) { + // Concatenates with delimiter ",". + concatenated_values += (*values)[i] + ","; + } + return concatenated_values; +} + +void DataReductionProxyTamperDetection::GetMD5( + const std::string& input, std::string* output) { + base::MD5Digest digest; + base::MD5Sum(input.c_str(), input.size(), &digest); + *output = std::string( + reinterpret_cast(digest.a), ARRAYSIZE_UNSAFE(digest.a)); +} + +std::vector DataReductionProxyTamperDetection::GetHeaderValues( + const net::HttpResponseHeaders* headers, + const std::string& header_name) { + std::vector values; + std::string value; + void* iter = NULL; + while (headers->EnumerateHeader(&iter, header_name, &value)) { + values.push_back(value); + } + return values; +} + +} // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h b/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h new file mode 100644 index 0000000000000..b4021fcc8c025 --- /dev/null +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h @@ -0,0 +1,155 @@ +// 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. + +// This file implements the tamper detection logic, which detects whether +// there are middleboxes and whether they are tampering with the response +// which may break correct communication and data transfer between the Chromium +// client and the data reduction proxy. +// +// At a high level, the tamper detection process works in two steps: +// 1. The data reduction proxy selects a fraction of responses to analyze, +// generates a series of fingerprints for each, and appends them to the +// Chrome-Proxy response headers; +// 2. The client re-generate the fingerprints using the same method as the +// proxy, compares them to the fingerprints in the response, and generates +// UMA. A response is considered to have been tampered with if the +// fingerprints do not match. +// +// Four fingerprints are generated by the data reduction proxy: +// 1. Fingerprint of the Chrome-Proxy header, which is designed to check +// whether the Chrome-Proxy header has been modified or not; +// 2. Fingerprint of the Via header, which is designed to check whether there +// are middleboxes between the Chromium client and the data reduction proxy; +// 3. Fingerprint of a list of headers, which is designed to check whether the +// values of a list of headers (list is defined by the data reduction proxy) +// have been modified or deleted; +// 4. Fingerprint of the Content-Length header, which is designed to check +// whether the response body has been modified or not (the code assumes that +// different Content-Length values indicate different response bodies). +// +// On the client side, the fingerprint of the Chrome-Proxy header will be +// checked first. If the fingerprint indicates that the Chrome-Proxy header has +// not been modified, then the other fingerprints will be considered to be +// reliable and will be checked next; if not, then it's possible that the other +// fingerprints have been tampered with and thus they will not be checked. +// If middlebox removes all the fingerprints then such tampering will not be +// detected. +// +// Detected tampering information will be reported to UMA. In general, for each +// fingerprint, the client reports the number of responses that have been +// tampered with for different carriers. For the fingerprint of the +// Content-Length header, which indicates whether the response body has been +// modified or not, the reports of tampering are separated by MIME type of the +// response body. + +#ifndef COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_ +#define COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_ + +#include +#include +#include + +#include "net/proxy/proxy_service.h" + +namespace net { +class HttpResponseHeaders; +} + +namespace data_reduction_proxy { + +// Detects if the response sent by the data reduction proxy has been modified +// by intermediaries on the Web. +class DataReductionProxyTamperDetection { + public: + // Checks if the response contains tamper detection fingerprints added by the + // data reduction proxy, and determines if the response had been tampered + // with if so. Results are reported to UMA. HTTP and HTTPS traffic are + // reported separately, specified by |scheme_is_https|. Returns true if + // the response has been tampered with. + static bool DetectAndReport(const net::HttpResponseHeaders* headers, + bool scheme_is_https); + + // Tamper detection checks |response_headers|. Histogram events are reported + // by |carrier_id|; |scheme_is_https| determines which histogram to report + // (HTTP and HTTPS are reported separately). + DataReductionProxyTamperDetection( + const net::HttpResponseHeaders* response_headers, + bool scheme_is_https, + unsigned carrier_id); + + virtual ~DataReductionProxyTamperDetection(); + + private: + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + TestFingerprintCommon); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + ChromeProxy); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + Via); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + OtherHeaders); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + ContentLength); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + HeaderRemoving); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + ValuesToSortedString); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + GetHeaderValues); + FRIEND_TEST_ALL_PREFIXES(DataReductionProxyTamperDetectionTest, + DetectAndReport); + + // Returns the result of validating Chrome-Proxy header. + bool ValidateChromeProxyHeader(const std::string& fingerprint) const; + + // Reports UMA for tampering of the Chrome-Proxy header. + void ReportUMAforChromeProxyHeaderValidation() const; + + // Returns the result of validating the Via header. |has_chrome_proxy| + // indicates that the data reduction proxy's Via header occurs or not. + bool ValidateViaHeader(const std::string& fingerprint, + bool* has_chrome_proxy_via_header) const; + + // Reports UMA for tampering of the Via header. + void ReportUMAforViaHeaderValidation(bool has_chrome_proxy_via_header) const; + + // Returns the result of validating a list of headers. + bool ValidateOtherHeaders(const std::string& fingerprint) const; + + // Reports UMA for tampering of values of the list of headers. + void ReportUMAforOtherHeadersValidation() const; + + // Returns the result of validating the Content-Length header. + bool ValidateContentLengthHeader(const std::string& fingerprint) const; + + // Reports UMA for tampering of the Content-Length header. + void ReportUMAforContentLengthHeaderValidation() const; + + // Returns a string representation of |values|. + static std::string ValuesToSortedString(std::vector* values); + + // Returns raw MD5 hash value for a given string |input|. It is different to + // base::MD5String which is base16 encoded. + static void GetMD5(const std::string& input, std::string* output); + + // Returns all the values of |header_name| of the response |headers| as a + // vector. This function is used for values that need to be sorted later. + static std::vector GetHeaderValues( + const net::HttpResponseHeaders* headers, + const std::string& header_name); + + // Pointer to response headers. + const net::HttpResponseHeaders* response_headers_; + + // If true, the connection to the data reduction proxy is over HTTPS; + const bool scheme_is_https_; + + // Carrier ID: the numeric name of the current registered operator. + const unsigned carrier_id_; + + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyTamperDetection); +}; + +} // namespace data_reduction_proxy +#endif // COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_TAMPER_DETECTION_H_ diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection_unittest.cc new file mode 100644 index 0000000000000..0ab9ac83647db --- /dev/null +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection_unittest.cc @@ -0,0 +1,756 @@ +// 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/data_reduction_proxy/browser/data_reduction_proxy_tamper_detection.h" + +#include +#include +#include +#include + +#include "base/base64.h" +#include "base/md5.h" +#include "base/memory/scoped_ptr.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" +#include "net/http/http_response_headers.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_ANDROID) +#include "base/android/jni_android.h" +#include "net/android/network_library.h" +#endif + +namespace { + +void HeadersToRaw(std::string* headers) { + std::replace(headers->begin(), headers->end(), '\n', '\0'); + if (!headers->empty()) + *headers += '\0'; +} + +// Calcuates MD5 hash value for a string and then base64 encode it. Testcases +// contain expected fingerprint in plain text, which needs to be encoded before +// comparison. +std::string GetEncoded(const std::string& input) { + base::MD5Digest digest; + base::MD5Sum(input.c_str(), input.size(), &digest); + std::string base64encoded; + base::Base64Encode(std::string((char*)digest.a, + ARRAYSIZE_UNSAFE(digest.a)), &base64encoded); + return base64encoded; +} + +// Replaces all contents within "[]" by corresponding base64 encoded MD5 value. +// It can handle nested case like: [[abc]def]. This helper function transforms +// fingerprint in plain text to actual encoded fingerprint. +void ReplaceWithEncodedString(std::string* input) { + size_t start, end, temp; + while (true) { + start = input->find("["); + if (start == std::string::npos) break; + while (true) { + temp = input->find("[", start + 1); + end = input->find("]", start + 1); + if (end != std::string::npos && end < temp) + break; + + start = temp; + } + std::string need_to_encode = input->substr(start + 1, end - start - 1); + *input = input->substr(0, start) + GetEncoded(need_to_encode) + + input->substr(end + 1); + } +} + +// Returns a vector contains all the values from a comma-separated string. +// Some testcases contain string representation of a vector, this helper +// function generates a vector from a input string. +std::vector StringsToVector(const std::string& values) { + std::vector ret; + if (values.empty()) + return ret; + size_t now = 0; + size_t next; + while ((next = values.find(",", now)) != std::string::npos) { + ret.push_back(values.substr(now, next - now)); + now = next + 1; + } + return ret; +} + +void InitEnv() { +#if defined(OS_ANDROID) + JNIEnv* env = base::android::AttachCurrentThread(); + static bool inited = false; + if (!inited) { + net::android::RegisterNetworkLibrary(env); + inited = true; + } +#endif +} + +} // namespace + +namespace data_reduction_proxy { + +class DataReductionProxyTamperDetectionTest : public testing::Test { + +}; + +// Tests function ValidateChromeProxyHeader. +TEST_F(DataReductionProxyTamperDetectionTest, ChromeProxy) { + // |received_fingerprint| is not the actual fingerprint from data reduction + // proxy, instead, the base64 encoded field is in plain text (within "[]") + // and needs to be encoded first. + struct { + std::string label; + std::string raw_header; + std::string received_fingerprint; + bool expected_tampered_with; + } test[] = { + { + "Checks sorting.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: c,b,a,3,2,1,fcp=f\n", + "[1,2,3,a,b,c,]", + false, + }, + { + "Checks Chrome-Proxy's fingerprint removing.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a,b,c,d,e,3,2,1,fcp=f\n", + "[1,2,3,a,b,c,d,e,]", + false, + }, + { + "Checks no Chrome-Proxy header case (should not happen).", + "HTTP/1.1 200 OK\n", + "[]", + false, + }, + { + "Checks empty Chrome-Proxy header case (should not happen).", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: \n", + "[,]", + false, + }, + { + "Checks Chrome-Proxy header with its fingerprint only case.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: fcp=f\n", + "[]", + false, + }, + { + "Checks empty Chrome-Proxy header case, with extra ',' and ' '", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: fcp=f , \n", + "[]", + false, + }, + { + "Changed no value to empty value.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: fcp=f\n", + "[,]", + true, + }, + { + "Changed header values.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a,b=2,c,d=1,fcp=f\n", + "[a,b=3,c,d=1,]", + true, + }, + { + "Changed order of header values.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: c,b,a,fcp=1\n", + "[c,b,a,]", + true, + }, + { + "Checks Chrome-Proxy header with extra ' '.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a , b , c, d, fcp=f\n", + "[a,b,c,d,]", + false + }, + { + "Check Chrome-Proxy header with multiple lines and ' '.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a , c , d, fcp=f \n" + "Chrome-Proxy: b \n", + "[a,b,c,d,]", + false + }, + { + "Checks Chrome-Proxy header with multiple lines, at different positions", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a \n" + "Chrome-Proxy: c \n" + "Content-Type: 1\n" + "Cache-Control: 2\n" + "ETag: 3\n" + "Chrome-Proxy: b \n" + "Connection: 4\n" + "Expires: 5\n" + "Chrome-Proxy: fcp=f \n" + "Via: \n" + "Content-Length: 12345\n", + "[a,b,c,]", + false + }, + { + "Checks Chrome-Proxy header with multiple same values.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a \n" + "Chrome-Proxy: b\n" + "Chrome-Proxy: c\n" + "Chrome-Proxy: d, fcp=f \n" + "Chrome-Proxy: a \n", + "[a,a,b,c,d,]", + false + }, + { + "Changed Chrome-Proxy header with multiple lines..", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a\n" + "Chrome-Proxy: a\n" + "Chrome-Proxy: b\n" + "Chrome-Proxy: c,fcp=f\n", + "[a,b,c,]", + true, + }, + { + "Checks case whose received fingerprint is empty.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a,b,c,fcp=1\n", + "[]", + true, + }, + { + "Checks case whose received fingerprint cannot be base64 decoded.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: a,b,c,fcp=1\n", + "not_base64_encoded", + true, + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + ReplaceWithEncodedString(&test[i].received_fingerprint); + + std::string raw_headers(test[i].raw_header); + HeadersToRaw(&raw_headers); + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + + DataReductionProxyTamperDetection tamper_detection(headers, true, 0); + + bool tampered = tamper_detection.ValidateChromeProxyHeader( + test[i].received_fingerprint); + + EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label; + } +} + +// Tests function ValidateViaHeader. +TEST_F(DataReductionProxyTamperDetectionTest, Via) { + struct { + std::string label; + std::string raw_header; + std::string received_fingerprint; + bool expected_tampered_with; + bool expected_has_chrome_proxy_via_header; + } test[] = { + { + "Checks the case that Chrome-Compression-Proxy occurs at the last.", + "HTTP/1.1 200 OK\n" + "Via: a, b, c, 1.1 Chrome-Compression-Proxy\n", + "", + false, + true, + }, + { + "Checks when there is intermediary.", + "HTTP/1.1 200 OK\n" + "Via: a, b, c, 1.1 Chrome-Compression-Proxy, xyz\n", + "", + true, + true, + }, + { + "Checks the case of empty Via header.", + "HTTP/1.1 200 OK\n" + "Via: \n", + "", + true, + false, + }, + { + "Checks the case that only the data reduction proxy's Via header occurs.", + "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Compression-Proxy \n", + "", + false, + true, + }, + { + "Checks the case that there are ' ', i.e., empty value after the data" + " reduction proxy's Via header.", + "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Compression-Proxy , , \n", + "", + false, + true, + }, + { + "Checks the case when there is no Via header", + "HTTP/1.1 200 OK\n", + "", + true, + false, + }, + // Same to above test cases, but with deprecated data reduciton proxy Via + // header. + { + "Checks the case that Chrome Compression Proxy occurs at the last.", + "HTTP/1.1 200 OK\n" + "Via: a, b, c, 1.1 Chrome Compression Proxy\n", + "", + false, + true, + }, + { + "Checks when there is intermediary.", + "HTTP/1.1 200 OK\n" + "Via: a, b, c, 1.1 Chrome Compression Proxy, xyz\n", + "", + true, + true, + }, + { + "Checks the case of empty Via header.", + "HTTP/1.1 200 OK\n" + "Via: \n", + "", + true, + false, + }, + { + "Checks the case that only the data reduction proxy's Via header occurs.", + "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome Compression Proxy \n", + "", + false, + true, + }, + { + "Checks the case that there are ' ', i.e., empty value after the data" + "reduction proxy's Via header.", + "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome Compression Proxy , , \n", + "", + false, + true, + }, + { + "Checks the case when there is no Via header", + "HTTP/1.1 200 OK\n", + "", + true, + false, + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + std::string raw_headers(test[i].raw_header); + HeadersToRaw(&raw_headers); + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + + DataReductionProxyTamperDetection tamper_detection(headers, true, 0); + + bool has_chrome_proxy_via_header; + bool tampered = tamper_detection.ValidateViaHeader( + test[i].received_fingerprint, &has_chrome_proxy_via_header); + + EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label; + EXPECT_EQ(test[i].expected_has_chrome_proxy_via_header, + has_chrome_proxy_via_header) << test[i].label; + } +} + +// Tests function ValidateOtherHeaders. +TEST_F(DataReductionProxyTamperDetectionTest, OtherHeaders) { + // For following testcases, |received_fingerprint| is not the actual + // fingerprint from data reduction proxy, instead, the base64 encoded field + // is in plain text (within "[]") and needs to be encoded first. For example, + // "[12345;]|content-length" needs to be encoded to + // "Base64Encoded(MD5(12345;))|content-length" before calling the checking + // function. + struct { + std::string label; + std::string raw_header; + std::string received_fingerprint; + bool expected_tampered_with; + } test[] = { + { + "Checks the case that only one header is requested.", + "HTTP/1.1 200 OK\n" + "Content-Length: 12345\n", + "[12345,;]|content-length", + false + }, + { + "Checks the case that there is only one requested header and it does not" + "exist.", + "HTTP/1.1 200 OK\n", + "[;]|non_exist_header", + false + }, + { + "Checks the case of multiple headers are requested.", + "HTTP/1.1 200 OK\n" + "Content-Type: 1\n" + "Cache-Control: 2\n" + "ETag: 3\n" + "Connection: 4\n" + "Expires: 5\n", + "[1,;2,;3,;4,;5,;]|content-type|cache-control|etag|connection|expires", + false + }, + { + "Checks the case that one header has multiple values.", + "HTTP/1.1 200 OK\n" + "Content-Type: aaa1, bbb1, ccc1\n" + "Cache-Control: aaa2\n", + "[aaa1,bbb1,ccc1,;aaa2,;]|content-type|cache-control", + false + }, + { + "Checks the case that one header has multiple lines.", + "HTTP/1.1 200 OK\n" + "Content-Type: aaa1, ccc1\n" + "Content-Type: xxx1, bbb1, ccc1\n" + "Cache-Control: aaa2\n", + "[aaa1,bbb1,ccc1,ccc1,xxx1,;aaa2,;]|content-type|cache-control", + false + }, + { + "Checks the case that more than one headers have multiple values.", + "HTTP/1.1 200 OK\n" + "Content-Type: aaa1, ccc1\n" + "Cache-Control: ccc2 , bbb2\n" + "Content-Type: bbb1, ccc1\n" + "Cache-Control: aaa2 \n", + "[aaa1,bbb1,ccc1,ccc1,;aaa2,bbb2,ccc2,;]|content-type|cache-control", + false + }, + { + "Checks the case that one of the requested headers is missing (Expires).", + "HTTP/1.1 200 OK\n" + "Content-Type: aaa1, ccc1\n", + "[aaa1,ccc1,;;]|content-type|expires", + false + }, + { + "Checks the case that some of the requested headers have empty value.", + "HTTP/1.1 200 OK\n" + "Content-Type: \n" + "Cache-Control: \n", + "[,;,;]|content-type|cache-control", + false + }, + { + "Checks the case that all the requested headers are missing.", + "HTTP/1.1 200 OK\n", + "[;;]|content-type|expires", + false + }, + { + "Checks the case that some headers are missing, some of them are empty.", + "HTTP/1.1 200 OK\n" + "Cache-Control: \n", + "[;,;]|content-type|cache-control", + false + }, + { + "Checks the case there is no requested header (header list is empty).", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: aut=aauutthh,bbbypas=0,aaxxx=xxx,bbbloc=1\n" + "Content-Type: 1\n" + "Cache-Control: 2\n", + "[]", + false + }, + { + "Checks tampered requested header values.", + "HTTP/1.1 200 OK\n" + "Content-Type: aaa1, ccc1\n" + "Cache-Control: ccc2 , bbb2\n", + "[aaa1,bbb1,;bbb2,ccc2,;]|content-type|cache-control", + true + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + ReplaceWithEncodedString(&test[i].received_fingerprint); + + std::string raw_headers(test[i].raw_header); + HeadersToRaw(&raw_headers); + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + + DataReductionProxyTamperDetection tamper_detection(headers, true, 0); + + bool tampered = tamper_detection.ValidateOtherHeaders( + test[i].received_fingerprint); + + EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label; + } +} + +// Tests function ValidateContentLengthHeader. +TEST_F(DataReductionProxyTamperDetectionTest, ContentLength) { + struct { + std::string label; + std::string raw_header; + std::string received_fingerprint; + bool expected_tampered_with; + } test[] = { + { + "Checks the case fingerprint matches received response.", + "HTTP/1.1 200 OK\n" + "Content-Length: 12345\n", + "12345", + false, + }, + { + "Checks case that response got modified.", + "HTTP/1.1 200 OK\n" + "Content-Length: 12345\n", + "125", + true, + }, + { + "Checks the case that the data reduction proxy has not sent" + "Content-Length header.", + "HTTP/1.1 200 OK\n" + "Content-Length: 12345\n", + "", + false, + }, + { + "Checks the case that the data reduction proxy sends invalid" + "Content-Length header.", + "HTTP/1.1 200 OK\n" + "Content-Length: 12345\n", + "aaa", + false, + }, + { + "Checks the case that the data reduction proxy sends invalid" + "Content-Length header.", + "HTTP/1.1 200 OK\n" + "Content-Length: aaa\n", + "aaa", + false, + }, + { + "Checks the case that Content-Length header is missing at the Chromium" + "client side.", + "HTTP/1.1 200 OK\n", + "123", + false, + }, + { + "Checks the case that Content-Length header are missing at both end.", + "HTTP/1.1 200 OK\n", + "", + false, + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + std::string raw_headers(test[i].raw_header); + HeadersToRaw(&raw_headers); + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + + DataReductionProxyTamperDetection tamper_detection(headers, true, 0); + + bool tampered = tamper_detection.ValidateContentLengthHeader( + test[i].received_fingerprint); + + EXPECT_EQ(test[i].expected_tampered_with, tampered) << test[i].label; + } +} + +// Tests ValuesToSortedString function. +TEST_F(DataReductionProxyTamperDetectionTest, ValuesToSortedString) { + struct { + std::string label; + std::string input_values; + std::string expected_output_string; + } test[] = { + { + "Checks the correctness of sorting.", + "3,2,1,", + "1,2,3,", + }, + { + "Checks the case that there is an empty input vector.", + "", + "", + }, + { + "Checks the case that there is an empty string in the input vector.", + ",", + ",", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + std::vector input_values = + StringsToVector(test[i].input_values); + std::string output_string = + DataReductionProxyTamperDetection::ValuesToSortedString(&input_values); + EXPECT_EQ(output_string, test[i].expected_output_string) << test[i].label; + } +} + +// Tests GetHeaderValues function. +TEST_F(DataReductionProxyTamperDetectionTest, GetHeaderValues) { + struct { + std::string label; + std::string raw_header; + std::string header_name; + std::string expected_output_values; + } test[] = { + { + "Checks the correctness of getting single line header.", + "HTTP/1.1 200 OK\n" + "test: 1, 2, 3\n", + "test", + "1,2,3,", + }, + { + "Checks the correctness of getting multiple lines header.", + "HTTP/1.1 200 OK\n" + "test: 1, 2, 3\n" + "test: 4, 5, 6\n" + "test: 7, 8, 9\n", + "test", + "1,2,3,4,5,6,7,8,9,", + }, + { + "Checks the correctness of getting missing header.", + "HTTP/1.1 200 OK\n", + "test", + "", + }, + { + "Checks the correctness of getting empty header.", + "HTTP/1.1 200 OK\n" + "test: \n", + "test", + ",", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + std::string raw_headers(test[i].raw_header); + HeadersToRaw(&raw_headers); + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + + std::vector expected_output_values = + StringsToVector(test[i].expected_output_values); + + std::vector output_values = + DataReductionProxyTamperDetection::GetHeaderValues( + headers, test[i].header_name); + EXPECT_EQ(expected_output_values, output_values) << test[i].label; + } +} + +// Tests main function DetectAndReport. +TEST_F(DataReductionProxyTamperDetectionTest, DetectAndReport) { + struct { + std::string label; + std::string raw_header; + bool expected_tampered_with; + } test[] = { + { + "Check no fingerprint added case.", + "HTTP/1.1 200 OK\n" + "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n" + "Content-Length: 12345\n" + "Chrome-Proxy: bypass=0\n", + false, + }, + { + "Check the case Chrome-Proxy fingerprint doesn't match.", + "HTTP/1.1 200 OK\n" + "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n" + "Content-Length: 12345\n" + "header1: header_1\n" + "header2: header_2\n" + "header3: header_3\n" + "Chrome-Proxy: fcl=12345, " + "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3,fvia=0," + "fcp=abc\n", + true, + }, + { + "Check the case response matches the fingerprint completely.", + "HTTP/1.1 200 OK\n" + "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n" + "Content-Length: 12345\n" + "header1: header_1\n" + "header2: header_2\n" + "header3: header_3\n" + "Chrome-Proxy: fcl=12345, " + "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3," + "fvia=0, fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]" + "|header1|header2|header3,fvia=0,]\n", + false, + }, + { + "Check the case that Content-Length doesn't match.", + "HTTP/1.1 200 OK\n" + "Via: a1, b2, 1.1 Chrome-Compression-Proxy\n" + "Content-Length: 0\n" + "header1: header_1\n" + "header2: header_2\n" + "header3: header_3\n" + "Chrome-Proxy: fcl=12345, " + "foh=[header_1,;header_2,;header_3,;]|header1|header2|header3, fvia=0, " + "fcp=[fcl=12345,foh=[header_1,;header_2,;header_3,;]|" + "header1|header2|header3,fvia=0,]\n", + true, + }, + }; + + InitEnv(); + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + std::string raw_headers(test[i].raw_header); + ReplaceWithEncodedString(&raw_headers); + HeadersToRaw(&raw_headers); + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + + EXPECT_EQ(test[i].expected_tampered_with, + DataReductionProxyTamperDetection::DetectAndReport(headers, true)) + << test[i].label; + } +} + +} // namespace diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc index d500301b25c72..bdbb9bfc76cee 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.cc @@ -4,7 +4,12 @@ #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/message_loop/message_loop_proxy.h" #include "base/metrics/histogram.h" +#include "base/metrics/sparse_histogram.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "net/base/net_errors.h" #include "net/proxy/proxy_retry_info.h" #include "net/proxy/proxy_server.h" @@ -21,17 +26,62 @@ using net::URLRequest; namespace data_reduction_proxy { +namespace { + +// Records a net error code that resulted in bypassing the data reduction +// proxy (|is_primary| is true) or the data reduction proxy fallback. +void RecordDataReductionProxyBypassOnNetworkError( + bool is_primary, + const ProxyServer& proxy_server, + int net_error) { + if (is_primary) { + UMA_HISTOGRAM_SPARSE_SLOWLY( + "DataReductionProxy.BypassOnNetworkErrorPrimary", + std::abs(net_error)); + return; + } + UMA_HISTOGRAM_SPARSE_SLOWLY( + "DataReductionProxy.BypassOnNetworkErrorFallback", + std::abs(net_error)); +} + +} // namespace + +// static +void DataReductionProxyUsageStats::RecordDataReductionProxyBypassInfo( + bool is_primary, + bool bypass_all, + const net::ProxyServer& proxy_server, + DataReductionProxyBypassType bypass_type) { + if (bypass_all) { + if (is_primary) { + UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BlockTypePrimary", + bypass_type, BYPASS_EVENT_TYPE_MAX); + } else { + UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BlockTypeFallback", + bypass_type, BYPASS_EVENT_TYPE_MAX); + } + } else { + if (is_primary) { + UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BypassTypePrimary", + bypass_type, BYPASS_EVENT_TYPE_MAX); + } else { + UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BypassTypeFallback", + bypass_type, BYPASS_EVENT_TYPE_MAX); + } + } +} + DataReductionProxyUsageStats::DataReductionProxyUsageStats( DataReductionProxyParams* params, - MessageLoopProxy* ui_thread_proxy, - MessageLoopProxy* io_thread_proxy) + MessageLoopProxy* ui_thread_proxy) : data_reduction_proxy_params_(params), - last_bypass_type_(ProxyService::BYPASS_EVENT_TYPE_MAX), + last_bypass_type_(BYPASS_EVENT_TYPE_MAX), triggering_request_(true), ui_thread_proxy_(ui_thread_proxy), - io_thread_proxy_(io_thread_proxy), eligible_num_requests_through_proxy_(0), - actual_num_requests_through_proxy_(0) { + actual_num_requests_through_proxy_(0), + unavailable_(false) { NetworkChangeNotifier::AddNetworkChangeObserver(this); }; @@ -41,47 +91,27 @@ DataReductionProxyUsageStats::~DataReductionProxyUsageStats() { void DataReductionProxyUsageStats::OnUrlRequestCompleted( const net::URLRequest* request, bool started) { - DCHECK(io_thread_proxy_->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); if (request->status().status() == net::URLRequestStatus::SUCCESS) { if (data_reduction_proxy_params_->IsDataReductionProxyEligible(request)) { bool was_received_via_proxy = data_reduction_proxy_params_->WasDataReductionProxyUsed( request, NULL); - ui_thread_proxy_->PostTask(FROM_HERE, base::Bind( - &DataReductionProxyUsageStats::IncRequestCountsOnUiThread, - base::Unretained(this), was_received_via_proxy)); + IncrementRequestCounts(was_received_via_proxy); } } } -/** - * Determines if the data reduction proxy is currently unreachable. We keep - * track of count of requests which go through data reduction proxy as well as - * count of requests which are eligible to go through the proxy. If and only if - * no requests go through proxy and some requests were eligible, we conclude - * that the proxy is unreachable. - * - * Returns true if the data reduction proxy is unreachable. - */ -bool DataReductionProxyUsageStats::isDataReductionProxyUnreachable() { - DCHECK(ui_thread_proxy_->BelongsToCurrentThread()); - - return eligible_num_requests_through_proxy_ > 0 && - actual_num_requests_through_proxy_ == 0; -} - void DataReductionProxyUsageStats::OnNetworkChanged( NetworkChangeNotifier::ConnectionType type) { DCHECK(thread_checker_.CalledOnValidThread()); - ui_thread_proxy_->PostTask(FROM_HERE, base::Bind( - &DataReductionProxyUsageStats::ClearRequestCountsOnUiThread, - base::Unretained(this))); + ClearRequestCounts(); } -void DataReductionProxyUsageStats::IncRequestCountsOnUiThread( +void DataReductionProxyUsageStats::IncrementRequestCounts( bool was_received_via_proxy) { - DCHECK(ui_thread_proxy_->BelongsToCurrentThread()); + DCHECK(thread_checker_.CalledOnValidThread()); if (was_received_via_proxy) { actual_num_requests_through_proxy_++; } @@ -91,26 +121,64 @@ void DataReductionProxyUsageStats::IncRequestCountsOnUiThread( // gets blocked, we reset the counts occasionally. if (eligible_num_requests_through_proxy_ > 50 && actual_num_requests_through_proxy_ > 0) { - ClearRequestCountsOnUiThread(); + ClearRequestCounts(); + } else { + MaybeNotifyUnavailability(); } } -void DataReductionProxyUsageStats::ClearRequestCountsOnUiThread() { - DCHECK(ui_thread_proxy_->BelongsToCurrentThread()); +void DataReductionProxyUsageStats::ClearRequestCounts() { + DCHECK(thread_checker_.CalledOnValidThread()); eligible_num_requests_through_proxy_ = 0; actual_num_requests_through_proxy_ = 0; + MaybeNotifyUnavailability(); +} + +void DataReductionProxyUsageStats::MaybeNotifyUnavailability() { + bool prev_unavailable = unavailable_; + unavailable_ = (eligible_num_requests_through_proxy_ > 0 && + actual_num_requests_through_proxy_ == 0); + if (prev_unavailable != unavailable_) { + ui_thread_proxy_->PostTask(FROM_HERE, base::Bind( + &DataReductionProxyUsageStats::NotifyUnavailabilityOnUIThread, + base::Unretained(this), + unavailable_)); + } +} + +void DataReductionProxyUsageStats::NotifyUnavailabilityOnUIThread( + bool unavailable) { + DCHECK(ui_thread_proxy_->BelongsToCurrentThread()); + if (!unavailable_callback_.is_null()) + unavailable_callback_.Run(unavailable); } void DataReductionProxyUsageStats::SetBypassType( - ProxyService::DataReductionProxyBypassType type) { + DataReductionProxyBypassType type) { last_bypass_type_ = type; triggering_request_ = true; } void DataReductionProxyUsageStats::RecordBypassedBytesHistograms( net::URLRequest& request, - const BooleanPrefMember& data_reduction_proxy_enabled) { + const BooleanPrefMember& data_reduction_proxy_enabled, + const net::ProxyConfig& data_reduction_proxy_config) { int64 content_length = request.received_response_content_length(); + + if (data_reduction_proxy_enabled.GetValue()) { + LOG(WARNING) << "managed pac: " << (!data_reduction_proxy_config.Equals( + request.context()->proxy_service()->config()) ? "true" : "false"); + } + + if (data_reduction_proxy_enabled.GetValue() && + !data_reduction_proxy_config.Equals( + request.context()->proxy_service()->config())) { + RecordBypassedBytes(last_bypass_type_, + DataReductionProxyUsageStats::MANAGED_PROXY_CONFIG, + content_length); + return; + } + if (data_reduction_proxy_params_->WasDataReductionProxyUsed(&request, NULL)) { RecordBypassedBytes(last_bypass_type_, DataReductionProxyUsageStats::NOT_BYPASSED, @@ -127,7 +195,8 @@ void DataReductionProxyUsageStats::RecordBypassedBytesHistograms( } if (data_reduction_proxy_enabled.GetValue() && - !data_reduction_proxy_params_->IsDataReductionProxyEligible(&request)) { + data_reduction_proxy_params_->IsBypassedByDataReductionProxyLocalRules( + request, data_reduction_proxy_config)) { RecordBypassedBytes(last_bypass_type_, DataReductionProxyUsageStats::LOCAL_BYPASS_RULES, content_length); @@ -135,24 +204,28 @@ void DataReductionProxyUsageStats::RecordBypassedBytesHistograms( } if (triggering_request_) { + // We only record when audio or video triggers a bypass. We don't care + // about audio and video bypassed as collateral damage. + std::string mime_type; + request.GetMimeType(&mime_type); + // MIME types are named by /. We check to see if the + // media type is audio or video. + if (mime_type.compare(0, 6, "audio/") == 0 || + mime_type.compare(0, 6, "video/") == 0) { + RecordBypassedBytes(last_bypass_type_, + DataReductionProxyUsageStats::AUDIO_VIDEO, + content_length); + return; + } + RecordBypassedBytes(last_bypass_type_, DataReductionProxyUsageStats::TRIGGERING_REQUEST, content_length); triggering_request_ = false; + return; } - std::string mime_type; - request.GetMimeType(&mime_type); - // MIME types are named by /. We check to see if the - // media type is audio or video. - if (mime_type.compare(0, 6, "audio/") == 0 || - mime_type.compare(0, 6, "video/") == 0) { - RecordBypassedBytes(last_bypass_type_, - DataReductionProxyUsageStats::AUDIO_VIDEO, - content_length); - } - - if (last_bypass_type_ != ProxyService::BYPASS_EVENT_TYPE_MAX) { + if (last_bypass_type_ != BYPASS_EVENT_TYPE_MAX) { RecordBypassedBytes(last_bypass_type_, DataReductionProxyUsageStats::BYPASSED_BYTES_TYPE_MAX, content_length); @@ -167,8 +240,31 @@ void DataReductionProxyUsageStats::RecordBypassedBytesHistograms( } } +void DataReductionProxyUsageStats::RecordBypassEventHistograms( + const net::ProxyServer& bypassed_proxy, + int net_error) const { + DataReductionProxyTypeInfo data_reduction_proxy_info; + if (bypassed_proxy.is_valid() && !bypassed_proxy.is_direct() && + data_reduction_proxy_params_->IsDataReductionProxy( + bypassed_proxy.host_port_pair(), &data_reduction_proxy_info)) { + if (data_reduction_proxy_info.is_ssl) + return; + if (!data_reduction_proxy_info.is_fallback) { + RecordDataReductionProxyBypassInfo( + true, false, bypassed_proxy, BYPASS_EVENT_TYPE_NETWORK_ERROR); + RecordDataReductionProxyBypassOnNetworkError( + true, bypassed_proxy, net_error); + } else { + RecordDataReductionProxyBypassInfo( + false, false, bypassed_proxy, BYPASS_EVENT_TYPE_NETWORK_ERROR); + RecordDataReductionProxyBypassOnNetworkError( + false, bypassed_proxy, net_error); + } + } +} + void DataReductionProxyUsageStats::RecordBypassedBytes( - ProxyService::DataReductionProxyBypassType bypass_type, + DataReductionProxyBypassType bypass_type, DataReductionProxyUsageStats::BypassedBytesType bypassed_bytes_type, int64 content_length) { // Individual histograms are needed to count the bypassed bytes for each @@ -188,8 +284,13 @@ void DataReductionProxyUsageStats::RecordBypassedBytes( "DataReductionProxy.BypassedBytes.LocalBypassRules", content_length); break; + case DataReductionProxyUsageStats::MANAGED_PROXY_CONFIG: + UMA_HISTOGRAM_COUNTS( + "DataReductionProxy.BypassedBytes.ManagedProxyConfig", + content_length); + break; case DataReductionProxyUsageStats::AUDIO_VIDEO: - if (last_bypass_type_ == ProxyService::SHORT_BYPASS) { + if (last_bypass_type_ == BYPASS_EVENT_TYPE_SHORT) { UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.ShortAudioVideo", content_length); @@ -197,17 +298,17 @@ void DataReductionProxyUsageStats::RecordBypassedBytes( break; case DataReductionProxyUsageStats::TRIGGERING_REQUEST: switch (bypass_type) { - case ProxyService::SHORT_BYPASS: + case BYPASS_EVENT_TYPE_SHORT: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.ShortTriggeringRequest", content_length); break; - case ProxyService::MEDIUM_BYPASS: + case BYPASS_EVENT_TYPE_MEDIUM: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.MediumTriggeringRequest", content_length); break; - case ProxyService::LONG_BYPASS: + case BYPASS_EVENT_TYPE_LONG: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.LongTriggeringRequest", content_length); @@ -223,48 +324,48 @@ void DataReductionProxyUsageStats::RecordBypassedBytes( break; case DataReductionProxyUsageStats::BYPASSED_BYTES_TYPE_MAX: switch (bypass_type) { - case ProxyService::CURRENT_BYPASS: + case BYPASS_EVENT_TYPE_CURRENT: UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.Current", content_length); break; - case ProxyService::SHORT_BYPASS: + case BYPASS_EVENT_TYPE_SHORT: UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.ShortAll", content_length); break; - case ProxyService::MEDIUM_BYPASS: + case BYPASS_EVENT_TYPE_MEDIUM: UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.MediumAll", content_length); break; - case ProxyService::LONG_BYPASS: + case BYPASS_EVENT_TYPE_LONG: UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.LongAll", content_length); break; - case ProxyService::MISSING_VIA_HEADER_4XX: + case BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.MissingViaHeader4xx", content_length); break; - case ProxyService::MISSING_VIA_HEADER_OTHER: + case BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.MissingViaHeaderOther", content_length); break; - case ProxyService::MALFORMED_407: + case BYPASS_EVENT_TYPE_MALFORMED_407: UMA_HISTOGRAM_COUNTS("DataReductionProxy.BypassedBytes.Malformed407", content_length); break; - case ProxyService::STATUS_500_HTTP_INTERNAL_SERVER_ERROR: + case BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes." "Status500HttpInternalServerError", content_length); break; - case ProxyService::STATUS_502_HTTP_BAD_GATEWAY: + case BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes.Status502HttpBadGateway", content_length); break; - case ProxyService::STATUS_503_HTTP_SERVICE_UNAVAILABLE: + case BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE: UMA_HISTOGRAM_COUNTS( "DataReductionProxy.BypassedBytes." "Status503HttpServiceUnavailable", diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h b/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h index 4e3dbfe6fb891..33fd29e560fa2 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h @@ -5,67 +5,104 @@ #ifndef COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_USAGE_STATS_H_ #define COMPONENTS_DATA_REDUCTION_PROXY_BROWSER_DATA_REDUCTION_PROXY_USAGE_STATS_H_ +#include "base/callback.h" #include "base/message_loop/message_loop_proxy.h" #include "base/prefs/pref_member.h" #include "base/threading/thread_checker.h" #include "components/data_reduction_proxy/browser/data_reduction_proxy_params.h" +#include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include "net/base/host_port_pair.h" #include "net/base/network_change_notifier.h" -#include "net/proxy/proxy_service.h" #include "net/url_request/url_request.h" +namespace net { +class ProxyServer; +} + namespace data_reduction_proxy { class DataReductionProxyUsageStats : public net::NetworkChangeNotifier::NetworkChangeObserver { public: - // MessageLoopProxy instances are owned by io_thread. |params| outlives + // Records a data reduction proxy bypass event as a "BlockType" if + // |bypass_all| is true and as a "BypassType" otherwise. Records the event as + // "Primary" if |is_primary| is true and "Fallback" otherwise. + static void RecordDataReductionProxyBypassInfo( + bool is_primary, + bool bypass_all, + const net::ProxyServer& proxy_server, + DataReductionProxyBypassType bypass_type); + + // MessageLoopProxy instance is owned by io_thread. |params| outlives // this class instance. DataReductionProxyUsageStats(DataReductionProxyParams* params, - base::MessageLoopProxy* ui_thread_proxy, - base::MessageLoopProxy* io_thread_proxy); + base::MessageLoopProxy* ui_thread_proxy); virtual ~DataReductionProxyUsageStats(); + // Sets the callback to be called on the UI thread when the unavailability + // status has changed. + void set_unavailable_callback( + const base::Callback& unavailable_callback) { + unavailable_callback_ = unavailable_callback; + } + // Callback intended to be called from |ChromeNetworkDelegate| when a // request completes. This method is used to gather usage stats. void OnUrlRequestCompleted(const net::URLRequest* request, bool started); - // Determines whether the data reduction proxy is unreachable. - // Returns true if data reduction proxy is unreachable. - bool isDataReductionProxyUnreachable(); - // Records the last bypass reason to |bypass_type_| and sets // |triggering_request_| to true. A triggering request is the first request to // cause the current bypass. - void SetBypassType(net::ProxyService::DataReductionProxyBypassType type); + void SetBypassType(DataReductionProxyBypassType type); - // Given the |content_length| and associated |request|, records the - // number of bypassed bytes for that |request| into UMAs based on bypass type. - // |data_reduction_proxy_enabled| tells us the state of the - // kDataReductionProxyEnabled preference. + // Given |data_reduction_proxy_enabled|, a |request|, and the + // |data_reduction_proxy_config| records the number of bypassed bytes for that + // |request| into UMAs based on bypass type. |data_reduction_proxy_enabled| + // tells us the state of the kDataReductionProxyEnabled preference. void RecordBypassedBytesHistograms( net::URLRequest& request, - const BooleanPrefMember& data_reduction_proxy_enabled); + const BooleanPrefMember& data_reduction_proxy_enabled, + const net::ProxyConfig& data_reduction_proxy_config); + + void RecordBypassEventHistograms(const net::ProxyServer& bypassed_proxy, + int net_error) const; private: enum BypassedBytesType { NOT_BYPASSED = 0, /* Not bypassed. */ SSL, /* Bypass due to SSL. */ LOCAL_BYPASS_RULES, /* Bypass due to client-side bypass rules. */ + MANAGED_PROXY_CONFIG, /* Bypass due to managed config. */ AUDIO_VIDEO, /* Audio/Video bypass. */ TRIGGERING_REQUEST, /* Triggering request bypass. */ NETWORK_ERROR, /* Network error. */ BYPASSED_BYTES_TYPE_MAX /* This must always be last.*/ }; + // NetworkChangeNotifier::NetworkChangeObserver: + virtual void OnNetworkChanged( + net::NetworkChangeNotifier::ConnectionType type) OVERRIDE; + + // Counts requests that went through the data reduction proxy and counts + // requests that were eligible to go through the proxy. + void IncrementRequestCounts(bool actual); + void ClearRequestCounts(); + + // Checks if the availability status of the data reduction proxy has changed, + // and notifies the UIThread via NotifyUnavailabilityOnUIThread if so. The + // data reduction proxy is considered unavailable if and only if no requests + // went through the proxy but some eligible requests were service by other + // routes. + void MaybeNotifyUnavailability(); + void NotifyUnavailabilityOnUIThread(bool unavailable); + DataReductionProxyParams* data_reduction_proxy_params_; // The last reason for bypass as determined by // MaybeBypassProxyAndPrepareToRetry - net::ProxyService::DataReductionProxyBypassType last_bypass_type_; + DataReductionProxyBypassType last_bypass_type_; // True if the last request triggered the current bypass. bool triggering_request_; base::MessageLoopProxy* ui_thread_proxy_; - base::MessageLoopProxy* io_thread_proxy_; // The following 2 fields are used to determine if data reduction proxy is // unreachable. We keep a count of requests which should go through @@ -78,23 +115,23 @@ class DataReductionProxyUsageStats // Explicit bypasses are not part of this count. This is the desired behavior // since otherwise both counts would be identical. unsigned long eligible_num_requests_through_proxy_; - // Count of successfull requests through data reduction proxy. - unsigned long actual_num_requests_through_proxy_; - // NetworkChangeNotifier::NetworkChangeObserver: - virtual void OnNetworkChanged( - net::NetworkChangeNotifier::ConnectionType type) OVERRIDE; + // Count of successful requests through data reduction proxy. + unsigned long actual_num_requests_through_proxy_; - void IncRequestCountsOnUiThread(bool actual); - void ClearRequestCountsOnUiThread(); + // Whether or not the data reduction proxy is unavailable. + bool unavailable_; base::ThreadChecker thread_checker_; void RecordBypassedBytes( - net::ProxyService::DataReductionProxyBypassType bypass_type, + DataReductionProxyBypassType bypass_type, BypassedBytesType bypassed_bytes_type, int64 content_length); + // Called when the unavailability status has changed. Runs on the UI thread. + base::Callback unavailable_callback_; + DISALLOW_COPY_AND_ASSIGN(DataReductionProxyUsageStats); }; diff --git a/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc b/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc index 39a951fc9442e..a3f02d32bd2ab 100644 --- a/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc +++ b/components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats_unittest.cc @@ -4,6 +4,7 @@ #include "components/data_reduction_proxy/browser/data_reduction_proxy_usage_stats.h" +#include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "net/base/request_priority.h" #include "net/url_request/url_request.h" @@ -29,8 +30,10 @@ class DataReductionProxyParamsMock : public DataReductionProxyParams { virtual ~DataReductionProxyParamsMock() {} MOCK_METHOD1(IsDataReductionProxyEligible, bool(const net::URLRequest*)); - MOCK_CONST_METHOD2(WasDataReductionProxyUsed, bool(const net::URLRequest*, - std::pair* proxy_servers)); + MOCK_CONST_METHOD2( + WasDataReductionProxyUsed, + bool(const net::URLRequest*, + data_reduction_proxy::DataReductionProxyTypeInfo* proxy_servers)); private: DISALLOW_COPY_AND_ASSIGN(DataReductionProxyParamsMock); @@ -45,9 +48,15 @@ class DataReductionProxyUsageStatsTest : public testing::Test { DataReductionProxyUsageStatsTest() : loop_proxy_(MessageLoopProxy::current().get()), context_(true), - mock_url_request_(GURL(), net::IDLE, &delegate_, &context_) { + mock_url_request_(GURL(), net::IDLE, &delegate_, &context_), + unavailable_(false) { context_.Init(); } + + void NotifyUnavailable(bool unavailable) { + unavailable_ = unavailable; + } + // Required for MessageLoopProxy::current(). base::MessageLoopForUI loop_; MessageLoopProxy* loop_proxy_; @@ -57,9 +66,10 @@ class DataReductionProxyUsageStatsTest : public testing::Test { TestDelegate delegate_; DataReductionProxyParamsMock mock_params_; URLRequest mock_url_request_; + bool unavailable_; }; -TEST_F(DataReductionProxyUsageStatsTest, isDataReductionProxyUnreachable) { +TEST_F(DataReductionProxyUsageStatsTest, IsDataReductionProxyUnreachable) { struct TestCase { bool is_proxy_eligible; bool was_proxy_used; @@ -93,13 +103,15 @@ TEST_F(DataReductionProxyUsageStatsTest, isDataReductionProxyUnreachable) { scoped_ptr usage_stats( new DataReductionProxyUsageStats( - &mock_params_, loop_proxy_, loop_proxy_)); + &mock_params_, loop_proxy_)); + usage_stats->set_unavailable_callback( + base::Bind(&DataReductionProxyUsageStatsTest::NotifyUnavailable, + base::Unretained(this))); usage_stats->OnUrlRequestCompleted(&mock_url_request_, false); MessageLoop::current()->RunUntilIdle(); - EXPECT_EQ(test_case.is_unreachable, - usage_stats->isDataReductionProxyUnreachable()); + EXPECT_EQ(test_case.is_unreachable, unavailable_); } } diff --git a/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc b/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc index d97c35c52220f..377db65cbb228 100644 --- a/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc +++ b/components/data_reduction_proxy/common/data_reduction_proxy_headers.cc @@ -5,6 +5,7 @@ #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" #include +#include #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" @@ -13,14 +14,25 @@ #include "base/time/time.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" -#include "net/proxy/proxy_service.h" using base::StringPiece; using base::TimeDelta; -using net::ProxyService; namespace { +const char kChromeProxyHeader[] = "chrome-proxy"; +const char kActionValueDelimiter = '='; + +const char kChromeProxyActionBlockOnce[] = "block-once"; +const char kChromeProxyActionBlock[] = "block"; +const char kChromeProxyActionBypass[] = "bypass"; + +// Actions for tamper detection fingerprints. +const char kChromeProxyActionFingerprintChromeProxy[] = "fcp"; +const char kChromeProxyActionFingerprintVia[] = "fvia"; +const char kChromeProxyActionFingerprintOtherHeaders[] = "foh"; +const char kChromeProxyActionFingerprintContentLength[] = "fcl"; + const int kShortBypassMaxSeconds = 59; const int kMediumBypassMaxSeconds = 300; @@ -31,25 +43,56 @@ base::TimeDelta GetDefaultBypassDuration() { base::TimeDelta::FromMinutes(5).InMilliseconds()); return TimeDelta::FromMilliseconds(delta_ms); } + } // namespace namespace data_reduction_proxy { +bool GetDataReductionProxyActionValue( + const net::HttpResponseHeaders* headers, + const std::string& action_prefix, + std::string* action_value) { + DCHECK(headers); + DCHECK(!action_prefix.empty()); + // A valid action does not include a trailing '='. + DCHECK(action_prefix[action_prefix.size() - 1] != kActionValueDelimiter); + void* iter = NULL; + std::string value; + std::string prefix = action_prefix + kActionValueDelimiter; + + while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { + if (value.size() > prefix.size()) { + if (LowerCaseEqualsASCII(value.begin(), + value.begin() + prefix.size(), + prefix.c_str())) { + if (action_value) + *action_value = value.substr(prefix.size()); + return true; + } + } + } + return false; +} + bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers, const std::string& action_prefix, base::TimeDelta* bypass_duration) { + DCHECK(headers); + DCHECK(!action_prefix.empty()); + // A valid action does not include a trailing '='. + DCHECK(action_prefix[action_prefix.size() - 1] != kActionValueDelimiter); void* iter = NULL; std::string value; - std::string name = "chrome-proxy"; + std::string prefix = action_prefix + kActionValueDelimiter; - while (headers->EnumerateHeader(&iter, name, &value)) { - if (value.size() > action_prefix.size()) { + while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { + if (value.size() > prefix.size()) { if (LowerCaseEqualsASCII(value.begin(), - value.begin() + action_prefix.size(), - action_prefix.c_str())) { + value.begin() + prefix.size(), + prefix.c_str())) { int64 seconds; if (!base::StringToInt64( - StringPiece(value.begin() + action_prefix.size(), value.end()), + StringPiece(value.begin() + prefix.size(), value.end()), &seconds) || seconds < 0) { continue; // In case there is a well formed instruction. } @@ -70,7 +113,7 @@ bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers, bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers, DataReductionProxyInfo* proxy_info) { DCHECK(proxy_info); - proxy_info->bypass_all = false; + // Support header of the form Chrome-Proxy: bypass|block=, where // is the number of seconds to wait before retrying // the proxy. If the duration is 0, then the default proxy retry delay @@ -79,23 +122,42 @@ bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers, // proxy, whereas 'block' instructs Chrome to bypass all available data // reduction proxies. - // 'block' takes precedence over 'bypass', so look for it first. + // 'block' takes precedence over 'bypass' and 'block-once', so look for it + // first. // TODO(bengr): Reduce checks for 'block' and 'bypass' to a single loop. if (ParseHeadersAndSetBypassDuration( - headers, "block=", &proxy_info->bypass_duration)) { + headers, kChromeProxyActionBlock, &proxy_info->bypass_duration)) { proxy_info->bypass_all = true; + proxy_info->mark_proxies_as_bad = true; return true; } // Next, look for 'bypass'. if (ParseHeadersAndSetBypassDuration( - headers, "bypass=", &proxy_info->bypass_duration)) { + headers, kChromeProxyActionBypass, &proxy_info->bypass_duration)) { + proxy_info->bypass_all = false; + proxy_info->mark_proxies_as_bad = true; + return true; + } + + // Lastly, look for 'block-once'. 'block-once' instructs Chrome to retry the + // current request (if it's idempotent), bypassing all available data + // reduction proxies. Unlike 'block', 'block-once' does not cause data + // reduction proxies to be bypassed for an extended period of time; + // 'block-once' only affects the retry of the current request. + if (headers->HasHeaderValue(kChromeProxyHeader, + kChromeProxyActionBlockOnce)) { + proxy_info->bypass_all = true; + proxy_info->mark_proxies_as_bad = false; + proxy_info->bypass_duration = TimeDelta(); return true; } + return false; } -bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) { +bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers, + bool* has_intermediary) { const size_t kVersionSize = 4; const char kDataReductionProxyViaValue[] = "Chrome-Compression-Proxy"; size_t value_len = strlen(kDataReductionProxyViaValue); @@ -107,8 +169,13 @@ bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) { // 'Via: 1.1 Chrome-Compression-Proxy' while (headers->EnumerateHeader(&iter, "via", &value)) { if (value.size() >= kVersionSize + value_len && - !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue)) + !value.compare(kVersionSize, value_len, kDataReductionProxyViaValue)) { + if (has_intermediary) + // We assume intermediary exists if there is another Via header after + // the data reduction proxy's Via header. + *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value)); return true; + } } // TODO(bengr): Remove deprecated header value. @@ -116,42 +183,53 @@ bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers) { "1.1 Chrome Compression Proxy"; iter = NULL; while (headers->EnumerateHeader(&iter, "via", &value)) - if (value == kDeprecatedDataReductionProxyViaValue) + if (value == kDeprecatedDataReductionProxyViaValue) { + if (has_intermediary) + *has_intermediary = !(headers->EnumerateHeader(&iter, "via", &value)); return true; + } return false; } -net::ProxyService::DataReductionProxyBypassType -GetDataReductionProxyBypassType( +DataReductionProxyBypassType GetDataReductionProxyBypassType( const net::HttpResponseHeaders* headers, DataReductionProxyInfo* data_reduction_proxy_info) { DCHECK(data_reduction_proxy_info); if (ParseHeadersAndSetProxyInfo(headers, data_reduction_proxy_info)) { // A chrome-proxy response header is only present in a 502. For proper // reporting, this check must come before the 5xx checks below. + if (!data_reduction_proxy_info->mark_proxies_as_bad) + return BYPASS_EVENT_TYPE_CURRENT; + const TimeDelta& duration = data_reduction_proxy_info->bypass_duration; if (duration <= TimeDelta::FromSeconds(kShortBypassMaxSeconds)) - return ProxyService::SHORT_BYPASS; + return BYPASS_EVENT_TYPE_SHORT; if (duration <= TimeDelta::FromSeconds(kMediumBypassMaxSeconds)) - return ProxyService::MEDIUM_BYPASS; - return ProxyService::LONG_BYPASS; + return BYPASS_EVENT_TYPE_MEDIUM; + return BYPASS_EVENT_TYPE_LONG; } + + // If a bypass is triggered by any of the following cases, then the data + // reduction proxy should be bypassed for a random duration between 1 and 5 + // minutes. + data_reduction_proxy_info->mark_proxies_as_bad = true; data_reduction_proxy_info->bypass_duration = GetDefaultBypassDuration(); + // Fall back if a 500, 502 or 503 is returned. if (headers->response_code() == net::HTTP_INTERNAL_SERVER_ERROR) - return ProxyService::STATUS_500_HTTP_INTERNAL_SERVER_ERROR; + return BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR; if (headers->response_code() == net::HTTP_BAD_GATEWAY) - return ProxyService::STATUS_502_HTTP_BAD_GATEWAY; + return BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY; if (headers->response_code() == net::HTTP_SERVICE_UNAVAILABLE) - return ProxyService::STATUS_503_HTTP_SERVICE_UNAVAILABLE; + return BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE; // TODO(kundaji): Bypass if Proxy-Authenticate header value cannot be // interpreted by data reduction proxy. if (headers->response_code() == net::HTTP_PROXY_AUTHENTICATION_REQUIRED && !headers->HasHeader("Proxy-Authenticate")) { - return ProxyService::MALFORMED_407; + return BYPASS_EVENT_TYPE_MALFORMED_407; } - if (!HasDataReductionProxyViaHeader(headers) && + if (!HasDataReductionProxyViaHeader(headers, NULL) && (headers->response_code() != net::HTTP_NOT_MODIFIED)) { // A Via header might not be present in a 304. Since the goal of a 304 // response is to minimize information transfer, a sender in general @@ -162,12 +240,70 @@ GetDataReductionProxyBypassType( // Separate this case from other responses that are missing the header. if (headers->response_code() >= net::HTTP_BAD_REQUEST && headers->response_code() < net::HTTP_INTERNAL_SERVER_ERROR) { - return ProxyService::MISSING_VIA_HEADER_4XX; + return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX; } - return ProxyService::MISSING_VIA_HEADER_OTHER; + return BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER; } // There is no bypass event. - return ProxyService::BYPASS_EVENT_TYPE_MAX; + return BYPASS_EVENT_TYPE_MAX; +} + +bool GetDataReductionProxyActionFingerprintChromeProxy( + const net::HttpResponseHeaders* headers, + std::string* chrome_proxy_fingerprint) { + return GetDataReductionProxyActionValue( + headers, + kChromeProxyActionFingerprintChromeProxy, + chrome_proxy_fingerprint); +} + +bool GetDataReductionProxyActionFingerprintVia( + const net::HttpResponseHeaders* headers, + std::string* via_fingerprint) { + return GetDataReductionProxyActionValue( + headers, + kChromeProxyActionFingerprintVia, + via_fingerprint); +} + +bool GetDataReductionProxyActionFingerprintOtherHeaders( + const net::HttpResponseHeaders* headers, + std::string* other_headers_fingerprint) { + return GetDataReductionProxyActionValue( + headers, + kChromeProxyActionFingerprintOtherHeaders, + other_headers_fingerprint); +} + +bool GetDataReductionProxyActionFingerprintContentLength( + const net::HttpResponseHeaders* headers, + std::string* content_length_fingerprint) { + return GetDataReductionProxyActionValue( + headers, + kChromeProxyActionFingerprintContentLength, + content_length_fingerprint); +} + +void GetDataReductionProxyHeaderWithFingerprintRemoved( + const net::HttpResponseHeaders* headers, + std::vector* values) { + DCHECK(values); + std::string chrome_proxy_fingerprint_prefix = std::string( + kChromeProxyActionFingerprintChromeProxy) + kActionValueDelimiter; + + std::string value; + void* iter = NULL; + while (headers->EnumerateHeader(&iter, kChromeProxyHeader, &value)) { + if (value.size() > chrome_proxy_fingerprint_prefix.size()) { + if (LowerCaseEqualsASCII( + value.begin(), + value.begin() + chrome_proxy_fingerprint_prefix.size(), + chrome_proxy_fingerprint_prefix.c_str())) { + continue; + } + } + values->push_back(value); + } } } // namespace data_reduction_proxy diff --git a/components/data_reduction_proxy/common/data_reduction_proxy_headers.h b/components/data_reduction_proxy/common/data_reduction_proxy_headers.h index 28eccee3073e6..849ade037bc78 100644 --- a/components/data_reduction_proxy/common/data_reduction_proxy_headers.h +++ b/components/data_reduction_proxy/common/data_reduction_proxy_headers.h @@ -19,15 +19,63 @@ class HttpResponseHeaders; namespace data_reduction_proxy { +// Values of the UMA DataReductionProxy.BypassType{Primary|Fallback} +// and DataReductionProxy.BlockType{Primary|Fallback} histograms. +// This enum must remain synchronized with the enum of the same +// name in metrics/histograms/histograms.xml. +enum DataReductionProxyBypassType { + // Bypass due to explicit instruction for the current request. + BYPASS_EVENT_TYPE_CURRENT = 0, + + // Bypass the proxy for less than one minute. + BYPASS_EVENT_TYPE_SHORT = 1, + + // Bypass the proxy for one to five minutes. + BYPASS_EVENT_TYPE_MEDIUM = 2, + + // Bypass the proxy for more than five minutes. + BYPASS_EVENT_TYPE_LONG = 3, + + // Bypass due to a 4xx missing via header. + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX = 4, + + // Bypass due to other missing via header, excluding 4xx errors. + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER = 5, + + // Bypass due to 407 response from proxy without a challenge. + BYPASS_EVENT_TYPE_MALFORMED_407 = 6, + + // Bypass due to a 500 internal server error + BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR = 7, + + // Bypass because the request URI was too long. + BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY = 8, + + // Bypass due to a 503 response. + BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE = 9, + + // Bypass due to any network error. + BYPASS_EVENT_TYPE_NETWORK_ERROR = 10, + + // This must always be last. + BYPASS_EVENT_TYPE_MAX = 11 +}; + // Contains instructions contained in the Chrome-Proxy header. struct DataReductionProxyInfo { - DataReductionProxyInfo() : bypass_all(false) {} + DataReductionProxyInfo() + : bypass_all(false), mark_proxies_as_bad(false) {} // True if Chrome should bypass all available data reduction proxies. False // if only the currently connected data reduction proxy should be bypassed. bool bypass_all; - // Amount of time to bypass the data reduction proxy or proxies. + // True iff Chrome should mark the data reduction proxy or proxies as bad for + // the period of time specified in |bypass_duration|. + bool mark_proxies_as_bad; + + // Amount of time to bypass the data reduction proxy or proxies. This value is + // ignored if |mark_proxies_as_bad| is false. base::TimeDelta bypass_duration; }; @@ -41,21 +89,58 @@ bool ParseHeadersAndSetProxyInfo(const net::HttpResponseHeaders* headers, DataReductionProxyInfo* proxy_info); // Returns true if the response contains the data reduction proxy Via header -// value. Used to check the integrity of data reduction proxy responses. -bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers); +// value. If non-NULL, sets |has_intermediary| to true if another server added +// a Via header after the data reduction proxy, and to false otherwise. Used to +// check the integrity of data reduction proxy responses and whether there are +// other middleboxes between the data reduction proxy and the client. +bool HasDataReductionProxyViaHeader(const net::HttpResponseHeaders* headers, + bool* has_intermediary); // Returns the reason why the Chrome proxy should be bypassed or not, and // populates |proxy_info| with information on how long to bypass if // applicable. -net::ProxyService::DataReductionProxyBypassType GetDataReductionProxyBypassType( +DataReductionProxyBypassType GetDataReductionProxyBypassType( const net::HttpResponseHeaders* headers, DataReductionProxyInfo* proxy_info); +// Searches for the specified Chrome-Proxy action, and if present saves its +// value as a string in |action_value|. Only returns the first one and ignores +// the rest if multiple actions match |action_prefix|. +bool GetDataReductionProxyActionValue( + const net::HttpResponseHeaders* headers, + const std::string& action_prefix, + std::string* action_value); + // Searches for the specified Chrome-Proxy action, and if present interprets // its value as a duration in seconds. bool ParseHeadersAndSetBypassDuration(const net::HttpResponseHeaders* headers, const std::string& action_prefix, base::TimeDelta* bypass_duration); +// Gets the fingerprint of the Chrome-Proxy header. +bool GetDataReductionProxyActionFingerprintChromeProxy( + const net::HttpResponseHeaders* headers, + std::string* chrome_proxy_fingerprint); + +// Gets the fingerprint of the Via header. +bool GetDataReductionProxyActionFingerprintVia( + const net::HttpResponseHeaders* headers, + std::string* via_fingerprint); + +// Gets the fingerprint of a list of headers. +bool GetDataReductionProxyActionFingerprintOtherHeaders( + const net::HttpResponseHeaders* headers, + std::string* other_headers_fingerprint); + +// Gets the fingerprint of Content-Length header. +bool GetDataReductionProxyActionFingerprintContentLength( + const net::HttpResponseHeaders* headers, + std::string* content_length_fingerprint); + +// Returns values of the Chrome-Proxy header, but with its fingerprint removed. +void GetDataReductionProxyHeaderWithFingerprintRemoved( + const net::HttpResponseHeaders* headers, + std::vector* values); + } // namespace data_reduction_proxy #endif // COMPONENTS_DATA_REDUCTION_PROXY_COMMON_DATA_REDUCTION_PROXY_HEADERS_H_ diff --git a/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc b/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc index ef3de65d94cf5..38a13e4f43f92 100644 --- a/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc +++ b/components/data_reduction_proxy/common/data_reduction_proxy_headers_unittest.cc @@ -4,6 +4,8 @@ #include "components/data_reduction_proxy/common/data_reduction_proxy_headers.h" +#include + #include "net/http/http_response_headers.h" #include "net/proxy/proxy_service.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,18 +26,114 @@ namespace data_reduction_proxy { class DataReductionProxyHeadersTest : public testing::Test {}; +TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyActionValue) { + const struct { + const char* headers; + std::string action_key; + bool expected_result; + std::string expected_action_value; + } tests[] = { + { "HTTP/1.1 200 OK\n" + "Content-Length: 999\n", + "a", + false, + "", + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Content-Length: 999\n", + "a", + false, + "", + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=86400\n" + "Content-Length: 999\n", + "bypass", + true, + "86400", + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass86400\n" + "Content-Length: 999\n", + "bypass", + false, + "", + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=0\n" + "Content-Length: 999\n", + "bypass", + true, + "0", + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=1500\n" + "Chrome-Proxy: bypass=86400\n" + "Content-Length: 999\n", + "bypass", + true, + "1500", + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block=1500, block=3600\n" + "Content-Length: 999\n", + "block", + true, + "1500", + }, + { "HTTP/1.1 200 OK\n" + "connection: proxy-bypass\n" + "Chrome-Proxy: key=123 \n" + "Content-Length: 999\n", + "key", + true, + "123", + }, + { "HTTP/1.1 200 OK\n" + "connection: proxy-bypass\n" + "Chrome-Proxy: block-once\n" + "Content-Length: 999\n", + "block-once", + false, + "", + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + std::string action_value; + bool has_action_key = GetDataReductionProxyActionValue( + parsed, tests[i].action_key, &action_value); + EXPECT_EQ(tests[i].expected_result, has_action_key); + if (has_action_key) { + EXPECT_EQ(tests[i].expected_action_value, action_value); + } + } +} + TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { const struct { const char* headers; bool expected_result; int64 expected_retry_delay; bool expected_bypass_all; + bool expected_mark_proxies_as_bad; } tests[] = { { "HTTP/1.1 200 OK\n" "Content-Length: 999\n", false, 0, false, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -43,6 +141,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { false, 0, false, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -51,6 +150,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 86400, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -59,6 +159,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { false, 0, false, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -67,6 +168,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { false, 0, false, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -75,6 +177,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { false, 0, false, + false, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -83,6 +186,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 86400, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -91,6 +195,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 86400, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -100,6 +205,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 3600, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -108,6 +214,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 3600, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -116,6 +223,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 86400, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -125,6 +233,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 86400, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -133,6 +242,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 3600, true, + true, }, { "HTTP/1.1 200 OK\n" "connection: keep-alive\n" @@ -141,6 +251,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 3600, true, + true, }, { "HTTP/1.1 200 OK\n" "connection: proxy-bypass\n" @@ -149,6 +260,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { true, 86400, false, + true, }, { "HTTP/1.1 200 OK\n" "connection: proxy-bypass\n" @@ -157,6 +269,7 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { false, 0, false, + false, }, { "HTTP/1.1 200 OK\n" "connection: proxy-bypass\n" @@ -165,6 +278,80 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { false, 0, false, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once\n" + "Content-Length: 999\n", + true, + 0, + true, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once=\n" + "Content-Length: 999\n", + false, + 0, + false, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once=10\n" + "Content-Length: 999\n", + false, + 0, + false, + false, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once, bypass=86400, block=3600\n" + "Content-Length: 999\n", + true, + 3600, + true, + true, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once\n" + "Chrome-Proxy: bypass=86400, block=3600\n" + "Content-Length: 999\n", + true, + 3600, + true, + true, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once, bypass=86400\n" + "Content-Length: 999\n", + true, + 86400, + false, + true, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: block-once, block=3600\n" + "Content-Length: 999\n", + true, + 3600, + true, + true, + }, + { "HTTP/1.1 200 OK\n" + "connection: keep-alive\n" + "Chrome-Proxy: bypass=, block=, block-once\n" + "Content-Length: 999\n", + true, + 0, + true, + false, }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { @@ -180,6 +367,8 @@ TEST_F(DataReductionProxyHeadersTest, GetProxyBypassInfo) { data_reduction_proxy_info.bypass_duration.InSeconds()); EXPECT_EQ(tests[i].expected_bypass_all, data_reduction_proxy_info.bypass_all); + EXPECT_EQ(tests[i].expected_mark_proxies_as_bad, + data_reduction_proxy_info.mark_proxies_as_bad); } } @@ -202,66 +391,117 @@ TEST_F(DataReductionProxyHeadersTest, ParseHeadersAndSetProxyInfo) { TEST_F(DataReductionProxyHeadersTest, HasDataReductionProxyViaHeader) { const struct { - const char* headers; - bool expected_result; + const char* headers; + bool expected_result; + bool expected_has_intermediary; + bool ignore_intermediary; } tests[] = { { "HTTP/1.1 200 OK\n" "Via: 1.1 Chrome-Proxy\n", false, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1\n", false, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Chrome-Compression-Proxy\n", true, + true, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.0 Chrome-Compression-Proxy\n", true, + true, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Foo-Bar, 1.1 Chrome-Compression-Proxy\n", true, + true, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Chrome-Compression-Proxy, 1.1 Bar-Foo\n", true, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 chrome-compression-proxy\n", false, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Foo-Bar\n" "Via: 1.1 Chrome-Compression-Proxy\n", true, + true, + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome-Compression-Proxy\n" + "Via: 1.1 Foo-Bar\n", + true, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Chrome-Proxy\n", false, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Chrome Compression Proxy\n", true, + true, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Foo-Bar, 1.1 Chrome Compression Proxy\n", true, + true, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Chrome Compression Proxy, 1.1 Bar-Foo\n", true, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 chrome compression proxy\n", false, + false, + false, }, { "HTTP/1.1 200 OK\n" "Via: 1.1 Foo-Bar\n" "Via: 1.1 Chrome Compression Proxy\n", true, + true, + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome Compression Proxy\n" + "Via: 1.1 Foo-Bar\n", + true, + false, + false, + }, + { "HTTP/1.1 200 OK\n" + "Via: 1.1 Chrome Compression Proxy\n" + "Via: 1.1 Foo-Bar\n", + true, + false, + true, }, }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { @@ -270,105 +510,121 @@ TEST_F(DataReductionProxyHeadersTest, HasDataReductionProxyViaHeader) { scoped_refptr parsed( new net::HttpResponseHeaders(headers)); - EXPECT_EQ(tests[i].expected_result, - HasDataReductionProxyViaHeader(parsed)); + bool has_chrome_proxy_via_header, has_intermediary; + if (tests[i].ignore_intermediary) { + has_chrome_proxy_via_header = + HasDataReductionProxyViaHeader(parsed, NULL); + } + else { + has_chrome_proxy_via_header = + HasDataReductionProxyViaHeader(parsed, &has_intermediary); + } + EXPECT_EQ(tests[i].expected_result, has_chrome_proxy_via_header); + if (has_chrome_proxy_via_header && !tests[i].ignore_intermediary) { + EXPECT_EQ(tests[i].expected_has_intermediary, has_intermediary); + } } } TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyBypassEventType) { const struct { const char* headers; - net::ProxyService::DataReductionProxyBypassType expected_result; + DataReductionProxyBypassType expected_result; } tests[] = { { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=0\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::MEDIUM_BYPASS, + BYPASS_EVENT_TYPE_MEDIUM, }, { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=1\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::SHORT_BYPASS, + BYPASS_EVENT_TYPE_SHORT, }, { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=59\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::SHORT_BYPASS, + BYPASS_EVENT_TYPE_SHORT, }, { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=60\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::MEDIUM_BYPASS, + BYPASS_EVENT_TYPE_MEDIUM, }, { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=300\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::MEDIUM_BYPASS, + BYPASS_EVENT_TYPE_MEDIUM, }, { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=301\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::LONG_BYPASS, + BYPASS_EVENT_TYPE_LONG, + }, + { "HTTP/1.1 200 OK\n" + "Chrome-Proxy: block-once\n" + "Via: 1.1 Chrome-Compression-Proxy\n", + BYPASS_EVENT_TYPE_CURRENT, }, { "HTTP/1.1 500 Internal Server Error\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::STATUS_500_HTTP_INTERNAL_SERVER_ERROR, + BYPASS_EVENT_TYPE_STATUS_500_HTTP_INTERNAL_SERVER_ERROR, }, { "HTTP/1.1 501 Not Implemented\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, }, { "HTTP/1.1 502 Bad Gateway\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::STATUS_502_HTTP_BAD_GATEWAY, + BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY, }, { "HTTP/1.1 503 Service Unavailable\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::STATUS_503_HTTP_SERVICE_UNAVAILABLE, + BYPASS_EVENT_TYPE_STATUS_503_HTTP_SERVICE_UNAVAILABLE, }, { "HTTP/1.1 504 Gateway Timeout\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, }, { "HTTP/1.1 505 HTTP Version Not Supported\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, }, { "HTTP/1.1 304 Not Modified\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, }, { "HTTP/1.1 200 OK\n", - net::ProxyService::MISSING_VIA_HEADER_OTHER, + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_OTHER, }, { "HTTP/1.1 200 OK\n" "Chrome-Proxy: bypass=59\n", - net::ProxyService::SHORT_BYPASS, + BYPASS_EVENT_TYPE_SHORT, }, { "HTTP/1.1 502 Bad Gateway\n", - net::ProxyService::STATUS_502_HTTP_BAD_GATEWAY, + BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY, }, { "HTTP/1.1 502 Bad Gateway\n" "Chrome-Proxy: bypass=59\n", - net::ProxyService::SHORT_BYPASS, + BYPASS_EVENT_TYPE_SHORT, }, { "HTTP/1.1 502 Bad Gateway\n" "Chrome-Proxy: bypass=59\n", - net::ProxyService::SHORT_BYPASS, + BYPASS_EVENT_TYPE_SHORT, }, { "HTTP/1.1 414 Request-URI Too Long\n", - net::ProxyService::MISSING_VIA_HEADER_4XX, + BYPASS_EVENT_TYPE_MISSING_VIA_HEADER_4XX, }, { "HTTP/1.1 414 Request-URI Too Long\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, }, { "HTTP/1.1 407 Proxy Authentication Required\n", - net::ProxyService::MALFORMED_407, + BYPASS_EVENT_TYPE_MALFORMED_407, }, { "HTTP/1.1 407 Proxy Authentication Required\n" "Proxy-Authenticate: Basic\n" "Via: 1.1 Chrome-Compression-Proxy\n", - net::ProxyService::BYPASS_EVENT_TYPE_MAX, + BYPASS_EVENT_TYPE_MAX, } }; for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { @@ -381,4 +637,248 @@ TEST_F(DataReductionProxyHeadersTest, GetDataReductionProxyBypassEventType) { GetDataReductionProxyBypassType(parsed, &chrome_proxy_info)); } } + +TEST_F(DataReductionProxyHeadersTest, + GetDataReductionProxyActionFingerprintChromeProxy) { + const struct { + std::string label; + const char* headers; + bool expected_fingerprint_exist; + std::string expected_fingerprint; + } tests[] = { + { "fingerprint doesn't exist", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=0\n", + false, + "", + }, + { "fingerprint occurs once", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1, fcp=fp\n", + true, + "fp", + }, + { "fingerprint occurs twice", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=2, fcp=fp1, fcp=fp2\n", + true, + "fp1", + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + std::string fingerprint; + bool fingerprint_exist = GetDataReductionProxyActionFingerprintChromeProxy( + parsed, &fingerprint); + EXPECT_EQ(tests[i].expected_fingerprint_exist, fingerprint_exist) + << tests[i].label; + + if (fingerprint_exist) + EXPECT_EQ(tests[i].expected_fingerprint, fingerprint) << tests[i].label; + } +} + +TEST_F(DataReductionProxyHeadersTest, + GetDataReductionProxyActionFingerprintVia) { + const struct { + std::string label; + const char* headers; + bool expected_fingerprint_exist; + std::string expected_fingerprint; + } tests[] = { + { "fingerprint doesn't exist", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=0\n", + false, + "", + }, + { "fingerprint occurs once", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1, fvia=fvia\n", + true, + "fvia", + }, + { "fingerprint occurs twice", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=2, fvia=fvia1, fvia=fvia2\n", + true, + "fvia1", + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + std::string fingerprint; + bool fingerprint_exist = + GetDataReductionProxyActionFingerprintVia(parsed, &fingerprint); + EXPECT_EQ(tests[i].expected_fingerprint_exist, fingerprint_exist) + << tests[i].label; + + if (fingerprint_exist) + EXPECT_EQ(tests[i].expected_fingerprint, fingerprint) << tests[i].label; + } +} + +TEST_F(DataReductionProxyHeadersTest, + GetDataReductionProxyActionFingerprintOtherHeaders) { + const struct { + std::string label; + const char* headers; + bool expected_fingerprint_exist; + std::string expected_fingerprint; + } tests[] = { + { "fingerprint doesn't exist", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=0\n", + false, + "", + }, + { "fingerprint occurs once", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1, foh=foh\n", + true, + "foh", + }, + { "fingerprint occurs twice", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=2, foh=foh1, foh=foh2\n", + true, + "foh1", + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + std::string fingerprint; + bool fingerprint_exist = + GetDataReductionProxyActionFingerprintOtherHeaders( + parsed, &fingerprint); + EXPECT_EQ(tests[i].expected_fingerprint_exist, fingerprint_exist) + << tests[i].label; + + if (fingerprint_exist) + EXPECT_EQ(tests[i].expected_fingerprint, fingerprint) << tests[i].label; + } +} + +TEST_F(DataReductionProxyHeadersTest, + GetDataReductionProxyActionFingerprintContentLength) { + const struct { + std::string label; + const char* headers; + bool expected_fingerprint_exist; + std::string expected_fingerprint; + } tests[] = { + { "fingerprint doesn't exist", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=0\n", + false, + "", + }, + { "fingerprint occurs once", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=1, fcl=fcl\n", + true, + "fcl", + }, + { "fingerprint occurs twice", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: bypass=2, fcl=fcl1, fcl=fcl2\n", + true, + "fcl1", + }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + std::string headers(tests[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + std::string fingerprint; + bool fingerprint_exist = + GetDataReductionProxyActionFingerprintContentLength( + parsed, &fingerprint); + EXPECT_EQ(tests[i].expected_fingerprint_exist, fingerprint_exist) + << tests[i].label; + + if (fingerprint_exist) + EXPECT_EQ(tests[i].expected_fingerprint, fingerprint) << tests[i].label; + } +} + +TEST_F(DataReductionProxyHeadersTest, + GetDataReductionProxyHeaderWithFingerprintRemoved) { + const struct { + std::string label; + const char* headers; + std::string expected_output_values_string; + } test[] = { + { + "Checks the case that there is no Chrome-Proxy header's fingerprint.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: 1,2,3,5\n", + "1,2,3,5,", + }, + { + "Checks the case that there is Chrome-Proxy header's fingerprint.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: 1,2,3,fcp=4,5\n", + "1,2,3,5,", + }, + { + "Checks the case that there is Chrome-Proxy header's fingerprint, and it" + "occurs at the end.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: 1,2,3,fcp=4,", + "1,2,3,", + }, + { + "Checks the case that there is Chrome-Proxy header's fingerprint, and it" + "occurs at the beginning.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: fcp=1,2,3,", + "2,3,", + }, + { + "Checks the case that value is longer than prefix.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: fcp=1,fcp!=1,fcp!=2,fcpfcp=3", + "fcp!=1,fcp!=2,fcpfcp=3,", + }, + { + "Checks the case that value is shorter than prefix but similar.", + "HTTP/1.1 200 OK\n" + "Chrome-Proxy: fcp=1,fcp,fcp=", + "fcp,fcp=,", + }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test); ++i) { + std::string headers(test[i].headers); + HeadersToRaw(&headers); + scoped_refptr parsed( + new net::HttpResponseHeaders(headers)); + + std::vector output_values; + GetDataReductionProxyHeaderWithFingerprintRemoved(parsed, &output_values); + + std::string output_values_string; + for (size_t j = 0; j < output_values.size(); ++j) + output_values_string += output_values[j] + ","; + + EXPECT_EQ(test[i].expected_output_values_string, output_values_string) + << test[i].label; + } +} + } // namespace data_reduction_proxy diff --git a/components/dom_distiller.gypi b/components/dom_distiller.gypi index b18bee7098366..865ef51af5d95 100644 --- a/components/dom_distiller.gypi +++ b/components/dom_distiller.gypi @@ -83,6 +83,7 @@ 'dom_distiller/core/dom_distiller_store.h', 'dom_distiller/core/feedback_reporter.cc', 'dom_distiller/core/feedback_reporter.h', + 'dom_distiller/core/font_family_list.h', 'dom_distiller/core/task_tracker.cc', 'dom_distiller/core/task_tracker.h', 'dom_distiller/core/theme_list.h', @@ -176,6 +177,7 @@ 'target_name': 'dom_distiller_core_java', 'type': 'none', 'dependencies': [ + 'dom_distiller_core_font_family_java', 'dom_distiller_core_theme_java', '../base/base.gyp:base', ], @@ -184,6 +186,18 @@ }, 'includes': [ '../build/java.gypi' ], }, + { + 'target_name': 'dom_distiller_core_font_family_java', + 'type': 'none', + 'sources': [ + 'dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/FontFamily.template', + ], + 'variables': { + 'package_name': 'org/chromium/components/dom_distiller/core', + 'template_deps': ['dom_distiller/core/font_family_list.h'], + }, + 'includes': [ '../build/android/java_cpp_template.gypi' ], + }, { 'target_name': 'dom_distiller_core_jni_headers', 'type': 'none', diff --git a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DistilledPagePrefs.java b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DistilledPagePrefs.java index 3ba9f7ea82d8c..2f249d0073f85 100644 --- a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DistilledPagePrefs.java +++ b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DistilledPagePrefs.java @@ -24,6 +24,7 @@ public final class DistilledPagePrefs { * Observer interface for observing DistilledPagePrefs changes. */ public interface Observer { + void onChangeFontFamily(FontFamily font); void onChangeTheme(Theme theme); } @@ -39,6 +40,12 @@ public DistilledPagePrefsObserverWrapper(Observer observer) { mDistilledPagePrefsObserver = observer; } + @CalledByNative("DistilledPagePrefsObserverWrapper") + private void onChangeFontFamily(int fontFamily) { + mDistilledPagePrefsObserver.onChangeFontFamily( + FontFamily.getFontFamilyForValue(fontFamily)); + } + @CalledByNative("DistilledPagePrefsObserverWrapper") private void onChangeTheme(int theme) { mDistilledPagePrefsObserver.onChangeTheme(Theme.getThemeForValue(theme)); @@ -90,6 +97,14 @@ public boolean removeObserver(Observer obs) { return true; } + public void setFontFamily(FontFamily fontFamily) { + nativeSetFontFamily(mDistilledPagePrefsAndroid, fontFamily.asNativeEnum()); + } + + public FontFamily getFontFamily() { + return FontFamily.getFontFamilyForValue(nativeGetFontFamily(mDistilledPagePrefsAndroid)); + } + public void setTheme(Theme theme) { nativeSetTheme(mDistilledPagePrefsAndroid, theme.asNativeEnum()); } @@ -100,6 +115,10 @@ public Theme getTheme() { private native long nativeInit(long distilledPagePrefPtr); + private native void nativeSetFontFamily(long nativeDistilledPagePrefsAndroid, int fontFamily); + + private native int nativeGetFontFamily(long nativeDistilledPagePrefsAndroid); + private native void nativeSetTheme(long nativeDistilledPagePrefsAndroid, int theme); private native int nativeGetTheme(long nativeDistilledPagePrefsAndroid); diff --git a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerService.java b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerService.java index e5b5cb8cddcad..ff098a6ec98b3 100644 --- a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerService.java +++ b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerService.java @@ -27,12 +27,23 @@ public DistilledPagePrefs getDistilledPagePrefs() { return mDistilledPagePrefs; } + public boolean hasEntry(String entryId) { + return nativeHasEntry(mDomDistillerServiceAndroid, entryId); + } + + public String getUrlForEntry(String entryId) { + return nativeGetUrlForEntry(mDomDistillerServiceAndroid, entryId); + } + @CalledByNative private static DomDistillerService create(long nativeDomDistillerServiceAndroid) { ThreadUtils.assertOnUiThread(); return new DomDistillerService(nativeDomDistillerServiceAndroid); } + private native boolean nativeHasEntry(long nativeDomDistillerServiceAndroid, String entryId); + private native String nativeGetUrlForEntry( + long nativeDomDistillerServiceAndroid, String entryId); private static native long nativeGetDistilledPagePrefsPtr( long nativeDomDistillerServiceAndroid); } diff --git a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerUrlUtils.java b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerUrlUtils.java index dc31b20d56f11..1082e8a2207e3 100644 --- a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerUrlUtils.java +++ b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/DomDistillerUrlUtils.java @@ -49,8 +49,20 @@ public static boolean isUrlDistillable(String url) { return nativeIsUrlDistillable(url); } + // TODO(yfriedman): Change method so that it takes in a WebContents and a + // callback. + public static String getIsDistillableJs() { + return nativeGetIsDistillableJs(); + } + + public static String getValueForKeyInUrl(String url, String key) { + return nativeGetValueForKeyInUrl(url, key); + } + private static native String nativeGetDistillerViewUrlFromUrl(String scheme, String url); + private static native String nativeGetIsDistillableJs(); private static native String nativeGetOriginalUrlFromDistillerUrl(String viewerUrl); private static native boolean nativeIsDistilledPage(String url); private static native boolean nativeIsUrlDistillable(String url); + private static native String nativeGetValueForKeyInUrl(String url, String key); } diff --git a/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/FontFamily.template b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/FontFamily.template new file mode 100644 index 0000000000000..25115499dd6e7 --- /dev/null +++ b/components/dom_distiller/android/java/src/org/chromium/components/dom_distiller/core/FontFamily.template @@ -0,0 +1,37 @@ +// 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.components.dom_distiller.core; + +// An auto-generated enum for Font Family preferences as used by +// org.chromium.components.dom_distiller.core.DistilledPagePrefs and +// the corresponding native class +// dom_distiller::android::DistilledPagePrefsAndroid +public enum FontFamily { + +#define DEFINE_FONT_FAMILY(name, value) name(value), +#include "components/dom_distiller/core/font_family_list.h" +#undef DEFINE_FONT_FAMILY +; + +private final int mFontFamily; + +private FontFamily(int value) { + mFontFamily = value; +} + +int asNativeEnum() { + return mFontFamily; +} + +static FontFamily getFontFamilyForValue(int value) { + for (FontFamily fontFamily: FontFamily.values()) { + if (fontFamily.mFontFamily == value) { + return fontFamily; + } + } + return null; +} + +} diff --git a/components/dom_distiller/content/dom_distiller_viewer_source.cc b/components/dom_distiller/content/dom_distiller_viewer_source.cc index 964794e7e40da..3d26eb8894263 100644 --- a/components/dom_distiller/content/dom_distiller_viewer_source.cc +++ b/components/dom_distiller/content/dom_distiller_viewer_source.cc @@ -73,6 +73,8 @@ class DomDistillerViewerSource::RequestViewerHandle void Cancel(); // DistilledPagePrefs::Observer implementation: + virtual void OnChangeFontFamily( + DistilledPagePrefs::FontFamily new_font_family) OVERRIDE; virtual void OnChangeTheme(DistilledPagePrefs::Theme new_theme) OVERRIDE; // The handle to the view request towards the DomDistillerService. It @@ -187,8 +189,11 @@ void DomDistillerViewerSource::RequestViewerHandle::OnArticleReady( const DistilledArticleProto* article_proto) { if (page_count_ == 0) { // This is a single-page article. - std::string unsafe_page_html = viewer::GetUnsafeArticleHtml( - article_proto, distilled_page_prefs_->GetTheme()); + std::string unsafe_page_html = + viewer::GetUnsafeArticleHtml( + article_proto, + distilled_page_prefs_->GetTheme(), + distilled_page_prefs_->GetFontFamily()); callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); } else if (page_count_ == article_proto->pages_size()) { // We may still be showing the "Loading" indicator. @@ -217,7 +222,9 @@ void DomDistillerViewerSource::RequestViewerHandle::OnArticleUpdated( if (page_count_ == 0) { // This is the first page, so send Viewer page scaffolding too. std::string unsafe_page_html = viewer::GetUnsafePartialArticleHtml( - &page, distilled_page_prefs_->GetTheme()); + &page, + distilled_page_prefs_->GetTheme(), + distilled_page_prefs_->GetFontFamily()); callback_.Run(base::RefCountedString::TakeString(&unsafe_page_html)); } else { SendJavaScript( @@ -236,6 +243,11 @@ void DomDistillerViewerSource::RequestViewerHandle::OnChangeTheme( SendJavaScript(viewer::GetDistilledPageThemeJs(new_theme)); } +void DomDistillerViewerSource::RequestViewerHandle::OnChangeFontFamily( + DistilledPagePrefs::FontFamily new_font) { + SendJavaScript(viewer::GetDistilledPageFontFamilyJs(new_font)); +} + DomDistillerViewerSource::DomDistillerViewerSource( DomDistillerServiceInterface* dom_distiller_service, const std::string& scheme) @@ -300,7 +312,8 @@ void DomDistillerViewerSource::StartDataRequest( delete request_viewer_handle; std::string error_page_html = viewer::GetErrorPageHtml( - dom_distiller_service_->GetDistilledPagePrefs()->GetTheme()); + dom_distiller_service_->GetDistilledPagePrefs()->GetTheme(), + dom_distiller_service_->GetDistilledPagePrefs()->GetFontFamily()); callback.Run(base::RefCountedString::TakeString(&error_page_html)); } }; @@ -329,7 +342,7 @@ void DomDistillerViewerSource::WillServiceRequest( std::string DomDistillerViewerSource::GetContentSecurityPolicyObjectSrc() const { - return "object-src 'none'; style-src 'self';"; + return "object-src 'none'; style-src 'self' https://fonts.googleapis.com;"; } } // namespace dom_distiller diff --git a/components/dom_distiller/content/resources/dom_distiller_viewer.html b/components/dom_distiller/content/resources/dom_distiller_viewer.html index e5b0e9938ac8f..4bf077cab0b37 100644 --- a/components/dom_distiller/content/resources/dom_distiller_viewer.html +++ b/components/dom_distiller/content/resources/dom_distiller_viewer.html @@ -10,6 +10,7 @@ $1 + @@ -21,9 +22,61 @@

    $1

    $5
    -
    $7
    +
    +
    +
    + + + +
    + +
    + + + + + + + + +
    + +
    + + + + + + + + +
    + +
    + + + + + + + + +
    + +
    + + + + + + + + +
    +
    +
    - $9 + $8
    diff --git a/components/dom_distiller/content/resources/dom_distiller_viewer.js b/components/dom_distiller/content/resources/dom_distiller_viewer.js index 94299467ef3df..3a7d0a078f2b9 100644 --- a/components/dom_distiller/content/resources/dom_distiller_viewer.js +++ b/components/dom_distiller/content/resources/dom_distiller_viewer.js @@ -11,6 +11,24 @@ function addToPage(html) { function showLoadingIndicator(isLastPage) { document.getElementById('loadingIndicator').className = isLastPage ? 'hidden' : 'visible'; + updateLoadingIndicator(isLastPage); +} + +// Maps JS Font Family to CSS class and then changes body class name. +// CSS classes must agree with distilledpage.css. +function useFontFamily(fontFamily) { + var cssClass; + if (fontFamily == "serif") { + cssClass = "serif"; + } else if (fontFamily == "monospace") { + cssClass = "monospace"; + } else { + cssClass = "sans-serif"; + } + // Relies on the classname order of the body being Theme class, then Font + // Family class. + var themeClass = document.body.className.split(" ")[0]; + document.body.className = themeClass + " " + cssClass; } // Maps JS theme to CSS class and then changes body class name. @@ -24,5 +42,26 @@ function useTheme(theme) { } else { cssClass = "light"; } - document.body.className = cssClass; + // Relies on the classname order of the body being Theme class, then Font + // Family class. + var fontFamilyClass = document.body.className.split(" ")[1]; + document.body.className = cssClass + " " + fontFamilyClass; } + +var updateLoadingIndicator = function() { + var colors = ["red", "yellow", "green", "blue"]; + return function(isLastPage) { + if (!isLastPage && typeof this.colorShuffle == "undefined") { + var loader = document.getElementById("loader"); + if (loader) { + var colorIndex = -1; + this.colorShuffle = setInterval(function() { + colorIndex = (colorIndex + 1) % colors.length; + loader.className = colors[colorIndex]; + }, 600); + } + } else if (isLastPage && typeof this.colorShuffle != "undefined") { + clearInterval(this.colorShuffle); + } + }; +}(); diff --git a/components/dom_distiller/core/css/distilledpage.css b/components/dom_distiller/core/css/distilledpage.css index 4527dee9922e7..4c09a0d2f90d6 100644 --- a/components/dom_distiller/core/css/distilledpage.css +++ b/components/dom_distiller/core/css/distilledpage.css @@ -75,7 +75,6 @@ th { body, html { - font-family: 'Open Sans', sans-serif; font-size: 14px; line-height: 1.4; text-rendering: optimizeLegibility; @@ -101,6 +100,18 @@ html { background-color: rgb(203, 173, 141); } +.serif { + font-family: serif; +} + +.sans-serif { + font-family: 'Open Sans', sans-serif; +} + +.monospace { + font-family: monospace; +} + /* Define vertical rhythm (baseline grid of 4px). */ blockquote, @@ -257,3 +268,409 @@ pre { .hidden { display: none; } + +/* Loading Indicator. */ +#loader { + height: 22px; + margin-left: auto; + margin-right: auto; + position: relative; + width: 22px; +} + +#loader * { + display: block; + position: absolute; +} + +#loader .circle { + border-radius: 50%; + height: 100%; + opacity: 0; + overflow: hidden; + width: 100%; +} + +#loader .mask { + height: 100%; + opacity: 0; + overflow: hidden; + width: 100%; +} + +#loader .mask.first { + transition-delay: 0.22s; + transition-duration: 0s; + transition-property: border-color; +} + +#loader .circle.initial .mask { + height: 50%; + top: 0; +} + +#loader .circle.red .mask.first { + border-bottom: 1px solid #555; + height: 50%; + top: 0; +} + +#loader .circle.red .mask.second { + bottom: 0; + height: 50%; +} + +#loader .circle.yellow .mask.first { + border-left: 1px solid #888; + right: 0; + width: 50%; +} + +#loader .circle.yellow .mask.second { + left: 0; + width: 50%; +} + +#loader .circle.green .mask.first { + border-top: 1px solid #555; + bottom: 0; + height: 50%; +} + +#loader .circle.green .mask.second { + height: 50%; + top: 0; +} + +#loader .circle.blue .mask.first { + border-right: 1px solid #888; + left: 0; + width: 50%; +} + +#loader .circle.blue .mask.second { + right: 0; + width: 50%; +} + +#loader .circle .mask .base { + border-radius: 50%; + height: 100%; + opacity: 0; + transition-property: opacity; + transition-timing-function: linear; + transition-duration: 0s; + transition-delay: 0s; + width: 100%; +} + +#loader .circle .mask .mover { + border-radius: 50%; + height: 100%; + transition-delay: 0s; + transition-duration: 0.22s; + transition-property: background-color, left, top, right, bottom, width, + height; + transition-timing-function: ease-in; + width: 100%; +} + +#loader .circle .mask.second .mover, +#loader .circle.initial .mask .mover { + transition-delay: 0.22s; + transition-timing-function: ease-out; +} + +#loader .circle.red .mask.first .base { + height: 200%; + top: 0; +} +#loader .circle.red .mask.second .base { + bottom: 0; + height: 200%; +} + +#loader .circle.yellow .mask.first .base { + right: 0; + width: 200%; +} + +#loader .circle.yellow .mask.second .base { + left: 0; + width: 200%; +} + +#loader .circle.green .mask.first .base { + bottom: 0; + height: 200%; +} + +#loader .circle.green .mask.second .base { + height: 200%; + top: 0; +} + +#loader .circle.blue .mask.first .base { + left: 0; + width: 200%; +} + +#loader .circle.blue .mask.second .base { + right: 0; + width: 200%; +} + +#loader .circle.initial .mask .mover { + height: 0; + top: 100%; +} + +#loader .circle.red .mask.first .mover { + height: 200%; + top: 0; +} + +#loader .circle.red .mask.second .mover { + bottom: 100%; + height: 0; +} + +#loader .circle.yellow .mask.first .mover { + right: 0; + width: 200%; +} + +#loader .circle.yellow .mask.second .mover { + left: 100%; + width: 0; +} + +#loader .circle.green .mask.first .mover { + bottom: 0; + height: 200%; +} + +#loader .circle.green .mask.second .mover { + height: 0; + top: 100%; +} + +#loader .circle.blue .mask.first .mover { + left: 0; + width: 200%; +} + +#loader .circle.blue .mask.second .mover { + right: 100%; + width: 0; +} + +/* Initial State. */ +#loader.initial .circle.initial, +#loader.initial .circle.initial .mask { + opacity: 1; +} +#loader.initial .circle.initial .mask .mover { + height: 200%; + top: 0; +} + +/* Moving from Red to Yellow. */ +#loader.yellow .circle.yellow, +#loader.yellow .circle.yellow .mask, +#loader.yellow .circle.yellow .mask .base { + opacity: 1; +} +#loader.yellow .circle.yellow .mask.first .mover { + right: 100%; + width: 0; +} +#loader.yellow .circle.yellow .mask.second .mover { + left: 0; + width: 200%; +} + +/* Moving from Yellow to Green. */ +#loader.green .circle.green, +#loader.green .circle.green .mask, +#loader.green .circle.green .mask .base { + opacity: 1; +} +#loader.green .circle.green .mask.first .mover { + bottom: 100%; + height: 0; +} +#loader.green .circle.green .mask.second .mover { + height: 200%; + top: 0; +} + + +/* Moving from Green to Blue. */ +#loader.blue .circle.blue, +#loader.blue .circle.blue .mask, +#loader.blue .circle.blue .mask .base { + opacity: 1; +} +#loader.blue .circle.blue .mask.first .mover { + left: 100%; + width: 0; +} +#loader.blue .circle.blue .mask.second .mover { + right: 0; + width: 200%; +} + +/* Moving from Blue to Red. */ +#loader.red .circle.red, +#loader.red .circle.red .mask, +#loader.red .circle.red .mask .base { + opacity: 1; +} +#loader.red.firstTime .circle.red .mask.second .base { + opacity: 0; +} +#loader.red .circle.red .mask.first .mover { + height: 0; + top: 100%; +} +#loader.red .circle.red .mask.second .mover { + bottom: 0; + height: 200%; +} + +#loader .circle.red .mask.first { + border-bottom-color: rgb(60,120,248); +} + +#loader .circle.yellow .mask.first { + border-left-color: rgb(250,36,36); +} + +#loader .circle.green .mask.first { + border-top-color: rgb(255,211,75); +} + +#loader .circle.blue .mask.first { + border-right-color: rgb(0,177,95); +} + +#loader .circle.red .mask.first .base { + background-color: rgb(250,36,36); +} + +#loader .circle.red .mask.second .base { + background-color: rgb(60,120,248); +} + +#loader .circle.yellow .mask.first .base { + background-color: rgb(255,211,75); +} + +#loader .circle.yellow .mask.second .base { + background-color: rgb(250,36,36); +} + +#loader .circle.green .mask.first .base { + background-color: rgb(0,177,95); +} + +#loader .circle.green .mask.second .base { + background-color: rgb(255,211,75); +} + +#loader .circle.blue .mask.first .base { + background-color: rgb(60,120,248); +} + +#loader .circle.blue .mask.second .base { + background-color: rgb(0,177,95); +} + +#loader .circle.initial .mask .mover { + background-color: rgb(33,89,189); +} + +#loader .circle.red .mask.first .mover { + background-color: rgb(60,120,248); +} + +#loader .circle.red .mask.second .mover { + background-color: rgb(158,18,18); +} + +#loader .circle.yellow .mask.first .mover { + background-color: rgb(250,36,36); +} + +#loader .circle.yellow .mask.second .mover { + background-color: rgb(222,161,26); +} + +#loader .circle.green .mask.first .mover { + background-color: rgb(255,211,75); +} + +#loader .circle.green .mask.second .mover { + background-color: rgb(0,137,72); +} + +#loader .circle.blue .mask.first .mover { + background-color: rgb(0,177,95); +} + +#loader .circle.blue .mask.second .mover { + background-color: rgb(33,89,189); +} + +#loader.initial .circle.initial .mask .mover { + background-color: rgb(60,120,248); +} + +#loader.yellow .circle.yellow .mask.first { + border-color: rgb(255,211,75); +} + +#loader.yellow .circle.yellow .mask.first .mover { + background-color: rgb(158,18,18); +} + +#loader.yellow .circle.yellow .mask.second .mover { + background-color: rgb(255,211,75); +} + +#loader.green .circle.green .mask.first { + border-color: rgb(0,177,95); +} + +#loader.green .circle.green .mask.first .mover { + background-color: rgb(222,161,26); +} + +#loader.green .circle.green .mask.second .mover { + background-color: rgb(0,177,95); +} + +#loader.blue .circle.blue .mask.first { + border-color: rgb(60,120,248); +} + +#loader.blue .circle.blue .mask.first .mover { + background-color: rgb(0,137,72); +} + +#loader.blue .circle.blue .mask.second .mover { + background-color: rgb(60,120,248); +} + +#loader.red .circle.red .mask.first { + border-color: rgb(250,36,36); +} + +#loader.red .circle.red .mask.first .mover { + background-color: rgb(33,89,189); +} + +#loader.red .circle.red .mask.second .mover { + background-color: rgb(250,36,36); +} diff --git a/components/dom_distiller/core/distilled_page_prefs.cc b/components/dom_distiller/core/distilled_page_prefs.cc index 5c23fb43f2211..01d1a07c96cea 100644 --- a/components/dom_distiller/core/distilled_page_prefs.cc +++ b/components/dom_distiller/core/distilled_page_prefs.cc @@ -14,6 +14,8 @@ namespace { // Path to the integer corresponding to user's preference theme. +const char kFontPref[] = "dom_distiller.font_family"; +// Path to the integer corresponding to user's preference font family. const char kThemePref[] = "dom_distiller.theme"; } @@ -33,6 +35,31 @@ void DistilledPagePrefs::RegisterProfilePrefs( kThemePref, DistilledPagePrefs::LIGHT, user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); + registry->RegisterIntegerPref( + kFontPref, + DistilledPagePrefs::SANS_SERIF, + user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); +} + +void DistilledPagePrefs::SetFontFamily( + DistilledPagePrefs::FontFamily new_font_family) { + pref_service_->SetInteger(kFontPref, new_font_family); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DistilledPagePrefs::NotifyOnChangeFontFamily, + weak_ptr_factory_.GetWeakPtr(), + new_font_family)); +} + +DistilledPagePrefs::FontFamily DistilledPagePrefs::GetFontFamily() { + int font_family = pref_service_->GetInteger(kFontPref); + if (font_family < 0 || font_family >= DistilledPagePrefs::FONT_FAMILY_COUNT) { + // Persisted data was incorrect, trying to clean it up by storing the + // default. + SetFontFamily(DistilledPagePrefs::SANS_SERIF); + return DistilledPagePrefs::SANS_SERIF; + } + return static_cast(font_family); } void DistilledPagePrefs::SetTheme(DistilledPagePrefs::Theme new_theme) { @@ -52,7 +79,7 @@ DistilledPagePrefs::Theme DistilledPagePrefs::GetTheme() { SetTheme(DistilledPagePrefs::LIGHT); return DistilledPagePrefs::LIGHT; } - return (Theme) theme; + return static_cast(theme); } void DistilledPagePrefs::AddObserver(Observer* obs) { @@ -63,6 +90,11 @@ void DistilledPagePrefs::RemoveObserver(Observer* obs) { observers_.RemoveObserver(obs); } +void DistilledPagePrefs::NotifyOnChangeFontFamily( + DistilledPagePrefs::FontFamily new_font_family) { + FOR_EACH_OBSERVER(Observer, observers_, OnChangeFontFamily(new_font_family)); +} + void DistilledPagePrefs::NotifyOnChangeTheme( DistilledPagePrefs::Theme new_theme) { FOR_EACH_OBSERVER(Observer, observers_, OnChangeTheme(new_theme)); diff --git a/components/dom_distiller/core/distilled_page_prefs.h b/components/dom_distiller/core/distilled_page_prefs.h index 7a40b2711c8a2..eeec8a9674ec9 100644 --- a/components/dom_distiller/core/distilled_page_prefs.h +++ b/components/dom_distiller/core/distilled_page_prefs.h @@ -20,6 +20,13 @@ namespace dom_distiller { // Interface for preferences used for distilled page. class DistilledPagePrefs { public: + // Possible font families for distilled page. + enum FontFamily { +#define DEFINE_FONT_FAMILY(name, value) name = value, +#include "components/dom_distiller/core/font_family_list.h" +#undef DEFINE_FONT_FAMILY + }; + // Possible themes for distilled page. enum Theme { #define DEFINE_THEME(name, value) name = value, @@ -29,6 +36,7 @@ class DistilledPagePrefs { class Observer { public: + virtual void OnChangeFontFamily(FontFamily font) = 0; virtual void OnChangeTheme(Theme theme) = 0; }; @@ -37,6 +45,11 @@ class DistilledPagePrefs { static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + // Sets the user's preference for the font family of distilled pages. + void SetFontFamily(FontFamily new_font); + // Returns the user's preference for the font family of distilled pages. + FontFamily GetFontFamily(); + // Sets the user's preference for the theme of distilled pages. void SetTheme(Theme new_theme); // Returns the user's preference for the theme of distilled pages. @@ -46,6 +59,8 @@ class DistilledPagePrefs { void RemoveObserver(Observer* obs); private: + // Notifies all Observers of new font family. + void NotifyOnChangeFontFamily(FontFamily font_family); // Notifies all Observers of new theme. void NotifyOnChangeTheme(Theme theme); diff --git a/components/dom_distiller/core/distilled_page_prefs_android.cc b/components/dom_distiller/core/distilled_page_prefs_android.cc index e6fb247376cc2..051d821c2e4ca 100644 --- a/components/dom_distiller/core/distilled_page_prefs_android.cc +++ b/components/dom_distiller/core/distilled_page_prefs_android.cc @@ -22,12 +22,24 @@ DistilledPagePrefsAndroid::DistilledPagePrefsAndroid( DistilledPagePrefsAndroid::~DistilledPagePrefsAndroid() { } +void DistilledPagePrefsAndroid::SetFontFamily(JNIEnv* env, + jobject obj, + jint font_family) { + distilled_page_prefs_->SetFontFamily( + static_cast(font_family)); +} + +jint DistilledPagePrefsAndroid::GetFontFamily(JNIEnv* env, jobject obj) { + return (int) distilled_page_prefs_->GetFontFamily(); +} + void DistilledPagePrefsAndroid::SetTheme(JNIEnv* env, jobject obj, jint theme) { - distilled_page_prefs_->SetTheme((DistilledPagePrefs::Theme)theme); + distilled_page_prefs_->SetTheme( + static_cast(theme)); } jint DistilledPagePrefsAndroid::GetTheme(JNIEnv* env, jobject obj) { - return (int)distilled_page_prefs_->GetTheme(); + return (int) distilled_page_prefs_->GetTheme(); } jlong Init(JNIEnv* env, jobject obj, jlong distilled_page_prefs_ptr) { @@ -71,6 +83,13 @@ void DistilledPagePrefsObserverAndroid::DestroyObserverAndroid(JNIEnv* env, delete this; } +void DistilledPagePrefsObserverAndroid::OnChangeFontFamily( + DistilledPagePrefs::FontFamily new_font_family) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_DistilledPagePrefsObserverWrapper_onChangeFontFamily( + env, java_ref_.obj(), (int)new_font_family); +} + void DistilledPagePrefsObserverAndroid::OnChangeTheme( DistilledPagePrefs::Theme new_theme) { JNIEnv* env = base::android::AttachCurrentThread(); diff --git a/components/dom_distiller/core/distilled_page_prefs_android.h b/components/dom_distiller/core/distilled_page_prefs_android.h index 6f0b1c8d8dfc6..cc7f9211c19ec 100644 --- a/components/dom_distiller/core/distilled_page_prefs_android.h +++ b/components/dom_distiller/core/distilled_page_prefs_android.h @@ -20,8 +20,11 @@ class DistilledPagePrefsAndroid { DistilledPagePrefs* distilled_page_prefs_ptr); virtual ~DistilledPagePrefsAndroid(); static bool Register(JNIEnv* env); + void SetFontFamily(JNIEnv* env, jobject obj, jint font_family); + jint GetFontFamily(JNIEnv* env, jobject obj); void SetTheme(JNIEnv* env, jobject obj, jint theme); jint GetTheme(JNIEnv* env, jobject obj); + void AddObserver(JNIEnv* env, jobject obj, jlong obs); void RemoveObserver(JNIEnv* env, jobject obj, jlong obs); @@ -37,6 +40,8 @@ class DistilledPagePrefsObserverAndroid : public DistilledPagePrefs::Observer { virtual ~DistilledPagePrefsObserverAndroid(); // DistilledPagePrefs::Observer implementation. + virtual void OnChangeFontFamily( + DistilledPagePrefs::FontFamily new_font_family) OVERRIDE; virtual void OnChangeTheme(DistilledPagePrefs::Theme new_theme) OVERRIDE; virtual void DestroyObserverAndroid(JNIEnv* env, jobject obj); diff --git a/components/dom_distiller/core/distilled_page_prefs_unittests.cc b/components/dom_distiller/core/distilled_page_prefs_unittests.cc index 700a9fcf089fb..fe9df73096afb 100644 --- a/components/dom_distiller/core/distilled_page_prefs_unittests.cc +++ b/components/dom_distiller/core/distilled_page_prefs_unittests.cc @@ -15,7 +15,16 @@ namespace { class TestingObserver : public DistilledPagePrefs::Observer { public: - TestingObserver() : theme_(DistilledPagePrefs::LIGHT) {} + TestingObserver() + : font_(DistilledPagePrefs::SANS_SERIF), + theme_(DistilledPagePrefs::LIGHT) {} + + virtual void OnChangeFontFamily( + DistilledPagePrefs::FontFamily new_font) OVERRIDE { + font_ = new_font; + } + + DistilledPagePrefs::FontFamily GetFontFamily() { return font_; } virtual void OnChangeTheme(DistilledPagePrefs::Theme new_theme) OVERRIDE { theme_ = new_theme; @@ -24,6 +33,7 @@ class TestingObserver : public DistilledPagePrefs::Observer { DistilledPagePrefs::Theme GetTheme() { return theme_; } private: + DistilledPagePrefs::FontFamily font_; DistilledPagePrefs::Theme theme_; }; @@ -44,6 +54,36 @@ class DistilledPagePrefsTest : public testing::Test { base::MessageLoop message_loop_; }; +TEST_F(DistilledPagePrefsTest, TestingOnChangeFontIsBeingCalled) { + TestingObserver obs; + distilled_page_prefs_->AddObserver(&obs); + distilled_page_prefs_->SetFontFamily(DistilledPagePrefs::MONOSPACE); + EXPECT_EQ(DistilledPagePrefs::SANS_SERIF, obs.GetFontFamily()); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(DistilledPagePrefs::MONOSPACE, obs.GetFontFamily()); + distilled_page_prefs_->SetFontFamily(DistilledPagePrefs::SERIF); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(DistilledPagePrefs::SERIF, obs.GetFontFamily()); + distilled_page_prefs_->RemoveObserver(&obs); +} + +TEST_F(DistilledPagePrefsTest, TestingMultipleObserversFont) { + TestingObserver obs; + distilled_page_prefs_->AddObserver(&obs); + TestingObserver obs2; + distilled_page_prefs_->AddObserver(&obs2); + distilled_page_prefs_->SetFontFamily(DistilledPagePrefs::SERIF); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(DistilledPagePrefs::SERIF, obs.GetFontFamily()); + EXPECT_EQ(DistilledPagePrefs::SERIF, obs2.GetFontFamily()); + distilled_page_prefs_->RemoveObserver(&obs); + distilled_page_prefs_->SetFontFamily(DistilledPagePrefs::MONOSPACE); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(DistilledPagePrefs::SERIF, obs.GetFontFamily()); + EXPECT_EQ(DistilledPagePrefs::MONOSPACE, obs2.GetFontFamily()); + distilled_page_prefs_->RemoveObserver(&obs2); +} + TEST_F(DistilledPagePrefsTest, TestingOnChangeThemeIsBeingCalled) { TestingObserver obs; distilled_page_prefs_->AddObserver(&obs); @@ -57,7 +97,7 @@ TEST_F(DistilledPagePrefsTest, TestingOnChangeThemeIsBeingCalled) { distilled_page_prefs_->RemoveObserver(&obs); } -TEST_F(DistilledPagePrefsTest, TestingMultipleObservers) { +TEST_F(DistilledPagePrefsTest, TestingMultipleObserversTheme) { TestingObserver obs; distilled_page_prefs_->AddObserver(&obs); TestingObserver obs2; diff --git a/components/dom_distiller/core/dom_distiller_service.cc b/components/dom_distiller/core/dom_distiller_service.cc index 983452d0fb483..98be75416dace 100644 --- a/components/dom_distiller/core/dom_distiller_service.cc +++ b/components/dom_distiller/core/dom_distiller_service.cc @@ -108,6 +108,18 @@ const std::string DomDistillerService::AddToList( return task_tracker->GetEntryId(); } +bool DomDistillerService::HasEntry(const std::string& entry_id) { + return store_->GetEntryById(entry_id, NULL); +} + +std::string DomDistillerService::GetUrlForEntry(const std::string& entry_id) { + ArticleEntry entry; + if (store_->GetEntryById(entry_id, &entry)) { + return entry.pages().Get(0).url(); + } + return ""; +} + std::vector DomDistillerService::GetEntries() const { return store_->GetEntries(); } diff --git a/components/dom_distiller/core/dom_distiller_service.h b/components/dom_distiller/core/dom_distiller_service.h index a0c55bd6466cc..bbf64e6e1573f 100644 --- a/components/dom_distiller/core/dom_distiller_service.h +++ b/components/dom_distiller/core/dom_distiller_service.h @@ -55,6 +55,14 @@ class DomDistillerServiceInterface { scoped_ptr distiller_page, const ArticleAvailableCallback& article_cb) = 0; + // Returns whether an article stored has the given entry id. + virtual bool HasEntry(const std::string& entry_id) = 0; + + // Returns the source URL given an entry ID. If the entry ID article has + // multiple pages, this will return the URL of the first page. Returns an + // empty string if there is no entry associated with the given entry ID. + virtual std::string GetUrlForEntry(const std::string& entry_id) = 0; + // Gets the full list of entries. virtual std::vector GetEntries() const = 0; @@ -118,6 +126,8 @@ class DomDistillerService : public DomDistillerServiceInterface { const GURL& url, scoped_ptr distiller_page, const ArticleAvailableCallback& article_cb) OVERRIDE; + virtual bool HasEntry(const std::string& entry_id) OVERRIDE; + virtual std::string GetUrlForEntry(const std::string& entry_id) OVERRIDE; virtual std::vector GetEntries() const OVERRIDE; virtual scoped_ptr RemoveEntry( const std::string& entry_id) OVERRIDE; diff --git a/components/dom_distiller/core/dom_distiller_service_android.cc b/components/dom_distiller/core/dom_distiller_service_android.cc index 5f65594045254..768ade899a655 100644 --- a/components/dom_distiller/core/dom_distiller_service_android.cc +++ b/components/dom_distiller/core/dom_distiller_service_android.cc @@ -5,11 +5,16 @@ #include "components/dom_distiller/core/dom_distiller_service_android.h" #include "base/android/jni_android.h" +#include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" #include "components/dom_distiller/core/distilled_page_prefs.h" #include "components/dom_distiller/core/distilled_page_prefs_android.h" #include "components/dom_distiller/core/dom_distiller_service.h" #include "jni/DomDistillerService_jni.h" +using base::android::ConvertUTF8ToJavaString; +using base::android::ScopedJavaLocalRef; + namespace dom_distiller { namespace android { @@ -25,6 +30,23 @@ DomDistillerServiceAndroid::DomDistillerServiceAndroid( DomDistillerServiceAndroid::~DomDistillerServiceAndroid() { } +bool DomDistillerServiceAndroid::HasEntry(JNIEnv* env, + jobject obj, + jstring j_entry_id) { + const std::string entry_id = + base::android::ConvertJavaStringToUTF8(env, j_entry_id); + return service_->HasEntry(entry_id); +} + +ScopedJavaLocalRef DomDistillerServiceAndroid::GetUrlForEntry( + JNIEnv* env, + jobject obj, + jstring j_entry_id) { + const std::string entry_id = + base::android::ConvertJavaStringToUTF8(env, j_entry_id); + return ConvertUTF8ToJavaString(env, service_->GetUrlForEntry(entry_id)); +} + jlong DomDistillerServiceAndroid::GetDistilledPagePrefsPtr(JNIEnv* env, jobject obj) { return reinterpret_cast(service_->GetDistilledPagePrefs()); diff --git a/components/dom_distiller/core/dom_distiller_service_android.h b/components/dom_distiller/core/dom_distiller_service_android.h index bf68947c282c3..b34e4497ebf81 100644 --- a/components/dom_distiller/core/dom_distiller_service_android.h +++ b/components/dom_distiller/core/dom_distiller_service_android.h @@ -25,6 +25,10 @@ class DomDistillerServiceAndroid { // Returns native pointer to native DistilledPagePrefs registered with // DomDistillerService. jlong GetDistilledPagePrefsPtr(JNIEnv* env, jobject obj); + bool HasEntry(JNIEnv* env, jobject obj, jstring entry_id); + base::android::ScopedJavaLocalRef GetUrlForEntry(JNIEnv* env, + jobject obj, + jstring entry_id); private: // Friend class so that DomDistillerServiceFactoryAndroid has access to diff --git a/components/dom_distiller/core/dom_distiller_service_unittest.cc b/components/dom_distiller/core/dom_distiller_service_unittest.cc index a584f005b6cfe..4745c25c530e3 100644 --- a/components/dom_distiller/core/dom_distiller_service_unittest.cc +++ b/components/dom_distiller/core/dom_distiller_service_unittest.cc @@ -458,5 +458,117 @@ TEST_F(DomDistillerServiceTest, TestMultiplePageArticle) { EXPECT_EQ(0u, store_->GetEntries().size()); } +TEST_F(DomDistillerServiceTest, TestHasEntry) { + FakeDistiller* distiller = new FakeDistiller(false); + EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) + .WillOnce(Return(distiller)); + + GURL url("http://www.example.com/p1"); + + MockArticleAvailableCallback article_cb; + EXPECT_CALL(article_cb, DistillationCompleted(true)); + + std::string entry_id = service_->AddToList( + url, + service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), + ArticleCallback(&article_cb)); + + ASSERT_FALSE(distiller->GetArticleCallback().is_null()); + EXPECT_EQ(url, distiller->GetUrl()); + + scoped_ptr proto = CreateArticleWithURL(url.spec()); + RunDistillerCallback(distiller, proto.Pass()); + + // Check that HasEntry returns true for the article just added. + EXPECT_TRUE(service_->HasEntry(entry_id)); + + // Remove article and check that there is no longer an entry for the given + // entry id. + service_->RemoveEntry(entry_id); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, store_->GetEntries().size()); + EXPECT_FALSE(service_->HasEntry(entry_id)); +} + +TEST_F(DomDistillerServiceTest, TestGetUrlForOnePageEntry) { + FakeDistiller* distiller = new FakeDistiller(false); + EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) + .WillOnce(Return(distiller)); + + GURL url("http://www.example.com/p1"); + + MockArticleAvailableCallback article_cb; + EXPECT_CALL(article_cb, DistillationCompleted(true)); + + std::string entry_id = service_->AddToList( + url, + service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), + ArticleCallback(&article_cb)); + + ASSERT_FALSE(distiller->GetArticleCallback().is_null()); + EXPECT_EQ(url, distiller->GetUrl()); + + scoped_ptr proto = CreateArticleWithURL(url.spec()); + RunDistillerCallback(distiller, proto.Pass()); + + // Check if retrieved URL is same as given URL. + GURL retrieved_url(service_->GetUrlForEntry(entry_id)); + EXPECT_EQ(url, retrieved_url); + + // Remove article and check that there is no longer an entry for the given + // entry id. + service_->RemoveEntry(entry_id); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, store_->GetEntries().size()); + EXPECT_EQ("", service_->GetUrlForEntry(entry_id)); +} + +TEST_F(DomDistillerServiceTest, TestGetUrlForMultiPageEntry) { + FakeDistiller* distiller = new FakeDistiller(false); + EXPECT_CALL(*distiller_factory_, CreateDistillerImpl()) + .WillOnce(Return(distiller)); + + const int kPageCount = 8; + + std::string base_url("http://www.example.com/p"); + GURL pages_url[kPageCount]; + for (int page_num = 0; page_num < kPageCount; ++page_num) { + pages_url[page_num] = GURL(base_url + base::IntToString(page_num)); + } + + MockArticleAvailableCallback article_cb; + EXPECT_CALL(article_cb, DistillationCompleted(true)); + + std::string entry_id = service_->AddToList( + pages_url[0], + service_->CreateDefaultDistillerPage(gfx::Size()).Pass(), + ArticleCallback(&article_cb)); + + ArticleEntry entry; + ASSERT_FALSE(distiller->GetArticleCallback().is_null()); + EXPECT_EQ(pages_url[0], distiller->GetUrl()); + + // Create the article with pages to pass to the distiller. + scoped_ptr proto = + CreateArticleWithURL(pages_url[0].spec()); + for (int page_num = 1; page_num < kPageCount; ++page_num) { + DistilledPageProto* distilled_page = proto->add_pages(); + distilled_page->set_url(pages_url[page_num].spec()); + } + + RunDistillerCallback(distiller, proto.Pass()); + EXPECT_TRUE(store_->GetEntryByUrl(pages_url[0], &entry)); + + // Check if retrieved URL is same as given URL for the first page. + GURL retrieved_url(service_->GetUrlForEntry(entry_id)); + EXPECT_EQ(pages_url[0], retrieved_url); + + // Remove the article and check that no URL can be retrieved for the entry. + service_->RemoveEntry(entry_id); + base::RunLoop().RunUntilIdle(); + EXPECT_EQ(0u, store_->GetEntries().size()); + EXPECT_EQ("", service_->GetUrlForEntry(entry_id)); +} + } // namespace test } // namespace dom_distiller diff --git a/components/dom_distiller/core/font_family_list.h b/components/dom_distiller/core/font_family_list.h new file mode 100644 index 0000000000000..98007419a2349 --- /dev/null +++ b/components/dom_distiller/core/font_family_list.h @@ -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. + +// This file intentionally does not have header guards, it's included +// inside a macro to generate enum values. + +#ifndef DEFINE_FONT_FAMILY +#error "DEFINE_FONT_FAMILY should be defined before including this file" +#endif + +// First argument represents the enum name, second argument represents enum +// value. FONT_FAMILY_COUNT used only by native enum. +DEFINE_FONT_FAMILY(SANS_SERIF, 0) +DEFINE_FONT_FAMILY(SERIF, 1) +DEFINE_FONT_FAMILY(MONOSPACE, 2) +DEFINE_FONT_FAMILY(FONT_FAMILY_COUNT, 3) diff --git a/components/dom_distiller/core/javascript/is_distillable_trigger.js b/components/dom_distiller/core/javascript/is_distillable_trigger.js new file mode 100644 index 0000000000000..b09aad48d8803 --- /dev/null +++ b/components/dom_distiller/core/javascript/is_distillable_trigger.js @@ -0,0 +1,21 @@ +// 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. + +(function() { + var elems = document.querySelectorAll( + 'meta[property="og:type"],meta[name="og:type"]'); + for (var i in elems) { + if (elems[i].content && elems[i].content.toUpperCase() == 'ARTICLE') { + return true; + } + } + var elems = document.querySelectorAll( + '*[itemtype="http://schema.org/Article"]'); + for (var i in elems) { + if (elems[i].itemscope) { + return true; + } + } + return false; +})() diff --git a/components/dom_distiller/core/url_utils.cc b/components/dom_distiller/core/url_utils.cc index d5ca57d4e3554..1a7545b7c059e 100644 --- a/components/dom_distiller/core/url_utils.cc +++ b/components/dom_distiller/core/url_utils.cc @@ -8,7 +8,9 @@ #include "base/guid.h" #include "components/dom_distiller/core/url_constants.h" +#include "grit/component_resources.h" #include "net/base/url_util.h" +#include "ui/base/resource/resource_bundle.h" #include "url/gurl.h" namespace dom_distiller { @@ -33,14 +35,22 @@ const GURL GetDistillerViewUrlFromUrl(const std::string& scheme, return net::AppendOrReplaceQueryParameter(url, kUrlKey, view_url.spec()); } +std::string GetValueForKeyInUrl(const GURL& url, const std::string& key) { + if (!url.is_valid()) + return ""; + std::string value; + if (net::GetValueForKeyInQuery(url, key, &value)) { + return value; + } + return ""; +} + std::string GetValueForKeyInUrlPathQuery(const std::string& path, const std::string& key) { // Tools for retrieving a value in a query only works with full GURLs, so // using a dummy scheme and host to create a fake URL which can be parsed. GURL dummy_url(kDummyInternalUrlPrefix + path); - std::string value; - net::GetValueForKeyInQuery(dummy_url, key, &value); - return value; + return GetValueForKeyInUrl(dummy_url, key); } bool IsUrlDistillable(const GURL& url) { @@ -51,6 +61,11 @@ bool IsDistilledPage(const GURL& url) { return url.is_valid() && url.scheme() == kDomDistillerScheme; } +std::string GetIsDistillableJs() { + return ResourceBundle::GetSharedInstance() + .GetRawDataResource(IDR_IS_DISTILLABLE_JS).as_string(); +} + } // namespace url_utils } // namespace dom_distiller diff --git a/components/dom_distiller/core/url_utils.h b/components/dom_distiller/core/url_utils.h index 4dc73b39d88e8..6ae185f329306 100644 --- a/components/dom_distiller/core/url_utils.h +++ b/components/dom_distiller/core/url_utils.h @@ -21,6 +21,12 @@ const GURL GetDistillerViewUrlFromEntryId(const std::string& scheme, const GURL GetDistillerViewUrlFromUrl(const std::string& scheme, const GURL& view_url); +// Returns the value of the query parameter for the given |key| for a given URL. +// If the URL is invalid or if the key is not found, returns an empty string. +// If there are multiple keys found in the URL, returns the value for the first +// key. +std::string GetValueForKeyInUrl(const GURL& url, const std::string& key); + // Returns the value of the query parameter for the given path. std::string GetValueForKeyInUrlPathQuery(const std::string& path, const std::string& key); @@ -31,6 +37,10 @@ bool IsUrlDistillable(const GURL& url); // Returns whether the given |url| is for a distilled page. bool IsDistilledPage(const GURL& url); +// Returns a JavaScript snippet that returns whether or not a page should be +// used with DomDistillerService and can be executed in a live page. +std::string GetIsDistillableJs(); + } // namespace url_utils } // namespace dom_distiller diff --git a/components/dom_distiller/core/url_utils_android.cc b/components/dom_distiller/core/url_utils_android.cc index 50b0266060c1e..d700ff64a3e46 100644 --- a/components/dom_distiller/core/url_utils_android.cc +++ b/components/dom_distiller/core/url_utils_android.cc @@ -63,6 +63,23 @@ jboolean IsUrlDistillable(JNIEnv* env, jclass clazz, jstring j_url) { return dom_distiller::url_utils::IsUrlDistillable(url); } +jstring GetIsDistillableJs(JNIEnv* env, jclass clazz) { + return base::android::ConvertUTF8ToJavaString( + env, dom_distiller::url_utils::GetIsDistillableJs()).Release(); +} + +jstring GetValueForKeyInUrl(JNIEnv* env, + jclass clazz, + jstring j_url, + jstring j_key) { + GURL url(base::android::ConvertJavaStringToUTF8(env, j_url)); + std::string key = base::android::ConvertJavaStringToUTF8(env, j_key); + return base::android:: + ConvertUTF8ToJavaString( + env, dom_distiller::url_utils::GetValueForKeyInUrl(url, key)) + .Release(); +} + bool RegisterUrlUtils(JNIEnv* env) { return RegisterNativesImpl(env); } } // namespace android diff --git a/components/dom_distiller/core/url_utils_unittest.cc b/components/dom_distiller/core/url_utils_unittest.cc index 366433f6894ed..d19a3fe1a13ac 100644 --- a/components/dom_distiller/core/url_utils_unittest.cc +++ b/components/dom_distiller/core/url_utils_unittest.cc @@ -20,6 +20,25 @@ TEST(DomDistillerUrlUtilsTest, TestPathUtil) { EXPECT_EQ("foo", GetValueForKeyInUrlPathQuery(multiple_same_key, "key")); } +TEST(DomDistillerUrlUtilsTest, TestGetValueForKeyInUrlPathQuery) { + // Tests an invalid url. + const std::string invalid_url = "http://%40[::1]/"; + EXPECT_EQ("", GetValueForKeyInUrlPathQuery(invalid_url, "key")); + + // Test a valid URL with the key we are searching for. + const std::string valid_url_with_key = "http://www.google.com?key=foo"; + EXPECT_EQ("foo", GetValueForKeyInUrlPathQuery(valid_url_with_key, "key")); + + // Test a valid URL without the key we are searching for. + const std::string valid_url_no_key = "http://www.google.com"; + EXPECT_EQ("", GetValueForKeyInUrlPathQuery(valid_url_no_key, "key")); + + // Test a valid URL with 2 values of the key we are searching for. + const std::string valid_url_two_keys = + "http://www.google.com?key=foo&key=bar"; + EXPECT_EQ("foo", GetValueForKeyInUrlPathQuery(valid_url_two_keys, "key")); +} + } // namespace url_utils } // namespace dom_distiller diff --git a/components/dom_distiller/core/viewer.cc b/components/dom_distiller/core/viewer.cc index be8463a3c6786..57d547320558c 100644 --- a/components/dom_distiller/core/viewer.cc +++ b/components/dom_distiller/core/viewer.cc @@ -34,11 +34,21 @@ const char kDarkJsTheme[] = "dark"; const char kLightJsTheme[] = "light"; const char kSepiaJsTheme[] = "sepia"; -// CSS classes. Must agree with classes in distilledpage.css. +// CSS Theme classes. Must agree with classes in distilledpage.css. const char kDarkCssClass[] = "dark"; const char kLightCssClass[] = "light"; const char kSepiaCssClass[] = "sepia"; +// JS FontFamilies. Must agree with useFontFamily() in dom_distiller_viewer.js. +const char kSerifJsFontFamily[] = "serif"; +const char kSansSerifJsFontFamily[] = "sans-serif"; +const char kMonospaceJsFontFamily[] = "monospace"; + +// CSS FontFamily classes. Must agree with classes in distilledpage.css. +const char kSerifCssClass[] = "serif"; +const char kSansSerifCssClass[] = "sans-serif"; +const char kMonospaceCssClass[] = "monospace"; + // Maps themes to JS themes. const std::string GetJsTheme(DistilledPagePrefs::Theme theme) { if (theme == DistilledPagePrefs::DARK) { @@ -50,7 +60,7 @@ const std::string GetJsTheme(DistilledPagePrefs::Theme theme) { } // Maps themes to CSS classes. -const std::string GetCssClass(DistilledPagePrefs::Theme theme) { +const std::string GetThemeCssClass(DistilledPagePrefs::Theme theme) { if (theme == DistilledPagePrefs::DARK) { return kDarkCssClass; } else if (theme == DistilledPagePrefs::SEPIA) { @@ -59,12 +69,33 @@ const std::string GetCssClass(DistilledPagePrefs::Theme theme) { return kLightCssClass; } +// Maps font families to JS font families. +const std::string GetJsFontFamily(DistilledPagePrefs::FontFamily font_family) { + if (font_family == DistilledPagePrefs::SERIF) { + return kSerifJsFontFamily; + } else if (font_family == DistilledPagePrefs::MONOSPACE) { + return kMonospaceJsFontFamily; + } + return kSansSerifJsFontFamily; +} + +// Maps fontFamilies to CSS fontFamily classes. +const std::string GetFontCssClass(DistilledPagePrefs::FontFamily font_family) { + if (font_family == DistilledPagePrefs::SERIF) { + return kSerifCssClass; + } else if (font_family == DistilledPagePrefs::MONOSPACE) { + return kMonospaceCssClass; + } + return kSansSerifCssClass; +} + std::string ReplaceHtmlTemplateValues( const std::string& title, const std::string& content, const std::string& loading_indicator_class, const std::string& original_url, - const DistilledPagePrefs::Theme theme) { + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family) { base::StringPiece html_template = ResourceBundle::GetSharedInstance().GetRawDataResource( IDR_DOM_DISTILLER_VIEWER_HTML); @@ -72,14 +103,13 @@ std::string ReplaceHtmlTemplateValues( substitutions.push_back(title); // $1 substitutions.push_back(kViewerCssPath); // $2 substitutions.push_back(kViewerJsPath); // $3 - substitutions.push_back(GetCssClass(theme)); // $4 + substitutions.push_back(GetThemeCssClass(theme) + " " + + GetFontCssClass(font_family)); // $4 substitutions.push_back(content); // $5 substitutions.push_back(loading_indicator_class); // $6 + substitutions.push_back(original_url); // $7 substitutions.push_back( - l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_LOADING_STRING)); // $7 - substitutions.push_back(original_url); // $8 - substitutions.push_back( - l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_VIEW_ORIGINAL)); // $9 + l10n_util::GetStringUTF8(IDS_DOM_DISTILLER_VIEWER_VIEW_ORIGINAL)); // $8 return ReplaceStringPlaceholders(html_template, substitutions, NULL); } @@ -109,23 +139,22 @@ const std::string GetToggleLoadingIndicatorJs(const bool is_last_page) { const std::string GetUnsafePartialArticleHtml( const DistilledPageProto* page_proto, - const DistilledPagePrefs::Theme theme) { + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family) { DCHECK(page_proto); std::string title = net::EscapeForHTML(page_proto->title()); std::ostringstream unsafe_output_stream; unsafe_output_stream << page_proto->html(); std::string unsafe_article_html = unsafe_output_stream.str(); std::string original_url = page_proto->url(); - return ReplaceHtmlTemplateValues(title, - unsafe_article_html, - "visible", - original_url, - theme); + return ReplaceHtmlTemplateValues( + title, unsafe_article_html, "visible", original_url, theme, font_family); } const std::string GetUnsafeArticleHtml( const DistilledArticleProto* article_proto, - const DistilledPagePrefs::Theme theme) { + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family) { DCHECK(article_proto); std::string title; std::string unsafe_article_html; @@ -148,19 +177,19 @@ const std::string GetUnsafeArticleHtml( original_url = article_proto->pages(0).url(); } - return ReplaceHtmlTemplateValues(title, - unsafe_article_html, - "hidden", - original_url, - theme); + return ReplaceHtmlTemplateValues( + title, unsafe_article_html, "hidden", original_url, theme, font_family); } -const std::string GetErrorPageHtml(const DistilledPagePrefs::Theme theme) { +const std::string GetErrorPageHtml( + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family) { std::string title = l10n_util::GetStringUTF8( IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_TITLE); std::string content = l10n_util::GetStringUTF8( IDS_DOM_DISTILLER_VIEWER_FAILED_TO_FIND_ARTICLE_CONTENT); - return ReplaceHtmlTemplateValues(title, content, "hidden", "", theme); + return ReplaceHtmlTemplateValues( + title, content, "hidden", "", theme, font_family); } const std::string GetCss() { @@ -215,6 +244,11 @@ const std::string GetDistilledPageThemeJs(DistilledPagePrefs::Theme theme) { return "useTheme('" + GetJsTheme(theme) + "');"; } +const std::string GetDistilledPageFontFamilyJs( + DistilledPagePrefs::FontFamily font_family) { + return "useFontFamily('" + GetJsFontFamily(font_family) + "');"; +} + } // namespace viewer } // namespace dom_distiller diff --git a/components/dom_distiller/core/viewer.h b/components/dom_distiller/core/viewer.h index 5812f8cda30f6..45b7e95e867d3 100644 --- a/components/dom_distiller/core/viewer.h +++ b/components/dom_distiller/core/viewer.h @@ -29,7 +29,8 @@ namespace viewer { // unsafe, so callers must ensure rendering it does not compromise Chrome. const std::string GetUnsafeArticleHtml( const DistilledArticleProto* article_proto, - const DistilledPagePrefs::Theme theme); + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family); // Returns the base Viewer HTML page based on the given |page_proto|. This is // supposed to be displayed to the end user. The returned HTML should be @@ -39,7 +40,8 @@ const std::string GetUnsafeArticleHtml( // article. const std::string GetUnsafePartialArticleHtml( const DistilledPageProto* page_proto, - const DistilledPagePrefs::Theme theme); + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family); // Returns a JavaScript blob for updating a partial view request with additional // distilled content. Meant for use when viewing a slow or long multi-page @@ -55,7 +57,9 @@ const std::string GetUnsafeIncrementalDistilledPageJs( const std::string GetToggleLoadingIndicatorJs(const bool is_last_page); // Returns a full HTML page which displays a generic error. -const std::string GetErrorPageHtml(const DistilledPagePrefs::Theme theme); +const std::string GetErrorPageHtml( + const DistilledPagePrefs::Theme theme, + const DistilledPagePrefs::FontFamily font_family); // Returns the default CSS to be used for a viewer. const std::string GetCss(); @@ -71,6 +75,10 @@ scoped_ptr CreateViewRequest( ViewRequestDelegate* view_request_delegate, const gfx::Size& render_view_size); +// Returns JavaScript coresponding to setting the font family. +const std::string GetDistilledPageFontFamilyJs( + DistilledPagePrefs::FontFamily font); + // Returns JavaScript corresponding to setting a specific theme. const std::string GetDistilledPageThemeJs(DistilledPagePrefs::Theme theme); diff --git a/components/dom_distiller/core/viewer_unittest.cc b/components/dom_distiller/core/viewer_unittest.cc index 7aeba31d8b9ac..15c839da134ef 100644 --- a/components/dom_distiller/core/viewer_unittest.cc +++ b/components/dom_distiller/core/viewer_unittest.cc @@ -39,6 +39,8 @@ class TestDomDistillerService : public DomDistillerServiceInterface { const ArticleAvailableCallback& article_cb) { return AddToList(url, distiller_page.get(), article_cb); } + MOCK_METHOD1(HasEntry, bool(const std::string&)); + MOCK_METHOD1(GetUrlForEntry, std::string(const std::string&)); MOCK_CONST_METHOD0(GetEntries, std::vector()); MOCK_METHOD1(AddObserver, void(DomDistillerObserver*)); MOCK_METHOD1(RemoveObserver, void(DomDistillerObserver*)); @@ -151,4 +153,19 @@ TEST_F(DomDistillerViewerTest, TestGetDistilledPageThemeJsOutput) { 0); } +TEST_F(DomDistillerViewerTest, TestGetDistilledPageFontFamilyJsOutput) { + std::string kSerifJsFontFamily = "useFontFamily('serif');"; + std::string kMonospaceJsFontFamily = "useFontFamily('monospace');"; + std::string kSansSerifJsFontFamily = "useFontFamily('sans-serif');"; + EXPECT_EQ(kSerifJsFontFamily.compare(viewer::GetDistilledPageFontFamilyJs( + DistilledPagePrefs::SERIF)), + 0); + EXPECT_EQ(kMonospaceJsFontFamily.compare(viewer::GetDistilledPageFontFamilyJs( + DistilledPagePrefs::MONOSPACE)), + 0); + EXPECT_EQ(kSansSerifJsFontFamily.compare(viewer::GetDistilledPageFontFamilyJs( + DistilledPagePrefs::SANS_SERIF)), + 0); +} + } // namespace dom_distiller diff --git a/components/domain_reliability.gypi b/components/domain_reliability.gypi index b324b205a62e5..9ef191131d4be 100644 --- a/components/domain_reliability.gypi +++ b/components/domain_reliability.gypi @@ -79,6 +79,29 @@ 'domain_reliability/baked_in_configs/redirector_gvt1_com.json', 'domain_reliability/baked_in_configs/s0_2mdn_net.json', 'domain_reliability/baked_in_configs/ssl_gstatic_com.json', + 'domain_reliability/baked_in_configs/star_admob_com.json', + 'domain_reliability/baked_in_configs/star_doubleclick_net.json', + 'domain_reliability/baked_in_configs/star_g_doubleclick_net.json', + 'domain_reliability/baked_in_configs/star_ggpht_com.json', + 'domain_reliability/baked_in_configs/star_google_cn.json', + 'domain_reliability/baked_in_configs/star_google_co_uk.json', + 'domain_reliability/baked_in_configs/star_google_com.json', + 'domain_reliability/baked_in_configs/star_google_com_au.json', + 'domain_reliability/baked_in_configs/star_google_de.json', + 'domain_reliability/baked_in_configs/star_google_fr.json', + 'domain_reliability/baked_in_configs/star_google_it.json', + 'domain_reliability/baked_in_configs/star_google_jp.json', + 'domain_reliability/baked_in_configs/star_google_org.json', + 'domain_reliability/baked_in_configs/star_google_ru.json', + 'domain_reliability/baked_in_configs/star_googleadservices_com.json', + 'domain_reliability/baked_in_configs/star_googleapis_com.json', + 'domain_reliability/baked_in_configs/star_googlesyndication_com.json', + 'domain_reliability/baked_in_configs/star_googleusercontent_com.json', + 'domain_reliability/baked_in_configs/star_googlevideo_com.json', + 'domain_reliability/baked_in_configs/star_gstatic_com.json', + 'domain_reliability/baked_in_configs/star_gvt1_com.json', + 'domain_reliability/baked_in_configs/star_youtube_com.json', + 'domain_reliability/baked_in_configs/star_ytimg_com.json', 'domain_reliability/baked_in_configs/t0_gstatic_com.json', 'domain_reliability/baked_in_configs/t1_gstatic_com.json', 'domain_reliability/baked_in_configs/t2_gstatic_com.json', diff --git a/components/domain_reliability/bake_in_configs.py b/components/domain_reliability/bake_in_configs.py index 937fce57a1eaf..56f7aae0215b5 100755 --- a/components/domain_reliability/bake_in_configs.py +++ b/components/domain_reliability/bake_in_configs.py @@ -18,9 +18,12 @@ # Chrome, to ensure incorrect ones are not added accidentally. Subdomains of # whitelist entries are also allowed (e.g. maps.google.com, ssl.gstatic.com). DOMAIN_WHITELIST = ('2mdn.net', 'admob.com', 'doubleclick.net', 'ggpht.com', - 'google.com', 'googleadservices.com', 'googleapis.com', - 'googlesyndication.com', 'googleusercontent.com', - 'googlevideo.com', 'gstatic.com', 'gvt1.com', 'youtube.com') + 'google.cn', 'google.co.uk', 'google.com', 'google.com.au', + 'google.de', 'google.fr', 'google.it', 'google.jp', + 'google.org', 'google.ru', 'googleadservices.com', + 'googleapis.com', 'googlesyndication.com', + 'googleusercontent.com', 'googlevideo.com', 'gstatic.com', + 'gvt1.com', 'youtube.com', 'ytimg.com') CC_HEADER = """// Copyright (C) 2014 The Chromium Authors. All rights reserved. diff --git a/components/domain_reliability/baked_in_configs/accounts_google_com.json b/components/domain_reliability/baked_in_configs/accounts_google_com.json index 59f277395ac6e..d66613a628159 100644 --- a/components/domain_reliability/baked_in_configs/accounts_google_com.json +++ b/components/domain_reliability/baked_in_configs/accounts_google_com.json @@ -1,10 +1,13 @@ { "config_version": "accounts-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "accounts.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/ad_doubleclick_net.json b/components/domain_reliability/baked_in_configs/ad_doubleclick_net.json index 0c452a9cdeb09..e042c4acccba5 100644 --- a/components/domain_reliability/baked_in_configs/ad_doubleclick_net.json +++ b/components/domain_reliability/baked_in_configs/ad_doubleclick_net.json @@ -1,10 +1,13 @@ { "config_version": "ad-doubleclick-net-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "ad.doubleclick.net", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/apis_google_com.json b/components/domain_reliability/baked_in_configs/apis_google_com.json index 496da8610ef06..6f442ea5bfb4c 100644 --- a/components/domain_reliability/baked_in_configs/apis_google_com.json +++ b/components/domain_reliability/baked_in_configs/apis_google_com.json @@ -1,10 +1,13 @@ { "config_version": "apis-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "apis.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/c_admob_com.json b/components/domain_reliability/baked_in_configs/c_admob_com.json index 5b9af1d8dd3d1..1de2ce70c21c4 100644 --- a/components/domain_reliability/baked_in_configs/c_admob_com.json +++ b/components/domain_reliability/baked_in_configs/c_admob_com.json @@ -1,10 +1,13 @@ { "config_version": "c-admob-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "c.admob.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/clients2_google_com.json b/components/domain_reliability/baked_in_configs/clients2_google_com.json new file mode 100644 index 0000000000000..18982df2c8ec6 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/clients2_google_com.json @@ -0,0 +1,27 @@ +{ + "config_version": "clients2-google-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "clients2.google.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "clients2-google-com-domainreliability", + "url_patterns": ["http*://clients2.google.com/domainreliability/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 0.50 + }, + { + "resource_name": "clients2-google-com-other", + "url_patterns": ["http*://clients2.google.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/csi_gstatic_com.json b/components/domain_reliability/baked_in_configs/csi_gstatic_com.json index 7eacd17b919dc..2cb1e82c21499 100644 --- a/components/domain_reliability/baked_in_configs/csi_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/csi_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "csi-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "csi.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/ddm_google_com.json b/components/domain_reliability/baked_in_configs/ddm_google_com.json index d0610f16ebd1d..4a333804007df 100644 --- a/components/domain_reliability/baked_in_configs/ddm_google_com.json +++ b/components/domain_reliability/baked_in_configs/ddm_google_com.json @@ -1,10 +1,13 @@ { "config_version": "ddm-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "ddm.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/docs_google_com.json b/components/domain_reliability/baked_in_configs/docs_google_com.json index 31e926f9bc2d9..555d0ca048545 100644 --- a/components/domain_reliability/baked_in_configs/docs_google_com.json +++ b/components/domain_reliability/baked_in_configs/docs_google_com.json @@ -1,10 +1,13 @@ { "config_version": "docs-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "docs.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/drive_google_com.json b/components/domain_reliability/baked_in_configs/drive_google_com.json index 0bbd3d6d33e33..7f9de68824205 100644 --- a/components/domain_reliability/baked_in_configs/drive_google_com.json +++ b/components/domain_reliability/baked_in_configs/drive_google_com.json @@ -1,10 +1,13 @@ { "config_version": "drive-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "drive.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/e_admob_com.json b/components/domain_reliability/baked_in_configs/e_admob_com.json index e67ea06eef8fa..287ebeaac0195 100644 --- a/components/domain_reliability/baked_in_configs/e_admob_com.json +++ b/components/domain_reliability/baked_in_configs/e_admob_com.json @@ -1,10 +1,13 @@ { "config_version": "e-admob-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "e.admob.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/fonts_googleapis_com.json b/components/domain_reliability/baked_in_configs/fonts_googleapis_com.json index 5aea5256b6f07..cb43d9a08f15e 100644 --- a/components/domain_reliability/baked_in_configs/fonts_googleapis_com.json +++ b/components/domain_reliability/baked_in_configs/fonts_googleapis_com.json @@ -1,10 +1,13 @@ { "config_version": "fonts-googleapis-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "fonts.googleapis.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/googleads4_g_doubleclick_net.json b/components/domain_reliability/baked_in_configs/googleads4_g_doubleclick_net.json index ba5d6f69bb7dc..4b1fb5d92db25 100644 --- a/components/domain_reliability/baked_in_configs/googleads4_g_doubleclick_net.json +++ b/components/domain_reliability/baked_in_configs/googleads4_g_doubleclick_net.json @@ -1,10 +1,13 @@ { "config_version": "googleads4-g-doubleclick-net-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "googleads4.g.doubleclick.net", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/googleads_g_doubleclick_net.json b/components/domain_reliability/baked_in_configs/googleads_g_doubleclick_net.json index a04fd233a93de..78cbf1907dd1a 100644 --- a/components/domain_reliability/baked_in_configs/googleads_g_doubleclick_net.json +++ b/components/domain_reliability/baked_in_configs/googleads_g_doubleclick_net.json @@ -1,10 +1,13 @@ { "config_version": "googleads-g-doubleclick-net-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "googleads.g.doubleclick.net", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/gstatic_com.json b/components/domain_reliability/baked_in_configs/gstatic_com.json index ff10091341a58..f2f9c82e4bffb 100644 --- a/components/domain_reliability/baked_in_configs/gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/lh3_ggpht_com.json b/components/domain_reliability/baked_in_configs/lh3_ggpht_com.json index cfcb290c4fa6d..db1b206f19811 100644 --- a/components/domain_reliability/baked_in_configs/lh3_ggpht_com.json +++ b/components/domain_reliability/baked_in_configs/lh3_ggpht_com.json @@ -1,10 +1,13 @@ { "config_version": "lh3-ggpht-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "lh3.ggpht.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/lh4_ggpht_com.json b/components/domain_reliability/baked_in_configs/lh4_ggpht_com.json index 9a7fb2add017c..c645f2712d414 100644 --- a/components/domain_reliability/baked_in_configs/lh4_ggpht_com.json +++ b/components/domain_reliability/baked_in_configs/lh4_ggpht_com.json @@ -1,10 +1,13 @@ { "config_version": "lh4-ggpht-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "lh4.ggpht.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/lh5_ggpht_com.json b/components/domain_reliability/baked_in_configs/lh5_ggpht_com.json index a44d3e3ef61e3..85e93f9ae8fc6 100644 --- a/components/domain_reliability/baked_in_configs/lh5_ggpht_com.json +++ b/components/domain_reliability/baked_in_configs/lh5_ggpht_com.json @@ -1,10 +1,13 @@ { "config_version": "lh5-ggpht-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "lh5.ggpht.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/lh6_ggpht_com.json b/components/domain_reliability/baked_in_configs/lh6_ggpht_com.json index c6eb890478fba..fbdd314ac96bd 100644 --- a/components/domain_reliability/baked_in_configs/lh6_ggpht_com.json +++ b/components/domain_reliability/baked_in_configs/lh6_ggpht_com.json @@ -1,10 +1,13 @@ { "config_version": "lh6-ggpht-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "lh6.ggpht.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/mail_google_com.json b/components/domain_reliability/baked_in_configs/mail_google_com.json index 95f9d1374a001..d876cc521a3f8 100644 --- a/components/domain_reliability/baked_in_configs/mail_google_com.json +++ b/components/domain_reliability/baked_in_configs/mail_google_com.json @@ -1,10 +1,13 @@ { "config_version": "mail-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "mail.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/media_admob_com.json b/components/domain_reliability/baked_in_configs/media_admob_com.json index 5fa79a6054ab8..12c2be0a87c5e 100644 --- a/components/domain_reliability/baked_in_configs/media_admob_com.json +++ b/components/domain_reliability/baked_in_configs/media_admob_com.json @@ -1,10 +1,13 @@ { "config_version": "media-admob-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "media.admob.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/pagead2_googlesyndication_com.json b/components/domain_reliability/baked_in_configs/pagead2_googlesyndication_com.json index 26ed9a11f1a34..747cc64b1ea66 100644 --- a/components/domain_reliability/baked_in_configs/pagead2_googlesyndication_com.json +++ b/components/domain_reliability/baked_in_configs/pagead2_googlesyndication_com.json @@ -1,10 +1,13 @@ { "config_version": "pagead2-googlesyndication-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "pagead2.googlesyndication.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/partner_googleadservices_com.json b/components/domain_reliability/baked_in_configs/partner_googleadservices_com.json index 4d7bcd6c32326..a6a918f883c8f 100644 --- a/components/domain_reliability/baked_in_configs/partner_googleadservices_com.json +++ b/components/domain_reliability/baked_in_configs/partner_googleadservices_com.json @@ -1,10 +1,13 @@ { "config_version": "partner-googleadservices-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "partner.googleadservices.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/pubads_g_doubleclick_net.json b/components/domain_reliability/baked_in_configs/pubads_g_doubleclick_net.json index 07a2ab97e2485..9bcf189ac2c83 100644 --- a/components/domain_reliability/baked_in_configs/pubads_g_doubleclick_net.json +++ b/components/domain_reliability/baked_in_configs/pubads_g_doubleclick_net.json @@ -1,10 +1,13 @@ { "config_version": "pubads-g-doubleclick-net-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "pubads.g.doubleclick.net", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/redirector_googlevideo_com.json b/components/domain_reliability/baked_in_configs/redirector_googlevideo_com.json index cffa94c48831b..19f6ea5264a4e 100644 --- a/components/domain_reliability/baked_in_configs/redirector_googlevideo_com.json +++ b/components/domain_reliability/baked_in_configs/redirector_googlevideo_com.json @@ -1,10 +1,13 @@ { "config_version": "redirector-googlevideo-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "redirector.googlevideo.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/s0_2mdn_net.json b/components/domain_reliability/baked_in_configs/s0_2mdn_net.json index 7865ead61b95b..427bdd2347d4e 100644 --- a/components/domain_reliability/baked_in_configs/s0_2mdn_net.json +++ b/components/domain_reliability/baked_in_configs/s0_2mdn_net.json @@ -1,10 +1,13 @@ { "config_version": "s0-2mdn-net-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "s0.2mdn.net", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json b/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json index ea16028e0b7e7..8abf57a95e030 100644 --- a/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/ssl_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "ssl-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "ssl.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/star_admob_com.json b/components/domain_reliability/baked_in_configs/star_admob_com.json new file mode 100644 index 0000000000000..b0e513dfb19e3 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_admob_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-admob-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.admob.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-admob-com-other", + "url_patterns": ["http*://*.admob.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_doubleclick_net.json b/components/domain_reliability/baked_in_configs/star_doubleclick_net.json new file mode 100644 index 0000000000000..f1e3617049ebd --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_doubleclick_net.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-doubleclick-net-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.doubleclick.net", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-doubleclick-net-other", + "url_patterns": ["http*://*.doubleclick.net/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_g_doubleclick_net.json b/components/domain_reliability/baked_in_configs/star_g_doubleclick_net.json new file mode 100644 index 0000000000000..7e6cbd6114fb6 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_g_doubleclick_net.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-g-doubleclick-net-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.g.doubleclick.net", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-g-doubleclick-net-other", + "url_patterns": ["http*://*.g.doubleclick.net/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_ggpht_com.json b/components/domain_reliability/baked_in_configs/star_ggpht_com.json new file mode 100644 index 0000000000000..9085235fca202 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_ggpht_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-ggpht-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.ggpht.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-ggpht-com-other", + "url_patterns": ["http*://*.ggpht.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_cn.json b/components/domain_reliability/baked_in_configs/star_google_cn.json new file mode 100644 index 0000000000000..332ebb0046a24 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_cn.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-cn-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.cn", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-cn-other", + "url_patterns": ["http*://*.google.cn/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_co_uk.json b/components/domain_reliability/baked_in_configs/star_google_co_uk.json new file mode 100644 index 0000000000000..292d1e27c9cf7 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_co_uk.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-co-uk-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.co.uk", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-co-uk-other", + "url_patterns": ["http*://*.google.co.uk/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_com.json b/components/domain_reliability/baked_in_configs/star_google_com.json new file mode 100644 index 0000000000000..f017e91119344 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-com-other", + "url_patterns": ["http*://*.google.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_com_au.json b/components/domain_reliability/baked_in_configs/star_google_com_au.json new file mode 100644 index 0000000000000..ac2d17d584ff6 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_com_au.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-com-au-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.com.au", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-com-au-other", + "url_patterns": ["http*://*.google.com.au/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_de.json b/components/domain_reliability/baked_in_configs/star_google_de.json new file mode 100644 index 0000000000000..37ad7f737ff4e --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_de.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-de-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.de", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-de-other", + "url_patterns": ["http*://*.google.de/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_fr.json b/components/domain_reliability/baked_in_configs/star_google_fr.json new file mode 100644 index 0000000000000..6d26930d20a0d --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_fr.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-fr-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.fr", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-fr-other", + "url_patterns": ["http*://*.google.fr/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_it.json b/components/domain_reliability/baked_in_configs/star_google_it.json new file mode 100644 index 0000000000000..3561d0a5671bf --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_it.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-it-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.it", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-it-other", + "url_patterns": ["http*://*.google.it/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_jp.json b/components/domain_reliability/baked_in_configs/star_google_jp.json new file mode 100644 index 0000000000000..b5d34c4cb6212 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_jp.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-jp-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.jp", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-jp-other", + "url_patterns": ["http*://*.google.jp/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_org.json b/components/domain_reliability/baked_in_configs/star_google_org.json new file mode 100644 index 0000000000000..8ac9b30a1e435 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_org.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-org-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.org", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-org-other", + "url_patterns": ["http*://*.google.org/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_google_ru.json b/components/domain_reliability/baked_in_configs/star_google_ru.json new file mode 100644 index 0000000000000..566b091422e62 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_google_ru.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-google-ru-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.google.ru", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-google-ru-other", + "url_patterns": ["http*://*.google.ru/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_googleadservices_com.json b/components/domain_reliability/baked_in_configs/star_googleadservices_com.json new file mode 100644 index 0000000000000..e170a5b1141f6 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_googleadservices_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-googleadservices-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.googleadservices.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-googleadservices-com-other", + "url_patterns": ["http*://*.googleadservices.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_googleapis_com.json b/components/domain_reliability/baked_in_configs/star_googleapis_com.json new file mode 100644 index 0000000000000..fee1665039b73 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_googleapis_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-googleapis-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.googleapis.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-googleapis-com-other", + "url_patterns": ["http*://*.googleapis.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_googlesyndication_com.json b/components/domain_reliability/baked_in_configs/star_googlesyndication_com.json new file mode 100644 index 0000000000000..e269f3e77379e --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_googlesyndication_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-googlesyndication-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.googlesyndication.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-googlesyndication-com-other", + "url_patterns": ["http*://*.googlesyndication.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_googleusercontent_com.json b/components/domain_reliability/baked_in_configs/star_googleusercontent_com.json new file mode 100644 index 0000000000000..f59251b68b938 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_googleusercontent_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-googleusercontent-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.googleusercontent.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-googleusercontent-com-other", + "url_patterns": ["http*://*.googleusercontent.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_googlevideo_com.json b/components/domain_reliability/baked_in_configs/star_googlevideo_com.json new file mode 100644 index 0000000000000..d749c173ce45e --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_googlevideo_com.json @@ -0,0 +1,33 @@ +{ + "config_version": "star-googlevideo-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.googlevideo.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-googlevideo-com-redirector", + "url_patterns": ["http*://redirector.googlevideo.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + }, + { + "resource_name": "star-googlevideo-com-rname", + "url_patterns": ["http*://r*.googlevideo.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + }, + { + "resource_name": "star-googlevideo-com-other", + "url_patterns": ["http*://*.googlevideo.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_gstatic_com.json b/components/domain_reliability/baked_in_configs/star_gstatic_com.json new file mode 100644 index 0000000000000..f83986d839134 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_gstatic_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-gstatic-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.gstatic.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-gstatic-com-other", + "url_patterns": ["http*://*.gstatic.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_gvt1_com.json b/components/domain_reliability/baked_in_configs/star_gvt1_com.json new file mode 100644 index 0000000000000..0022c183efe55 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_gvt1_com.json @@ -0,0 +1,39 @@ +{ + "config_version": "star-gvt1-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.gvt1.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-gvt1-com-domain-reliability", + "url_patterns": ["http*://domain-reliability.gvt1.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 0.50 + }, + { + "resource_name": "star-gvt1-com-redirector", + "url_patterns": ["http*://redirector.gvt1.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + }, + { + "resource_name": "star-gvt1-com-rname", + "url_patterns": ["http*://r*.gvt1.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + }, + { + "resource_name": "star-gvt1-com-other", + "url_patterns": ["http*://*.gvt1.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_youtube_com.json b/components/domain_reliability/baked_in_configs/star_youtube_com.json new file mode 100644 index 0000000000000..b74bbd1073346 --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_youtube_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-youtube-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.youtube.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-youtube-com-other", + "url_patterns": ["http*://*.youtube.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/star_ytimg_com.json b/components/domain_reliability/baked_in_configs/star_ytimg_com.json new file mode 100644 index 0000000000000..df4e2c0a344fe --- /dev/null +++ b/components/domain_reliability/baked_in_configs/star_ytimg_com.json @@ -0,0 +1,21 @@ +{ + "config_version": "star-ytimg-com-v1", + "config_valid_until": 1425168000.0, + "monitored_domain": "*.ytimg.com", + "collectors": [ + { + "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" + } + ], + "monitored_resources": [ + { + "resource_name": "star-ytimg-com-other", + "url_patterns": ["http*://*.ytimg.com/*"], + "success_sample_rate": 0.05, + "failure_sample_rate": 1.00 + } + ] +} diff --git a/components/domain_reliability/baked_in_configs/t0_gstatic_com.json b/components/domain_reliability/baked_in_configs/t0_gstatic_com.json index 3963b3d1a52f6..c8525457a7268 100644 --- a/components/domain_reliability/baked_in_configs/t0_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/t0_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "t0-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "t0.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/t1_gstatic_com.json b/components/domain_reliability/baked_in_configs/t1_gstatic_com.json index 39f3ac3f3e99e..91b54f470a0bf 100644 --- a/components/domain_reliability/baked_in_configs/t1_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/t1_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "t1-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "t1.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/t2_gstatic_com.json b/components/domain_reliability/baked_in_configs/t2_gstatic_com.json index 83c7ff4dfca50..ae5c2ad22b329 100644 --- a/components/domain_reliability/baked_in_configs/t2_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/t2_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "t2-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "t2.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/t3_gstatic_com.json b/components/domain_reliability/baked_in_configs/t3_gstatic_com.json index 0c6095e170ed5..5e8dd2f4273aa 100644 --- a/components/domain_reliability/baked_in_configs/t3_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/t3_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "t3-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "t3.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/themes_googleusercontent_com.json b/components/domain_reliability/baked_in_configs/themes_googleusercontent_com.json index bb47961e63951..6092b5f1b018d 100644 --- a/components/domain_reliability/baked_in_configs/themes_googleusercontent_com.json +++ b/components/domain_reliability/baked_in_configs/themes_googleusercontent_com.json @@ -1,10 +1,13 @@ { "config_version": "themes-googleusercontent-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "themes.googleusercontent.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/www_google_com.json b/components/domain_reliability/baked_in_configs/www_google_com.json index 78011ad7b1a33..793c6120794ff 100644 --- a/components/domain_reliability/baked_in_configs/www_google_com.json +++ b/components/domain_reliability/baked_in_configs/www_google_com.json @@ -1,10 +1,13 @@ { "config_version": "www-google-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "www.google.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/www_googleadservices_com.json b/components/domain_reliability/baked_in_configs/www_googleadservices_com.json index 755bd781cae1b..203ce51d1b87e 100644 --- a/components/domain_reliability/baked_in_configs/www_googleadservices_com.json +++ b/components/domain_reliability/baked_in_configs/www_googleadservices_com.json @@ -1,10 +1,13 @@ { "config_version": "www-googleadservices-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "www.googleadservices.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/www_gstatic_com.json b/components/domain_reliability/baked_in_configs/www_gstatic_com.json index cec78b972b181..e7d315030d957 100644 --- a/components/domain_reliability/baked_in_configs/www_gstatic_com.json +++ b/components/domain_reliability/baked_in_configs/www_gstatic_com.json @@ -1,10 +1,13 @@ { "config_version": "www-gstatic-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "www.gstatic.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/baked_in_configs/www_youtube_com.json b/components/domain_reliability/baked_in_configs/www_youtube_com.json index 7669f3fe6f989..fd70e91290ecb 100644 --- a/components/domain_reliability/baked_in_configs/www_youtube_com.json +++ b/components/domain_reliability/baked_in_configs/www_youtube_com.json @@ -1,10 +1,13 @@ { "config_version": "www-youtube-com-v1", - "config_valid_until": 1413331200.0, + "config_valid_until": 1425168000.0, "monitored_domain": "www.youtube.com", "collectors": [ { "upload_url": "https://clients2.google.com/domainreliability/upload" + }, + { + "upload_url": "https://beacons.gvt2.com/domainreliability/upload" } ], "monitored_resources": [ diff --git a/components/domain_reliability/context_unittest.cc b/components/domain_reliability/context_unittest.cc index 43323022bff99..43a4a51816c69 100644 --- a/components/domain_reliability/context_unittest.cc +++ b/components/domain_reliability/context_unittest.cc @@ -176,7 +176,7 @@ TEST_F(DomainReliabilityContextTest, ReportUpload) { time_.Advance(max_delay()); EXPECT_TRUE(upload_pending()); EXPECT_EQ(kExpectedReport, upload_report()); - EXPECT_EQ(GURL("https://example/upload"), upload_url()); + EXPECT_EQ(GURL("https://exampleuploader/upload"), upload_url()); CallUploadCallback(true); EXPECT_TRUE(CheckNoBeacons()); diff --git a/components/domain_reliability/monitor.cc b/components/domain_reliability/monitor.cc index fc71cea333853..bde4c63c6dfd1 100644 --- a/components/domain_reliability/monitor.cc +++ b/components/domain_reliability/monitor.cc @@ -196,13 +196,14 @@ void DomainReliabilityMonitor::OnRequestLegComplete( response_code = request.response_info.headers->response_code(); else response_code = -1; - ContextMap::iterator context_it; std::string beacon_status; int error_code = net::OK; if (request.status.status() == net::URLRequestStatus::FAILED) error_code = request.status.error(); + DomainReliabilityContext* context = GetContextForHost(request.url.host()); + // Ignore requests where: // 1. There is no context for the request host. // 2. The request did not access the network. @@ -211,7 +212,7 @@ void DomainReliabilityMonitor::OnRequestLegComplete( // 4. The request was itself a Domain Reliability upload (to avoid loops). // 5. There is no defined beacon status for the error or HTTP response code // (to avoid leaking network-local errors). - if ((context_it = contexts_.find(request.url.host())) == contexts_.end() || + if (!context || !request.AccessedNetwork() || (request.load_flags & net::LOAD_DO_NOT_SEND_COOKIES) || request.is_upload || @@ -233,7 +234,36 @@ void DomainReliabilityMonitor::OnRequestLegComplete( beacon.http_response_code = response_code; beacon.start_time = request.load_timing_info.request_start; beacon.elapsed = time_->NowTicks() - beacon.start_time; - context_it->second->OnBeacon(request.url, beacon); + context->OnBeacon(request.url, beacon); +} + +// TODO(ttuttle): Keep a separate wildcard_contexts_ map to avoid having to +// prepend '*.' to domains. +DomainReliabilityContext* DomainReliabilityMonitor::GetContextForHost( + const std::string& host) const { + ContextMap::const_iterator context_it; + + context_it = contexts_.find(host); + if (context_it != contexts_.end()) + return context_it->second; + + std::string host_with_asterisk = "*." + host; + context_it = contexts_.find(host_with_asterisk); + if (context_it != contexts_.end()) + return context_it->second; + + size_t dot_pos = host.find('.'); + if (dot_pos == std::string::npos) + return NULL; + + // TODO(ttuttle): Make sure parent is not in PSL before using. + + std::string parent_with_asterisk = "*." + host.substr(dot_pos + 1); + context_it = contexts_.find(parent_with_asterisk); + if (context_it != contexts_.end()) + return context_it->second; + + return NULL; } base::WeakPtr diff --git a/components/domain_reliability/monitor.h b/components/domain_reliability/monitor.h index 1e58694a632a6..7886f89e8721c 100644 --- a/components/domain_reliability/monitor.h +++ b/components/domain_reliability/monitor.h @@ -112,6 +112,8 @@ class DOMAIN_RELIABILITY_EXPORT DomainReliabilityMonitor { void ClearContexts(); void OnRequestLegComplete(const RequestInfo& info); + DomainReliabilityContext* GetContextForHost(const std::string& host) const; + base::WeakPtr MakeWeakPtr(); scoped_ptr thread_checker_; diff --git a/components/domain_reliability/monitor_unittest.cc b/components/domain_reliability/monitor_unittest.cc index 19cedbb3ba631..6e7b9134fe434 100644 --- a/components/domain_reliability/monitor_unittest.cc +++ b/components/domain_reliability/monitor_unittest.cc @@ -87,13 +87,27 @@ class DomainReliabilityMonitorTest : public testing::Test { bool CheckRequestCounts(size_t index, uint32 expected_successful, uint32 expected_failed) { + return CheckRequestCounts(context_, + index, + expected_successful, + expected_failed); + } + + bool CheckRequestCounts(DomainReliabilityContext* context, + size_t index, + uint32 expected_successful, + uint32 expected_failed) { uint32 successful, failed; - context_->GetRequestCountsForTesting(index, &successful, &failed); + context->GetRequestCountsForTesting(index, &successful, &failed); EXPECT_EQ(expected_successful, successful); EXPECT_EQ(expected_failed, failed); return expected_successful == successful && expected_failed == failed; } + DomainReliabilityContext* CreateAndAddContext(const std::string& domain) { + return monitor_.AddContextForTesting(MakeTestConfigWithDomain(domain)); + } + scoped_refptr network_task_runner_; scoped_refptr url_request_context_getter_; MockTime* time_; @@ -301,6 +315,70 @@ TEST_F(DomainReliabilityMonitorTest, IgnoreSuccessError) { EXPECT_TRUE(CheckRequestCounts(kAlwaysReportIndex, 1u, 0u)); } +TEST_F(DomainReliabilityMonitorTest, WildcardMatchesSelf) { + DomainReliabilityContext* context = CreateAndAddContext("*.wildcard"); + + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://wildcard/always_report"); + OnRequestLegComplete(request); + EXPECT_TRUE(CheckRequestCounts(context, kAlwaysReportIndex, 1u, 0u)); +} + +TEST_F(DomainReliabilityMonitorTest, WildcardMatchesSubdomain) { + DomainReliabilityContext* context = CreateAndAddContext("*.wildcard"); + + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://test.wildcard/always_report"); + OnRequestLegComplete(request); + EXPECT_TRUE(CheckRequestCounts(context, kAlwaysReportIndex, 1u, 0u)); +} + +TEST_F(DomainReliabilityMonitorTest, WildcardDoesntMatchSubsubdomain) { + DomainReliabilityContext* context = CreateAndAddContext("*.wildcard"); + + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://test.test.wildcard/always_report"); + OnRequestLegComplete(request); + EXPECT_TRUE(CheckRequestCounts(context, kAlwaysReportIndex, 0u, 0u)); +} + +TEST_F(DomainReliabilityMonitorTest, WildcardPrefersSelfToSelfWildcard) { + DomainReliabilityContext* context1 = CreateAndAddContext("wildcard"); + DomainReliabilityContext* context2 = CreateAndAddContext("*.wildcard"); + + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://wildcard/always_report"); + OnRequestLegComplete(request); + + EXPECT_TRUE(CheckRequestCounts(context1, kAlwaysReportIndex, 1u, 0u)); + EXPECT_TRUE(CheckRequestCounts(context2, kAlwaysReportIndex, 0u, 0u)); +} + +TEST_F(DomainReliabilityMonitorTest, WildcardPrefersSelfToParentWildcard) { + DomainReliabilityContext* context1 = CreateAndAddContext("test.wildcard"); + DomainReliabilityContext* context2 = CreateAndAddContext("*.wildcard"); + + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://test.wildcard/always_report"); + OnRequestLegComplete(request); + + EXPECT_TRUE(CheckRequestCounts(context1, kAlwaysReportIndex, 1u, 0u)); + EXPECT_TRUE(CheckRequestCounts(context2, kAlwaysReportIndex, 0u, 0u)); +} + +TEST_F(DomainReliabilityMonitorTest, + WildcardPrefersSelfWildcardToParentWildcard) { + DomainReliabilityContext* context1 = CreateAndAddContext("*.test.wildcard"); + DomainReliabilityContext* context2 = CreateAndAddContext("*.wildcard"); + + RequestInfo request = MakeRequestInfo(); + request.url = GURL("http://test.wildcard/always_report"); + OnRequestLegComplete(request); + + EXPECT_TRUE(CheckRequestCounts(context1, kAlwaysReportIndex, 1u, 0u)); + EXPECT_TRUE(CheckRequestCounts(context2, kAlwaysReportIndex, 0u, 0u)); +} + } // namespace } // namespace domain_reliability diff --git a/components/domain_reliability/test_util.cc b/components/domain_reliability/test_util.cc index e1fb5d73e0c70..69d21efa21ef4 100644 --- a/components/domain_reliability/test_util.cc +++ b/components/domain_reliability/test_util.cc @@ -154,13 +154,18 @@ DomainReliabilityScheduler::Params MakeTestSchedulerParams() { } scoped_ptr MakeTestConfig() { + return MakeTestConfigWithDomain("example"); +} + +scoped_ptr MakeTestConfigWithDomain( + const std::string& domain) { DomainReliabilityConfig* config = new DomainReliabilityConfig(); DomainReliabilityConfig::Resource* resource; resource = new DomainReliabilityConfig::Resource(); resource->name = "always_report"; resource->url_patterns.push_back( - new std::string("http://example/always_report")); + new std::string("http://*/always_report")); resource->success_sample_rate = 1.0; resource->failure_sample_rate = 1.0; config->resources.push_back(resource); @@ -168,19 +173,19 @@ scoped_ptr MakeTestConfig() { resource = new DomainReliabilityConfig::Resource(); resource->name = "never_report"; resource->url_patterns.push_back( - new std::string("http://example/never_report")); + new std::string("http://*/never_report")); resource->success_sample_rate = 0.0; resource->failure_sample_rate = 0.0; config->resources.push_back(resource); DomainReliabilityConfig::Collector* collector; collector = new DomainReliabilityConfig::Collector(); - collector->upload_url = GURL("https://example/upload"); + collector->upload_url = GURL("https://exampleuploader/upload"); config->collectors.push_back(collector); config->version = "1"; config->valid_until = 1234567890.0; - config->domain = "example"; + config->domain = domain; DCHECK(config->IsValid()); diff --git a/components/domain_reliability/test_util.h b/components/domain_reliability/test_util.h index a4529dd90f505..a5367e9465366 100644 --- a/components/domain_reliability/test_util.h +++ b/components/domain_reliability/test_util.h @@ -115,6 +115,8 @@ class MockTime : public MockableTime { }; scoped_ptr MakeTestConfig(); +scoped_ptr MakeTestConfigWithDomain( + const std::string& domain); DomainReliabilityScheduler::Params MakeTestSchedulerParams(); } // namespace domain_reliability diff --git a/components/enhanced_bookmarks.gypi b/components/enhanced_bookmarks.gypi index f5e41ecd4aa27..a26cc602391fa 100644 --- a/components/enhanced_bookmarks.gypi +++ b/components/enhanced_bookmarks.gypi @@ -15,9 +15,12 @@ '../sql/sql.gyp:sql', '../ui/gfx/gfx.gyp:gfx', '../url/url.gyp:url_lib', + 'bookmarks_browser', 'enhanced_bookmarks_proto', ], 'sources': [ + 'enhanced_bookmarks/enhanced_bookmark_utils.cc', + 'enhanced_bookmarks/enhanced_bookmark_utils.h', 'enhanced_bookmarks/image_store.cc', 'enhanced_bookmarks/image_store.h', 'enhanced_bookmarks/image_store_util.cc', @@ -29,6 +32,17 @@ 'enhanced_bookmarks/persistent_image_store.h', ], 'conditions': [ + ['OS=="android"', { + 'sources': [ + 'enhanced_bookmarks/android/component_jni_registrar.cc', + 'enhanced_bookmarks/android/component_jni_registrar.h', + 'enhanced_bookmarks/android/enhanced_bookmarks_bridge.cc', + 'enhanced_bookmarks/android/enhanced_bookmarks_bridge.h', + ], + 'dependencies': [ + 'enhanced_bookmarks_jni_headers', + ], + }], ['OS=="ios"', { 'sources!': [ 'enhanced_bookmarks/image_store_util.cc', @@ -64,4 +78,32 @@ 'includes': [ '../build/protoc.gypi' ], }, ], + 'conditions' : [ + ['OS=="android"', { + 'targets': [ + { + 'target_name': 'enhanced_bookmarks_java', + 'type': 'none', + 'dependencies': [ + 'components.gyp:bookmarks_java' + ], + 'variables': { + 'java_in_dir': 'enhanced_bookmarks/android/java', + }, + 'includes': [ '../build/java.gypi' ], + }, + { + 'target_name': 'enhanced_bookmarks_jni_headers', + 'type': 'none', + 'sources': [ + 'enhanced_bookmarks/android/java/src/org/chromium/components/enhancedbookmarks/EnhancedBookmarksBridge.java', + ], + 'variables': { + 'jni_gen_package': 'enhanced_bookmarks', + }, + 'includes': [ '../build/jni_generator.gypi' ], + }, + ], + }] + ], } diff --git a/components/enhanced_bookmarks/DEPS b/components/enhanced_bookmarks/DEPS index b3cce4858a85d..de785aef16fdb 100644 --- a/components/enhanced_bookmarks/DEPS +++ b/components/enhanced_bookmarks/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/bookmarks", + "+jni", "+sql", "+ui", ] diff --git a/components/enhanced_bookmarks/OWNERS b/components/enhanced_bookmarks/OWNERS index aa04d488498f8..65b4a95d6b903 100644 --- a/components/enhanced_bookmarks/OWNERS +++ b/components/enhanced_bookmarks/OWNERS @@ -1,2 +1,3 @@ noyau@chromium.org sky@chromium.org +yfriedman@chromium.org \ No newline at end of file diff --git a/components/enhanced_bookmarks/android/OWNERS b/components/enhanced_bookmarks/android/OWNERS new file mode 100644 index 0000000000000..3df886e30ca65 --- /dev/null +++ b/components/enhanced_bookmarks/android/OWNERS @@ -0,0 +1,3 @@ +yfriedman@chromium.org +tedchoc@chromium.org +kkimlabs@chromium.org diff --git a/components/enhanced_bookmarks/android/component_jni_registrar.cc b/components/enhanced_bookmarks/android/component_jni_registrar.cc new file mode 100644 index 0000000000000..ab4440d5844c2 --- /dev/null +++ b/components/enhanced_bookmarks/android/component_jni_registrar.cc @@ -0,0 +1,29 @@ +// 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/enhanced_bookmarks/android/component_jni_registrar.h" + +#include "base/android/jni_android.h" +#include "base/android/jni_registrar.h" +#include "base/basictypes.h" +#include "components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h" + +using base::android::RegistrationMethod; + +namespace enhanced_bookmarks { +namespace android { + +static RegistrationMethod kEnhancedBookmarksRegisteredMethods[] = { + {"EnhancedBookmarksBridge", RegisterEnhancedBookmarksBridge}, +}; + +bool RegisterEnhancedBookmarks(JNIEnv* env) { + return base::android::RegisterNativeMethods( + env, + kEnhancedBookmarksRegisteredMethods, + arraysize(kEnhancedBookmarksRegisteredMethods)); +} + +} // namespace android +} // namespace enhanced_bookmarks diff --git a/components/enhanced_bookmarks/android/component_jni_registrar.h b/components/enhanced_bookmarks/android/component_jni_registrar.h new file mode 100644 index 0000000000000..d02d626be1f5a --- /dev/null +++ b/components/enhanced_bookmarks/android/component_jni_registrar.h @@ -0,0 +1,18 @@ +// 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_ENHANCED_BOOKMARKS_ANDROID_COMPONENT_JNI_REGISTRAR_H_ +#define COMPONENTS_ENHANCED_BOOKMARKS_ANDROID_COMPONENT_JNI_REGISTRAR_H_ + +#include + +namespace enhanced_bookmarks { +namespace android { + +bool RegisterEnhancedBookmarks(JNIEnv* env); + +} // namespace android +} // namespace enhanced_bookmarks + +#endif // COMPONENTS_ENHANCED_BOOKMARKS_ANDROID_COMPONENT_JNI_REGISTRAR_H_ diff --git a/components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.cc b/components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.cc new file mode 100644 index 0000000000000..445f746c41cae --- /dev/null +++ b/components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.cc @@ -0,0 +1,69 @@ +// 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/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h" + +#include "base/android/jni_string.h" +#include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/browser/bookmark_utils.h" +#include "components/bookmarks/common/android/bookmark_type.h" +#include "components/enhanced_bookmarks/metadata_accessor.h" +#include "jni/EnhancedBookmarksBridge_jni.h" + +using bookmarks::BookmarkType; + +namespace enhanced_bookmarks { +namespace android { + +EnhancedBookmarksBridge::EnhancedBookmarksBridge(JNIEnv* env, + jobject obj, + jlong bookmark_model_ptr) { + bookmark_model_ = reinterpret_cast(bookmark_model_ptr); +} + +void EnhancedBookmarksBridge::Destroy(JNIEnv*, jobject) { + delete this; +} + +ScopedJavaLocalRef EnhancedBookmarksBridge::GetBookmarkDescription( + JNIEnv* env, jobject obj, jlong id, jint type) { + DCHECK(bookmark_model_->loaded()); + DCHECK_EQ(type, BookmarkType::NORMAL); + + const BookmarkNode* node = bookmarks::GetBookmarkNodeByID( + bookmark_model_, static_cast(id)); + + return node ? + base::android::ConvertUTF8ToJavaString( + env, enhanced_bookmarks::DescriptionFromBookmark(node)) : + ScopedJavaLocalRef(); +} + +void EnhancedBookmarksBridge::SetBookmarkDescription(JNIEnv* env, + jobject obj, + jlong id, + jint type, + jstring description) { + DCHECK(bookmark_model_->loaded()); + DCHECK_EQ(type, BookmarkType::NORMAL); + + const BookmarkNode* node = bookmarks::GetBookmarkNodeByID( + bookmark_model_, static_cast(id)); + + enhanced_bookmarks::SetDescriptionForBookmark( + bookmark_model_, node, + base::android::ConvertJavaStringToUTF8(env, description)); +} + +static jlong Init(JNIEnv* env, jobject obj, jlong bookmark_model_ptr) { + return reinterpret_cast( + new EnhancedBookmarksBridge(env, obj, bookmark_model_ptr)); +} + +bool RegisterEnhancedBookmarksBridge(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +} // namespace android +} // namespace enhanced_bookmarks diff --git a/components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h b/components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h new file mode 100644 index 0000000000000..1319686955ba0 --- /dev/null +++ b/components/enhanced_bookmarks/android/enhanced_bookmarks_bridge.h @@ -0,0 +1,43 @@ +// 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_ENHANCED_BOOKMARKS_ANDROID_ENHANCED_BOOKMARKS_BRIDGE_H_ +#define COMPONENTS_ENHANCED_BOOKMARKS_ANDROID_ENHANCED_BOOKMARKS_BRIDGE_H_ + +#include "base/android/jni_android.h" + +class BookmarkModel; + +namespace enhanced_bookmarks { +namespace android { + +class EnhancedBookmarksBridge { + public: + EnhancedBookmarksBridge(JNIEnv* env, jobject obj, jlong bookmark_model_ptr); + void Destroy(JNIEnv*, jobject); + + base::android::ScopedJavaLocalRef GetBookmarkDescription( + JNIEnv* env, + jobject obj, + jlong id, + jint type); + + void SetBookmarkDescription(JNIEnv* env, + jobject obj, + jlong id, + jint type, + jstring description); + + private: + BookmarkModel* bookmark_model_; // weak + + DISALLOW_COPY_AND_ASSIGN(EnhancedBookmarksBridge); +}; + +bool RegisterEnhancedBookmarksBridge(JNIEnv* env); + +} // namespace android +} // namespace enhanced_bookmarks + +#endif // COMPONENTS_ENHANCED_BOOKMARKS_ANDROID_ENHANCED_BOOKMARKS_BRIDGE_H_ diff --git a/components/enhanced_bookmarks/android/java/src/org/chromium/components/enhancedbookmarks/EnhancedBookmarksBridge.java b/components/enhanced_bookmarks/android/java/src/org/chromium/components/enhancedbookmarks/EnhancedBookmarksBridge.java new file mode 100644 index 0000000000000..c244cb5f4c8d9 --- /dev/null +++ b/components/enhanced_bookmarks/android/java/src/org/chromium/components/enhancedbookmarks/EnhancedBookmarksBridge.java @@ -0,0 +1,45 @@ +// 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.components.enhancedbookmarks; + +import org.chromium.base.JNINamespace; +import org.chromium.components.bookmarks.BookmarkId; + +/** + * Access gate to C++ side enhanced bookmarks functionalities. + */ +@JNINamespace("enhanced_bookmarks::android") +public final class EnhancedBookmarksBridge { + private long mNativeEnhancedBookmarksBridge; + + public EnhancedBookmarksBridge(long nativeBookmarkModel) { + mNativeEnhancedBookmarksBridge = nativeInit(nativeBookmarkModel); + } + + public void destroy() { + assert mNativeEnhancedBookmarksBridge != 0; + nativeDestroy(mNativeEnhancedBookmarksBridge); + mNativeEnhancedBookmarksBridge = 0; + } + + public String getBookmarkDescription(BookmarkId id) { + return nativeGetBookmarkDescription(mNativeEnhancedBookmarksBridge, id.getId(), + id.getType()); + } + + public void setBookmarkDescription(BookmarkId id, String description) { + nativeSetBookmarkDescription(mNativeEnhancedBookmarksBridge, id.getId(), id.getType(), + description); + } + + private native long nativeInit(long bookmarkModelPointer); + + private native void nativeDestroy(long nativeEnhancedBookmarksBridge); + + private native String nativeGetBookmarkDescription(long nativeEnhancedBookmarksBridge, long id, + int type); + private native void nativeSetBookmarkDescription(long nativeEnhancedBookmarksBridge, long id, + int type, String description); +} diff --git a/components/enhanced_bookmarks/enhanced_bookmark_utils.cc b/components/enhanced_bookmarks/enhanced_bookmark_utils.cc new file mode 100644 index 0000000000000..85415cfebf1d3 --- /dev/null +++ b/components/enhanced_bookmarks/enhanced_bookmark_utils.cc @@ -0,0 +1,127 @@ +// 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/enhanced_bookmarks/enhanced_bookmark_utils.h" + +#include "base/i18n/string_compare.h" +#include "base/i18n/string_search.h" +#include "base/strings/utf_string_conversions.h" +#include "components/bookmarks/browser/bookmark_model.h" +#include "ui/base/models/tree_node_iterator.h" + +namespace enhanced_bookmark_utils { + +std::vector FindBookmarksWithQuery( + BookmarkModel* bookmark_model, + const std::string& query) { + std::vector results; + base::string16 query16 = base::UTF8ToUTF16(query); + base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents pattern16(query16); + + ui::TreeNodeIterator iterator( + bookmark_model->root_node()); + while (iterator.has_next()) { + const BookmarkNode* node = iterator.Next(); + if (!node->is_url()) + continue; + + // Search the title for the query. + size_t match_index; + size_t match_length; + bool found = + pattern16.Search(node->GetTitle(), &match_index, &match_length); + if (found) + results.push_back(node); + } + return results; +} + +// Comparator used to sort bookmarks. No folders are allowed. +class BookmarkNameComparator : public std::binary_function { + public: + explicit BookmarkNameComparator(icu::Collator* collator) + : collator_(collator) {} + + // Returns true if |n1| preceeds |n2|. + bool operator()(const BookmarkNode* n1, const BookmarkNode* n2) { + DCHECK(!n1->is_folder()); + DCHECK(!n2->is_folder()); + if (!collator_) + return n1->GetTitle() < n2->GetTitle(); + return base::i18n::CompareString16WithCollator( + collator_, n1->GetTitle(), n2->GetTitle()) == UCOL_LESS; + } + + private: + icu::Collator* collator_; +}; + +void SortBookmarksByName(std::vector& nodes) { + UErrorCode error = U_ZERO_ERROR; + scoped_ptr collator(icu::Collator::createInstance(error)); + if (U_FAILURE(error)) + collator.reset(NULL); + std::sort(nodes.begin(), nodes.end(), BookmarkNameComparator(collator.get())); +} + +std::vector PrimaryPermanentNodes(BookmarkModel* model) { + DCHECK(model->loaded()); + std::vector nodes; + nodes.push_back(model->other_node()); + nodes.push_back(model->mobile_node()); + return nodes; +} + +std::vector RootLevelFolders(BookmarkModel* model) { + std::vector root_level_folders; + + // Find the direct folder children of the primary permanent nodes. + std::vector primary_permanent_nodes = + PrimaryPermanentNodes(model); + for (const BookmarkNode* parent : primary_permanent_nodes) { + int child_count = parent->child_count(); + for (int i = 0; i < child_count; ++i) { + const BookmarkNode* node = parent->GetChild(i); + if (node->is_folder() && node->IsVisible()) + root_level_folders.push_back(node); + } + } + + // Add the bookmark bar if it has children. + const BookmarkNode* bb_node = model->bookmark_bar_node(); + if (bb_node->child_count() > 0) + root_level_folders.push_back(bb_node); + + return root_level_folders; +} + +bool IsPrimaryPermanentNode(const BookmarkNode* node, BookmarkModel* model) { + std::vector primary_nodes(PrimaryPermanentNodes(model)); + if (std::find(primary_nodes.begin(), primary_nodes.end(), node) != + primary_nodes.end()) { + return true; + } + return false; +} + +const BookmarkNode* RootLevelFolderForNode(const BookmarkNode* node, + BookmarkModel* model) { + // This helper function doesn't work for managed bookmarks. This checks that + // |node| is editable by the user, which currently covers all the other + // bookmarks except the managed bookmarks. + DCHECK(model->client()->CanBeEditedByUser(node)); + + const std::vector root_folders(RootLevelFolders(model)); + const BookmarkNode* top = node; + while (top && + std::find(root_folders.begin(), root_folders.end(), top) == + root_folders.end()) { + top = top->parent(); + } + return top; +} + +} // namespace enhanced_bookmark_utils diff --git a/components/enhanced_bookmarks/enhanced_bookmark_utils.h b/components/enhanced_bookmarks/enhanced_bookmark_utils.h new file mode 100644 index 0000000000000..54417e1b72d79 --- /dev/null +++ b/components/enhanced_bookmarks/enhanced_bookmark_utils.h @@ -0,0 +1,49 @@ +// 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_ENHANCED_BOOKMARKS_ENHANCED_BOOKMARK_UTILS_H_ +#define COMPONENTS_ENHANCED_BOOKMARKS_ENHANCED_BOOKMARK_UTILS_H_ + +#include +#include +#include + +class BookmarkModel; +class BookmarkNode; + +namespace enhanced_bookmark_utils { + +// Returns an ordered vector of bookmarks that are urls that match |query|. +// |query| must be UTF8 encoded. +std::vector FindBookmarksWithQuery( + BookmarkModel* bookmark_model, + const std::string& query); + +// The vector is sorted in place. +// All of the bookmarks in |nodes| must be urls. +void SortBookmarksByName(std::vector& nodes); + +// Returns the permanent nodes whose url children are considered uncategorized +// and whose folder children should be shown in the bookmark menu. +// |model| must be loaded. +std::vector PrimaryPermanentNodes(BookmarkModel* model); + +// Returns an unsorted vector of folders that are considered to be at the "root" +// level of the bookmark hierarchy. Functionally, this means all direct +// descendants of PrimaryPermanentNodes, and possibly a node for the bookmarks +// bar. +std::vector RootLevelFolders(BookmarkModel* model); + +// Returns whether |node| is a primary permanent node in the sense of +// |PrimaryPermanentNodes|. +bool IsPrimaryPermanentNode(const BookmarkNode* node, BookmarkModel* model); + +// Returns the root level folder in which this node is directly, or indirectly +// via subfolders, located. +const BookmarkNode* RootLevelFolderForNode(const BookmarkNode* node, + BookmarkModel* model); + +} // namespace enhanced_bookmark_utils + +#endif // COMPONENTS_ENHANCED_BOOKMARKS_ENHANCED_BOOKMARK_UTILS_H_ diff --git a/components/enhanced_bookmarks/enhanced_bookmark_utils_unittest.cc b/components/enhanced_bookmarks/enhanced_bookmark_utils_unittest.cc new file mode 100644 index 0000000000000..ce9dec5ab55ef --- /dev/null +++ b/components/enhanced_bookmarks/enhanced_bookmark_utils_unittest.cc @@ -0,0 +1,57 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base64.h" +#include "base/strings/utf_string_conversions.h" +#include "components/bookmarks/browser/bookmark_model.h" +#include "components/bookmarks/test/test_bookmark_client.h" +#include "components/enhanced_bookmarks/enhanced_bookmark_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const GURL bookmark_url("http://example.com/index.html"); + +class EnhancedBookmarkUtilsTest : public testing::Test { + public: + EnhancedBookmarkUtilsTest() {} + virtual ~EnhancedBookmarkUtilsTest() {} + + protected: + DISALLOW_COPY_AND_ASSIGN(EnhancedBookmarkUtilsTest); + + // Adds a bookmark as the subnode at index 0 to other_node. + // |name| should be ASCII encoded. + // Returns the newly added bookmark. + const BookmarkNode* AddBookmark(BookmarkModel* model, std::string name) { + return model->AddURL(model->other_node(), + 0, // index. + base::ASCIIToUTF16(name), + bookmark_url); + } +}; + +TEST_F(EnhancedBookmarkUtilsTest, TestBookmarkSearch) { + test::TestBookmarkClient bookmark_client; + scoped_ptr bookmark_model(bookmark_client.CreateModel(false)); + const BookmarkNode* node1 = AddBookmark(bookmark_model.get(), "john hopkins"); + const BookmarkNode* node2 = AddBookmark(bookmark_model.get(), "JohN hopkins"); + const BookmarkNode* node3 = AddBookmark(bookmark_model.get(), "katy perry"); + const BookmarkNode* node4 = AddBookmark(bookmark_model.get(), "lil'john13"); + const BookmarkNode* node5 = AddBookmark(bookmark_model.get(), "jo hn"); + + std::vector result = + enhanced_bookmark_utils::FindBookmarksWithQuery(bookmark_model.get(), + "john"); + ASSERT_EQ(result.size(), 3u); + + CHECK(std::find(result.begin(), result.end(), node1) != result.end()); + CHECK(std::find(result.begin(), result.end(), node2) != result.end()); + CHECK(std::find(result.begin(), result.end(), node4) != result.end()); + + CHECK(std::find(result.begin(), result.end(), node3) == result.end()); + CHECK(std::find(result.begin(), result.end(), node5) == result.end()); +}; + +} // namespace diff --git a/components/history.gypi b/components/history.gypi index 63dae2eaf0e0f..868d9feaeace9 100644 --- a/components/history.gypi +++ b/components/history.gypi @@ -15,7 +15,9 @@ '../base/base.gyp:base', '../net/net.gyp:net', '../sql/sql.gyp:sql', + '../ui/gfx/gfx.gyp:gfx', '../url/url.gyp:url_lib', + 'favicon_base', 'keyed_service_core', 'query_parser', ], @@ -23,6 +25,8 @@ # Note: sources list duplicated in GN build. 'history/core/browser/history_client.cc', 'history/core/browser/history_client.h', + 'history/core/browser/history_types.cc', + 'history/core/browser/history_types.h', 'history/core/browser/history_match.cc', 'history/core/browser/history_match.h', 'history/core/browser/in_memory_database.cc', @@ -30,6 +34,8 @@ 'history/core/browser/keyword_id.h', 'history/core/browser/keyword_search_term.cc', 'history/core/browser/keyword_search_term.h', + 'history/core/browser/page_usage_data.cc', + 'history/core/browser/page_usage_data.h', 'history/core/browser/url_database.cc', 'history/core/browser/url_database.h', 'history/core/browser/url_row.cc', diff --git a/components/history/DEPS b/components/history/DEPS index 9222d5a27f39e..26313ed7c6576 100644 --- a/components/history/DEPS +++ b/components/history/DEPS @@ -1,6 +1,8 @@ include_rules = [ + "+components/favicon_base", "+components/keyed_service", "+components/query_parser", "+net", "+sql", + "+ui/gfx", ] diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn index bbf27e046200e..824abf211baaf 100644 --- a/components/history/core/browser/BUILD.gn +++ b/components/history/core/browser/BUILD.gn @@ -6,6 +6,8 @@ static_library("browser") { sources = [ "history_client.cc", "history_client.h", + "history_types.cc", + "history_types.h", "history_match.cc", "history_match.h", "in_memory_database.cc", @@ -13,6 +15,8 @@ static_library("browser") { "keyword_id.h", "keyword_search_term.cc", "keyword_search_term.h", + "page_usage_data.cc", + "page_usage_data.h", "url_database.cc", "url_database.h", "url_row.cc", @@ -21,9 +25,11 @@ static_library("browser") { deps = [ "//base", + "//components/favicon_base", "//components/keyed_service/core", "//components/query_parser", "//net", "//sql", + "//ui/gfx", ] } diff --git a/components/history/core/browser/history_types.cc b/components/history/core/browser/history_types.cc new file mode 100644 index 0000000000000..abcee1106038a --- /dev/null +++ b/components/history/core/browser/history_types.cc @@ -0,0 +1,260 @@ +// 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/history/core/browser/history_types.h" + +#include + +#include "base/logging.h" +#include "base/stl_util.h" +#include "components/history/core/browser/page_usage_data.h" + +namespace history { + +// QueryResults ---------------------------------------------------------------- + +QueryResults::QueryResults() : reached_beginning_(false) { +} + +QueryResults::~QueryResults() {} + +const size_t* QueryResults::MatchesForURL(const GURL& url, + size_t* num_matches) const { + URLToResultIndices::const_iterator found = url_to_results_.find(url); + if (found == url_to_results_.end()) { + if (num_matches) + *num_matches = 0; + return NULL; + } + + // All entries in the map should have at least one index, otherwise it + // shouldn't be in the map. + DCHECK(!found->second->empty()); + if (num_matches) + *num_matches = found->second->size(); + return &found->second->front(); +} + +void QueryResults::Swap(QueryResults* other) { + std::swap(first_time_searched_, other->first_time_searched_); + std::swap(reached_beginning_, other->reached_beginning_); + results_.swap(other->results_); + url_to_results_.swap(other->url_to_results_); +} + +void QueryResults::AppendURLBySwapping(URLResult* result) { + URLResult* new_result = new URLResult; + new_result->SwapResult(result); + + results_.push_back(new_result); + AddURLUsageAtIndex(new_result->url(), results_.size() - 1); +} + +void QueryResults::DeleteURL(const GURL& url) { + // Delete all instances of this URL. We re-query each time since each + // mutation will cause the indices to change. + while (const size_t* match_indices = MatchesForURL(url, NULL)) + DeleteRange(*match_indices, *match_indices); +} + +void QueryResults::DeleteRange(size_t begin, size_t end) { + DCHECK(begin <= end && begin < size() && end < size()); + + // First delete the pointers in the given range and store all the URLs that + // were modified. We will delete references to these later. + std::set urls_modified; + for (size_t i = begin; i <= end; i++) { + urls_modified.insert(results_[i]->url()); + } + + // Now just delete that range in the vector en masse (the STL ending is + // exclusive, while ours is inclusive, hence the +1). + results_.erase(results_.begin() + begin, results_.begin() + end + 1); + + // Delete the indicies referencing the deleted entries. + for (std::set::const_iterator url = urls_modified.begin(); + url != urls_modified.end(); ++url) { + URLToResultIndices::iterator found = url_to_results_.find(*url); + if (found == url_to_results_.end()) { + NOTREACHED(); + continue; + } + + // Need a signed loop type since we do -- which may take us to -1. + for (int match = 0; match < static_cast(found->second->size()); + match++) { + if (found->second[match] >= begin && found->second[match] <= end) { + // Remove this referece from the list. + found->second->erase(found->second->begin() + match); + match--; + } + } + + // Clear out an empty lists if we just made one. + if (found->second->empty()) + url_to_results_.erase(found); + } + + // Shift all other indices over to account for the removed ones. + AdjustResultMap(end + 1, std::numeric_limits::max(), + -static_cast(end - begin + 1)); +} + +void QueryResults::AddURLUsageAtIndex(const GURL& url, size_t index) { + URLToResultIndices::iterator found = url_to_results_.find(url); + if (found != url_to_results_.end()) { + // The URL is already in the list, so we can just append the new index. + found->second->push_back(index); + return; + } + + // Need to add a new entry for this URL. + base::StackVector new_list; + new_list->push_back(index); + url_to_results_[url] = new_list; +} + +void QueryResults::AdjustResultMap(size_t begin, size_t end, ptrdiff_t delta) { + for (URLToResultIndices::iterator i = url_to_results_.begin(); + i != url_to_results_.end(); ++i) { + for (size_t match = 0; match < i->second->size(); match++) { + size_t match_index = i->second[match]; + if (match_index >= begin && match_index <= end) + i->second[match] += delta; + } + } +} + +// QueryOptions ---------------------------------------------------------------- + +QueryOptions::QueryOptions() + : max_count(0), + duplicate_policy(QueryOptions::REMOVE_ALL_DUPLICATES) { +} + +void QueryOptions::SetRecentDayRange(int days_ago) { + end_time = base::Time::Now(); + begin_time = end_time - base::TimeDelta::FromDays(days_ago); +} + +int64 QueryOptions::EffectiveBeginTime() const { + return begin_time.ToInternalValue(); +} + +int64 QueryOptions::EffectiveEndTime() const { + return end_time.is_null() ? + std::numeric_limits::max() : end_time.ToInternalValue(); +} + +int QueryOptions::EffectiveMaxCount() const { + return max_count ? max_count : std::numeric_limits::max(); +} + +// MostVisitedURL -------------------------------------------------------------- + +MostVisitedURL::MostVisitedURL() {} + +MostVisitedURL::MostVisitedURL(const GURL& url, + const base::string16& title) + : url(url), + title(title) { +} + +MostVisitedURL::MostVisitedURL(const GURL& url, + const base::string16& title, + const base::Time& last_forced_time) + : url(url), + title(title), + last_forced_time(last_forced_time) { +} + +MostVisitedURL::~MostVisitedURL() {} + +// FilteredURL ----------------------------------------------------------------- + +FilteredURL::FilteredURL() : score(0.0) {} + +FilteredURL::FilteredURL(const PageUsageData& page_data) + : url(page_data.GetURL()), + title(page_data.GetTitle()), + score(page_data.GetScore()) { +} + +FilteredURL::~FilteredURL() {} + +// FilteredURL::ExtendedInfo --------------------------------------------------- + +FilteredURL::ExtendedInfo::ExtendedInfo() + : total_visits(0), + visits(0), + duration_opened(0) { +} + +// Images --------------------------------------------------------------------- + +Images::Images() {} + +Images::~Images() {} + +// TopSitesDelta -------------------------------------------------------------- + +TopSitesDelta::TopSitesDelta() {} + +TopSitesDelta::~TopSitesDelta() {} + +// ThumbnailMigration --------------------------------------------------------- + +ThumbnailMigration::ThumbnailMigration() {} + +ThumbnailMigration::~ThumbnailMigration() {} + +// MostVisitedThumbnails ------------------------------------------------------ + +MostVisitedThumbnails::MostVisitedThumbnails() {} + +MostVisitedThumbnails::~MostVisitedThumbnails() {} + +// IconMapping ---------------------------------------------------------------- + +IconMapping::IconMapping() + : mapping_id(0), icon_id(0), icon_type(favicon_base::INVALID_ICON) {} + +IconMapping::~IconMapping() {} + +// FaviconBitmapIDSize --------------------------------------------------------- + +FaviconBitmapIDSize::FaviconBitmapIDSize() + : bitmap_id(0) { +} + +FaviconBitmapIDSize::~FaviconBitmapIDSize() { +} + +// FaviconBitmap -------------------------------------------------------------- + +FaviconBitmap::FaviconBitmap() + : bitmap_id(0), + icon_id(0) { +} + +FaviconBitmap::~FaviconBitmap() { +} + +// ExpireHistoryArgs ---------------------------------------------------------- + +ExpireHistoryArgs::ExpireHistoryArgs() { +} + +ExpireHistoryArgs::~ExpireHistoryArgs() { +} + +void ExpireHistoryArgs::SetTimeRangeForOneDay(base::Time time) { + begin_time = time.LocalMidnight(); + + // Due to DST, leap seconds, etc., the next day at midnight may be more than + // 24 hours away, so add 36 hours and round back down to midnight. + end_time = (begin_time + base::TimeDelta::FromHours(36)).LocalMidnight(); +} + +} // namespace history diff --git a/components/history/core/browser/history_types.h b/components/history/core/browser/history_types.h new file mode 100644 index 0000000000000..05c740a725438 --- /dev/null +++ b/components/history/core/browser/history_types.h @@ -0,0 +1,420 @@ +// 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_HISTORY_CORE_BROWSER_HISTORY_TYPES_H_ +#define COMPONENTS_HISTORY_CORE_BROWSER_HISTORY_TYPES_H_ + +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/containers/stack_container.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_vector.h" +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "components/favicon_base/favicon_types.h" +#include "components/history/core/browser/url_row.h" +#include "components/history/core/common/thumbnail_score.h" +#include "ui/gfx/size.h" +#include "url/gurl.h" + +class PageUsageData; + +namespace history { + +// Forward declaration for friend statements. +class HistoryBackend; +class URLDatabase; + +// Container for a list of URLs. +typedef std::vector RedirectList; + +typedef int64 FaviconBitmapID; // Identifier for a bitmap in a favicon. +typedef int64 SegmentID; // URL segments for the most visited view. +typedef int64 IconMappingID; // For page url and icon mapping. + +// The enumeration of all possible sources of visits is listed below. +// The source will be propagated along with a URL or a visit item +// and eventually be stored in the history database, +// visit_source table specifically. +// Different from page transition types, they describe the origins of visits. +// (Warning): Please don't change any existing values while it is ok to add +// new values when needed. +enum VisitSource { + SOURCE_SYNCED = 0, // Synchronized from somewhere else. + SOURCE_BROWSED = 1, // User browsed. + SOURCE_EXTENSION = 2, // Added by an extension. + SOURCE_FIREFOX_IMPORTED = 3, + SOURCE_IE_IMPORTED = 4, + SOURCE_SAFARI_IMPORTED = 5, +}; + +typedef int64 VisitID; +// Structure to hold the mapping between each visit's id and its source. +typedef std::map VisitSourceMap; + +// PageVisit ------------------------------------------------------------------ + +// Represents a simplified version of a visit for external users. Normally, +// views are only interested in the time, and not the other information +// associated with a VisitRow. +struct PageVisit { + URLID page_id; + base::Time visit_time; +}; + +// QueryResults ---------------------------------------------------------------- + +// Encapsulates the results of a history query. It supports an ordered list of +// URLResult objects, plus an efficient way of looking up the index of each time +// a given URL appears in those results. +class QueryResults { + public: + typedef std::vector URLResultVector; + + QueryResults(); + ~QueryResults(); + + // Indicates the first time that the query includes results for (queries are + // clipped at the beginning, so it will always include to the end of the time + // queried). + // + // If the number of results was clipped as a result of the max count, this + // will be the time of the first query returned. If there were fewer results + // than we were allowed to return, this represents the first date considered + // in the query (this will be before the first result if there was time + // queried with no results). + // + // TODO(brettw): bug 1203054: This field is not currently set properly! Do + // not use until the bug is fixed. + base::Time first_time_searched() const { return first_time_searched_; } + void set_first_time_searched(base::Time t) { first_time_searched_ = t; } + // Note: If you need end_time_searched, it can be added. + + void set_reached_beginning(bool reached) { reached_beginning_ = reached; } + bool reached_beginning() { return reached_beginning_; } + + size_t size() const { return results_.size(); } + bool empty() const { return results_.empty(); } + + URLResult& back() { return *results_.back(); } + const URLResult& back() const { return *results_.back(); } + + URLResult& operator[](size_t i) { return *results_[i]; } + const URLResult& operator[](size_t i) const { return *results_[i]; } + + URLResultVector::const_iterator begin() const { return results_.begin(); } + URLResultVector::const_iterator end() const { return results_.end(); } + URLResultVector::const_reverse_iterator rbegin() const { + return results_.rbegin(); + } + URLResultVector::const_reverse_iterator rend() const { + return results_.rend(); + } + + // Returns a pointer to the beginning of an array of all matching indices + // for entries with the given URL. The array will be |*num_matches| long. + // |num_matches| can be NULL if the caller is not interested in the number of + // results (commonly it will only be interested in the first one and can test + // the pointer for NULL). + // + // When there is no match, it will return NULL and |*num_matches| will be 0. + const size_t* MatchesForURL(const GURL& url, size_t* num_matches) const; + + // Swaps the current result with another. This allows ownership to be + // efficiently transferred without copying. + void Swap(QueryResults* other); + + // Adds the given result to the map, using swap() on the members to avoid + // copying (there are a lot of strings and vectors). This means the parameter + // object will be cleared after this call. + void AppendURLBySwapping(URLResult* result); + + // Removes all instances of the given URL from the result set. + void DeleteURL(const GURL& url); + + // Deletes the given range of items in the result set. + void DeleteRange(size_t begin, size_t end); + + private: + // Maps the given URL to a list of indices into results_ which identify each + // time an entry with that URL appears. Normally, each URL will have one or + // very few indices after it, so we optimize this to use statically allocated + // memory when possible. + typedef std::map > URLToResultIndices; + + // Inserts an entry into the |url_to_results_| map saying that the given URL + // is at the given index in the results_. + void AddURLUsageAtIndex(const GURL& url, size_t index); + + // Adds |delta| to each index in url_to_results_ in the range [begin,end] + // (this is inclusive). This is used when inserting or deleting. + void AdjustResultMap(size_t begin, size_t end, ptrdiff_t delta); + + base::Time first_time_searched_; + + // Whether the query reaches the beginning of the database. + bool reached_beginning_; + + // The ordered list of results. The pointers inside this are owned by this + // QueryResults object. + ScopedVector results_; + + // Maps URLs to entries in results_. + URLToResultIndices url_to_results_; + + DISALLOW_COPY_AND_ASSIGN(QueryResults); +}; + +// QueryOptions ---------------------------------------------------------------- + +struct QueryOptions { + QueryOptions(); + + // The time range to search for matches in. The beginning is inclusive and + // the ending is exclusive. Either one (or both) may be null. + // + // This will match only the one recent visit of a URL. For text search + // queries, if the URL was visited in the given time period, but has also + // been visited more recently than that, it will not be returned. When the + // text query is empty, this will return the most recent visit within the + // time range. + base::Time begin_time; + base::Time end_time; + + // Sets the query time to the last |days_ago| days to the present time. + void SetRecentDayRange(int days_ago); + + // The maximum number of results to return. The results will be sorted with + // the most recent first, so older results may not be returned if there is not + // enough room. When 0, this will return everything (the default). + int max_count; + + enum DuplicateHandling { + // Omit visits for which there is a more recent visit to the same URL. + // Each URL in the results will appear only once. + REMOVE_ALL_DUPLICATES, + + // Omit visits for which there is a more recent visit to the same URL on + // the same day. Each URL will appear no more than once per day, where the + // day is defined by the local timezone. + REMOVE_DUPLICATES_PER_DAY, + + // Return all visits without deduping. + KEEP_ALL_DUPLICATES + }; + + // Allows the caller to specify how duplicate URLs in the result set should + // be handled. The default is REMOVE_DUPLICATES. + DuplicateHandling duplicate_policy; + + // Helpers to get the effective parameters values, since a value of 0 means + // "unspecified". + int EffectiveMaxCount() const; + int64 EffectiveBeginTime() const; + int64 EffectiveEndTime() const; +}; + +// VisibleVisitCountToHostResult ---------------------------------------------- + +// VisibleVisitCountToHostResult encapsulates the result of a call to +// HistoryBackend::GetVisibleVisitCountToHost. +struct VisibleVisitCountToHostResult { + // Indicates whether the call to HistoryBackend::GetVisibleVisitCountToHost + // was successfull or not. If false, then both |count| and |first_visit| are + // undefined. + bool success; + int count; + base::Time first_visit; +}; + +// MostVisitedURL -------------------------------------------------------------- + +// Holds the per-URL information of the most visited query. +struct MostVisitedURL { + MostVisitedURL(); + MostVisitedURL(const GURL& url, const base::string16& title); + MostVisitedURL(const GURL& url, + const base::string16& title, + const base::Time& last_forced_time); + ~MostVisitedURL(); + + GURL url; + base::string16 title; + + // If this is a URL for which we want to force a thumbnail, records the last + // time it was forced so we can evict it when more recent URLs are requested. + // If it's not a forced thumbnail, keep a time of 0. + base::Time last_forced_time; + + RedirectList redirects; + + bool operator==(const MostVisitedURL& other) { + return url == other.url; + } +}; + +// FilteredURL ----------------------------------------------------------------- + +// Holds the per-URL information of the filterd url query. +struct FilteredURL { + struct ExtendedInfo { + ExtendedInfo(); + // The absolute number of visits. + unsigned int total_visits; + // The number of visits, as seen by the Most Visited NTP pane. + unsigned int visits; + // The total number of seconds that the page was open. + int64 duration_opened; + // The time when the page was last visited. + base::Time last_visit_time; + }; + + FilteredURL(); + explicit FilteredURL(const PageUsageData& data); + ~FilteredURL(); + + GURL url; + base::string16 title; + double score; + ExtendedInfo extended_info; +}; + +// TopSites ------------------------------------------------------------------- + +typedef std::vector MostVisitedURLList; +typedef std::vector FilteredURLList; + +// Used by TopSites to store the thumbnails. +struct Images { + Images(); + ~Images(); + + scoped_refptr thumbnail; + ThumbnailScore thumbnail_score; + + // TODO(brettw): this will eventually store the favicon. + // scoped_refptr favicon; +}; + +struct MostVisitedURLWithRank { + MostVisitedURL url; + int rank; +}; + +typedef std::vector MostVisitedURLWithRankList; + +struct TopSitesDelta { + TopSitesDelta(); + ~TopSitesDelta(); + + MostVisitedURLList deleted; + MostVisitedURLWithRankList added; + MostVisitedURLWithRankList moved; +}; + +typedef std::map > URLToThumbnailMap; + +// Used when migrating most visited thumbnails out of history and into topsites. +struct ThumbnailMigration { + ThumbnailMigration(); + ~ThumbnailMigration(); + + MostVisitedURLList most_visited; + URLToThumbnailMap url_to_thumbnail_map; +}; + +typedef std::map URLToImagesMap; + +class MostVisitedThumbnails + : public base::RefCountedThreadSafe { + public: + MostVisitedThumbnails(); + + MostVisitedURLList most_visited; + URLToImagesMap url_to_images_map; + + private: + friend class base::RefCountedThreadSafe; + virtual ~MostVisitedThumbnails(); + + DISALLOW_COPY_AND_ASSIGN(MostVisitedThumbnails); +}; + +// Favicons ------------------------------------------------------------------- + +// Used for the mapping between the page and icon. +struct IconMapping { + IconMapping(); + ~IconMapping(); + + // The unique id of the mapping. + IconMappingID mapping_id; + + // The url of a web page. + GURL page_url; + + // The unique id of the icon. + favicon_base::FaviconID icon_id; + + // The url of the icon. + GURL icon_url; + + // The type of icon. + favicon_base::IconType icon_type; +}; + +// Defines a favicon bitmap and its associated pixel size. +struct FaviconBitmapIDSize { + FaviconBitmapIDSize(); + ~FaviconBitmapIDSize(); + + // The unique id of the favicon bitmap. + FaviconBitmapID bitmap_id; + + // The pixel dimensions of the associated bitmap. + gfx::Size pixel_size; +}; + +// Defines a favicon bitmap stored in the history backend. +struct FaviconBitmap { + FaviconBitmap(); + ~FaviconBitmap(); + + // The unique id of the bitmap. + FaviconBitmapID bitmap_id; + + // The id of the favicon to which the bitmap belongs to. + favicon_base::FaviconID icon_id; + + // Time at which |bitmap_data| was last updated. + base::Time last_updated; + + // The bits of the bitmap. + scoped_refptr bitmap_data; + + // The pixel dimensions of bitmap_data. + gfx::Size pixel_size; +}; + +struct ExpireHistoryArgs { + ExpireHistoryArgs(); + ~ExpireHistoryArgs(); + + // Sets |begin_time| and |end_time| to the beginning and end of the day (in + // local time) on which |time| occurs. + void SetTimeRangeForOneDay(base::Time time); + + std::set urls; + base::Time begin_time; + base::Time end_time; +}; + +} // namespace history + +#endif // COMPONENTS_HISTORY_CORE_BROWSER_HISTORY_TYPES_H_ diff --git a/chrome/browser/history/page_usage_data.cc b/components/history/core/browser/page_usage_data.cc similarity index 88% rename from chrome/browser/history/page_usage_data.cc rename to components/history/core/browser/page_usage_data.cc index e77740850748e..e240ff96bec1a 100644 --- a/chrome/browser/history/page_usage_data.cc +++ b/components/history/core/browser/page_usage_data.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/history/page_usage_data.h" +#include "components/history/core/browser/page_usage_data.h" #include diff --git a/chrome/browser/history/page_usage_data.h b/components/history/core/browser/page_usage_data.h similarity index 84% rename from chrome/browser/history/page_usage_data.h rename to components/history/core/browser/page_usage_data.h index ccc85e07b603c..284b19658c94b 100644 --- a/chrome/browser/history/page_usage_data.h +++ b/components/history/core/browser/page_usage_data.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 CHROME_BROWSER_HISTORY_PAGE_USAGE_DATA_H__ -#define CHROME_BROWSER_HISTORY_PAGE_USAGE_DATA_H__ +#ifndef COMPONENTS_HISTORY_CORE_BROWSER_PAGE_USAGE_DATA_H__ +#define COMPONENTS_HISTORY_CORE_BROWSER_PAGE_USAGE_DATA_H__ #include "base/strings/string16.h" -#include "chrome/browser/history/history_types.h" +#include "components/history/core/browser/history_types.h" #include "url/gurl.h" class SkBitmap; @@ -67,4 +67,4 @@ class PageUsageData { double score_; }; -#endif // CHROME_BROWSER_HISTORY_PAGE_USAGE_DATA_H__ +#endif // COMPONENTS_HISTORY_CORE_BROWSER_PAGE_USAGE_DATA_H__ diff --git a/components/invalidation.gypi b/components/invalidation.gypi index e7be8dd7c480e..3da96511bd131 100644 --- a/components/invalidation.gypi +++ b/components/invalidation.gypi @@ -12,7 +12,6 @@ '../base/base.gyp:base', '../google_apis/google_apis.gyp:google_apis', '../jingle/jingle.gyp:notifier', - '../sync/sync.gyp:sync', '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation', # TODO(akalin): Remove this (http://crbug.com/133352). '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_proto_cpp', @@ -115,9 +114,9 @@ '../jingle/jingle.gyp:notifier', '../jingle/jingle.gyp:notifier_test_util', '../net/net.gyp:net', - '../sync/sync.gyp:sync', '../testing/gmock.gyp:gmock', '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation', + '../third_party/cacheinvalidation/cacheinvalidation.gyp:cacheinvalidation_proto_cpp', 'gcm_driver_test_support', 'keyed_service_core', ], diff --git a/components/invalidation/BUILD.gn b/components/invalidation/BUILD.gn index 1476b479e0e92..40ec2b020cbde 100644 --- a/components/invalidation/BUILD.gn +++ b/components/invalidation/BUILD.gn @@ -87,10 +87,10 @@ static_library("invalidation") { "//components/signin/core/browser", "//google_apis", "//jingle:notifier", - "//sync", "//third_party/cacheinvalidation", # TODO(sync): Remove this (http://crbug.com/133352); + "//third_party/protobuf:protobuf_lite", # "//third_party/cacheinvalidation/src/google/cacheinvalidation:cacheinvalidation_proto_cpp", ] } @@ -129,8 +129,11 @@ static_library("test_support") { "//jingle:notifier", "//jingle:notifier_test_util", "//net", - "//sync", "//testing/gmock", "//third_party/cacheinvalidation", + + # TODO(sync): Remove this (http://crbug.com/133352); + "//third_party/protobuf:protobuf_lite", + # "//third_party/cacheinvalidation/src/google/cacheinvalidation:cacheinvalidation_proto_cpp", ] } diff --git a/components/invalidation/DEPS b/components/invalidation/DEPS index e56ab22731c3f..215635581aa3c 100644 --- a/components/invalidation/DEPS +++ b/components/invalidation/DEPS @@ -18,7 +18,4 @@ include_rules = [ # invalidation_notifier depends on the xmpp part of libjingle. "+talk/xmpp", - - #TODO(rlarocque): Remove this dependency. See crbug.com/394925. - "+sync/internal_api/public/util/weak_handle.h", ] diff --git a/components/invalidation/invalidation.cc b/components/invalidation/invalidation.cc index 0962c9e9cc2e2..f6d2c77ea3f1f 100644 --- a/components/invalidation/invalidation.cc +++ b/components/invalidation/invalidation.cc @@ -6,7 +6,9 @@ #include +#include "base/bind.h" #include "base/json/json_string_value_serializer.h" +#include "base/location.h" #include "base/rand_util.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" @@ -109,23 +111,30 @@ const AckHandle& Invalidation::ack_handle() const { return ack_handle_; } -void Invalidation::set_ack_handler(syncer::WeakHandle handler) { +void Invalidation::SetAckHandler( + base::WeakPtr handler, + scoped_refptr handler_task_runner) { ack_handler_ = handler; + ack_handler_task_runner_ = handler_task_runner; } bool Invalidation::SupportsAcknowledgement() const { - return ack_handler_.IsInitialized(); + return !!ack_handler_task_runner_; } void Invalidation::Acknowledge() const { if (SupportsAcknowledgement()) { - ack_handler_.Call(FROM_HERE, &AckHandler::Acknowledge, id_, ack_handle_); + ack_handler_task_runner_->PostTask( + FROM_HERE, + base::Bind(&AckHandler::Acknowledge, ack_handler_, id_, ack_handle_)); } } void Invalidation::Drop() { if (SupportsAcknowledgement()) { - ack_handler_.Call(FROM_HERE, &AckHandler::Drop, id_, ack_handle_); + ack_handler_task_runner_->PostTask( + FROM_HERE, + base::Bind(&AckHandler::Drop, ack_handler_, id_, ack_handle_)); } } diff --git a/components/invalidation/invalidation.h b/components/invalidation/invalidation.h index 4d6a5e3d7cc5e..ccc1b199e7dc3 100644 --- a/components/invalidation/invalidation.h +++ b/components/invalidation/invalidation.h @@ -9,11 +9,12 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" #include "base/values.h" #include "components/invalidation/ack_handle.h" #include "components/invalidation/invalidation_export.h" #include "google/cacheinvalidation/include/types.h" -#include "sync/internal_api/public/util/weak_handle.h" namespace syncer { @@ -57,7 +58,9 @@ class INVALIDATION_EXPORT Invalidation { // // Note that some sources of invalidations do not support ack tracking, and do // not set the ack_handler. This will be hidden from users of this class. - void set_ack_handler(syncer::WeakHandle ack_handler); + void SetAckHandler( + base::WeakPtr handler, + scoped_refptr handler_task_runner); // Returns whether or not this instance supports ack tracking. This will // depend on whether or not the source of invaliadations supports @@ -113,7 +116,10 @@ class INVALIDATION_EXPORT Invalidation { // A locally generated unique ID used to manage local acknowledgements. AckHandle ack_handle_; - syncer::WeakHandle ack_handler_; + + // The acknowledgement tracking handler and its thread. + base::WeakPtr ack_handler_; + scoped_refptr ack_handler_task_runner_; }; } // namespace syncer diff --git a/components/invalidation/invalidation_notifier.cc b/components/invalidation/invalidation_notifier.cc index daaec4d43516a..359ac059d9b14 100644 --- a/components/invalidation/invalidation_notifier.cc +++ b/components/invalidation/invalidation_notifier.cc @@ -22,11 +22,15 @@ InvalidationNotifier::InvalidationNotifier( const std::string& invalidator_client_id, const UnackedInvalidationsMap& saved_invalidations, const std::string& invalidation_bootstrap_data, - const WeakHandle& invalidation_state_tracker, + const base::WeakPtr& invalidation_state_tracker, + scoped_refptr + invalidation_state_tracker_task_runner, const std::string& client_info) : state_(STOPPED), saved_invalidations_(saved_invalidations), invalidation_state_tracker_(invalidation_state_tracker), + invalidation_state_tracker_task_runner_( + invalidation_state_tracker_task_runner), client_info_(client_info), invalidator_client_id_(invalidator_client_id), invalidation_bootstrap_data_(invalidation_bootstrap_data), @@ -64,9 +68,12 @@ void InvalidationNotifier::UpdateCredentials( if (state_ == STOPPED) { invalidation_listener_.Start( base::Bind(&invalidation::CreateInvalidationClient), - invalidator_client_id_, client_info_, invalidation_bootstrap_data_, + invalidator_client_id_, + client_info_, + invalidation_bootstrap_data_, saved_invalidations_, invalidation_state_tracker_, + invalidation_state_tracker_task_runner_, this); state_ = STARTED; } diff --git a/components/invalidation/invalidation_notifier.h b/components/invalidation/invalidation_notifier.h index 154d1e6dff404..c8812dbb1cf60 100644 --- a/components/invalidation/invalidation_notifier.h +++ b/components/invalidation/invalidation_notifier.h @@ -16,14 +16,16 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/sequenced_task_runner.h" #include "base/threading/non_thread_safe.h" #include "components/invalidation/invalidation_export.h" #include "components/invalidation/invalidation_state_tracker.h" #include "components/invalidation/invalidator.h" #include "components/invalidation/invalidator_registrar.h" #include "components/invalidation/sync_invalidation_listener.h" -#include "sync/internal_api/public/util/weak_handle.h" namespace notifier { class PushClient; @@ -43,8 +45,9 @@ class INVALIDATION_EXPORT_PRIVATE InvalidationNotifier const std::string& invalidator_client_id, const UnackedInvalidationsMap& saved_invalidations, const std::string& invalidation_bootstrap_data, - const WeakHandle& - invalidation_state_tracker, + const base::WeakPtr& invalidation_state_tracker, + scoped_refptr + invalidation_state_tracker_task_runner, const std::string& client_info); virtual ~InvalidationNotifier(); @@ -84,8 +87,9 @@ class INVALIDATION_EXPORT_PRIVATE InvalidationNotifier const UnackedInvalidationsMap saved_invalidations_; // Passed to |invalidation_listener_|. - const WeakHandle - invalidation_state_tracker_; + const base::WeakPtr invalidation_state_tracker_; + scoped_refptr + invalidation_state_tracker_task_runner_; // Passed to |invalidation_listener_|. const std::string client_info_; diff --git a/components/invalidation/invalidation_notifier_unittest.cc b/components/invalidation/invalidation_notifier_unittest.cc index 5fd249f6c2218..0c5df39a07aad 100644 --- a/components/invalidation/invalidation_notifier_unittest.cc +++ b/components/invalidation/invalidation_notifier_unittest.cc @@ -15,7 +15,6 @@ #include "jingle/notifier/base/notifier_options.h" #include "jingle/notifier/listener/fake_push_client.h" #include "net/url_request/url_request_test_util.h" -#include "sync/internal_api/public/util/weak_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace syncer { @@ -41,13 +40,13 @@ class InvalidationNotifierTestDelegate { scoped_ptr network_channel( new PushClientChannel(push_client.Pass())); invalidator_.reset( - new InvalidationNotifier( - network_channel.Pass(), - invalidator_client_id, - UnackedInvalidationsMap(), - initial_state, - MakeWeakHandle(invalidation_state_tracker), - "fake_client_info")); + new InvalidationNotifier(network_channel.Pass(), + invalidator_client_id, + UnackedInvalidationsMap(), + initial_state, + invalidation_state_tracker, + base::MessageLoopProxy::current(), + "fake_client_info")); } Invalidator* GetInvalidator() { diff --git a/components/invalidation/mock_ack_handler.cc b/components/invalidation/mock_ack_handler.cc index aba96cb7742fa..bdda9552c9638 100644 --- a/components/invalidation/mock_ack_handler.cc +++ b/components/invalidation/mock_ack_handler.cc @@ -4,6 +4,7 @@ #include "components/invalidation/mock_ack_handler.h" +#include "base/message_loop/message_loop_proxy.h" #include "components/invalidation/ack_handle.h" #include "components/invalidation/invalidation.h" @@ -34,7 +35,7 @@ MockAckHandler::~MockAckHandler() {} void MockAckHandler::RegisterInvalidation(Invalidation* invalidation) { unacked_invalidations_.push_back(*invalidation); - invalidation->set_ack_handler(WeakHandleThis()); + invalidation->SetAckHandler(AsWeakPtr(), base::MessageLoopProxy::current()); } void MockAckHandler::RegisterUnsentInvalidation(Invalidation* invalidation) { @@ -116,8 +117,4 @@ void MockAckHandler::Drop( unrecovered_drop_events_.insert(std::make_pair(id, handle)); } -WeakHandle MockAckHandler::WeakHandleThis() { - return WeakHandle(AsWeakPtr()); -} - } // namespace syncer diff --git a/components/invalidation/mock_ack_handler.h b/components/invalidation/mock_ack_handler.h index cd394b7510774..792c57a976fb0 100644 --- a/components/invalidation/mock_ack_handler.h +++ b/components/invalidation/mock_ack_handler.h @@ -13,7 +13,6 @@ #include "components/invalidation/ack_handler.h" #include "components/invalidation/invalidation_export.h" #include "components/invalidation/invalidation_util.h" -#include "sync/internal_api/public/util/weak_handle.h" namespace syncer { @@ -68,8 +67,6 @@ class INVALIDATION_EXPORT MockAckHandler AckHandle, ObjectIdLessThan> IdHandleMap; - WeakHandle WeakHandleThis(); - InvalidationVector unsent_invalidations_; InvalidationVector unacked_invalidations_; InvalidationVector acked_invalidations_; diff --git a/components/invalidation/non_blocking_invalidator.cc b/components/invalidation/non_blocking_invalidator.cc index 95ee59fe7ebbd..95b2b150c3956 100644 --- a/components/invalidation/non_blocking_invalidator.cc +++ b/components/invalidation/non_blocking_invalidator.cc @@ -18,7 +18,6 @@ #include "components/invalidation/object_id_invalidation_map.h" #include "components/invalidation/sync_system_resources.h" #include "jingle/notifier/listener/push_client.h" -#include "sync/internal_api/public/util/weak_handle.h" namespace syncer { @@ -28,8 +27,9 @@ struct NonBlockingInvalidator::InitializeOptions { const std::string& invalidator_client_id, const UnackedInvalidationsMap& saved_invalidations, const std::string& invalidation_bootstrap_data, - const WeakHandle& - invalidation_state_tracker, + const base::WeakPtr& invalidation_state_tracker, + const scoped_refptr& + invalidation_state_tracker_task_runner, const std::string& client_info, scoped_refptr request_context_getter) : network_channel_creator(network_channel_creator), @@ -37,15 +37,18 @@ struct NonBlockingInvalidator::InitializeOptions { saved_invalidations(saved_invalidations), invalidation_bootstrap_data(invalidation_bootstrap_data), invalidation_state_tracker(invalidation_state_tracker), + invalidation_state_tracker_task_runner( + invalidation_state_tracker_task_runner), client_info(client_info), - request_context_getter(request_context_getter) { - } + request_context_getter(request_context_getter) {} NetworkChannelCreator network_channel_creator; std::string invalidator_client_id; UnackedInvalidationsMap saved_invalidations; std::string invalidation_bootstrap_data; - WeakHandle invalidation_state_tracker; + base::WeakPtr invalidation_state_tracker; + scoped_refptr + invalidation_state_tracker_task_runner; std::string client_info; scoped_refptr request_context_getter; }; @@ -98,8 +101,9 @@ class NonBlockingInvalidator::Core public InvalidationHandler { public: // Called on parent thread. |delegate_observer| should be initialized. - explicit Core( - const WeakHandle& delegate_observer); + Core(const base::WeakPtr& delegate_observer, + const scoped_refptr& + delegate_observer_task_runner); // Helpers called on I/O thread. void Initialize( @@ -124,7 +128,8 @@ class NonBlockingInvalidator::Core virtual ~Core(); // The variables below should be used only on the I/O thread. - const WeakHandle delegate_observer_; + const base::WeakPtr delegate_observer_; + scoped_refptr delegate_observer_task_runner_; scoped_ptr invalidation_notifier_; scoped_refptr network_task_runner_; @@ -132,9 +137,13 @@ class NonBlockingInvalidator::Core }; NonBlockingInvalidator::Core::Core( - const WeakHandle& delegate_observer) - : delegate_observer_(delegate_observer) { - DCHECK(delegate_observer_.IsInitialized()); + const base::WeakPtr& delegate_observer, + const scoped_refptr& + delegate_observer_task_runner) + : delegate_observer_(delegate_observer), + delegate_observer_task_runner_(delegate_observer_task_runner) { + DCHECK(delegate_observer_); + DCHECK(delegate_observer_task_runner_); } NonBlockingInvalidator::Core::~Core() { @@ -148,14 +157,14 @@ void NonBlockingInvalidator::Core::Initialize( DCHECK(network_task_runner_->BelongsToCurrentThread()); scoped_ptr network_channel = initialize_options.network_channel_creator.Run(); - invalidation_notifier_.reset( - new InvalidationNotifier( - network_channel.Pass(), - initialize_options.invalidator_client_id, - initialize_options.saved_invalidations, - initialize_options.invalidation_bootstrap_data, - initialize_options.invalidation_state_tracker, - initialize_options.client_info)); + invalidation_notifier_.reset(new InvalidationNotifier( + network_channel.Pass(), + initialize_options.invalidator_client_id, + initialize_options.saved_invalidations, + initialize_options.invalidation_bootstrap_data, + initialize_options.invalidation_state_tracker, + initialize_options.invalidation_state_tracker_task_runner, + initialize_options.client_info)); invalidation_notifier_->RegisterHandler(this); } @@ -186,17 +195,21 @@ void NonBlockingInvalidator::Core::RequestDetailedStatus( void NonBlockingInvalidator::Core::OnInvalidatorStateChange( InvalidatorState reason) { DCHECK(network_task_runner_->BelongsToCurrentThread()); - delegate_observer_.Call(FROM_HERE, - &NonBlockingInvalidator::OnInvalidatorStateChange, - reason); + delegate_observer_task_runner_->PostTask( + FROM_HERE, + base::Bind(&NonBlockingInvalidator::OnInvalidatorStateChange, + delegate_observer_, + reason)); } void NonBlockingInvalidator::Core::OnIncomingInvalidation( const ObjectIdInvalidationMap& invalidation_map) { DCHECK(network_task_runner_->BelongsToCurrentThread()); - delegate_observer_.Call(FROM_HERE, - &NonBlockingInvalidator::OnIncomingInvalidation, - invalidation_map); + delegate_observer_task_runner_->PostTask( + FROM_HERE, + base::Bind(&NonBlockingInvalidator::OnIncomingInvalidation, + delegate_observer_, + invalidation_map)); } std::string NonBlockingInvalidator::Core::GetOwnerName() const { @@ -215,16 +228,21 @@ NonBlockingInvalidator::NonBlockingInvalidator( parent_task_runner_(base::ThreadTaskRunnerHandle::Get()), network_task_runner_(request_context_getter->GetNetworkTaskRunner()), weak_ptr_factory_(this) { - core_ = new Core(MakeWeakHandle(weak_ptr_factory_.GetWeakPtr())); - - InitializeOptions initialize_options( - network_channel_creator, - invalidator_client_id, - saved_invalidations, - invalidation_bootstrap_data, - MakeWeakHandle(weak_ptr_factory_.GetWeakPtr()), - client_info, - request_context_getter); + base::WeakPtr weak_ptr_this = + weak_ptr_factory_.GetWeakPtr(); + weak_ptr_this.get(); // Bind to this thread. + + core_ = new Core(weak_ptr_this, + base::MessageLoopProxy::current()); + + InitializeOptions initialize_options(network_channel_creator, + invalidator_client_id, + saved_invalidations, + invalidation_bootstrap_data, + weak_ptr_this, + base::MessageLoopProxy::current(), + client_info, + request_context_getter); if (!network_task_runner_->PostTask( FROM_HERE, diff --git a/components/invalidation/sync_invalidation_listener.cc b/components/invalidation/sync_invalidation_listener.cc index a6566be297d31..21abd527fd746 100644 --- a/components/invalidation/sync_invalidation_listener.cc +++ b/components/invalidation/sync_invalidation_listener.cc @@ -48,12 +48,14 @@ SyncInvalidationListener::~SyncInvalidationListener() { } void SyncInvalidationListener::Start( - const CreateInvalidationClientCallback& - create_invalidation_client_callback, - const std::string& client_id, const std::string& client_info, + const CreateInvalidationClientCallback& create_invalidation_client_callback, + const std::string& client_id, + const std::string& client_info, const std::string& invalidation_bootstrap_data, const UnackedInvalidationsMap& initial_unacked_invalidations, - const WeakHandle& invalidation_state_tracker, + const base::WeakPtr& invalidation_state_tracker, + const scoped_refptr& + invalidation_state_tracker_task_runner, Delegate* delegate) { DCHECK(CalledOnValidThread()); Stop(); @@ -69,7 +71,9 @@ void SyncInvalidationListener::Start( unacked_invalidations_map_ = initial_unacked_invalidations; invalidation_state_tracker_ = invalidation_state_tracker; - DCHECK(invalidation_state_tracker_.IsInitialized()); + invalidation_state_tracker_task_runner_ = + invalidation_state_tracker_task_runner; + DCHECK(invalidation_state_tracker_task_runner_); DCHECK(!delegate_); DCHECK(delegate); @@ -133,7 +137,7 @@ void SyncInvalidationListener::Invalidate( ObjectIdInvalidationMap invalidations; Invalidation inv = Invalidation::Init(id, invalidation.version(), payload); - inv.set_ack_handler(GetThisAsAckHandler()); + inv.SetAckHandler(AsWeakPtr(), base::MessageLoopProxy::current()); invalidations.Insert(inv); DispatchInvalidations(invalidations); @@ -150,7 +154,7 @@ void SyncInvalidationListener::InvalidateUnknownVersion( ObjectIdInvalidationMap invalidations; Invalidation unknown_version = Invalidation::InitUnknownVersion(object_id); - unknown_version.set_ack_handler(GetThisAsAckHandler()); + unknown_version.SetAckHandler(AsWeakPtr(), base::MessageLoopProxy::current()); invalidations.Insert(unknown_version); DispatchInvalidations(invalidations); @@ -170,7 +174,8 @@ void SyncInvalidationListener::InvalidateAll( for (ObjectIdSet::iterator it = registered_ids_.begin(); it != registered_ids_.end(); ++it) { Invalidation unknown_version = Invalidation::InitUnknownVersion(*it); - unknown_version.set_ack_handler(GetThisAsAckHandler()); + unknown_version.SetAckHandler(AsWeakPtr(), + base::MessageLoopProxy::current()); invalidations.Insert(unknown_version); } @@ -204,10 +209,11 @@ void SyncInvalidationListener::SaveInvalidations( lookup->second.AddSet(to_save.ForObject(*it)); } - invalidation_state_tracker_.Call( + invalidation_state_tracker_task_runner_->PostTask( FROM_HERE, - &InvalidationStateTracker::SetSavedInvalidations, - unacked_invalidations_map_); + base::Bind(&InvalidationStateTracker::SetSavedInvalidations, + invalidation_state_tracker_, + unacked_invalidations_map_)); } void SyncInvalidationListener::EmitSavedInvalidations( @@ -296,10 +302,11 @@ void SyncInvalidationListener::Acknowledge( return; } lookup->second.Acknowledge(handle); - invalidation_state_tracker_.Call( + invalidation_state_tracker_task_runner_->PostTask( FROM_HERE, - &InvalidationStateTracker::SetSavedInvalidations, - unacked_invalidations_map_); + base::Bind(&InvalidationStateTracker::SetSavedInvalidations, + invalidation_state_tracker_, + unacked_invalidations_map_)); } void SyncInvalidationListener::Drop( @@ -312,17 +319,21 @@ void SyncInvalidationListener::Drop( return; } lookup->second.Drop(handle); - invalidation_state_tracker_.Call( + invalidation_state_tracker_task_runner_->PostTask( FROM_HERE, - &InvalidationStateTracker::SetSavedInvalidations, - unacked_invalidations_map_); + base::Bind(&InvalidationStateTracker::SetSavedInvalidations, + invalidation_state_tracker_, + unacked_invalidations_map_)); } void SyncInvalidationListener::WriteState(const std::string& state) { DCHECK(CalledOnValidThread()); DVLOG(1) << "WriteState"; - invalidation_state_tracker_.Call( - FROM_HERE, &InvalidationStateTracker::SetBootstrapData, state); + invalidation_state_tracker_task_runner_->PostTask( + FROM_HERE, + base::Bind(&InvalidationStateTracker::SetBootstrapData, + invalidation_state_tracker_, + state)); } void SyncInvalidationListener::DoRegistrationUpdate() { @@ -333,10 +344,11 @@ void SyncInvalidationListener::DoRegistrationUpdate() { it != unregistered_ids.end(); ++it) { unacked_invalidations_map_.erase(*it); } - invalidation_state_tracker_.Call( + invalidation_state_tracker_task_runner_->PostTask( FROM_HERE, - &InvalidationStateTracker::SetSavedInvalidations, - unacked_invalidations_map_); + base::Bind(&InvalidationStateTracker::SetSavedInvalidations, + invalidation_state_tracker_, + unacked_invalidations_map_)); ObjectIdInvalidationMap object_id_invalidation_map; for (UnackedInvalidationsMap::iterator map_it = @@ -345,9 +357,9 @@ void SyncInvalidationListener::DoRegistrationUpdate() { if (registered_ids_.find(map_it->first) == registered_ids_.end()) { continue; } - map_it->second.ExportInvalidations( - GetThisAsAckHandler(), - &object_id_invalidation_map); + map_it->second.ExportInvalidations(AsWeakPtr(), + base::MessageLoopProxy::current(), + &object_id_invalidation_map); } // There's no need to run these through DispatchInvalidations(); they've @@ -428,9 +440,11 @@ void SyncInvalidationListener::EmitStateChange() { delegate_->OnInvalidatorStateChange(GetState()); } -WeakHandle SyncInvalidationListener::GetThisAsAckHandler() { +base::WeakPtr SyncInvalidationListener::AsWeakPtr() { DCHECK(CalledOnValidThread()); - return WeakHandle(weak_ptr_factory_.GetWeakPtr()); + base::WeakPtr weak_ptr = weak_ptr_factory_.GetWeakPtr(); + weak_ptr.get(); // Binds the pointer to this thread. + return weak_ptr; } void SyncInvalidationListener::OnNetworkChannelStateChanged( diff --git a/components/invalidation/sync_invalidation_listener.h b/components/invalidation/sync_invalidation_listener.h index 647a04d543967..3efc4a1a32ce8 100644 --- a/components/invalidation/sync_invalidation_listener.h +++ b/components/invalidation/sync_invalidation_listener.h @@ -24,7 +24,6 @@ #include "components/invalidation/sync_system_resources.h" #include "components/invalidation/unacked_invalidation_set.h" #include "google/cacheinvalidation/include/invalidation-listener.h" -#include "sync/internal_api/public/util/weak_handle.h" namespace buzz { class XmppTaskParentInterface; @@ -76,10 +75,13 @@ class INVALIDATION_EXPORT_PRIVATE SyncInvalidationListener void Start( const CreateInvalidationClientCallback& create_invalidation_client_callback, - const std::string& client_id, const std::string& client_info, + const std::string& client_id, + const std::string& client_info, const std::string& invalidation_bootstrap_data, const UnackedInvalidationsMap& initial_object_states, - const WeakHandle& invalidation_state_tracker, + const base::WeakPtr& invalidation_state_tracker, + const scoped_refptr& + invalidation_state_tracker_task_runner, Delegate* delegate); void UpdateCredentials(const std::string& email, const std::string& token); @@ -170,12 +172,14 @@ class INVALIDATION_EXPORT_PRIVATE SyncInvalidationListener // Generate a Dictionary with all the debugging information. scoped_ptr CollectDebugData() const; - WeakHandle GetThisAsAckHandler(); + base::WeakPtr AsWeakPtr(); scoped_ptr sync_network_channel_; SyncSystemResources sync_system_resources_; UnackedInvalidationsMap unacked_invalidations_map_; - WeakHandle invalidation_state_tracker_; + base::WeakPtr invalidation_state_tracker_; + scoped_refptr + invalidation_state_tracker_task_runner_; Delegate* delegate_; scoped_ptr invalidation_client_; scoped_ptr registration_manager_; diff --git a/components/invalidation/sync_invalidation_listener_unittest.cc b/components/invalidation/sync_invalidation_listener_unittest.cc index 339511e608e1c..141dec5f44f37 100644 --- a/components/invalidation/sync_invalidation_listener_unittest.cc +++ b/components/invalidation/sync_invalidation_listener_unittest.cc @@ -20,7 +20,6 @@ #include "google/cacheinvalidation/include/invalidation-client.h" #include "google/cacheinvalidation/include/types.h" #include "jingle/notifier/listener/fake_push_client.h" -#include "sync/internal_api/public/util/weak_handle.h" #include "testing/gtest/include/gtest/gtest.h" namespace syncer { @@ -292,12 +291,15 @@ class SyncInvalidationListenerTest : public testing::Test { void StartClient() { fake_invalidation_client_ = NULL; - listener_.Start(base::Bind(&CreateFakeInvalidationClient, - &fake_invalidation_client_), - kClientId, kClientInfo, kState, - fake_tracker_.GetSavedInvalidations(), - MakeWeakHandle(fake_tracker_.AsWeakPtr()), - &fake_delegate_); + listener_.Start( + base::Bind(&CreateFakeInvalidationClient, &fake_invalidation_client_), + kClientId, + kClientInfo, + kState, + fake_tracker_.GetSavedInvalidations(), + fake_tracker_.AsWeakPtr(), + base::MessageLoopProxy::current(), + &fake_delegate_); DCHECK(fake_invalidation_client_); } @@ -376,7 +378,10 @@ class SyncInvalidationListenerTest : public testing::Test { return SingleObjectInvalidationSet(); } ObjectIdInvalidationMap map; - it->second.ExportInvalidations(WeakHandle(), &map); + it->second.ExportInvalidations( + base::WeakPtr(), + scoped_refptr(), + &map); if (map.Empty()) { return SingleObjectInvalidationSet(); } else { diff --git a/components/invalidation/unacked_invalidation_set.cc b/components/invalidation/unacked_invalidation_set.cc index b2d2e68ecdae2..9519f33c8d513 100644 --- a/components/invalidation/unacked_invalidation_set.cc +++ b/components/invalidation/unacked_invalidation_set.cc @@ -56,13 +56,14 @@ void UnackedInvalidationSet::AddSet( } void UnackedInvalidationSet::ExportInvalidations( - WeakHandle ack_handler, + base::WeakPtr ack_handler, + scoped_refptr ack_handler_task_runner, ObjectIdInvalidationMap* out) const { for (SingleObjectInvalidationSet::const_iterator it = invalidations_.begin(); it != invalidations_.end(); ++it) { // Copy the invalidation and set the copy's ack_handler. Invalidation inv(*it); - inv.set_ack_handler(ack_handler); + inv.SetAckHandler(ack_handler, ack_handler_task_runner); out->Insert(inv); } } diff --git a/components/invalidation/unacked_invalidation_set.h b/components/invalidation/unacked_invalidation_set.h index 7b9b729957172..ac9b2c318e4a6 100644 --- a/components/invalidation/unacked_invalidation_set.h +++ b/components/invalidation/unacked_invalidation_set.h @@ -7,10 +7,11 @@ #include +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" #include "components/invalidation/invalidation.h" #include "components/invalidation/invalidation_export.h" #include "components/invalidation/invalidation_util.h" -#include "sync/internal_api/public/util/weak_handle.h" namespace base { class DictionaryValue; @@ -53,8 +54,10 @@ class INVALIDATION_EXPORT UnackedInvalidationSet { // The contents of the UnackedInvalidationSet are not directly modified by // this procedure, but the AckHandles stored in those exported invalidations // are likely to end up back here in calls to Acknowledge() or Drop(). - void ExportInvalidations(WeakHandle ack_handler, - ObjectIdInvalidationMap* out) const; + void ExportInvalidations( + base::WeakPtr ack_handler, + scoped_refptr ack_handler_task_runner, + ObjectIdInvalidationMap* out) const; // Removes all stored invalidations from this object. void Clear(); diff --git a/components/invalidation/unacked_invalidation_set_test_util.cc b/components/invalidation/unacked_invalidation_set_test_util.cc index d3cdd850cab9f..fc6ead97518bb 100644 --- a/components/invalidation/unacked_invalidation_set_test_util.cc +++ b/components/invalidation/unacked_invalidation_set_test_util.cc @@ -88,8 +88,10 @@ ObjectIdInvalidationMap UnackedInvalidationsMapToObjectIdInvalidationMap( ObjectIdInvalidationMap object_id_invalidation_map; for (UnackedInvalidationsMap::const_iterator it = state_map.begin(); it != state_map.end(); ++it) { - it->second.ExportInvalidations(syncer::WeakHandle(), - &object_id_invalidation_map); + it->second.ExportInvalidations( + base::WeakPtr(), + scoped_refptr(), + &object_id_invalidation_map); } return object_id_invalidation_map; } diff --git a/components/invalidation/unacked_invalidation_set_unittest.cc b/components/invalidation/unacked_invalidation_set_unittest.cc index 0abd189f86d00..51bdbcd86fc88 100644 --- a/components/invalidation/unacked_invalidation_set_unittest.cc +++ b/components/invalidation/unacked_invalidation_set_unittest.cc @@ -20,7 +20,10 @@ class UnackedInvalidationSetTest : public testing::Test { SingleObjectInvalidationSet GetStoredInvalidations() { ObjectIdInvalidationMap map; - unacked_invalidations_.ExportInvalidations(WeakHandle(), &map); + unacked_invalidations_.ExportInvalidations( + base::WeakPtr(), + scoped_refptr(), + &map); ObjectIdSet ids = map.GetObjectIds(); if (ids.find(kObjectId_) != ids.end()) { return map.ForObject(kObjectId_); diff --git a/components/metrics/metrics_service.cc b/components/metrics/metrics_service.cc index 6d6559222946c..e0915c9a992db 100644 --- a/components/metrics/metrics_service.cc +++ b/components/metrics/metrics_service.cc @@ -438,7 +438,6 @@ void MetricsService::DisableRecording() { metrics_providers_[i]->OnRecordingDisabled(); PushPendingLogsToPersistentStorage(); - DCHECK(!log_manager_.has_staged_log()); } bool MetricsService::recording_active() const { diff --git a/components/metrics/proto/system_profile.proto b/components/metrics/proto/system_profile.proto index 11453333049aa..e9ba5a429c856 100644 --- a/components/metrics/proto/system_profile.proto +++ b/components/metrics/proto/system_profile.proto @@ -11,7 +11,7 @@ option optimize_for = LITE_RUNTIME; package metrics; -// Next tag: 19 +// Next tag: 20 message SystemProfileProto { // The time when the client was compiled/linked, in seconds since the epoch. optional int64 build_timestamp = 1; @@ -486,6 +486,158 @@ message SystemProfileProto { } repeated FieldTrial field_trial = 9; + // Information about the A/V output device(s) (typically just a TV). + // However, a configuration may have one or more intermediate A/V devices + // between the source device and the TV (e.g. an A/V receiver, video + // processor, etc.). + message ExternalAudioVideoDevice { + // The manufacturer name (possibly encoded as a 3-letter code, e.g. "YMH" + // for Yamaha). + optional string manufacturer_name = 1; + + // The model name (e.g. "RX-V1900"). Some devices may report generic names + // like "receiver" or use the full manufacturer name (e.g "PHILIPS"). + optional string model_name = 2; + + // The product code (e.g. "0218"). + optional string product_code = 3; + + // The device types. A single device can have multiple types (e.g. a set-top + // box could be both a tuner and a player). The same type may even be + // repeated (e.g a device that reports two tuners). + enum AVDeviceType { + AV_DEVICE_TYPE_UNKNOWN = 0; + AV_DEVICE_TYPE_TV = 1; + AV_DEVICE_TYPE_RECORDER = 2; + AV_DEVICE_TYPE_TUNER = 3; + AV_DEVICE_TYPE_PLAYER = 4; + AV_DEVICE_TYPE_AUDIO_SYSTEM = 5; + } + repeated AVDeviceType av_device_type = 4; + + // The year of manufacture. + optional int32 manufacture_year = 5; + + // The week of manufacture. + // Note: per the Wikipedia EDID article, numbering for this field may not + // be consistent between manufacturers. + optional int32 manufacture_week = 6; + + // Max horizontal resolution in pixels. + optional int32 horizontal_resolution = 7; + + // Max vertical resolution in pixels. + optional int32 vertical_resolution = 8; + + // Audio capabilities of the device. + // Ref: http://en.wikipedia.org/wiki/Extended_display_identification_data + message AudioDescription { + // Audio format + enum AudioFormat { + AUDIO_FORMAT_UNKNOWN = 0; + AUDIO_FORMAT_LPCM = 1; + AUDIO_FORMAT_AC_3 = 2; + AUDIO_FORMAT_MPEG1 = 3; + AUDIO_FORMAT_MP3 = 4; + AUDIO_FORMAT_MPEG2 = 5; + AUDIO_FORMAT_AAC = 6; + AUDIO_FORMAT_DTS = 7; + AUDIO_FORMAT_ATRAC = 8; + AUDIO_FORMAT_ONE_BIT = 9; + AUDIO_FORMAT_DD_PLUS = 10; + AUDIO_FORMAT_DTS_HD = 11; + AUDIO_FORMAT_MLP_DOLBY_TRUEHD = 12; + AUDIO_FORMAT_DST_AUDIO = 13; + AUDIO_FORMAT_MICROSOFT_WMA_PRO = 14; + } + optional AudioFormat audio_format = 1; + + // Number of channels (e.g. 1, 2, 8, etc.). + optional int32 num_channels = 2; + + // Supported sample frequencies in Hz (e.g. 32000, 44100, etc.). + // Multiple frequencies may be specified. + repeated int32 sample_frequency_hz = 3; + + // Maximum bit rate in bits/s. + optional int32 max_bit_rate_per_second = 4; + + // Bit depth (e.g. 16, 20, 24, etc.). + optional int32 bit_depth = 5; + } + repeated AudioDescription audio_description = 9; + + // The position in AV setup. + // A value of 0 means this device is the TV. + // A value of 1 means this device is directly connected to one of + // the TV's inputs. + // Values > 1 indicate there are 1 or more devices between this device + // and the TV. + optional int32 position_in_setup = 10; + + // Whether this device is in the path to the TV. + optional bool is_in_path_to_tv = 11; + + // The CEC version the device supports. + // CEC stands for Consumer Electronics Control, a part of the HDMI + // specification. Not all HDMI devices support CEC. + // Only devices that support CEC will report a value here. + optional int32 cec_version = 12; + + // This message reports CEC commands seen by a device. + // After each log is sent, this information is cleared and gathered again. + // By collecting CEC status information by opcode we can determine + // which CEC features can be supported. + message CECCommand { + // The CEC command opcode. CEC supports up to 256 opcodes. + // We add only one CECCommand message per unique opcode. Only opcodes + // seen by the device will be reported. The remainder of the message + // accumulates status for this opcode (and device). + optional int32 opcode = 1; + + // The total number of commands received from the external device. + optional int32 num_received_direct = 2; + + // The number of commands received from the external device as part of a + // broadcast message. + optional int32 num_received_broadcast = 3; + + // The total number of commands sent to the external device. + optional int32 num_sent_direct = 4; + + // The number of commands sent to the external device as part of a + // broadcast message. + optional int32 num_sent_broadcast = 5; + + // The number of aborted commands for unknown reasons. + optional int32 num_aborted_unknown_reason = 6; + + // The number of aborted commands because of an unrecognized opcode. + optional int32 num_aborted_unrecognized = 7; + } + repeated CECCommand cec_command = 13; + } + repeated ExternalAudioVideoDevice external_audio_video_device = 14; + + // Information about the current wireless access point. Collected directly + // from the wireless access point via standard apis if the device is + // connected to the Internet wirelessly. Introduced for Chrome on TV devices + // but also can be collected by ChromeOS, Android or other clients. + message ExternalAccessPoint { + // The manufacturer name, for example "ASUSTeK Computer Inc.". + optional string manufacturer = 1; + + // The model name, for example "Wi-Fi Protected Setup Router". + optional string model_name = 2; + + // The model number, for example "RT-N16". + optional string model_number = 3; + + // The device name (sometime same as model_number), for example "RT-N16". + optional string device_name = 4; + } + optional ExternalAccessPoint external_access_point = 15; + // Number of users currently signed into a multiprofile session. // A zero value indicates that the user count changed while the log is open. // Logged only on ChromeOS. @@ -507,4 +659,18 @@ message SystemProfileProto { // extensions. If multiple extensions map to the same bucket, that bucket is // still only reported once. repeated int32 occupied_extension_bucket = 18; + + // The state of loaded extensions for this system. The system can have either + // no applicable extensions, extensions only from the webstore and verified by + // the webstore, extensions only from the webstore but not verified, or + // extensions not from the store. If there is a single off-store extension, + // then HAS_OFFSTORE is reported. This should be kept in sync with the + // corresponding enum in chrome/browser/metrics/extensions_metrics_provider.cc + enum ExtensionsState { + NO_EXTENSIONS = 0; + NO_OFFSTORE_VERIFIED = 1; + NO_OFFSTORE_UNVERIFIED = 2; + HAS_OFFSTORE = 3; + } + optional ExtensionsState offstore_extensions_state = 19; } diff --git a/components/nacl.gyp b/components/nacl.gyp index 2620b11829e17..7c096f92991bd 100644 --- a/components/nacl.gyp +++ b/components/nacl.gyp @@ -283,6 +283,8 @@ 'nacl/loader/nonsfi/irt_resource_open.cc', 'nacl/loader/nonsfi/irt_thread.cc', 'nacl/loader/nonsfi/irt_util.h', + 'nacl/loader/nonsfi/nonsfi_listener.cc', + 'nacl/loader/nonsfi/nonsfi_listener.h', 'nacl/loader/nonsfi/nonsfi_main.cc', 'nacl/loader/nonsfi/nonsfi_main.h', 'nacl/loader/nonsfi/nonsfi_sandbox.cc', diff --git a/components/nacl/loader/nacl_helper_linux.cc b/components/nacl/loader/nacl_helper_linux.cc index 8c042d02688e6..9789199828eba 100644 --- a/components/nacl/loader/nacl_helper_linux.cc +++ b/components/nacl/loader/nacl_helper_linux.cc @@ -35,6 +35,7 @@ #include "components/nacl/common/nacl_switches.h" #include "components/nacl/loader/nacl_listener.h" #include "components/nacl/loader/nonsfi/irt_exception_handling.h" +#include "components/nacl/loader/nonsfi/nonsfi_listener.h" #include "components/nacl/loader/sandbox_linux/nacl_sandbox_linux.h" #include "content/public/common/child_process_sandbox_support_linux.h" #include "content/public/common/content_descriptors.h" @@ -106,11 +107,15 @@ void BecomeNaClLoader(base::ScopedFD browser_fd, browser_fd.release()); base::MessageLoopForIO main_message_loop; - NaClListener listener; - listener.set_uses_nonsfi_mode(uses_nonsfi_mode); - listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size); - listener.set_number_of_cores(system_info.number_of_cores); - listener.Listen(); + if (uses_nonsfi_mode) { + nacl::nonsfi::NonSfiListener listener; + listener.Listen(); + } else { + NaClListener listener; + listener.set_prereserved_sandbox_size(system_info.prereserved_sandbox_size); + listener.set_number_of_cores(system_info.number_of_cores); + listener.Listen(); + } _exit(0); } diff --git a/components/nacl/loader/nacl_listener.cc b/components/nacl/loader/nacl_listener.cc index 433d9417d2abd..d7c97ad918bfc 100644 --- a/components/nacl/loader/nacl_listener.cc +++ b/components/nacl/loader/nacl_listener.cc @@ -35,10 +35,7 @@ #endif #if defined(OS_LINUX) -#include "components/nacl/loader/nonsfi/irt_random.h" -#include "components/nacl/loader/nonsfi/nonsfi_main.h" #include "content/public/common/child_process_sandbox_support_linux.h" -#include "ppapi/nacl_irt/plugin_startup.h" #endif #if defined(OS_WIN) @@ -207,7 +204,6 @@ class BrowserValidationDBProxy : public NaClValidationDB { NaClListener::NaClListener() : shutdown_event_(true, false), io_thread_("NaCl_IOThread"), - uses_nonsfi_mode_(false), #if defined(OS_LINUX) prereserved_sandbox_size_(0), #endif @@ -265,11 +261,6 @@ bool NaClListener::OnMessageReceived(const IPC::Message& msg) { } void NaClListener::OnStart(const nacl::NaClStartParams& params) { - if (uses_nonsfi_mode_) { - StartNonSfi(params); - return; - } - #if defined(OS_LINUX) || defined(OS_MACOSX) int urandom_fd = dup(base::GetUrandomFD()); if (urandom_fd < 0) { @@ -303,11 +294,14 @@ void NaClListener::OnStart(const nacl::NaClStartParams& params) { nap, NACL_CHROME_DESC_BASE + 1); } - IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener( - io_thread_.message_loop_proxy(), &shutdown_event_); + trusted_listener_ = new NaClTrustedListener( + IPC::Channel::GenerateVerifiedChannelID("nacl"), + io_thread_.message_loop_proxy().get()); if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated( - browser_handle, ppapi_renderer_handle, - trusted_renderer_handle, IPC::ChannelHandle()))) + browser_handle, + ppapi_renderer_handle, + trusted_listener_->TakeClientChannelHandle(), + IPC::ChannelHandle()))) LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost."; std::vector handles = params.handles; @@ -408,106 +402,3 @@ void NaClListener::OnStart(const nacl::NaClStartParams& params) { NaClChromeMainStartApp(nap, args); } - -void NaClListener::StartNonSfi(const nacl::NaClStartParams& params) { -#if !defined(OS_LINUX) - NOTREACHED() << "Non-SFI NaCl is only supported on Linux"; -#else - // Random number source initialization. - nacl::nonsfi::SetUrandomFd(base::GetUrandomFD()); - - IPC::ChannelHandle browser_handle; - IPC::ChannelHandle ppapi_renderer_handle; - IPC::ChannelHandle manifest_service_handle; - - if (params.enable_ipc_proxy) { - browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); - ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); - manifest_service_handle = - IPC::Channel::GenerateVerifiedChannelID("nacl"); - - // In non-SFI mode, we neither intercept nor rewrite the message using - // NaClIPCAdapter, and the channels are connected between the plugin and - // the hosts directly. So, the IPC::Channel instances will be created in - // the plugin side, because the IPC::Listener needs to live on the - // plugin's main thread. However, on initialization (i.e. before loading - // the plugin binary), the FD needs to be passed to the hosts. So, here - // we create raw FD pairs, and pass the client side FDs to the hosts, - // and the server side FDs to the plugin. - int browser_server_ppapi_fd; - int browser_client_ppapi_fd; - int renderer_server_ppapi_fd; - int renderer_client_ppapi_fd; - int manifest_service_server_fd; - int manifest_service_client_fd; - if (!IPC::SocketPair( - &browser_server_ppapi_fd, &browser_client_ppapi_fd) || - !IPC::SocketPair( - &renderer_server_ppapi_fd, &renderer_client_ppapi_fd) || - !IPC::SocketPair( - &manifest_service_server_fd, &manifest_service_client_fd)) { - LOG(ERROR) << "Failed to create sockets for IPC."; - return; - } - - // Set the plugin IPC channel FDs. - ppapi::SetIPCFileDescriptors(browser_server_ppapi_fd, - renderer_server_ppapi_fd, - manifest_service_server_fd); - ppapi::StartUpPlugin(); - - // Send back to the client side IPC channel FD to the host. - browser_handle.socket = - base::FileDescriptor(browser_client_ppapi_fd, true); - ppapi_renderer_handle.socket = - base::FileDescriptor(renderer_client_ppapi_fd, true); - manifest_service_handle.socket = - base::FileDescriptor(manifest_service_client_fd, true); - } - - // TODO(teravest): Do we plan on using this renderer handle for nexe loading - // for non-SFI? Right now, passing an empty channel handle instead causes - // hangs, so we'll keep it. - IPC::ChannelHandle trusted_renderer_handle = CreateTrustedListener( - io_thread_.message_loop_proxy(), &shutdown_event_); - if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated( - browser_handle, ppapi_renderer_handle, - trusted_renderer_handle, manifest_service_handle))) - LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost."; - - // Ensure that the validation cache key (used as an extra input to the - // validation cache's hashing) isn't exposed accidentally. - CHECK(!params.validation_cache_enabled); - CHECK(params.validation_cache_key.size() == 0); - CHECK(params.version.size() == 0); - // Ensure that a debug stub FD isn't passed through accidentally. - CHECK(!params.enable_debug_stub); - CHECK(params.debug_stub_server_bound_socket.fd == -1); - - CHECK(!params.uses_irt); - CHECK(params.handles.empty()); - - CHECK(params.nexe_file != IPC::InvalidPlatformFileForTransit()); - CHECK(params.nexe_token_lo == 0); - CHECK(params.nexe_token_hi == 0); - nacl::nonsfi::MainStart( - IPC::PlatformFileForTransitToPlatformFile(params.nexe_file)); -#endif // defined(OS_LINUX) -} - -IPC::ChannelHandle NaClListener::CreateTrustedListener( - base::MessageLoopProxy* message_loop_proxy, - base::WaitableEvent* shutdown_event) { - // The argument passed to GenerateVerifiedChannelID() here MUST be "nacl". - // Using an alternate channel name prevents the pipe from being created on - // Windows when the sandbox is enabled. - IPC::ChannelHandle trusted_renderer_handle = - IPC::Channel::GenerateVerifiedChannelID("nacl"); - trusted_listener_ = new NaClTrustedListener( - trusted_renderer_handle, io_thread_.message_loop_proxy().get()); -#if defined(OS_POSIX) - trusted_renderer_handle.socket = base::FileDescriptor( - trusted_listener_->TakeClientFileDescriptor(), true); -#endif - return trusted_renderer_handle; -} diff --git a/components/nacl/loader/nacl_listener.h b/components/nacl/loader/nacl_listener.h index 6ca1ec2cb792f..31a0048a0fbff 100644 --- a/components/nacl/loader/nacl_listener.h +++ b/components/nacl/loader/nacl_listener.h @@ -34,9 +34,6 @@ class NaClListener : public IPC::Listener { bool Send(IPC::Message* msg); - void set_uses_nonsfi_mode(bool uses_nonsfi_mode) { - uses_nonsfi_mode_ = uses_nonsfi_mode; - } #if defined(OS_LINUX) void set_prereserved_sandbox_size(size_t prereserved_sandbox_size) { prereserved_sandbox_size_ = prereserved_sandbox_size; @@ -53,13 +50,6 @@ class NaClListener : public IPC::Listener { void OnStart(const nacl::NaClStartParams& params); - // Non-SFI version of OnStart(). - void StartNonSfi(const nacl::NaClStartParams& params); - - IPC::ChannelHandle CreateTrustedListener( - base::MessageLoopProxy* message_loop_proxy, - base::WaitableEvent* shutdown_event); - // A channel back to the browser. scoped_ptr channel_; @@ -69,7 +59,6 @@ class NaClListener : public IPC::Listener { base::WaitableEvent shutdown_event_; base::Thread io_thread_; - bool uses_nonsfi_mode_; #if defined(OS_LINUX) size_t prereserved_sandbox_size_; #endif diff --git a/components/nacl/loader/nacl_trusted_listener.cc b/components/nacl/loader/nacl_trusted_listener.cc index 5f6410a83f0f1..125f146c3352d 100644 --- a/components/nacl/loader/nacl_trusted_listener.cc +++ b/components/nacl/loader/nacl_trusted_listener.cc @@ -8,22 +8,23 @@ NaClTrustedListener::NaClTrustedListener( const IPC::ChannelHandle& handle, - base::SingleThreadTaskRunner* ipc_task_runner) { - channel_proxy_ = IPC::ChannelProxy::Create( - handle, - IPC::Channel::MODE_SERVER, - this, - ipc_task_runner).Pass(); + base::SingleThreadTaskRunner* ipc_task_runner) + : channel_handle_(handle), + channel_proxy_(IPC::ChannelProxy::Create( + handle, IPC::Channel::MODE_SERVER, this, ipc_task_runner)) { } NaClTrustedListener::~NaClTrustedListener() { } +IPC::ChannelHandle NaClTrustedListener::TakeClientChannelHandle() { + IPC::ChannelHandle handle = channel_handle_; #if defined(OS_POSIX) -int NaClTrustedListener::TakeClientFileDescriptor() { - return channel_proxy_->TakeClientFileDescriptor(); -} + handle.socket = + base::FileDescriptor(channel_proxy_->TakeClientFileDescriptor(), true); #endif + return handle; +} bool NaClTrustedListener::OnMessageReceived(const IPC::Message& msg) { return false; diff --git a/components/nacl/loader/nacl_trusted_listener.h b/components/nacl/loader/nacl_trusted_listener.h index 4819e05f59345..bb66b42a3e5e6 100644 --- a/components/nacl/loader/nacl_trusted_listener.h +++ b/components/nacl/loader/nacl_trusted_listener.h @@ -20,9 +20,7 @@ class NaClTrustedListener : public base::RefCounted, NaClTrustedListener(const IPC::ChannelHandle& handle, base::SingleThreadTaskRunner* ipc_task_runner); -#if defined(OS_POSIX) - int TakeClientFileDescriptor(); -#endif + IPC::ChannelHandle TakeClientChannelHandle(); // Listener implementation. virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; @@ -33,6 +31,7 @@ class NaClTrustedListener : public base::RefCounted, private: friend class base::RefCounted; virtual ~NaClTrustedListener(); + IPC::ChannelHandle channel_handle_; scoped_ptr channel_proxy_; DISALLOW_COPY_AND_ASSIGN(NaClTrustedListener); diff --git a/components/nacl/loader/nonsfi/irt_exception_handling.cc b/components/nacl/loader/nonsfi/irt_exception_handling.cc index 0e230d78e332c..327f6a40e598d 100644 --- a/components/nacl/loader/nonsfi/irt_exception_handling.cc +++ b/components/nacl/loader/nonsfi/irt_exception_handling.cc @@ -25,7 +25,10 @@ namespace { // NonSFI NaCl does not use NACL_THREAD_SUSPEND_SIGNAL (==SIGUSR1), // and SIGSYS is reserved for seccomp-bpf. const int kSignals[] = { +#if !defined(__mips__) + // This signal does not exist on MIPS. SIGSTKFLT, +#endif SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGBUS, SIGFPE, SIGSEGV, // Handle SIGABRT in case someone sends it asynchronously using kill(). SIGABRT diff --git a/components/nacl/loader/nonsfi/nonsfi_listener.cc b/components/nacl/loader/nonsfi/nonsfi_listener.cc new file mode 100644 index 0000000000000..839772cf5e617 --- /dev/null +++ b/components/nacl/loader/nonsfi/nonsfi_listener.cc @@ -0,0 +1,150 @@ +// 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/nacl/loader/nonsfi/nonsfi_listener.h" + +#include "base/command_line.h" +#include "base/file_descriptor_posix.h" +#include "base/logging.h" +#include "base/message_loop/message_loop.h" +#include "base/rand_util.h" +#include "components/nacl/common/nacl_messages.h" +#include "components/nacl/common/nacl_types.h" +#include "components/nacl/loader/nacl_trusted_listener.h" +#include "components/nacl/loader/nonsfi/irt_random.h" +#include "components/nacl/loader/nonsfi/nonsfi_main.h" +#include "ipc/ipc_channel.h" +#include "ipc/ipc_channel_handle.h" +#include "ipc/ipc_switches.h" +#include "ipc/ipc_sync_channel.h" +#include "ppapi/nacl_irt/plugin_startup.h" + +#if !defined(OS_LINUX) +# error "non-SFI mode is supported only on linux." +#endif + +namespace nacl { +namespace nonsfi { + +NonSfiListener::NonSfiListener() : io_thread_("NaCl_IOThread"), + shutdown_event_(true, false) { + io_thread_.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); +} + +NonSfiListener::~NonSfiListener() { +} + +void NonSfiListener::Listen() { + channel_ = IPC::SyncChannel::Create( + CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kProcessChannelID), + IPC::Channel::MODE_CLIENT, + this, // As a Listener. + io_thread_.message_loop_proxy().get(), + true, // Create pipe now. + &shutdown_event_); + base::MessageLoop::current()->Run(); +} + +bool NonSfiListener::Send(IPC::Message* msg) { + DCHECK(channel_.get() != NULL); + return channel_->Send(msg); +} + +bool NonSfiListener::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(NonSfiListener, msg) + IPC_MESSAGE_HANDLER(NaClProcessMsg_Start, OnStart) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void NonSfiListener::OnStart(const nacl::NaClStartParams& params) { + // Random number source initialization. + SetUrandomFd(base::GetUrandomFD()); + + IPC::ChannelHandle browser_handle; + IPC::ChannelHandle ppapi_renderer_handle; + IPC::ChannelHandle manifest_service_handle; + + if (params.enable_ipc_proxy) { + browser_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); + ppapi_renderer_handle = IPC::Channel::GenerateVerifiedChannelID("nacl"); + manifest_service_handle = + IPC::Channel::GenerateVerifiedChannelID("nacl"); + + // In non-SFI mode, we neither intercept nor rewrite the message using + // NaClIPCAdapter, and the channels are connected between the plugin and + // the hosts directly. So, the IPC::Channel instances will be created in + // the plugin side, because the IPC::Listener needs to live on the + // plugin's main thread. However, on initialization (i.e. before loading + // the plugin binary), the FD needs to be passed to the hosts. So, here + // we create raw FD pairs, and pass the client side FDs to the hosts, + // and the server side FDs to the plugin. + int browser_server_ppapi_fd; + int browser_client_ppapi_fd; + int renderer_server_ppapi_fd; + int renderer_client_ppapi_fd; + int manifest_service_server_fd; + int manifest_service_client_fd; + if (!IPC::SocketPair( + &browser_server_ppapi_fd, &browser_client_ppapi_fd) || + !IPC::SocketPair( + &renderer_server_ppapi_fd, &renderer_client_ppapi_fd) || + !IPC::SocketPair( + &manifest_service_server_fd, &manifest_service_client_fd)) { + LOG(ERROR) << "Failed to create sockets for IPC."; + return; + } + + // Set the plugin IPC channel FDs. + ppapi::SetIPCFileDescriptors(browser_server_ppapi_fd, + renderer_server_ppapi_fd, + manifest_service_server_fd); + ppapi::StartUpPlugin(); + + // Send back to the client side IPC channel FD to the host. + browser_handle.socket = + base::FileDescriptor(browser_client_ppapi_fd, true); + ppapi_renderer_handle.socket = + base::FileDescriptor(renderer_client_ppapi_fd, true); + manifest_service_handle.socket = + base::FileDescriptor(manifest_service_client_fd, true); + } + + // TODO(teravest): Do we plan on using this renderer handle for nexe loading + // for non-SFI? Right now, passing an empty channel handle instead causes + // hangs, so we'll keep it. + trusted_listener_ = new NaClTrustedListener( + IPC::Channel::GenerateVerifiedChannelID("nacl"), + io_thread_.message_loop_proxy().get()); + if (!Send(new NaClProcessHostMsg_PpapiChannelsCreated( + browser_handle, + ppapi_renderer_handle, + trusted_listener_->TakeClientChannelHandle(), + manifest_service_handle))) + LOG(ERROR) << "Failed to send IPC channel handle to NaClProcessHost."; + + // Ensure that the validation cache key (used as an extra input to the + // validation cache's hashing) isn't exposed accidentally. + CHECK(!params.validation_cache_enabled); + CHECK(params.validation_cache_key.size() == 0); + CHECK(params.version.size() == 0); + // Ensure that a debug stub FD isn't passed through accidentally. + CHECK(!params.enable_debug_stub); + CHECK(params.debug_stub_server_bound_socket.fd == -1); + + CHECK(!params.uses_irt); + CHECK(params.handles.empty()); + + CHECK(params.nexe_file != IPC::InvalidPlatformFileForTransit()); + CHECK(params.nexe_token_lo == 0); + CHECK(params.nexe_token_hi == 0); + MainStart(IPC::PlatformFileForTransitToPlatformFile(params.nexe_file)); +} + +} // namespace nonsfi +} // namespace nacl diff --git a/components/nacl/loader/nonsfi/nonsfi_listener.h b/components/nacl/loader/nonsfi/nonsfi_listener.h new file mode 100644 index 0000000000000..a2acc61a6308c --- /dev/null +++ b/components/nacl/loader/nonsfi/nonsfi_listener.h @@ -0,0 +1,53 @@ +// 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_NACL_LOADER_NONSFI_NONSFI_LISTENER_H_ +#define COMPONENTS_NACL_LOADER_NONSFI_NONSFI_LISTENER_H_ + +#include "base/macros.h" + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "ipc/ipc_listener.h" + +namespace IPC { +class Message; +class SyncChannel; +} // namespace IPC + +class NaClTrustedListener; + +namespace nacl { + +struct NaClStartParams; + +namespace nonsfi { + +class NonSfiListener : public IPC::Listener { + public: + NonSfiListener(); + virtual ~NonSfiListener(); + + // Listen for a request to launch a non-SFI NaCl module. + void Listen(); + bool Send(IPC::Message* msg); + + private: + virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + void OnStart(const nacl::NaClStartParams& params); + + base::Thread io_thread_; + base::WaitableEvent shutdown_event_; + scoped_ptr channel_; + scoped_refptr trusted_listener_; + + DISALLOW_COPY_AND_ASSIGN(NonSfiListener); +}; + +} // namespace nonsfi +} // namespace nacl + +#endif // COMPONENTS_NACL_LOADER_NONSFI_NONSFI_LISTENER_H_ diff --git a/components/nacl/renderer/ppb_nacl_private_impl.cc b/components/nacl/renderer/ppb_nacl_private_impl.cc index 5090ad1ac06c9..36c9272831a42 100644 --- a/components/nacl/renderer/ppb_nacl_private_impl.cc +++ b/components/nacl/renderer/ppb_nacl_private_impl.cc @@ -1699,6 +1699,7 @@ void StreamPexe(PP_Instance instance, url_request.addHTTPHeaderField( blink::WebString::fromUTF8("Accept"), blink::WebString::fromUTF8("application/x-pnacl, */*")); + url_request.setRequestContext(blink::WebURLRequest::RequestContextObject); downloader->Load(url_request); } diff --git a/components/navigation_interception/intercept_navigation_resource_throttle.cc b/components/navigation_interception/intercept_navigation_resource_throttle.cc index f8f094e51a251..8729d2ceeb9d8 100644 --- a/components/navigation_interception/intercept_navigation_resource_throttle.cc +++ b/components/navigation_interception/intercept_navigation_resource_throttle.cc @@ -92,6 +92,8 @@ std::string InterceptNavigationResourceThrottle::GetMethodAfterRedirect() { net::HttpResponseHeaders* headers = request_->response_headers(); if (!headers) return request_->method(); + // TODO(davidben): Plumb net::RedirectInfo through content::ResourceThrottle + // and unexpose net::URLRequest::ComputeMethodForRedirect. return net::URLRequest::ComputeMethodForRedirect( request_->method(), headers->response_code()); } diff --git a/components/omnibox.gypi b/components/omnibox.gypi index 6721110168cbc..4024f8ccf4985 100644 --- a/components/omnibox.gypi +++ b/components/omnibox.gypi @@ -12,7 +12,9 @@ '../base/base.gyp:base', '../net/net.gyp:net', '../url/url.gyp:url_lib', + 'search', 'search_engines', + 'variations', 'component_metrics_proto', 'components_resources.gyp:components_resources', 'url_fixer', @@ -31,7 +33,13 @@ 'omnibox/autocomplete_provider.cc', 'omnibox/autocomplete_provider.h', 'omnibox/autocomplete_provider_listener.h', + 'omnibox/autocomplete_result.cc', + 'omnibox/autocomplete_result.h', 'omnibox/autocomplete_scheme_classifier.h', + 'omnibox/omnibox_field_trial.cc', + 'omnibox/omnibox_field_trial.h', + 'omnibox/omnibox_switches.cc', + 'omnibox/omnibox_switches.h', 'omnibox/search_suggestion_parser.cc', 'omnibox/search_suggestion_parser.h', 'omnibox/url_prefix.cc', diff --git a/components/omnibox/BUILD.gn b/components/omnibox/BUILD.gn index 1b2735be9172e..fa873aa9490da 100644 --- a/components/omnibox/BUILD.gn +++ b/components/omnibox/BUILD.gn @@ -12,7 +12,15 @@ static_library("omnibox") { "autocomplete_match_type.h", "autocomplete_provider.cc", "autocomplete_provider.h", + "autocomplete_result.cc", + "autocomplete_result.h", "autocomplete_scheme_classifier.h", + "omnibox_field_trial.cc", + "omnibox_field_trial.h", + "omnibox_switches.cc", + "omnibox_switches.h", + "search_suggestion_parser.cc", + "search_suggestion_parser.h", "url_prefix.cc", "url_prefix.h", ] @@ -49,6 +57,8 @@ source_set("unit_tests") { sources = [ "autocomplete_input_unittest.cc", "autocomplete_match_unittest.cc", + "autocomplete_result_unittest.cc", + "omnibox_field_trial_unittest.cc", ] deps = [ diff --git a/components/omnibox/DEPS b/components/omnibox/DEPS index a919c9468ba3c..86542c846b232 100644 --- a/components/omnibox/DEPS +++ b/components/omnibox/DEPS @@ -1,7 +1,9 @@ include_rules = [ "+components/metrics/proto", + "+components/search", "+components/search_engines", "+components/url_fixer", + "+components/variations", "+grit", "+net", "+url", diff --git a/chrome/browser/autocomplete/autocomplete_result.cc b/components/omnibox/autocomplete_result.cc similarity index 97% rename from chrome/browser/autocomplete/autocomplete_result.cc rename to components/omnibox/autocomplete_result.cc index 8d2bd6f344ee6..41ab4d0948783 100644 --- a/chrome/browser/autocomplete/autocomplete_result.cc +++ b/components/omnibox/autocomplete_result.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 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. -#include "chrome/browser/autocomplete/autocomplete_result.h" +#include "components/omnibox/autocomplete_result.h" #include #include @@ -10,15 +10,14 @@ #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/omnibox/omnibox_field_trial.h" -#include "chrome/browser/search/search.h" #include "components/metrics/proto/omnibox_event.pb.h" #include "components/metrics/proto/omnibox_input_type.pb.h" #include "components/omnibox/autocomplete_input.h" #include "components/omnibox/autocomplete_match.h" #include "components/omnibox/autocomplete_provider.h" -#include "content/public/common/url_constants.h" -#include "url/url_constants.h" +#include "components/omnibox/omnibox_field_trial.h" +#include "components/search/search.h" +#include "components/url_fixer/url_fixer.h" using metrics::OmniboxEventProto; @@ -272,9 +271,8 @@ void AutocompleteResult::SortAndCull( const std::string& in_scheme = base::UTF16ToUTF8(input.scheme()); const std::string& dest_scheme = default_match_->destination_url.scheme(); - DCHECK((in_scheme == dest_scheme) || - ((in_scheme == url::kAboutScheme) && - (dest_scheme == content::kChromeUIScheme))) << debug_info; + DCHECK(url_fixer::IsEquivalentScheme(in_scheme, dest_scheme)) + << debug_info; } } } diff --git a/chrome/browser/autocomplete/autocomplete_result.h b/components/omnibox/autocomplete_result.h similarity index 97% rename from chrome/browser/autocomplete/autocomplete_result.h rename to components/omnibox/autocomplete_result.h index e128ae7c92dd3..6681581647dc5 100644 --- a/chrome/browser/autocomplete/autocomplete_result.h +++ b/components/omnibox/autocomplete_result.h @@ -1,9 +1,9 @@ -// Copyright (c) 2012 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. -#ifndef CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_RESULT_H_ -#define CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_RESULT_H_ +#ifndef COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_ +#define COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_ #include @@ -202,4 +202,4 @@ class AutocompleteResult { DISALLOW_COPY_AND_ASSIGN(AutocompleteResult); }; -#endif // CHROME_BROWSER_AUTOCOMPLETE_AUTOCOMPLETE_RESULT_H_ +#endif // COMPONENTS_OMNIBOX_AUTOCOMPLETE_RESULT_H_ diff --git a/chrome/browser/autocomplete/autocomplete_result_unittest.cc b/components/omnibox/autocomplete_result_unittest.cc similarity index 90% rename from chrome/browser/autocomplete/autocomplete_result_unittest.cc rename to components/omnibox/autocomplete_result_unittest.cc index 80a9dfb6aa9bf..d80f7e645fd8c 100644 --- a/chrome/browser/autocomplete/autocomplete_result_unittest.cc +++ b/components/omnibox/autocomplete_result_unittest.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 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. -#include "chrome/browser/autocomplete/autocomplete_result.h" +#include "components/omnibox/autocomplete_result.h" #include @@ -11,15 +11,13 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h" -#include "chrome/browser/omnibox/omnibox_field_trial.h" -#include "chrome/browser/search_engines/template_url_service_test_util.h" -#include "chrome/test/base/testing_profile.h" #include "components/metrics/proto/omnibox_event.pb.h" #include "components/omnibox/autocomplete_input.h" #include "components/omnibox/autocomplete_match.h" #include "components/omnibox/autocomplete_match_type.h" #include "components/omnibox/autocomplete_provider.h" +#include "components/omnibox/omnibox_field_trial.h" +#include "components/omnibox/test_scheme_classifier.h" #include "components/search_engines/template_url_prepopulate_data.h" #include "components/search_engines/template_url_service.h" #include "components/variations/entropy_provider.h" @@ -96,7 +94,8 @@ class AutocompleteResultTest : public testing::Test { TemplateURLPrepopulateData::InitCountryCode( std::string() /* unknown country code */); #endif - test_util_.VerifyLoad(); + template_url_service_.reset(new TemplateURLService(NULL, 0)); + template_url_service_->Load(); } // Configures |match| from |data|. @@ -120,7 +119,7 @@ class AutocompleteResultTest : public testing::Test { const TestData* expected, size_t expected_size); protected: - TemplateURLServiceTestUtil test_util_; + scoped_ptr template_url_service_; private: scoped_ptr field_trial_list_; @@ -177,21 +176,21 @@ void AutocompleteResultTest::RunCopyOldMatchesTest( base::string16(), GURL(), OmniboxEventProto::INVALID_SPEC, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); + TestSchemeClassifier()); ACMatches last_matches; PopulateAutocompleteMatches(last, last_size, &last_matches); AutocompleteResult last_result; last_result.AppendMatches(last_matches); - last_result.SortAndCull(input, test_util_.model()); + last_result.SortAndCull(input, template_url_service_.get()); ACMatches current_matches; PopulateAutocompleteMatches(current, current_size, ¤t_matches); AutocompleteResult current_result; current_result.AppendMatches(current_matches); - current_result.SortAndCull(input, test_util_.model()); - current_result.CopyOldMatches(input, last_result, test_util_.model()); + current_result.SortAndCull(input, template_url_service_.get()); + current_result.CopyOldMatches( + input, last_result, template_url_service_.get()); AssertResultMatches(current_result, expected, expected_size); } @@ -214,11 +213,10 @@ TEST_F(AutocompleteResultTest, Swap) { AutocompleteInput input(base::ASCIIToUTF16("a"), base::string16::npos, base::string16(), GURL(), OmniboxEventProto::INVALID_SPEC, false, false, false, - true, ChromeAutocompleteSchemeClassifier( - test_util_.profile())); + true, TestSchemeClassifier()); matches.push_back(match); r1.AppendMatches(matches); - r1.SortAndCull(input, test_util_.model()); + r1.SortAndCull(input, template_url_service_.get()); EXPECT_EQ(r1.begin(), r1.default_match()); EXPECT_EQ("http://a/", r1.alternate_nav_url().spec()); r1.Swap(&r2); @@ -299,9 +297,8 @@ TEST_F(AutocompleteResultTest, SortAndCullEmptyDestinationURLs) { base::string16(), GURL(), OmniboxEventProto::INVALID_SPEC, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); // Of the two results with the same non-empty destination URL, the // lower-relevance one should be dropped. All of the results with empty URLs @@ -323,7 +320,7 @@ TEST_F(AutocompleteResultTest, SortAndCullDuplicateSearchURLs) { url_data.short_name = base::ASCIIToUTF16("unittest"); url_data.SetKeyword(base::ASCIIToUTF16("foo")); url_data.SetURL("http://www.foo.com/s?q={searchTerms}"); - test_util_.model()->Add(new TemplateURL(url_data)); + template_url_service_.get()->Add(new TemplateURL(url_data)); TestData data[] = { { 0, 0, 1300 }, @@ -347,9 +344,8 @@ TEST_F(AutocompleteResultTest, SortAndCullDuplicateSearchURLs) { base::string16(), GURL(), OmniboxEventProto::INVALID_SPEC, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); // We expect the 3rd and 4th results to be removed. ASSERT_EQ(3U, result.size()); @@ -370,7 +366,7 @@ TEST_F(AutocompleteResultTest, SortAndCullWithMatchDups) { url_data.short_name = base::ASCIIToUTF16("unittest"); url_data.SetKeyword(base::ASCIIToUTF16("foo")); url_data.SetURL("http://www.foo.com/s?q={searchTerms}"); - test_util_.model()->Add(new TemplateURL(url_data)); + template_url_service_.get()->Add(new TemplateURL(url_data)); AutocompleteMatch dup_match; dup_match.destination_url = GURL("http://www.foo.com/s?q=foo&oq=dup"); @@ -401,9 +397,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithMatchDups) { base::string16(), GURL(), OmniboxEventProto::INVALID_SPEC, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); // Expect 3 unique results after SortAndCull(). ASSERT_EQ(3U, result.size()); @@ -457,9 +452,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDemotionsByType) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); // Check the new ordering. The history-title results should be omitted. // We cannot check relevance scores because the matches are sorted by @@ -504,8 +498,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithMatchDupsAndDemotionsByType) { base::string16(), base::string16::npos, base::string16(), GURL(), OmniboxEventProto::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS, false, false, false, true, - ChromeAutocompleteSchemeClassifier(test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); // The NAVSUGGEST dup-url stay above search-url since the navsuggest // variant should not be demoted. @@ -543,9 +537,8 @@ TEST_F(AutocompleteResultTest, SortAndCullReorderForDefaultMatch) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); AssertResultMatches(result, data, 4); } @@ -561,9 +554,8 @@ TEST_F(AutocompleteResultTest, SortAndCullReorderForDefaultMatch) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); ASSERT_EQ(4U, result.size()); EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); @@ -594,9 +586,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); AssertResultMatches(result, data, 4); } @@ -621,9 +612,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); ASSERT_EQ(4U, result.size()); EXPECT_EQ("http://b/", result.match_at(0)->destination_url.spec()); EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); @@ -644,9 +634,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); AssertResultMatches(result, data, 4); } @@ -662,9 +651,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); ASSERT_EQ(4U, result.size()); EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); @@ -684,9 +672,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); ASSERT_EQ(4U, result.size()); EXPECT_EQ("http://c/", result.match_at(0)->destination_url.spec()); EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); @@ -711,9 +698,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); AssertResultMatches(result, data, 4); } @@ -734,9 +720,8 @@ TEST_F(AutocompleteResultTest, SortAndCullWithDisableInlining) { base::string16(), GURL(), OmniboxEventProto::HOME_PAGE, false, false, false, true, - ChromeAutocompleteSchemeClassifier( - test_util_.profile())); - result.SortAndCull(input, test_util_.model()); + TestSchemeClassifier()); + result.SortAndCull(input, template_url_service_.get()); ASSERT_EQ(4U, result.size()); EXPECT_EQ("http://b/", result.match_at(0)->destination_url.spec()); EXPECT_EQ("http://a/", result.match_at(1)->destination_url.spec()); diff --git a/chrome/browser/omnibox/omnibox_field_trial.cc b/components/omnibox/omnibox_field_trial.cc similarity index 98% rename from chrome/browser/omnibox/omnibox_field_trial.cc rename to components/omnibox/omnibox_field_trial.cc index bef5a8445af2e..943b5dca52f62 100644 --- a/chrome/browser/omnibox/omnibox_field_trial.cc +++ b/components/omnibox/omnibox_field_trial.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 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. -#include "chrome/browser/omnibox/omnibox_field_trial.h" +#include "components/omnibox/omnibox_field_trial.h" #include #include @@ -14,9 +14,8 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/time/time.h" -#include "chrome/common/chrome_switches.h" -#include "chrome/common/variations/variation_ids.h" #include "components/metrics/proto/omnibox_event.pb.h" +#include "components/omnibox/omnibox_switches.h" #include "components/search/search.h" #include "components/variations/active_field_trials.h" #include "components/variations/metrics_util.h" diff --git a/chrome/browser/omnibox/omnibox_field_trial.h b/components/omnibox/omnibox_field_trial.h similarity index 98% rename from chrome/browser/omnibox/omnibox_field_trial.h rename to components/omnibox/omnibox_field_trial.h index 0dc26a1142df4..cc4440d66ccaa 100644 --- a/chrome/browser/omnibox/omnibox_field_trial.h +++ b/components/omnibox/omnibox_field_trial.h @@ -1,9 +1,9 @@ -// Copyright (c) 2012 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. -#ifndef CHROME_BROWSER_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ -#define CHROME_BROWSER_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ +#ifndef COMPONENTS_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ +#define COMPONENTS_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ #include #include @@ -344,4 +344,4 @@ class OmniboxFieldTrial { DISALLOW_IMPLICIT_CONSTRUCTORS(OmniboxFieldTrial); }; -#endif // CHROME_BROWSER_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ +#endif // COMPONENTS_OMNIBOX_OMNIBOX_FIELD_TRIAL_H_ diff --git a/chrome/browser/omnibox/omnibox_field_trial_unittest.cc b/components/omnibox/omnibox_field_trial_unittest.cc similarity index 98% rename from chrome/browser/omnibox/omnibox_field_trial_unittest.cc rename to components/omnibox/omnibox_field_trial_unittest.cc index fb6bcd6ed05ba..9a78a67b014ad 100644 --- a/chrome/browser/omnibox/omnibox_field_trial_unittest.cc +++ b/components/omnibox/omnibox_field_trial_unittest.cc @@ -1,15 +1,14 @@ -// Copyright (c) 2013 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. -#include "chrome/browser/omnibox/omnibox_field_trial.h" +#include "components/omnibox/omnibox_field_trial.h" #include "base/basictypes.h" #include "base/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/metrics/field_trial.h" #include "base/strings/string16.h" -#include "chrome/common/chrome_switches.h" #include "components/metrics/proto/omnibox_event.pb.h" #include "components/search/search.h" #include "components/variations/entropy_provider.h" diff --git a/components/omnibox/omnibox_switches.cc b/components/omnibox/omnibox_switches.cc new file mode 100644 index 0000000000000..ce498be7f4e3e --- /dev/null +++ b/components/omnibox/omnibox_switches.cc @@ -0,0 +1,15 @@ +// 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/omnibox/omnibox_switches.h" + +namespace switches { + +// Disables the experimental Answers in Suggest feature. +const char kDisableAnswersInSuggest[] = "disable-answers-in-suggest"; + +// Enables the experimental Answers in Suggest feature. +const char kEnableAnswersInSuggest[] = "enable-answers-in-suggest"; + +} // namespace switches diff --git a/components/omnibox/omnibox_switches.h b/components/omnibox/omnibox_switches.h new file mode 100644 index 0000000000000..7cd73e2c10e6d --- /dev/null +++ b/components/omnibox/omnibox_switches.h @@ -0,0 +1,15 @@ +// 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_OMNIBOX_OMNIBOX_SWITCHES_H_ +#define COMPONENTS_OMNIBOX_OMNIBOX_SWITCHES_H_ + +namespace switches { + +extern const char kDisableAnswersInSuggest[]; +extern const char kEnableAnswersInSuggest[]; + +} // namespace switches + +#endif // COMPONENTS_OMNIBOX_OMNIBOX_SWITCHES_H_ diff --git a/components/onc/onc_constants.cc b/components/onc/onc_constants.cc index f7e932bcc2ed5..1c1dccdaca42c 100644 --- a/components/onc/onc_constants.cc +++ b/components/onc/onc_constants.cc @@ -75,8 +75,8 @@ const char kWireless[] = "Wireless"; } // namespace network_type namespace cellular { -const char kActivateOverNonCellularNetwork[] = "ActivateOverNonCellularNetwork"; const char kActivationState[] = "ActivationState"; +const char kActivationType[] = "ActivationType"; const char kAllowRoaming[] = "AllowRoaming"; const char kAPN[] = "APN"; const char kAPNList[] = "APNList"; diff --git a/components/onc/onc_constants.h b/components/onc/onc_constants.h index ca85a76d9bdf3..5db4c920954b2 100644 --- a/components/onc/onc_constants.h +++ b/components/onc/onc_constants.h @@ -92,8 +92,8 @@ ONC_EXPORT extern const char kWireless[]; } // namespace network_type namespace cellular { -ONC_EXPORT extern const char kActivateOverNonCellularNetwork[]; ONC_EXPORT extern const char kActivationState[]; +ONC_EXPORT extern const char kActivationType[]; ONC_EXPORT extern const char kAllowRoaming[]; ONC_EXPORT extern const char kAPN[]; ONC_EXPORT extern const char kAPNList[]; diff --git a/components/pairing.gypi b/components/pairing.gypi index b6c00ff99248e..4dd03d1ea237c 100644 --- a/components/pairing.gypi +++ b/components/pairing.gypi @@ -14,16 +14,21 @@ 'pairing_api_proto', '../base/base.gyp:base', '../device/bluetooth/bluetooth.gyp:device_bluetooth', + '../net/net.gyp:net', ], 'sources': [ + 'pairing/controller_pairing_controller.cc', + 'pairing/controller_pairing_controller.h', 'pairing/fake_controller_pairing_controller.cc', 'pairing/fake_controller_pairing_controller.h', 'pairing/fake_host_pairing_controller.cc', 'pairing/fake_host_pairing_controller.h', - 'pairing/controller_pairing_controller.cc', - 'pairing/controller_pairing_controller.h', 'pairing/host_pairing_controller.cc', 'pairing/host_pairing_controller.h', + 'pairing/message_buffer.cc', + 'pairing/message_buffer.h', + 'pairing/proto_decoder.cc', + 'pairing/proto_decoder.h', ], }, { diff --git a/components/pairing/BUILD.gn b/components/pairing/BUILD.gn index af7b9793203f2..d017ec44ce405 100644 --- a/components/pairing/BUILD.gn +++ b/components/pairing/BUILD.gn @@ -14,11 +14,27 @@ source_set("pairing") { "pairing/controller_pairing_controller.h", "pairing/host_pairing_controller.cc", "pairing/host_pairing_controller.h", + "pairing/message_buffer.cc", + "pairing/message_buffer.h", + "pairing/proto_decoder.cc", + "pairing/proto_decoder.h", ] deps = [ "//base", "//device/bluetooth", + "//net", + ] +} + +source_set("unit_tests") { + sources = [ + "message_buffer_unittest.cc", + ] + + deps = [ + ":pairing", + "//testing/gtest", ] } diff --git a/components/pairing/message_buffer.cc b/components/pairing/message_buffer.cc new file mode 100644 index 0000000000000..3cfe95ddac2a7 --- /dev/null +++ b/components/pairing/message_buffer.cc @@ -0,0 +1,56 @@ +// 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/pairing/message_buffer.h" + +#include "net/base/io_buffer.h" + +namespace pairing_chromeos { + +MessageBuffer::MessageBuffer() + : buffer_offset_(0), + total_buffer_size_(0) { +} + +MessageBuffer::~MessageBuffer() {} + +int MessageBuffer::AvailableBytes() { + return total_buffer_size_ - buffer_offset_; +} + +void MessageBuffer::ReadBytes(char* buffer, int size) { + CHECK_LE(size, AvailableBytes()); + + int offset = 0; + while (offset < size) { + scoped_refptr io_buffer = pending_data_.front().first; + int io_buffer_size = pending_data_.front().second; + + CHECK_GT(io_buffer_size, buffer_offset_); + int copy_size = std::min(size - offset, io_buffer_size - buffer_offset_); + memcpy(&buffer[offset], &io_buffer->data()[buffer_offset_], copy_size); + + offset += copy_size; + buffer_offset_ += copy_size; + + CHECK_LE(buffer_offset_, io_buffer_size); + if (buffer_offset_ == io_buffer_size) { + CHECK_GE(total_buffer_size_, io_buffer_size); + total_buffer_size_ -= io_buffer_size; + pending_data_.pop_front(); + buffer_offset_ = 0; + } + } + CHECK_EQ(offset, size); +} + +void MessageBuffer::AddIOBuffer(scoped_refptr io_buffer, + int size) { + if (size == 0) + return; + pending_data_.push_back(std::make_pair(io_buffer, size)); + total_buffer_size_ += size; +} + +} // namespace pairing_chromeos diff --git a/components/pairing/message_buffer.h b/components/pairing/message_buffer.h new file mode 100644 index 0000000000000..8b2fcc0186dbc --- /dev/null +++ b/components/pairing/message_buffer.h @@ -0,0 +1,52 @@ +// 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_PAIRING_MESSAGE_BUFFER_H_ +#define COMPONENTS_PAIRING_MESSAGE_BUFFER_H_ + +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace net { +class IOBuffer; +} + +namespace pairing_chromeos { + +// A MessageBuffer is a simple wrapper around an ordered set of buffers received +// from a socket. It keeps track of the amount of unread data, and allows data +// to be retreived that was split across buffers in a single chunk. +class MessageBuffer { + public: + MessageBuffer(); + ~MessageBuffer(); + + // Returns the number of bytes currently received, but not yet read. + int AvailableBytes(); + + // Read |size| bytes into |buffer|. |size| must be smaller than or equal to + // the current number of available bytes. + void ReadBytes(char* buffer, int size); + + // Add the data from an IOBuffer. A reference to the IOBuffer will be + // maintained until it is drained. + void AddIOBuffer(scoped_refptr io_buffer, int size); + + private: + // Offset of the next byte to be read from the current IOBuffer. + int buffer_offset_; + // Total number of bytes in IOBuffers in |pending_data_|, including bytes that + // have already been read. + int total_buffer_size_; + std::deque, int> > pending_data_; + + DISALLOW_COPY_AND_ASSIGN(MessageBuffer); +}; + +} // namespace pairing_chromeos + +#endif // COMPONENTS_PAIRING_MESSAGE_BUFFER_H_ diff --git a/components/pairing/message_buffer_unittest.cc b/components/pairing/message_buffer_unittest.cc new file mode 100644 index 0000000000000..2f9fe40721fb5 --- /dev/null +++ b/components/pairing/message_buffer_unittest.cc @@ -0,0 +1,84 @@ +// 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/pairing/message_buffer.h" + +#include "net/base/io_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace pairing_chromeos { + +typedef testing::Test MessageBufferTest; + +TEST_F(MessageBufferTest, BasicReadWrite) { + MessageBuffer message_buffer; + scoped_refptr io_buffer(new net::IOBuffer(3)); + io_buffer->data()[0] = 3; + io_buffer->data()[1] = 1; + io_buffer->data()[2] = 4; + + message_buffer.AddIOBuffer(io_buffer, 3); + + EXPECT_EQ(message_buffer.AvailableBytes(), 3); + char data = 0; + message_buffer.ReadBytes(&data, 1); + EXPECT_EQ(data, 3); + EXPECT_EQ(message_buffer.AvailableBytes(), 2); + message_buffer.ReadBytes(&data, 1); + EXPECT_EQ(data, 1); + EXPECT_EQ(message_buffer.AvailableBytes(), 1); + message_buffer.ReadBytes(&data, 1); + EXPECT_EQ(data, 4); + EXPECT_EQ(message_buffer.AvailableBytes(), 0); +} + +TEST_F(MessageBufferTest, SplitBuffer) { + MessageBuffer message_buffer; + scoped_refptr io_buffer0(new net::IOBuffer(1)); + io_buffer0->data()[0] = 3; + + scoped_refptr io_buffer1(new net::IOBuffer(2)); + io_buffer1->data()[0] = 1; + io_buffer1->data()[1] = 4; + + message_buffer.AddIOBuffer(io_buffer0, 1); + message_buffer.AddIOBuffer(io_buffer1, 2); + + EXPECT_EQ(message_buffer.AvailableBytes(), 3); + char data[3]; + message_buffer.ReadBytes(data, 3); + EXPECT_EQ(message_buffer.AvailableBytes(), 0); + EXPECT_EQ(data[0], 3); + EXPECT_EQ(data[1], 1); + EXPECT_EQ(data[2], 4); +} + +TEST_F(MessageBufferTest, EmptyBuffer) { + MessageBuffer message_buffer; + scoped_refptr io_buffer0(new net::IOBuffer(1)); + io_buffer0->data()[0] = 3; + + scoped_refptr io_buffer1(new net::IOBuffer(0)); + scoped_refptr io_buffer2(new net::IOBuffer(2)); + io_buffer2->data()[0] = 1; + io_buffer2->data()[1] = 4; + + message_buffer.AddIOBuffer(io_buffer0, 1); + message_buffer.AddIOBuffer(io_buffer1, 0); + message_buffer.AddIOBuffer(io_buffer2, 2); + + EXPECT_EQ(message_buffer.AvailableBytes(), 3); + char data = 0; + message_buffer.ReadBytes(&data, 1); + EXPECT_EQ(data, 3); + EXPECT_EQ(message_buffer.AvailableBytes(), 2); + message_buffer.ReadBytes(&data, 1); + EXPECT_EQ(data, 1); + EXPECT_EQ(message_buffer.AvailableBytes(), 1); + message_buffer.ReadBytes(&data, 1); + EXPECT_EQ(data, 4); + EXPECT_EQ(message_buffer.AvailableBytes(), 0); +} + +} // namespace pairing_chromeos diff --git a/components/pairing/proto_decoder.cc b/components/pairing/proto_decoder.cc new file mode 100644 index 0000000000000..96b3c24feb7a8 --- /dev/null +++ b/components/pairing/proto_decoder.cc @@ -0,0 +1,201 @@ +// 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/pairing/proto_decoder.h" + +#include "components/pairing/pairing_api.pb.h" +#include "net/base/io_buffer.h" + +namespace { +enum { + MESSAGE_NONE, + MESSAGE_HOST_STATUS, + MESSAGE_CONFIGURE_HOST, + MESSAGE_PAIR_DEVICES, + MESSAGE_COMPLETE_SETUP, + MESSAGE_ERROR, + NUM_MESSAGES, +}; +} + +namespace pairing_chromeos { + +ProtoDecoder::ProtoDecoder(Observer* observer) + : observer_(observer), + next_message_type_(MESSAGE_NONE), + next_message_size_(0) { + DCHECK(observer_); +} + +ProtoDecoder::~ProtoDecoder() {} + +bool ProtoDecoder::DecodeIOBuffer(int size, + ProtoDecoder::IOBufferRefPtr io_buffer) { + // Update the message buffer. + message_buffer_.AddIOBuffer(io_buffer, size); + + // If there is no current message, the next byte is the message type. + if (next_message_type_ == MESSAGE_NONE) { + if (message_buffer_.AvailableBytes() < static_cast(sizeof(uint8_t))) + return true; + + uint8_t message_type = MESSAGE_NONE; + message_buffer_.ReadBytes(reinterpret_cast(&message_type), + sizeof(message_type)); + + if (message_type == MESSAGE_NONE || message_type >= NUM_MESSAGES) { + LOG(ERROR) << "Unknown message type received: " << message_type; + return false; + } + next_message_type_ = message_type; + } + + // If the message size isn't set, the next two bytes are the message size. + if (next_message_size_ == 0) { + if (message_buffer_.AvailableBytes() < static_cast(sizeof(uint16_t))) + return true; + + // The size is sent in network byte order. + uint8_t high_byte = 0; + message_buffer_.ReadBytes(reinterpret_cast(&high_byte), + sizeof(high_byte)); + uint8_t low_byte = 0; + message_buffer_.ReadBytes(reinterpret_cast(&low_byte), + sizeof(low_byte)); + + next_message_size_ = (high_byte << 8) + low_byte; + } + + // If the whole proto buffer is not yet available, return early. + if (message_buffer_.AvailableBytes() < next_message_size_) + return true; + + std::vector buffer(next_message_size_); + message_buffer_.ReadBytes(&buffer[0], next_message_size_); + + switch (next_message_type_) { + case MESSAGE_HOST_STATUS: { + pairing_api::HostStatus message; + message.ParseFromArray(&buffer[0], buffer.size()); + observer_->OnHostStatusMessage(message); + } + break; + case MESSAGE_CONFIGURE_HOST: { + pairing_api::ConfigureHost message; + message.ParseFromArray(&buffer[0], buffer.size()); + observer_->OnConfigureHostMessage(message); + } + break; + case MESSAGE_PAIR_DEVICES: { + pairing_api::PairDevices message; + message.ParseFromArray(&buffer[0], buffer.size()); + observer_->OnPairDevicesMessage(message); + } + break; + case MESSAGE_COMPLETE_SETUP: { + pairing_api::CompleteSetup message; + message.ParseFromArray(&buffer[0], buffer.size()); + observer_->OnCompleteSetupMessage(message); + } + break; + case MESSAGE_ERROR: { + pairing_api::Error message; + message.ParseFromArray(&buffer[0], buffer.size()); + observer_->OnErrorMessage(message); + } + break; + + default: + NOTREACHED(); + break; + } + + // Reset the message data. + next_message_type_ = MESSAGE_NONE; + next_message_size_ = 0; + + return true; +} + +ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendHostStatus( + const pairing_api::HostStatus& message, int* size) { + std::string serialized_proto; + if (!message.SerializeToString(&serialized_proto)) { + NOTREACHED(); + } + + return SendMessage(MESSAGE_HOST_STATUS, serialized_proto, size); +} + +ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendConfigureHost( + const pairing_api::ConfigureHost& message, int* size) { + std::string serialized_proto; + if (!message.SerializeToString(&serialized_proto)) { + NOTREACHED(); + } + + return SendMessage(MESSAGE_CONFIGURE_HOST, serialized_proto, size); +} + +ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendPairDevices( + const pairing_api::PairDevices& message, int* size) { + std::string serialized_proto; + if (!message.SerializeToString(&serialized_proto)) { + NOTREACHED(); + } + + return SendMessage(MESSAGE_PAIR_DEVICES, serialized_proto, size); +} + +ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendCompleteSetup( + const pairing_api::CompleteSetup& message, int* size) { + std::string serialized_proto; + if (!message.SerializeToString(&serialized_proto)) { + NOTREACHED(); + } + + return SendMessage(MESSAGE_COMPLETE_SETUP, serialized_proto, size); +} + +ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendError( + const pairing_api::Error& message, int* size) { + std::string serialized_proto; + if (!message.SerializeToString(&serialized_proto)) { + NOTREACHED(); + } + + return SendMessage(MESSAGE_ERROR, serialized_proto, size); +} + +ProtoDecoder::IOBufferRefPtr ProtoDecoder::SendMessage( + uint8_t message_type, + const std::string& message, + int* size) { + uint16_t message_size = message.size(); + + *size = sizeof(message_type) + sizeof(message_size) + message.size(); + IOBufferRefPtr io_buffer(new net::IOBuffer(*size)); + + // Write the message type. + int offset = 0; + memcpy(&io_buffer->data()[offset], &message_type, sizeof(message_type)); + offset += sizeof(message_type); + + // Network byte order. + // Write the high byte of the size. + uint8_t data = (message_size >> 8) & 0xFF; + memcpy(&io_buffer->data()[offset], &data, sizeof(data)); + offset += sizeof(data); + // Write the low byte of the size. + data = message_size & 0xFF; + memcpy(&io_buffer->data()[offset], &data, sizeof(data)); + offset += sizeof(data); + + // Write the actual message. + memcpy(&io_buffer->data()[offset], message.data(), message.size()); + + return io_buffer; +} + +} // namespace pairing_chromeos diff --git a/components/pairing/proto_decoder.h b/components/pairing/proto_decoder.h new file mode 100644 index 0000000000000..e36eb7fce0613 --- /dev/null +++ b/components/pairing/proto_decoder.h @@ -0,0 +1,91 @@ +// 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_PAIRING_PROTO_DECODER_H_ +#define COMPONENTS_PAIRING_PROTO_DECODER_H_ + +#include + +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "components/pairing/message_buffer.h" + +namespace net { +class IOBuffer; +} + +namespace pairing_api { +class CompleteSetup; +class ConfigureHost; +class Error; +class HostStatus; +class PairDevices; +} // namespace pairing_api + +namespace pairing_chromeos { + +// A ProtoDecoder collects data from a series of IOBuffers and decodes Proto +// buffers from the data. The decoded messages are then forwarded to an +// observer. +class ProtoDecoder { + public: + typedef scoped_refptr IOBufferRefPtr; + class Observer { + public: + virtual ~Observer() {} + + virtual void OnHostStatusMessage( + const pairing_api::HostStatus& message) = 0; + virtual void OnConfigureHostMessage( + const pairing_api::ConfigureHost& message) = 0; + virtual void OnPairDevicesMessage( + const pairing_api::PairDevices& message) = 0; + virtual void OnCompleteSetupMessage( + const pairing_api::CompleteSetup& message) = 0; + virtual void OnErrorMessage( + const pairing_api::Error& message) = 0; + + protected: + Observer() {} + + private: + DISALLOW_COPY_AND_ASSIGN(Observer); + }; + + explicit ProtoDecoder(Observer* observer); + ~ProtoDecoder(); + + // Decodes the data from an io_buffer, and sends out events for any complete + // messages. + bool DecodeIOBuffer(int size, IOBufferRefPtr io_buffer); + + // Convenience functions for serializing messages into an IOBuffer. + static IOBufferRefPtr SendHostStatus(const pairing_api::HostStatus& message, + int* size); + static IOBufferRefPtr SendConfigureHost( + const pairing_api::ConfigureHost& message, int* size); + static IOBufferRefPtr SendPairDevices(const pairing_api::PairDevices& message, + int* size); + static IOBufferRefPtr SendCompleteSetup( + const pairing_api::CompleteSetup& message, int* size); + static IOBufferRefPtr SendError(const pairing_api::Error& message, int* size); + + private: + static IOBufferRefPtr SendMessage(uint8_t message_type, + const std::string& message, + int* size); + + Observer* observer_; + MessageBuffer message_buffer_; + int next_message_type_; + int next_message_size_; + + DISALLOW_COPY_AND_ASSIGN(ProtoDecoder); +}; + +} // namespace pairing_chromeos + +#endif // COMPONENTS_PAIRING_PROTO_DECODER_H_ diff --git a/components/password_manager/core/browser/password_form_manager.cc b/components/password_manager/core/browser/password_form_manager.cc index 1248b11787ab7..dfe94a3533c2f 100644 --- a/components/password_manager/core/browser/password_form_manager.cc +++ b/components/password_manager/core/browser/password_form_manager.cc @@ -379,7 +379,7 @@ void PasswordFormManager::OnRequestDone( // These credentials will be in the final result regardless of score. std::vector credentials_to_keep; for (size_t i = 0; i < logins_result.size(); i++) { - if (IgnoreResult(*logins_result[i])) { + if (ShouldIgnoreResult(*logins_result[i])) { delete logins_result[i]; continue; } @@ -439,6 +439,8 @@ void PasswordFormManager::OnRequestDone( // We're done matching now. state_ = POST_MATCHING_PHASE; + client_->AutofillResultsComputed(); + if (best_score <= 0) { return; } @@ -507,7 +509,7 @@ void PasswordFormManager::OnGetPasswordStoreResults( OnRequestDone(results); } -bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { +bool PasswordFormManager::ShouldIgnoreResult(const PasswordForm& form) const { // Do not autofill on sign-up or change password forms (until we have some // working change password functionality). if (!observed_form_.new_password_element.empty()) @@ -515,6 +517,10 @@ bool PasswordFormManager::IgnoreResult(const PasswordForm& form) const { // Don't match an invalid SSL form with one saved under secure circumstances. if (form.ssl_valid && !observed_form_.ssl_valid) return true; + + if (client_->ShouldFilterAutofillResult(form)) + return true; + return false; } @@ -534,6 +540,13 @@ void PasswordFormManager::SaveAsNewLogin(bool reset_preferred_login) { return; } + // Upload credentials the first time they are saved. This data is used + // by password generation to help determine account creation sites. + // Blacklisted credentials will never be used, so don't upload a vote for + // them. + if (!pending_credentials_.blacklisted_by_user) + UploadPasswordForm(pending_credentials_.form_data, autofill::PASSWORD); + pending_credentials_.date_created = Time::Now(); SanitizePossibleUsernames(&pending_credentials_); password_store->AddLogin(pending_credentials_); @@ -694,19 +707,28 @@ void PasswordFormManager::CheckForAccountCreationForm( if (!pending.form_data.fields.empty() && pending_structure.FormSignature() != observed_structure.FormSignature()) { - autofill::AutofillManager* autofill_manager = - driver_->GetAutofillManager(); - if (autofill_manager) { - // Note that this doesn't guarantee that the upload succeeded, only that - // |pending.form_data| is considered uploadable. - bool success = - autofill_manager->UploadPasswordGenerationForm(pending.form_data); - UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success); - } + UploadPasswordForm(pending.form_data, + autofill::ACCOUNT_CREATION_PASSWORD); } } } +void PasswordFormManager::UploadPasswordForm( + const autofill::FormData& form_data, + const autofill::ServerFieldType& password_type) { + autofill::AutofillManager* autofill_manager = + driver_->GetAutofillManager(); + if (!autofill_manager) + return; + + // Note that this doesn't guarantee that the upload succeeded, only that + // |form_data| is considered uploadable. + bool success = + autofill_manager->UploadPasswordForm( + form_data, autofill::ACCOUNT_CREATION_PASSWORD); + UMA_HISTOGRAM_BOOLEAN("PasswordGeneration.UploadStarted", success); +} + int PasswordFormManager::ScoreResult(const PasswordForm& candidate) const { DCHECK_EQ(state_, MATCHING_PHASE); // For scoring of candidate login data: diff --git a/components/password_manager/core/browser/password_form_manager.h b/components/password_manager/core/browser/password_form_manager.h index 89c6933f4da20..a4fa82ed531e9 100644 --- a/components/password_manager/core/browser/password_form_manager.h +++ b/components/password_manager/core/browser/password_form_manager.h @@ -11,6 +11,7 @@ #include "build/build_config.h" #include "base/stl_util.h" +#include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/common/password_form.h" #include "components/password_manager/core/browser/password_manager_driver.h" #include "components/password_manager/core/browser/password_store.h" @@ -219,7 +220,7 @@ class PasswordFormManager : public PasswordStoreConsumer { // Helper for OnGetPasswordStoreResults to determine whether or not // the given result form is worth scoring. - bool IgnoreResult(const autofill::PasswordForm& form) const; + bool ShouldIgnoreResult(const autofill::PasswordForm& form) const; // Helper for Save in the case that best_matches.size() == 0, meaning // we have no prior record of this form/username/password and the user @@ -263,6 +264,11 @@ class PasswordFormManager : public PasswordStoreConsumer { // duplicates. void SanitizePossibleUsernames(autofill::PasswordForm* form); + // Helper function to delegate uploading to the AutofillManager. + virtual void UploadPasswordForm( + const autofill::FormData& form_data, + const autofill::ServerFieldType& password_type); + // Set of PasswordForms from the DB that best match the form // being managed by this. Use a map instead of vector, because we most // frequently require lookups by username value in IsNewLogin. diff --git a/components/password_manager/core/browser/password_form_manager_unittest.cc b/components/password_manager/core/browser/password_form_manager_unittest.cc index a3399cd47ae73..a0a3c4b2e35cb 100644 --- a/components/password_manager/core/browser/password_form_manager_unittest.cc +++ b/components/password_manager/core/browser/password_form_manager_unittest.cc @@ -60,6 +60,13 @@ class TestPasswordManagerClient : public StubPasswordManagerClient { true); } + virtual bool ShouldFilterAutofillResult( + const autofill::PasswordForm& form) OVERRIDE { + if (form == form_to_filter_) + return true; + return false; + } + virtual void PromptUserToSavePassword( scoped_ptr form_to_save) OVERRIDE {} virtual PrefService* GetPrefs() OVERRIDE { return &prefs_; } @@ -70,12 +77,18 @@ class TestPasswordManagerClient : public StubPasswordManagerClient { driver_.FillPasswordForm(*fill_data.get()); } + void SetFormToFilter(const autofill::PasswordForm& form) { + form_to_filter_ = form; + } + MockPasswordManagerDriver* GetMockDriver() { return &driver_; } private: + autofill::PasswordForm form_to_filter_; + TestingPrefServiceSimple prefs_; PasswordStore* password_store_; - MockPasswordManagerDriver driver_; + testing::NiceMock driver_; }; class TestPasswordManager : public PasswordManager { @@ -99,6 +112,20 @@ class TestPasswordManager : public PasswordManager { mutable autofill::PasswordFormMap best_matches_; }; +class MockPasswordFormManager : public PasswordFormManager { + public: + MockPasswordFormManager(PasswordManager* manager, + PasswordManagerClient* client, + PasswordManagerDriver* driver, + const autofill::PasswordForm& observed_form, + bool ssl_valid) + : PasswordFormManager(manager, client, driver, observed_form, ssl_valid) + {} + + MOCK_METHOD2(UploadPasswordForm, void(const autofill::FormData&, + const autofill::ServerFieldType&)); +}; + } // namespace class PasswordFormManagerTest : public testing::Test { @@ -133,9 +160,9 @@ class PasswordFormManagerTest : public testing::Test { } void InitializeMockStore() { - if (!mock_store_) { - mock_store_ = new MockPasswordStore(); - ASSERT_TRUE(mock_store_); + if (!mock_store_.get()) { + mock_store_ = new testing::NiceMock(); + ASSERT_TRUE(mock_store_.get()); } } @@ -177,7 +204,7 @@ class PasswordFormManagerTest : public testing::Test { } bool IgnoredResult(PasswordFormManager* p, PasswordForm* form) { - return p->IgnoreResult(*form); + return p->ShouldIgnoreResult(*form); } PasswordForm* observed_form() { return &observed_form_; } @@ -194,7 +221,7 @@ class PasswordFormManagerTest : public testing::Test { private: PasswordForm observed_form_; PasswordForm saved_match_; - scoped_refptr mock_store_; + scoped_refptr > mock_store_; TestPasswordManagerClient client_; }; @@ -427,6 +454,10 @@ TEST_F(PasswordFormManagerTest, TestIgnoreResult) { saved_match()->action = GURL("http://www.google.com/b/Login"); saved_match()->origin = GURL("http://www.google.com/foo"); EXPECT_FALSE(IgnoredResult(&manager, saved_match())); + + // Results should be ignored if the client requests it. + client()->SetFormToFilter(*saved_match()); + EXPECT_TRUE(IgnoredResult(&manager, saved_match())); } TEST_F(PasswordFormManagerTest, TestEmptyAction) { @@ -1071,4 +1102,123 @@ TEST_F(PasswordFormManagerTest, CorrectlyUpdatePasswordsWithSameUsername) { password_store->Shutdown(); } +TEST_F(PasswordFormManagerTest, UploadFormData_NewPassword) { + InitializeMockStore(); + TestPasswordManagerClient client_with_store(mock_store()); + TestPasswordManager password_manager(&client_with_store); + EXPECT_CALL(*client_with_store.GetMockDriver(), IsOffTheRecord()) + .WillRepeatedly(Return(false)); + + PasswordForm form(*observed_form()); + + autofill::FormFieldData field; + field.label = ASCIIToUTF16("full_name"); + field.name = ASCIIToUTF16("full_name"); + field.form_control_type = "text"; + form.form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("Email"); + field.name = ASCIIToUTF16("Email"); + field.form_control_type = "text"; + form.form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("password"); + field.name = ASCIIToUTF16("password"); + field.form_control_type = "password"; + form.form_data.fields.push_back(field); + + // For newly saved passwords, upload a vote for autofill::PASSWORD. + MockPasswordFormManager form_manager(&password_manager, + &client_with_store, + client_with_store.GetDriver(), + form, + false); + SimulateMatchingPhase(&form_manager, RESULT_NO_MATCH); + + PasswordForm form_to_save(form); + form_to_save.preferred = true; + form_to_save.username_value = ASCIIToUTF16("username"); + form_to_save.password_value = ASCIIToUTF16("1234"); + + EXPECT_CALL(form_manager, UploadPasswordForm(_, autofill::PASSWORD)).Times(1); + form_manager.ProvisionallySave( + form_to_save, + PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + form_manager.Save(); + Mock::VerifyAndClearExpectations(&form_manager); + + // Do not upload a vote if the user is blacklisting the form. + MockPasswordFormManager blacklist_form_manager(&password_manager, + &client_with_store, + client_with_store.GetDriver(), + form, + false); + SimulateMatchingPhase(&blacklist_form_manager, RESULT_NO_MATCH); + + EXPECT_CALL(blacklist_form_manager, + UploadPasswordForm(_, autofill::PASSWORD)).Times(0); + blacklist_form_manager.PermanentlyBlacklist(); + Mock::VerifyAndClearExpectations(&blacklist_form_manager); +} + +TEST_F(PasswordFormManagerTest, UploadFormData_AccountCreationPassword) { + InitializeMockStore(); + TestPasswordManagerClient client_with_store(mock_store()); + TestPasswordManager password_manager(&client_with_store); + EXPECT_CALL(*client_with_store.GetMockDriver(), IsOffTheRecord()) + .WillRepeatedly(Return(false)); + + PasswordForm form(*observed_form()); + + autofill::FormFieldData field; + field.label = ASCIIToUTF16("Email"); + field.name = ASCIIToUTF16("Email"); + field.form_control_type = "text"; + form.form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("password"); + field.name = ASCIIToUTF16("password"); + field.form_control_type = "password"; + form.form_data.fields.push_back(field); + + MockPasswordFormManager form_manager(&password_manager, + &client_with_store, + client_with_store.GetDriver(), + form, + false); + std::vector result; + result.push_back(CreateSavedMatch(false)); + + field.label = ASCIIToUTF16("full_name"); + field.name = ASCIIToUTF16("full_name"); + field.form_control_type = "text"; + result[0]->form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("Email"); + field.name = ASCIIToUTF16("Email"); + field.form_control_type = "text"; + result[0]->form_data.fields.push_back(field); + + field.label = ASCIIToUTF16("password"); + field.name = ASCIIToUTF16("password"); + field.form_control_type = "password"; + result[0]->form_data.fields.push_back(field); + + PasswordForm form_to_save(form); + form_to_save.preferred = true; + form_to_save.username_value = result[0]->username_value; + form_to_save.password_value = result[0]->password_value; + + SimulateFetchMatchingLoginsFromPasswordStore(&form_manager); + SimulateResponseFromPasswordStore(&form_manager, result); + + EXPECT_CALL(form_manager, + UploadPasswordForm(_, + autofill::ACCOUNT_CREATION_PASSWORD)).Times(1); + form_manager.ProvisionallySave( + form_to_save, + PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES); + form_manager.Save(); +} + } // namespace password_manager diff --git a/components/password_manager/core/browser/password_manager_client.h b/components/password_manager/core/browser/password_manager_client.h index 4607800d7357c..99f1282258cbf 100644 --- a/components/password_manager/core/browser/password_manager_client.h +++ b/components/password_manager/core/browser/password_manager_client.h @@ -35,11 +35,19 @@ class PasswordManagerClient { // always returns true. virtual bool IsPasswordManagerEnabledForCurrentPage() const; + // Return true if |form| should not be available for autofill. + virtual bool ShouldFilterAutofillResult( + const autofill::PasswordForm& form) = 0; + // Returns true if |username| and |origin| correspond to the account which is // syncing. virtual bool IsSyncAccountCredential( const std::string& username, const std::string& origin) const = 0; + // Called when all autofill results have been computed. Client can use + // this signal to report statistics. Default implementation is a noop. + virtual void AutofillResultsComputed() {} + // Informs the embedder of a password form that can be saved if the user // allows it. The embedder is not required to prompt the user if it decides // that this form doesn't need to be saved. diff --git a/components/password_manager/core/browser/password_store.cc b/components/password_manager/core/browser/password_store.cc index 9710bca729371..b5a2b734ae05f 100644 --- a/components/password_manager/core/browser/password_store.cc +++ b/components/password_manager/core/browser/password_store.cc @@ -226,6 +226,18 @@ void PasswordStore::LogStatsForBulkDeletion(int num_deletions) { num_deletions); } +void PasswordStore::NotifyLoginsChanged( + const PasswordStoreChangeList& changes) { + DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); + if (!changes.empty()) { + observers_->Notify(&Observer::OnLoginsChanged, changes); +#if defined(PASSWORD_MANAGER_ENABLE_SYNC) + if (syncable_service_) + syncable_service_->ActOnPasswordStoreChanges(changes); +#endif + } +} + template void PasswordStore::Schedule( BackendFunc func, @@ -242,18 +254,6 @@ void PasswordStore::WrapModificationTask(ModificationTask task) { NotifyLoginsChanged(changes); } -void PasswordStore::NotifyLoginsChanged( - const PasswordStoreChangeList& changes) { - DCHECK(GetBackgroundTaskRunner()->BelongsToCurrentThread()); - if (!changes.empty()) { - observers_->Notify(&Observer::OnLoginsChanged, changes); -#if defined(PASSWORD_MANAGER_ENABLE_SYNC) - if (syncable_service_) - syncable_service_->ActOnPasswordStoreChanges(changes); -#endif - } -} - #if defined(PASSWORD_MANAGER_ENABLE_SYNC) void PasswordStore::InitSyncableService( const syncer::SyncableService::StartSyncFlare& flare) { diff --git a/components/password_manager/core/browser/password_store.h b/components/password_manager/core/browser/password_store.h index b2549a3d89c53..64ec8e925d9f6 100644 --- a/components/password_manager/core/browser/password_store.h +++ b/components/password_manager/core/browser/password_store.h @@ -251,6 +251,13 @@ class PasswordStore : protected PasswordStoreSync, // Log UMA stats for number of bulk deletions. void LogStatsForBulkDeletion(int num_deletions); + // PasswordStoreSync: + // Called by WrapModificationTask() once the underlying data-modifying + // operation has been performed. Notifies observers that password store data + // may have been changed. + virtual void NotifyLoginsChanged( + const PasswordStoreChangeList& changes) OVERRIDE; + // TaskRunner for tasks that run on the main thread (usually the UI thread). scoped_refptr main_thread_runner_; @@ -258,8 +265,6 @@ class PasswordStore : protected PasswordStoreSync, // background tasks -- see |GetBackgroundTaskRunner|. scoped_refptr db_thread_runner_; - scoped_ptr syncable_service_; - private: // Schedule the given |func| to be run in the PasswordStore's own thread with // responses delivered to |consumer| on the current thread. @@ -273,13 +278,6 @@ class PasswordStore : protected PasswordStoreSync, // method will actually modify the password store data. virtual void WrapModificationTask(ModificationTask task); - // PasswordStoreSync: - // Called by WrapModificationTask() once the underlying data-modifying - // operation has been performed. Notifies observers that password store data - // may have been changed. - virtual void NotifyLoginsChanged( - const PasswordStoreChangeList& changes) OVERRIDE; - // Copies |matched_forms| into the request's result vector, then calls // |ForwardLoginsResult|. Temporarily used as an adapter between the API of // |GetLoginsImpl| and |PasswordStoreConsumer|. @@ -300,6 +298,8 @@ class PasswordStore : protected PasswordStoreSync, // The observers. scoped_refptr > observers_; + scoped_ptr syncable_service_; + bool shutdown_called_; DISALLOW_COPY_AND_ASSIGN(PasswordStore); diff --git a/components/password_manager/core/browser/stub_password_manager_client.cc b/components/password_manager/core/browser/stub_password_manager_client.cc index ecaf21999e5a3..ca4fa29c4f8cd 100644 --- a/components/password_manager/core/browser/stub_password_manager_client.cc +++ b/components/password_manager/core/browser/stub_password_manager_client.cc @@ -17,6 +17,11 @@ bool StubPasswordManagerClient::IsSyncAccountCredential( return false; } +bool StubPasswordManagerClient::ShouldFilterAutofillResult( + const autofill::PasswordForm& form) { + return false; +} + void StubPasswordManagerClient::PromptUserToSavePassword( scoped_ptr form_to_save) {} diff --git a/components/password_manager/core/browser/stub_password_manager_client.h b/components/password_manager/core/browser/stub_password_manager_client.h index deac6ceda8d6b..df98851ecb53c 100644 --- a/components/password_manager/core/browser/stub_password_manager_client.h +++ b/components/password_manager/core/browser/stub_password_manager_client.h @@ -20,6 +20,8 @@ class StubPasswordManagerClient : public PasswordManagerClient { // PasswordManagerClient: virtual bool IsSyncAccountCredential( const std::string& username, const std::string& origin) const OVERRIDE; + virtual bool ShouldFilterAutofillResult( + const autofill::PasswordForm& form) OVERRIDE; virtual void PromptUserToSavePassword( scoped_ptr form_to_save) OVERRIDE; virtual void AutomaticPasswordSave( diff --git a/components/password_manager/core/common/password_manager_switches.cc b/components/password_manager/core/common/password_manager_switches.cc index 25d75f01e210a..846d5990def36 100644 --- a/components/password_manager/core/common/password_manager_switches.cc +++ b/components/password_manager/core/common/password_manager_switches.cc @@ -8,6 +8,14 @@ namespace password_manager { namespace switches { +// Force the password manager to allow sync credentials to be autofilled. +const char kAllowAutofillSyncCredential[] = + "allow-autofill-sync-credential"; + +// Disable the link in the password manager settings page that points to account +// central. +const char kDisableAndroidPasswordLink[] = "disable-android-password-link"; + // Disable dropping the credential used to sync passwords. const char kDisableDropSyncCredential[] = "disable-drop-sync-credential"; @@ -16,6 +24,19 @@ const char kDisableDropSyncCredential[] = const char kDisableManagerForSyncSignin[] = "disable-manager-for-sync-signin"; +// Disallow autofilling of the sync credential. +const char kDisallowAutofillSyncCredential[] = + "disallow-autofill-sync-credential"; + +// Disallow autofilling of the sync credential only for transactional reauth +// pages. +const char kDisallowAutofillSyncCredentialForReauth[] = + "disallow-autofill-sync-credential-for-reauth"; + +// Enable the link in the password manager settings page that points to account +// central. +const char kEnableAndroidPasswordLink[] = "enable-android-password-link"; + // Enable dropping the credential used to sync passwords. const char kEnableDropSyncCredential[] = "enable-drop-sync-credential"; diff --git a/components/password_manager/core/common/password_manager_switches.h b/components/password_manager/core/common/password_manager_switches.h index 2520601c771c4..9377375301eb9 100644 --- a/components/password_manager/core/common/password_manager_switches.h +++ b/components/password_manager/core/common/password_manager_switches.h @@ -12,8 +12,13 @@ namespace switches { // All switches in alphabetical order. The switches should be documented // alongside the definition of their values in the .cc file. +extern const char kAllowAutofillSyncCredential[]; +extern const char kDisableAndroidPasswordLink[]; extern const char kDisableDropSyncCredential[]; extern const char kDisableManagerForSyncSignin[]; +extern const char kDisallowAutofillSyncCredential[]; +extern const char kDisallowAutofillSyncCredentialForReauth[]; +extern const char kEnableAndroidPasswordLink[]; extern const char kEnableDropSyncCredential[]; extern const char kEnableManagerForSyncSignin[]; extern const char kEnableAutomaticPasswordSaving[]; diff --git a/components/password_manager/core/common/password_manager_ui.h b/components/password_manager/core/common/password_manager_ui.h index 2728c61dd9816..f09ccf7d2c50a 100644 --- a/components/password_manager/core/common/password_manager_ui.h +++ b/components/password_manager/core/common/password_manager_ui.h @@ -37,6 +37,15 @@ enum State { BLACKLIST_STATE, }; +// The position of a password item in a list of credentials. +enum PasswordItemPosition { + // The password item is the first in the list. + FIRST_ITEM, + + // The password item is not the first item in the list. + SUBSEQUENT_ITEM, +}; + // Returns true if |state| represents a pending password. bool IsPendingState(State state); diff --git a/components/policy/core/common/cloud/enterprise_metrics.cc b/components/policy/core/common/cloud/enterprise_metrics.cc index b541e80c79d2a..96274f3e6b816 100644 --- a/components/policy/core/common/cloud/enterprise_metrics.cc +++ b/components/policy/core/common/cloud/enterprise_metrics.cc @@ -10,7 +10,10 @@ const char kMetricToken[] = "Enterprise.DMToken"; const char kMetricPolicy[] = "Enterprise.Policy"; const char kMetricEnrollment[] = "Enterprise.Enrollment"; const char kMetricEnrollmentRecovery[] = "Enterprise.EnrollmentRecovery"; -const char kMetricPolicyRefresh[] = "Enterprise.PolicyRefresh"; -const char kMetricPolicyInvalidations[] = "Enterprise.PolicyInvalidations"; +const char kMetricUserPolicyRefresh[] = "Enterprise.PolicyRefresh"; +const char kMetricUserPolicyInvalidations[] = "Enterprise.PolicyInvalidations"; +const char kMetricDevicePolicyRefresh[] = "Enterprise.DevicePolicyRefresh"; +const char kMetricDevicePolicyInvalidations[] = + "Enterprise.DevicePolicyInvalidations"; } // namespace policy diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h index 27ad36d85d669..cc6d2ee11c8b5 100644 --- a/components/policy/core/common/cloud/enterprise_metrics.h +++ b/components/policy/core/common/cloud/enterprise_metrics.h @@ -185,6 +185,14 @@ enum MetricEnrollment { kMetricEnrollmentRetried = 25, // Enrollment failed because DM token and device ID couldn't be stored. kMetricEnrollmentStoreTokenAndIdFailed = 26, + // Enrollment failed because FRE state keys couldn't be obtained. + kMetricEnrollmentNoStateKeys = 27, + // Enrollment failed because policy couldn't be validated. + kMetricEnrollmentPolicyValidationFailed = 28, + // Enrollment failed because of error in CloudPolicyStore. + kMetricEnrollmentCloudPolicyStoreError = 29, + // Enrollment failed because device couldn't be locked. + kMetricEnrollmentLockBackendError = 30, kMetricEnrollmentSize // Must be the last. }; @@ -239,8 +247,10 @@ POLICY_EXPORT extern const char kMetricToken[]; POLICY_EXPORT extern const char kMetricPolicy[]; POLICY_EXPORT extern const char kMetricEnrollment[]; POLICY_EXPORT extern const char kMetricEnrollmentRecovery[]; -POLICY_EXPORT extern const char kMetricPolicyRefresh[]; -POLICY_EXPORT extern const char kMetricPolicyInvalidations[]; +POLICY_EXPORT extern const char kMetricUserPolicyRefresh[]; +POLICY_EXPORT extern const char kMetricUserPolicyInvalidations[]; +POLICY_EXPORT extern const char kMetricDevicePolicyRefresh[]; +POLICY_EXPORT extern const char kMetricDevicePolicyInvalidations[]; } // namespace policy diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json index ee37368cb4dd5..d0f3c1700374b 100644 --- a/components/policy/resources/policy_templates.json +++ b/components/policy/resources/policy_templates.json @@ -120,7 +120,7 @@ # persistent IDs for all fields (but not for groups!) are needed. These are # specified by the 'id' keys of each policy. NEVER CHANGE EXISTING IDs, # because doing so would break the deployed wire format! -# For your editing convenience: highest ID currently used: 274 +# For your editing convenience: highest ID currently used: 275 # # Placeholders: # The following placeholder strings are automatically substituted: @@ -401,7 +401,7 @@ 'supported_on': ['chrome.*:12-', 'chrome_os:12-'], 'features': { 'dynamic_refresh': True, - 'per_profile': False, + 'per_profile': True, }, 'deprecated': True, 'example_value': ['file', 'https'], @@ -3822,7 +3822,6 @@ 'device_only': True, 'features': { 'dynamic_refresh': True, - 'per_profile': False, }, 'deprecated': True, 'example_value': 'remove-lru', @@ -6139,7 +6138,6 @@ 'supported_on': ['chrome_os:28-'], 'features': { 'dynamic_refresh': False, - 'per_profile': False, }, 'device_only': True, 'example_value': 'restricted', @@ -6165,7 +6163,6 @@ 'supported_on': ['chrome_os:28-'], 'features': { 'dynamic_refresh': True, - 'per_profile': False, }, 'device_only': True, 'example_value': True, @@ -6610,8 +6607,7 @@ If you disable this setting, users will not be allowed to use EasyUnlock. - If this policy is left not set, EasyUnlock is allowed if the requirements for the feature are satified. - ''', + If this policy is left not set, the default is not allowed for enterprise-managed users and allowed for non-managed users.''', }, { 'name': 'SessionLocales', @@ -6646,7 +6642,22 @@ This policy can only be set as recommended. You can use this policy to move a set of recommended locales to the top but users are always allowed to choose any locale supported by $2Google Chrome OS for their session. ''', }, + { + 'name': 'BrowserGuestModeEnabled', + 'type': 'main', + 'schema': { 'type': 'boolean' }, + 'supported_on': ['chrome.*:38-'], + 'features': { + 'dynamic_refresh': True, + 'per_profile': False, + }, + 'example_value': True, + 'id': 275, + 'caption': '''Enable guest mode in browser''', + 'desc': '''If this policy is set to true or not configured, $1Google Chrome will enable guest logins. Guest logins are $1Google Chrome profiles where all windows are in incognito mode. + If this policy is set to false, $1Google Chrome will not allow guest profiles to be started.''', + }, ], 'messages': { # Messages that are not associated to any policies. diff --git a/components/policy/resources/policy_templates_cs.xtb b/components/policy/resources/policy_templates_cs.xtb index b6f4a7251d851..a51b863c47de7 100644 --- a/components/policy/resources/policy_templates_cs.xtb +++ b/components/policy/resources/policy_templates_cs.xtb @@ -65,11 +65,11 @@ Hodnota prodloužení musí být 100 % nebo více. Pokud zásadu nenastavíte, použije se pro všechny webové stránky globální výchozí hodnota buď ze zásady DefaultImagesSetting (pokud je nastavena), nebo z osobního nastavení uživatele. Aktivovat Automatické vyplňování -Umožňuje určit rozšíření, kterých se netýká černá listina. +Umožňuje určit rozšíření, kterých se netýká seznam zakázaných rozšíření. - Pokud je na černé listině uvedena hodnota *, znamená to, že jsou na ní všechna rozšíření a uživatelé mohou instalovat pouze rozšíření uvedená na bílé listině. + Pokud je na seznamu zakázaných rozšíření uvedena hodnota *, znamená to, že jsou na ní všechna rozšíření a uživatelé mohou instalovat pouze rozšíření uvedená v seznamu povolených rozšíření. - Ve výchozím nastavení jsou všechna rozšíření na bílé listině. Pokud však byla všechna rozšíření pomocí zásady umístěna na černou listinu, lze tuto zásadu pomocí bílé listiny přepsat. + Ve výchozím nastavení jsou všechna rozšíření na seznamu povolených. Pokud však byla všechna rozšíření pomocí zásady umístěna na seznam zakázaných rozšíření, lze tuto zásadu pomocí seznamu povolených přepsat. Nastavuje výchozí stav funkce usnadnění přístupu pomocí velkého kurzoru na přihlašovací obrazovce. Pokud je tato zásada nastavena na hodnotu true, bude při zobrazení přihlašovací obrazovky velký kurzor aktivní. @@ -233,9 +233,9 @@ Pokud bude tato zásada ponechána nenastavená nebo bude seznam prázdný, bude Rozšíření, která mohou používat rozhraní API vzdáleného ověření identity Aktivovat smazání historie prohlížeče a stahování Určete seznam pluginů, které uživatel může povolit nebo zakázat -Umožňuje určit rozšíření, která uživatelé NEMOHOU nainstalovat. Pokud se na černé listině vyskytne rozšíření, které již bylo nainstalováno, bude odebráno +Umožňuje určit rozšíření, která uživatelé NEMOHOU nainstalovat. Pokud se na seznamu zakázaných vyskytne rozšíření, které již bylo nainstalováno, bude odebráno. - Pokud je na černé listině uvedena hodnota *, znamená to, že jsou zakázána všechna rozšíření, která nejsou explicitně uvedena na bílé listině. + Pokud je na seznamu zakázaných rozšíření uvedena hodnota *, znamená to, že jsou zakázána všechna rozšíření, která nejsou explicitně uvedena na seznamu povolených rozšíření. Pokud zásadu nenastavíte, mohou uživatelé v aplikaci instalovat libovolná rozšíření. Pokud má hodnotu true, uživatel může pomocí hardwaru, který je součástí zařízení Chrome, vzdáleně sdělit svou identitu funkci Privacy CA prostřednictvím rozhraní Enterprise Platform Keys API chrome.enterprise.platformKeysPrivate.challengeUserKey(). @@ -654,11 +654,11 @@ Pokud tuto zásadu ponecháte nenastavenou, bude tato možnost aktivována a už Příklad: Zásada nainstaluje rozšíření ze standardní adresy URL Internetového obchodu Chrome. Další informace o hostování rozšíření naleznete na adrese . - Rozšíření nainstalovaná pomocí této zásady nemohou uživatelé odinstalovat. Pokud rozšíření z tohoto seznamu odeberete, bude prohlížečem automaticky odinstalováno. Rozšíření uvedená v tomto seznamu jsou také automaticky přidána na bílou listinu a zásada ExtensionsInstallBlacklist je neovlivňuje. + Rozšíření nainstalovaná pomocí této zásady nemohou uživatelé odinstalovat. Pokud rozšíření z tohoto seznamu odeberete, bude prohlížečem automaticky odinstalováno. Rozšíření uvedená v tomto seznamu jsou také automaticky přidána na seznam povolených rozšíření a zásada ExtensionsInstallBlacklist je neovlivňuje. Není-li tato zásada nastavena na konkrétní hodnotu, uživatel může z prohlížeče odinstalovat libovolné rozšíření. Zjistit nastavení proxy serveru automaticky -Názvy hostitelů zasílání nativních zpráv, kterým chcete udělit výjimku z černé listiny. +Názvy hostitelů zasílání nativních zpráv, kterým chcete udělit výjimku ze seznamu zakázaných položek. Povolit použití vzdáleného ověření identity k ochraně obsahu v zařízení Je-li tato zásada aktivovaná, vynucuje import uložených hesel z předchozího výchozího prohlížeče. Aktivace této zásady má také vliv na dialogové okno importu. @@ -732,7 +732,7 @@ Pokud tuto zásadu ponecháte nenastavenou, bude tato možnost aktivována a už Pokud toto nastavení aktivujete nebo deaktivujete, uživatelé je v aplikaci nebudou moci změnit ani přepsat. Pokud nastavení neupravíte, mohou si uživatelé vybrat, zda bude funkci používat, či nikoliv. -ID rozšíření, která mají být vyňata z černé listiny +ID rozšíření, která mají být vyňata ze seznamu zakázaných Maximální zpoždění načítání po zneplatnění zásady Povolit vytváření nových uživatelských účtů Procento prodloužení prodlevy režimu nečinnosti v režimu prezentace (podpora ukončena) @@ -749,11 +749,11 @@ Pokud tuto zásadu ponecháte nenastavenou, bude tato možnost aktivována a už Pokud toto nastavení aktivujete nebo deaktivujete, uživatelé je nebudou moci změnit ani přepsat. Není-li zásada nastavena, mohou si uživatelé vybrat, zda chtějí být vyzváni k zadání hesla k odemčení zařízení, nebo nikoliv. -Umožňuje určit, na které hostitele zasílání nativních zpráv se nebude vztahovat černá listina. +Umožňuje určit, na které hostitele zasílání nativních zpráv se nebude vztahovat seznam zakázaných hostitelů. - Hodnota * v černé listině znamená, že jsou všichni hostitelé zasílání nativních zpráv zakázáni a že budou načteni pouze hostitelé uvedení na bílé listině. + Hodnota * v seznamu zakázaných znamená, že jsou všichni hostitelé zasílání nativních zpráv zakázáni a že budou načteni pouze hostitelé uvedení na seznamu povolených hostitelů. - Ve výchozím nastavení jsou povoleni všichni hostitelé zasílání nativních zpráv. Pokud však byly všichni hostitelé zasílání nativních zpráv pomocí zásady přidáni na černou listinu, je možné tuto zásadu pomocí bílé listiny přepsat. + Ve výchozím nastavení jsou povoleni všichni hostitelé zasílání nativních zpráv. Pokud však byly všichni hostitelé zasílání nativních zpráv pomocí zásady přidáni na seznam zakázaných, je možné tuto zásadu pomocí seznamu povolených hostitelů přepsat. Akce při spuštění Umožňuje přenést nastavení sítě, která se uplatní pro jednotlivé uživatele v zařízení se systémem . Konfigurace sítě je řetězec ve formátu JSON, který se řídí definicí formátu Open Network Configuration popsanou na stránce . Seznam identifikátorů aplikací, které se v zobrazují jako připnuté na liště spouštěče. @@ -820,11 +820,11 @@ POZNÁMKA: Tato zásada je v současnosti podporována pouze v režimu veřejné Konfigurovat povolené typy aplikací nebo rozšíření Povoluje přístup k adresám URL uvedeným v seznamu, které tedy představují výjimky ze seznamu zakázaných adres URL. - Formát položek v tomto seznamu naleznete v popisu zásady pro vytvoření černé listiny adres URL. + Formát položek v tomto seznamu naleznete v popisu zásady pro vytvoření seznamu zakázaných adres URL. - Pomocí této zásady můžete určit výjimky z omezující černé listiny. Příklad: Přidáním pravidla „*“ na černou listinu zablokujete všechny požadavky. Pomocí této zásady poté můžete povolit přístup omezenému seznamu adres URL. Můžete definovat výjimky v podobě schémat, subdomén, portů nebo konkrétních cest. + Pomocí této zásady můžete určit výjimky ze seznamu zakázaných adres. Příklad: Přidáním pravidla „*“ na seznam zakázaných zablokujete všechny požadavky. Pomocí této zásady poté můžete povolit přístup omezenému seznamu adres URL. Můžete definovat výjimky v podobě schémat, subdomén, portů nebo konkrétních cest. - Zda je adresa URL blokována nebo povolena, určuje vždy nejkonkrétnější filtr, přičemž bílá listina má přednost před černou listinou. + Zda je adresa URL blokována nebo povolena, určuje vždy nejkonkrétnější filtr, přičemž seznam povolených má přednost před seznamem zakázaných adres. Počet záznamů v této zásadě je omezen na 1000. Všechny další záznamy budou ignorovány. @@ -838,7 +838,7 @@ Pokud je tato zásada nastavena na hodnotu false, režim vysokého kontrastu bud Pokud tuto zásadu nastavíte, uživatelé ji nebudou moci změnit ani přepsat. Pokud ji ponecháte nenastavenou, režim vysokého kontrastu bude ve výchozím nastavení deaktivován, ale uživatelé jej budou moci kdykoli aktivovat. -Konfigurace bílé listiny zasílání nativních zpráv +Konfigurace seznamu povolených hostitelů zasílání nativních zpráv Nastavuje cílovou verzi automatické aktualizace. Určuje předponu cílové verze, na kterou se má aktualizovat. Pokud zařízení obsahuje starší verzi, než jakou určuje předpona, bude systém aktualizován na poslední verzi s danou předponou. Jestliže zařízení již obsahuje novější verzi, nestane se nic (tj. nedojde k přechodu na nižší verzi) a zařízení zůstane u stávající verze. Formát předpony funguje na základě komponent, jak ukazuje následující příklad: @@ -872,7 +872,7 @@ Pokud ji ponecháte nenastavenou, režim vysokého kontrastu bude ve výchozím Seznam rozšíření, která se automaticky nainstalují pro uživatele ukázky v zařízeních v režimu prodeje. Tato rozšíření se uloží v zařízení a po instalaci mohou být nainstalována v režimu offline. Každý záznam v seznamu obsahuje adresář, který musí zahrnovat ID rozšíření v poli „extension-id“ a jeho webovou adresu pro aktualizace v poli „update-url“. -Bílá listina serverů pro ověřování +Seznam povolených serverů pro ověřování Povoluje nebo zakazuje záznam videa. Pokud je tato zásada aktivní nebo není nakonfigurována (výchozí nastavení), uživatel bude vyzván k udělení přístupu pro záznam videa (s výjimkou adres URL nakonfigurovaných v seznamu VideoCaptureAllowedUrls, kterým bude přístup udělen bez vyzvání). @@ -958,7 +958,7 @@ Tato zásada ovlivňuje kromě integrované webové kamery i všechny ostatní v Nedělat nic Zobrazit uživatelská jména na přihlašovací obrazovce Zobrazit na liště tlačítko Domů -Konfigurace černé listiny instalace rozšíření +Konfigurace seznamu zakázaných položek instalace rozšíření Obrázek tapety Jako domovskou použít stránku Nová karta Přeskočení kontroly metaznaček ve službě @@ -1169,7 +1169,7 @@ Pokud ji ponecháte nenastavenou, velký kurzor bude ve výchozím nastavení de Pokud tuto zásadu nenastavíte, bude použita výchozí velikost a uživatel ji bude moci přepsat pomocí příznaku „--disk-cache-size“. Povolit tisk -Určuje, které servery mají být umístěny na bílou listinu pro integrované ověření. Integrované ověření je povoleno pouze v případě, že obdrží výzvu ověřování ze proxy serveru nebo jiného serveru, které jsou uvedeny v tomto seznamu povolených serverů. +Určuje, které servery mají být umístěny na seznam povolených pro integrované ověření. Integrované ověření je povoleno pouze v případě, že obdrží výzvu ověřování ze proxy serveru nebo jiného serveru, které jsou uvedeny v tomto seznamu povolených serverů.   Chcete-li zadat několik názvů serverů, oddělte je čárkami. Zástupné znaky (*) jsou povoleny. @@ -1254,7 +1254,7 @@ Pokud ji ponecháte nenastavenou, velký kurzor bude ve výchozím nastavení de Tato zásada je určena k internímu použití prohlížečem Chrome. Blokování přístupu k seznamu adres URL -Nakonfiguruje zásady související s rozšířeními. Uživatel nebude moci instalovat rozšíření na černé listině, pokud nebudou autorizována. U aplikace také můžete vynutit automatickou instalaci rozšíření jejich zadáním do zásady . Rozšíření, jejichž instalace je vynucena, budou nainstalována bez ohledu na to, zda jsou uvedena na černé listině. +Nakonfiguruje zásady související s rozšířeními. Uživatel nebude moci instalovat rozšíření na seznamu zakázaných rozšíření, pokud nebudou autorizována. U aplikace také můžete vynutit automatickou instalaci rozšíření jejich zadáním do zásady . Rozšíření, jejichž instalace je vynucena, budou nainstalována bez ohledu na to, zda jsou uvedena na seznamu zakázaných. Umožňuje prohlížeči fungovat jako proxy server mezi službou a staršími tiskárnami připojenými k počítači. Pokud je toto nastavení aktivované nebo není nakonfigurované, mohou uživatelé proxy server pro tisk z cloudu aktivovat ověřením pomocí účtu Google. @@ -1274,7 +1274,7 @@ Pokud ji ponecháte nenastavenou, velký kurzor bude ve výchozím nastavení de Táto zásada má umožnit striktní zakázání pluginů tam, kde seznam DisabledPlugins obsahuje zástupné znaky, jako například deaktivovat všechny pluginy „*“ nebo deaktivovat všechny pluginy Java „'*Java*“, avšak správce chce určité verze povolit (například „IcedTea Java 2.3“). Tuto konkrétní verzi lze určit v této zásadě. - Upozorňujeme, že je třeba zadat výjimku pro název pluginu i pro název skupiny pluginů. Jednotlivé skupiny pluginů se na stránce about:plugins zobrazují v samostatných sekcích a každá sekce může obsahovat jeden nebo více pluginů. Plugin Shockwave Flash například patří do skupiny Adobe Flash Player. Pokud má být vyloučen z černé listiny, musí být v seznamu výjimek uvedeny oba názvy. + Upozorňujeme, že je třeba zadat výjimku pro název pluginu i pro název skupiny pluginů. Jednotlivé skupiny pluginů se na stránce about:plugins zobrazují v samostatných sekcích a každá sekce může obsahovat jeden nebo více pluginů. Plugin Shockwave Flash například patří do skupiny Adobe Flash Player. Pokud má být vyloučen ze seznamu zakázaných, musí být v seznamu výjimek uvedeny oba názvy. Pokud tato zásada není nastavena, bude jakýkoliv plugin, který odpovídá vzoru v seznamu DisabledPlugins, uzamčen jako deaktivovaný a uživatelé jej nebudou moci aktivovat. Nastavit adresář mezipaměti na disku @@ -1382,7 +1382,7 @@ Pokud ji ponecháte nenastavenou, velký kurzor bude ve výchozím nastavení de Pokud je tato zásada ponechána nenastavená, uživatel si může zvolit, jaký obrázek bude mít zobrazený na ploše a na pozadí přihlašovací obrazovky. Prodleva upozornění na nečinnost při napájení z baterie -Konfiguruje zásady pro zasílání nativních zpráv. Hostitelé zasílání nativních zpráv uvedení na černé listině nebudou povoleni (pokud nebudou uvedeni na bílé listině). +Konfiguruje zásady pro zasílání nativních zpráv. Hostitelé zasílání nativních zpráv uvedení na seznamu zakázaných nebudou povoleni (pokud nebudou uvedeni na seznamu povolených hostitelů). Nastavuje výchozí stav funkce usnadnění přístupu pomocí režimu vysokého kontrastu na přihlašovací obrazovce. Pokud je tato zásada nastavena na hodnotu true, bude při zobrazení obrazovky přihlášení režim vysokého kontrastu aktivní. @@ -1494,7 +1494,7 @@ Pokud ji ponecháte nenastavenou, bude hlasová odezva ve výchozím nastavení Pokud zásadu nenastavíte, použije se pro všechny webové stránky globální výchozí hodnota buď ze zásady DefaultNotificationsSetting (pokud je nastavena), nebo z osobního nastavení uživatele. Umožňuje zadat, kteří hostitelé zasílání nativních zpráv se nemají načítat. - Hodnota * v černé listině znamená, že jsou zakázáni všichni hostitelé zasílání nativních zpráv, kteří nejsou uvedeni na bílé listině. + Hodnota * v seznamu zakázaných hostitelů znamená, že jsou zakázáni všichni hostitelé zasílání nativních zpráv, kteří nejsou uvedeni na seznamu povolených. Pokud je tato zásada ponechána nenastavená, bude načítat všechny nainstalované hostitele zasílání nativních zpráv. Hlásí verzi operačního systému a firmwaru v registrovaných zařízeních. @@ -1560,7 +1560,7 @@ Pokud ji ponecháte nenastavenou, bude hlasová odezva ve výchozím nastavení Pokud tuto zásadu nenastavíte, bude použita výchozí velikost a uživatel ji bude moci přepsat pomocí příznaku „--media-cache-size“. Následující vzory adres URL vykreslovat vždy v pluginu -Konfigurovat bílou listinu pro instalaci rozšíření +Konfigurovat seznam povolených pro instalaci rozšíření Pokud je toto nastavení povoleno, žádosti gnubby o ověření budou zprostředkovány přes připojení ke vzdálenému hostiteli. Pokud je toto nastavení zakázáno nebo není nakonfigurováno, žádosti gnubby o ověření nebudou zprostředkovány. @@ -1632,7 +1632,7 @@ Pokud ji ponecháte nenastavenou, lupa bude ve výchozím nastavení deaktivová Zásada bude dodržována pouze v případě, že je aktivní zásada DefaultSearchProviderEnabled. Název výchozího poskytovatele vyhledávání Obnovovací frekvence zásad pro uživatele -Bílá listina serverů pro delegování ověření protokolu Kerberos +Seznam povolených serverů pro delegování ověření protokolu Kerberos Zakázat připojení externího úložiště Udává parametry, které budou použity při vyhledávání adresy URL pomocí metody POST. Sestává z párů název–hodnota oddělených čárkou. Pokud je hodnotou parametr šablony (např. {searchTerms} v příkladu výše), bude nahrazen skutečnými údaji vyhledávacích dotazů. @@ -1812,7 +1812,7 @@ Až bude okamžité přihlašování podporovat všechny přihlašovací procesy Deaktivací tohoto nastavení umožníte, aby prvky webové stránky, které nepocházejí z domény uvedené v adresním řádku prohlížeče, mohly nastavovat soubory cookie. Pokud zásadu nenastavíte, budou soubory cookie třetích stran povoleny, uživatelé však budou moci toto nastavení změnit. -Konfigurace černé listiny zasílání nativních zpráv +Konfigurace seznamu zakázaných položek zasílání nativních zpráv Blokovat JavaScript na těchto stránkách Tato zásada se již nepoužívá. Namísto ní prosím použijte zásadu ProxyMode. diff --git a/components/policy/resources/policy_templates_da.xtb b/components/policy/resources/policy_templates_da.xtb index 843bb0ba4205b..09d1a3d8ceed2 100644 --- a/components/policy/resources/policy_templates_da.xtb +++ b/components/policy/resources/policy_templates_da.xtb @@ -256,11 +256,11 @@ Hvis denne politik ikke indstilles, vil den blive aktiveret, men brugeren vil være i stand til at ændre den. Aktivér proxyfunktionen for datakomprimering -Aktivér tilgængelighedsfunktionen Skærmtastatur. +Aktivér hjælpefunktionen Skærmtastatur. - Hvis denne politik er angivet til Sand, vil tilgængelighedsfunktionen Skærmtastatur altid være slået til. + Hvis denne politik er angivet som Sand, vil hjælpefunktionen Skærmtastatur altid være slået til. - Hvis denne politik er angivet til Falsk, vil tilgængelighedsfunktionen Skærmtastatur altid være slået fra. + Hvis denne politik er angivet som Falsk, vil hjælpefunktionen Skærmtastatur altid være slået fra. Hvis du indstiller denne politik, kan brugerne ikke ændre eller tilsidesætte den. @@ -1141,11 +1141,11 @@ Hvis denne indstilling ikke er angivet, vil tillade br Tillad, at alle websites viser pop op-vinduer Bloker alle plugins Angiv størrelse på mediediskcache -Angiv standardtilstanden for tilgængelighedsfunktionen Skærmtastatur på loginskærmen. +Angiv standardtilstanden for hjælpefunktionen Skærmtastatur på loginskærmen. - Hvis denne politik er angivet til Sand, aktiveres skærmtastaturet, når loginskærmen vises. + Hvis denne politik er angivet som Sand, aktiveres skærmtastaturet, når loginskærmen vises. - Hvis denne politik er angivet til Falsk, deaktiveres skærmtastaturet, når loginskærmen vises. + Hvis denne politik er angivet som Falsk, deaktiveres skærmtastaturet, når loginskærmen vises. Hvis du har indstillet denne politik, kan brugerne midlertidigt tilsidesætte den ved at aktivere eller deaktivere skærmtastaturet. Brugerens valg er dog ikke permanent, og standardindstillingen gendannes, når loginskærmen vises på ny, eller når brugeren har været inaktiv på loginskærmen i et minut. @@ -1397,11 +1397,11 @@ Hvis denne indstilling ikke er angivet, vil tillade br Aktiver JavaScript Denne politik konfigurerer aktiveringen af det virtuelle tastatur som inputenhed i ChromeOS. Brugere kan ikke tilsidesætte denne politik. - Hvis politikken er indstillet til sand, vil det virtuelle tastatur på skærmen altid være aktiveret. + Hvis politikken er angivet som Sand, vil det virtuelle tastatur på skærmen altid være aktiveret. - Hvis den er sat til falsk, vil det virtuelle tastatur på skærmen altid være deaktiveret. + Hvis den er angivet som Falsk, vil det virtuelle tastatur på skærmen altid være deaktiveret. - Hvis du har indstillet denne politik, kan brugere ikke ændre eller tilsidesætte den. Brugere kan dog stadig aktivere/deaktivere et tilgængelighedstastatur på skærmen, som har forrang over det virtuelle tastatur, der kontrolleres af denne politik. Se mere om kontrol af tilgængelighedstastaturet på skærmen i politikken |VirtualKeyboardEnabled|. + Hvis du har indstillet denne politik, kan brugere ikke ændre eller tilsidesætte den. Brugere kan dog stadig aktivere/deaktivere et skærmtastatur som hjælpefunktion, som har forrang over det virtuelle tastatur, der kontrolleres af denne politik. Se mere om kontrol af skærmtastaturet som hjælpefunktion i politikken |VirtualKeyboardEnabled|. Hvis denne politik ikke indstilles, er tastaturet på skærmen slået fra til at starte med, men kan til enhver tid aktiveres af brugeren. Der kan også benyttes heuristiske regler til at bestemme, hvornår tastaturet skal vises. Tillad indbyggede beskedhosts, der er installeret på brugerniveau (installeret uden administratortilladelser). @@ -1629,15 +1629,15 @@ Hvis denne indstilling ikke er angivet, vil tillade br Når denne politik er angivet, åbnes de administrerede bogmærker som standard, når visningen Bogmærker åbnes i Chrome. Administrerede bogmærker synkroniseres ikke med brugerkontoen. -Vis tilgængelighedsindstillingerne for i systemmenuen. +Vis hjælpeindstillingerne for i systemmenuen. - Hvis denne politik er angivet til Sand, vises tilgængelighedsindstillingerne altid i proceslinjemenuen. + Hvis denne politik er angivet som Sand, vises hjælpeindstillingerne altid i proceslinjemenuen. - Hvis denne politik er angivet til Falsk, vises tilgængelighedsindstillingerne aldrig i proceslinjemenuen. + Hvis denne politik er angivet som Falsk, vises hjælpeindstillingerne aldrig i proceslinjemenuen. Hvis du har indstillet denne politik, kan brugerne ikke ændre eller tilsidesætte den. - Hvis denne politik ikke er indstillet, vises tilgængelighedsindstillingerne ikke i proceslinjemenuen, men brugeren kan forårsage, at tilgængelighedsindstillingerne vises via siden Indstillinger. + Hvis denne politik ikke er indstillet, vises hjælpeindstillingerne ikke i proceslinjemenuen, men brugeren kan forårsage, at hjælpeindstillingerne vises via siden Indstillinger. Angiver, hvilket GSSAPI-samling der skal bruges til HTTP-godkendelse. Du kan enten angive et samlingsnavn eller en hel sti. Hvis der ikke angives en indstilling, går tilbage til at bruge et standardsamlingsnavn. Vis en advarsel, når du er på websites uden for indholdspakkerne Aktiverer godkendelse med to faktorer for værter med fjernadgang i stedet for en brugerangivet pinkode. @@ -1712,9 +1712,9 @@ Hvis denne indstilling ikke er angivet, vil tillade br Hvis denne politik ikke indstilles, kan brugeren selv vælge sin startside, hvis HomepageIsNewTabPage heller ikke er indstillet. Tillad lydafspilning. - Hvis denne politik er angivet til Falsk, vil lydoutput ikke være tilgængeligt på enheden, mens brugeren er logget ind. + Hvis denne politik er angivet som Falsk, vil lydoutput ikke være tilgængeligt på enheden, mens brugeren er logget ind. - Denne politik påvirker alle former for lydoutput, og ikke kun de indbyggede højttalere. Tilgængelighedsfunktioner for lyd forhindres også af denne politik. Hvis brugeren har behov for en skærmlæser, skal du ikke aktivere denne politik. + Denne politik påvirker alle former for lydoutput, og ikke kun de indbyggede højttalere. Hjælpefunktioner for lyd forhindres også af denne politik. Hvis brugeren har behov for en skærmlæser, skal du ikke aktivere denne politik. Hvis denne indstilling er angivet til Sand eller ikke er konfigureret, kan brugerne bruge alle understøttede former for lydoutput på deres enhed. Tillad gnubby-godkendelse diff --git a/components/policy/resources/policy_templates_de.xtb b/components/policy/resources/policy_templates_de.xtb index ade005bc10b35..19dc4a3c0ced9 100644 --- a/components/policy/resources/policy_templates_de.xtb +++ b/components/policy/resources/policy_templates_de.xtb @@ -810,7 +810,7 @@ Falls die Richtlinie auf "false" gesetzt oder nicht festgelegt wird, k Gibt die URL der Suchmaschine an, die zur Ausgabe von Instant-Ergebnissen verwendet wird. Die URL sollte den String enthalten. Dieser wird bei der Suchanfrage mit dem Text ersetzt, den der Nutzer bis dahin eingegeben hat. Diese Richtlinie ist optional. Wenn keine Festlegung erfolgt, so werden keine Instant-Ergebnisse ausgegeben. Diese Richtlinie wird nur dann umgesetzt, wenn die Richtlinie "DefaultSearchProviderEnabled" aktiviert ist. Wenn Sie diese Einstellung aktivieren, wird die automatische Suche und Installation fehlender Plug-ins in deaktiviert. Ist sie deaktiviert oder nicht konfiguriert, so ist die Plug-in-Suche aktiv. Auszuführende Aktion beim Zuklappen des Geräts -Ermöglicht Ihnen die Zusammenstellung einer Liste mit URL-Mustern, die Websites angeben, für die automatisch ein Client-Zertifikat auswählen soll, wenn die Website ein Zertifikat anfordert. Wenn diese Richtlinie nicht konfiguriert ist, erfolgt keine automatische Auswahl für Websites. +Ermöglicht Ihnen die Zusammenstellung einer Liste mit URL-Mustern, die Websites angeben, für die automatisch ein Clientzertifikat auswählen soll, wenn die Website ein Zertifikat anfordert. Wenn diese Richtlinie nicht konfiguriert ist, erfolgt keine automatische Auswahl für Websites. Beschreibung Aktiviert die Verwendung alternativer Fehlerseiten, die in integriert sind, etwa "Seite nicht gefunden", und verhindert, dass die Einstellung durch Nutzer geändert wird. Wenn Sie diese Einstellung aktivieren, kommen alternative Fehlerseiten zum Einsatz. Sollten Sie sie deaktivieren, werden alternative Fehlerseiten grundsätzlich nicht verwendet. Ist diese Einstellung aktiviert oder deaktiviert, können Nutzer sie in nicht ändern oder außer Kraft setzen. Wird die Richtlinie nicht konfiguriert, so ist die Funktion aktiv, aber der Nutzer kann dies ändern. Speichern von lokalen Daten für keine Website zulassen diff --git a/components/policy/resources/policy_templates_fa.xtb b/components/policy/resources/policy_templates_fa.xtb index 0429514ad3fa5..60897eb8be0d3 100644 --- a/components/policy/resources/policy_templates_fa.xtb +++ b/components/policy/resources/policy_templates_fa.xtb @@ -826,7 +826,7 @@ اگر این خط‌مشی را تنظیم کرده باشید، کاربران نمی‌توانند آن را تغییر دهند یا لغو نمایند. چنانچه این خط‌مشی بدون تنظیم رها شده باشد، نشانگر موشواره بزرگ از ابتدا از کار افتاده است اما می‌تواند در هر زمان توسط کاربر به کار انداخته شود. -فعال کردن بازخورد صوتی +فعال کردن بازخورد گفتاری این خط‌مشی از نسخه ۳۵ کنار گذاشته شده است. اطلاعات حافظه به هر حال، بدون در نظر گرفتن مقدار گزینه، به صفحه گزارش داده خواهد شد، اما اندازه‌های گزارش شده diff --git a/components/policy/resources/policy_templates_id.xtb b/components/policy/resources/policy_templates_id.xtb index 8291a71552ad3..63d62ce697528 100644 --- a/components/policy/resources/policy_templates_id.xtb +++ b/components/policy/resources/policy_templates_id.xtb @@ -545,7 +545,7 @@ Ekstensi URL yang akan diberi akses ke perangkat perekam video tanpa peringatan Laporkan lokasi perangkat -Setel ukuran cache disk dalam byte +Setel ukuran cache disk dalam bita Mengaktifkan pengoptimalan WPAD pada dan mencegah pengguna mengubah setelan ini. Menyetel kebijakan ini menjadi aktif akan menyebabkan Chrome menunggu dalam interval yang lebih pendek bagi server WPAD berbasis DNS. @@ -687,7 +687,7 @@ Nilai kebijakan harus ditentukan dalam milidetik. Nilai dibatasi agar kurang dari atau sama dengan penundaan menganggur. Aktifkan permintaan konfigurasi jaringan saat offline Izinkan atau tolak penangkapan video -Menentukan parameter yang digunakan saat melakukan penelusuran gambar dengan POST. Berisi pasangan nama/nilai yang dipisahkan koma. Jika nilai adalah parameter template, seperti {imageThumbnail} contoh di atas, akan diganti dengan data thumbnail gambar nyata. +Menentukan parameter yang digunakan saat melakukan penelusuran gambar dengan POST. Berisi pasangan nama/nilai yang dipisahkan koma. Jika nilai adalah parameter template, seperti {imageGambar kecil} contoh di atas, akan diganti dengan data thumbnail gambar nyata. Kebijakan ini bersifat opsional. Jika tidak disetel, permintaan penelusuran akan dikirim menggunakan metode GET. @@ -1353,7 +1353,7 @@ Jika kebijakan ini tidak disetel, keyboard di layar akan dinonaktifkan pada awalnya namun dapat diaktifkan oleh pengguna kapan saja. Aturan heuristis juga dapat digunakan untuk menentukan waktu untuk menampilkan keyboard. Izinkan host Native Messaging tingkat pengguna (dipasang tanpa izin admin). Izinkan semua situs menjalankan plugin secara otomatis -Setel ukuran cache disk media dalam byte +Setel ukuran cache disk media dalam bita Menentukan apakah pencari plugin harus dinonaktifkan Nama hosting perpesanan asli yang dilarang (atau * untuk semua) Menyetel jenis lup yang diaktifkan. diff --git a/components/policy/resources/policy_templates_zh-CN.xtb b/components/policy/resources/policy_templates_zh-CN.xtb index 8dd5900b9511c..d9a5238952426 100644 --- a/components/policy/resources/policy_templates_zh-CN.xtb +++ b/components/policy/resources/policy_templates_zh-CN.xtb @@ -738,7 +738,7 @@ 列出系统自动为演示用户安装的扩展程序(它们将安装在零售模式下的设备上)。安装后,这些扩展程序保存在该设备中,并且可以在离线状态下安装。 - 每个列表条目都包含一个词典,其“extension-id”字段必须包含扩展程序 ID,而其“update-url”字段必须包含更新网址。 + 每个列表条目都包含一个字典,其“extension-id”字段必须包含扩展程序 ID,而其“update-url”字段必须包含更新网址。 身份验证服务器白名单 允许或拒绝视频捕获。 diff --git a/components/power.gypi b/components/power.gypi new file mode 100644 index 0000000000000..a1757a0cd0f73 --- /dev/null +++ b/components/power.gypi @@ -0,0 +1,25 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'power', + 'type': 'static_library', + 'dependencies': [ + '../base/base.gyp:base', + 'keyed_service_content', + ], + 'include_dirs': [ + '..', + ], + 'sources': [ + 'power/origin_power_map.cc', + 'power/origin_power_map.h', + 'power/origin_power_map_factory.cc', + 'power/origin_power_map_factory.h', + ], + }, + ], +} diff --git a/components/power/BUILD.gn b/components/power/BUILD.gn new file mode 100644 index 0000000000000..c77545c412f7a --- /dev/null +++ b/components/power/BUILD.gn @@ -0,0 +1,18 @@ +# 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. + +static_library("power") { + sources = [ + "origin_power_map.cc", + "origin_power_map.h", + "origin_power_map_factory.cc", + "origin_power_map_factory.h", + ] + + deps = [ + "//base", + "//components/keyed_service/core:core", + "//content/public/common", + ] +} diff --git a/components/power/DEPS b/components/power/DEPS new file mode 100644 index 0000000000000..df2301d5f9692 --- /dev/null +++ b/components/power/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+components/keyed_service", + "+content/public/common", +] diff --git a/components/power/OWNERS b/components/power/OWNERS new file mode 100644 index 0000000000000..76a50a47e6a6f --- /dev/null +++ b/components/power/OWNERS @@ -0,0 +1,3 @@ +derat@chromium.org +dhnishi@chromium.org +sivachandra@chromium.org diff --git a/components/power/origin_power_map.cc b/components/power/origin_power_map.cc new file mode 100644 index 0000000000000..dfb0f172a93d5 --- /dev/null +++ b/components/power/origin_power_map.cc @@ -0,0 +1,52 @@ +// 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/power/origin_power_map.h" + +#include "base/logging.h" +#include "content/public/common/url_constants.h" +#include "url/gurl.h" + +namespace power { + +OriginPowerMap::OriginPowerMap() : total_consumed_(0.0) { +} + +OriginPowerMap::~OriginPowerMap() { +} + +int OriginPowerMap::GetPowerForOrigin(const GURL& url) { + if (!total_consumed_) + return 0; + + OriginMap::const_iterator it = origin_map_.find(url.GetOrigin()); + return it == origin_map_.end() ? 0 : + static_cast(it->second * 100 / total_consumed_ + 0.5); +} + +void OriginPowerMap::AddPowerForOrigin(const GURL& url, double power) { + DCHECK_GE(power, 0); + GURL origin = url.GetOrigin(); + if (!origin.is_valid() || origin.SchemeIs(content::kChromeUIScheme)) + return; + + origin_map_[origin] += power; + total_consumed_ += power; +} + +OriginPowerMap::PercentOriginMap OriginPowerMap::GetPercentOriginMap() { + OriginPowerMap::PercentOriginMap percent_map; + + if (!total_consumed_) + return percent_map; + + for (OriginMap::iterator it = origin_map_.begin(); it != origin_map_.end(); + ++it) { + percent_map[it->first] = + static_cast(it->second * 100 / total_consumed_ + 0.5); + } + return percent_map; +} + +} // namespace power diff --git a/components/power/origin_power_map.h b/components/power/origin_power_map.h new file mode 100644 index 0000000000000..0936be46d8af9 --- /dev/null +++ b/components/power/origin_power_map.h @@ -0,0 +1,51 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_POWER_ORIGIN_POWER_MAP_H_ +#define COMPONENTS_POWER_ORIGIN_POWER_MAP_H_ + +#include + +#include "components/keyed_service/core/keyed_service.h" +#include "url/gurl.h" + +namespace power { + +// Tracks app and website origins and how much power they are consuming while +// running. +class OriginPowerMap : public KeyedService { + public: + typedef std::map PercentOriginMap; + + OriginPowerMap(); + virtual ~OriginPowerMap(); + + // Returns the integer percentage usage of the total power consumed by a + // given URL's origin. + int GetPowerForOrigin(const GURL& url); + + // Adds a certain amount of power consumption to a given URL's origin. + // |power| is a platform-specific heuristic estimating power consumption. + void AddPowerForOrigin(const GURL& url, double power); + + // Returns a map of all origins to the integer percentage usage of power + // consumed. + PercentOriginMap GetPercentOriginMap(); + + private: + // OriginMap maps a URL to the amount of power consumed by the URL using the + // same units as |total_consumed_|. + typedef std::map OriginMap; + OriginMap origin_map_; + + // Total amount of power consumed using units determined by + // the power heuristics available to the platform. + double total_consumed_; + + DISALLOW_COPY_AND_ASSIGN(OriginPowerMap); +}; + +} // namespace power + +#endif // COMPONENTS_POWER_ORIGIN_POWER_MAP_H_ diff --git a/components/power/origin_power_map_factory.cc b/components/power/origin_power_map_factory.cc new file mode 100644 index 0000000000000..8553aec96aa59 --- /dev/null +++ b/components/power/origin_power_map_factory.cc @@ -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. + +#include "components/power/origin_power_map_factory.h" + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "components/power/origin_power_map.h" + +namespace power { +// static +OriginPowerMap* OriginPowerMapFactory::GetForBrowserContext( + content::BrowserContext* context) { + return static_cast( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +OriginPowerMapFactory* OriginPowerMapFactory::GetInstance() { + return Singleton::get(); +} + +OriginPowerMapFactory::OriginPowerMapFactory() + : BrowserContextKeyedServiceFactory( + "OriginPowerMap", + BrowserContextDependencyManager::GetInstance()) { +} + +OriginPowerMapFactory::~OriginPowerMapFactory() { +} + +KeyedService* OriginPowerMapFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return new OriginPowerMap(); +} + +} // namespace power diff --git a/components/power/origin_power_map_factory.h b/components/power/origin_power_map_factory.h new file mode 100644 index 0000000000000..c772a2bc4a162 --- /dev/null +++ b/components/power/origin_power_map_factory.h @@ -0,0 +1,35 @@ +// 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_POWER_ORIGIN_POWER_MAP_FACTORY_H_ +#define COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace power { + +class OriginPowerMap; + +class OriginPowerMapFactory : public BrowserContextKeyedServiceFactory { + public: + static OriginPowerMap* GetForBrowserContext(content::BrowserContext* context); + static OriginPowerMapFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits; + + OriginPowerMapFactory(); + virtual ~OriginPowerMapFactory(); + + // BrowserContextKeyedServiceFactory: + virtual KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(OriginPowerMapFactory); +}; + +} // namespace power + +#endif // COMPONENTS_POWER_ORIGIN_POWER_MAP_FACTORY_H_ diff --git a/components/power/origin_power_map_unittest.cc b/components/power/origin_power_map_unittest.cc new file mode 100644 index 0000000000000..0cf2981a6a81a --- /dev/null +++ b/components/power/origin_power_map_unittest.cc @@ -0,0 +1,68 @@ +// 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/power/origin_power_map.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace power { + +TEST(OriginPowerMapTest, StartEmpty) { + OriginPowerMap origin_power_map; + EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size()); +} + +TEST(OriginPowerMapTest, AddOneOriginNotInMap) { + OriginPowerMap origin_power_map; + GURL url("http://www.google.com"); + EXPECT_EQ(0, origin_power_map.GetPowerForOrigin(url)); + origin_power_map.AddPowerForOrigin(url, 10); + EXPECT_EQ(size_t(1), origin_power_map.GetPercentOriginMap().size()); + EXPECT_EQ(100, origin_power_map.GetPowerForOrigin(url)); +} + +TEST(OriginPowerMapTest, AddMultiplesOrigins) { + OriginPowerMap origin_power_map; + GURL url1("http://www.google.com"); + EXPECT_EQ(0, origin_power_map.GetPowerForOrigin(url1)); + origin_power_map.AddPowerForOrigin(url1, 10); + EXPECT_EQ(size_t(1), origin_power_map.GetPercentOriginMap().size()); + EXPECT_EQ(100, origin_power_map.GetPowerForOrigin(url1)); + + GURL url2("http://www.example.com"); + origin_power_map.AddPowerForOrigin(url2, 30); + EXPECT_EQ(25, origin_power_map.GetPowerForOrigin(url1)); + EXPECT_EQ(75, origin_power_map.GetPowerForOrigin(url2)); + origin_power_map.AddPowerForOrigin(url2, 10); + EXPECT_EQ(20, origin_power_map.GetPowerForOrigin(url1)); + EXPECT_EQ(80, origin_power_map.GetPowerForOrigin(url2)); + + GURL url3("https://www.google.com"); + origin_power_map.AddPowerForOrigin(url3, 50); + EXPECT_EQ(10, origin_power_map.GetPowerForOrigin(url1)); + EXPECT_EQ(40, origin_power_map.GetPowerForOrigin(url2)); + EXPECT_EQ(50, origin_power_map.GetPowerForOrigin(url3)); +} + +TEST(OriginPowerMapTest, PercentOriginMap) { + OriginPowerMap origin_power_map; + GURL url1("http://www.google.com"); + GURL url2("http://www.example.com"); + origin_power_map.AddPowerForOrigin(url1, 10); + origin_power_map.AddPowerForOrigin(url2, 40); + OriginPowerMap::PercentOriginMap origin_map = + origin_power_map.GetPercentOriginMap(); + EXPECT_EQ(20, origin_map.find(url1)->second); + EXPECT_EQ(80, origin_map.find(url2)->second); +} + +TEST(OriginPowerMapTest, EmptyPercentOriginMapWhenZeroConsumed) { + OriginPowerMap origin_power_map; + EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size()); + GURL url("http://www.google.com"); + origin_power_map.AddPowerForOrigin(url, 0); + EXPECT_EQ(size_t(0), origin_power_map.GetPercentOriginMap().size()); +} + +} // namespace power diff --git a/components/renderer_context_menu.gypi b/components/renderer_context_menu.gypi index 90ad5bb22668a..dadcba3ba7a0f 100644 --- a/components/renderer_context_menu.gypi +++ b/components/renderer_context_menu.gypi @@ -25,6 +25,8 @@ 'renderer_context_menu/render_view_context_menu_observer.cc', 'renderer_context_menu/render_view_context_menu_observer.h', 'renderer_context_menu/render_view_context_menu_proxy.h', + 'renderer_context_menu/views/toolkit_delegate_views.cc', + 'renderer_context_menu/views/toolkit_delegate_views.h', ], }, ], diff --git a/components/renderer_context_menu/DEPS b/components/renderer_context_menu/DEPS index 49e84e68c4b85..9c9916cedcf4a 100644 --- a/components/renderer_context_menu/DEPS +++ b/components/renderer_context_menu/DEPS @@ -4,5 +4,7 @@ include_rules = [ "+extensions/browser", "+extensions/common", "+ui/base", + "+ui/gfx", + "+ui/views", "+third_party/WebKit/public/web", ] diff --git a/components/renderer_context_menu/render_view_context_menu_base.cc b/components/renderer_context_menu/render_view_context_menu_base.cc index 3d0f89a012d18..5cfa2d83f4446 100644 --- a/components/renderer_context_menu/render_view_context_menu_base.cc +++ b/components/renderer_context_menu/render_view_context_menu_base.cc @@ -161,9 +161,9 @@ RenderViewContextMenuBase::RenderViewContextMenuBase( source_web_contents_(WebContents::FromRenderFrameHost(render_frame_host)), browser_context_(source_web_contents_->GetBrowserContext()), menu_model_(this), + render_frame_id_(render_frame_host->GetRoutingID()), command_executed_(false), - render_process_id_(render_frame_host->GetProcess()->GetID()), - render_frame_id_(render_frame_host->GetRoutingID()) { + render_process_id_(render_frame_host->GetProcess()->GetID()) { } RenderViewContextMenuBase::~RenderViewContextMenuBase() { @@ -250,25 +250,31 @@ bool RenderViewContextMenuBase::AppendCustomItems() { return total_items > 0; } -// Menu delegate functions ----------------------------------------------------- - -bool RenderViewContextMenuBase::IsCommandIdEnabled(int id) const { +bool RenderViewContextMenuBase::IsCommandIdKnown( + int id, + bool* enabled) const { // If this command is is added by one of our observers, we dispatch // it to the observer. ObserverListBase::Iterator it(observers_); RenderViewContextMenuObserver* observer; while ((observer = it.GetNext()) != NULL) { - if (observer->IsCommandIdSupported(id)) - return observer->IsCommandIdEnabled(id); + if (observer->IsCommandIdSupported(id)) { + *enabled = observer->IsCommandIdEnabled(id); + return true; + } } // Custom items. - if (IsContentCustomCommandId(id)) - return IsCustomItemEnabled(id); + if (IsContentCustomCommandId(id)) { + *enabled = IsCustomItemEnabled(id); + return true; + } return false; } +// Menu delegate functions ----------------------------------------------------- + bool RenderViewContextMenuBase::IsCommandIdChecked(int id) const { // If this command is is added by one of our observers, we dispatch it to the // observer. diff --git a/components/renderer_context_menu/render_view_context_menu_base.h b/components/renderer_context_menu/render_view_context_menu_base.h index 9d4ac636c0fc3..db6c175c016a8 100644 --- a/components/renderer_context_menu/render_view_context_menu_base.h +++ b/components/renderer_context_menu/render_view_context_menu_base.h @@ -78,9 +78,13 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, const ui::SimpleMenuModel& menu_model() const { return menu_model_; } const content::ContextMenuParams& params() const { return params_; } + // Returns true if the specified command id is known and valid for + // this menu. If the command is known |enabled| is set to indicate + // if the command is enabled. + bool IsCommandIdKnown(int command_id, bool* enabled) const; + // SimpleMenuModel::Delegate implementation. virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; - virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; virtual void ExecuteCommand(int command_id, int event_flags) OVERRIDE; virtual void MenuWillShow(ui::SimpleMenuModel* source) OVERRIDE; virtual void MenuClosed(ui::SimpleMenuModel* source) OVERRIDE; @@ -162,6 +166,9 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, ui::SimpleMenuModel menu_model_; + // Renderer's frame id. + int render_frame_id_; + // Our observers. mutable ObserverList observers_; @@ -176,7 +183,6 @@ class RenderViewContextMenuBase : public ui::SimpleMenuModel::Delegate, // The RenderFrameHost's IDs. int render_process_id_; - int render_frame_id_; scoped_ptr toolkit_delegate_; diff --git a/components/renderer_context_menu/views/toolkit_delegate_views.cc b/components/renderer_context_menu/views/toolkit_delegate_views.cc new file mode 100644 index 0000000000000..f3d47a48f03ef --- /dev/null +++ b/components/renderer_context_menu/views/toolkit_delegate_views.cc @@ -0,0 +1,59 @@ +// 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/renderer_context_menu/views/toolkit_delegate_views.h" + +#include "ui/gfx/geometry/rect.h" +#include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/controls/menu/menu_model_adapter.h" +#include "ui/views/controls/menu/menu_runner.h" + +ToolkitDelegateViews::ToolkitDelegateViews() : menu_view_(NULL) {} + +ToolkitDelegateViews::~ToolkitDelegateViews() {} + +void ToolkitDelegateViews::RunMenuAt(views::Widget* parent, + const gfx::Point& point, + ui::MenuSourceType type) { + views::MenuAnchorPosition anchor_position = + (type == ui::MENU_SOURCE_TOUCH || + type == ui::MENU_SOURCE_TOUCH_EDIT_MENU) + ? views::MENU_ANCHOR_BOTTOMCENTER + : views::MENU_ANCHOR_TOPLEFT; + views::MenuRunner::RunResult result ALLOW_UNUSED = menu_runner_->RunMenuAt( + parent, NULL, gfx::Rect(point, gfx::Size()), anchor_position, type); +} + +void ToolkitDelegateViews::Init(ui::SimpleMenuModel* menu_model) { + menu_adapter_.reset(new views::MenuModelAdapter(menu_model)); + menu_view_ = menu_adapter_->CreateMenu(); + menu_runner_.reset(new views::MenuRunner( + menu_view_, + views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU)); +} + +void ToolkitDelegateViews::Cancel() { + DCHECK(menu_runner_.get()); + menu_runner_->Cancel(); +} + +void ToolkitDelegateViews::UpdateMenuItem(int command_id, + bool enabled, + bool hidden, + const base::string16& title) { + views::MenuItemView* item = menu_view_->GetMenuItemByID(command_id); + if (!item) + return; + + item->SetEnabled(enabled); + item->SetTitle(title); + item->SetVisible(!hidden); + + views::MenuItemView* parent = item->GetParentMenuItem(); + if (!parent) + return; + + parent->ChildrenChanged(); +} + diff --git a/components/renderer_context_menu/views/toolkit_delegate_views.h b/components/renderer_context_menu/views/toolkit_delegate_views.h new file mode 100644 index 0000000000000..ec6d2a09c1a13 --- /dev/null +++ b/components/renderer_context_menu/views/toolkit_delegate_views.h @@ -0,0 +1,53 @@ +// 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_RENDERER_CONTEXT_MENU_RENDER_TOOLKIT_DELEGATE_VIEWS_H_ +#define COMPONENTS_RENDERER_CONTEXT_MENU_RENDER_TOOLKIT_DELEGATE_VIEWS_H_ + +#include "components/renderer_context_menu/render_view_context_menu_base.h" +#include "ui/base/ui_base_types.h" + +namespace gfx { +class Point; +} + +namespace views { +class MenuItemView; +class MenuModelAdapter; +class MenuRunner; +class Widget; +} + +namespace ui { +class SimpleMenuModel; +} + +class ToolkitDelegateViews : public RenderViewContextMenuBase::ToolkitDelegate { + public: + ToolkitDelegateViews(); + virtual ~ToolkitDelegateViews(); + + void RunMenuAt(views::Widget* parent, + const gfx::Point& point, + ui::MenuSourceType type); + + private: + // ToolkitDelegate: + virtual void Init(ui::SimpleMenuModel* menu_model) OVERRIDE; + virtual void Cancel() OVERRIDE; + virtual void UpdateMenuItem(int command_id, + bool enabled, + bool hidden, + const base::string16& title) OVERRIDE; + + scoped_ptr menu_adapter_; + scoped_ptr menu_runner_; + + // Weak. Owned by menu_runner_; + views::MenuItemView* menu_view_; + + DISALLOW_COPY_AND_ASSIGN(ToolkitDelegateViews); +}; + +#endif // COMPONENTS_RENDERER_CONTEXT_MENU_RENDER_TOOLKIT_DELEGATE_VIEWS_H_ diff --git a/components/resources/dom_distiller_resources.grdp b/components/resources/dom_distiller_resources.grdp index 122b800e84ff1..45b70c62fc3ca 100644 --- a/components/resources/dom_distiller_resources.grdp +++ b/components/resources/dom_distiller_resources.grdp @@ -7,4 +7,5 @@ + diff --git a/components/search/search.cc b/components/search/search.cc index 8689e515b2f1a..d7e7276fcfade 100644 --- a/components/search/search.cc +++ b/components/search/search.cc @@ -35,6 +35,8 @@ const uint64 kEmbeddedSearchEnabledVersion = 2; const uint64 kEmbeddedPageVersionDefault = 2; #endif +const char kHideVerbatimFlagName[] = "hide_verbatim"; + // Constants for the field trial name and group prefix. // Note in M30 and below this field trial was named "InstantExtended" and in // M31 was renamed to EmbeddedSearch for clarity and cleanliness. Since we @@ -143,4 +145,10 @@ bool GetBoolValueForFlagWithDefault(const std::string& flag, return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags); } +bool ShouldHideTopVerbatimMatch() { + FieldTrialFlags flags; + return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( + kHideVerbatimFlagName, false, flags); +} + } // namespace chrome diff --git a/components/search/search.h b/components/search/search.h index df27e608350e1..6cc792772502e 100644 --- a/components/search/search.h +++ b/components/search/search.h @@ -54,6 +54,11 @@ bool GetBoolValueForFlagWithDefault(const std::string& flag, bool default_value, const FieldTrialFlags& flags); +// Returns true if 'hide_verbatim' flag is enabled in field trials +// to hide the top match in the native suggestions dropdown if it is a verbatim +// match. See comments on ShouldHideTopMatch in autocomplete_result.h. +bool ShouldHideTopVerbatimMatch(); + } // namespace chrome #endif // COMPONENTS_SEARCH_SEARCH_H_ diff --git a/components/search/search_unittest.cc b/components/search/search_unittest.cc index b8c4e1d65f49a..f4382cc1e7257 100644 --- a/components/search/search_unittest.cc +++ b/components/search/search_unittest.cc @@ -130,4 +130,36 @@ TEST_F(EmbeddedSearchFieldTrialTest, GetFieldTrialInfoControlFlags) { EXPECT_EQ(3ul, flags.size()); } +typedef EmbeddedSearchFieldTrialTest ShouldHideTopVerbatimTest; + +TEST_F(ShouldHideTopVerbatimTest, DoNotHideByDefault) { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch", + "Control")); + EXPECT_FALSE(ShouldHideTopVerbatimMatch()); +} + +TEST_F(ShouldHideTopVerbatimTest, DoNotHideInInstantExtended) { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch", + "Group1")); + EXPECT_FALSE(ShouldHideTopVerbatimMatch()); +} + +TEST_F(ShouldHideTopVerbatimTest, EnableByFlagInInstantExtended) { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch", + "Group1 hide_verbatim:1")); + EXPECT_TRUE(ShouldHideTopVerbatimMatch()); +} + +TEST_F(ShouldHideTopVerbatimTest, EnableByFlagOutsideInstantExtended) { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial( + "EmbeddedSearch", "Controll1 hide_verbatim:1")); + EXPECT_TRUE(ShouldHideTopVerbatimMatch()); +} + +TEST_F(ShouldHideTopVerbatimTest, DisableByFlag) { + ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch", + "Group1 hide_verbatim:0")); + EXPECT_FALSE(ShouldHideTopVerbatimMatch()); +} + } // namespace chrome diff --git a/components/signin/core/browser/about_signin_internals.cc b/components/signin/core/browser/about_signin_internals.cc index c567d8d4979c4..01f5782209078 100644 --- a/components/signin/core/browser/about_signin_internals.cc +++ b/components/signin/core/browser/about_signin_internals.cc @@ -18,7 +18,11 @@ #include "components/signin/core/browser/signin_manager.h" #include "components/signin/core/common/profile_management_switches.h" #include "components/signin/core/common/signin_switches.h" +#include "google_apis/gaia/gaia_auth_fetcher.h" +#include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_constants.h" +#include "google_apis/gaia/gaia_urls.h" +#include "net/cookies/canonical_cookie.h" using base::Time; using namespace signin_internals_util; @@ -51,6 +55,15 @@ void AddSectionEntry(base::ListValue* section_list, section_list->Append(entry.release()); } +void AddCookieEntry(base::ListValue* accounts_list, + const std::string& field_email, + const std::string& field_valid) { + scoped_ptr entry(new base::DictionaryValue()); + entry->SetString("email", field_email); + entry->SetString("valid", field_valid); + accounts_list->Append(entry.release()); +} + std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) { switch (field) { case USERNAME: @@ -195,11 +208,15 @@ void AboutSigninInternals::Initialize(SigninClient* client) { signin_manager_->AddSigninDiagnosticsObserver(this); token_service_->AddDiagnosticsObserver(this); + cookie_changed_subscription_ = client_->AddCookieChangedCallback( + base::Bind(&AboutSigninInternals::OnCookieChanged, + base::Unretained(this))); } void AboutSigninInternals::Shutdown() { signin_manager_->RemoveSigninDiagnosticsObserver(this); token_service_->RemoveDiagnosticsObserver(this); + cookie_changed_subscription_.reset(); } void AboutSigninInternals::NotifyObservers() { @@ -267,6 +284,65 @@ void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) { NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status); } +void AboutSigninInternals::OnCookieChanged( + const net::CanonicalCookie* cookie) { + if (cookie->Name() == "LSID" && + cookie->Domain() == GaiaUrls::GetInstance()->gaia_url().host() && + cookie->IsSecure() && + cookie->IsHttpOnly()) { + GetCookieAccountsAsync(); + } +} + +void AboutSigninInternals::GetCookieAccountsAsync() { + if (!gaia_fetcher_) { + // There is no list account request in flight. + gaia_fetcher_.reset(new GaiaAuthFetcher( + this, GaiaConstants::kChromeSource, client_->GetURLRequestContext())); + gaia_fetcher_->StartListAccounts(); + } +} + +void AboutSigninInternals::OnListAccountsSuccess(const std::string& data) { + gaia_fetcher_.reset(); + + // Get account information from response data. + std::vector > gaia_accounts; + bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts); + if (!valid_json) { + VLOG(1) << "AboutSigninInternals::OnListAccountsSuccess: parsing error"; + } else { + OnListAccountsComplete(gaia_accounts); + } +} + +void AboutSigninInternals::OnListAccountsFailure( + const GoogleServiceAuthError& error) { + gaia_fetcher_.reset(); + VLOG(1) << "AboutSigninInternals::OnListAccountsFailure:" << error.ToString(); +} + +void AboutSigninInternals::OnListAccountsComplete( + std::vector >& gaia_accounts) { + scoped_ptr signin_status(new base::DictionaryValue()); + base::ListValue* cookie_info = new base::ListValue(); + signin_status->Set("cookie_info", cookie_info); + + for (size_t i = 0; i < gaia_accounts.size(); ++i) { + AddCookieEntry(cookie_info, + gaia_accounts[i].first, + gaia_accounts[i].second ? "Valid" : "Invalid"); + } + + if (gaia_accounts.size() == 0) + AddCookieEntry(cookie_info, "No Accounts Present.", ""); + + // Update the observers that the cookie's accounts are updated. + FOR_EACH_OBSERVER(AboutSigninInternals::Observer, + signin_observers_, + OnCookieAccountsFetched(signin_status.Pass())); +} + AboutSigninInternals::TokenInfo::TokenInfo( const std::string& consumer_id, const OAuth2TokenService::ScopeSet& scopes) diff --git a/components/signin/core/browser/about_signin_internals.h b/components/signin/core/browser/about_signin_internals.h index e305ce7099a65..f3458219fa0f4 100644 --- a/components/signin/core/browser/about_signin_internals.h +++ b/components/signin/core/browser/about_signin_internals.h @@ -12,10 +12,13 @@ #include "base/observer_list.h" #include "base/values.h" #include "components/keyed_service/core/keyed_service.h" +#include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_internals_util.h" #include "components/signin/core/browser/signin_manager.h" +#include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/oauth2_token_service.h" +class GaiaAuthFetcher; class ProfileOAuth2TokenService; class SigninClient; class SigninManagerBase; @@ -29,7 +32,8 @@ typedef std::pair TimedSigninStatusValue; class AboutSigninInternals : public KeyedService, public signin_internals_util::SigninDiagnosticsObserver, - public OAuth2TokenService::DiagnosticsObserver { + public OAuth2TokenService::DiagnosticsObserver, + public GaiaAuthConsumer { public: class Observer { public: @@ -37,6 +41,10 @@ class AboutSigninInternals // in the comments for GetSigninStatus() below. virtual void OnSigninStateChanged( scoped_ptr info) = 0; + + // Notification that the cookie accounts are ready to be displayed. + virtual void OnCookieAccountsFetched( + scoped_ptr info) = 0; }; AboutSigninInternals(ProfileOAuth2TokenService* token_service, @@ -81,6 +89,10 @@ class AboutSigninInternals // } scoped_ptr GetSigninStatus(); + // Triggers a ListAccounts call to acquire a list of the email addresses + // corresponding to the cookies residing on the current cookie jar. + void GetCookieAccountsAsync(); + // OAuth2TokenService::DiagnosticsObserver implementations. virtual void OnAccessTokenRequested( const std::string& account_id, @@ -161,6 +173,21 @@ class AboutSigninInternals void NotifyObservers(); + + // Overriden from GaiaAuthConsumer. + virtual void OnListAccountsSuccess(const std::string& data) OVERRIDE; + virtual void OnListAccountsFailure(const GoogleServiceAuthError& error) + OVERRIDE; + + // Callback for ListAccounts. Once the email addresses are fetched from GAIA, + // they are pushed to the signin_internals_ui. + void OnListAccountsComplete( + std::vector >& gaia_accounts); + + // Called when a cookie changes. If the cookie relates to a GAIA LSID cookie, + // then we call ListAccounts and update the UI element. + void OnCookieChanged(const net::CanonicalCookie* cookie); + // Weak pointer to the token service. ProfileOAuth2TokenService* token_service_; @@ -170,12 +197,18 @@ class AboutSigninInternals // Weak pointer to the client. SigninClient* client_; + // Fetcher for information about accounts in the cookie jar from GAIA. + scoped_ptr gaia_fetcher_; + // Encapsulates the actual signin and token related values. // Most of the values are mirrored in the prefs for persistence. SigninStatus signin_status_; ObserverList signin_observers_; + scoped_ptr + cookie_changed_subscription_; + DISALLOW_COPY_AND_ASSIGN(AboutSigninInternals); }; diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc index 154c54bfa24b0..8b2932a24bf89 100644 --- a/components/signin/core/browser/account_reconcilor.cc +++ b/components/signin/core/browser/account_reconcilor.cc @@ -275,12 +275,12 @@ void AccountReconcilor::RegisterForCookieChanges() { // First clear any existing registration to avoid DCHECKs that can otherwise // go off in some embedders on reauth (e.g., ChromeSigninClient). UnregisterForCookieChanges(); - client_->SetCookieChangedCallback( + cookie_changed_subscription_ = client_->AddCookieChangedCallback( base::Bind(&AccountReconcilor::OnCookieChanged, base::Unretained(this))); } void AccountReconcilor::UnregisterForCookieChanges() { - client_->SetCookieChangedCallback(SigninClient::CookieChangedCallback()); + cookie_changed_subscription_.reset(); } void AccountReconcilor::RegisterWithSigninManager() { @@ -431,6 +431,8 @@ void AccountReconcilor::StartReconcile() { is_reconcile_started_ = true; + StartFetchingExternalCcResult(); + // Reset state for validating gaia cookie. are_gaia_accounts_set_ = false; gaia_accounts_.clear(); @@ -460,6 +462,10 @@ void AccountReconcilor::GetAccountsFromCookie( } } +void AccountReconcilor::StartFetchingExternalCcResult() { + merge_session_helper_.StartFetchingExternalCcResult(); +} + void AccountReconcilor::OnListAccountsSuccess(const std::string& data) { gaia_fetcher_.reset(); @@ -650,6 +656,8 @@ void AccountReconcilor::FinishReconcile() { // SignalComplete() will change the array. std::vector add_to_cookie_copy = add_to_cookie_; int added_to_cookie = 0; + bool external_cc_result_completed = + !merge_session_helper_.StillFetchingExternalCcResult(); for (size_t i = 0; i < add_to_cookie_copy.size(); ++i) { if (gaia_accounts_.end() != std::find_if(gaia_accounts_.begin(), @@ -666,6 +674,11 @@ void AccountReconcilor::FinishReconcile() { } } + // Log whether the external connection checks were completed when we tried + // to add the accounts to the cookie. + if (added_to_cookie > 0) + signin_metrics::LogExternalCcResultFetches(external_cc_result_completed); + std::string signin_scoped_device_id = client_->GetSigninScopedDeviceId(); // For each account in the gaia cookie not known to chrome, // PerformAddToChromeAction. Make a copy of |add_to_chrome| since calls to diff --git a/components/signin/core/browser/account_reconcilor.h b/components/signin/core/browser/account_reconcilor.h index da62b2c43fae7..8b87f2093829a 100644 --- a/components/signin/core/browser/account_reconcilor.h +++ b/components/signin/core/browser/account_reconcilor.h @@ -17,6 +17,7 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "components/keyed_service/core/keyed_service.h" +#include "components/signin/core/browser/signin_client.h" #include "components/signin/core/browser/signin_manager.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/google_service_auth_error.h" @@ -109,6 +110,8 @@ class AccountReconcilor : public KeyedService, return invalid_chrome_accounts_; } + // Virtual so that it can be overridden in tests. + virtual void StartFetchingExternalCcResult(); friend class AccountReconcilorTest; FRIEND_TEST_ALL_PREFIXES(AccountReconcilorTest, SigninManagerRegistration); @@ -259,6 +262,9 @@ class AccountReconcilor : public KeyedService, std::deque get_gaia_accounts_callbacks_; + scoped_ptr + cookie_changed_subscription_; + DISALLOW_COPY_AND_ASSIGN(AccountReconcilor); }; diff --git a/components/signin/core/browser/signin_client.h b/components/signin/core/browser/signin_client.h index 8668ab629781d..fafcada2b6eff 100644 --- a/components/signin/core/browser/signin_client.h +++ b/components/signin/core/browser/signin_client.h @@ -6,6 +6,8 @@ #define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_CLIENT_H_ #include "base/callback.h" +#include "base/callback_list.h" +#include "base/time/time.h" #include "components/keyed_service/core/keyed_service.h" #include "components/signin/core/browser/webdata/token_web_data.h" @@ -33,6 +35,9 @@ class SigninClient : public KeyedService { typedef base::Callback CookieChangedCallback; + typedef base::CallbackList + CookieChangedCallbackList; + virtual ~SigninClient() {} // Gets the preferences associated with the client. @@ -65,13 +70,12 @@ class SigninClient : public KeyedService { // Signin component is being used. virtual std::string GetProductVersion() = 0; - // Sets the callback that should be called when a cookie changes. The - // callback will be called only if it is not empty. + // Adds or removes a callback that should be called when a cookie changes. // TODO(blundell): Eliminate this interface in favor of having core signin // code observe cookie changes once //chrome/browser/net has been // componentized. - virtual void SetCookieChangedCallback( - const CookieChangedCallback& callback) = 0; + virtual scoped_ptr + AddCookieChangedCallback(const CookieChangedCallback& callback) = 0; // Called when Google signin has succeeded. virtual void GoogleSigninSucceeded(const std::string& username, @@ -82,6 +86,8 @@ class SigninClient : public KeyedService { virtual bool IsSigninProcess(int host_id) const = 0; virtual bool HasSigninProcess() const = 0; + virtual bool IsFirstRun() const = 0; + virtual base::Time GetInstallDate() = 0; #if defined(OS_IOS) // TODO(msarda): http://crbug.com/358544 Remove this iOS specific code from diff --git a/components/signin/core/browser/signin_manager.cc b/components/signin/core/browser/signin_manager.cc index 9c68eb98915ca..7358cbb7fbb8d 100644 --- a/components/signin/core/browser/signin_manager.cc +++ b/components/signin/core/browser/signin_manager.cc @@ -7,6 +7,7 @@ #include #include +#include "base/metrics/histogram.h" #include "base/prefs/pref_service.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" @@ -201,13 +202,24 @@ void SigninManager::SignOut( ClearTransientSigninData(); const std::string username = GetAuthenticatedUsername(); + const base::Time signin_time = + base::Time::FromInternalValue( + client_->GetPrefs()->GetInt64(prefs::kSignedInTime)); clear_authenticated_username(); client_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername); + client_->GetPrefs()->ClearPref(prefs::kSignedInTime); client_->ClearSigninScopedDeviceId(); // Erase (now) stale information from AboutSigninInternals. NotifyDiagnosticsObservers(USERNAME, ""); + // Determine the duration the user was logged in and log that to UMA. + if (!signin_time.is_null()) { + base::TimeDelta signed_in_duration = base::Time::Now() - signin_time; + UMA_HISTOGRAM_COUNTS("Signin.SignedInDurationBeforeSignout", + signed_in_duration.InMinutes()); + } + // Revoke all tokens before sending signed_out notification, because there // may be components that don't listen for token service events when the // profile is not connected to an account. @@ -353,6 +365,8 @@ void SigninManager::OnExternalSigninCompleted(const std::string& username) { } void SigninManager::OnSignedIn(const std::string& username) { + client_->GetPrefs()->SetInt64(prefs::kSignedInTime, + base::Time::Now().ToInternalValue()); SetAuthenticatedUsername(username); possibly_invalid_username_.clear(); @@ -363,6 +377,9 @@ void SigninManager::OnSignedIn(const std::string& username) { client_->GoogleSigninSucceeded(GetAuthenticatedUsername(), password_); + signin_metrics::LogSigninProfile(client_->IsFirstRun(), + client_->GetInstallDate()); + password_.clear(); // Don't need it anymore. DisableOneClickSignIn(client_->GetPrefs()); // Don't ever offer again. } diff --git a/components/signin/core/browser/signin_metrics.cc b/components/signin/core/browser/signin_metrics.cc index 5530d4cef40dd..ad78b64049776 100644 --- a/components/signin/core/browser/signin_metrics.cc +++ b/components/signin/core/browser/signin_metrics.cc @@ -51,6 +51,17 @@ void LogSigninAccountReconciliation(int total_number_accounts, } } +void LogSigninProfile(bool is_first_run, base::Time install_date) { + // Track whether or not the user signed in during the first run of Chrome. + UMA_HISTOGRAM_BOOLEAN("Signin.DuringFirstRun", is_first_run); + + // Determine how much time passed since install when this profile was signed + // in. + base::TimeDelta elapsed_time = base::Time::Now() - install_date; + UMA_HISTOGRAM_COUNTS("Signin.ElapsedTimeFromInstallToSignin", + elapsed_time.InMinutes()); +} + void LogSigninAddAccount() { // Account signin may fail for a wide variety of reasons. There is no // explicit false, but one can compare this value with the various UI @@ -64,4 +75,9 @@ void LogSignout(ProfileSignout metric) { NUM_PROFILE_SIGNOUT_METRICS); } +void LogExternalCcResultFetches(bool fetches_completed) { + UMA_HISTOGRAM_BOOLEAN("Signin.Reconciler.AllExternalCcResultCompleted", + fetches_completed); +} + } // namespace signin_metrics diff --git a/components/signin/core/browser/signin_metrics.h b/components/signin/core/browser/signin_metrics.h index cee56a26d8b10..3d8832e58b79b 100644 --- a/components/signin/core/browser/signin_metrics.h +++ b/components/signin/core/browser/signin_metrics.h @@ -5,6 +5,8 @@ #ifndef COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_METRICS_H_ #define COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_METRICS_H_ +#include "base/time/time.h" + namespace signin_metrics { // Enum for the ways in which primary account detection is done. @@ -70,9 +72,16 @@ void LogSigninAccountReconciliation(int total_number_accounts, // Track a successful signin. void LogSigninAddAccount(); +// Track a successful signin of a profile. +void LogSigninProfile(bool is_first_run, base::Time install_date); + // Track a profile signout. void LogSignout(ProfileSignout metric); +// Tracks whether the external connection results were all fetched before +// the reconcilor tried to use them with MergeSession. +void LogExternalCcResultFetches(bool fetches_completed); + } // namespace signin_metrics #endif // COMPONENTS_SIGNIN_CORE_BROWSER_SIGNIN_METRICS_H_ diff --git a/components/signin/core/browser/test_signin_client.cc b/components/signin/core/browser/test_signin_client.cc index d05c65b58fda1..ab63db9940654 100644 --- a/components/signin/core/browser/test_signin_client.cc +++ b/components/signin/core/browser/test_signin_client.cc @@ -52,6 +52,11 @@ net::URLRequestContextGetter* TestSigninClient::GetURLRequestContext() { return request_context_; } +void TestSigninClient::SetURLRequestContext( + net::URLRequestContextGetter* request_context) { + request_context_ = request_context; +} + std::string TestSigninClient::GetProductVersion() { return ""; } void TestSigninClient::LoadDatabase() { @@ -74,8 +79,11 @@ bool TestSigninClient::ShouldMergeSigninCredentialsIntoCookieJar() { return true; } -void TestSigninClient::SetCookieChangedCallback( - const CookieChangedCallback& callback) {} +scoped_ptr +TestSigninClient::AddCookieChangedCallback( + const SigninClient::CookieChangedCallback& callback) { + return cookie_callbacks_.Add(callback); +} #if defined(OS_IOS) ios::ProfileOAuth2TokenServiceIOSProvider* TestSigninClient::GetIOSProvider() { @@ -110,3 +118,11 @@ bool TestSigninClient::IsSigninProcess(int process_id) const { bool TestSigninClient::HasSigninProcess() const { return signin_host_id_ != kInvalidProcessId; } + +bool TestSigninClient::IsFirstRun() const { + return false; +} + +base::Time TestSigninClient::GetInstallDate() { + return base::Time::Now(); +} diff --git a/components/signin/core/browser/test_signin_client.h b/components/signin/core/browser/test_signin_client.h index 09da08f30d0d4..94240a067647f 100644 --- a/components/signin/core/browser/test_signin_client.h +++ b/components/signin/core/browser/test_signin_client.h @@ -50,9 +50,14 @@ class TestSigninClient : public SigninClient { // Returns the empty string. virtual std::string GetProductVersion() OVERRIDE; - // Returns a TestURLRequestContextGetter. + // Returns a TestURLRequestContextGetter or an manually provided + // URLRequestContextGetter. virtual net::URLRequestContextGetter* GetURLRequestContext() OVERRIDE; + // For testing purposes, can override the TestURLRequestContextGetter created + // in the default constructor. + void SetURLRequestContext(net::URLRequestContextGetter* request_context); + #if defined(OS_IOS) virtual ios::ProfileOAuth2TokenServiceIOSProvider* GetIOSProvider() OVERRIDE; #endif @@ -61,8 +66,8 @@ class TestSigninClient : public SigninClient { virtual bool ShouldMergeSigninCredentialsIntoCookieJar() OVERRIDE; // Does nothing. - virtual void SetCookieChangedCallback(const CookieChangedCallback& callback) - OVERRIDE; + virtual scoped_ptr + AddCookieChangedCallback(const CookieChangedCallback& callback) OVERRIDE; #if defined(OS_IOS) ios::FakeProfileOAuth2TokenServiceIOSProvider* GetIOSProviderAsFake(); @@ -73,15 +78,18 @@ class TestSigninClient : public SigninClient { virtual void ClearSigninProcess() OVERRIDE; virtual bool IsSigninProcess(int host_id) const OVERRIDE; virtual bool HasSigninProcess() const OVERRIDE; + virtual bool IsFirstRun() const OVERRIDE; + virtual base::Time GetInstallDate() OVERRIDE; private: // Loads the token database. void LoadDatabase(); base::ScopedTempDir temp_dir_; - scoped_refptr request_context_; + scoped_refptr request_context_; scoped_refptr database_; int signin_host_id_; + CookieChangedCallbackList cookie_callbacks_; PrefService* pref_service_; diff --git a/components/signin/core/common/profile_management_switches.cc b/components/signin/core/common/profile_management_switches.cc index c2cab31693ae6..e51c77e79dfb2 100644 --- a/components/signin/core/common/profile_management_switches.cc +++ b/components/signin/core/common/profile_management_switches.cc @@ -81,9 +81,9 @@ State GetProcessState() { } else if (is_new_avatar_menu) { return STATE_NEW_AVATAR_MENU; } else if (not_new_profile_management) { - return STATE_NEW_AVATAR_MENU; + return STATE_OLD_AVATAR_MENU; } else if (not_consistent_identity) { - return STATE_NEW_AVATAR_MENU; + return STATE_OLD_AVATAR_MENU; } // Set the default state @@ -98,6 +98,8 @@ State GetProcessState() { state = STATE_NEW_PROFILE_MANAGEMENT; } else if (trial_type == "AccountConsistency") { state = STATE_ACCOUNT_CONSISTENCY; + } else if (trial_type == "NewAvatarMenu") { + state = STATE_NEW_AVATAR_MENU; } else { state = STATE_OLD_AVATAR_MENU; } @@ -138,8 +140,7 @@ bool IsFastUserSwitching() { } bool IsGoogleProfileInfo() { - return IsNewAvatarMenu() || - CheckFlag(switches::kGoogleProfileInfo, STATE_OLD_AVATAR_MENU); + return CheckFlag(switches::kGoogleProfileInfo, STATE_NEW_AVATAR_MENU); } bool IsNewAvatarMenu() { @@ -160,6 +161,11 @@ void EnableNewAvatarMenuForTesting(base::CommandLine* command_line) { DCHECK(!command_line->HasSwitch(switches::kDisableNewAvatarMenu)); } +void DisableNewAvatarMenuForTesting(base::CommandLine* command_line) { + command_line->AppendSwitch(switches::kDisableNewAvatarMenu); + DCHECK(!command_line->HasSwitch(switches::kEnableNewAvatarMenu)); +} + void EnableNewProfileManagementForTesting(base::CommandLine* command_line) { command_line->AppendSwitch(switches::kEnableNewProfileManagement); DCHECK(!command_line->HasSwitch(switches::kDisableNewProfileManagement)); diff --git a/components/signin/core/common/profile_management_switches.h b/components/signin/core/common/profile_management_switches.h index 657ce81c8033b..13042953a72b3 100644 --- a/components/signin/core/common/profile_management_switches.h +++ b/components/signin/core/common/profile_management_switches.h @@ -44,6 +44,7 @@ bool IsNewProfileManagementPreviewEnabled(); // Called in tests to force enabling different modes. void EnableNewAvatarMenuForTesting(base::CommandLine* command_line); +void DisableNewAvatarMenuForTesting(base::CommandLine* command_line); void EnableNewProfileManagementForTesting(base::CommandLine* command_line); void EnableAccountConsistencyForTesting(base::CommandLine* command_line); diff --git a/components/signin/core/common/signin_pref_names.cc b/components/signin/core/common/signin_pref_names.cc index c90c08bdd02e2..b5d72275bb79f 100644 --- a/components/signin/core/common/signin_pref_names.cc +++ b/components/signin/core/common/signin_pref_names.cc @@ -43,6 +43,10 @@ const char kReverseAutologinEnabled[] = "reverse_autologin.enabled"; const char kReverseAutologinRejectedEmailList[] = "reverse_autologin.rejected_email_list"; +// Int64 which tracks, as time from epoch, when last time the user signed in +// to the browser. +const char kSignedInTime[] = "signin.signedin_time"; + // Boolean which stores if the user is allowed to signin to chrome. const char kSigninAllowed[] = "signin.allowed"; diff --git a/components/signin/core/common/signin_pref_names.h b/components/signin/core/common/signin_pref_names.h index 65e41a1ded994..5789ab13e1b88 100644 --- a/components/signin/core/common/signin_pref_names.h +++ b/components/signin/core/common/signin_pref_names.h @@ -15,6 +15,7 @@ extern const char kGoogleServicesUsernamePattern[]; extern const char kGoogleServicesSigninScopedDeviceId[]; extern const char kReverseAutologinEnabled[]; extern const char kReverseAutologinRejectedEmailList[]; +extern const char kSignedInTime[]; extern const char kSigninAllowed[]; } // namespace prefs diff --git a/components/strings/components_strings_sr.xtb b/components/strings/components_strings_sr.xtb index 8ababa47e54d7..e45672977511d 100644 --- a/components/strings/components_strings_sr.xtb +++ b/components/strings/components_strings_sr.xtb @@ -64,7 +64,7 @@ Онемогућен је Ниво Вредност је изван опсега . -Учитавање... +Учитава се... Департман Неисправан потпис Погрешан тип смерница diff --git a/components/suggestions.gypi b/components/suggestions.gypi index 0dae348d07599..f4820ec79edfd 100644 --- a/components/suggestions.gypi +++ b/components/suggestions.gypi @@ -31,6 +31,8 @@ 'suggestions/suggestions_service.h', 'suggestions/suggestions_store.cc', 'suggestions/suggestions_store.h', + 'suggestions/suggestions_utils.cc', + 'suggestions/suggestions_utils.h', ], 'variables': { 'proto_in_dir': 'suggestions/proto', diff --git a/components/suggestions/BUILD.gn b/components/suggestions/BUILD.gn index 1fe2ff9b32e83..d505c402a2f44 100644 --- a/components/suggestions/BUILD.gn +++ b/components/suggestions/BUILD.gn @@ -13,6 +13,8 @@ static_library("suggestions") { "suggestions_service.h", "suggestions_store.cc", "suggestions_store.h", + "suggestions_utils.cc", + "suggestions_utils.h", ] deps = [ diff --git a/components/suggestions/proto/suggestions.proto b/components/suggestions/proto/suggestions.proto index aaf5ef6ce813f..abbb58fa2bf29 100644 --- a/components/suggestions/proto/suggestions.proto +++ b/components/suggestions/proto/suggestions.proto @@ -42,6 +42,9 @@ message ChromeSuggestion { // The provider(s) responsible for this suggestion. repeated ProviderId providers = 5; + + // The timestamp (usec) at which this suggestion ceases to be valid. + optional int64 expiry_ts = 7; } // A list of URLs that should be filtered from the SuggestionsProfile. @@ -52,11 +55,11 @@ message SuggestionsBlacklist { repeated string urls = 1; } -// ThumbnailData contains the data to represent a website thumbnail. +// ImageData contains the data to represent a website image (e.g. thumbnail). // // Next tag: 3 -message ThumbnailData { - // The URL of the website represented by this Thumbnail. +message ImageData { + // The URL of the website represented by this image. optional string url = 1; // Bitmap bytes, encoded as JPEG. diff --git a/components/suggestions/suggestions_service.cc b/components/suggestions/suggestions_service.cc index aa4258f72acdd..b12aae1a1cc40 100644 --- a/components/suggestions/suggestions_service.cc +++ b/components/suggestions/suggestions_service.cc @@ -100,6 +100,9 @@ const char kSuggestionsFieldTrialControlParam[] = "control"; const char kSuggestionsFieldTrialStateEnabled[] = "enabled"; const char kSuggestionsFieldTrialTimeoutMs[] = "timeout_ms"; +// The default expiry timeout is 72 hours. +const int64 kDefaultExpiryUsec = 72 * base::Time::kMicrosecondsPerHour; + namespace { std::string GetBlacklistUrlPrefix() { @@ -153,8 +156,25 @@ bool SuggestionsService::IsControlGroup() { } void SuggestionsService::FetchSuggestionsData( + SyncState sync_state, SuggestionsService::ResponseCallback callback) { DCHECK(thread_checker_.CalledOnValidThread()); + if (sync_state == NOT_INITIALIZED_ENABLED) { + // Sync is not initialized yet, but enabled. Serve previously cached + // suggestions if available. + waiting_requestors_.push_back(callback); + ServeFromCache(); + return; + } else if (sync_state == SYNC_OR_HISTORY_SYNC_DISABLED) { + // Cancel any ongoing request (and the timeout closure). We must no longer + // interact with the server. + pending_request_.reset(NULL); + pending_timeout_closure_.reset(NULL); + suggestions_store_->ClearSuggestions(); + callback.Run(SuggestionsProfile()); + DispatchRequestsAndClear(SuggestionsProfile(), &waiting_requestors_); + return; + } FetchSuggestionsDataNoTimeout(callback); @@ -167,21 +187,6 @@ void SuggestionsService::FetchSuggestionsData( base::TimeDelta::FromMilliseconds(request_timeout_ms_)); } -void SuggestionsService::FetchSuggestionsDataNoTimeout( - SuggestionsService::ResponseCallback callback) { - DCHECK(thread_checker_.CalledOnValidThread()); - if (pending_request_.get()) { - // Request already exists, so just add requestor to queue. - waiting_requestors_.push_back(callback); - return; - } - - // Form new request. - DCHECK(waiting_requestors_.empty()); - waiting_requestors_.push_back(callback); - IssueRequest(suggestions_url_); -} - void SuggestionsService::GetPageThumbnail( const GURL& url, base::Callback callback) { @@ -232,6 +237,33 @@ void SuggestionsService::RegisterProfilePrefs( BlacklistStore::RegisterProfilePrefs(registry); } +void SuggestionsService::SetDefaultExpiryTimestamp( + SuggestionsProfile* suggestions, int64 default_timestamp_usec) { + for (int i = 0; i < suggestions->suggestions_size(); ++i) { + ChromeSuggestion* suggestion = suggestions->mutable_suggestions(i); + // Do not set expiry if the server has already provided a more specific + // expiry time for this suggestion. + if (!suggestion->has_expiry_ts()) { + suggestion->set_expiry_ts(default_timestamp_usec); + } + } +} + +void SuggestionsService::FetchSuggestionsDataNoTimeout( + SuggestionsService::ResponseCallback callback) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (pending_request_.get()) { + // Request already exists, so just add requestor to queue. + waiting_requestors_.push_back(callback); + return; + } + + // Form new request. + DCHECK(waiting_requestors_.empty()); + waiting_requestors_.push_back(callback); + IssueRequest(suggestions_url_); +} + void SuggestionsService::IssueRequest(const GURL& url) { pending_request_.reset(CreateSuggestionsRequest(url)); pending_request_->Start(); @@ -303,7 +335,7 @@ void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) { bool success = request->GetResponseAsString(&suggestions_data); DCHECK(success); - // Compute suggestions, and dispatch then to requestors. On error still + // Compute suggestions, and dispatch them to requestors. On error still // dispatch empty suggestions. SuggestionsProfile suggestions; if (suggestions_data.empty()) { @@ -312,10 +344,15 @@ void SuggestionsService::OnURLFetchComplete(const net::URLFetcher* source) { } else if (suggestions.ParseFromString(suggestions_data)) { LogResponseState(RESPONSE_VALID); thumbnail_manager_->Initialize(suggestions); + + int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) + .ToInternalValue(); + SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec); suggestions_store_->StoreSuggestions(suggestions); } else { LogResponseState(RESPONSE_INVALID); suggestions_store_->LoadSuggestions(&suggestions); + thumbnail_manager_->Initialize(suggestions); } FilterAndServe(&suggestions); @@ -333,6 +370,7 @@ void SuggestionsService::Shutdown() { void SuggestionsService::ServeFromCache() { SuggestionsProfile suggestions; suggestions_store_->LoadSuggestions(&suggestions); + thumbnail_manager_->Initialize(suggestions); FilterAndServe(&suggestions); } diff --git a/components/suggestions/suggestions_service.h b/components/suggestions/suggestions_service.h index ee1ed92ca6afa..e495f7f8aac23 100644 --- a/components/suggestions/suggestions_service.h +++ b/components/suggestions/suggestions_service.h @@ -19,6 +19,7 @@ #include "components/keyed_service/core/keyed_service.h" #include "components/suggestions/image_manager.h" #include "components/suggestions/proto/suggestions.pb.h" +#include "components/suggestions/suggestions_utils.h" #include "net/url_request/url_fetcher_delegate.h" #include "ui/gfx/image/image_skia.h" #include "url/gurl.h" @@ -44,6 +45,7 @@ extern const char kSuggestionsFieldTrialBlacklistUrlParam[]; extern const char kSuggestionsFieldTrialStateParam[]; extern const char kSuggestionsFieldTrialControlParam[]; extern const char kSuggestionsFieldTrialStateEnabled[]; +extern const int64 kDefaultExpiryUsec; // An interface to fetch server suggestions asynchronously. class SuggestionsService : public KeyedService, public net::URLFetcherDelegate { @@ -63,16 +65,20 @@ class SuggestionsService : public KeyedService, public net::URLFetcherDelegate { // Whether the user is part of a control group. static bool IsControlGroup(); - // Request suggestions data, which will be passed to |callback|. Initiates a - // fetch request unless a pending one exists. To prevent multiple requests, - // we place all |callback|s in a queue and update them simultaneously when - // fetch request completes. Also posts a task to execute OnRequestTimeout - // if the request hasn't completed in a given amount of time. - void FetchSuggestionsData(ResponseCallback callback); - - // Similar to FetchSuggestionsData but doesn't post a task to execute - // OnDelaySinceFetch. - void FetchSuggestionsDataNoTimeout(ResponseCallback callback); + // Request suggestions data, which will be passed to |callback|. |sync_state| + // will influence the behavior of this function (see SyncState definition). + // + // |sync_state| must be specified based on the current state of the system + // (see suggestions::GetSyncState). Callers should call this function again if + // sync state changes. + // + // If state allows for a network request, it is initiated unless a pending one + // exists. To prevent multiple requests, all |callback|s are placed in a queue + // and are updated simultaneously when the fetch completes. Also posts a task + // to execute OnRequestTimeout if the request hasn't completed in a given + // amount of time. + void FetchSuggestionsData(SyncState sync_state, + ResponseCallback callback); // Retrieves stored thumbnail for website |url| asynchronously. Calls // |callback| with Bitmap pointer if found, and NULL otherwise. @@ -92,11 +98,19 @@ class SuggestionsService : public KeyedService, public net::URLFetcherDelegate { // Register SuggestionsService related prefs in the Profile prefs. static void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + // Sets default timestamp for suggestions which do not have expiry timestamp. + void SetDefaultExpiryTimestamp(SuggestionsProfile* suggestions, + int64 timestamp_usec); private: + friend class SuggestionsServiceTest; FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, BlacklistURLFails); FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, FetchSuggestionsData); FRIEND_TEST_ALL_PREFIXES(SuggestionsServiceTest, UpdateBlacklistDelay); + // Similar to FetchSuggestionsData but doesn't post a task to execute + // OnDelaySinceFetch. + void FetchSuggestionsDataNoTimeout(ResponseCallback callback); + // Issue a request. void IssueRequest(const GURL& url); diff --git a/components/suggestions/suggestions_service_unittest.cc b/components/suggestions/suggestions_service_unittest.cc index c6d888400824b..a4d60585866e9 100644 --- a/components/suggestions/suggestions_service_unittest.cc +++ b/components/suggestions/suggestions_service_unittest.cc @@ -18,6 +18,7 @@ #include "components/suggestions/image_manager.h" #include "components/suggestions/proto/suggestions.pb.h" #include "components/suggestions/suggestions_store.h" +#include "components/suggestions/suggestions_utils.h" #include "components/variations/entropy_provider.h" #include "components/variations/variations_associated_data.h" #include "net/http/http_response_headers.h" @@ -46,6 +47,8 @@ const char kFakeBlacklistUrlParam[] = "baz"; const char kTestTitle[] = "a title"; const char kTestUrl[] = "http://go.com"; const char kBlacklistUrl[] = "http://blacklist.com"; +const int64 kTestDefaultExpiry = 1402200000000000; +const int64 kTestSetExpiry = 1404792000000000; scoped_ptr CreateURLFetcher( const GURL& url, net::URLFetcherDelegate* delegate, @@ -91,9 +94,25 @@ scoped_ptr CreateSuggestionsProfile() { ChromeSuggestion* suggestion = profile->add_suggestions(); suggestion->set_title(kTestTitle); suggestion->set_url(kTestUrl); + suggestion->set_expiry_ts(kTestSetExpiry); return profile.Pass(); } +// Creates one suggestion with expiry timestamp and one without. +SuggestionsProfile CreateSuggestionsProfileWithExpiryTimestamps() { + SuggestionsProfile profile; + ChromeSuggestion* suggestion = profile.add_suggestions(); + suggestion->set_title(kTestTitle); + suggestion->set_url(kTestUrl); + suggestion->set_expiry_ts(kTestSetExpiry); + + suggestion = profile.add_suggestions(); + suggestion->set_title(kTestTitle); + suggestion->set_url(kTestUrl); + + return profile; +} + class MockSuggestionsStore : public suggestions::SuggestionsStore { public: MOCK_METHOD1(LoadSuggestions, bool(SuggestionsProfile*)); @@ -185,7 +204,7 @@ class SuggestionsServiceTest : public testing::Test { // SuggestionsStore in |mock_suggestions_store_|. SuggestionsService* CreateSuggestionsServiceWithMocks() { mock_suggestions_store_ = new StrictMock(); - mock_thumbnail_manager_ = new NiceMock(); + mock_thumbnail_manager_ = new StrictMock(); mock_blacklist_store_ = new MockBlacklistStore(); return new SuggestionsService( request_context_, scoped_ptr(mock_suggestions_store_), @@ -202,14 +221,12 @@ class SuggestionsServiceTest : public testing::Test { EXPECT_TRUE(suggestions_service != NULL); scoped_ptr suggestions_profile( CreateSuggestionsProfile()); - // Set up net::FakeURLFetcherFactory. std::string expected_url = (std::string(kFakeSuggestionsURL) + "?") + kFakeSuggestionsCommonParams; factory_.SetFakeResponse(GURL(expected_url), suggestions_profile->SerializeAsString(), net::HTTP_OK, net::URLRequestStatus::SUCCESS); - // Set up expectations on the SuggestionsStore. The number depends on // whether the second request is issued (it won't be issued if the second // fetch occurs before the first request has completed). @@ -219,6 +236,11 @@ class SuggestionsServiceTest : public testing::Test { .Times(expected_count) .WillRepeatedly(Return(true)); + // Since there are two requests below, Initialize() will be called twice. + EXPECT_CALL(*mock_thumbnail_manager_, + Initialize(EqualsProto(*suggestions_profile))) + .Times(expected_count); + // Expect a call to the blacklist store. Return that there's nothing to // blacklist. EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)) @@ -295,6 +317,7 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataRequestError) { // Set up expectations on the SuggestionsStore. EXPECT_CALL(*mock_suggestions_store_, LoadSuggestions(_)) .WillOnce(Return(true)); + EXPECT_CALL(*mock_thumbnail_manager_, Initialize(_)); // Expect a call to the blacklist store. Return that there's nothing to // blacklist. @@ -304,6 +327,7 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataRequestError) { // Send the request. Empty data will be returned to the callback. suggestions_service->FetchSuggestionsData( + INITIALIZED_ENABLED_HISTORY, // Normal mode. base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile, base::Unretained(this))); @@ -339,6 +363,7 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataResponseNotOK) { // Send the request. Empty data will be returned to the callback. suggestions_service->FetchSuggestionsData( + INITIALIZED_ENABLED_HISTORY, // Normal mode. base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile, base::Unretained(this))); @@ -349,6 +374,62 @@ TEST_F(SuggestionsServiceTest, FetchSuggestionsDataResponseNotOK) { EXPECT_EQ(1, suggestions_empty_data_count_); } +TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncDisabled) { + // Field trial enabled with a specific suggestions URL. + EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams, + kFakeBlacklistPath, kFakeBlacklistUrlParam, false); + scoped_ptr suggestions_service( + CreateSuggestionsServiceWithMocks()); + EXPECT_TRUE(suggestions_service != NULL); + + // Set up expectations on the SuggestionsStore. + EXPECT_CALL(*mock_suggestions_store_, ClearSuggestions()); + + // Send the request. Cache is cleared and empty data will be returned to the + // callback. + suggestions_service->FetchSuggestionsData( + SYNC_OR_HISTORY_SYNC_DISABLED, + base::Bind(&SuggestionsServiceTest::ExpectEmptySuggestionsProfile, + base::Unretained(this))); + + // Wait for posted task to complete. + base::MessageLoop::current()->RunUntilIdle(); + + // Ensure that ExpectEmptySuggestionsProfile ran once. + EXPECT_EQ(1, suggestions_empty_data_count_); +} + +TEST_F(SuggestionsServiceTest, FetchSuggestionsDataSyncNotInitializedEnabled) { + // Field trial enabled with a specific suggestions URL. + EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams, + kFakeBlacklistPath, kFakeBlacklistUrlParam, false); + scoped_ptr suggestions_service( + CreateSuggestionsServiceWithMocks()); + EXPECT_TRUE(suggestions_service != NULL); + scoped_ptr suggestions_profile( + CreateSuggestionsProfile()); + + // Expectations. + EXPECT_CALL(*mock_suggestions_store_, LoadSuggestions(_)) + .WillOnce(DoAll(SetArgPointee<0>(*suggestions_profile), Return(true))); + EXPECT_CALL(*mock_thumbnail_manager_, + Initialize(EqualsProto(*suggestions_profile))); + EXPECT_CALL(*mock_blacklist_store_, FilterSuggestions(_)); + + // Send the request. In this state, cached data will be returned to the + // caller. + suggestions_service->FetchSuggestionsData( + NOT_INITIALIZED_ENABLED, + base::Bind(&SuggestionsServiceTest::CheckSuggestionsData, + base::Unretained(this))); + + // Wait for posted task to complete. + base::MessageLoop::current()->RunUntilIdle(); + + // Ensure that CheckSuggestionsData ran once. + EXPECT_EQ(1, suggestions_data_check_count_); +} + TEST_F(SuggestionsServiceTest, BlacklistURL) { EnableFieldTrial(kFakeSuggestionsURL, kFakeSuggestionsCommonParams, kFakeBlacklistPath, kFakeBlacklistUrlParam, false); @@ -368,6 +449,8 @@ TEST_F(SuggestionsServiceTest, BlacklistURL) { EXPECT_CALL(*mock_suggestions_store_, StoreSuggestions(EqualsProto(*suggestions_profile))) .WillOnce(Return(true)); + EXPECT_CALL(*mock_thumbnail_manager_, + Initialize(EqualsProto(*suggestions_profile))); // Expected calls to the blacklist store. EXPECT_CALL(*mock_blacklist_store_, BlacklistUrl(Eq(blacklist_url))) @@ -427,6 +510,10 @@ TEST_F(SuggestionsServiceTest, BlacklistURLFails) { .WillOnce(Return(true)) .WillOnce(DoAll(SetArgPointee<0>(blacklist_url), Return(true))) .WillOnce(Return(false)); + // There will be two calls to Initialize() (one store, one load). + EXPECT_CALL(*mock_thumbnail_manager_, + Initialize(EqualsProto(*suggestions_profile))) + .Times(2); // Send the request. The data will be returned to the callback. suggestions_service->BlacklistURL( @@ -497,4 +584,14 @@ TEST_F(SuggestionsServiceTest, UpdateBlacklistDelay) { EXPECT_EQ(initial_delay, suggestions_service->blacklist_delay()); } +TEST_F(SuggestionsServiceTest, CheckDefaultTimeStamps) { + scoped_ptr suggestions_service( + CreateSuggestionsServiceWithMocks()); + SuggestionsProfile suggestions = + CreateSuggestionsProfileWithExpiryTimestamps(); + suggestions_service->SetDefaultExpiryTimestamp(&suggestions, + kTestDefaultExpiry); + EXPECT_EQ(kTestSetExpiry, suggestions.suggestions(0).expiry_ts()); + EXPECT_EQ(kTestDefaultExpiry, suggestions.suggestions(1).expiry_ts()); +} } // namespace suggestions diff --git a/components/suggestions/suggestions_store.cc b/components/suggestions/suggestions_store.cc index 1d1147b342c12..01c8e97050cf9 100644 --- a/components/suggestions/suggestions_store.cc +++ b/components/suggestions/suggestions_store.cc @@ -8,6 +8,7 @@ #include "base/base64.h" #include "base/prefs/pref_service.h" +#include "base/time/time.h" #include "components/pref_registry/pref_registry_syncable.h" #include "components/suggestions/suggestions_pref_names.h" @@ -40,9 +41,38 @@ bool SuggestionsStore::LoadSuggestions(SuggestionsProfile* suggestions) { return false; } + // Filter expired suggestions and update the stored suggestions if at least + // one was filtered. Return false if all suggestions are filtered. + int unfiltered_size = suggestions->suggestions_size(); + FilterExpiredSuggestions(suggestions); + if (suggestions->suggestions_size() != unfiltered_size) { + if (!suggestions->suggestions_size()) { + suggestions->Clear(); + ClearSuggestions(); + return false; + } else { + StoreSuggestions(*suggestions); + } + } + return true; } +void SuggestionsStore::FilterExpiredSuggestions( + SuggestionsProfile* suggestions) { + SuggestionsProfile filtered_suggestions; + int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) + .ToInternalValue(); + + for (int i = 0; i < suggestions->suggestions_size(); ++i) { + ChromeSuggestion* suggestion = suggestions->mutable_suggestions(i); + if (!suggestion->has_expiry_ts() || suggestion->expiry_ts() > now_usec) { + filtered_suggestions.add_suggestions()->Swap(suggestion); + } + } + suggestions->Swap(&filtered_suggestions); +} + bool SuggestionsStore::StoreSuggestions(const SuggestionsProfile& suggestions) { std::string suggestions_data; if (!suggestions.SerializeToString(&suggestions_data)) return false; diff --git a/components/suggestions/suggestions_store.h b/components/suggestions/suggestions_store.h index c39f6620077e2..78ff7f6b17cb9 100644 --- a/components/suggestions/suggestions_store.h +++ b/components/suggestions/suggestions_store.h @@ -48,6 +48,9 @@ class SuggestionsStore { PrefService* pref_service_; DISALLOW_COPY_AND_ASSIGN(SuggestionsStore); + + // Filters expired suggestions. + void FilterExpiredSuggestions(SuggestionsProfile* suggestions); }; } // namespace suggestions diff --git a/components/suggestions/suggestions_store_unittest.cc b/components/suggestions/suggestions_store_unittest.cc index 08930133f01ad..1b7ebfc88e0ba 100644 --- a/components/suggestions/suggestions_store_unittest.cc +++ b/components/suggestions/suggestions_store_unittest.cc @@ -4,6 +4,7 @@ #include "components/suggestions/suggestions_store.h" +#include "base/time/time.h" #include "components/pref_registry/testing_pref_service_syncable.h" #include "components/suggestions/proto/suggestions.pb.h" #include "testing/gtest/include/gtest/gtest.h" @@ -16,12 +17,38 @@ namespace { const char kTestTitle[] = "Foo site"; const char kTestUrl[] = "http://foo.com/"; +const int kTimeGapUsec = 100000; + +void AddSuggestion(SuggestionsProfile* suggestions, const char *title, + const char *url, int64 expiry_ts) { + ChromeSuggestion* suggestion = suggestions->add_suggestions(); + suggestion->set_url(title); + suggestion->set_title(url); + suggestion->set_expiry_ts(expiry_ts); +} SuggestionsProfile CreateTestSuggestions() { SuggestionsProfile suggestions; ChromeSuggestion* suggestion = suggestions.add_suggestions(); - suggestion->set_url(kTestUrl); - suggestion->set_title(kTestTitle); + suggestion->set_url(kTestTitle); + suggestion->set_title(kTestUrl); + return suggestions; +} + +SuggestionsProfile CreateTestSuggestionsProfileWithExpiry(int expired_count, + int valid_count) { + int64 now_usec = (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) + .ToInternalValue(); + srand(7); // Constant seed for rand() function. + int64 offset_limit_usec = 30 * base::Time::kMicrosecondsPerDay; + int64 offset_usec = rand() % offset_limit_usec + kTimeGapUsec; + + SuggestionsProfile suggestions; + for (int i = 0; i < valid_count; i++) + AddSuggestion(&suggestions, kTestTitle, kTestUrl, now_usec + offset_usec); + for (int i = 0; i < expired_count; i++) + AddSuggestion(&suggestions, kTestTitle, kTestUrl, now_usec - offset_usec); + return suggestions; } @@ -31,6 +58,8 @@ void ValidateSuggestions(const SuggestionsProfile& expected, for (int i = 0; i < expected.suggestions_size(); ++i) { EXPECT_EQ(expected.suggestions(i).url(), actual.suggestions(i).url()); EXPECT_EQ(expected.suggestions(i).title(), actual.suggestions(i).title()); + EXPECT_EQ(expected.suggestions(i).expiry_ts(), + actual.suggestions(i).expiry_ts()); EXPECT_EQ(expected.suggestions(i).favicon_url(), actual.suggestions(i).favicon_url()); EXPECT_EQ(expected.suggestions(i).thumbnail(), @@ -40,27 +69,77 @@ void ValidateSuggestions(const SuggestionsProfile& expected, } // namespace -TEST(SuggestionsStoreTest, LoadStoreClear) { - TestingPrefServiceSyncable prefs; - SuggestionsStore::RegisterProfilePrefs(prefs.registry()); - SuggestionsStore suggestions_store(&prefs); +class SuggestionsStoreTest : public testing::Test { + public: + SuggestionsStoreTest() + : pref_service_(new user_prefs::TestingPrefServiceSyncable) {} + + virtual void SetUp() OVERRIDE { + SuggestionsStore::RegisterProfilePrefs(pref_service_->registry()); + suggestions_store_.reset(new SuggestionsStore(pref_service_.get())); + } + + protected: + scoped_ptr pref_service_; + scoped_ptr suggestions_store_; + + DISALLOW_COPY_AND_ASSIGN(SuggestionsStoreTest); +}; + +// Tests LoadSuggestions function to filter expired suggestions. +TEST_F(SuggestionsStoreTest, LoadAllExpired) { + SuggestionsProfile suggestions = CreateTestSuggestionsProfileWithExpiry(5, 0); + SuggestionsProfile filtered_suggestions; + + // Store and load. Expired suggestions should not be loaded. + EXPECT_TRUE(suggestions_store_->StoreSuggestions(suggestions)); + EXPECT_FALSE(suggestions_store_->LoadSuggestions(&filtered_suggestions)); + EXPECT_EQ(0, filtered_suggestions.suggestions_size()); +} + +// Tests LoadSuggestions function to filter expired suggestions. +TEST_F(SuggestionsStoreTest, LoadValidAndExpired) { + SuggestionsProfile suggestions = CreateTestSuggestionsProfileWithExpiry(5, 3); + SuggestionsProfile filtered_suggestions; + + // Store and load. Expired suggestions should not be loaded. + EXPECT_TRUE(suggestions_store_->StoreSuggestions(suggestions)); + EXPECT_TRUE(suggestions_store_->LoadSuggestions(&filtered_suggestions)); + EXPECT_EQ(3, filtered_suggestions.suggestions_size()); +} + +// Tests LoadSuggestions function to filter expired suggestions. +TEST_F(SuggestionsStoreTest, CheckStoreAfterLoadExpired) { + SuggestionsProfile suggestions = CreateTestSuggestionsProfileWithExpiry(5, 3); + SuggestionsProfile filtered_suggestions; + + // Store and load. Expired suggestions should not be loaded. + EXPECT_TRUE(suggestions_store_->StoreSuggestions(suggestions)); + EXPECT_TRUE(suggestions_store_->LoadSuggestions(&filtered_suggestions)); + + SuggestionsProfile loaded_suggestions; + EXPECT_TRUE(suggestions_store_->LoadSuggestions(&loaded_suggestions)); + EXPECT_EQ(3, loaded_suggestions.suggestions_size()); + ValidateSuggestions(filtered_suggestions, loaded_suggestions); +} +TEST_F(SuggestionsStoreTest, LoadStoreClear) { const SuggestionsProfile suggestions = CreateTestSuggestions(); const SuggestionsProfile empty_suggestions; SuggestionsProfile recovered_suggestions; // Attempt to load when prefs are empty. - EXPECT_FALSE(suggestions_store.LoadSuggestions(&recovered_suggestions)); + EXPECT_FALSE(suggestions_store_->LoadSuggestions(&recovered_suggestions)); ValidateSuggestions(empty_suggestions, recovered_suggestions); // Store then reload. - EXPECT_TRUE(suggestions_store.StoreSuggestions(suggestions)); - EXPECT_TRUE(suggestions_store.LoadSuggestions(&recovered_suggestions)); + EXPECT_TRUE(suggestions_store_->StoreSuggestions(suggestions)); + EXPECT_TRUE(suggestions_store_->LoadSuggestions(&recovered_suggestions)); ValidateSuggestions(suggestions, recovered_suggestions); // Clear. - suggestions_store.ClearSuggestions(); - EXPECT_FALSE(suggestions_store.LoadSuggestions(&recovered_suggestions)); + suggestions_store_->ClearSuggestions(); + EXPECT_FALSE(suggestions_store_->LoadSuggestions(&recovered_suggestions)); ValidateSuggestions(empty_suggestions, recovered_suggestions); } diff --git a/components/suggestions/suggestions_utils.cc b/components/suggestions/suggestions_utils.cc new file mode 100644 index 0000000000000..9c327917cfadc --- /dev/null +++ b/components/suggestions/suggestions_utils.cc @@ -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. + +#include "components/suggestions/suggestions_utils.h" + +namespace suggestions { + +SyncState GetSyncState(bool sync_enabled, + bool sync_initialized, + bool history_sync_enabled) { + if (!sync_enabled) + return SYNC_OR_HISTORY_SYNC_DISABLED; + + if (!sync_initialized) + return NOT_INITIALIZED_ENABLED; + + return history_sync_enabled ? + INITIALIZED_ENABLED_HISTORY : SYNC_OR_HISTORY_SYNC_DISABLED; +} + +} // namespace suggestions diff --git a/components/suggestions/suggestions_utils.h b/components/suggestions/suggestions_utils.h new file mode 100644 index 0000000000000..eedef03dfb3a3 --- /dev/null +++ b/components/suggestions/suggestions_utils.h @@ -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. + +#ifndef COMPONENTS_SUGGESTIONS_SUGGESTIONS_UTILS_H_ +#define COMPONENTS_SUGGESTIONS_SUGGESTIONS_UTILS_H_ + +namespace suggestions { + +// Establishes the different sync states that users of SuggestionsService can +// specify. There are three different concepts in the sync service: initialized, +// sync enabled and history sync enabled. +enum SyncState { + // State: Sync service is not initialized, yet not disabled. History sync + // state is unknown (since not initialized). + // Behavior: Does not issue a server request, but serves from cache if + // available. + NOT_INITIALIZED_ENABLED, + + // State: Sync service is initialized, sync is enabled and history sync is + // enabled. + // Behavior: Update suggestions from the server. Serve from cache on timeout. + INITIALIZED_ENABLED_HISTORY, + + // State: Sync service is disabled or history sync is disabled. + // Behavior: Do not issue a server request. Clear the cache. Serve empty + // suggestions. + SYNC_OR_HISTORY_SYNC_DISABLED, +}; + +// Users of SuggestionsService should always use this function to get SyncState. +SyncState GetSyncState(bool sync_enabled, + bool sync_initialized, + bool history_sync_enabled); + +} // namespace suggestions + +#endif // COMPONENTS_SUGGESTIONS_SUGGESTIONS_UTILS_H_ diff --git a/components/sync_driver/change_processor_mock.h b/components/sync_driver/change_processor_mock.h index aaa7e82d32dd7..7acf83f0b9ba3 100644 --- a/components/sync_driver/change_processor_mock.h +++ b/components/sync_driver/change_processor_mock.h @@ -25,9 +25,8 @@ class ChangeProcessorMock MOCK_CONST_METHOD0(IsRunning, bool()); MOCK_METHOD2(OnUnrecoverableError, void(const tracked_objects::Location&, const std::string&)); - MOCK_METHOD2(OnSingleDatatypeUnrecoverableError, - void(const tracked_objects::Location&, - const std::string&)); + MOCK_METHOD1(OnSingleDataTypeUnrecoverableError, + void(const syncer::SyncError&)); MOCK_METHOD3(CreateAndUploadError, syncer::SyncError(const tracked_objects::Location&, const std::string&, diff --git a/components/sync_driver/data_type_controller.cc b/components/sync_driver/data_type_controller.cc index bfabe75894f48..db26151946f9b 100644 --- a/components/sync_driver/data_type_controller.cc +++ b/components/sync_driver/data_type_controller.cc @@ -12,22 +12,20 @@ namespace sync_driver { DataTypeController::DataTypeController( scoped_refptr ui_thread, - const base::Closure& error_callback, - const DisableTypeCallback& disable_callback) + const base::Closure& error_callback) : base::RefCountedDeleteOnMessageLoop(ui_thread), error_callback_(error_callback), - disable_callback_(disable_callback), user_share_(NULL) { } DataTypeController::~DataTypeController() { } -bool DataTypeController::IsUnrecoverableResult(StartResult result) { +bool DataTypeController::IsUnrecoverableResult(ConfigureResult result) { return (result == UNRECOVERABLE_ERROR); } -bool DataTypeController::IsSuccessfulResult(StartResult result) { +bool DataTypeController::IsSuccessfulResult(ConfigureResult result) { return (result == OK || result == OK_FIRST_RUN); } @@ -51,11 +49,6 @@ syncer::UserShare* DataTypeController::user_share() const { return user_share_; } -DataTypeController::DisableTypeCallback -DataTypeController::disable_callback() { - return disable_callback_; -} - bool DataTypeController::ReadyForStart() const { return true; } diff --git a/components/sync_driver/data_type_controller.h b/components/sync_driver/data_type_controller.h index c1e8a05e71e62..a673b1f473d1b 100644 --- a/components/sync_driver/data_type_controller.h +++ b/components/sync_driver/data_type_controller.h @@ -51,7 +51,7 @@ class DataTypeController // so it is disabled waiting for it to be stopped. }; - enum StartResult { + enum ConfigureResult { OK, // The data type has started normally. OK_FIRST_RUN, // Same as OK, but sent on first successful // start for this type for this user as @@ -61,10 +61,11 @@ class DataTypeController UNRECOVERABLE_ERROR, // An unrecoverable error occured. NEEDS_CRYPTO, // The data type cannot be started yet because it // depends on the cryptographer. + RUNTIME_ERROR, // After starting, a runtime error was encountered. MAX_START_RESULT }; - typedef base::Callback StartCallback; @@ -80,10 +81,10 @@ class DataTypeController // Returns true if the start result should trigger an unrecoverable error. // Public so unit tests can use this function as well. - static bool IsUnrecoverableResult(StartResult result); + static bool IsUnrecoverableResult(ConfigureResult result); // Returns true if the datatype started successfully. - static bool IsSuccessfulResult(StartResult result); + static bool IsSuccessfulResult(ConfigureResult result); // Begins asynchronous operation of loading the model to get it ready for // model association. Once the models are loaded the callback will be invoked @@ -148,8 +149,7 @@ class DataTypeController friend class base::DeleteHelper; DataTypeController(scoped_refptr ui_thread, - const base::Closure& error_callback, - const DisableTypeCallback& disable_callback); + const base::Closure& error_callback); // If the DTC is waiting for models to load, once the models are // loaded the datatype service will call this function on DTC to let @@ -159,16 +159,12 @@ class DataTypeController virtual ~DataTypeController(); syncer::UserShare* user_share() const; - DisableTypeCallback disable_callback(); // The callback that will be invoked when an unrecoverable error occurs. // TODO(sync): protected for use by legacy controllers. base::Closure error_callback_; private: - // TODO(tim): Bug 383480. Do we need two callbacks? - DisableTypeCallback disable_callback_; - syncer::UserShare* user_share_; }; diff --git a/components/sync_driver/data_type_controller_mock.h b/components/sync_driver/data_type_controller_mock.h index 57fe8ece31554..acc54b9f2a33c 100644 --- a/components/sync_driver/data_type_controller_mock.h +++ b/components/sync_driver/data_type_controller_mock.h @@ -16,7 +16,7 @@ class StartCallbackMock { StartCallbackMock(); virtual ~StartCallbackMock(); - MOCK_METHOD3(Run, void(DataTypeController::StartResult result, + MOCK_METHOD3(Run, void(DataTypeController::ConfigureResult result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result)); }; diff --git a/components/sync_driver/data_type_error_handler.h b/components/sync_driver/data_type_error_handler.h index a33a151f21718..174b9a8fdb896 100644 --- a/components/sync_driver/data_type_error_handler.h +++ b/components/sync_driver/data_type_error_handler.h @@ -18,9 +18,8 @@ class DataTypeErrorHandler { public: // Call this to disable a datatype while it is running. This is usually // called for a runtime failure that is specific to a datatype. - virtual void OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) = 0; + virtual void OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) = 0; // This will create a syncer::SyncError object. This will also upload // a breakpad call stack to crash server. A sync error usually means diff --git a/components/sync_driver/data_type_error_handler_mock.h b/components/sync_driver/data_type_error_handler_mock.h index 6e70e2bee2820..a5cd6d4e3e9ed 100644 --- a/components/sync_driver/data_type_error_handler_mock.h +++ b/components/sync_driver/data_type_error_handler_mock.h @@ -14,8 +14,8 @@ class DataTypeErrorHandlerMock : public DataTypeErrorHandler { public: DataTypeErrorHandlerMock(); virtual ~DataTypeErrorHandlerMock(); - MOCK_METHOD2(OnSingleDatatypeUnrecoverableError, - void(const tracked_objects::Location&, const std::string&)); + MOCK_METHOD1(OnSingleDataTypeUnrecoverableError, + void(const syncer::SyncError&)); MOCK_METHOD3(CreateAndUploadError, syncer::SyncError(const tracked_objects::Location&, const std::string&, diff --git a/components/sync_driver/data_type_manager.cc b/components/sync_driver/data_type_manager.cc index 433d5ca160e64..99dc70ae5edd6 100644 --- a/components/sync_driver/data_type_manager.cc +++ b/components/sync_driver/data_type_manager.cc @@ -10,25 +10,10 @@ DataTypeManager::ConfigureResult::ConfigureResult() : status(UNKNOWN) { } -DataTypeManager::ConfigureResult::ConfigureResult(ConfigureStatus status, - syncer::ModelTypeSet - requested_types) - : status(status), - requested_types(requested_types) { - DCHECK_EQ(OK, status); -} - DataTypeManager::ConfigureResult::ConfigureResult( ConfigureStatus status, - syncer::ModelTypeSet requested_types, - std::map failed_data_types, - syncer::ModelTypeSet unfinished_data_types, - syncer::ModelTypeSet needs_crypto) - : status(status), - requested_types(requested_types), - failed_data_types(failed_data_types), - unfinished_data_types(unfinished_data_types), - needs_crypto(needs_crypto) { + syncer::ModelTypeSet requested_types) + : status(status), requested_types(requested_types) { } DataTypeManager::ConfigureResult::~ConfigureResult() { @@ -43,12 +28,11 @@ std::string DataTypeManager::ConfigureStatusToString(ConfigureStatus status) { return "Aborted"; case UNRECOVERABLE_ERROR: return "Unrecoverable Error"; - case PARTIAL_SUCCESS: - return "Partial Success"; - default: + case UNKNOWN: NOTREACHED(); return std::string(); } + return std::string(); } } // namespace sync_driver diff --git a/components/sync_driver/data_type_manager.h b/components/sync_driver/data_type_manager.h index ecb3f0cb461fd..6ea5254c5beae 100644 --- a/components/sync_driver/data_type_manager.h +++ b/components/sync_driver/data_type_manager.h @@ -37,8 +37,7 @@ class DataTypeManager { // this. enum ConfigureStatus { UNKNOWN = -1, - OK, // Configuration finished without error. - PARTIAL_SUCCESS, // Some data types had an error while starting up. + OK, // Configuration finished some or all types. ABORTED, // Start was aborted by calling Stop() before // all types were started. UNRECOVERABLE_ERROR // We got an unrecoverable error during startup. @@ -49,28 +48,10 @@ class DataTypeManager { ConfigureResult(); ConfigureResult(ConfigureStatus status, syncer::ModelTypeSet requested_types); - ConfigureResult(ConfigureStatus status, - syncer::ModelTypeSet requested_types, - std::map - failed_data_types, - syncer::ModelTypeSet unfinished_data_types, - syncer::ModelTypeSet needs_crypto); ~ConfigureResult(); + ConfigureStatus status; syncer::ModelTypeSet requested_types; - - // These types encountered a failure in association. - std::map failed_data_types; - - // List of types that failed to finish loading/associating within our - // alloted time period(see |kAssociationTimeOutInSeconds|). We move - // forward here and allow these types to continue to load/associate in - // the background. - syncer::ModelTypeSet unfinished_data_types; - - // Those types that are unable to start due to the cryptographer not being - // ready. - syncer::ModelTypeSet needs_crypto; }; virtual ~DataTypeManager() {} diff --git a/components/sync_driver/data_type_manager_impl.cc b/components/sync_driver/data_type_manager_impl.cc index 8f75985bca9b4..77545aa34359a 100644 --- a/components/sync_driver/data_type_manager_impl.cc +++ b/components/sync_driver/data_type_manager_impl.cc @@ -88,7 +88,9 @@ void DataTypeManagerImpl::Configure(syncer::ModelTypeSet desired_types, if (syncer::IsControlType(type.Get()) || iter != controllers_->end()) { if (iter != controllers_->end()) { - if (!iter->second->ReadyForStart()) { + if (!iter->second->ReadyForStart() && + !failed_data_types_handler_->GetUnreadyErrorTypes().Has( + type.Get())) { // Add the type to the unready types set to prevent purging it. It's // up to the datatype controller to, if necessary, explicitly // mark the type as broken to trigger a purge. @@ -99,7 +101,7 @@ void DataTypeManagerImpl::Configure(syncer::ModelTypeSet desired_types, std::map errors; errors[type.Get()] = error; failed_data_types_handler_->UpdateFailedDataTypes(errors); - } else { + } else if (iter->second->ReadyForStart()) { failed_data_types_handler_->ResetUnreadyErrorFor(type.Get()); } } @@ -345,14 +347,18 @@ void DataTypeManagerImpl::DownloadReady( if (!failed_configuration_types.Empty()) { if (!unrecoverable_error_method_.is_null()) unrecoverable_error_method_.Run(); - std::string error_msg = - "Configuration failed for types " + - syncer::ModelTypeSetToString(failed_configuration_types); - syncer::SyncError error(FROM_HERE, - syncer::SyncError::UNRECOVERABLE_ERROR, - error_msg, - failed_configuration_types.First().Get()); - Abort(UNRECOVERABLE_ERROR, error); + FailedDataTypesHandler::TypeErrorMap errors; + for (syncer::ModelTypeSet::Iterator iter = + failed_configuration_types.First(); iter.Good(); iter.Inc()) { + syncer::SyncError error( + FROM_HERE, + syncer::SyncError::UNRECOVERABLE_ERROR, + "Backend failed to download type.", + iter.Get()); + errors[iter.Get()] = error; + } + failed_data_types_handler_->UpdateFailedDataTypes(errors); + Abort(UNRECOVERABLE_ERROR); return; } @@ -400,8 +406,24 @@ void DataTypeManagerImpl::StartNextAssociation() { } void DataTypeManagerImpl::OnSingleDataTypeWillStop( - syncer::ModelType type) { + syncer::ModelType type, + const syncer::SyncError& error) { configurer_->DeactivateDataType(type); + if (error.IsSet()) { + FailedDataTypesHandler::TypeErrorMap failed_types; + failed_types[type] = error; + failed_data_types_handler_->UpdateFailedDataTypes( + failed_types); + + // Unrecoverable errors will shut down the entire backend, so no need to + // reconfigure. + if (error.error_type() != syncer::SyncError::UNRECOVERABLE_ERROR) { + needs_reconfigure_ = true; + ProcessReconfigure(); + } else { + DCHECK_EQ(state_, CONFIGURING); + } + } } void DataTypeManagerImpl::OnSingleDataTypeAssociationDone( @@ -449,25 +471,6 @@ void DataTypeManagerImpl::OnModelAssociationDone( if (state_ == STOPPING) return; - // Don't reconfigure due to failed data types if we have an unrecoverable - // error or have already aborted. - if (result.status == PARTIAL_SUCCESS) { - if (!result.needs_crypto.Empty()) { - needs_reconfigure_ = true; - syncer::ModelTypeSet encrypted_types = result.needs_crypto; - encrypted_types.RemoveAll( - failed_data_types_handler_->GetCryptoErrorTypes()); - FailedDataTypesHandler::TypeErrorMap crypto_errors = - GenerateCryptoErrorsForTypes(encrypted_types); - failed_data_types_handler_->UpdateFailedDataTypes(crypto_errors); - } - if (!result.failed_data_types.empty()) { - needs_reconfigure_ = true; - failed_data_types_handler_->UpdateFailedDataTypes( - result.failed_data_types); - } - } - // Ignore abort/unrecoverable error if we need to reconfigure anyways. if (needs_reconfigure_) { association_types_queue_ = std::queue(); @@ -476,38 +479,18 @@ void DataTypeManagerImpl::OnModelAssociationDone( } if (result.status == ABORTED || result.status == UNRECOVERABLE_ERROR) { - Abort(result.status, result.failed_data_types.size() >= 1 ? - result.failed_data_types.begin()->second : - syncer::SyncError()); + Abort(result.status); return; } - DCHECK(result.status == PARTIAL_SUCCESS || result.status == OK); - DCHECK(result.status != OK || - (result.needs_crypto.Empty() && result.failed_data_types.empty())); - - // It's possible this is a retry to disable failed types, in which case - // the association would be SUCCESS, but the overall configuration should - // still be PARTIAL_SUCCESS. - syncer::ModelTypeSet failed_data_types = - failed_data_types_handler_->GetFailedTypes(); - ConfigureStatus status = result.status; - if (!syncer::Intersection(last_requested_types_, - failed_data_types).Empty() && result.status == OK) { - status = PARTIAL_SUCCESS; - } + DCHECK(result.status == OK); association_types_queue_.pop(); if (!association_types_queue_.empty()) { StartNextAssociation(); } else if (download_types_queue_.empty()) { state_ = CONFIGURED; - ConfigureResult configure_result(status, - result.requested_types, - failed_data_types_handler_->GetAllErrors(), - result.unfinished_data_types, - result.needs_crypto); - NotifyDone(configure_result); + NotifyDone(result); } } @@ -521,29 +504,19 @@ void DataTypeManagerImpl::Stop() { if (need_to_notify) { ConfigureResult result(ABORTED, - last_requested_types_, - std::map(), - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + last_requested_types_); NotifyDone(result); } } -void DataTypeManagerImpl::Abort(ConfigureStatus status, - const syncer::SyncError& error) { +void DataTypeManagerImpl::Abort(ConfigureStatus status) { DCHECK(state_ == DOWNLOAD_PENDING || state_ == CONFIGURING); StopImpl(); DCHECK_NE(OK, status); - std::map errors; - if (error.IsSet()) - errors[error.model_type()] = error; ConfigureResult result(status, - last_requested_types_, - errors, - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + last_requested_types_); NotifyDone(result); } @@ -593,12 +566,7 @@ void DataTypeManagerImpl::NotifyDone(const ConfigureResult& result) { UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.UNRECOVERABLE_ERROR", configure_time_delta_); break; - case DataTypeManager::PARTIAL_SUCCESS: - DVLOG(1) << "NotifyDone called with result: PARTIAL_SUCCESS"; - UMA_HISTOGRAM_LONG_TIMES("Sync.ConfigureTime_Long.PARTIAL_SUCCESS", - configure_time_delta_); - break; - default: + case DataTypeManager::UNKNOWN: NOTREACHED(); break; } diff --git a/components/sync_driver/data_type_manager_impl.h b/components/sync_driver/data_type_manager_impl.h index 6beb108b04907..84f96a8f9274d 100644 --- a/components/sync_driver/data_type_manager_impl.h +++ b/components/sync_driver/data_type_manager_impl.h @@ -68,7 +68,9 @@ class DataTypeManagerImpl : public DataTypeManager, const syncer::DataTypeAssociationStats& association_stats) OVERRIDE; virtual void OnModelAssociationDone( const DataTypeManager::ConfigureResult& result) OVERRIDE; - virtual void OnSingleDataTypeWillStop(syncer::ModelType type) OVERRIDE; + virtual void OnSingleDataTypeWillStop( + syncer::ModelType type, + const syncer::SyncError& error) OVERRIDE; // Used by unit tests. TODO(sync) : This would go away if we made // this class be able to do Dependency injection. crbug.com/129212. @@ -80,8 +82,7 @@ class DataTypeManagerImpl : public DataTypeManager, friend class TestDataTypeManager; // Abort configuration and stop all data types due to configuration errors. - void Abort(ConfigureStatus status, - const syncer::SyncError& error); + void Abort(ConfigureStatus status); // Returns the priority types (control + priority user types). // Virtual for overriding during tests. diff --git a/components/sync_driver/data_type_manager_impl_unittest.cc b/components/sync_driver/data_type_manager_impl_unittest.cc index 2f35d0363d9fb..de5855bafa69f 100644 --- a/components/sync_driver/data_type_manager_impl_unittest.cc +++ b/components/sync_driver/data_type_manager_impl_unittest.cc @@ -394,7 +394,7 @@ TEST_F(SyncDataTypeManagerImplTest, OneWaitingForCrypto) { AddController(PASSWORDS); SetConfigureStartExpectation(); - SetConfigureDoneExpectation(DataTypeManager::PARTIAL_SUCCESS); + SetConfigureDoneExpectation(DataTypeManager::OK); const ModelTypeSet types(PASSWORDS); dtm_->set_priority_types(AddHighPriorityTypesTo(types)); @@ -651,7 +651,7 @@ TEST_F(SyncDataTypeManagerImplTest, OneControllerFailsAssociation) { AddController(PREFERENCES); SetConfigureStartExpectation(); - SetConfigureDoneExpectation(DataTypeManager::PARTIAL_SUCCESS); + SetConfigureDoneExpectation(DataTypeManager::OK); // Step 1. Configure(dtm_.get(), ModelTypeSet(BOOKMARKS, PREFERENCES)); @@ -1024,7 +1024,7 @@ TEST_F(SyncDataTypeManagerImplTest, HighPriorityAssociationFailure) { // Initial configure. SetConfigureStartExpectation(); - SetConfigureDoneExpectation(DataTypeManager::PARTIAL_SUCCESS); + SetConfigureDoneExpectation(DataTypeManager::OK); // Initially only PREFERENCES is configured. configurer_.set_expected_configure_types( @@ -1076,7 +1076,7 @@ TEST_F(SyncDataTypeManagerImplTest, LowPriorityAssociationFailure) { // Initial configure. SetConfigureStartExpectation(); - SetConfigureDoneExpectation(DataTypeManager::PARTIAL_SUCCESS); + SetConfigureDoneExpectation(DataTypeManager::OK); // Initially only PREFERENCES is configured. configurer_.set_expected_configure_types( @@ -1167,7 +1167,7 @@ TEST_F(SyncDataTypeManagerImplTest, ReenableAfterDataTypeError) { AddController(BOOKMARKS); // Will be disabled due to datatype error. SetConfigureStartExpectation(); - SetConfigureDoneExpectation(DataTypeManager::PARTIAL_SUCCESS); + SetConfigureDoneExpectation(DataTypeManager::OK); Configure(dtm_.get(), ModelTypeSet(BOOKMARKS, PREFERENCES)); FinishDownload(*dtm_, ModelTypeSet(), ModelTypeSet()); @@ -1196,4 +1196,38 @@ TEST_F(SyncDataTypeManagerImplTest, ReenableAfterDataTypeError) { EXPECT_EQ(DataTypeController::RUNNING, GetController(BOOKMARKS)->state()); } +TEST_F(SyncDataTypeManagerImplTest, UnreadyType) { + AddController(BOOKMARKS); + GetController(BOOKMARKS)->SetReadyForStart(false); + + // Bookmarks is never started due to being unready. + SetConfigureStartExpectation(); + SetConfigureDoneExpectation(DataTypeManager::OK); + Configure(dtm_.get(), ModelTypeSet(BOOKMARKS)); + FinishDownload(*dtm_, ModelTypeSet(), ModelTypeSet()); + EXPECT_EQ(DataTypeController::NOT_RUNNING, GetController(BOOKMARKS)->state()); + EXPECT_EQ(DataTypeManager::CONFIGURED, dtm_->state()); + EXPECT_EQ(0U, configurer_.activated_types().Size()); + Mock::VerifyAndClearExpectations(&observer_); + + // Bookmarks should start normally now. + GetController(BOOKMARKS)->SetReadyForStart(true); + SetConfigureStartExpectation(); + SetConfigureDoneExpectation(DataTypeManager::OK); + Configure(dtm_.get(), ModelTypeSet(BOOKMARKS)); + EXPECT_EQ(DataTypeManager::DOWNLOAD_PENDING, dtm_->state()); + + FinishDownload(*dtm_, ModelTypeSet(), ModelTypeSet()); + FinishDownload(*dtm_, ModelTypeSet(BOOKMARKS), ModelTypeSet()); + EXPECT_EQ(DataTypeManager::CONFIGURING, dtm_->state()); + + GetController(BOOKMARKS)->FinishStart(DataTypeController::OK); + EXPECT_EQ(DataTypeManager::CONFIGURED, dtm_->state()); + EXPECT_EQ(1U, configurer_.activated_types().Size()); + + dtm_->Stop(); + EXPECT_EQ(DataTypeManager::STOPPED, dtm_->state()); + EXPECT_TRUE(configurer_.activated_types().Empty()); +} + } // namespace sync_driver diff --git a/components/sync_driver/failed_data_types_handler.cc b/components/sync_driver/failed_data_types_handler.cc index cb18febac7f2f..8ee9c1dea6531 100644 --- a/components/sync_driver/failed_data_types_handler.cc +++ b/components/sync_driver/failed_data_types_handler.cc @@ -32,6 +32,8 @@ bool FailedDataTypesHandler::UpdateFailedDataTypes(const TypeErrorMap& errors) { if (errors.empty()) return false; + DVLOG(1) << "Setting " << errors.size() << " new failed types."; + for (TypeErrorMap::const_iterator iter = errors.begin(); iter != errors.end(); ++iter) { syncer::SyncError::ErrorType failure_type = iter->second.error_type(); @@ -91,11 +93,11 @@ bool FailedDataTypesHandler::ResetUnreadyErrorFor(syncer::ModelType type) { FailedDataTypesHandler::TypeErrorMap FailedDataTypesHandler::GetAllErrors() const { TypeErrorMap result; - result = unrecoverable_errors_; result.insert(data_type_errors_.begin(), data_type_errors_.end()); result.insert(crypto_errors_.begin(), crypto_errors_.end()); result.insert(persistence_errors_.begin(), persistence_errors_.end()); result.insert(unready_errors_.begin(), unready_errors_.end()); + result.insert(unrecoverable_errors_.begin(), unrecoverable_errors_.end()); return result; } @@ -108,8 +110,9 @@ syncer::ModelTypeSet FailedDataTypesHandler::GetFailedTypes() const { syncer::ModelTypeSet FailedDataTypesHandler::GetFatalErrorTypes() const { - syncer::ModelTypeSet result = GetTypesFromErrorMap(unrecoverable_errors_); + syncer::ModelTypeSet result; result.PutAll(GetTypesFromErrorMap(data_type_errors_)); + result.PutAll(GetTypesFromErrorMap(unrecoverable_errors_)); return result; } @@ -128,10 +131,25 @@ syncer::ModelTypeSet FailedDataTypesHandler::GetUnreadyErrorTypes() const { return result; } +syncer::ModelTypeSet FailedDataTypesHandler::GetUnrecoverableErrorTypes() + const { + syncer::ModelTypeSet result = GetTypesFromErrorMap(unrecoverable_errors_); + return result; +} + +syncer::SyncError FailedDataTypesHandler::GetUnrecoverableError() const { + // Just return the first one. It is assumed all the unrecoverable errors + // have the same cause. The others are just tracked to know which types + // were involved. + return (unrecoverable_errors_.empty() + ? syncer::SyncError() + : unrecoverable_errors_.begin()->second); +} + bool FailedDataTypesHandler::AnyFailedDataType() const { // Note: persistence errors are not failed types. They just trigger automatic // unapply + getupdates, at which point they are associated like normal. - return !unrecoverable_errors_.empty() || + return unrecoverable_errors_.empty() || !data_type_errors_.empty() || !crypto_errors_.empty(); } diff --git a/components/sync_driver/failed_data_types_handler.h b/components/sync_driver/failed_data_types_handler.h index 93b6fae6ac6eb..985e75923dfb9 100644 --- a/components/sync_driver/failed_data_types_handler.h +++ b/components/sync_driver/failed_data_types_handler.h @@ -61,12 +61,18 @@ class FailedDataTypesHandler { // Returns the types that cannot be configured due to not being ready. syncer::ModelTypeSet GetUnreadyErrorTypes() const; + // Returns the types that triggered the unrecoverable error. + syncer::ModelTypeSet GetUnrecoverableErrorTypes() const; + + // Returns the current unrecoverable error, if there is one. + syncer::SyncError GetUnrecoverableError() const; + private: // Returns true if there are any types with errors. bool AnyFailedDataType() const; - // List of data types that failed due to unrecoverable errors and should - // be disabled. + // The current unrecoverable errors. Only one unrecoverable error can be + // active at a time, but it may apply to more than one type. TypeErrorMap unrecoverable_errors_; // List of data types that failed due to runtime errors and should be diff --git a/components/sync_driver/fake_data_type_controller.cc b/components/sync_driver/fake_data_type_controller.cc index 1aeb0fb0853c8..9f9c7693f5d8e 100644 --- a/components/sync_driver/fake_data_type_controller.cc +++ b/components/sync_driver/fake_data_type_controller.cc @@ -12,11 +12,11 @@ using syncer::ModelType; namespace sync_driver { FakeDataTypeController::FakeDataTypeController(ModelType type) - : DataTypeController(base::MessageLoopProxy::current(), base::Closure(), - DisableTypeCallback()), + : DataTypeController(base::MessageLoopProxy::current(), base::Closure()), state_(NOT_RUNNING), model_load_delayed_(false), - type_(type) {} + type_(type), + ready_for_start_(true) {} FakeDataTypeController::~FakeDataTypeController() { } @@ -54,7 +54,7 @@ void FakeDataTypeController::StartAssociating( // MODEL_STARTING | ASSOCIATING -> RUNNING | DISABLED | NOT_RUNNING // (depending on |result|) -void FakeDataTypeController::FinishStart(StartResult result) { +void FakeDataTypeController::FinishStart(ConfigureResult result) { // We should have a callback from Start(). if (last_start_callback_.is_null()) { ADD_FAILURE(); @@ -73,6 +73,13 @@ void FakeDataTypeController::FinishStart(StartResult result) { syncer::SyncError::DATATYPE_ERROR, "Association failed", type())); + } else if (result == UNRECOVERABLE_ERROR) { + state_ = NOT_RUNNING; + local_merge_result.set_error( + syncer::SyncError(FROM_HERE, + syncer::SyncError::UNRECOVERABLE_ERROR, + "Unrecoverable error", + type())); } else { state_ = NOT_RUNNING; local_merge_result.set_error( @@ -81,11 +88,7 @@ void FakeDataTypeController::FinishStart(StartResult result) { "Fake error", type())); } - StartCallback start_callback = last_start_callback_; - last_start_callback_.Reset(); - start_callback.Run(result, - local_merge_result, - syncer_merge_result); + last_start_callback_.Run(result, local_merge_result, syncer_merge_result); } // * -> NOT_RUNNING @@ -97,19 +100,6 @@ void FakeDataTypeController::Stop() { // unnecessary pieces. SimulateModelLoadFinishing(); } - - // The DTM still expects |last_start_callback_| to be called back. - if (!last_start_callback_.is_null()) { - syncer::SyncError error(FROM_HERE, - syncer::SyncError::DATATYPE_ERROR, - "Fake error", - type_); - syncer::SyncMergeResult local_merge_result(type_); - local_merge_result.set_error(error); - last_start_callback_.Run(ABORTED, - local_merge_result, - syncer::SyncMergeResult(type_)); - } } ModelType FakeDataTypeController::type() const { @@ -132,10 +122,16 @@ DataTypeController::State FakeDataTypeController::state() const { return state_; } -void FakeDataTypeController::OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) { - ADD_FAILURE() << message; +void FakeDataTypeController::OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) { + syncer::SyncMergeResult local_merge_result(type()); + local_merge_result.set_error(error); + last_start_callback_.Run( + RUNTIME_ERROR, local_merge_result, syncer::SyncMergeResult(type_)); +} + +bool FakeDataTypeController::ReadyForStart() const { + return ready_for_start_; } void FakeDataTypeController::SetDelayModelLoad() { @@ -152,4 +148,8 @@ void FakeDataTypeController::SimulateModelLoadFinishing() { model_load_callback_.Reset(); } +void FakeDataTypeController::SetReadyForStart(bool ready) { + ready_for_start_ = ready; +} + } // namespace sync_driver diff --git a/components/sync_driver/fake_data_type_controller.h b/components/sync_driver/fake_data_type_controller.h index 4fa5f7b32c20f..a84bc27257bad 100644 --- a/components/sync_driver/fake_data_type_controller.h +++ b/components/sync_driver/fake_data_type_controller.h @@ -25,34 +25,27 @@ class FakeDataTypeController : public DataTypeController { virtual void LoadModels( const ModelLoadCallback& model_load_callback) OVERRIDE; - virtual void OnModelLoaded() OVERRIDE; - virtual void StartAssociating(const StartCallback& start_callback) OVERRIDE; - - void FinishStart(StartResult result); - virtual void Stop() OVERRIDE; - virtual syncer::ModelType type() const OVERRIDE; - virtual std::string name() const OVERRIDE; - virtual syncer::ModelSafeGroup model_safe_group() const OVERRIDE; - virtual ChangeProcessor* GetChangeProcessor() const OVERRIDE; - virtual State state() const OVERRIDE; + virtual void OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) OVERRIDE; + virtual bool ReadyForStart() const OVERRIDE; - virtual void OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) OVERRIDE; + void FinishStart(ConfigureResult result); - virtual void SetDelayModelLoad(); + void SetDelayModelLoad(); void SetModelLoadError(syncer::SyncError error); - virtual void SimulateModelLoadFinishing(); + void SimulateModelLoadFinishing(); + + void SetReadyForStart(bool ready); protected: virtual ~FakeDataTypeController(); @@ -64,6 +57,7 @@ class FakeDataTypeController : public DataTypeController { StartCallback last_start_callback_; ModelLoadCallback model_load_callback_; syncer::SyncError load_error_; + bool ready_for_start_; }; } // namespace sync_driver diff --git a/components/sync_driver/generic_change_processor.cc b/components/sync_driver/generic_change_processor.cc index d7a5c6eacc010..c44ce72a1bd23 100644 --- a/components/sync_driver/generic_change_processor.cc +++ b/components/sync_driver/generic_change_processor.cc @@ -144,10 +144,13 @@ void GenericChangeProcessor::ApplyChangesFromSyncModel( // Need to load specifics from node. syncer::ReadNode read_node(trans); if (read_node.InitByIdLookup(it->id) != syncer::BaseNode::INIT_OK) { - error_handler()->OnSingleDatatypeUnrecoverableError( + syncer::SyncError error( FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, "Failed to look up data for received change with id " + - base::Int64ToString(it->id)); + base::Int64ToString(it->id), + syncer::GetModelTypeFromSpecifics(it->specifics)); + error_handler()->OnSingleDataTypeUnrecoverableError(error); return; } syncer_changes_.push_back(syncer::SyncChange( @@ -168,17 +171,14 @@ void GenericChangeProcessor::CommitChangesFromSyncModel() { syncer::SyncError::DATATYPE_ERROR, "Local service destroyed.", type); - error_handler()->OnSingleDatatypeUnrecoverableError(error.location(), - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); return; } syncer::SyncError error = local_service_->ProcessSyncChanges(FROM_HERE, syncer_changes_); syncer_changes_.clear(); - if (error.IsSet()) { - error_handler()->OnSingleDatatypeUnrecoverableError( - error.location(), error.message()); - } + if (error.IsSet()) + error_handler()->OnSingleDataTypeUnrecoverableError(error); } syncer::SyncDataList GenericChangeProcessor::GetAllSyncData( @@ -285,9 +285,8 @@ int GenericChangeProcessor::GetSyncCountForType(syncer::ModelType type) { namespace { -// TODO(isherman): Investigating http://crbug.com/121592 // WARNING: this code is sensitive to compiler optimizations. Be careful -// modifying any code around an OnSingleDatatypeUnrecoverableError call, else +// modifying any code around an OnSingleDataTypeUnrecoverableError call, else // the compiler attempts to merge it with other calls, losing useful information // in breakpad uploads. syncer::SyncError LogLookupFailure( @@ -303,24 +302,21 @@ syncer::SyncError LogLookupFailure( error_prefix + "could not find entry matching the lookup criteria.", type); - error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Delete: Bad entry."; return error; } case syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL: { syncer::SyncError error; error.Reset(from_here, error_prefix + "entry is already deleted.", type); - error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Delete: Deleted entry."; return error; } case syncer::BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY: { syncer::SyncError error; error.Reset(from_here, error_prefix + "unable to decrypt", type); - error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Delete: Undecryptable entry."; return error; } @@ -329,8 +325,7 @@ syncer::SyncError LogLookupFailure( error.Reset(from_here, error_prefix + "a precondition was not met for calling init.", type); - error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Delete: Failed precondition."; return error; } @@ -338,8 +333,7 @@ syncer::SyncError LogLookupFailure( syncer::SyncError error; // Should have listed all the possible error cases above. error.Reset(from_here, error_prefix + "unknown error", type); - error_handler->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Delete: Unknown error."; return error; } @@ -361,8 +355,7 @@ syncer::SyncError AttemptDelete(const syncer::SyncChange& change, "Failed to delete " + type_str + " node. Local data, empty tag. " + change.location().ToString(), type); - error_handler->OnSingleDatatypeUnrecoverableError(error.location(), - error.message()); + error_handler->OnSingleDataTypeUnrecoverableError(error); NOTREACHED(); return error; } @@ -464,8 +457,7 @@ syncer::SyncError GenericChangeProcessor::ProcessSyncChanges( "Received unset SyncChange in the change processor, " + change.location().ToString(), type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); NOTREACHED(); LOG(ERROR) << "Unset sync change."; return error; @@ -480,7 +472,7 @@ syncer::SyncError GenericChangeProcessor::ProcessSyncChanges( } // WARNING: this code is sensitive to compiler optimizations. Be careful -// modifying any code around an OnSingleDatatypeUnrecoverableError call, else +// modifying any code around an OnSingleDataTypeUnrecoverableError call, else // the compiler attempts to merge it with other calls, losing useful information // in breakpad uploads. syncer::SyncError GenericChangeProcessor::HandleActionAdd( @@ -500,8 +492,7 @@ syncer::SyncError GenericChangeProcessor::HandleActionAdd( syncer::SyncError::DATATYPE_ERROR, "Failed to look up root node for type " + type_str, type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); NOTREACHED(); LOG(ERROR) << "Create: no root node."; return error; @@ -516,24 +507,21 @@ syncer::SyncError GenericChangeProcessor::HandleActionAdd( case syncer::WriteNode::INIT_FAILED_EMPTY_TAG: { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "empty tag", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Create: Empty tag."; return error; } case syncer::WriteNode::INIT_FAILED_ENTRY_ALREADY_EXISTS: { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "entry already exists", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Create: Entry exists."; return error; } case syncer::WriteNode::INIT_FAILED_COULD_NOT_CREATE_ENTRY: { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "failed to create entry", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Create: Could not create entry."; return error; } @@ -541,16 +529,14 @@ syncer::SyncError GenericChangeProcessor::HandleActionAdd( syncer::SyncError error; error.Reset( FROM_HERE, error_prefix + "failed to set predecessor", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Create: Bad predecessor."; return error; } default: { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "unknown error", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Create: Unknown error."; return error; } @@ -575,7 +561,7 @@ syncer::SyncError GenericChangeProcessor::HandleActionAdd( return syncer::SyncError(); } // WARNING: this code is sensitive to compiler optimizations. Be careful -// modifying any code around an OnSingleDatatypeUnrecoverableError call, else +// modifying any code around an OnSingleDataTypeUnrecoverableError call, else // the compiler attempts to merge it with other calls, losing useful information // in breakpad uploads. syncer::SyncError GenericChangeProcessor::HandleActionUpdate( @@ -597,22 +583,19 @@ syncer::SyncError GenericChangeProcessor::HandleActionUpdate( if (result == syncer::BaseNode::INIT_FAILED_PRECONDITION) { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "empty tag", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: Empty tag."; return error; } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_NOT_GOOD) { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "bad entry", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: bad entry."; return error; } else if (result == syncer::BaseNode::INIT_FAILED_ENTRY_IS_DEL) { syncer::SyncError error; error.Reset(FROM_HERE, error_prefix + "deleted entry", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: deleted entry."; return error; } else { @@ -630,8 +613,7 @@ syncer::SyncError GenericChangeProcessor::HandleActionUpdate( "nigori mismatch for " + type_str + ".", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: encr case 1."; return error; } else if (agreement && can_decrypt) { @@ -641,8 +623,7 @@ syncer::SyncError GenericChangeProcessor::HandleActionUpdate( "and the nigori matches (?!) for " + type_str + ".", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: encr case 2."; return error; } else if (agreement) { @@ -652,8 +633,7 @@ syncer::SyncError GenericChangeProcessor::HandleActionUpdate( "the nigori matches for " + type_str + ".", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: encr case 3."; return error; } else { @@ -663,8 +643,7 @@ syncer::SyncError GenericChangeProcessor::HandleActionUpdate( "(?!) and nigori mismatch for " + type_str + ".", type); - error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE, - error.message()); + error_handler()->OnSingleDataTypeUnrecoverableError(error); LOG(ERROR) << "Update: encr case 4."; return error; } diff --git a/components/sync_driver/model_association_manager.cc b/components/sync_driver/model_association_manager.cc index 7a2aaf875af0c..05ef0ab5109da 100644 --- a/components/sync_driver/model_association_manager.cc +++ b/components/sync_driver/model_association_manager.cc @@ -64,8 +64,8 @@ COMPILE_ASSERT(arraysize(kStartOrder) == kStartOrder_IncorrectSize); // The amount of time we wait for association to finish. If some types haven't -// finished association by the time, configuration result will be -// PARTIAL_SUCCESS and DataTypeManager is notified of the unfinished types. +// finished association by the time, DataTypeManager is notified of the +// unfinished types. const int64 kAssociationTimeOutInSeconds = 600; syncer::DataTypeAssociationStats BuildAssociationStatsFromMergeResults( @@ -134,7 +134,6 @@ void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) { // Only keep types that have controllers. desired_types_.Clear(); - slow_types_.Clear(); for (syncer::ModelTypeSet::Iterator it = desired_types.First(); it.Good(); it.Inc()) { if (controllers_->find(it.Get()) != controllers_->end()) @@ -150,13 +149,18 @@ void ModelAssociationManager::Initialize(syncer::ModelTypeSet desired_types) { LoadEnabledTypes(); } -void ModelAssociationManager::StopDatatype(DataTypeController* dtc) { - // First tell the sync backend that we no longer want to listen to - // changes for this type. - delegate_->OnSingleDataTypeWillStop(dtc->type()); - - // Then tell all data type specific logic to shut down. - dtc->Stop(); +void ModelAssociationManager::StopDatatype( + const syncer::SyncError& error, + DataTypeController* dtc) { + loaded_types_.Remove(dtc->type()); + associated_types_.Remove(dtc->type()); + associating_types_.Remove(dtc->type()); + + if (error.IsSet() || dtc->state() != DataTypeController::NOT_RUNNING) { + // If an error was set, the delegate must be informed of the error. + delegate_->OnSingleDataTypeWillStop(dtc->type(), error); + dtc->Stop(); + } } void ModelAssociationManager::StopDisabledTypes() { @@ -165,12 +169,9 @@ void ModelAssociationManager::StopDisabledTypes() { it != controllers_->end(); ++it) { DataTypeController* dtc = (*it).second.get(); if (dtc->state() != DataTypeController::NOT_RUNNING && - (!desired_types_.Has(dtc->type()) || - failed_data_types_info_.count(dtc->type()) > 0)) { + !desired_types_.Has(dtc->type())) { DVLOG(1) << "ModelTypeToString: stop " << dtc->name(); - StopDatatype(dtc); - loaded_types_.Remove(dtc->type()); - associated_types_.Remove(dtc->type()); + StopDatatype(syncer::SyncError(), dtc); } } } @@ -209,13 +210,6 @@ void ModelAssociationManager::StartAssociationAsync( // Assume success. configure_status_ = DataTypeManager::OK; - // Remove types that already failed. - for (std::map::const_iterator it = - failed_data_types_info_.begin(); - it != failed_data_types_info_.end(); ++it) { - associating_types_.Remove(it->first); - } - // Done if no types to associate. if (associating_types_.Empty()) { ModelAssociationDone(); @@ -255,30 +249,28 @@ void ModelAssociationManager::ResetForNextAssociation() { // |loaded_types_| and |associated_types_| are not cleared. So // reconfiguration won't restart types that are already started. requested_types_.Clear(); - failed_data_types_info_.clear(); associating_types_.Clear(); - needs_crypto_types_.Clear(); } void ModelAssociationManager::Stop() { // Ignore callbacks from controllers. weak_ptr_factory_.InvalidateWeakPtrs(); + desired_types_.Clear(); + loaded_types_.Clear(); + associated_types_.Clear(); + associating_types_.Clear(); + // Stop started data types. for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); it != controllers_->end(); ++it) { DataTypeController* dtc = (*it).second.get(); if (dtc->state() != DataTypeController::NOT_RUNNING) { - StopDatatype(dtc); + StopDatatype(syncer::SyncError(), dtc); DVLOG(1) << "ModelAssociationManager: Stopped " << dtc->name(); } } - desired_types_.Clear(); - loaded_types_.Clear(); - associated_types_.Clear(); - slow_types_.Clear(); - if (state_ == CONFIGURING) { if (configure_status_ == DataTypeManager::OK) configure_status_ = DataTypeManager::ABORTED; @@ -291,25 +283,11 @@ void ModelAssociationManager::Stop() { state_ = IDLE; } -void ModelAssociationManager::AppendToFailedDatatypesAndLogError( - const syncer::SyncError& error) { - failed_data_types_info_[error.model_type()] = error; - LOG(ERROR) << "Failed to associate models for " - << syncer::ModelTypeToString(error.model_type()); - UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed", - ModelTypeToHistogramInt(error.model_type()), - syncer::MODEL_TYPE_COUNT); -} - void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, syncer::SyncError error) { DVLOG(1) << "ModelAssociationManager: ModelLoadCallback for " << syncer::ModelTypeToString(type); - // TODO(haitaol): temporary fix for 335606. - if (slow_types_.Has(type)) - return; - // This happens when slow loading type is disabled by new configuration. if (!desired_types_.Has(type)) return; @@ -327,7 +305,7 @@ void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, } loaded_types_.Put(type); - if (associating_types_.Has(type) || slow_types_.Has(type)) { + if (associating_types_.Has(type)) { DataTypeController* dtc = controllers_->find(type)->second.get(); dtc->StartAssociating( base::Bind(&ModelAssociationManager::TypeStartCallback, @@ -339,41 +317,34 @@ void ModelAssociationManager::ModelLoadCallback(syncer::ModelType type, void ModelAssociationManager::TypeStartCallback( syncer::ModelType type, base::TimeTicks type_start_time, - DataTypeController::StartResult start_result, + DataTypeController::ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { - // TODO(haitaol): temporary fix for 335606. - if (slow_types_.Has(type)) - return; + if (desired_types_.Has(type) && + !DataTypeController::IsSuccessfulResult(start_result)) { + DVLOG(1) << "ModelAssociationManager: Type encountered an error."; + desired_types_.Remove(type); + DataTypeController* dtc = controllers_->find(type)->second.get(); + StopDatatype(local_merge_result.error(), dtc); - // This happens when slow associating type is disabled by new configuration. - if (!desired_types_.Has(type)) - return; + // Update configuration result. + if (start_result == DataTypeController::UNRECOVERABLE_ERROR) + configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR; + } - slow_types_.Remove(type); + // This happens when a slow associating type is disabled or if a type + // disables itself after initial configuration. + if (!desired_types_.Has(type)) { + // It's possible all types failed to associate, in which case association + // is complete. + if (state_ == CONFIGURING && associating_types_.Empty()) + ModelAssociationDone(); + return; + } DCHECK(!associated_types_.Has(type)); - if (DataTypeController::IsSuccessfulResult(start_result)) { - associated_types_.Put(type); - } else if (state_ == IDLE) { - // For type that failed in IDLE mode, simply stop the controller. Next - // configuration will try to restart from scratch if the type is still - // enabled. - DataTypeController* dtc = controllers_->find(type)->second.get(); - if (dtc->state() != DataTypeController::NOT_RUNNING) - StopDatatype(dtc); - loaded_types_.Remove(type); - } else { - // Record error in CONFIGURING or INITIALIZED_TO_CONFIGURE mode. The error - // will be reported when data types association finishes. - if (start_result == DataTypeController::NEEDS_CRYPTO) { - DVLOG(1) << "ModelAssociationManager: Encountered an undecryptable type"; - needs_crypto_types_.Put(type); - } else { - DVLOG(1) << "ModelAssociationManager: Encountered a failed type"; - AppendToFailedDatatypesAndLogError(local_merge_result.error()); - } - } + DCHECK(DataTypeController::IsSuccessfulResult(start_result)); + associated_types_.Put(type); if (state_ != CONFIGURING) return; @@ -385,9 +356,7 @@ void ModelAssociationManager::TypeStartCallback( // Track the merge results if we succeeded or an association failure // occurred. - if ((DataTypeController::IsSuccessfulResult(start_result) || - start_result == DataTypeController::ASSOCIATION_FAILED) && - syncer::ProtocolTypes().Has(type)) { + if (syncer::ProtocolTypes().Has(type)) { base::TimeDelta association_wait_time = std::max(base::TimeDelta(), type_start_time - association_start_time_); base::TimeDelta association_time = @@ -400,14 +369,6 @@ void ModelAssociationManager::TypeStartCallback( delegate_->OnSingleDataTypeAssociationDone(type, stats); } - // Update configuration result. - if (configure_status_ == DataTypeManager::OK && - start_result == DataTypeController::ASSOCIATION_FAILED) { - configure_status_ = DataTypeManager::PARTIAL_SUCCESS; - } - if (start_result == DataTypeController::UNRECOVERABLE_ERROR) - configure_status_ = DataTypeManager::UNRECOVERABLE_ERROR; - associating_types_.Remove(type); if (associating_types_.Empty()) @@ -419,34 +380,26 @@ void ModelAssociationManager::ModelAssociationDone() { timer_.Stop(); - slow_types_.PutAll(associating_types_); - - // TODO(haitaol): temporary fix for 335606. - for (syncer::ModelTypeSet::Iterator it = associating_types_.First(); - it.Good(); it.Inc()) { - AppendToFailedDatatypesAndLogError( - syncer::SyncError(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Association timed out.", it.Get())); - } - - // Stop controllers of failed types. - StopDisabledTypes(); - - if (configure_status_ == DataTypeManager::OK && - (!associating_types_.Empty() || !failed_data_types_info_.empty() || - !needs_crypto_types_.Empty())) { - // We have not configured all types that we have been asked to configure. - // Either we have failed types or types that have not completed loading - // yet. - DVLOG(1) << "ModelAssociationManager: setting partial success"; - configure_status_ = DataTypeManager::PARTIAL_SUCCESS; + // Treat any unfinished types as having errors. + desired_types_.RemoveAll(associating_types_); + for (DataTypeController::TypeMap::const_iterator it = controllers_->begin(); + it != controllers_->end(); ++it) { + DataTypeController* dtc = (*it).second.get(); + if (associating_types_.Has(dtc->type()) && + dtc->state() != DataTypeController::NOT_RUNNING) { + UMA_HISTOGRAM_ENUMERATION("Sync.ConfigureFailed", + ModelTypeToHistogramInt(dtc->type()), + syncer::MODEL_TYPE_COUNT); + StopDatatype(syncer::SyncError(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "Association timed out.", + dtc->type()), + dtc); + } } DataTypeManager::ConfigureResult result(configure_status_, - requested_types_, - failed_data_types_info_, - associating_types_, - needs_crypto_types_); + requested_types_); // Reset state before notifying |delegate_| because that might // trigger a new round of configuration. diff --git a/components/sync_driver/model_association_manager.h b/components/sync_driver/model_association_manager.h index f737c59303399..a29eea6a0b203 100644 --- a/components/sync_driver/model_association_manager.h +++ b/components/sync_driver/model_association_manager.h @@ -36,7 +36,8 @@ class ModelAssociationManagerDelegate { // Called when the ModelAssociationManager has decided it must stop |type|, // likely because it is no longer a desired data type or sync is shutting // down. - virtual void OnSingleDataTypeWillStop(syncer::ModelType type) = 0; + virtual void OnSingleDataTypeWillStop(syncer::ModelType type, + const syncer::SyncError& error) = 0; // Called when the ModelAssociationManager has tried to perform model // association for all desired types and has nothing left to do. @@ -98,7 +99,7 @@ class ModelAssociationManager { // callback will be invoked when the model association is done. void TypeStartCallback(syncer::ModelType type, base::TimeTicks type_start_time, - DataTypeController::StartResult start_result, + DataTypeController::ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result); @@ -106,16 +107,12 @@ class ModelAssociationManager { // will be passed to |LoadModels| function. void ModelLoadCallback(syncer::ModelType type, syncer::SyncError error); - // When a type fails to load or fails associating this method is invoked to - // do the book keeping and do the UMA reporting. - void AppendToFailedDatatypesAndLogError(const syncer::SyncError& error); - // Called when all requested types are associated or association times out. // Notify |delegate_| of configuration results. void ModelAssociationDone(); // A helper to stop an individual datatype. - void StopDatatype(DataTypeController* dtc); + void StopDatatype(const syncer::SyncError& error, DataTypeController* dtc); State state_; @@ -136,18 +133,6 @@ class ModelAssociationManager { // reconfiguration if not disabled. syncer::ModelTypeSet associated_types_; - // Data types that are still loading/associating when configuration times - // out. - syncer::ModelTypeSet slow_types_; - - // Collects the list of errors resulting from failing to start a type. This - // would eventually be sent to the listeners after all the types have - // been given a chance to start. - std::map failed_data_types_info_; - - // The set of types that can't configure due to cryptographer errors. - syncer::ModelTypeSet needs_crypto_types_; - // Time when StartAssociationAsync() is called to associate for a set of data // types. base::TimeTicks association_start_time_; diff --git a/components/sync_driver/model_association_manager_unittest.cc b/components/sync_driver/model_association_manager_unittest.cc index 9282f3b6a4713..4d78870a84c2c 100644 --- a/components/sync_driver/model_association_manager_unittest.cc +++ b/components/sync_driver/model_association_manager_unittest.cc @@ -21,7 +21,8 @@ class MockModelAssociationManagerDelegate : MOCK_METHOD2(OnSingleDataTypeAssociationDone, void(syncer::ModelType type, const syncer::DataTypeAssociationStats& association_stats)); - MOCK_METHOD1(OnSingleDataTypeWillStop, void(syncer::ModelType)); + MOCK_METHOD2(OnSingleDataTypeWillStop, + void(syncer::ModelType, const syncer::SyncError& error)); MOCK_METHOD1(OnModelAssociationDone, void( const DataTypeManager::ConfigureResult& result)); }; @@ -40,22 +41,6 @@ FakeDataTypeController* GetController( ACTION_P(VerifyResult, expected_result) { EXPECT_EQ(arg0.status, expected_result.status); EXPECT_TRUE(arg0.requested_types.Equals(expected_result.requested_types)); - EXPECT_EQ(arg0.failed_data_types.size(), - expected_result.failed_data_types.size()); - - if (arg0.failed_data_types.size() == - expected_result.failed_data_types.size()) { - std::map::const_iterator it1, it2; - for (it1 = arg0.failed_data_types.begin(), - it2 = expected_result.failed_data_types.begin(); - it1 != arg0.failed_data_types.end(); - ++it1, ++it2) { - EXPECT_EQ((*it1).first, (*it2).first); - } - } - - EXPECT_TRUE(arg0.unfinished_data_types.Equals( - expected_result.unfinished_data_types)); } class SyncModelAssociationManagerTest : public testing::Test { @@ -79,12 +64,7 @@ TEST_F(SyncModelAssociationManagerTest, SimpleModelStart) { ModelAssociationManager model_association_manager(&controllers_, &delegate_); syncer::ModelTypeSet types(syncer::BOOKMARKS, syncer::APPS); - DataTypeManager::ConfigureResult expected_result( - DataTypeManager::OK, - types, - std::map(), - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). WillOnce(VerifyResult(expected_result)); @@ -124,24 +104,13 @@ TEST_F(SyncModelAssociationManagerTest, StopModelBeforeFinish) { syncer::ModelTypeSet types; types.Put(syncer::BOOKMARKS); - std::map errors; - syncer::SyncError error(FROM_HERE, - syncer::SyncError::DATATYPE_ERROR, - "Failed", - syncer::BOOKMARKS); - errors[syncer::BOOKMARKS] = error; - - DataTypeManager::ConfigureResult expected_result( - DataTypeManager::ABORTED, - types, - errors, - syncer::ModelTypeSet(syncer::BOOKMARKS), - syncer::ModelTypeSet()); + DataTypeManager::ConfigureResult expected_result(DataTypeManager::ABORTED, + types); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). WillOnce(VerifyResult(expected_result)); EXPECT_CALL(delegate_, - OnSingleDataTypeWillStop(syncer::BOOKMARKS)); + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); model_association_manager.Initialize(types); model_association_manager.StartAssociationAsync(types); @@ -162,16 +131,11 @@ TEST_F(SyncModelAssociationManagerTest, StopAfterFinish) { &delegate_); syncer::ModelTypeSet types; types.Put(syncer::BOOKMARKS); - DataTypeManager::ConfigureResult expected_result( - DataTypeManager::OK, - types, - std::map(), - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). WillOnce(VerifyResult(expected_result)); EXPECT_CALL(delegate_, - OnSingleDataTypeWillStop(syncer::BOOKMARKS)); + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); model_association_manager.Initialize(types); model_association_manager.StartAssociationAsync(types); @@ -195,18 +159,9 @@ TEST_F(SyncModelAssociationManagerTest, TypeFailModelAssociation) { &delegate_); syncer::ModelTypeSet types; types.Put(syncer::BOOKMARKS); - std::map errors; - syncer::SyncError error(FROM_HERE, - syncer::SyncError::DATATYPE_ERROR, - "Failed", - syncer::BOOKMARKS); - errors[syncer::BOOKMARKS] = error; - DataTypeManager::ConfigureResult expected_result( - DataTypeManager::PARTIAL_SUCCESS, - types, - errors, - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types); + EXPECT_CALL(delegate_, + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). WillOnce(VerifyResult(expected_result)); @@ -230,18 +185,10 @@ TEST_F(SyncModelAssociationManagerTest, TypeReturnUnrecoverableError) { &delegate_); syncer::ModelTypeSet types; types.Put(syncer::BOOKMARKS); - std::map errors; - syncer::SyncError error(FROM_HERE, - syncer::SyncError::DATATYPE_ERROR, - "Failed", - syncer::BOOKMARKS); - errors[syncer::BOOKMARKS] = error; DataTypeManager::ConfigureResult expected_result( - DataTypeManager::UNRECOVERABLE_ERROR, - types, - errors, - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + DataTypeManager::UNRECOVERABLE_ERROR, types); + EXPECT_CALL(delegate_, + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). WillOnce(VerifyResult(expected_result)); @@ -267,21 +214,10 @@ TEST_F(SyncModelAssociationManagerTest, SlowTypeAsFailedType) { types.Put(syncer::BOOKMARKS); types.Put(syncer::APPS); - std::map errors; - syncer::SyncError error(FROM_HERE, - syncer::SyncError::DATATYPE_ERROR, - "Association timed out.", - syncer::BOOKMARKS); - errors[syncer::BOOKMARKS] = error; - syncer::ModelTypeSet expected_types_unfinished; expected_types_unfinished.Put(syncer::BOOKMARKS); DataTypeManager::ConfigureResult expected_result_partially_done( - DataTypeManager::PARTIAL_SUCCESS, - types, - errors, - expected_types_unfinished, - syncer::ModelTypeSet()); + DataTypeManager::OK, types); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). WillOnce(VerifyResult(expected_result_partially_done)); @@ -291,6 +227,8 @@ TEST_F(SyncModelAssociationManagerTest, SlowTypeAsFailedType) { GetController(controllers_, syncer::APPS)->FinishStart( DataTypeController::OK); + EXPECT_CALL(delegate_, + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); model_association_manager.GetTimerForTesting()->user_task().Run(); EXPECT_EQ(DataTypeController::NOT_RUNNING, @@ -310,16 +248,10 @@ TEST_F(SyncModelAssociationManagerTest, StartMultipleTimes) { DataTypeManager::ConfigureResult result_1st( DataTypeManager::OK, - syncer::ModelTypeSet(syncer::BOOKMARKS), - std::map(), - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + syncer::ModelTypeSet(syncer::BOOKMARKS)); DataTypeManager::ConfigureResult result_2nd( DataTypeManager::OK, - syncer::ModelTypeSet(syncer::APPS), - std::map(), - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + syncer::ModelTypeSet(syncer::APPS)); EXPECT_CALL(delegate_, OnModelAssociationDone(_)). Times(2). WillOnce(VerifyResult(result_1st)). @@ -367,25 +299,86 @@ TEST_F(SyncModelAssociationManagerTest, ModelLoadFailBeforeAssociationStart) { &delegate_); syncer::ModelTypeSet types; types.Put(syncer::BOOKMARKS); - std::map errors; + DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types); + EXPECT_CALL(delegate_, + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); + EXPECT_CALL(delegate_, OnModelAssociationDone(_)). + WillOnce(VerifyResult(expected_result)); + + model_association_manager.Initialize(types); + EXPECT_EQ(DataTypeController::NOT_RUNNING, + GetController(controllers_, syncer::BOOKMARKS)->state()); + model_association_manager.StartAssociationAsync(types); + EXPECT_EQ(DataTypeController::NOT_RUNNING, + GetController(controllers_, syncer::BOOKMARKS)->state()); +} + +// Test that a runtime error is handled by stopping the type. +TEST_F(SyncModelAssociationManagerTest, StopAfterConfiguration) { + controllers_[syncer::BOOKMARKS] = + new FakeDataTypeController(syncer::BOOKMARKS); + ModelAssociationManager model_association_manager( + &controllers_, + &delegate_); + syncer::ModelTypeSet types; + types.Put(syncer::BOOKMARKS); + DataTypeManager::ConfigureResult expected_result(DataTypeManager::OK, types); + EXPECT_CALL(delegate_, OnModelAssociationDone(_)). + WillOnce(VerifyResult(expected_result)); + + model_association_manager.Initialize(types); + model_association_manager.StartAssociationAsync(types); + + EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), + DataTypeController::ASSOCIATING); + GetController(controllers_, syncer::BOOKMARKS)->FinishStart( + DataTypeController::OK); + EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), + DataTypeController::RUNNING); + + testing::Mock::VerifyAndClearExpectations(&delegate_); + EXPECT_CALL(delegate_, + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); syncer::SyncError error(FROM_HERE, syncer::SyncError::DATATYPE_ERROR, - "Failed", + "error", syncer::BOOKMARKS); - errors[syncer::BOOKMARKS] = error; - DataTypeManager::ConfigureResult expected_result( - DataTypeManager::PARTIAL_SUCCESS, - types, - errors, - syncer::ModelTypeSet(), - syncer::ModelTypeSet()); + GetController(controllers_, syncer::BOOKMARKS) + ->OnSingleDataTypeUnrecoverableError(error); +} + +TEST_F(SyncModelAssociationManagerTest, AbortDuringAssociation) { + controllers_[syncer::BOOKMARKS] = + new FakeDataTypeController(syncer::BOOKMARKS); + controllers_[syncer::APPS] = + new FakeDataTypeController(syncer::APPS); + ModelAssociationManager model_association_manager(&controllers_, + &delegate_); + syncer::ModelTypeSet types; + types.Put(syncer::BOOKMARKS); + types.Put(syncer::APPS); + + syncer::ModelTypeSet expected_types_unfinished; + expected_types_unfinished.Put(syncer::BOOKMARKS); + DataTypeManager::ConfigureResult expected_result_partially_done( + DataTypeManager::OK, types); + EXPECT_CALL(delegate_, OnModelAssociationDone(_)). - WillOnce(VerifyResult(expected_result)); + WillOnce(VerifyResult(expected_result_partially_done)); model_association_manager.Initialize(types); - EXPECT_EQ(DataTypeController::DISABLED, - GetController(controllers_, syncer::BOOKMARKS)->state()); model_association_manager.StartAssociationAsync(types); + GetController(controllers_, syncer::APPS)->FinishStart( + DataTypeController::OK); + EXPECT_EQ(GetController(controllers_, syncer::APPS)->state(), + DataTypeController::RUNNING); + EXPECT_EQ(GetController(controllers_, syncer::BOOKMARKS)->state(), + DataTypeController::ASSOCIATING); + + EXPECT_CALL(delegate_, + OnSingleDataTypeWillStop(syncer::BOOKMARKS, _)); + model_association_manager.GetTimerForTesting()->user_task().Run(); + EXPECT_EQ(DataTypeController::NOT_RUNNING, GetController(controllers_, syncer::BOOKMARKS)->state()); } diff --git a/components/sync_driver/non_ui_data_type_controller.cc b/components/sync_driver/non_ui_data_type_controller.cc index 97caccb7e8a37..02845b023210a 100644 --- a/components/sync_driver/non_ui_data_type_controller.cc +++ b/components/sync_driver/non_ui_data_type_controller.cc @@ -24,9 +24,8 @@ NonUIDataTypeController::CreateSharedChangeProcessor() { NonUIDataTypeController::NonUIDataTypeController( scoped_refptr ui_thread, const base::Closure& error_callback, - const DisableTypeCallback& disable_callback, SyncApiComponentFactory* sync_factory) - : DataTypeController(ui_thread, error_callback, disable_callback), + : DataTypeController(ui_thread, error_callback), sync_factory_(sync_factory), state_(NOT_RUNNING), ui_thread_(ui_thread) { @@ -111,11 +110,9 @@ void NonUIDataTypeController::StartAssociating( void NonUIDataTypeController::Stop() { DCHECK(ui_thread_->BelongsToCurrentThread()); - if (state() == NOT_RUNNING) { - // Stop() should never be called for datatypes that are already stopped. - NOTREACHED(); + + if (state() == NOT_RUNNING) return; - } // Disconnect the change processor. At this point, the // syncer::SyncableService can no longer interact with the Syncer, even if @@ -130,10 +127,6 @@ void NonUIDataTypeController::Stop() { return; // The datatype was never activated, we're done. case ASSOCIATING: state_ = STOPPING; - StartDoneImpl(ABORTED, - NOT_RUNNING, - syncer::SyncMergeResult(type()), - syncer::SyncMergeResult(type())); // We continue on to deactivate the datatype and stop the local service. break; case MODEL_LOADED: @@ -169,31 +162,26 @@ DataTypeController::State NonUIDataTypeController::state() const { return state_; } -void NonUIDataTypeController::OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, const std::string& message) { +void NonUIDataTypeController::OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) { DCHECK(!ui_thread_->BelongsToCurrentThread()); // TODO(tim): We double-upload some errors. See bug 383480. if (!error_callback_.is_null()) error_callback_.Run(); - UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", - ModelTypeToHistogramInt(type()), - syncer::MODEL_TYPE_COUNT); - ui_thread_->PostTask(from_here, + ui_thread_->PostTask(error.location(), base::Bind(&NonUIDataTypeController::DisableImpl, this, - from_here, - message)); + error)); } NonUIDataTypeController::NonUIDataTypeController() - : DataTypeController(base::MessageLoopProxy::current(), base::Closure(), - DisableTypeCallback()), + : DataTypeController(base::MessageLoopProxy::current(), base::Closure()), sync_factory_(NULL) {} NonUIDataTypeController::~NonUIDataTypeController() {} void NonUIDataTypeController::StartDone( - DataTypeController::StartResult start_result, + DataTypeController::ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { DCHECK(!ui_thread_->BelongsToCurrentThread()); @@ -215,7 +203,7 @@ void NonUIDataTypeController::StartDone( } void NonUIDataTypeController::StartDoneImpl( - DataTypeController::StartResult start_result, + DataTypeController::ConfigureResult start_result, DataTypeController::State new_state, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { @@ -232,7 +220,6 @@ void NonUIDataTypeController::StartDoneImpl( // (due to Stop being called) and then posted from the non-UI thread. In // this case, we drop the second call because we've already been stopped. if (state_ == NOT_RUNNING) { - DCHECK(start_callback_.is_null()); return; } @@ -243,12 +230,7 @@ void NonUIDataTypeController::StartDoneImpl( RecordStartFailure(start_result); } - // We have to release the callback before we call it, since it's possible - // invoking the callback will trigger a call to STOP(), which will get - // confused by the non-NULL start_callback_. - StartCallback callback = start_callback_; - start_callback_.Reset(); - callback.Run(start_result, local_merge_result, syncer_merge_result); + start_callback_.Run(start_result, local_merge_result, syncer_merge_result); } void NonUIDataTypeController::RecordAssociationTime(base::TimeDelta time) { @@ -259,7 +241,7 @@ void NonUIDataTypeController::RecordAssociationTime(base::TimeDelta time) { #undef PER_DATA_TYPE_MACRO } -void NonUIDataTypeController::RecordStartFailure(StartResult result) { +void NonUIDataTypeController::RecordStartFailure(ConfigureResult result) { DCHECK(ui_thread_->BelongsToCurrentThread()); UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", ModelTypeToHistogramInt(type()), @@ -284,11 +266,18 @@ void NonUIDataTypeController::AbortModelLoad() { } void NonUIDataTypeController::DisableImpl( - const tracked_objects::Location& from_here, - const std::string& message) { + const syncer::SyncError& error) { DCHECK(ui_thread_->BelongsToCurrentThread()); - if (!disable_callback().is_null()) - disable_callback().Run(from_here, message); + UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", + ModelTypeToHistogramInt(type()), + syncer::MODEL_TYPE_COUNT); + if (!start_callback_.is_null()) { + syncer::SyncMergeResult local_merge_result(type()); + local_merge_result.set_error(error); + start_callback_.Run(RUNTIME_ERROR, + local_merge_result, + syncer::SyncMergeResult(type())); + } } bool NonUIDataTypeController::StartAssociationAsync() { @@ -348,6 +337,11 @@ void NonUIDataTypeController:: } if (!shared_change_processor->CryptoReadyIfNecessary()) { + syncer::SyncError error(FROM_HERE, + syncer::SyncError::CRYPTO_ERROR, + "", + type()); + local_merge_result.set_error(error); StartDone(NEEDS_CRYPTO, local_merge_result, syncer_merge_result); diff --git a/components/sync_driver/non_ui_data_type_controller.h b/components/sync_driver/non_ui_data_type_controller.h index 4143906ae15a4..0f4fdabc9425e 100644 --- a/components/sync_driver/non_ui_data_type_controller.h +++ b/components/sync_driver/non_ui_data_type_controller.h @@ -27,7 +27,6 @@ class NonUIDataTypeController : public DataTypeController { NonUIDataTypeController( scoped_refptr ui_thread, const base::Closure& error_callback, - const DisableTypeCallback& disable_callback, SyncApiComponentFactory* sync_factory); // DataTypeController interface. @@ -40,9 +39,8 @@ class NonUIDataTypeController : public DataTypeController { virtual ChangeProcessor* GetChangeProcessor() const OVERRIDE; virtual std::string name() const OVERRIDE; virtual State state() const OVERRIDE; - virtual void OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) OVERRIDE; + virtual void OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) OVERRIDE; protected: // For testing only. @@ -77,13 +75,13 @@ class NonUIDataTypeController : public DataTypeController { // Start up complete, update the state and invoke the callback. // Note: this is performed on the datatype's thread. virtual void StartDone( - DataTypeController::StartResult start_result, + DataTypeController::ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result); // UI thread implementation of StartDone. virtual void StartDoneImpl( - DataTypeController::StartResult start_result, + DataTypeController::ConfigureResult start_result, DataTypeController::State new_state, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result); @@ -95,7 +93,7 @@ class NonUIDataTypeController : public DataTypeController { virtual void RecordAssociationTime(base::TimeDelta time); // Record causes of start failure. - virtual void RecordStartFailure(StartResult result); + virtual void RecordStartFailure(ConfigureResult result); // To allow unit tests to control thread interaction during non-ui startup // and shutdown, use a factory method to create the SharedChangeProcessor. @@ -125,8 +123,7 @@ class NonUIDataTypeController : public DataTypeController { // Disable this type with the sync service. Should only be invoked in case of // an unrecoverable error. // Note: this is performed on the UI thread. - void DisableImpl(const tracked_objects::Location& from_here, - const std::string& message); + void DisableImpl(const syncer::SyncError& error); SyncApiComponentFactory* const sync_factory_; diff --git a/components/sync_driver/non_ui_data_type_controller_mock.h b/components/sync_driver/non_ui_data_type_controller_mock.h index 68f889fa47473..3716a356fd8a6 100644 --- a/components/sync_driver/non_ui_data_type_controller_mock.h +++ b/components/sync_driver/non_ui_data_type_controller_mock.h @@ -26,9 +26,8 @@ class NonUIDataTypeControllerMock MOCK_CONST_METHOD0(name, std::string()); MOCK_CONST_METHOD0(model_safe_group, syncer::ModelSafeGroup()); MOCK_CONST_METHOD0(state, State()); - MOCK_METHOD2(OnSingleDataTypeUnrecoverableError, - void(const tracked_objects::Location&, - const std::string&)); + MOCK_METHOD1(OnSingleDataTypeUnrecoverableError, + void(const syncer::SyncError& error)); // NonUIDataTypeController mocks. MOCK_METHOD0(StartModels, bool()); @@ -37,16 +36,16 @@ class NonUIDataTypeControllerMock bool(const tracked_objects::Location&, const base::Closure&)); MOCK_METHOD3(StartDone, - void(DataTypeController::StartResult result, + void(DataTypeController::ConfigureResult result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result)); MOCK_METHOD4(StartDoneImpl, - void(DataTypeController::StartResult result, + void(DataTypeController::ConfigureResult result, DataTypeController::State new_state, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result)); MOCK_METHOD1(RecordAssociationTime, void(base::TimeDelta time)); - MOCK_METHOD1(RecordStartFailure, void(StartResult result)); + MOCK_METHOD1(RecordStartFailure, void(ConfigureResult result)); protected: virtual ~NonUIDataTypeControllerMock(); diff --git a/components/sync_driver/non_ui_data_type_controller_unittest.cc b/components/sync_driver/non_ui_data_type_controller_unittest.cc index 869a8bb284352..61720192e1d92 100644 --- a/components/sync_driver/non_ui_data_type_controller_unittest.cc +++ b/components/sync_driver/non_ui_data_type_controller_unittest.cc @@ -96,12 +96,10 @@ class NonUIDataTypeControllerFake SyncApiComponentFactory* sync_factory, NonUIDataTypeControllerMock* mock, SharedChangeProcessor* change_processor, - const DisableTypeCallback& disable_callback, scoped_refptr backend_loop) : NonUIDataTypeController( base::MessageLoopProxy::current(), base::Closure(), - disable_callback, sync_factory), blocked_(false), mock_(mock), @@ -159,7 +157,7 @@ class NonUIDataTypeControllerFake virtual void RecordAssociationTime(base::TimeDelta time) OVERRIDE { mock_->RecordAssociationTime(time); } - virtual void RecordStartFailure(DataTypeController::StartResult result) + virtual void RecordStartFailure(DataTypeController::ConfigureResult result) OVERRIDE { mock_->RecordStartFailure(result); } @@ -188,22 +186,17 @@ class NonUIDataTypeControllerFake class SyncNonUIDataTypeControllerTest : public testing::Test { public: SyncNonUIDataTypeControllerTest() - : backend_thread_("dbthread"), - disable_callback_invoked_(false) {} + : backend_thread_("dbthread") {} virtual void SetUp() OVERRIDE { backend_thread_.Start(); change_processor_ = new SharedChangeProcessorMock(); // All of these are refcounted, so don't need to be released. dtc_mock_ = new StrictMock(); - DataTypeController::DisableTypeCallback disable_callback = - base::Bind(&SyncNonUIDataTypeControllerTest::DisableTypeCallback, - base::Unretained(this)); non_ui_dtc_ = new NonUIDataTypeControllerFake(NULL, dtc_mock_.get(), change_processor_, - disable_callback, backend_thread_.message_loop_proxy()); } @@ -243,7 +236,7 @@ class SyncNonUIDataTypeControllerTest : public testing::Test { EXPECT_CALL(*dtc_mock_.get(), RecordAssociationTime(_)); } - void SetActivateExpectations(DataTypeController::StartResult result) { + void SetActivateExpectations(DataTypeController::ConfigureResult result) { EXPECT_CALL(start_callback_, Run(result,_,_)); } @@ -252,7 +245,7 @@ class SyncNonUIDataTypeControllerTest : public testing::Test { EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true)); } - void SetStartFailExpectations(DataTypeController::StartResult result) { + void SetStartFailExpectations(DataTypeController::ConfigureResult result) { EXPECT_CALL(*dtc_mock_.get(), StopModels()).Times(AtLeast(1)); EXPECT_CALL(*dtc_mock_.get(), RecordStartFailure(result)); EXPECT_CALL(start_callback_, Run(result, _, _)); @@ -271,12 +264,6 @@ class SyncNonUIDataTypeControllerTest : public testing::Test { done->Signal(); } - void DisableTypeCallback(const tracked_objects::Location& location, - const std::string& message) { - disable_callback_invoked_ = true; - non_ui_dtc_->Stop(); - } - base::MessageLoopForUI message_loop_; base::Thread backend_thread_; @@ -288,8 +275,6 @@ class SyncNonUIDataTypeControllerTest : public testing::Test { scoped_refptr dtc_mock_; scoped_refptr change_processor_; scoped_ptr saved_change_processor_; - - bool disable_callback_invoked_; }; TEST_F(SyncNonUIDataTypeControllerTest, StartOk) { @@ -404,7 +389,6 @@ TEST_F(SyncNonUIDataTypeControllerTest, AbortDuringAssociation) { WaitableEvent pause_db_thread(false, false); SetStartExpectations(); - SetStartFailExpectations(DataTypeController::ABORTED); EXPECT_CALL(*change_processor_.get(), Connect(_, _, _, _, _, _)) .WillOnce(GetWeakPtrToSyncableService(&syncable_service_)); EXPECT_CALL(*change_processor_.get(), CryptoReadyIfNecessary()) @@ -439,10 +423,6 @@ TEST_F(SyncNonUIDataTypeControllerTest, StartAfterSyncShutdown) { // We don't expect StopSyncing to be called because local_service_ will never // have been set. EXPECT_CALL(*change_processor_.get(), Disconnect()).WillOnce(Return(true)); - EXPECT_CALL(*dtc_mock_.get(), StopModels()); - EXPECT_CALL(*dtc_mock_.get(), - RecordStartFailure(DataTypeController::ABORTED)); - EXPECT_CALL(start_callback_, Run(DataTypeController::ABORTED, _, _)); EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); Start(); non_ui_dtc_->Stop(); @@ -496,26 +476,27 @@ TEST_F(SyncNonUIDataTypeControllerTest, StopStart) { EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); } -TEST_F(SyncNonUIDataTypeControllerTest, - OnSingleDatatypeUnrecoverableError) { +TEST_F(SyncNonUIDataTypeControllerTest, OnSingleDataTypeUnrecoverableError) { SetStartExpectations(); SetAssociateExpectations(); SetActivateExpectations(DataTypeController::OK); - SetStopExpectations(); EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); Start(); WaitForDTC(); EXPECT_EQ(DataTypeController::RUNNING, non_ui_dtc_->state()); - // This should cause non_ui_dtc_->Stop() to be called. + + testing::Mock::VerifyAndClearExpectations(&start_callback_); + EXPECT_CALL(start_callback_, Run(DataTypeController::RUNTIME_ERROR, _, _)); + syncer::SyncError error(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "error", + non_ui_dtc_->type()); backend_thread_.message_loop_proxy()->PostTask(FROM_HERE, base::Bind( &NonUIDataTypeControllerFake:: - OnSingleDatatypeUnrecoverableError, + OnSingleDataTypeUnrecoverableError, non_ui_dtc_.get(), - FROM_HERE, - std::string("Test"))); + error)); WaitForDTC(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, non_ui_dtc_->state()); - EXPECT_TRUE(disable_callback_invoked_); } } // namespace diff --git a/components/sync_driver/proxy_data_type_controller.cc b/components/sync_driver/proxy_data_type_controller.cc index ae1ea53bbb51c..c017802975cbe 100644 --- a/components/sync_driver/proxy_data_type_controller.cc +++ b/components/sync_driver/proxy_data_type_controller.cc @@ -9,7 +9,7 @@ namespace sync_driver { ProxyDataTypeController::ProxyDataTypeController( scoped_refptr ui_thread, syncer::ModelType type) - : DataTypeController(ui_thread, base::Closure(), DisableTypeCallback()), + : DataTypeController(ui_thread, base::Closure()), state_(NOT_RUNNING), type_(type) { DCHECK(syncer::ProxyTypes().Has(type_)); @@ -61,8 +61,8 @@ DataTypeController::State ProxyDataTypeController::state() const { return state_; } -void ProxyDataTypeController::OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, const std::string& message) { +void ProxyDataTypeController::OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) { NOTIMPLEMENTED(); } diff --git a/components/sync_driver/proxy_data_type_controller.h b/components/sync_driver/proxy_data_type_controller.h index e90ee2409c338..6e255c659cb28 100644 --- a/components/sync_driver/proxy_data_type_controller.h +++ b/components/sync_driver/proxy_data_type_controller.h @@ -32,9 +32,8 @@ class ProxyDataTypeController : public DataTypeController { virtual State state() const OVERRIDE; // DataTypeErrorHandler interface. - virtual void OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) OVERRIDE; + virtual void OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) OVERRIDE; protected: // DataTypeController is RefCounted. diff --git a/components/sync_driver/ui_data_type_controller.cc b/components/sync_driver/ui_data_type_controller.cc index edfe452acad5a..23487a2edb8e4 100644 --- a/components/sync_driver/ui_data_type_controller.cc +++ b/components/sync_driver/ui_data_type_controller.cc @@ -17,8 +17,7 @@ namespace sync_driver { UIDataTypeController::UIDataTypeController() : DataTypeController(base::MessageLoopProxy::current(), - base::Closure(), - DisableTypeCallback()), + base::Closure()), sync_factory_(NULL), state_(NOT_RUNNING), type_(syncer::UNSPECIFIED) { @@ -27,10 +26,9 @@ UIDataTypeController::UIDataTypeController() UIDataTypeController::UIDataTypeController( scoped_refptr ui_thread, const base::Closure& error_callback, - const DisableTypeCallback& disable_callback, syncer::ModelType type, SyncApiComponentFactory* sync_factory) - : DataTypeController(ui_thread, error_callback, disable_callback), + : DataTypeController(ui_thread, error_callback), sync_factory_(sync_factory), state_(NOT_RUNNING), type_(type), @@ -145,6 +143,11 @@ void UIDataTypeController::Associate() { } if (!shared_change_processor_->CryptoReadyIfNecessary()) { + syncer::SyncError error(FROM_HERE, + syncer::SyncError::CRYPTO_ERROR, + "", + type()); + local_merge_result.set_error(error); StartDone(NEEDS_CRYPTO, local_merge_result, syncer_merge_result); @@ -237,7 +240,7 @@ void UIDataTypeController::AbortModelLoad() { } void UIDataTypeController::StartDone( - StartResult start_result, + ConfigureResult start_result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result) { DCHECK(ui_thread_->BelongsToCurrentThread()); @@ -257,18 +260,16 @@ void UIDataTypeController::StartDone( } } - // We have to release the callback before we call it, since it's possible - // invoking the callback will trigger a call to Stop(), which will get - // confused by the non-NULL start_callback_. - StartCallback callback = start_callback_; - start_callback_.Reset(); - callback.Run(start_result, local_merge_result, syncer_merge_result); + start_callback_.Run(start_result, local_merge_result, syncer_merge_result); } void UIDataTypeController::Stop() { DCHECK(ui_thread_->BelongsToCurrentThread()); DCHECK(syncer::IsRealDataType(type_)); + if (state_ == NOT_RUNNING) + return; + State prev_state = state_; state_ = STOPPING; @@ -285,7 +286,6 @@ void UIDataTypeController::Stop() { // still in MODEL_STARTING. return; } - DCHECK(start_callback_.is_null()); StopModels(); @@ -319,16 +319,25 @@ DataTypeController::State UIDataTypeController::state() const { return state_; } -void UIDataTypeController::OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, const std::string& message) { +void UIDataTypeController::OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) { + DCHECK_EQ(type(), error.model_type()); UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeRunFailures", ModelTypeToHistogramInt(type()), syncer::MODEL_TYPE_COUNT); // TODO(tim): We double-upload some errors. See bug 383480. if (!error_callback_.is_null()) error_callback_.Run(); - if (!disable_callback().is_null()) - disable_callback().Run(from_here, message); + if (!start_callback_.is_null()) { + syncer::SyncMergeResult local_merge_result(type()); + local_merge_result.set_error(error); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(start_callback_, + RUNTIME_ERROR, + local_merge_result, + syncer::SyncMergeResult(type()))); + } } void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) { @@ -339,7 +348,7 @@ void UIDataTypeController::RecordAssociationTime(base::TimeDelta time) { #undef PER_DATA_TYPE_MACRO } -void UIDataTypeController::RecordStartFailure(StartResult result) { +void UIDataTypeController::RecordStartFailure(ConfigureResult result) { DCHECK(ui_thread_->BelongsToCurrentThread()); UMA_HISTOGRAM_ENUMERATION("Sync.DataTypeStartFailures", ModelTypeToHistogramInt(type()), diff --git a/components/sync_driver/ui_data_type_controller.h b/components/sync_driver/ui_data_type_controller.h index 3d609a8108f2c..30289919e84c1 100644 --- a/components/sync_driver/ui_data_type_controller.h +++ b/components/sync_driver/ui_data_type_controller.h @@ -34,7 +34,6 @@ class UIDataTypeController : public DataTypeController { UIDataTypeController( scoped_refptr ui_thread, const base::Closure& error_callback, - const DisableTypeCallback& disable_callback, syncer::ModelType type, SyncApiComponentFactory* sync_factory); @@ -50,9 +49,8 @@ class UIDataTypeController : public DataTypeController { virtual State state() const OVERRIDE; // DataTypeErrorHandler interface. - virtual void OnSingleDatatypeUnrecoverableError( - const tracked_objects::Location& from_here, - const std::string& message) OVERRIDE; + virtual void OnSingleDataTypeUnrecoverableError( + const syncer::SyncError& error) OVERRIDE; // Used by tests to override the factory used to create // GenericChangeProcessors. @@ -81,14 +79,14 @@ class UIDataTypeController : public DataTypeController { virtual void OnModelLoaded() OVERRIDE; // Helper method for cleaning up state and invoking the start callback. - virtual void StartDone(StartResult result, + virtual void StartDone(ConfigureResult result, const syncer::SyncMergeResult& local_merge_result, const syncer::SyncMergeResult& syncer_merge_result); // Record association time. virtual void RecordAssociationTime(base::TimeDelta time); // Record causes of start failure. - virtual void RecordStartFailure(StartResult result); + virtual void RecordStartFailure(ConfigureResult result); SyncApiComponentFactory* const sync_factory_; diff --git a/components/sync_driver/ui_data_type_controller_unittest.cc b/components/sync_driver/ui_data_type_controller_unittest.cc index 0b2c3e0665dbc..20bd3046e1062 100644 --- a/components/sync_driver/ui_data_type_controller_unittest.cc +++ b/components/sync_driver/ui_data_type_controller_unittest.cc @@ -15,6 +15,7 @@ #include "testing/gtest/include/gtest/gtest.h" using testing::_; +using testing::Invoke; using testing::InvokeWithoutArgs; using testing::Return; @@ -30,17 +31,13 @@ class SyncUIDataTypeControllerTest : public testing::Test, public: SyncUIDataTypeControllerTest() : type_(syncer::PREFERENCES), - change_processor_(NULL), - disable_callback_invoked_(false) {} + change_processor_(NULL) {} virtual void SetUp() { preference_dtc_ = new UIDataTypeController( base::MessageLoopProxy::current(), base::Closure(), - base::Bind(&SyncUIDataTypeControllerTest::DisableTypeCallback, - base::Unretained(this), - type_), type_, this); SetStartExpectations(); @@ -88,13 +85,6 @@ class SyncUIDataTypeControllerTest : public testing::Test, message_loop_.RunUntilIdle(); } - void DisableTypeCallback(syncer::ModelType type, - const tracked_objects::Location& location, - const std::string& message) { - disable_callback_invoked_ = true; - preference_dtc_->Stop(); - } - base::MessageLoopForUI message_loop_; const syncer::ModelType type_; StartCallbackMock start_callback_; @@ -102,7 +92,6 @@ class SyncUIDataTypeControllerTest : public testing::Test, scoped_refptr preference_dtc_; FakeGenericChangeProcessor* change_processor_; syncer::FakeSyncableService syncable_service_; - bool disable_callback_invoked_; }; // Start the DTC. Verify that the callback is called with OK, the @@ -196,11 +185,14 @@ TEST_F(SyncUIDataTypeControllerTest, OnSingleDatatypeUnrecoverableError) { EXPECT_FALSE(syncable_service_.syncing()); Start(); EXPECT_TRUE(syncable_service_.syncing()); - preference_dtc_->OnSingleDatatypeUnrecoverableError(FROM_HERE, "Test"); - PumpLoop(); - EXPECT_EQ(DataTypeController::NOT_RUNNING, preference_dtc_->state()); - EXPECT_TRUE(disable_callback_invoked_); - EXPECT_FALSE(syncable_service_.syncing()); + + testing::Mock::VerifyAndClearExpectations(&start_callback_); + EXPECT_CALL(start_callback_, Run(DataTypeController::RUNTIME_ERROR, _, _)); + syncer::SyncError error(FROM_HERE, + syncer::SyncError::DATATYPE_ERROR, + "error", + syncer::PREFERENCES); + preference_dtc_->OnSingleDataTypeUnrecoverableError(error); } } // namespace diff --git a/components/test/data/password_manager/environment.py b/components/test/data/password_manager/environment.py index 1baa316a0efd8..c508b0221c3a6 100644 --- a/components/test/data/password_manager/environment.py +++ b/components/test/data/password_manager/environment.py @@ -172,14 +172,29 @@ def RemoveAllPasswords(self): self.driver.switch_to_frame("settings") while True: try: - self.driver.execute_script("document.querySelector('" - "#saved-passwords-list .row-delete-button').click()") + self.driver.execute_script( + "document.querySelector('#saved-passwords-list .row-delete-button')" + ".click()") time.sleep(1) except NoSuchElementException: break except WebDriverException: break + def ClearAllCookies(self): + """Removes all the cookies.""" + logging.info("\nClearAllCookies\n") + self.driver.get("chrome://settings/clearBrowserData") + self.driver.switch_to_frame("settings") + self.driver.execute_script( + "var checkboxes = document.querySelectorAll(" + " '#clear-data-checkboxes [type=\\\'checkbox\\\']');" + "for (var i in checkboxes)" + " checkboxes[i].checked = false;" + "document.querySelector('#delete-cookies-checkbox').checked = true;" + "document.querySelector('#clear-browser-data-commit').click();") + time.sleep(2) + def OpenTabAndGoToInternals(self, url): """If there is no |self.website_window|, opens a new tab and navigates to |url| in the new tab. Navigates to the passwords internals page in the diff --git a/components/test/data/password_manager/tests.py b/components/test/data/password_manager/tests.py index bdefcaa5dcaa9..557ce4100fddf 100644 --- a/components/test/data/password_manager/tests.py +++ b/components/test/data/password_manager/tests.py @@ -35,12 +35,6 @@ def Login(self): self.FillPasswordInto("[name='pass']") self.Submit("[name='pass']") - def Logout(self): - self.WaitUntilDisplayed("#userNavigationLabel") - self.Click("#userNavigationLabel") - self.WaitUntilDisplayed("#logout_form [type='submit']") - self.Click("#logout_form [type='submit']") - class Google(WebsiteTest): @@ -50,9 +44,6 @@ def Login(self): self.FillPasswordInto("#Passwd") self.Submit("#Passwd") - def Logout(self): - self.GoTo("https://accounts.google.com/Logout") - class Linkedin(WebsiteTest): @@ -62,12 +53,6 @@ def Login(self): self.FillPasswordInto("#session_password-login") self.Submit("#session_password-login") - def Logout(self): - self.WaitUntilDisplayed(".account-toggle") - self.HoverOver(".account-toggle") - self.WaitUntilDisplayed(".account-settings .act-set-action") - self.Click(".account-settings .act-set-action") - class Mailru(WebsiteTest): @@ -77,9 +62,6 @@ def Login(self): self.FillPasswordInto("#mailbox__password") self.Submit("#mailbox__password") - def Logout(self): - self.Click("#PH_logoutLink") - class Nytimes(WebsiteTest): @@ -89,9 +71,6 @@ def Login(self): self.FillPasswordInto("#password") self.Submit("#password") - def Logout(self): - self.GoTo("https://myaccount.nytimes.com/gst/signout") - class Pinterest(WebsiteTest): @@ -101,9 +80,6 @@ def Login(self): self.FillPasswordInto("[name='password']") self.Submit("[name='password']") - def Logout(self): - self.GoTo("https://www.pinterest.com/logout/") - class Reddit(WebsiteTest): @@ -115,9 +91,6 @@ def Login(self): self.Wait(2) self.Submit("#passwd_login") - def Logout(self): - self.Click("form[action='http://www.reddit.com/logout'] a") - class Tumblr(WebsiteTest): @@ -127,9 +100,6 @@ def Login(self): self.FillPasswordInto("#signup_password") self.Submit("#signup_password") - def Logout(self): - self.GoTo("https://www.tumblr.com/logout") - class Wikipedia(WebsiteTest): @@ -139,9 +109,6 @@ def Login(self): self.FillPasswordInto("#wpPassword1") self.Submit("#wpPassword1") - def Logout(self): - self.GoTo("https://en.wikipedia.org/w/index.php?title=Special:UserLogout") - class Yandex(WebsiteTest): @@ -151,13 +118,6 @@ def Login(self): self.FillPasswordInto("#b-mail-domik-password11") self.Click(".b-mail-button__button") - def Logout(self): - while not self.IsDisplayed(".b-mail-dropdown__item__content" - u".Выход.daria-action"): - self.ClickIfClickable(".header-user-pic.b-mail-dropdown__handle") - self.Wait(1) - self.Click(u".b-mail-dropdown__item__content.Выход.daria-action") - # Disabled tests. @@ -174,12 +134,6 @@ def Login(self): self.FillPasswordInto("[name='password']") self.Submit("[name='password']") - def Logout(self): - while not self.IsDisplayed("#nav-item-signout"): - self.Wait(1) - self.HoverOver("#nav-signin-title") - self.Click("#nav-item-signout") - # Password not saved. class Ask(WebsiteTest): @@ -193,10 +147,6 @@ def Login(self): self.FillPasswordInto("[name='password']") self.Click(".signin_show.signin_submit") - def Logout(self): - self.WaitUntilDisplayed("#a16CnbSignInText") - self.Click("#a16CnbSignInText") - # Password not saved. class Baidu(WebsiteTest): @@ -209,10 +159,6 @@ def Login(self): self.FillPasswordInto("[name='password']") self.Submit("[name='password']") - def Logout(self): - self.Wait(1) - self.GoTo("https://passport.baidu.com/?logout&u=http://www.baidu.com") - # http://crbug.com/368690 class Cnn(WebsiteTest): @@ -231,10 +177,6 @@ def Login(self): self.Click(".cnnOvrlyBtn.cnnBtnLogIn") self.Wait(5) - def Logout(self): - self.Wait(4) - self.Click("#hdr-auth .no-border.no-pad-right") - # http://crbug.com/368690 class Ebay(WebsiteTest): @@ -245,12 +187,6 @@ def Login(self): self.FillPasswordInto("[name='pass']") self.Submit("[name='pass']") - def Logout(self): - self.WaitUntilDisplayed("#gh-ug") - self.Click("#gh-ug") - self.WaitUntilDisplayed("#gh-uo") - self.Click("#gh-uo") - # Iframe, password saved but not autofileld. class Espn(WebsiteTest): @@ -270,10 +206,6 @@ def Login(self): self.ClickIfClickable("#submitBtn") self.Wait(1) - def Logout(self): - self.WaitUntilDisplayed("#signin .small") - self.Click("#signin .small") - # http://crbug.com/367768 class Live(WebsiteTest): @@ -284,12 +216,6 @@ def Login(self): self.FillPasswordInto("[name='passwd']") self.Submit("[name='passwd']") - def Logout(self): - self.WaitUntilDisplayed("#c_meun") - self.Click("#c_meun") - self.WaitUntilDisplayed("#c_signout") - self.Click("#c_signout") - # http://crbug.com/368690 class One63(WebsiteTest): @@ -302,10 +228,6 @@ def Login(self): self.FillPasswordInto(".ntes-loginframe-label-ipt[type='password']") self.Click(".ntes-loginframe-btn") - def Logout(self): - self.WaitUntilDisplayed("#js_N_navLogout") - self.Click("#js_N_navLogout") - # http://crbug.com/368690 class Vube(WebsiteTest): @@ -321,10 +243,6 @@ def Login(self): self.ClickIfClickable("[ng-click='login()']") self.Wait(1) - def Logout(self): - self.WaitUntilDisplayed("[ng-click='user.logout()']") - self.Click("[ng-click='user.logout()']") - # Tests that can cause a crash. @@ -337,12 +255,6 @@ def Login(self): self.FillPasswordInto("#passwd") self.Submit("#passwd") - def Logout(self): - self.WaitUntilDisplayed(".tab.tab-user>.mod.view_default") - self.HoverOver(".tab.tab-user>.mod.view_default") - self.WaitUntilDisplayed("[data-pos='4'] .lbl.y-link-1") - self.Click("[data-pos='4'] .lbl.y-link-1") - def Tests(environment): diff --git a/components/test/data/password_manager/websitetest.py b/components/test/data/password_manager/websitetest.py index 8bc619a2d0c2a..9a72b44652dd7 100644 --- a/components/test/data/password_manager/websitetest.py +++ b/components/test/data/password_manager/websitetest.py @@ -294,8 +294,8 @@ def LoginWhenNotAutofilled(self): self.Login() def Logout(self): - """Logout Method. Has to be overloaded by the Website test.""" - raise NotImplementedError("Logout is not implemented.") + """Logout Method.""" + self.environment.ClearAllCookies() # Tests diff --git a/components/test/run_all_unittests.cc b/components/test/run_all_unittests.cc index ca4e064fcf58a..1cee176940ba7 100644 --- a/components/test/run_all_unittests.cc +++ b/components/test/run_all_unittests.cc @@ -76,7 +76,8 @@ class ComponentsTestSuite : public base::TestSuite { // TODO(tfarina): This should be changed to InitSharedInstanceWithPakFile() // so we can load our pak file instead of chrome.pak. crbug.com/348563 - ui::ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); base::FilePath resources_pack_path; PathService::Get(base::DIR_MODULE, &resources_pack_path); ui::ResourceBundle::GetSharedInstance().AddDataPackFromPath( diff --git a/components/url_fixer/url_fixer.cc b/components/url_fixer/url_fixer.cc index 5b90cd203e1ee..a0417328ba18d 100644 --- a/components/url_fixer/url_fixer.cc +++ b/components/url_fixer/url_fixer.cc @@ -660,3 +660,10 @@ void url_fixer::OffsetComponent(int offset, url::Component* part) { part->reset(); } } + +bool url_fixer::IsEquivalentScheme(const std::string& scheme1, + const std::string& scheme2) { + return scheme1 == scheme2 || + (scheme1 == url::kAboutScheme && scheme2 == kChromeUIScheme) || + (scheme1 == kChromeUIScheme && scheme2 == url::kAboutScheme); +} diff --git a/components/url_fixer/url_fixer.h b/components/url_fixer/url_fixer.h index fa682d74a5898..baf14c69b95c1 100644 --- a/components/url_fixer/url_fixer.h +++ b/components/url_fixer/url_fixer.h @@ -21,64 +21,66 @@ struct Parsed; // This object is designed to convert various types of input into URLs that we // know are valid. For example, user typing in the URL bar or command line -// options. This is NOT the place for converting between different types of -// URLs or parsing them, see net_util.h for that. +// options. This is NOT the place for converting between different types of URLs +// or parsing them, see net_util.h for that. namespace url_fixer { - // Segments the given text string into parts of a URL. This is most useful - // for schemes such as http, https, and ftp where |SegmentURL| will find many - // segments. Currently does not segment "file" schemes. - // Returns the canonicalized scheme, or the empty string when |text| is only - // whitespace. +// Segments the given text string into parts of a URL. This is most useful for +// schemes such as http, https, and ftp where |SegmentURL| will find many +// segments. Currently does not segment "file" schemes. +// Returns the canonicalized scheme, or the empty string when |text| is only +// whitespace. std::string SegmentURL(const std::string& text, url::Parsed* parts); base::string16 SegmentURL(const base::string16& text, url::Parsed* parts); - // Converts |text| to a fixed-up URL and returns it. Attempts to make - // some "smart" adjustments to obviously-invalid input where possible. - // |text| may be an absolute path to a file, which will get converted to a - // "file:" URL. - // - // The result will be a "more" valid URL than the input. It may still not - // be valid, so check the return value's validity or use - // possibly_invalid_spec(). - // - // Schemes "about" and "chrome" are normalized to "chrome://", with slashes. - // "about:blank" is unaltered, as Webkit allows frames to access about:blank. - // Additionally, if a chrome URL does not have a valid host, as in "about:", - // the returned URL will have the host "version", as in "chrome://version". - // - // If |desired_tld| is non-empty, it represents the TLD the user wishes to - // append in the case of an incomplete domain. We check that this is not a - // file path and there does not appear to be a valid TLD already, then append - // |desired_tld| to the domain and prepend "www." (unless it, or a scheme, - // are already present.) This TLD should not have a leading '.' (use "com" - // instead of ".com"). - GURL FixupURL(const std::string& text, const std::string& desired_tld); +// Converts |text| to a fixed-up URL and returns it. Attempts to make some +// "smart" adjustments to obviously-invalid input where possible. +// |text| may be an absolute path to a file, which will get converted to a +// "file:" URL. +// +// The result will be a "more" valid URL than the input. It may still not be +// valid, so check the return value's validity or use possibly_invalid_spec(). +// +// Schemes "about" and "chrome" are normalized to "chrome://", with slashes. +// "about:blank" is unaltered, as Webkit allows frames to access about:blank. +// Additionally, if a chrome URL does not have a valid host, as in "about:", the +// returned URL will have the host "version", as in "chrome://version". +// +// If |desired_tld| is non-empty, it represents the TLD the user wishes to +// append in the case of an incomplete domain. We check that this is not a file +// path and there does not appear to be a valid TLD already, then append +// |desired_tld| to the domain and prepend "www." (unless it, or a scheme, are +// already present.) This TLD should not have a leading '.' (use "com" instead +// of ".com"). +GURL FixupURL(const std::string& text, const std::string& desired_tld); - // Converts |text| to a fixed-up URL, allowing it to be a relative path on - // the local filesystem. Begin searching in |base_dir|; if empty, use the - // current working directory. If this resolves to a file on disk, convert it - // to a "file:" URL in |fixed_up_url|; otherwise, fall back to the behavior - // of FixupURL(). - // - // For "regular" input, even if it is possibly a file with a full path, you - // should use FixupURL() directly. This function should only be used when - // relative path handling is desired, as for command line processing. - GURL FixupRelativeFile(const base::FilePath& base_dir, - const base::FilePath& text); +// Converts |text| to a fixed-up URL, allowing it to be a relative path on the +// local filesystem. Begin searching in |base_dir|; if empty, use the current +// working directory. If this resolves to a file on disk, convert it to a +// "file:" URL in |fixed_up_url|; otherwise, fall back to the behavior of +// FixupURL(). +// +// For "regular" input, even if it is possibly a file with a full path, you +// should use FixupURL() directly. This function should only be used when +// relative path handling is desired, as for command line processing. +GURL FixupRelativeFile(const base::FilePath& base_dir, + const base::FilePath& text); - // Offsets the beginning index of |part| by |offset|, which is allowed to be - // negative. In some cases, the desired component does not exist at the given - // offset. For example, when converting from "http://foo" to "foo", the - // scheme component no longer exists. In such a case, the beginning index is - // set to 0. - // Does nothing if |part| is invalid. - void OffsetComponent(int offset, url::Component* part); +// Offsets the beginning index of |part| by |offset|, which is allowed to be +// negative. In some cases, the desired component does not exist at the given +// offset. For example, when converting from "http://foo" to "foo", the scheme +// component no longer exists. In such a case, the beginning index is set to 0. +// Does nothing if |part| is invalid. +void OffsetComponent(int offset, url::Component* part); - // For paths like ~, we use $HOME for the current user's home - // directory. For tests, we allow our idea of $HOME to be overriden - // by this variable. - extern const char* home_directory_override; +// Returns true if |scheme1| is equivalent to |scheme2|. +// Generally this is true if the two schemes are actually identical, but it's +// also true when one scheme is "about" and the other "chrome". +bool IsEquivalentScheme(const std::string& scheme1, const std::string& scheme2); + +// For paths like ~, we use $HOME for the current user's home directory. +// For tests, we allow our idea of $HOME to be overriden by this variable. +extern const char* home_directory_override; } // namespace url_fixer diff --git a/components/url_fixer/url_fixer_unittest.cc b/components/url_fixer/url_fixer_unittest.cc index 18820d8741bbe..9eb330bd87b9d 100644 --- a/components/url_fixer/url_fixer_unittest.cc +++ b/components/url_fixer/url_fixer_unittest.cc @@ -322,24 +322,41 @@ TEST(URLFixerTest, FixupURL) { // Check the TLD-appending functionality. FixupCase tld_cases[] = { - {"google", "http://www.google.com/"}, - {"google.", "http://www.google.com/"}, - {"google..", "http://www.google.com/"}, - {".google", "http://www.google.com/"}, - {"www.google", "http://www.google.com/"}, - {"google.com", "http://google.com/"}, - {"http://google", "http://www.google.com/"}, - {"..google..", "http://www.google.com/"}, - {"http://www.google", "http://www.google.com/"}, - {"9999999999999999", "http://www.9999999999999999.com/"}, - {"google/foo", "http://www.google.com/foo"}, - {"google.com/foo", "http://google.com/foo"}, - {"google/?foo=.com", "http://www.google.com/?foo=.com"}, - {"www.google/?foo=www.", "http://www.google.com/?foo=www."}, - {"google.com/?foo=.com", "http://google.com/?foo=.com"}, - {"http://www.google.com", "http://www.google.com/"}, - {"google:123", "http://www.google.com:123/"}, - {"http://google:123", "http://www.google.com:123/"}, + {"somedomainthatwillnotbeagtld", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"somedomainthatwillnotbeagtld.", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"somedomainthatwillnotbeagtld..", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {".somedomainthatwillnotbeagtld", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"www.somedomainthatwillnotbeagtld", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"somedomainthatwillnotbeagtld.com", + "http://somedomainthatwillnotbeagtld.com/"}, + {"http://somedomainthatwillnotbeagtld", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"..somedomainthatwillnotbeagtld..", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"http://www.somedomainthatwillnotbeagtld", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"9999999999999999", "http://www.9999999999999999.com/"}, + {"somedomainthatwillnotbeagtld/foo", + "http://www.somedomainthatwillnotbeagtld.com/foo"}, + {"somedomainthatwillnotbeagtld.com/foo", + "http://somedomainthatwillnotbeagtld.com/foo"}, + {"somedomainthatwillnotbeagtld/?foo=.com", + "http://www.somedomainthatwillnotbeagtld.com/?foo=.com"}, + {"www.somedomainthatwillnotbeagtld/?foo=www.", + "http://www.somedomainthatwillnotbeagtld.com/?foo=www."}, + {"somedomainthatwillnotbeagtld.com/?foo=.com", + "http://somedomainthatwillnotbeagtld.com/?foo=.com"}, + {"http://www.somedomainthatwillnotbeagtld.com", + "http://www.somedomainthatwillnotbeagtld.com/"}, + {"somedomainthatwillnotbeagtld:123", + "http://www.somedomainthatwillnotbeagtld.com:123/"}, + {"http://somedomainthatwillnotbeagtld:123", + "http://www.somedomainthatwillnotbeagtld.com:123/"}, }; for (size_t i = 0; i < arraysize(tld_cases); ++i) { FixupCase value = tld_cases[i]; diff --git a/components/usb_service.gypi b/components/usb_service.gypi index 5f85452225d35..ed165f72c9b91 100644 --- a/components/usb_service.gypi +++ b/components/usb_service.gypi @@ -27,6 +27,8 @@ 'usb_service/usb_device_impl.cc', 'usb_service/usb_device_impl.h', 'usb_service/usb_device.h', + 'usb_service/usb_device_filter.cc', + 'usb_service/usb_device_filter.h', 'usb_service/usb_device_handle_impl.cc', 'usb_service/usb_device_handle_impl.h', 'usb_service/usb_device_handle.h', diff --git a/components/usb_service/usb_device.h b/components/usb_service/usb_device.h index 076af18b1c065..48ca51678304a 100644 --- a/components/usb_service/usb_device.h +++ b/components/usb_service/usb_device.h @@ -31,7 +31,7 @@ class USB_SERVICE_EXPORT UsbDevice // permission broker can change the owner of the device so that the unclaimed // interfaces can be used. If this argument is missing, permission broker will // not be used and this method fails if the device is claimed. - virtual void RequestUsbAcess( + virtual void RequestUsbAccess( int interface_id, const base::Callback& callback) = 0; #endif // OS_CHROMEOS diff --git a/components/usb_service/usb_device_filter.cc b/components/usb_service/usb_device_filter.cc new file mode 100644 index 0000000000000..45f8d12d1f9d4 --- /dev/null +++ b/components/usb_service/usb_device_filter.cc @@ -0,0 +1,128 @@ +// 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/usb_service/usb_device_filter.h" + +#include "base/values.h" +#include "components/usb_service/usb_device.h" +#include "components/usb_service/usb_device_handle.h" +#include "components/usb_service/usb_interface.h" + +namespace usb_service { + +namespace { + +const char kProductIdKey[] = "productId"; +const char kVendorIdKey[] = "vendorId"; +const char kInterfaceClassKey[] = "interfaceClass"; +const char kInterfaceSubclassKey[] = "interfaceSubclass"; +const char kInterfaceProtocolKey[] = "interfaceProtocol"; + +} // namespace + +UsbDeviceFilter::UsbDeviceFilter() + : vendor_id_set_(false), + product_id_set_(false), + interface_class_set_(false), + interface_subclass_set_(false), + interface_protocol_set_(false) { +} + +UsbDeviceFilter::~UsbDeviceFilter() { +} + +void UsbDeviceFilter::SetVendorId(uint16 vendor_id) { + vendor_id_set_ = true; + vendor_id_ = vendor_id; +} + +void UsbDeviceFilter::SetProductId(uint16 product_id) { + product_id_set_ = true; + product_id_ = product_id; +} + +void UsbDeviceFilter::SetInterfaceClass(uint8 interface_class) { + interface_class_set_ = true; + interface_class_ = interface_class; +} + +void UsbDeviceFilter::SetInterfaceSubclass(uint8 interface_subclass) { + interface_subclass_set_ = true; + interface_subclass_ = interface_subclass; +} + +void UsbDeviceFilter::SetInterfaceProtocol(uint8 interface_protocol) { + interface_protocol_set_ = true; + interface_protocol_ = interface_protocol; +} + +bool UsbDeviceFilter::Matches(scoped_refptr device) { + if (vendor_id_set_) { + if (device->vendor_id() != vendor_id_) { + return false; + } + + if (product_id_set_ && device->product_id() != product_id_) { + return false; + } + } + + if (interface_class_set_) { + bool foundMatch = false; + scoped_refptr config = device->ListInterfaces(); + + // TODO(reillyg): Check device configuration if the class is not defined at + // a per-interface level. This is not really important because most devices + // have per-interface classes. The only counter-examples I know of are hubs. + + for (size_t i = 0; i < config->GetNumInterfaces() && !foundMatch; ++i) { + scoped_refptr iface = + config->GetInterface(i); + + for (size_t j = 0; j < iface->GetNumAltSettings() && !foundMatch; ++j) { + scoped_refptr altSetting = + iface->GetAltSetting(j); + + if (altSetting->GetInterfaceClass() == interface_class_ && + (!interface_subclass_set_ || + (altSetting->GetInterfaceSubclass() == interface_subclass_ && + (!interface_protocol_set_ || + altSetting->GetInterfaceProtocol() == interface_protocol_)))) { + foundMatch = true; + } + } + } + + if (!foundMatch) { + return false; + } + } + + return true; +} + +base::Value* UsbDeviceFilter::ToValue() const { + scoped_ptr obj(new base::DictionaryValue()); + + if (vendor_id_set_) { + obj->SetInteger(kVendorIdKey, vendor_id_); + if (product_id_set_) { + obj->SetInteger(kProductIdKey, product_id_); + } + } + + if (interface_class_set_) { + obj->SetInteger(kInterfaceClassKey, interface_class_); + if (interface_subclass_set_) { + obj->SetInteger(kInterfaceSubclassKey, interface_subclass_); + if (interface_protocol_set_) { + obj->SetInteger(kInterfaceProtocolKey, interface_protocol_); + } + } + } + + return obj.release(); +} + +} // namespace usb_service diff --git a/components/usb_service/usb_device_filter.h b/components/usb_service/usb_device_filter.h new file mode 100644 index 0000000000000..76a3b2c73967f --- /dev/null +++ b/components/usb_service/usb_device_filter.h @@ -0,0 +1,48 @@ +// 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_USB_SERVICE_USB_DEVICE_FILTER_H_ +#define COMPONENTS_USB_SERVICE_USB_DEVICE_FILTER_H_ + +#include "base/memory/ref_counted.h" +#include "components/usb_service/usb_service_export.h" + +namespace base { +class Value; +} + +namespace usb_service { + +class UsbDevice; + +class USB_SERVICE_EXPORT UsbDeviceFilter { + public: + UsbDeviceFilter(); + ~UsbDeviceFilter(); + + void SetVendorId(uint16 vendor_id); + void SetProductId(uint16 product_id); + void SetInterfaceClass(uint8 interface_class); + void SetInterfaceSubclass(uint8 interface_subclass); + void SetInterfaceProtocol(uint8 interface_protocol); + + bool Matches(scoped_refptr device); + base::Value* ToValue() const; + + private: + uint16 vendor_id_; + uint16 product_id_; + uint8 interface_class_; + uint8 interface_subclass_; + uint8 interface_protocol_; + bool vendor_id_set_ : 1; + bool product_id_set_ : 1; + bool interface_class_set_ : 1; + bool interface_subclass_set_ : 1; + bool interface_protocol_set_ : 1; +}; + +} // namespace usb_service + +#endif // COMPONENTS_USB_SERVICE_USB_DEVICE_FILTER_H_ diff --git a/components/usb_service/usb_device_filter_unittest.cc b/components/usb_service/usb_device_filter_unittest.cc new file mode 100644 index 0000000000000..a8120682a32ce --- /dev/null +++ b/components/usb_service/usb_device_filter_unittest.cc @@ -0,0 +1,239 @@ +// 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 + +#include "base/memory/ref_counted.h" +#include "components/usb_service/usb_device.h" +#include "components/usb_service/usb_device_filter.h" +#include "components/usb_service/usb_device_handle.h" +#include "components/usb_service/usb_interface.h" +#include "testing/gtest/include/gtest/gtest.h" + +using usb_service::UsbConfigDescriptor; +using usb_service::UsbDevice; +using usb_service::UsbDeviceFilter; +using usb_service::UsbDeviceHandle; +using usb_service::UsbEndpointDescriptor; +using usb_service::UsbInterfaceAltSettingDescriptor; +using usb_service::UsbInterfaceDescriptor; + +namespace { + +class MockUsbInterfaceAltSettingDescriptor + : public UsbInterfaceAltSettingDescriptor { + public: + MockUsbInterfaceAltSettingDescriptor(int interface_number, + int alternate_setting, + int interface_class, + int interface_subclass, + int interface_protocol) + : interface_number_(interface_number), + alternate_setting_(alternate_setting), + interface_class_(interface_class), + interface_subclass_(interface_subclass), + interface_protocol_(interface_protocol) {} + + virtual size_t GetNumEndpoints() const OVERRIDE { return 0; } + virtual scoped_refptr GetEndpoint( + size_t index) const OVERRIDE { + return NULL; + } + virtual int GetInterfaceNumber() const OVERRIDE { return interface_number_; } + virtual int GetAlternateSetting() const OVERRIDE { + return alternate_setting_; + } + virtual int GetInterfaceClass() const OVERRIDE { return interface_class_; } + virtual int GetInterfaceSubclass() const OVERRIDE { + return interface_subclass_; + } + virtual int GetInterfaceProtocol() const OVERRIDE { + return interface_protocol_; + } + + protected: + virtual ~MockUsbInterfaceAltSettingDescriptor() {} + + private: + int interface_number_; + int alternate_setting_; + int interface_class_; + int interface_subclass_; + int interface_protocol_; +}; + +typedef std::vector > + UsbInterfaceAltSettingDescriptorList; + +class MockUsbInterfaceDescriptor : public UsbInterfaceDescriptor { + public: + MockUsbInterfaceDescriptor( + const UsbInterfaceAltSettingDescriptorList& alt_settings) + : alt_settings_(alt_settings) {} + + virtual size_t GetNumAltSettings() const OVERRIDE { + return alt_settings_.size(); + } + virtual scoped_refptr GetAltSetting( + size_t index) const OVERRIDE { + return alt_settings_[index]; + } + + protected: + virtual ~MockUsbInterfaceDescriptor() {} + + private: + UsbInterfaceAltSettingDescriptorList alt_settings_; +}; + +typedef std::vector > + UsbInterfaceDescriptorList; + +class MockUsbConfigDescriptor : public UsbConfigDescriptor { + public: + MockUsbConfigDescriptor(const UsbInterfaceDescriptorList& interfaces) + : interfaces_(interfaces) {} + + virtual size_t GetNumInterfaces() const OVERRIDE { + return interfaces_.size(); + } + virtual scoped_refptr GetInterface( + size_t index) const OVERRIDE { + return interfaces_[index]; + } + + protected: + virtual ~MockUsbConfigDescriptor() {} + + private: + UsbInterfaceDescriptorList interfaces_; +}; + +class MockUsbDevice : public UsbDevice { + public: + MockUsbDevice(uint16 vendor_id, + uint16 product_id, + uint32 unique_id, + scoped_refptr config_desc) + : UsbDevice(vendor_id, product_id, unique_id), + config_desc_(config_desc) {} + + virtual scoped_refptr Open() OVERRIDE { return NULL; } + virtual bool Close(scoped_refptr handle) OVERRIDE { + NOTREACHED(); + return true; + } +#if defined(OS_CHROMEOS) + virtual void RequestUsbAccess( + int interface_id, + const base::Callback& callback) OVERRIDE { + NOTREACHED(); + } +#endif // OS_CHROMEOS + virtual scoped_refptr ListInterfaces() OVERRIDE { + return config_desc_; + } + + protected: + virtual ~MockUsbDevice() {} + + private: + scoped_refptr config_desc_; +}; + +class UsbFilterTest : public testing::Test { + public: + virtual void SetUp() OVERRIDE { + UsbInterfaceAltSettingDescriptorList alt_settings; + alt_settings.push_back(make_scoped_refptr( + new MockUsbInterfaceAltSettingDescriptor(1, 0, 0xFF, 0x42, 1))); + + UsbInterfaceDescriptorList interfaces; + interfaces.push_back( + make_scoped_refptr(new MockUsbInterfaceDescriptor(alt_settings))); + + scoped_refptr config_desc( + new MockUsbConfigDescriptor(interfaces)); + + android_phone_ = new MockUsbDevice(0x18d1, 0x4ee2, 0, config_desc); + } + + protected: + scoped_refptr android_phone_; +}; + +} // namespace + +TEST_F(UsbFilterTest, MatchAny) { + UsbDeviceFilter filter; + ASSERT_TRUE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchVendorId) { + UsbDeviceFilter filter; + filter.SetVendorId(0x18d1); + ASSERT_TRUE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchVendorIdNegative) { + UsbDeviceFilter filter; + filter.SetVendorId(0x1d6b); + ASSERT_FALSE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchProductId) { + UsbDeviceFilter filter; + filter.SetVendorId(0x18d1); + filter.SetProductId(0x4ee2); + ASSERT_TRUE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchProductIdNegative) { + UsbDeviceFilter filter; + filter.SetVendorId(0x18d1); + filter.SetProductId(0x4ee1); + ASSERT_FALSE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchInterfaceClass) { + UsbDeviceFilter filter; + filter.SetInterfaceClass(0xff); + ASSERT_TRUE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchInterfaceClassNegative) { + UsbDeviceFilter filter; + filter.SetInterfaceClass(0xe0); + ASSERT_FALSE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchInterfaceSubclass) { + UsbDeviceFilter filter; + filter.SetInterfaceClass(0xff); + filter.SetInterfaceSubclass(0x42); + ASSERT_TRUE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchInterfaceSubclassNegative) { + UsbDeviceFilter filter; + filter.SetInterfaceClass(0xff); + filter.SetInterfaceSubclass(0x01); + ASSERT_FALSE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchInterfaceProtocol) { + UsbDeviceFilter filter; + filter.SetInterfaceClass(0xff); + filter.SetInterfaceSubclass(0x42); + filter.SetInterfaceProtocol(0x01); + ASSERT_TRUE(filter.Matches(android_phone_)); +} + +TEST_F(UsbFilterTest, MatchInterfaceProtocolNegative) { + UsbDeviceFilter filter; + filter.SetInterfaceClass(0xff); + filter.SetInterfaceSubclass(0x42); + filter.SetInterfaceProtocol(0x02); + ASSERT_FALSE(filter.Matches(android_phone_)); +} diff --git a/components/usb_service/usb_device_handle.h b/components/usb_service/usb_device_handle.h index 605e05e5301b8..0115e64d17c12 100644 --- a/components/usb_service/usb_device_handle.h +++ b/components/usb_service/usb_device_handle.h @@ -58,6 +58,8 @@ class USB_SERVICE_EXPORT UsbDeviceHandle virtual bool SetInterfaceAlternateSetting(const int interface_number, const int alternate_setting) = 0; virtual bool ResetDevice() = 0; + virtual bool GetManufacturer(base::string16* manufacturer) = 0; + virtual bool GetProduct(base::string16* product) = 0; virtual bool GetSerial(base::string16* serial) = 0; // Async IO. Can be called on any thread. diff --git a/components/usb_service/usb_device_handle_impl.cc b/components/usb_service/usb_device_handle_impl.cc index c78a976c0a202..71883177cad6c 100644 --- a/components/usb_service/usb_device_handle_impl.cc +++ b/components/usb_service/usb_device_handle_impl.cc @@ -196,7 +196,7 @@ UsbDeviceHandleImpl::UsbDeviceHandleImpl( context_(context) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(handle) << "Cannot create device with NULL handle."; - DCHECK(interfaces_) << "Unabled to list interfaces"; + DCHECK(interfaces_) << "Unable to list interfaces"; } UsbDeviceHandleImpl::~UsbDeviceHandleImpl() { @@ -372,64 +372,147 @@ bool UsbDeviceHandleImpl::ResetDevice() { return rv == LIBUSB_SUCCESS; } -bool UsbDeviceHandleImpl::GetSerial(base::string16* serial) { - DCHECK(thread_checker_.CalledOnValidThread()); - PlatformUsbDevice device = libusb_get_device(handle_); - libusb_device_descriptor desc; - - const int rv = libusb_get_device_descriptor(device, &desc); - if (rv != LIBUSB_SUCCESS) { - VLOG(1) << "Failed to read device descriptor: " << ConvertErrorToString(rv); - return false; +bool UsbDeviceHandleImpl::GetSupportedLanguages() { + if (!languages_.empty()) { + return true; } - if (desc.iSerialNumber == 0) + // The 1-byte length field limits the descriptor to 256-bytes (128 uint16s). + uint16 languages[128]; + int size = libusb_get_string_descriptor( + handle_, + 0, + 0, + reinterpret_cast(&languages[0]), + sizeof(languages)); + if (size < 0) { + VLOG(1) << "Failed to get list of supported languages: " + << ConvertErrorToString(size); + return false; + } else if (size < 2) { + VLOG(1) << "String descriptor zero has no header."; + return false; + // The first 2 bytes of the descriptor are the total length and type tag. + } else if ((languages[0] & 0xff) != size) { + VLOG(1) << "String descriptor zero size mismatch: " << (languages[0] & 0xff) + << " != " << size; + return false; + } else if ((languages[0] >> 8) != LIBUSB_DT_STRING) { + VLOG(1) << "String descriptor zero is not a string descriptor."; return false; + } - // Getting supported language ID. - uint16 langid[128] = {0}; + languages_.assign(languages[1], languages[(size - 2) / 2]); + return true; +} - int size = - libusb_get_string_descriptor(handle_, - 0, - 0, - reinterpret_cast(&langid[0]), - sizeof(langid)); - if (size < 0) { - VLOG(1) << "Failed to get language IDs: " << ConvertErrorToString(size); +bool UsbDeviceHandleImpl::GetStringDescriptor(uint8 string_id, + base::string16* string) { + if (!GetSupportedLanguages()) { return false; } - int language_count = (size - 2) / 2; + std::map::const_iterator it = strings_.find(string_id); + if (it != strings_.end()) { + *string = it->second; + return true; + } - for (int i = 1; i <= language_count; ++i) { + for (size_t i = 0; i < languages_.size(); ++i) { // Get the string using language ID. - base::char16 text[256] = {0}; - size = + uint16 language_id = languages_[i]; + // The 1-byte length field limits the descriptor to 256-bytes (128 char16s). + base::char16 text[128]; + int size = libusb_get_string_descriptor(handle_, - desc.iSerialNumber, - langid[i], + string_id, + language_id, reinterpret_cast(&text[0]), sizeof(text)); if (size < 0) { - VLOG(1) << "Failed to get serial number (langid " << langid[i] << "): " - << ConvertErrorToString(size); + VLOG(1) << "Failed to get string descriptor " << string_id << " (langid " + << language_id << "): " << ConvertErrorToString(size); continue; - } - if (size <= 2) + } else if (size < 2) { + VLOG(1) << "String descriptor " << string_id << " (langid " << language_id + << ") has no header."; continue; - if ((text[0] >> 8) != LIBUSB_DT_STRING) + // The first 2 bytes of the descriptor are the total length and type tag. + } else if ((text[0] & 0xff) != size) { + VLOG(1) << "String descriptor " << string_id << " (langid " << language_id + << ") size mismatch: " << (text[0] & 0xff) << " != " << size; continue; - if ((text[0] & 255) > size) + } else if ((text[0] >> 8) != LIBUSB_DT_STRING) { + VLOG(1) << "String descriptor " << string_id << " (langid " << language_id + << ") is not a string descriptor."; continue; + } - size = size / 2 - 1; - *serial = base::string16(text + 1, size); + *string = base::string16(text + 1, (size - 2) / 2); + strings_[string_id] = *string; return true; } + return false; } +bool UsbDeviceHandleImpl::GetManufacturer(base::string16* manufacturer) { + DCHECK(thread_checker_.CalledOnValidThread()); + PlatformUsbDevice device = libusb_get_device(handle_); + libusb_device_descriptor desc; + + // This is a non-blocking call as libusb has the descriptor in memory. + const int rv = libusb_get_device_descriptor(device, &desc); + if (rv != LIBUSB_SUCCESS) { + VLOG(1) << "Failed to read device descriptor: " << ConvertErrorToString(rv); + return false; + } + + if (desc.iManufacturer == 0) { + return false; + } + + return GetStringDescriptor(desc.iManufacturer, manufacturer); +} + +bool UsbDeviceHandleImpl::GetProduct(base::string16* product) { + DCHECK(thread_checker_.CalledOnValidThread()); + PlatformUsbDevice device = libusb_get_device(handle_); + libusb_device_descriptor desc; + + // This is a non-blocking call as libusb has the descriptor in memory. + const int rv = libusb_get_device_descriptor(device, &desc); + if (rv != LIBUSB_SUCCESS) { + VLOG(1) << "Failed to read device descriptor: " << ConvertErrorToString(rv); + return false; + } + + if (desc.iProduct == 0) { + return false; + } + + return GetStringDescriptor(desc.iProduct, product); +} + +bool UsbDeviceHandleImpl::GetSerial(base::string16* serial) { + DCHECK(thread_checker_.CalledOnValidThread()); + PlatformUsbDevice device = libusb_get_device(handle_); + libusb_device_descriptor desc; + + // This is a non-blocking call as libusb has the descriptor in memory. + const int rv = libusb_get_device_descriptor(device, &desc); + if (rv != LIBUSB_SUCCESS) { + VLOG(1) << "Failed to read device descriptor: " << ConvertErrorToString(rv); + return false; + } + + if (desc.iSerialNumber == 0) { + return false; + } + + return GetStringDescriptor(desc.iSerialNumber, serial); +} + void UsbDeviceHandleImpl::ControlTransfer( const UsbEndpointDirection direction, const TransferRequestType request_type, diff --git a/components/usb_service/usb_device_handle_impl.h b/components/usb_service/usb_device_handle_impl.h index 0af9f3a50536c..7d113377ee1ad 100644 --- a/components/usb_service/usb_device_handle_impl.h +++ b/components/usb_service/usb_device_handle_impl.h @@ -45,6 +45,8 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { const int interface_number, const int alternate_setting) OVERRIDE; virtual bool ResetDevice() OVERRIDE; + virtual bool GetManufacturer(base::string16* manufacturer) OVERRIDE; + virtual bool GetProduct(base::string16* product) OVERRIDE; virtual bool GetSerial(base::string16* serial) OVERRIDE; virtual void ControlTransfer(const UsbEndpointDirection direction, const TransferRequestType request_type, @@ -123,6 +125,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { // the in-flight transfer set. void TransferComplete(PlatformUsbTransferHandle transfer); + bool GetSupportedLanguages(); + bool GetStringDescriptor(uint8 string_id, base::string16* string); + // Informs the object to drop internal references. void InternalClose(); @@ -132,6 +137,9 @@ class UsbDeviceHandleImpl : public UsbDeviceHandle { scoped_refptr interfaces_; + std::vector languages_; + std::map strings_; + typedef std::map > ClaimedInterfaceMap; ClaimedInterfaceMap claimed_interfaces_; diff --git a/components/usb_service/usb_device_impl.cc b/components/usb_service/usb_device_impl.cc index 98c3a49e3bdd8..aaba7e34f0944 100644 --- a/components/usb_service/usb_device_impl.cc +++ b/components/usb_service/usb_device_impl.cc @@ -61,7 +61,7 @@ UsbDeviceImpl::~UsbDeviceImpl() { #if defined(OS_CHROMEOS) -void UsbDeviceImpl::RequestUsbAcess( +void UsbDeviceImpl::RequestUsbAccess( int interface_id, const base::Callback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); diff --git a/components/usb_service/usb_device_impl.h b/components/usb_service/usb_device_impl.h index db84f5e76f69e..e27a692853b1c 100644 --- a/components/usb_service/usb_device_impl.h +++ b/components/usb_service/usb_device_impl.h @@ -27,7 +27,7 @@ class UsbDeviceImpl : public UsbDevice { public: // UsbDevice implementation: #if defined(OS_CHROMEOS) - virtual void RequestUsbAcess( + virtual void RequestUsbAccess( int interface_id, const base::Callback& callback) OVERRIDE; #endif // OS_CHROMEOS diff --git a/components/user_manager.gypi b/components/user_manager.gypi index f65fb3a613623..8aa5e8ca2d98d 100644 --- a/components/user_manager.gypi +++ b/components/user_manager.gypi @@ -16,12 +16,17 @@ ], # Chrome OS user_manager sources. 'user_manager_chromeos_sources': [ + 'user_manager/remove_user_delegate.h', 'user_manager/user_image/default_user_images.cc', 'user_manager/user_image/default_user_images.h', 'user_manager/user_image/user_image.cc', 'user_manager/user_image/user_image.h', 'user_manager/user.cc', 'user_manager/user.h', + 'user_manager/user_manager.cc', + 'user_manager/user_manager.h', + 'user_manager/user_manager_base.cc', + 'user_manager/user_manager_base.h', 'user_manager/user_type.h', ], }, @@ -45,6 +50,8 @@ 'conditions': [ ['chromeos == 1', { 'dependencies': [ + '<(DEPTH)/base/base.gyp:base_prefs', + '<(DEPTH)/components/components.gyp:session_manager_component', '<(DEPTH)/google_apis/google_apis.gyp:google_apis', '<(DEPTH)/ui/chromeos/ui_chromeos.gyp:ui_chromeos_resources', '<(DEPTH)/ui/chromeos/ui_chromeos.gyp:ui_chromeos_strings', diff --git a/components/user_manager/DEPS b/components/user_manager/DEPS index 2cbd1db80193a..f3fe77c79afdf 100644 --- a/components/user_manager/DEPS +++ b/components/user_manager/DEPS @@ -1,5 +1,9 @@ include_rules = [ +"+chromeos/chromeos_switches.h", +"+chromeos/cryptohome/async_method_caller.h", +"+chromeos/login/login_state.h", "+chromeos/login/user_names.h", +"+components/session_manager/core/session_manager.h", "+google_apis/gaia/gaia_auth_util.h", "+grit/ui_chromeos_resources.h", "+grit/ui_chromeos_strings.h", diff --git a/components/user_manager/remove_user_delegate.h b/components/user_manager/remove_user_delegate.h new file mode 100644 index 0000000000000..1077bda527fa6 --- /dev/null +++ b/components/user_manager/remove_user_delegate.h @@ -0,0 +1,24 @@ +// 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_USER_MANAGER_REMOVE_USER_DELEGATE_H_ +#define COMPONENTS_USER_MANAGER_REMOVE_USER_DELEGATE_H_ + +#include "components/user_manager/user_manager_export.h" + +namespace user_manager { + +// Delegate to be used with |UserManager::RemoveUser|. +class USER_MANAGER_EXPORT RemoveUserDelegate { + public: + // Called right before actual user removal process is initiated. + virtual void OnBeforeUserRemoved(const std::string& username) = 0; + + // Called right after user removal process has been initiated. + virtual void OnUserRemoved(const std::string& username) = 0; +}; + +} // namespace user_manager + +#endif // COMPONENTS_USER_MANAGER_REMOVE_USER_DELEGATE_H_ diff --git a/components/user_manager/user.h b/components/user_manager/user.h index 006761db2048b..5ff8a10b124b5 100644 --- a/components/user_manager/user.h +++ b/components/user_manager/user.h @@ -18,19 +18,20 @@ #include "ui/gfx/image/image_skia.h" namespace chromeos { -class ChromeUserManager; +class ChromeUserManagerImpl; class FakeLoginUtils; class FakeUserManager; class MockUserManager; class SupervisedUserManagerImpl; class UserAddingScreenTest; class UserImageManagerImpl; -class UserManagerBase; class UserSessionManager; } namespace user_manager { +class UserManagerBase; + // A class representing information about a previously logged in user. // Each user has a canonical email (username), returned by |email()| and // may have a different displayed email (in the raw form as entered by user), @@ -60,12 +61,16 @@ class USER_MANAGER_EXPORT User : public UserInfo { USER_IMAGE_EXTERNAL = -1, } UserImageType; + // This enum is used to define the buckets for an enumerated UMA histogram. + // Hence, + // (a) existing enumerated constants should never be deleted or reordered, + // (b) new constants should only be appended at the end of the enumeration. enum WallpaperType { - /* DAILY = 0 */ // Removed. Do not re-use the id! + /* DAILY = 0 */ // Removed. CUSTOMIZED = 1, // Selected by user. DEFAULT = 2, // Default. - /* UNKNOWN = 3 */ // Removed. Do not re-use the id! - ONLINE = 4, // WallpaperInfo.file denotes an URL. + /* UNKNOWN = 3 */ // Removed. + ONLINE = 4, // WallpaperInfo.location denotes an URL. POLICY = 5, // Controlled by policy, can't be changed by the user. WALLPAPER_TYPE_COUNT = 6 }; @@ -144,10 +149,10 @@ class USER_MANAGER_EXPORT User : public UserInfo { bool is_profile_created() const { return profile_is_created_; } protected: - friend class chromeos::ChromeUserManager; + friend class UserManagerBase; + friend class chromeos::ChromeUserManagerImpl; friend class chromeos::SupervisedUserManagerImpl; friend class chromeos::UserImageManagerImpl; - friend class chromeos::UserManagerBase; friend class chromeos::UserSessionManager; // For testing: diff --git a/components/user_manager/user_manager.cc b/components/user_manager/user_manager.cc new file mode 100644 index 0000000000000..597407fa58180 --- /dev/null +++ b/components/user_manager/user_manager.cc @@ -0,0 +1,85 @@ +// 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/user_manager/user_manager.h" + +#include "base/logging.h" + +namespace user_manager { + +UserManager* UserManager::instance = NULL; + +UserManager::Observer::~Observer() { +} + +void UserManager::Observer::LocalStateChanged(UserManager* user_manager) { +} + +void UserManager::UserSessionStateObserver::ActiveUserChanged( + const User* active_user) { +} + +void UserManager::UserSessionStateObserver::UserAddedToSession( + const User* active_user) { +} + +void UserManager::UserSessionStateObserver::ActiveUserHashChanged( + const std::string& hash) { +} + +UserManager::UserSessionStateObserver::~UserSessionStateObserver() { +} + +UserManager::UserAccountData::UserAccountData( + const base::string16& display_name, + const base::string16& given_name, + const std::string& locale) + : display_name_(display_name), given_name_(given_name), locale_(locale) { +} + +UserManager::UserAccountData::~UserAccountData() { +} + +void UserManager::Initialize() { + DCHECK(!UserManager::instance); + UserManager::SetInstance(this); +} + +// static +bool UserManager::IsInitialized() { + return UserManager::instance; +} + +void UserManager::Destroy() { + DCHECK(UserManager::instance == this); + UserManager::SetInstance(NULL); +} + +// static +UserManager* user_manager::UserManager::Get() { + CHECK(UserManager::instance); + return UserManager::instance; +} + +UserManager::~UserManager() { +} + +// static +void UserManager::SetInstance(UserManager* user_manager) { + UserManager::instance = user_manager; +} + +// static +UserManager* user_manager::UserManager::GetForTesting() { + return UserManager::instance; +} + +// static +UserManager* UserManager::SetForTesting(UserManager* user_manager) { + UserManager* previous_instance = UserManager::instance; + UserManager::instance = user_manager; + return previous_instance; +} + +} // namespace user_manager diff --git a/components/user_manager/user_manager.h b/components/user_manager/user_manager.h new file mode 100644 index 0000000000000..e775279ba0a9b --- /dev/null +++ b/components/user_manager/user_manager.h @@ -0,0 +1,316 @@ +// 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_USER_MANAGER_USER_MANAGER_H_ +#define COMPONENTS_USER_MANAGER_USER_MANAGER_H_ + +#include + +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager_export.h" + +namespace chromeos { +class ScopedUserManagerEnabler; +} + +namespace user_manager { + +class RemoveUserDelegate; + +// Interface for UserManagerBase - that provides base implementation for +// Chrome OS user management. Typical features: +// * Get list of all know users (who have logged into this Chrome OS device) +// * Keep track for logged in/LRU users, active user in multi-user session. +// * Find/modify users, store user meta-data such as display name/email. +class USER_MANAGER_EXPORT UserManager { + public: + // Interface that observers of UserManager must implement in order + // to receive notification when local state preferences is changed + class Observer { + public: + // Called when the local state preferences is changed. + virtual void LocalStateChanged(UserManager* user_manager); + + protected: + virtual ~Observer(); + }; + + // TODO(nkostylev): Refactor and move this observer out of UserManager. + // Observer interface that defines methods used to notify on user session / + // active user state changes. Default implementation is empty. + class UserSessionStateObserver { + public: + // Called when active user has changed. + virtual void ActiveUserChanged(const User* active_user); + + // Called when another user got added to the existing session. + virtual void UserAddedToSession(const User* added_user); + + // Called right before notifying on user change so that those who rely + // on user_id hash would be accessing up-to-date value. + virtual void ActiveUserHashChanged(const std::string& hash); + + protected: + virtual ~UserSessionStateObserver(); + }; + + // Data retrieved from user account. + class UserAccountData { + public: + UserAccountData(const base::string16& display_name, + const base::string16& given_name, + const std::string& locale); + ~UserAccountData(); + const base::string16& display_name() const { return display_name_; } + const base::string16& given_name() const { return given_name_; } + const std::string& locale() const { return locale_; } + + private: + const base::string16 display_name_; + const base::string16 given_name_; + const std::string locale_; + + DISALLOW_COPY_AND_ASSIGN(UserAccountData); + }; + + // Initializes UserManager instance to this. Normally should be called right + // after creation so that user_manager::UserManager::Get() doesn't fail. + // Tests could call this method if they are replacing existing UserManager + // instance with their own test instance. + void Initialize(); + + // Checks whether the UserManager instance has been created already. + // This method is not thread-safe and must be called from the main UI thread. + static bool IsInitialized(); + + // Shuts down the UserManager. After this method has been called, the + // singleton has unregistered itself as an observer but remains available so + // that other classes can access it during their shutdown. This method is not + // thread-safe and must be called from the main UI thread. + virtual void Shutdown() = 0; + + // Sets UserManager instance to NULL. Always call Shutdown() first. + // This method is not thread-safe and must be called from the main UI thread. + void Destroy(); + + // Returns UserManager instance or will crash if it is |NULL| (has either not + // been created yet or is already destroyed). This method is not thread-safe + // and must be called from the main UI thread. + static UserManager* Get(); + + virtual ~UserManager(); + + // Returns a list of users who have logged into this device previously. This + // is sorted by last login date with the most recent user at the beginning. + virtual const UserList& GetUsers() const = 0; + + // Returns list of users admitted for logging in into multi-profile session. + // Users that have a policy that prevents them from being added to the + // multi-profile session will still be part of this list as long as they + // are regular users (i.e. not a public session/supervised etc.). + // Returns an empty list in case when primary user is not a regular one or + // has a policy that prohibids it to be part of multi-profile session. + virtual UserList GetUsersAdmittedForMultiProfile() const = 0; + + // Returns a list of users who are currently logged in. + virtual const UserList& GetLoggedInUsers() const = 0; + + // Returns a list of users who are currently logged in in the LRU order - + // so the active user is the first one in the list. If there is no user logged + // in, the current user will be returned. + virtual const UserList& GetLRULoggedInUsers() const = 0; + + // Returns a list of users who can unlock the device. + // This list is based on policy and whether user is able to do unlock. + // Policy: + // * If user has primary-only policy then it is the only user in unlock users. + // * Otherwise all users with unrestricted policy are added to this list. + // All users that are unable to perform unlock are excluded from this list. + virtual UserList GetUnlockUsers() const = 0; + + // Returns the email of the owner user. Returns an empty string if there is + // no owner for the device. + virtual const std::string& GetOwnerEmail() const = 0; + + // Indicates that a user with the given |user_id| has just logged in. The + // persistent list is updated accordingly if the user is not ephemeral. + // |browser_restart| is true when reloading Chrome after crash to distinguish + // from normal sign in flow. + // |username_hash| is used to identify homedir mount point. + virtual void UserLoggedIn(const std::string& user_id, + const std::string& username_hash, + bool browser_restart) = 0; + + // Switches to active user identified by |user_id|. User has to be logged in. + virtual void SwitchActiveUser(const std::string& user_id) = 0; + + // Called when browser session is started i.e. after + // browser_creator.LaunchBrowser(...) was called after user sign in. + // When user is at the image screen IsUserLoggedIn() will return true + // but IsSessionStarted() will return false. During the kiosk splash screen, + // we perform additional initialization after the user is logged in but + // before the session has been started. + // Fires NOTIFICATION_SESSION_STARTED. + virtual void SessionStarted() = 0; + + // Removes the user from the device. Note, it will verify that the given user + // isn't the owner, so calling this method for the owner will take no effect. + // Note, |delegate| can be NULL. + virtual void RemoveUser(const std::string& user_id, + RemoveUserDelegate* delegate) = 0; + + // Removes the user from the persistent list only. Also removes the user's + // picture. + virtual void RemoveUserFromList(const std::string& user_id) = 0; + + // Returns true if a user with the given user id is found in the persistent + // list or currently logged in as ephemeral. + virtual bool IsKnownUser(const std::string& user_id) const = 0; + + // Returns the user with the given user id if found in the persistent + // list or currently logged in as ephemeral. Returns |NULL| otherwise. + virtual const User* FindUser(const std::string& user_id) const = 0; + + // Returns the user with the given user id if found in the persistent + // list or currently logged in as ephemeral. Returns |NULL| otherwise. + // Same as FindUser but returns non-const pointer to User object. + virtual User* FindUserAndModify(const std::string& user_id) = 0; + + // Returns the logged-in user. + // TODO(nkostylev): Deprecate this call, move clients to GetActiveUser(). + // http://crbug.com/230852 + virtual const User* GetLoggedInUser() const = 0; + virtual User* GetLoggedInUser() = 0; + + // Returns the logged-in user that is currently active within this session. + // There could be multiple users logged in at the the same but for now + // we support only one of them being active. + virtual const User* GetActiveUser() const = 0; + virtual User* GetActiveUser() = 0; + + // Returns the primary user of the current session. It is recorded for the + // first signed-in user and does not change thereafter. + virtual const User* GetPrimaryUser() const = 0; + + // Saves user's oauth token status in local state preferences. + virtual void SaveUserOAuthStatus( + const std::string& user_id, + User::OAuthTokenStatus oauth_token_status) = 0; + + // Saves a flag indicating whether online authentication against GAIA should + // be enforced during the user's next sign-in. + virtual void SaveForceOnlineSignin(const std::string& user_id, + bool force_online_signin) = 0; + + // Saves user's displayed name in local state preferences. + // Ignored If there is no such user. + virtual void SaveUserDisplayName(const std::string& user_id, + const base::string16& display_name) = 0; + + // Updates data upon User Account download. + virtual void UpdateUserAccountData(const std::string& user_id, + const UserAccountData& account_data) = 0; + + // Returns the display name for user |user_id| if it is known (was + // previously set by a |SaveUserDisplayName| call). + // Otherwise, returns an empty string. + virtual base::string16 GetUserDisplayName( + const std::string& user_id) const = 0; + + // Saves user's displayed (non-canonical) email in local state preferences. + // Ignored If there is no such user. + virtual void SaveUserDisplayEmail(const std::string& user_id, + const std::string& display_email) = 0; + + // Returns the display email for user |user_id| if it is known (was + // previously set by a |SaveUserDisplayEmail| call). + // Otherwise, returns |user_id| itself. + virtual std::string GetUserDisplayEmail(const std::string& user_id) const = 0; + + // Returns true if current user is an owner. + virtual bool IsCurrentUserOwner() const = 0; + + // Returns true if current user is not existing one (hasn't signed in before). + virtual bool IsCurrentUserNew() const = 0; + + // Returns true if data stored or cached for the current user outside that + // user's cryptohome (wallpaper, avatar, OAuth token status, display name, + // display email) is ephemeral. + virtual bool IsCurrentUserNonCryptohomeDataEphemeral() const = 0; + + // Returns true if the current user's session can be locked (i.e. the user has + // a password with which to unlock the session). + virtual bool CanCurrentUserLock() const = 0; + + // Returns true if at least one user has signed in. + virtual bool IsUserLoggedIn() const = 0; + + // Returns true if we're logged in as a regular user. + virtual bool IsLoggedInAsRegularUser() const = 0; + + // Returns true if we're logged in as a demo user. + virtual bool IsLoggedInAsDemoUser() const = 0; + + // Returns true if we're logged in as a public account. + virtual bool IsLoggedInAsPublicAccount() const = 0; + + // Returns true if we're logged in as a Guest. + virtual bool IsLoggedInAsGuest() const = 0; + + // Returns true if we're logged in as a supervised user. + virtual bool IsLoggedInAsSupervisedUser() const = 0; + + // Returns true if we're logged in as a kiosk app. + virtual bool IsLoggedInAsKioskApp() const = 0; + + // Returns true if we're logged in as the stub user used for testing on Linux. + virtual bool IsLoggedInAsStub() const = 0; + + // Returns true if we're logged in and browser has been started i.e. + // browser_creator.LaunchBrowser(...) was called after sign in + // or restart after crash. + virtual bool IsSessionStarted() const = 0; + + // Returns true if data stored or cached for the user with the given user id + // address outside that user's cryptohome (wallpaper, avatar, OAuth token + // status, display name, display email) is to be treated as ephemeral. + virtual bool IsUserNonCryptohomeDataEphemeral( + const std::string& user_id) const = 0; + + virtual void AddObserver(Observer* obs) = 0; + virtual void RemoveObserver(Observer* obs) = 0; + + virtual void AddSessionStateObserver(UserSessionStateObserver* obs) = 0; + virtual void RemoveSessionStateObserver(UserSessionStateObserver* obs) = 0; + + virtual void NotifyLocalStateChanged() = 0; + + // Returns true if supervised users allowed. + virtual bool AreSupervisedUsersAllowed() const = 0; + + protected: + // Sets UserManager instance. + static void SetInstance(UserManager* user_manager); + + // Pointer to the existing UserManager instance (if any). + // Usually is set by calling Initialize(), reset by calling Destroy(). + // Not owned since specific implementation of UserManager should decide on its + // own appropriate owner. For src/chrome implementation such place is + // g_browser_process->platform_part(). + static UserManager* instance; + + private: + friend class chromeos::ScopedUserManagerEnabler; + + // Same as Get() but doesn't won't crash is current instance is NULL. + static UserManager* GetForTesting(); + + // Sets UserManager instance to the given |user_manager|. + // Returns the previous value of the instance. + static UserManager* SetForTesting(UserManager* user_manager); +}; + +} // namespace user_manager + +#endif // COMPONENTS_USER_MANAGER_USER_MANAGER_H_ diff --git a/components/user_manager/user_manager_base.cc b/components/user_manager/user_manager_base.cc new file mode 100644 index 0000000000000..fd50a75d3b539 --- /dev/null +++ b/components/user_manager/user_manager_base.cc @@ -0,0 +1,1028 @@ +// 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/user_manager/user_manager_base.h" + +#include +#include + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/format_macros.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/prefs/scoped_user_pref_update.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/task_runner.h" +#include "base/values.h" +#include "chromeos/chromeos_switches.h" +#include "chromeos/cryptohome/async_method_caller.h" +#include "chromeos/login/login_state.h" +#include "chromeos/login/user_names.h" +#include "components/session_manager/core/session_manager.h" +#include "components/user_manager/remove_user_delegate.h" +#include "components/user_manager/user_type.h" +#include "google_apis/gaia/gaia_auth_util.h" +#include "ui/base/l10n/l10n_util.h" + +namespace user_manager { +namespace { + +// A vector pref of the the regular users known on this device, arranged in LRU +// order. +const char kRegularUsers[] = "LoggedInUsers"; + +// A dictionary that maps user IDs to the displayed name. +const char kUserDisplayName[] = "UserDisplayName"; + +// A dictionary that maps user IDs to the user's given name. +const char kUserGivenName[] = "UserGivenName"; + +// A dictionary that maps user IDs to the displayed (non-canonical) emails. +const char kUserDisplayEmail[] = "UserDisplayEmail"; + +// A dictionary that maps user IDs to OAuth token presence flag. +const char kUserOAuthTokenStatus[] = "OAuthTokenStatus"; + +// A dictionary that maps user IDs to a flag indicating whether online +// authentication against GAIA should be enforced during the next sign-in. +const char kUserForceOnlineSignin[] = "UserForceOnlineSignin"; + +// A string pref containing the ID of the last user who logged in if it was +// a regular user or an empty string if it was another type of user (guest, +// kiosk, public account, etc.). +const char kLastLoggedInRegularUser[] = "LastLoggedInRegularUser"; + +// Upper bound for a histogram metric reporting the amount of time between +// one regular user logging out and a different regular user logging in. +const int kLogoutToLoginDelayMaxSec = 1800; + +// Callback that is called after user removal is complete. +void OnRemoveUserComplete(const std::string& user_email, + bool success, + cryptohome::MountError return_code) { + // Log the error, but there's not much we can do. + if (!success) { + LOG(ERROR) << "Removal of cryptohome for " << user_email + << " failed, return code: " << return_code; + } +} + +} // namespace + +// static +void UserManagerBase::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterListPref(kRegularUsers); + registry->RegisterStringPref(kLastLoggedInRegularUser, std::string()); + registry->RegisterDictionaryPref(kUserDisplayName); + registry->RegisterDictionaryPref(kUserGivenName); + registry->RegisterDictionaryPref(kUserDisplayEmail); + registry->RegisterDictionaryPref(kUserOAuthTokenStatus); + registry->RegisterDictionaryPref(kUserForceOnlineSignin); +} + +UserManagerBase::UserManagerBase( + scoped_refptr task_runner, + scoped_refptr blocking_task_runner) + : active_user_(NULL), + primary_user_(NULL), + user_loading_stage_(STAGE_NOT_LOADED), + session_started_(false), + is_current_user_owner_(false), + is_current_user_new_(false), + is_current_user_ephemeral_regular_user_(false), + ephemeral_users_enabled_(false), + manager_creation_time_(base::TimeTicks::Now()), + task_runner_(task_runner), + blocking_task_runner_(blocking_task_runner), + weak_factory_(this) { + UpdateLoginState(); +} + +UserManagerBase::~UserManagerBase() { + // Can't use STLDeleteElements because of the private destructor of User. + for (UserList::iterator it = users_.begin(); it != users_.end(); + it = users_.erase(it)) { + DeleteUser(*it); + } + // These are pointers to the same User instances that were in users_ list. + logged_in_users_.clear(); + lru_logged_in_users_.clear(); + + DeleteUser(active_user_); +} + +void UserManagerBase::Shutdown() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); +} + +const UserList& UserManagerBase::GetUsers() const { + const_cast(this)->EnsureUsersLoaded(); + return users_; +} + +const UserList& UserManagerBase::GetLoggedInUsers() const { + return logged_in_users_; +} + +const UserList& UserManagerBase::GetLRULoggedInUsers() const { + return lru_logged_in_users_; +} + +const std::string& UserManagerBase::GetOwnerEmail() const { + return owner_email_; +} + +void UserManagerBase::UserLoggedIn(const std::string& user_id, + const std::string& username_hash, + bool browser_restart) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + User* user = FindUserInListAndModify(user_id); + if (active_user_ && user) { + user->set_is_logged_in(true); + user->set_username_hash(username_hash); + logged_in_users_.push_back(user); + lru_logged_in_users_.push_back(user); + + // Reset the new user flag if the user already exists. + SetIsCurrentUserNew(false); + NotifyUserAddedToSession(user, true /* user switch pending */); + + return; + } + + if (user_id == chromeos::login::kGuestUserName) { + GuestUserLoggedIn(); + } else if (user_id == chromeos::login::kRetailModeUserName) { + RetailModeUserLoggedIn(); + } else if (IsKioskApp(user_id)) { + KioskAppLoggedIn(user_id); + } else if (IsDemoApp(user_id)) { + DemoAccountLoggedIn(); + } else { + EnsureUsersLoaded(); + + if (user && user->GetType() == USER_TYPE_PUBLIC_ACCOUNT) { + PublicAccountUserLoggedIn(user); + } else if ((user && user->GetType() == USER_TYPE_SUPERVISED) || + (!user && + gaia::ExtractDomainName(user_id) == + chromeos::login::kSupervisedUserDomain)) { + SupervisedUserLoggedIn(user_id); + } else if (browser_restart && IsPublicAccountMarkedForRemoval(user_id)) { + PublicAccountUserLoggedIn(User::CreatePublicAccountUser(user_id)); + } else if (user_id != GetOwnerEmail() && !user && + (AreEphemeralUsersEnabled() || browser_restart)) { + RegularUserLoggedInAsEphemeral(user_id); + } else { + RegularUserLoggedIn(user_id); + } + } + + DCHECK(active_user_); + active_user_->set_is_logged_in(true); + active_user_->set_is_active(true); + active_user_->set_username_hash(username_hash); + + // Place user who just signed in to the top of the logged in users. + logged_in_users_.insert(logged_in_users_.begin(), active_user_); + SetLRUUser(active_user_); + + if (!primary_user_) { + primary_user_ = active_user_; + if (primary_user_->GetType() == USER_TYPE_REGULAR) + SendRegularUserLoginMetrics(user_id); + } + + UMA_HISTOGRAM_ENUMERATION( + "UserManager.LoginUserType", active_user_->GetType(), NUM_USER_TYPES); + + GetLocalState()->SetString( + kLastLoggedInRegularUser, + (active_user_->GetType() == USER_TYPE_REGULAR) ? user_id : ""); + + NotifyOnLogin(); + PerformPostUserLoggedInActions(browser_restart); +} + +void UserManagerBase::SwitchActiveUser(const std::string& user_id) { + User* user = FindUserAndModify(user_id); + if (!user) { + NOTREACHED() << "Switching to a non-existing user"; + return; + } + if (user == active_user_) { + NOTREACHED() << "Switching to a user who is already active"; + return; + } + if (!user->is_logged_in()) { + NOTREACHED() << "Switching to a user that is not logged in"; + return; + } + if (user->GetType() != USER_TYPE_REGULAR) { + NOTREACHED() << "Switching to a non-regular user"; + return; + } + if (user->username_hash().empty()) { + NOTREACHED() << "Switching to a user that doesn't have username_hash set"; + return; + } + + DCHECK(active_user_); + active_user_->set_is_active(false); + user->set_is_active(true); + active_user_ = user; + + // Move the user to the front. + SetLRUUser(active_user_); + + NotifyActiveUserHashChanged(active_user_->username_hash()); + NotifyActiveUserChanged(active_user_); +} + +void UserManagerBase::SessionStarted() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + session_started_ = true; + + UpdateLoginState(); + session_manager::SessionManager::Get()->SetSessionState( + session_manager::SESSION_STATE_ACTIVE); + + if (IsCurrentUserNew()) { + // Make sure that the new user's data is persisted to Local State. + GetLocalState()->CommitPendingWrite(); + } +} + +void UserManagerBase::RemoveUser(const std::string& user_id, + RemoveUserDelegate* delegate) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + if (!CanUserBeRemoved(FindUser(user_id))) + return; + + RemoveUserInternal(user_id, delegate); +} + +void UserManagerBase::RemoveUserInternal(const std::string& user_email, + RemoveUserDelegate* delegate) { + RemoveNonOwnerUserInternal(user_email, delegate); +} + +void UserManagerBase::RemoveNonOwnerUserInternal(const std::string& user_email, + RemoveUserDelegate* delegate) { + if (delegate) + delegate->OnBeforeUserRemoved(user_email); + RemoveUserFromList(user_email); + cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( + user_email, base::Bind(&OnRemoveUserComplete, user_email)); + + if (delegate) + delegate->OnUserRemoved(user_email); +} + +void UserManagerBase::RemoveUserFromList(const std::string& user_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + RemoveNonCryptohomeData(user_id); + if (user_loading_stage_ == STAGE_LOADED) { + DeleteUser(RemoveRegularOrSupervisedUserFromList(user_id)); + } else if (user_loading_stage_ == STAGE_LOADING) { + DCHECK(gaia::ExtractDomainName(user_id) == + chromeos::login::kSupervisedUserDomain); + // Special case, removing partially-constructed supervised user during user + // list loading. + ListPrefUpdate users_update(GetLocalState(), kRegularUsers); + users_update->Remove(base::StringValue(user_id), NULL); + } else { + NOTREACHED() << "Users are not loaded yet."; + return; + } + + // Make sure that new data is persisted to Local State. + GetLocalState()->CommitPendingWrite(); +} + +bool UserManagerBase::IsKnownUser(const std::string& user_id) const { + return FindUser(user_id) != NULL; +} + +const User* UserManagerBase::FindUser(const std::string& user_id) const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (active_user_ && active_user_->email() == user_id) + return active_user_; + return FindUserInList(user_id); +} + +User* UserManagerBase::FindUserAndModify(const std::string& user_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (active_user_ && active_user_->email() == user_id) + return active_user_; + return FindUserInListAndModify(user_id); +} + +const User* UserManagerBase::GetLoggedInUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +User* UserManagerBase::GetLoggedInUser() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +const User* UserManagerBase::GetActiveUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +User* UserManagerBase::GetActiveUser() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +const User* UserManagerBase::GetPrimaryUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return primary_user_; +} + +void UserManagerBase::SaveUserOAuthStatus( + const std::string& user_id, + User::OAuthTokenStatus oauth_token_status) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + DVLOG(1) << "Saving user OAuth token status in Local State"; + User* user = FindUserAndModify(user_id); + if (user) + user->set_oauth_token_status(oauth_token_status); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (IsUserNonCryptohomeDataEphemeral(user_id)) + return; + + DictionaryPrefUpdate oauth_status_update(GetLocalState(), + kUserOAuthTokenStatus); + oauth_status_update->SetWithoutPathExpansion( + user_id, + new base::FundamentalValue(static_cast(oauth_token_status))); +} + +void UserManagerBase::SaveForceOnlineSignin(const std::string& user_id, + bool force_online_signin) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (IsUserNonCryptohomeDataEphemeral(user_id)) + return; + + DictionaryPrefUpdate force_online_update(GetLocalState(), + kUserForceOnlineSignin); + force_online_update->SetBooleanWithoutPathExpansion(user_id, + force_online_signin); +} + +void UserManagerBase::SaveUserDisplayName(const std::string& user_id, + const base::string16& display_name) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + if (User* user = FindUserAndModify(user_id)) { + user->set_display_name(display_name); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (!IsUserNonCryptohomeDataEphemeral(user_id)) { + DictionaryPrefUpdate display_name_update(GetLocalState(), + kUserDisplayName); + display_name_update->SetWithoutPathExpansion( + user_id, new base::StringValue(display_name)); + } + } +} + +base::string16 UserManagerBase::GetUserDisplayName( + const std::string& user_id) const { + const User* user = FindUser(user_id); + return user ? user->display_name() : base::string16(); +} + +void UserManagerBase::SaveUserDisplayEmail(const std::string& user_id, + const std::string& display_email) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + User* user = FindUserAndModify(user_id); + if (!user) { + LOG(ERROR) << "User not found: " << user_id; + return; // Ignore if there is no such user. + } + + user->set_display_email(display_email); + + // Do not update local state if data stored or cached outside the user's + // cryptohome is to be treated as ephemeral. + if (IsUserNonCryptohomeDataEphemeral(user_id)) + return; + + DictionaryPrefUpdate display_email_update(GetLocalState(), kUserDisplayEmail); + display_email_update->SetWithoutPathExpansion( + user_id, new base::StringValue(display_email)); +} + +std::string UserManagerBase::GetUserDisplayEmail( + const std::string& user_id) const { + const User* user = FindUser(user_id); + return user ? user->display_email() : user_id; +} + +void UserManagerBase::UpdateUserAccountData( + const std::string& user_id, + const UserAccountData& account_data) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + SaveUserDisplayName(user_id, account_data.display_name()); + + if (User* user = FindUserAndModify(user_id)) { + base::string16 given_name = account_data.given_name(); + user->set_given_name(given_name); + if (!IsUserNonCryptohomeDataEphemeral(user_id)) { + DictionaryPrefUpdate given_name_update(GetLocalState(), kUserGivenName); + given_name_update->SetWithoutPathExpansion( + user_id, new base::StringValue(given_name)); + } + } + + UpdateUserAccountLocale(user_id, account_data.locale()); +} + +// static +void UserManagerBase::ParseUserList(const base::ListValue& users_list, + const std::set& existing_users, + std::vector* users_vector, + std::set* users_set) { + users_vector->clear(); + users_set->clear(); + for (size_t i = 0; i < users_list.GetSize(); ++i) { + std::string email; + if (!users_list.GetString(i, &email) || email.empty()) { + LOG(ERROR) << "Corrupt entry in user list at index " << i << "."; + continue; + } + if (existing_users.find(email) != existing_users.end() || + !users_set->insert(email).second) { + LOG(ERROR) << "Duplicate user: " << email; + continue; + } + users_vector->push_back(email); + } +} + +bool UserManagerBase::IsCurrentUserOwner() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + base::AutoLock lk(is_current_user_owner_lock_); + return is_current_user_owner_; +} + +void UserManagerBase::SetCurrentUserIsOwner(bool is_current_user_owner) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + { + base::AutoLock lk(is_current_user_owner_lock_); + is_current_user_owner_ = is_current_user_owner; + } + UpdateLoginState(); +} + +bool UserManagerBase::IsCurrentUserNew() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return is_current_user_new_; +} + +bool UserManagerBase::IsCurrentUserNonCryptohomeDataEphemeral() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && + IsUserNonCryptohomeDataEphemeral(GetLoggedInUser()->email()); +} + +bool UserManagerBase::CanCurrentUserLock() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->can_lock(); +} + +bool UserManagerBase::IsUserLoggedIn() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return active_user_; +} + +bool UserManagerBase::IsLoggedInAsRegularUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_REGULAR; +} + +bool UserManagerBase::IsLoggedInAsDemoUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_RETAIL_MODE; +} + +bool UserManagerBase::IsLoggedInAsPublicAccount() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && + active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT; +} + +bool UserManagerBase::IsLoggedInAsGuest() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_GUEST; +} + +bool UserManagerBase::IsLoggedInAsSupervisedUser() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_SUPERVISED; +} + +bool UserManagerBase::IsLoggedInAsKioskApp() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && active_user_->GetType() == USER_TYPE_KIOSK_APP; +} + +bool UserManagerBase::IsLoggedInAsStub() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return IsUserLoggedIn() && + active_user_->email() == chromeos::login::kStubUser; +} + +bool UserManagerBase::IsSessionStarted() const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + return session_started_; +} + +bool UserManagerBase::IsUserNonCryptohomeDataEphemeral( + const std::string& user_id) const { + // Data belonging to the guest, retail mode and stub users is always + // ephemeral. + if (user_id == chromeos::login::kGuestUserName || + user_id == chromeos::login::kRetailModeUserName || + user_id == chromeos::login::kStubUser) { + return true; + } + + // Data belonging to the owner, anyone found on the user list and obsolete + // public accounts whose data has not been removed yet is not ephemeral. + if (user_id == GetOwnerEmail() || UserExistsInList(user_id) || + IsPublicAccountMarkedForRemoval(user_id)) { + return false; + } + + // Data belonging to the currently logged-in user is ephemeral when: + // a) The user logged into a regular account while the ephemeral users policy + // was enabled. + // - or - + // b) The user logged into any other account type. + if (IsUserLoggedIn() && (user_id == GetLoggedInUser()->email()) && + (is_current_user_ephemeral_regular_user_ || !IsLoggedInAsRegularUser())) { + return true; + } + + // Data belonging to any other user is ephemeral when: + // a) Going through the regular login flow and the ephemeral users policy is + // enabled. + // - or - + // b) The browser is restarting after a crash. + return AreEphemeralUsersEnabled() || + session_manager::SessionManager::HasBrowserRestarted(); +} + +void UserManagerBase::AddObserver(UserManager::Observer* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + observer_list_.AddObserver(obs); +} + +void UserManagerBase::RemoveObserver(UserManager::Observer* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + observer_list_.RemoveObserver(obs); +} + +void UserManagerBase::AddSessionStateObserver( + UserManager::UserSessionStateObserver* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + session_state_observer_list_.AddObserver(obs); +} + +void UserManagerBase::RemoveSessionStateObserver( + UserManager::UserSessionStateObserver* obs) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + session_state_observer_list_.RemoveObserver(obs); +} + +void UserManagerBase::NotifyLocalStateChanged() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER( + UserManager::Observer, observer_list_, LocalStateChanged(this)); +} + +bool UserManagerBase::CanUserBeRemoved(const User* user) const { + // Only regular and supervised users are allowed to be manually removed. + if (!user || (user->GetType() != USER_TYPE_REGULAR && + user->GetType() != USER_TYPE_SUPERVISED)) { + return false; + } + + // Sanity check: we must not remove single user unless it's an enterprise + // device. This check may seem redundant at a first sight because + // this single user must be an owner and we perform special check later + // in order not to remove an owner. However due to non-instant nature of + // ownership assignment this later check may sometimes fail. + // See http://crosbug.com/12723 + if (users_.size() < 2 && !IsEnterpriseManaged()) + return false; + + // Sanity check: do not allow any of the the logged in users to be removed. + for (UserList::const_iterator it = logged_in_users_.begin(); + it != logged_in_users_.end(); + ++it) { + if ((*it)->email() == user->email()) + return false; + } + + return true; +} + +bool UserManagerBase::GetEphemeralUsersEnabled() const { + return ephemeral_users_enabled_; +} + +void UserManagerBase::SetEphemeralUsersEnabled(bool enabled) { + ephemeral_users_enabled_ = enabled; +} + +void UserManagerBase::SetIsCurrentUserNew(bool is_new) { + is_current_user_new_ = is_new; +} + +void UserManagerBase::SetOwnerEmail(std::string owner_user_id) { + owner_email_ = owner_user_id; +} + +const std::string& UserManagerBase::GetPendingUserSwitchID() const { + return pending_user_switch_; +} + +void UserManagerBase::SetPendingUserSwitchID(std::string user_id) { + pending_user_switch_ = user_id; +} + +void UserManagerBase::EnsureUsersLoaded() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (!GetLocalState()) + return; + + if (user_loading_stage_ != STAGE_NOT_LOADED) + return; + user_loading_stage_ = STAGE_LOADING; + + PerformPreUserListLoadingActions(); + + PrefService* local_state = GetLocalState(); + const base::ListValue* prefs_regular_users = + local_state->GetList(kRegularUsers); + + const base::DictionaryValue* prefs_display_names = + local_state->GetDictionary(kUserDisplayName); + const base::DictionaryValue* prefs_given_names = + local_state->GetDictionary(kUserGivenName); + const base::DictionaryValue* prefs_display_emails = + local_state->GetDictionary(kUserDisplayEmail); + + // Load public sessions first. + std::set public_sessions_set; + LoadPublicAccounts(&public_sessions_set); + + // Load regular users and supervised users. + std::vector regular_users; + std::set regular_users_set; + ParseUserList(*prefs_regular_users, + public_sessions_set, + ®ular_users, + ®ular_users_set); + for (std::vector::const_iterator it = regular_users.begin(); + it != regular_users.end(); + ++it) { + User* user = NULL; + const std::string domain = gaia::ExtractDomainName(*it); + if (domain == chromeos::login::kSupervisedUserDomain) + user = User::CreateSupervisedUser(*it); + else + user = User::CreateRegularUser(*it); + user->set_oauth_token_status(LoadUserOAuthStatus(*it)); + user->set_force_online_signin(LoadForceOnlineSignin(*it)); + users_.push_back(user); + + base::string16 display_name; + if (prefs_display_names->GetStringWithoutPathExpansion(*it, + &display_name)) { + user->set_display_name(display_name); + } + + base::string16 given_name; + if (prefs_given_names->GetStringWithoutPathExpansion(*it, &given_name)) { + user->set_given_name(given_name); + } + + std::string display_email; + if (prefs_display_emails->GetStringWithoutPathExpansion(*it, + &display_email)) { + user->set_display_email(display_email); + } + } + + user_loading_stage_ = STAGE_LOADED; + + PerformPostUserListLoadingActions(); +} + +UserList& UserManagerBase::GetUsersAndModify() { + EnsureUsersLoaded(); + return users_; +} + +const User* UserManagerBase::FindUserInList(const std::string& user_id) const { + const UserList& users = GetUsers(); + for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) { + if ((*it)->email() == user_id) + return *it; + } + return NULL; +} + +const bool UserManagerBase::UserExistsInList(const std::string& user_id) const { + const base::ListValue* user_list = GetLocalState()->GetList(kRegularUsers); + for (size_t i = 0; i < user_list->GetSize(); ++i) { + std::string email; + if (user_list->GetString(i, &email) && (user_id == email)) + return true; + } + return false; +} + +User* UserManagerBase::FindUserInListAndModify(const std::string& user_id) { + UserList& users = GetUsersAndModify(); + for (UserList::iterator it = users.begin(); it != users.end(); ++it) { + if ((*it)->email() == user_id) + return *it; + } + return NULL; +} + +void UserManagerBase::GuestUserLoggedIn() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + active_user_ = User::CreateGuestUser(); +} + +void UserManagerBase::AddUserRecord(User* user) { + // Add the user to the front of the user list. + ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsers); + prefs_users_update->Insert(0, new base::StringValue(user->email())); + users_.insert(users_.begin(), user); +} + +void UserManagerBase::RegularUserLoggedIn(const std::string& user_id) { + // Remove the user from the user list. + active_user_ = RemoveRegularOrSupervisedUserFromList(user_id); + + // If the user was not found on the user list, create a new user. + SetIsCurrentUserNew(!active_user_); + if (IsCurrentUserNew()) { + active_user_ = User::CreateRegularUser(user_id); + active_user_->set_oauth_token_status(LoadUserOAuthStatus(user_id)); + SaveUserDisplayName(active_user_->email(), + base::UTF8ToUTF16(active_user_->GetAccountName(true))); + } + + AddUserRecord(active_user_); + + // Make sure that new data is persisted to Local State. + GetLocalState()->CommitPendingWrite(); +} + +void UserManagerBase::RegularUserLoggedInAsEphemeral( + const std::string& user_id) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + SetIsCurrentUserNew(true); + is_current_user_ephemeral_regular_user_ = true; + active_user_ = User::CreateRegularUser(user_id); +} + +void UserManagerBase::NotifyOnLogin() { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + NotifyActiveUserHashChanged(active_user_->username_hash()); + NotifyActiveUserChanged(active_user_); + UpdateLoginState(); +} + +User::OAuthTokenStatus UserManagerBase::LoadUserOAuthStatus( + const std::string& user_id) const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + const base::DictionaryValue* prefs_oauth_status = + GetLocalState()->GetDictionary(kUserOAuthTokenStatus); + int oauth_token_status = User::OAUTH_TOKEN_STATUS_UNKNOWN; + if (prefs_oauth_status && + prefs_oauth_status->GetIntegerWithoutPathExpansion(user_id, + &oauth_token_status)) { + User::OAuthTokenStatus status = + static_cast(oauth_token_status); + HandleUserOAuthTokenStatusChange(user_id, status); + + return status; + } + return User::OAUTH_TOKEN_STATUS_UNKNOWN; +} + +bool UserManagerBase::LoadForceOnlineSignin(const std::string& user_id) const { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + + const base::DictionaryValue* prefs_force_online = + GetLocalState()->GetDictionary(kUserForceOnlineSignin); + bool force_online_signin = false; + if (prefs_force_online) { + prefs_force_online->GetBooleanWithoutPathExpansion(user_id, + &force_online_signin); + } + return force_online_signin; +} + +void UserManagerBase::RemoveNonCryptohomeData(const std::string& user_id) { + PrefService* prefs = GetLocalState(); + DictionaryPrefUpdate prefs_display_name_update(prefs, kUserDisplayName); + prefs_display_name_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_given_name_update(prefs, kUserGivenName); + prefs_given_name_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_display_email_update(prefs, kUserDisplayEmail); + prefs_display_email_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_oauth_update(prefs, kUserOAuthTokenStatus); + prefs_oauth_update->RemoveWithoutPathExpansion(user_id, NULL); + + DictionaryPrefUpdate prefs_force_online_update(prefs, kUserForceOnlineSignin); + prefs_force_online_update->RemoveWithoutPathExpansion(user_id, NULL); +} + +User* UserManagerBase::RemoveRegularOrSupervisedUserFromList( + const std::string& user_id) { + ListPrefUpdate prefs_users_update(GetLocalState(), kRegularUsers); + prefs_users_update->Clear(); + User* user = NULL; + for (UserList::iterator it = users_.begin(); it != users_.end();) { + const std::string user_email = (*it)->email(); + if (user_email == user_id) { + user = *it; + it = users_.erase(it); + } else { + if ((*it)->GetType() == USER_TYPE_REGULAR || + (*it)->GetType() == USER_TYPE_SUPERVISED) { + prefs_users_update->Append(new base::StringValue(user_email)); + } + ++it; + } + } + return user; +} + +void UserManagerBase::NotifyActiveUserChanged(const User* active_user) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, + session_state_observer_list_, + ActiveUserChanged(active_user)); +} + +void UserManagerBase::NotifyUserAddedToSession(const User* added_user, + bool user_switch_pending) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, + session_state_observer_list_, + UserAddedToSession(added_user)); +} + +void UserManagerBase::NotifyActiveUserHashChanged(const std::string& hash) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + FOR_EACH_OBSERVER(UserManager::UserSessionStateObserver, + session_state_observer_list_, + ActiveUserHashChanged(hash)); +} + +void UserManagerBase::UpdateLoginState() { + if (!chromeos::LoginState::IsInitialized()) + return; // LoginState may not be intialized in tests. + + chromeos::LoginState::LoggedInState logged_in_state; + logged_in_state = active_user_ ? chromeos::LoginState::LOGGED_IN_ACTIVE + : chromeos::LoginState::LOGGED_IN_NONE; + + chromeos::LoginState::LoggedInUserType login_user_type; + if (logged_in_state == chromeos::LoginState::LOGGED_IN_NONE) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_NONE; + else if (is_current_user_owner_) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_OWNER; + else if (active_user_->GetType() == USER_TYPE_GUEST) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_GUEST; + else if (active_user_->GetType() == USER_TYPE_RETAIL_MODE) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_RETAIL_MODE; + else if (active_user_->GetType() == USER_TYPE_PUBLIC_ACCOUNT) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_PUBLIC_ACCOUNT; + else if (active_user_->GetType() == USER_TYPE_SUPERVISED) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_SUPERVISED; + else if (active_user_->GetType() == USER_TYPE_KIOSK_APP) + login_user_type = chromeos::LoginState::LOGGED_IN_USER_KIOSK_APP; + else + login_user_type = chromeos::LoginState::LOGGED_IN_USER_REGULAR; + + if (primary_user_) { + chromeos::LoginState::Get()->SetLoggedInStateAndPrimaryUser( + logged_in_state, login_user_type, primary_user_->username_hash()); + } else { + chromeos::LoginState::Get()->SetLoggedInState(logged_in_state, + login_user_type); + } +} + +void UserManagerBase::SetLRUUser(User* user) { + UserList::iterator it = + std::find(lru_logged_in_users_.begin(), lru_logged_in_users_.end(), user); + if (it != lru_logged_in_users_.end()) + lru_logged_in_users_.erase(it); + lru_logged_in_users_.insert(lru_logged_in_users_.begin(), user); +} + +void UserManagerBase::SendRegularUserLoginMetrics(const std::string& user_id) { + // If this isn't the first time Chrome was run after the system booted, + // assume that Chrome was restarted because a previous session ended. + if (!CommandLine::ForCurrentProcess()->HasSwitch( + chromeos::switches::kFirstExecAfterBoot)) { + const std::string last_email = + GetLocalState()->GetString(kLastLoggedInRegularUser); + const base::TimeDelta time_to_login = + base::TimeTicks::Now() - manager_creation_time_; + if (!last_email.empty() && user_id != last_email && + time_to_login.InSeconds() <= kLogoutToLoginDelayMaxSec) { + UMA_HISTOGRAM_CUSTOM_COUNTS("UserManager.LogoutToLoginDelay", + time_to_login.InSeconds(), + 0, + kLogoutToLoginDelayMaxSec, + 50); + } + } +} + +void UserManagerBase::UpdateUserAccountLocale(const std::string& user_id, + const std::string& locale) { + if (!locale.empty() && locale != GetApplicationLocale()) { + base::Callback on_resolve_callback = + base::Bind(&UserManagerBase::DoUpdateAccountLocale, + weak_factory_.GetWeakPtr(), + user_id); + blocking_task_runner_->PostTask(FROM_HERE, + base::Bind(&UserManagerBase::ResolveLocale, + weak_factory_.GetWeakPtr(), + locale, + on_resolve_callback)); + } else { + DoUpdateAccountLocale(user_id, locale); + } +} + +void UserManagerBase::ResolveLocale( + const std::string& raw_locale, + base::Callback on_resolve_callback) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + std::string resolved_locale; + ignore_result(l10n_util::CheckAndResolveLocale(raw_locale, &resolved_locale)); + task_runner_->PostTask(FROM_HERE, + base::Bind(on_resolve_callback, resolved_locale)); +} + +void UserManagerBase::DoUpdateAccountLocale( + const std::string& user_id, + const std::string& resolved_locale) { + if (User* user = FindUserAndModify(user_id)) + user->SetAccountLocale(resolved_locale); +} + +void UserManagerBase::DeleteUser(User* user) { + const bool is_active_user = (user == active_user_); + delete user; + if (is_active_user) + active_user_ = NULL; +} + +} // namespace user_manager diff --git a/chrome/browser/chromeos/login/users/user_manager_base.h b/components/user_manager/user_manager_base.h similarity index 84% rename from chrome/browser/chromeos/login/users/user_manager_base.h rename to components/user_manager/user_manager_base.h index 5fcea54048263..e7651cbd4b7c8 100644 --- a/chrome/browser/chromeos/login/users/user_manager_base.h +++ b/components/user_manager/user_manager_base.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_CHROMEOS_LOGIN_USERS_USER_MANAGER_BASE_H_ -#define CHROME_BROWSER_CHROMEOS_LOGIN_USERS_USER_MANAGER_BASE_H_ +#ifndef COMPONENTS_USER_MANAGER_USER_MANAGER_BASE_H_ +#define COMPONENTS_USER_MANAGER_USER_MANAGER_BASE_H_ #include #include @@ -14,19 +14,27 @@ #include "base/observer_list.h" #include "base/synchronization/lock.h" #include "base/time/time.h" -#include "chrome/browser/chromeos/login/users/user_manager.h" #include "components/user_manager/user.h" +#include "components/user_manager/user_manager.h" +#include "components/user_manager/user_manager_export.h" class PrefService; class PrefRegistrySimple; -namespace chromeos { +namespace base { +class ListValue; +class TaskRunner; +} + +namespace user_manager { class RemoveUserDelegate; // Base implementation of the UserManager interface. -class UserManagerBase : public UserManager { +class USER_MANAGER_EXPORT UserManagerBase : public UserManager { public: + UserManagerBase(scoped_refptr task_runner, + scoped_refptr blocking_task_runner); virtual ~UserManagerBase(); // Registers UserManagerBase preferences. @@ -34,9 +42,9 @@ class UserManagerBase : public UserManager { // UserManager implementation: virtual void Shutdown() OVERRIDE; - virtual const user_manager::UserList& GetUsers() const OVERRIDE; - virtual const user_manager::UserList& GetLoggedInUsers() const OVERRIDE; - virtual const user_manager::UserList& GetLRULoggedInUsers() const OVERRIDE; + virtual const UserList& GetUsers() const OVERRIDE; + virtual const UserList& GetLoggedInUsers() const OVERRIDE; + virtual const UserList& GetLRULoggedInUsers() const OVERRIDE; virtual const std::string& GetOwnerEmail() const OVERRIDE; virtual void UserLoggedIn(const std::string& user_id, const std::string& user_id_hash, @@ -47,18 +55,16 @@ class UserManagerBase : public UserManager { RemoveUserDelegate* delegate) OVERRIDE; virtual void RemoveUserFromList(const std::string& user_id) OVERRIDE; virtual bool IsKnownUser(const std::string& user_id) const OVERRIDE; - virtual const user_manager::User* FindUser( - const std::string& user_id) const OVERRIDE; - virtual user_manager::User* FindUserAndModify( - const std::string& user_id) OVERRIDE; - virtual const user_manager::User* GetLoggedInUser() const OVERRIDE; - virtual user_manager::User* GetLoggedInUser() OVERRIDE; - virtual const user_manager::User* GetActiveUser() const OVERRIDE; - virtual user_manager::User* GetActiveUser() OVERRIDE; - virtual const user_manager::User* GetPrimaryUser() const OVERRIDE; + virtual const User* FindUser(const std::string& user_id) const OVERRIDE; + virtual User* FindUserAndModify(const std::string& user_id) OVERRIDE; + virtual const User* GetLoggedInUser() const OVERRIDE; + virtual User* GetLoggedInUser() OVERRIDE; + virtual const User* GetActiveUser() const OVERRIDE; + virtual User* GetActiveUser() OVERRIDE; + virtual const User* GetPrimaryUser() const OVERRIDE; virtual void SaveUserOAuthStatus( const std::string& user_id, - user_manager::User::OAuthTokenStatus oauth_token_status) OVERRIDE; + User::OAuthTokenStatus oauth_token_status) OVERRIDE; virtual void SaveForceOnlineSignin(const std::string& user_id, bool force_online_signin) OVERRIDE; virtual void SaveUserDisplayName(const std::string& user_id, @@ -108,18 +114,18 @@ class UserManagerBase : public UserManager { // Adds |user| to users list, and adds it to front of LRU list. It is assumed // that there is no user with same id. - virtual void AddUserRecord(user_manager::User* user); + virtual void AddUserRecord(User* user); // Returns true if trusted device policies have successfully been retrieved // and ephemeral users are enabled. virtual bool AreEphemeralUsersEnabled() const = 0; // Returns true if user may be removed. - virtual bool CanUserBeRemoved(const user_manager::User* user) const; + virtual bool CanUserBeRemoved(const User* user) const; // A wrapper around C++ delete operator. Deletes |user|, and when |user| // equals to active_user_, active_user_ is reset to NULL. - virtual void DeleteUser(user_manager::User* user); + virtual void DeleteUser(User* user); // Returns the locale used by the application. virtual const std::string& GetApplicationLocale() const = 0; @@ -131,6 +137,11 @@ class UserManagerBase : public UserManager { // Subsequent calls have no effect. Must be called on the UI thread. void EnsureUsersLoaded(); + // Handle OAuth token |status| change for |user_id|. + virtual void HandleUserOAuthTokenStatusChange( + const std::string& user_id, + User::OAuthTokenStatus status) const = 0; + // Returns true if device is enterprise managed. virtual bool IsEnterpriseManaged() const = 0; @@ -148,7 +159,7 @@ class UserManagerBase : public UserManager { // Notifies observers that another user was added to the session. // If |user_switch_pending| is true this means that user has not been fully // initialized yet like waiting for profile to be loaded. - virtual void NotifyUserAddedToSession(const user_manager::User* added_user, + virtual void NotifyUserAddedToSession(const User* added_user, bool user_switch_pending); // Performs any additional actions before user list is loaded. @@ -171,8 +182,7 @@ class UserManagerBase : public UserManager { // Removes a regular or supervised user from the user list. // Returns the user if found or NULL otherwise. // Also removes the user from the persistent user list. - user_manager::User* RemoveRegularOrSupervisedUserFromList( - const std::string& user_id); + User* RemoveRegularOrSupervisedUserFromList(const std::string& user_id); // Implementation for RemoveUser method. This is an asynchronous part of the // method, that verifies that owner will not get deleted, and calls @@ -209,7 +219,7 @@ class UserManagerBase : public UserManager { virtual void KioskAppLoggedIn(const std::string& app_id) = 0; // Indicates that a user just logged into a public session. - virtual void PublicAccountUserLoggedIn(user_manager::User* user) = 0; + virtual void PublicAccountUserLoggedIn(User* user) = 0; // Indicates that a regular user just logged in. virtual void RegularUserLoggedIn(const std::string& user_id); @@ -241,16 +251,16 @@ class UserManagerBase : public UserManager { // NULL until a user has logged in, then points to one // of the User instances in |users_|, the |guest_user_| instance or an // ephemeral user instance. - user_manager::User* active_user_; + User* active_user_; // The primary user of the current session. It is recorded for the first // signed-in user and does not change thereafter. - user_manager::User* primary_user_; + User* primary_user_; // List of all known users. User instances are owned by |this|. Regular users // are removed by |RemoveUserFromList|, public accounts by // |UpdateAndCleanUpPublicAccounts|. - user_manager::UserList users_; + UserList users_; private: // Stages of loading user list from preferences. Some methods can have @@ -259,22 +269,21 @@ class UserManagerBase : public UserManager { // Returns a list of users who have logged into this device previously. // Same as GetUsers but used if you need to modify User from that list. - user_manager::UserList& GetUsersAndModify(); + UserList& GetUsersAndModify(); // Returns the user with the given email address if found in the persistent // list. Returns |NULL| otherwise. - const user_manager::User* FindUserInList(const std::string& user_id) const; + const User* FindUserInList(const std::string& user_id) const; // Returns |true| if user with the given id is found in the persistent list. // Returns |false| otherwise. Does not trigger user loading. const bool UserExistsInList(const std::string& user_id) const; // Same as FindUserInList but returns non-const pointer to User object. - user_manager::User* FindUserInListAndModify(const std::string& user_id); + User* FindUserInListAndModify(const std::string& user_id); // Reads user's oauth token status from local state preferences. - user_manager::User::OAuthTokenStatus LoadUserOAuthStatus( - const std::string& user_id) const; + User::OAuthTokenStatus LoadUserOAuthStatus(const std::string& user_id) const; // Read a flag indicating whether online authentication against GAIA should // be enforced during the user's next sign-in from local state preferences. @@ -284,7 +293,7 @@ class UserManagerBase : public UserManager { void NotifyMergeSessionStateChanged(); // Notifies observers that active user has changed. - void NotifyActiveUserChanged(const user_manager::User* active_user); + void NotifyActiveUserChanged(const User* active_user); // Notifies observers that active user_id hash has changed. void NotifyActiveUserHashChanged(const std::string& hash); @@ -293,7 +302,7 @@ class UserManagerBase : public UserManager { void UpdateLoginState(); // Insert |user| at the front of the LRU user list. - void SetLRUUser(user_manager::User* user); + void SetLRUUser(User* user); // Sends metrics in response to a regular user logging in. void SendRegularUserLoginMetrics(const std::string& user_id); @@ -302,6 +311,12 @@ class UserManagerBase : public UserManager { virtual void UpdateUserAccountLocale(const std::string& user_id, const std::string& locale); + // Runs on SequencedWorkerPool thread. Passes resolved locale to + // |on_resolve_callback| on UI thread. + void ResolveLocale( + const std::string& raw_locale, + base::Callback on_resolve_callback); + // Updates user account after locale was resolved. void DoUpdateAccountLocale(const std::string& user_id, const std::string& resolved_locale); @@ -311,12 +326,12 @@ class UserManagerBase : public UserManager { // List of all users that are logged in current session. These point to User // instances in |users_|. Only one of them could be marked as active. - user_manager::UserList logged_in_users_; + UserList logged_in_users_; // A list of all users that are logged in the current session. In contrast to // |logged_in_users|, the order of this list is least recently used so that // the active user should always be the first one in the list. - user_manager::UserList lru_logged_in_users_; + UserList lru_logged_in_users_; // True if SessionStarted() has been called. bool session_started_; @@ -359,11 +374,14 @@ class UserManagerBase : public UserManager { // as soon as user's profile is loaded. std::string pending_user_switch_; + scoped_refptr task_runner_; + scoped_refptr blocking_task_runner_; + base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(UserManagerBase); }; -} // namespace chromeos +} // namespace user_manager -#endif // CHROME_BROWSER_CHROMEOS_LOGIN_USERS_USER_MANAGER_BASE_H_ +#endif // COMPONENTS_USER_MANAGER_USER_MANAGER_BASE_H_ diff --git a/components/web_contents_delegate_android/web_contents_delegate_android.cc b/components/web_contents_delegate_android/web_contents_delegate_android.cc index 525e5ba4ef4a9..9e6e15e3fb656 100644 --- a/components/web_contents_delegate_android/web_contents_delegate_android.cc +++ b/components/web_contents_delegate_android/web_contents_delegate_android.cc @@ -140,7 +140,7 @@ WebContents* WebContentsDelegateAndroid::OpenURLFromTab( } void WebContentsDelegateAndroid::NavigationStateChanged( - const WebContents* source, unsigned changed_flags) { + const WebContents* source, content::InvalidateTypes changed_flags) { JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef obj = GetJavaDelegate(env); if (obj.is_null()) diff --git a/components/web_contents_delegate_android/web_contents_delegate_android.h b/components/web_contents_delegate_android/web_contents_delegate_android.h index b61eba47f1268..33a46186ed551 100644 --- a/components/web_contents_delegate_android/web_contents_delegate_android.h +++ b/components/web_contents_delegate_android/web_contents_delegate_android.h @@ -58,8 +58,9 @@ class WebContentsDelegateAndroid : public content::WebContentsDelegate { content::WebContents* source, SkColor color, const std::vector& suggestions) OVERRIDE; - virtual void NavigationStateChanged(const content::WebContents* source, - unsigned changed_flags) OVERRIDE; + virtual void NavigationStateChanged( + const content::WebContents* source, + content::InvalidateTypes changed_flags) OVERRIDE; virtual void VisibleSSLStateChanged( const content::WebContents* source) OVERRIDE; virtual void ActivateContents(content::WebContents* contents) OVERRIDE; diff --git a/content/BUILD.gn b/content/BUILD.gn index a2bd4894665db..94c944da9cc2b 100644 --- a/content/BUILD.gn +++ b/content/BUILD.gn @@ -14,7 +14,7 @@ content_shared_components = [ "//content/plugin", "//content/ppapi_plugin", "//content/public/app", - "//content/public/browser", + "//content/public/browser:sources", "//content/public/child", "//content/public/common", "//content/public/plugin", @@ -28,6 +28,7 @@ if (is_component_build) { deps = content_shared_components + [ "//content/app", ] + forward_dependent_configs_from = deps } } else { group("content") { diff --git a/content/DEPS b/content/DEPS index 9d1cdbcdd1bed..d964f5d5af2ad 100644 --- a/content/DEPS +++ b/content/DEPS @@ -26,20 +26,20 @@ include_rules = [ "+crypto", "+grit/blink_resources.h", "+grit/content_resources.h", + "+grit/content_strings.h", "+grit/ui_resources.h", "+grit/ui_strings.h", "+grit/webkit_resources.h", - "+grit/webkit_strings.h", "+grit/webui_resources.h", "+grit/webui_resources_map.h", "+dbus", "+gpu", - "+mojo/public", + "+mojo/application_manager", "+mojo/bindings/js", "+mojo/common", "+mojo/embedder", - "+mojo/service_manager", + "+mojo/public", "+net", "+ppapi", "+printing", diff --git a/content/app/BUILD.gn b/content/app/BUILD.gn index cefe8144b6e7b..f636d9f3735c4 100644 --- a/content/app/BUILD.gn +++ b/content/app/BUILD.gn @@ -51,9 +51,9 @@ if (is_ios) { ] } else { content_app_deps += [ + "//mojo/application_manager", "//mojo/environment:chromium", "//mojo/public/interfaces/application", - "//mojo/service_manager", "//mojo/system", ] } diff --git a/content/app/android/library_loader_hooks.cc b/content/app/android/library_loader_hooks.cc index 4778bfbadc81a..2b81f9a3f2ffd 100644 --- a/content/app/android/library_loader_hooks.cc +++ b/content/app/android/library_loader_hooks.cc @@ -71,11 +71,8 @@ bool EnsureJniRegistered(JNIEnv* env) { return true; } -bool LibraryLoaded(JNIEnv* env, jclass clazz, - jobjectArray init_command_line) { - base::android::InitNativeCommandLineFromJavaArray(env, init_command_line); - - CommandLine* command_line = CommandLine::ForCurrentProcess(); +bool LibraryLoaded(JNIEnv* env, jclass clazz) { + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kTraceStartup)) { base::debug::CategoryFilter category_filter( diff --git a/content/app/content_main_runner.cc b/content/app/content_main_runner.cc index ba132cb0473e7..a3fcaed906c0f 100644 --- a/content/app/content_main_runner.cc +++ b/content/app/content_main_runner.cc @@ -190,7 +190,7 @@ void CommonSubprocessInit(const std::string& process_type) { // Only needed on Windows for creating stats tables. #if defined(OS_WIN) -static base::ProcessId GetBrowserPid(const CommandLine& command_line) { +static base::ProcessId GetBrowserPid(const base::CommandLine& command_line) { base::ProcessId browser_pid = base::GetCurrentProcId(); if (command_line.HasSwitch(switches::kProcessChannelID)) { std::string channel_name = @@ -205,7 +205,7 @@ static base::ProcessId GetBrowserPid(const CommandLine& command_line) { } #endif -static void InitializeStatsTable(const CommandLine& command_line) { +static void InitializeStatsTable(const base::CommandLine& command_line) { // Initialize the Stats Counters table. With this initialized, // the StatsViewer can be utilized to read counters outside of // Chrome. These lines can be commented out to effectively turn @@ -258,7 +258,7 @@ class ContentClientInitializer { content_client->plugin_ = &g_empty_content_plugin_client.Get(); // Single process not supported in split dll mode. } else if (process_type == switches::kRendererProcess || - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kSingleProcess)) { if (delegate) content_client->renderer_ = delegate->CreateContentRendererClient(); @@ -267,7 +267,7 @@ class ContentClientInitializer { } if (process_type == switches::kUtilityProcess || - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kSingleProcess)) { if (delegate) content_client->utility_ = delegate->CreateContentUtilityClient(); @@ -320,7 +320,8 @@ int RunZygote(const MainFunctionParams& main_function_params, // Zygote::HandleForkRequest may have reallocated the command // line so update it here with the new version. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); ContentClientInitializer::Set(process_type, delegate); @@ -356,7 +357,7 @@ static void RegisterMainThreadFactories() { GpuProcessHost::RegisterGpuMainThreadFactory( CreateInProcessGpuThread); #else - CommandLine& command_line = *CommandLine::ForCurrentProcess(); + base::CommandLine& command_line = *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kSingleProcess)) { LOG(FATAL) << "--single-process is not supported in chrome multiple dll browser."; @@ -589,7 +590,7 @@ class ContentMainRunnerImpl : public ContentMainRunner { argv = params.argv; #endif - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); if (!delegate_ || delegate_->ShouldEnableTerminationOnHeapCorruption()) base::EnableTerminationOnHeapCorruption(); @@ -606,7 +607,8 @@ class ContentMainRunnerImpl : public ContentMainRunner { completed_basic_startup_ = true; - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); @@ -738,6 +740,15 @@ class ContentMainRunnerImpl : public ContentMainRunner { if (delegate_) delegate_->SandboxInitialized(process_type); +#if defined(OS_TIZEN_MOBILE) + if (process_type.empty()) + StoreArgvPointerAddress(argv); + else + SetProcessTitleFromCommandLine(argv); +#elif defined(OS_POSIX) && !defined(OS_IOS) && !defined(OS_ANDROID) + SetProcessTitleFromCommandLine(argv); +#endif + // Return -1 to indicate no early termination. return -1; } @@ -745,9 +756,10 @@ class ContentMainRunnerImpl : public ContentMainRunner { virtual int Run() OVERRIDE { DCHECK(is_initialized_); DCHECK(!is_shutdown_); - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); std::string process_type = - command_line.GetSwitchValueASCII(switches::kProcessType); + command_line.GetSwitchValueASCII(switches::kProcessType); MainFunctionParams main_params(command_line); main_params.ui_task = ui_task_; @@ -769,7 +781,8 @@ class ContentMainRunnerImpl : public ContentMainRunner { DCHECK(!is_shutdown_); if (completed_basic_startup_ && delegate_) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); diff --git a/content/app/mojo/mojo_browsertest.cc b/content/app/mojo/mojo_browsertest.cc index f578b5724601f..17b869b99444c 100644 --- a/content/app/mojo/mojo_browsertest.cc +++ b/content/app/mojo/mojo_browsertest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "content/public/test/content_browser_test.h" -#include "mojo/service_manager/service_manager.h" +#include "mojo/application_manager/application_manager.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { @@ -14,7 +14,7 @@ class MojoTest : public ContentBrowserTest { protected: bool HasCreatedInstance() { - return mojo::ServiceManager::TestAPI::HasCreatedInstance(); + return mojo::ApplicationManager::TestAPI::HasCreatedInstance(); } private: diff --git a/content/app/mojo/mojo_init.cc b/content/app/mojo/mojo_init.cc index 55630b4a4aec0..2873d311aa71e 100644 --- a/content/app/mojo/mojo_init.cc +++ b/content/app/mojo/mojo_init.cc @@ -5,14 +5,14 @@ #include "content/app/mojo/mojo_init.h" #include "base/logging.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/embedder/embedder.h" -#include "mojo/service_manager/service_manager.h" namespace content { void InitializeMojo() { mojo::embedder::Init(); - mojo::ServiceManager::GetInstance(); + mojo::ApplicationManager::GetInstance(); } } // namespace content diff --git a/content/app/resources/README.txt b/content/app/resources/README.txt new file mode 100644 index 0000000000000..1472c31c8d637 --- /dev/null +++ b/content/app/resources/README.txt @@ -0,0 +1,108 @@ +The files listed below and found in this directory are copied from the +Mozilla source tree, where they are licensed under the MPL/GPL/LGPL. While +binary files do not contain a license block, I have pasted the relevant +Mozilla license block below. + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +///////////////////////////////////////////////////////////////////////////// + +The files listed below and found in this directory are copied from the WebKit +source tree or are derivative works of files from the WebKit source tree. +They are licensed under a BSD license. While binary files do not contain +a license block, I have pasted the relevant WebKit license block below. + +pan_icon.png +pan_east.cur +pan_middle.cur +pan_north.cur +pan_north_east.cur +pan_north_west.cur +pan_south.cur +pan_south_east.cur +pan_south_west.cur +pan_west.cur +textarea_resize_corner.png +alias_cursor.png +cell_cursor.png +east_resize_cursor.png +east_west_resize_cursor.png +help_cursor.png +link_cursor.png +move_cursor.png +none_cursor.png +north_east_resize_cursor.png +north_east_south_west_resize_cursor.png +north_resize_cursor.png +north_south_resize_cursor.png +north_west_resize_cursor.png +north_west_south_east_resize_cursor.png +progress_cursor.png +south_east_resize_cursor.png +south_resize_cursor.png +south_west_resize_cursor.png +vertical_text_cursor.png +wait_cursor.png +west_resize_cursor.png +zoom_in_cursor.png +zoom_out_cursor.png + +// ***** BEGIN LICENSE BLOCK ***** + +Copyright (C) 2005 Apple Computer, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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/content/app/resources/default_100_percent/alias_cursor.png b/content/app/resources/default_100_percent/alias_cursor.png new file mode 100644 index 0000000000000..36d48d34393dc Binary files /dev/null and b/content/app/resources/default_100_percent/alias_cursor.png differ diff --git a/content/app/resources/default_100_percent/broken_image.png b/content/app/resources/default_100_percent/broken_image.png new file mode 100644 index 0000000000000..ad76cc1965b64 Binary files /dev/null and b/content/app/resources/default_100_percent/broken_image.png differ diff --git a/content/app/resources/default_100_percent/cell_cursor.png b/content/app/resources/default_100_percent/cell_cursor.png new file mode 100644 index 0000000000000..6dc266c8cb1bf Binary files /dev/null and b/content/app/resources/default_100_percent/cell_cursor.png differ diff --git a/content/app/resources/default_100_percent/east_resize_cursor.png b/content/app/resources/default_100_percent/east_resize_cursor.png new file mode 100644 index 0000000000000..73f9cf8c5ec68 Binary files /dev/null and b/content/app/resources/default_100_percent/east_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/east_west_resize_cursor.png b/content/app/resources/default_100_percent/east_west_resize_cursor.png new file mode 100644 index 0000000000000..460f417138c1f Binary files /dev/null and b/content/app/resources/default_100_percent/east_west_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/help_cursor.png b/content/app/resources/default_100_percent/help_cursor.png new file mode 100644 index 0000000000000..8706f4fb2c592 Binary files /dev/null and b/content/app/resources/default_100_percent/help_cursor.png differ diff --git a/content/app/resources/default_100_percent/link_cursor.png b/content/app/resources/default_100_percent/link_cursor.png new file mode 100644 index 0000000000000..e48cd588d0c7a Binary files /dev/null and b/content/app/resources/default_100_percent/link_cursor.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption.png new file mode 100644 index 0000000000000..a92b3d2051d42 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_closedcaption.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption_disabled.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption_disabled.png new file mode 100644 index 0000000000000..7ac1a898758d2 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_closedcaption_disabled.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption_down.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption_down.png new file mode 100644 index 0000000000000..60da8a2973f3d Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_closedcaption_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_closedcaption_hover.png b/content/app/resources/default_100_percent/mediaplayer_closedcaption_hover.png new file mode 100644 index 0000000000000..a3e069f991e30 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_closedcaption_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen.png new file mode 100644 index 0000000000000..713a87796d2b1 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_fullscreen.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen_disabled.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen_disabled.png new file mode 100644 index 0000000000000..f0958a64c0f0d Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_fullscreen_disabled.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen_down.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen_down.png new file mode 100644 index 0000000000000..3a6cd5db13a5f Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_fullscreen_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_fullscreen_hover.png b/content/app/resources/default_100_percent/mediaplayer_fullscreen_hover.png new file mode 100644 index 0000000000000..38e33304dfa1f Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_fullscreen_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_overlay_play.png b/content/app/resources/default_100_percent/mediaplayer_overlay_play.png new file mode 100644 index 0000000000000..cfd48d74e2873 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_overlay_play.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_pause.png b/content/app/resources/default_100_percent/mediaplayer_pause.png new file mode 100644 index 0000000000000..b488e97a66bac Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_pause.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_pause_down.png b/content/app/resources/default_100_percent/mediaplayer_pause_down.png new file mode 100644 index 0000000000000..bf754e9c4bd3f Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_pause_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_pause_hover.png b/content/app/resources/default_100_percent/mediaplayer_pause_hover.png new file mode 100644 index 0000000000000..ac3334c4c88c4 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_pause_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_play.png b/content/app/resources/default_100_percent/mediaplayer_play.png new file mode 100644 index 0000000000000..1e53d4358099a Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_play.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_play_disabled.png b/content/app/resources/default_100_percent/mediaplayer_play_disabled.png new file mode 100644 index 0000000000000..8e1220018cc40 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_play_disabled.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_play_down.png b/content/app/resources/default_100_percent/mediaplayer_play_down.png new file mode 100644 index 0000000000000..fa6a243429b25 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_play_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_play_hover.png b/content/app/resources/default_100_percent/mediaplayer_play_hover.png new file mode 100644 index 0000000000000..a71c2f327a864 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_play_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_slider_thumb.png b/content/app/resources/default_100_percent/mediaplayer_slider_thumb.png new file mode 100644 index 0000000000000..4cc24d1591a27 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_slider_thumb.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_slider_thumb_down.png b/content/app/resources/default_100_percent/mediaplayer_slider_thumb_down.png new file mode 100644 index 0000000000000..483101d906701 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_slider_thumb_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_slider_thumb_hover.png b/content/app/resources/default_100_percent/mediaplayer_slider_thumb_hover.png new file mode 100644 index 0000000000000..2a968424f5d6a Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_slider_thumb_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_disabled.png b/content/app/resources/default_100_percent/mediaplayer_sound_disabled.png new file mode 100644 index 0000000000000..38fa4baaa9529 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_disabled.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level0.png b/content/app/resources/default_100_percent/mediaplayer_sound_level0.png new file mode 100644 index 0000000000000..22d6a109de778 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level0.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level0_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level0_down.png new file mode 100644 index 0000000000000..d956e5810c245 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level0_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level0_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level0_hover.png new file mode 100644 index 0000000000000..56214a1494cc5 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level0_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level1.png b/content/app/resources/default_100_percent/mediaplayer_sound_level1.png new file mode 100644 index 0000000000000..2c6809abb1b8e Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level1.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level1_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level1_down.png new file mode 100644 index 0000000000000..faa188841ec6f Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level1_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level1_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level1_hover.png new file mode 100644 index 0000000000000..1fdaf21003a80 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level1_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level2.png b/content/app/resources/default_100_percent/mediaplayer_sound_level2.png new file mode 100644 index 0000000000000..e98c51d306c14 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level2.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level2_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level2_down.png new file mode 100644 index 0000000000000..37f5d76942c12 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level2_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level2_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level2_hover.png new file mode 100644 index 0000000000000..e44356bb2ef2a Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level2_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level3.png b/content/app/resources/default_100_percent/mediaplayer_sound_level3.png new file mode 100644 index 0000000000000..05caa64243986 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level3.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level3_down.png b/content/app/resources/default_100_percent/mediaplayer_sound_level3_down.png new file mode 100644 index 0000000000000..db38404858620 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level3_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_sound_level3_hover.png b/content/app/resources/default_100_percent/mediaplayer_sound_level3_hover.png new file mode 100644 index 0000000000000..838b07f34d7bc Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_sound_level3_hover.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb.png new file mode 100644 index 0000000000000..c4620f542593d Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_disabled.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_disabled.png new file mode 100644 index 0000000000000..d32434b6d2ad3 Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_disabled.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_down.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_down.png new file mode 100644 index 0000000000000..98ee8509264af Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_down.png differ diff --git a/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_hover.png b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_hover.png new file mode 100644 index 0000000000000..93cca9b325f5b Binary files /dev/null and b/content/app/resources/default_100_percent/mediaplayer_volume_slider_thumb_hover.png differ diff --git a/content/app/resources/default_100_percent/move_cursor.png b/content/app/resources/default_100_percent/move_cursor.png new file mode 100644 index 0000000000000..2d82272129f01 Binary files /dev/null and b/content/app/resources/default_100_percent/move_cursor.png differ diff --git a/content/app/resources/default_100_percent/none_cursor.png b/content/app/resources/default_100_percent/none_cursor.png new file mode 100644 index 0000000000000..bd36820b890be Binary files /dev/null and b/content/app/resources/default_100_percent/none_cursor.png differ diff --git a/content/app/resources/default_100_percent/north_east_resize_cursor.png b/content/app/resources/default_100_percent/north_east_resize_cursor.png new file mode 100644 index 0000000000000..f2104f92b911a Binary files /dev/null and b/content/app/resources/default_100_percent/north_east_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/north_east_south_west_resize_cursor.png b/content/app/resources/default_100_percent/north_east_south_west_resize_cursor.png new file mode 100644 index 0000000000000..5db337a1d5199 Binary files /dev/null and b/content/app/resources/default_100_percent/north_east_south_west_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/north_resize_cursor.png b/content/app/resources/default_100_percent/north_resize_cursor.png new file mode 100644 index 0000000000000..bdd01b9fc4073 Binary files /dev/null and b/content/app/resources/default_100_percent/north_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/north_south_resize_cursor.png b/content/app/resources/default_100_percent/north_south_resize_cursor.png new file mode 100644 index 0000000000000..17971e73e9f75 Binary files /dev/null and b/content/app/resources/default_100_percent/north_south_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/north_west_resize_cursor.png b/content/app/resources/default_100_percent/north_west_resize_cursor.png new file mode 100644 index 0000000000000..d03e5efa2a2e0 Binary files /dev/null and b/content/app/resources/default_100_percent/north_west_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/north_west_south_east_resize_cursor.png b/content/app/resources/default_100_percent/north_west_south_east_resize_cursor.png new file mode 100644 index 0000000000000..78552e7d83d4c Binary files /dev/null and b/content/app/resources/default_100_percent/north_west_south_east_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/overhang_pattern.png b/content/app/resources/default_100_percent/overhang_pattern.png new file mode 100644 index 0000000000000..838e87c323878 Binary files /dev/null and b/content/app/resources/default_100_percent/overhang_pattern.png differ diff --git a/content/app/resources/default_100_percent/overhang_shadow.png b/content/app/resources/default_100_percent/overhang_shadow.png new file mode 100644 index 0000000000000..4e0fc4b720c19 Binary files /dev/null and b/content/app/resources/default_100_percent/overhang_shadow.png differ diff --git a/content/app/resources/default_100_percent/pan_icon.png b/content/app/resources/default_100_percent/pan_icon.png new file mode 100644 index 0000000000000..e07d96255b83e Binary files /dev/null and b/content/app/resources/default_100_percent/pan_icon.png differ diff --git a/content/app/resources/default_100_percent/password_generation.png b/content/app/resources/default_100_percent/password_generation.png new file mode 100644 index 0000000000000..2b45ac3ac26ce Binary files /dev/null and b/content/app/resources/default_100_percent/password_generation.png differ diff --git a/content/app/resources/default_100_percent/password_generation_hover.png b/content/app/resources/default_100_percent/password_generation_hover.png new file mode 100644 index 0000000000000..a832cdc81ed96 Binary files /dev/null and b/content/app/resources/default_100_percent/password_generation_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_ftp.png b/content/app/resources/default_100_percent/pdf_button_ftp.png new file mode 100644 index 0000000000000..0f12d12a88282 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_ftp.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_ftp_hover.png b/content/app/resources/default_100_percent/pdf_button_ftp_hover.png new file mode 100644 index 0000000000000..1ce6c67f44840 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_ftp_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_ftp_pressed.png b/content/app/resources/default_100_percent/pdf_button_ftp_pressed.png new file mode 100644 index 0000000000000..8ef7969aba56f Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_ftp_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_ftw.png b/content/app/resources/default_100_percent/pdf_button_ftw.png new file mode 100644 index 0000000000000..9835b73bcb8f0 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_ftw.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_ftw_hover.png b/content/app/resources/default_100_percent/pdf_button_ftw_hover.png new file mode 100644 index 0000000000000..87b92054624a6 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_ftw_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_ftw_pressed.png b/content/app/resources/default_100_percent/pdf_button_ftw_pressed.png new file mode 100644 index 0000000000000..8bc51efb8a70b Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_ftw_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_print.png b/content/app/resources/default_100_percent/pdf_button_print.png new file mode 100644 index 0000000000000..412b3890aac3d Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_print.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_print_disabled.png b/content/app/resources/default_100_percent/pdf_button_print_disabled.png new file mode 100644 index 0000000000000..d38dc223a7e08 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_print_disabled.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_print_hover.png b/content/app/resources/default_100_percent/pdf_button_print_hover.png new file mode 100644 index 0000000000000..b75dc06fe8adb Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_print_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_print_pressed.png b/content/app/resources/default_100_percent/pdf_button_print_pressed.png new file mode 100644 index 0000000000000..443c9537bced2 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_print_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_save.png b/content/app/resources/default_100_percent/pdf_button_save.png new file mode 100644 index 0000000000000..bead7030a1a64 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_save.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_save_hover.png b/content/app/resources/default_100_percent/pdf_button_save_hover.png new file mode 100644 index 0000000000000..7dd185dc94f92 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_save_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_save_pressed.png b/content/app/resources/default_100_percent/pdf_button_save_pressed.png new file mode 100644 index 0000000000000..d2553f2640a50 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_save_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomin.png b/content/app/resources/default_100_percent/pdf_button_zoomin.png new file mode 100644 index 0000000000000..2d20de905e145 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomin.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomin_end.png b/content/app/resources/default_100_percent/pdf_button_zoomin_end.png new file mode 100644 index 0000000000000..cb84fc14f0734 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomin_end.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomin_end_hover.png b/content/app/resources/default_100_percent/pdf_button_zoomin_end_hover.png new file mode 100644 index 0000000000000..504d5086eb1ca Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomin_end_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomin_end_pressed.png b/content/app/resources/default_100_percent/pdf_button_zoomin_end_pressed.png new file mode 100644 index 0000000000000..a79c3f89b8e98 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomin_end_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomin_hover.png b/content/app/resources/default_100_percent/pdf_button_zoomin_hover.png new file mode 100644 index 0000000000000..048bdfcacdfc6 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomin_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomin_pressed.png b/content/app/resources/default_100_percent/pdf_button_zoomin_pressed.png new file mode 100644 index 0000000000000..a007c53cb41d9 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomin_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomout.png b/content/app/resources/default_100_percent/pdf_button_zoomout.png new file mode 100644 index 0000000000000..747bd563f6bbf Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomout.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomout_hover.png b/content/app/resources/default_100_percent/pdf_button_zoomout_hover.png new file mode 100644 index 0000000000000..6fba14175b60b Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomout_hover.png differ diff --git a/content/app/resources/default_100_percent/pdf_button_zoomout_pressed.png b/content/app/resources/default_100_percent/pdf_button_zoomout_pressed.png new file mode 100644 index 0000000000000..fe5d18d1631ab Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_button_zoomout_pressed.png differ diff --git a/content/app/resources/default_100_percent/pdf_dropshadow.png b/content/app/resources/default_100_percent/pdf_dropshadow.png new file mode 100644 index 0000000000000..d7400877e357b Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_dropshadow.png differ diff --git a/content/app/resources/default_100_percent/pdf_page_indicator_background.png b/content/app/resources/default_100_percent/pdf_page_indicator_background.png new file mode 100644 index 0000000000000..ba78d88b0f865 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_page_indicator_background.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_0.png b/content/app/resources/default_100_percent/pdf_progress_0.png new file mode 100644 index 0000000000000..00ea760499aea Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_0.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_1.png b/content/app/resources/default_100_percent/pdf_progress_1.png new file mode 100644 index 0000000000000..8f0e38e281fe5 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_1.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_2.png b/content/app/resources/default_100_percent/pdf_progress_2.png new file mode 100644 index 0000000000000..9f77742a39a7e Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_2.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_3.png b/content/app/resources/default_100_percent/pdf_progress_3.png new file mode 100644 index 0000000000000..546dee3539898 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_3.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_4.png b/content/app/resources/default_100_percent/pdf_progress_4.png new file mode 100644 index 0000000000000..6f6be8cf6d558 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_4.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_5.png b/content/app/resources/default_100_percent/pdf_progress_5.png new file mode 100644 index 0000000000000..176e27a039599 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_5.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_6.png b/content/app/resources/default_100_percent/pdf_progress_6.png new file mode 100644 index 0000000000000..ae9441b5141ca Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_6.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_7.png b/content/app/resources/default_100_percent/pdf_progress_7.png new file mode 100644 index 0000000000000..fcbb3163b2c3a Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_7.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_8.png b/content/app/resources/default_100_percent/pdf_progress_8.png new file mode 100644 index 0000000000000..efb86e783a26f Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_8.png differ diff --git a/content/app/resources/default_100_percent/pdf_progress_background.png b/content/app/resources/default_100_percent/pdf_progress_background.png new file mode 100644 index 0000000000000..927feea7b8f62 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_progress_background.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_0.png b/content/app/resources/default_100_percent/pdf_thumbnail_0.png new file mode 100644 index 0000000000000..0a8590aaf3526 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_0.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_1.png b/content/app/resources/default_100_percent/pdf_thumbnail_1.png new file mode 100644 index 0000000000000..30497d906a893 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_1.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_2.png b/content/app/resources/default_100_percent/pdf_thumbnail_2.png new file mode 100644 index 0000000000000..103d8996d9a46 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_2.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_3.png b/content/app/resources/default_100_percent/pdf_thumbnail_3.png new file mode 100644 index 0000000000000..b5aaf6203e7d9 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_3.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_4.png b/content/app/resources/default_100_percent/pdf_thumbnail_4.png new file mode 100644 index 0000000000000..eb52c77deece0 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_4.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_5.png b/content/app/resources/default_100_percent/pdf_thumbnail_5.png new file mode 100644 index 0000000000000..90c907c004563 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_5.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_6.png b/content/app/resources/default_100_percent/pdf_thumbnail_6.png new file mode 100644 index 0000000000000..ceeefb3c66a70 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_6.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_7.png b/content/app/resources/default_100_percent/pdf_thumbnail_7.png new file mode 100644 index 0000000000000..83eba5a9e4c04 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_7.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_8.png b/content/app/resources/default_100_percent/pdf_thumbnail_8.png new file mode 100644 index 0000000000000..9dd398e415a60 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_8.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_9.png b/content/app/resources/default_100_percent/pdf_thumbnail_9.png new file mode 100644 index 0000000000000..6b50290f55fd9 Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_9.png differ diff --git a/content/app/resources/default_100_percent/pdf_thumbnail_num_background.png b/content/app/resources/default_100_percent/pdf_thumbnail_num_background.png new file mode 100644 index 0000000000000..30c61921e4a9e Binary files /dev/null and b/content/app/resources/default_100_percent/pdf_thumbnail_num_background.png differ diff --git a/content/app/resources/default_100_percent/progress_cursor.png b/content/app/resources/default_100_percent/progress_cursor.png new file mode 100644 index 0000000000000..32ea1bef4e9ac Binary files /dev/null and b/content/app/resources/default_100_percent/progress_cursor.png differ diff --git a/content/app/resources/default_100_percent/search_cancel.png b/content/app/resources/default_100_percent/search_cancel.png new file mode 100644 index 0000000000000..1291bf84302b5 Binary files /dev/null and b/content/app/resources/default_100_percent/search_cancel.png differ diff --git a/content/app/resources/default_100_percent/search_cancel_pressed.png b/content/app/resources/default_100_percent/search_cancel_pressed.png new file mode 100644 index 0000000000000..4abc0fd4ac0d1 Binary files /dev/null and b/content/app/resources/default_100_percent/search_cancel_pressed.png differ diff --git a/content/app/resources/default_100_percent/search_magnifier.png b/content/app/resources/default_100_percent/search_magnifier.png new file mode 100644 index 0000000000000..0aea5daeded81 Binary files /dev/null and b/content/app/resources/default_100_percent/search_magnifier.png differ diff --git a/content/app/resources/default_100_percent/search_magnifier_results.png b/content/app/resources/default_100_percent/search_magnifier_results.png new file mode 100644 index 0000000000000..2f773c06943b1 Binary files /dev/null and b/content/app/resources/default_100_percent/search_magnifier_results.png differ diff --git a/content/app/resources/default_100_percent/solo.png b/content/app/resources/default_100_percent/solo.png new file mode 100644 index 0000000000000..e5d24f4decb7d Binary files /dev/null and b/content/app/resources/default_100_percent/solo.png differ diff --git a/content/app/resources/default_100_percent/south_east_resize_cursor.png b/content/app/resources/default_100_percent/south_east_resize_cursor.png new file mode 100644 index 0000000000000..cdd6f78012d2a Binary files /dev/null and b/content/app/resources/default_100_percent/south_east_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/south_resize_cursor.png b/content/app/resources/default_100_percent/south_resize_cursor.png new file mode 100644 index 0000000000000..a511157836c59 Binary files /dev/null and b/content/app/resources/default_100_percent/south_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/south_west_resize_cursor.png b/content/app/resources/default_100_percent/south_west_resize_cursor.png new file mode 100644 index 0000000000000..2b3bb0fcafe9d Binary files /dev/null and b/content/app/resources/default_100_percent/south_west_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/textarea_resize_corner.png b/content/app/resources/default_100_percent/textarea_resize_corner.png new file mode 100644 index 0000000000000..a379bdd305358 Binary files /dev/null and b/content/app/resources/default_100_percent/textarea_resize_corner.png differ diff --git a/content/app/resources/default_100_percent/vertical_text_cursor.png b/content/app/resources/default_100_percent/vertical_text_cursor.png new file mode 100644 index 0000000000000..dd60889eb2554 Binary files /dev/null and b/content/app/resources/default_100_percent/vertical_text_cursor.png differ diff --git a/content/app/resources/default_100_percent/wait_cursor.png b/content/app/resources/default_100_percent/wait_cursor.png new file mode 100644 index 0000000000000..e564fb14160c9 Binary files /dev/null and b/content/app/resources/default_100_percent/wait_cursor.png differ diff --git a/content/app/resources/default_100_percent/west_resize_cursor.png b/content/app/resources/default_100_percent/west_resize_cursor.png new file mode 100644 index 0000000000000..2d2dca9fd8b94 Binary files /dev/null and b/content/app/resources/default_100_percent/west_resize_cursor.png differ diff --git a/content/app/resources/default_100_percent/zoom_in_cursor.png b/content/app/resources/default_100_percent/zoom_in_cursor.png new file mode 100644 index 0000000000000..619f70a971025 Binary files /dev/null and b/content/app/resources/default_100_percent/zoom_in_cursor.png differ diff --git a/content/app/resources/default_100_percent/zoom_out_cursor.png b/content/app/resources/default_100_percent/zoom_out_cursor.png new file mode 100644 index 0000000000000..817dc8c16b94b Binary files /dev/null and b/content/app/resources/default_100_percent/zoom_out_cursor.png differ diff --git a/content/app/resources/default_200_percent/broken_image.png b/content/app/resources/default_200_percent/broken_image.png new file mode 100644 index 0000000000000..45edebcd5150d Binary files /dev/null and b/content/app/resources/default_200_percent/broken_image.png differ diff --git a/content/app/resources/default_200_percent/pan_icon.png b/content/app/resources/default_200_percent/pan_icon.png new file mode 100644 index 0000000000000..d9c0c1592ac9d Binary files /dev/null and b/content/app/resources/default_200_percent/pan_icon.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_ftp.png b/content/app/resources/default_200_percent/pdf_button_ftp.png new file mode 100644 index 0000000000000..c696924daf32c Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_ftp.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_ftp_hover.png b/content/app/resources/default_200_percent/pdf_button_ftp_hover.png new file mode 100644 index 0000000000000..5ddd00c1a3b57 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_ftp_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_ftp_pressed.png b/content/app/resources/default_200_percent/pdf_button_ftp_pressed.png new file mode 100644 index 0000000000000..bb5ee8c1c2d8e Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_ftp_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_ftw.png b/content/app/resources/default_200_percent/pdf_button_ftw.png new file mode 100644 index 0000000000000..b34746a507629 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_ftw.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_ftw_hover.png b/content/app/resources/default_200_percent/pdf_button_ftw_hover.png new file mode 100644 index 0000000000000..e006a62ce287a Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_ftw_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_ftw_pressed.png b/content/app/resources/default_200_percent/pdf_button_ftw_pressed.png new file mode 100644 index 0000000000000..386a18c631ce5 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_ftw_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_print.png b/content/app/resources/default_200_percent/pdf_button_print.png new file mode 100644 index 0000000000000..48a200b0b563e Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_print.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_print_disabled.png b/content/app/resources/default_200_percent/pdf_button_print_disabled.png new file mode 100644 index 0000000000000..4870ba8268b05 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_print_disabled.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_print_hover.png b/content/app/resources/default_200_percent/pdf_button_print_hover.png new file mode 100644 index 0000000000000..e3c0ecffadeb3 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_print_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_print_pressed.png b/content/app/resources/default_200_percent/pdf_button_print_pressed.png new file mode 100644 index 0000000000000..6a50f90100fe4 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_print_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_save.png b/content/app/resources/default_200_percent/pdf_button_save.png new file mode 100644 index 0000000000000..89d939dd54d5c Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_save.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_save_hover.png b/content/app/resources/default_200_percent/pdf_button_save_hover.png new file mode 100644 index 0000000000000..caed081124c89 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_save_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_save_pressed.png b/content/app/resources/default_200_percent/pdf_button_save_pressed.png new file mode 100644 index 0000000000000..7d403070db3e2 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_save_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomin.png b/content/app/resources/default_200_percent/pdf_button_zoomin.png new file mode 100644 index 0000000000000..896bdd8ea115e Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomin.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomin_end.png b/content/app/resources/default_200_percent/pdf_button_zoomin_end.png new file mode 100644 index 0000000000000..ce042ad545e9f Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomin_end.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomin_end_hover.png b/content/app/resources/default_200_percent/pdf_button_zoomin_end_hover.png new file mode 100644 index 0000000000000..d23b989303431 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomin_end_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomin_end_pressed.png b/content/app/resources/default_200_percent/pdf_button_zoomin_end_pressed.png new file mode 100644 index 0000000000000..a7be72603ecb5 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomin_end_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomin_hover.png b/content/app/resources/default_200_percent/pdf_button_zoomin_hover.png new file mode 100644 index 0000000000000..747fb6a65ac97 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomin_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomin_pressed.png b/content/app/resources/default_200_percent/pdf_button_zoomin_pressed.png new file mode 100644 index 0000000000000..b4678cf1da8a9 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomin_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomout.png b/content/app/resources/default_200_percent/pdf_button_zoomout.png new file mode 100644 index 0000000000000..473df97ee2e49 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomout.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomout_hover.png b/content/app/resources/default_200_percent/pdf_button_zoomout_hover.png new file mode 100644 index 0000000000000..e6c3c900f099c Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomout_hover.png differ diff --git a/content/app/resources/default_200_percent/pdf_button_zoomout_pressed.png b/content/app/resources/default_200_percent/pdf_button_zoomout_pressed.png new file mode 100644 index 0000000000000..0991b7efada7c Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_button_zoomout_pressed.png differ diff --git a/content/app/resources/default_200_percent/pdf_dropshadow.png b/content/app/resources/default_200_percent/pdf_dropshadow.png new file mode 100644 index 0000000000000..d3ffc3f321d66 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_dropshadow.png differ diff --git a/content/app/resources/default_200_percent/pdf_page_indicator_background.png b/content/app/resources/default_200_percent/pdf_page_indicator_background.png new file mode 100644 index 0000000000000..0c8d468abd9c2 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_page_indicator_background.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_0.png b/content/app/resources/default_200_percent/pdf_progress_0.png new file mode 100644 index 0000000000000..607fe5252811b Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_0.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_1.png b/content/app/resources/default_200_percent/pdf_progress_1.png new file mode 100644 index 0000000000000..9b974b270560c Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_1.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_2.png b/content/app/resources/default_200_percent/pdf_progress_2.png new file mode 100644 index 0000000000000..952ded3e66077 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_2.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_3.png b/content/app/resources/default_200_percent/pdf_progress_3.png new file mode 100644 index 0000000000000..0d4b2e72440e9 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_3.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_4.png b/content/app/resources/default_200_percent/pdf_progress_4.png new file mode 100644 index 0000000000000..bd3c4df8c860d Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_4.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_5.png b/content/app/resources/default_200_percent/pdf_progress_5.png new file mode 100644 index 0000000000000..28781916bd463 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_5.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_6.png b/content/app/resources/default_200_percent/pdf_progress_6.png new file mode 100644 index 0000000000000..33f16c8a113fe Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_6.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_7.png b/content/app/resources/default_200_percent/pdf_progress_7.png new file mode 100644 index 0000000000000..6c014baa9d21b Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_7.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_8.png b/content/app/resources/default_200_percent/pdf_progress_8.png new file mode 100644 index 0000000000000..36847c30e7404 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_8.png differ diff --git a/content/app/resources/default_200_percent/pdf_progress_background.png b/content/app/resources/default_200_percent/pdf_progress_background.png new file mode 100644 index 0000000000000..5c7c1083f49a0 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_progress_background.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_0.png b/content/app/resources/default_200_percent/pdf_thumbnail_0.png new file mode 100644 index 0000000000000..461040082a5fd Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_0.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_1.png b/content/app/resources/default_200_percent/pdf_thumbnail_1.png new file mode 100644 index 0000000000000..5f6a6f4ddc714 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_1.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_2.png b/content/app/resources/default_200_percent/pdf_thumbnail_2.png new file mode 100644 index 0000000000000..938c64dc415ce Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_2.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_3.png b/content/app/resources/default_200_percent/pdf_thumbnail_3.png new file mode 100644 index 0000000000000..eb1f0b918f616 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_3.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_4.png b/content/app/resources/default_200_percent/pdf_thumbnail_4.png new file mode 100644 index 0000000000000..7add17b8f7dbb Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_4.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_5.png b/content/app/resources/default_200_percent/pdf_thumbnail_5.png new file mode 100644 index 0000000000000..fe6b537328bf2 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_5.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_6.png b/content/app/resources/default_200_percent/pdf_thumbnail_6.png new file mode 100644 index 0000000000000..393fc40ba10ca Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_6.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_7.png b/content/app/resources/default_200_percent/pdf_thumbnail_7.png new file mode 100644 index 0000000000000..84312a1937de3 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_7.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_8.png b/content/app/resources/default_200_percent/pdf_thumbnail_8.png new file mode 100644 index 0000000000000..c1b7875d0c98e Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_8.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_9.png b/content/app/resources/default_200_percent/pdf_thumbnail_9.png new file mode 100644 index 0000000000000..f133eb937447f Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_9.png differ diff --git a/content/app/resources/default_200_percent/pdf_thumbnail_num_background.png b/content/app/resources/default_200_percent/pdf_thumbnail_num_background.png new file mode 100644 index 0000000000000..45c23d1b31490 Binary files /dev/null and b/content/app/resources/default_200_percent/pdf_thumbnail_num_background.png differ diff --git a/content/app/resources/default_200_percent/textarea_resize_corner.png b/content/app/resources/default_200_percent/textarea_resize_corner.png new file mode 100644 index 0000000000000..77fa3cac8ba0d Binary files /dev/null and b/content/app/resources/default_200_percent/textarea_resize_corner.png differ diff --git a/content/app/startup_helper_win.cc b/content/app/startup_helper_win.cc index 17fd323397fce..ab85a43e80689 100644 --- a/content/app/startup_helper_win.cc +++ b/content/app/startup_helper_win.cc @@ -56,7 +56,7 @@ void RegisterInvalidParamHandler() { _set_new_mode(1); } -void SetupCRT(const CommandLine& command_line) { +void SetupCRT(const base::CommandLine& command_line) { #if defined(_CRTDBG_MAP_ALLOC) _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); diff --git a/content/app/strings/BUILD.gn b/content/app/strings/BUILD.gn new file mode 100644 index 0000000000000..c0dafd09c5920 --- /dev/null +++ b/content/app/strings/BUILD.gn @@ -0,0 +1,69 @@ +# 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("//tools/grit/grit_rule.gni") + +# GYP version: content/app/strings/content_strings.gyp:content_strings +grit("strings") { + use_qualified_include = true + source = "content_strings.grd" + outputs = [ + "grit/content_strings.h", + "content_strings_en-US.rc", + "content_strings_am.pak", + "content_strings_ar.pak", + "content_strings_bg.pak", + "content_strings_bn.pak", + "content_strings_ca.pak", + "content_strings_cs.pak", + "content_strings_da.pak", + "content_strings_de.pak", + "content_strings_el.pak", + "content_strings_en-GB.pak", + "content_strings_en-US.pak", + "content_strings_es.pak", + "content_strings_es-419.pak", + "content_strings_et.pak", + "content_strings_fa.pak", + "content_strings_fake-bidi.pak", + "content_strings_fi.pak", + "content_strings_fil.pak", + "content_strings_fr.pak", + "content_strings_gu.pak", + "content_strings_he.pak", + "content_strings_hi.pak", + "content_strings_hr.pak", + "content_strings_hu.pak", + "content_strings_id.pak", + "content_strings_it.pak", + "content_strings_ja.pak", + "content_strings_kn.pak", + "content_strings_ko.pak", + "content_strings_lt.pak", + "content_strings_lv.pak", + "content_strings_ml.pak", + "content_strings_mr.pak", + "content_strings_ms.pak", + "content_strings_nl.pak", + "content_strings_nb.pak", + "content_strings_pl.pak", + "content_strings_pt-BR.pak", + "content_strings_pt-PT.pak", + "content_strings_ro.pak", + "content_strings_ru.pak", + "content_strings_sk.pak", + "content_strings_sl.pak", + "content_strings_sr.pak", + "content_strings_sv.pak", + "content_strings_sw.pak", + "content_strings_ta.pak", + "content_strings_te.pak", + "content_strings_th.pak", + "content_strings_tr.pak", + "content_strings_uk.pak", + "content_strings_vi.pak", + "content_strings_zh-CN.pak", + "content_strings_zh-TW.pak", + ] +} diff --git a/content/app/strings/content_strings.grd b/content/app/strings/content_strings.grd new file mode 100644 index 0000000000000..3118ba2d56122 --- /dev/null +++ b/content/app/strings/content_strings.grd @@ -0,0 +1,690 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Details + + + This is a searchable index. Enter search keywords: ''' + + + Clear + + + Today + + + Day + + + Month + + + Year + + + Submit + + + Submit + + + Reset + + + Choose File + + + Choose Files + + + No file chosen + + + $13 files + + + Other... + + + Other... + + + Other... + + + Other... + + + Other... + + + dd + + + mm + + + yyyy + + + $13 selected + + + This month + + + This week + + + Week + + + + No recent searches + + + Recent Searches + + + Clear Recent Searches + + + + HTML content + + + link + + + list marker + + + image map + + + heading + + + + footer + + + stepper + + + toggle button + + + + + press + + + select + + + activate + + + uncheck + + + check + + + jump + + + + AM/PM + + + + Day + + + + blank + + + + Hours + + + + media control + + + + audio + + + + video + + + + mute + + + + unmute + + + + play + + + + pause + + + + movie time + + + + movie timeline thumb + + + + elapsed time + + + + remaining time + + + + status + + + + enter full screen + + + + exit full screen + + + + show closed captions + + + + hide closed captions + + + + audio + + + + video + + + + mute audio track + + + + unmute audio track + + + + begin playback + + + + pause playback + + + + movie time scrubber + + + + movie time scrubber thumb + + + + current time in seconds + + + + number of seconds ofmovie remaining + + + + current movie status + + + + play movie in full screen mode + + + + exit full screen + + + + start displaying closed captions + + + + stop displaying closed captions + + + + Milliseconds + + + + Minutes + + + + Month + + + + Seconds + + + + Week + + + + Year + + + + 2048 (High Grade) + + + 1024 (Medium Grade) + + + + Week $251, $12012 + + + Please select one or more files. + + + Invalid value. + + + Please enter a non-empty email address. + + + Please enter a part following '$1@'. '$2user@' is incomplete. + + + Please enter a part followed by '$1@'. '$2@example.com' is incomplete. + + + A part following '$1@' should not contain the symbol '$2,'. + + + '$1.' is used at a wrong position in '$2example..com'. + + + A part followed by '$1@' should not contain the symbol '$2,'. + + + Please include an '$1@' in the email address. '$2user' is missing an '$1@'. + + + Please enter a comma separated list of email addresses. + + + Value must be greater than or equal to $10. + + + Value must be $101/04/2013 or later. + + + Value must be less than or equal to $1100. + + + Value must be $112/31/2013 or earlier. + + + Please enter a valid value. The field is incomplete or has an invalid date. + + + Please enter a number. + + + + + Please fill out this field. + + + Please check this box if you want to proceed. + + + Please select a file. + + + Please select one of these options. + + + Please select an item in the list. + + + Please enter an email address. + + + Please enter a URL. + + + Please match the requested format. + + + Please enter a valid value. The two nearest valid values are $14 and $28. + + + Please enter a valid value. The nearest valid value is $10. + + + Please shorten this text to $2100 characters or less (you are currently using $1101 characters). + + + + This document is password protected. Please enter a password. + + + + Loading... + + + + Failed to load PDF document + + + + Loading + + + + Couldn't load plug-in. + + + + + diff --git a/content/app/strings/content_strings.gyp b/content/app/strings/content_strings.gyp new file mode 100644 index 0000000000000..e8f431018f20d --- /dev/null +++ b/content/app/strings/content_strings.gyp @@ -0,0 +1,30 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + # GN version: //content/app/strings + 'target_name': 'content_strings', + 'type': 'none', + 'variables': { + 'grit_out_dir': '<(SHARED_INTERMEDIATE_DIR)/content/app/strings', + }, + 'actions': [ + { + 'action_name': 'generate_content_strings', + 'variables': { + 'grit_grd_file': 'content_strings.grd', + }, + 'includes': [ '../../../build/grit_action.gypi' ], + }, + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + }, + }, + ] +} diff --git a/webkit/glue/resources/webkit_strings_am.xtb b/content/app/strings/translations/content_strings_am.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_am.xtb rename to content/app/strings/translations/content_strings_am.xtb diff --git a/webkit/glue/resources/webkit_strings_ar.xtb b/content/app/strings/translations/content_strings_ar.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ar.xtb rename to content/app/strings/translations/content_strings_ar.xtb diff --git a/webkit/glue/resources/webkit_strings_bg.xtb b/content/app/strings/translations/content_strings_bg.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_bg.xtb rename to content/app/strings/translations/content_strings_bg.xtb diff --git a/webkit/glue/resources/webkit_strings_bn.xtb b/content/app/strings/translations/content_strings_bn.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_bn.xtb rename to content/app/strings/translations/content_strings_bn.xtb diff --git a/webkit/glue/resources/webkit_strings_ca.xtb b/content/app/strings/translations/content_strings_ca.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ca.xtb rename to content/app/strings/translations/content_strings_ca.xtb diff --git a/webkit/glue/resources/webkit_strings_cs.xtb b/content/app/strings/translations/content_strings_cs.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_cs.xtb rename to content/app/strings/translations/content_strings_cs.xtb diff --git a/webkit/glue/resources/webkit_strings_da.xtb b/content/app/strings/translations/content_strings_da.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_da.xtb rename to content/app/strings/translations/content_strings_da.xtb diff --git a/webkit/glue/resources/webkit_strings_de.xtb b/content/app/strings/translations/content_strings_de.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_de.xtb rename to content/app/strings/translations/content_strings_de.xtb diff --git a/webkit/glue/resources/webkit_strings_el.xtb b/content/app/strings/translations/content_strings_el.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_el.xtb rename to content/app/strings/translations/content_strings_el.xtb diff --git a/webkit/glue/resources/webkit_strings_en-GB.xtb b/content/app/strings/translations/content_strings_en-GB.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_en-GB.xtb rename to content/app/strings/translations/content_strings_en-GB.xtb diff --git a/webkit/glue/resources/webkit_strings_es-419.xtb b/content/app/strings/translations/content_strings_es-419.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_es-419.xtb rename to content/app/strings/translations/content_strings_es-419.xtb diff --git a/webkit/glue/resources/webkit_strings_es.xtb b/content/app/strings/translations/content_strings_es.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_es.xtb rename to content/app/strings/translations/content_strings_es.xtb diff --git a/webkit/glue/resources/webkit_strings_et.xtb b/content/app/strings/translations/content_strings_et.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_et.xtb rename to content/app/strings/translations/content_strings_et.xtb diff --git a/webkit/glue/resources/webkit_strings_fa.xtb b/content/app/strings/translations/content_strings_fa.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_fa.xtb rename to content/app/strings/translations/content_strings_fa.xtb diff --git a/webkit/glue/resources/webkit_strings_fi.xtb b/content/app/strings/translations/content_strings_fi.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_fi.xtb rename to content/app/strings/translations/content_strings_fi.xtb diff --git a/webkit/glue/resources/webkit_strings_fil.xtb b/content/app/strings/translations/content_strings_fil.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_fil.xtb rename to content/app/strings/translations/content_strings_fil.xtb diff --git a/webkit/glue/resources/webkit_strings_fr.xtb b/content/app/strings/translations/content_strings_fr.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_fr.xtb rename to content/app/strings/translations/content_strings_fr.xtb diff --git a/webkit/glue/resources/webkit_strings_gu.xtb b/content/app/strings/translations/content_strings_gu.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_gu.xtb rename to content/app/strings/translations/content_strings_gu.xtb diff --git a/webkit/glue/resources/webkit_strings_hi.xtb b/content/app/strings/translations/content_strings_hi.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_hi.xtb rename to content/app/strings/translations/content_strings_hi.xtb diff --git a/webkit/glue/resources/webkit_strings_hr.xtb b/content/app/strings/translations/content_strings_hr.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_hr.xtb rename to content/app/strings/translations/content_strings_hr.xtb diff --git a/webkit/glue/resources/webkit_strings_hu.xtb b/content/app/strings/translations/content_strings_hu.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_hu.xtb rename to content/app/strings/translations/content_strings_hu.xtb diff --git a/webkit/glue/resources/webkit_strings_id.xtb b/content/app/strings/translations/content_strings_id.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_id.xtb rename to content/app/strings/translations/content_strings_id.xtb diff --git a/webkit/glue/resources/webkit_strings_it.xtb b/content/app/strings/translations/content_strings_it.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_it.xtb rename to content/app/strings/translations/content_strings_it.xtb diff --git a/webkit/glue/resources/webkit_strings_iw.xtb b/content/app/strings/translations/content_strings_iw.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_iw.xtb rename to content/app/strings/translations/content_strings_iw.xtb diff --git a/webkit/glue/resources/webkit_strings_ja.xtb b/content/app/strings/translations/content_strings_ja.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ja.xtb rename to content/app/strings/translations/content_strings_ja.xtb diff --git a/webkit/glue/resources/webkit_strings_kn.xtb b/content/app/strings/translations/content_strings_kn.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_kn.xtb rename to content/app/strings/translations/content_strings_kn.xtb diff --git a/webkit/glue/resources/webkit_strings_ko.xtb b/content/app/strings/translations/content_strings_ko.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ko.xtb rename to content/app/strings/translations/content_strings_ko.xtb diff --git a/webkit/glue/resources/webkit_strings_lt.xtb b/content/app/strings/translations/content_strings_lt.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_lt.xtb rename to content/app/strings/translations/content_strings_lt.xtb diff --git a/webkit/glue/resources/webkit_strings_lv.xtb b/content/app/strings/translations/content_strings_lv.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_lv.xtb rename to content/app/strings/translations/content_strings_lv.xtb diff --git a/webkit/glue/resources/webkit_strings_ml.xtb b/content/app/strings/translations/content_strings_ml.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ml.xtb rename to content/app/strings/translations/content_strings_ml.xtb diff --git a/webkit/glue/resources/webkit_strings_mr.xtb b/content/app/strings/translations/content_strings_mr.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_mr.xtb rename to content/app/strings/translations/content_strings_mr.xtb diff --git a/webkit/glue/resources/webkit_strings_ms.xtb b/content/app/strings/translations/content_strings_ms.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ms.xtb rename to content/app/strings/translations/content_strings_ms.xtb diff --git a/webkit/glue/resources/webkit_strings_nl.xtb b/content/app/strings/translations/content_strings_nl.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_nl.xtb rename to content/app/strings/translations/content_strings_nl.xtb diff --git a/webkit/glue/resources/webkit_strings_no.xtb b/content/app/strings/translations/content_strings_no.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_no.xtb rename to content/app/strings/translations/content_strings_no.xtb diff --git a/webkit/glue/resources/webkit_strings_pl.xtb b/content/app/strings/translations/content_strings_pl.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_pl.xtb rename to content/app/strings/translations/content_strings_pl.xtb diff --git a/webkit/glue/resources/webkit_strings_pt-BR.xtb b/content/app/strings/translations/content_strings_pt-BR.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_pt-BR.xtb rename to content/app/strings/translations/content_strings_pt-BR.xtb diff --git a/webkit/glue/resources/webkit_strings_pt-PT.xtb b/content/app/strings/translations/content_strings_pt-PT.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_pt-PT.xtb rename to content/app/strings/translations/content_strings_pt-PT.xtb diff --git a/webkit/glue/resources/webkit_strings_ro.xtb b/content/app/strings/translations/content_strings_ro.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ro.xtb rename to content/app/strings/translations/content_strings_ro.xtb diff --git a/webkit/glue/resources/webkit_strings_ru.xtb b/content/app/strings/translations/content_strings_ru.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ru.xtb rename to content/app/strings/translations/content_strings_ru.xtb diff --git a/webkit/glue/resources/webkit_strings_sk.xtb b/content/app/strings/translations/content_strings_sk.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_sk.xtb rename to content/app/strings/translations/content_strings_sk.xtb diff --git a/webkit/glue/resources/webkit_strings_sl.xtb b/content/app/strings/translations/content_strings_sl.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_sl.xtb rename to content/app/strings/translations/content_strings_sl.xtb diff --git a/webkit/glue/resources/webkit_strings_sr.xtb b/content/app/strings/translations/content_strings_sr.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_sr.xtb rename to content/app/strings/translations/content_strings_sr.xtb diff --git a/webkit/glue/resources/webkit_strings_sv.xtb b/content/app/strings/translations/content_strings_sv.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_sv.xtb rename to content/app/strings/translations/content_strings_sv.xtb diff --git a/webkit/glue/resources/webkit_strings_sw.xtb b/content/app/strings/translations/content_strings_sw.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_sw.xtb rename to content/app/strings/translations/content_strings_sw.xtb diff --git a/webkit/glue/resources/webkit_strings_ta.xtb b/content/app/strings/translations/content_strings_ta.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_ta.xtb rename to content/app/strings/translations/content_strings_ta.xtb diff --git a/webkit/glue/resources/webkit_strings_te.xtb b/content/app/strings/translations/content_strings_te.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_te.xtb rename to content/app/strings/translations/content_strings_te.xtb diff --git a/webkit/glue/resources/webkit_strings_th.xtb b/content/app/strings/translations/content_strings_th.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_th.xtb rename to content/app/strings/translations/content_strings_th.xtb diff --git a/webkit/glue/resources/webkit_strings_tr.xtb b/content/app/strings/translations/content_strings_tr.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_tr.xtb rename to content/app/strings/translations/content_strings_tr.xtb diff --git a/webkit/glue/resources/webkit_strings_uk.xtb b/content/app/strings/translations/content_strings_uk.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_uk.xtb rename to content/app/strings/translations/content_strings_uk.xtb diff --git a/webkit/glue/resources/webkit_strings_vi.xtb b/content/app/strings/translations/content_strings_vi.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_vi.xtb rename to content/app/strings/translations/content_strings_vi.xtb diff --git a/webkit/glue/resources/webkit_strings_zh-CN.xtb b/content/app/strings/translations/content_strings_zh-CN.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_zh-CN.xtb rename to content/app/strings/translations/content_strings_zh-CN.xtb diff --git a/webkit/glue/resources/webkit_strings_zh-TW.xtb b/content/app/strings/translations/content_strings_zh-TW.xtb similarity index 100% rename from webkit/glue/resources/webkit_strings_zh-TW.xtb rename to content/app/strings/translations/content_strings_zh-TW.xtb diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn index 76fb1147bcd96..b52a66695cf40 100644 --- a/content/browser/BUILD.gn +++ b/content/browser/BUILD.gn @@ -23,8 +23,9 @@ source_set("browser") { # Shared deps. See also non-iOS deps below. deps = [ "//base", + "//base:base_static", "//content:resources", - "//content/browser/service_worker:database_proto", + "//content/browser/service_worker:proto", "//content/browser/speech/proto", "//crypto", "//google_apis", @@ -87,25 +88,26 @@ source_set("browser") { # Non-iOS deps. deps += [ - "//content/browser/devtools:resources", - "//content/common:mojo_bindings", "//cc", "//cc/surfaces", + "//content/app/strings", + "//content/browser/devtools:resources", + "//content/common:mojo_bindings", "//mojo/public/cpp/bindings", "//mojo/public/interfaces/application", "//mojo/public/js/bindings", "//net:http_server", + "//third_party/WebKit/public:resources", + "//third_party/angle:commit_id", "//third_party/icu", "//third_party/leveldatabase", "//third_party/libyuv", "//ui/resources", "//ui/surface", - "//webkit:resources", - "//webkit:strings", "//webkit/browser:storage", "//webkit/common", "//webkit/common:storage", - "//third_party/angle:commit_id", + "//webkit/glue/resources", ] } @@ -378,16 +380,16 @@ source_set("browser") { "compositor/software_output_device_x11.h", ] } - deps += [ "//ui/compositor" ] - } - if (!use_ozone) { - sources -= [ - "compositor/overlay_candidate_validator_ozone.cc", - "compositor/overlay_candidate_validator_ozone.h", - "compositor/software_output_device_ozone.cc", - "compositor/software_output_device_ozone.h", - ] + if (!use_ozone) { + sources -= [ + "compositor/overlay_candidate_validator_ozone.cc", + "compositor/overlay_candidate_validator_ozone.h", + "compositor/software_output_device_ozone.cc", + "compositor/software_output_device_ozone.h", + ] + } + deps += [ "//ui/compositor" ] } if (enable_web_speech) { @@ -400,12 +402,16 @@ source_set("browser") { if (is_linux) { if (use_dbus) { sources -= [ + "battery_status/battery_status_manager_default.cc", "geolocation/empty_wifi_data_provider.cc", ] deps += [ "//dbus" ] } else { # This will already have gotten removed for all non-Linux cases. - sources -= [ "geolocation/wifi_data_provider_linux.cc" ] + sources -= [ + "battery_status/battery_status_manager_linux.cc", + "geolocation/wifi_data_provider_linux.cc", + ] } } } diff --git a/content/browser/DEPS b/content/browser/DEPS index 8474946ae138c..63d9c41d24088 100644 --- a/content/browser/DEPS +++ b/content/browser/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+content/app/strings/grit", # For generated headers "+content/public/browser", "+media/audio", # For audio input for speech input feature. "+media/base", # For Android JNI registration. @@ -44,6 +45,7 @@ include_rules = [ "+third_party/WebKit/public/platform/WebScreenOrientationLockType.h", "+third_party/WebKit/public/platform/WebScreenOrientationType.h", "+third_party/WebKit/public/platform/WebScreenInfo.h", + "+third_party/WebKit/public/platform/WebServiceWorkerCacheError.h", "+third_party/WebKit/public/platform/WebServiceWorkerError.h", "+third_party/WebKit/public/platform/WebServiceWorkerEventResult.h", "+third_party/WebKit/public/platform/WebServiceWorkerState.h", @@ -65,6 +67,7 @@ include_rules = [ "+third_party/WebKit/public/web/WebPopupType.h", "+third_party/WebKit/public/web/WebSerializedScriptValueVersion.h", "+third_party/WebKit/public/web/WebTextDirection.h", + "+third_party/WebKit/public/web/WebTextInputType.h", # These should be burned down. http://crbug.com/237267 "!third_party/WebKit/public/web/mac/WebInputEventFactory.h", diff --git a/content/browser/accessibility/accessibility_win_browsertest.cc b/content/browser/accessibility/accessibility_win_browsertest.cc index a11c026f8ad3f..6e8ba536de059 100644 --- a/content/browser/accessibility/accessibility_win_browsertest.cc +++ b/content/browser/accessibility/accessibility_win_browsertest.cc @@ -640,8 +640,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, // If you made a change and this test now fails, check that the NativeViewHost // that wraps the tab contents returns the IAccessible implementation // provided by RenderWidgetHostViewWin in GetNativeViewAccessible(). +// flaky: http://crbug.com/402190 IN_PROC_BROWSER_TEST_F(AccessibilityWinBrowserTest, - ContainsRendererAccessibilityTree) { + DISABLED_ContainsRendererAccessibilityTree) { LoadInitialAccessibilityTreeFromHtml( "MyDocument" "Content"); diff --git a/content/browser/accessibility/browser_accessibility_cocoa.mm b/content/browser/accessibility/browser_accessibility_cocoa.mm index c016a032f5725..5086982a90f46 100644 --- a/content/browser/accessibility/browser_accessibility_cocoa.mm +++ b/content/browser/accessibility/browser_accessibility_cocoa.mm @@ -12,10 +12,10 @@ #include "base/strings/string16.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "content/app/strings/grit/content_strings.h" #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_manager_mac.h" #include "content/public/common/content_client.h" -#include "grit/webkit_strings.h" #import "ui/accessibility/platform/ax_platform_node_mac.h" // See http://openradar.appspot.com/9896491. This SPI has been tested on 10.5, diff --git a/content/browser/accessibility/browser_accessibility_state_impl.cc b/content/browser/accessibility/browser_accessibility_state_impl.cc index 7fdab1a07998a..1e3063e19de30 100644 --- a/content/browser/accessibility/browser_accessibility_state_impl.cc +++ b/content/browser/accessibility/browser_accessibility_state_impl.cc @@ -62,7 +62,7 @@ BrowserAccessibilityStateImpl::~BrowserAccessibilityStateImpl() { } void BrowserAccessibilityStateImpl::OnScreenReaderDetected() { - if (CommandLine::ForCurrentProcess()->HasSwitch( + if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableRendererAccessibility)) { return; } @@ -83,13 +83,13 @@ void BrowserAccessibilityStateImpl::ResetAccessibilityModeValue() { // On Windows 8, always enable accessibility for editable text controls // so we can show the virtual keyboard when one is enabled. if (base::win::GetVersion() >= base::win::VERSION_WIN8 && - !CommandLine::ForCurrentProcess()->HasSwitch( + !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableRendererAccessibility)) { accessibility_mode_ = AccessibilityModeEditableTextOnly; } #endif // defined(OS_WIN) - if (CommandLine::ForCurrentProcess()->HasSwitch( + if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kForceRendererAccessibility)) { accessibility_mode_ = AccessibilityModeComplete; } @@ -128,7 +128,7 @@ void BrowserAccessibilityStateImpl::UpdateHistograms() { UMA_HISTOGRAM_BOOLEAN("Accessibility.InvertedColors", gfx::IsInvertedColorScheme()); UMA_HISTOGRAM_BOOLEAN("Accessibility.ManuallyEnabled", - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kForceRendererAccessibility)); } @@ -139,7 +139,7 @@ void BrowserAccessibilityStateImpl::UpdatePlatformSpecificHistograms() { void BrowserAccessibilityStateImpl::AddAccessibilityMode( AccessibilityMode mode) { - if (CommandLine::ForCurrentProcess()->HasSwitch( + if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableRendererAccessibility)) { return; } @@ -152,7 +152,7 @@ void BrowserAccessibilityStateImpl::AddAccessibilityMode( void BrowserAccessibilityStateImpl::RemoveAccessibilityMode( AccessibilityMode mode) { - if (CommandLine::ForCurrentProcess()->HasSwitch( + if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kForceRendererAccessibility) && mode == AccessibilityModeComplete) { return; diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc index 4b461be793f76..493900461af3e 100644 --- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc +++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc @@ -146,10 +146,10 @@ class DumpAccessibilityTreeTest : public ContentBrowserTest { } } - virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { ContentBrowserTest::SetUpCommandLine(command_line); // Enable , which is used in some tests. - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalWebPlatformFeatures); } diff --git a/content/browser/android/child_process_launcher_android.cc b/content/browser/android/child_process_launcher_android.cc index baac2b0a715c7..0e42fc49e9a4e 100644 --- a/content/browser/android/child_process_launcher_android.cc +++ b/content/browser/android/child_process_launcher_android.cc @@ -100,7 +100,7 @@ static void OnChildProcessStarted(JNIEnv*, } void StartChildProcess( - const CommandLine::StringVector& argv, + const base::CommandLine::StringVector& argv, int child_process_id, const std::vector& files_to_register, const StartChildProcessCallback& callback) { @@ -211,7 +211,8 @@ void UnregisterChildProcessSurfaceTexture(int surface_texture_id, } jboolean IsSingleProcess(JNIEnv* env, jclass clazz) { - return CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess); } bool RegisterChildProcessLauncher(JNIEnv* env) { diff --git a/content/browser/android/content_settings.cc b/content/browser/android/content_settings.cc index 7ab181b30ad4e..2d32143807a9e 100644 --- a/content/browser/android/content_settings.cc +++ b/content/browser/android/content_settings.cc @@ -39,7 +39,7 @@ bool ContentSettings::GetJavaScriptEnabled(JNIEnv* env, jobject obj) { RenderViewHost* render_view_host = web_contents()->GetRenderViewHost(); if (!render_view_host) return false; - return render_view_host->GetDelegate()->GetWebkitPrefs().javascript_enabled; + return render_view_host->GetWebkitPreferences().javascript_enabled; } void ContentSettings::WebContentsDestroyed() { diff --git a/content/browser/android/content_startup_flags.cc b/content/browser/android/content_startup_flags.cc index d315371ca4ba8..ca3141f0055ce 100644 --- a/content/browser/android/content_startup_flags.cc +++ b/content/browser/android/content_startup_flags.cc @@ -26,7 +26,8 @@ void SetContentCommandLineFlags(bool single_process, return; already_initialized = true; - CommandLine* parsed_command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* parsed_command_line = + base::CommandLine::ForCurrentProcess(); int command_line_renderer_limit = -1; if (parsed_command_line->HasSwitch(switches::kRendererProcessLimit)) { diff --git a/content/browser/android/content_video_view.cc b/content/browser/android/content_video_view.cc index 5407032441b88..9e0a830bae958 100644 --- a/content/browser/android/content_video_view.cc +++ b/content/browser/android/content_video_view.cc @@ -211,7 +211,7 @@ gfx::NativeView ContentVideoView::GetNativeView() { JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject() { ContentViewCoreImpl* content_view_core = manager_->GetContentViewCore(); JNIEnv* env = AttachCurrentThread(); - bool legacyMode = CommandLine::ForCurrentProcess()->HasSwitch( + bool legacyMode = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableOverlayFullscreenVideoSubtitle); return JavaObjectWeakGlobalRef( env, @@ -224,7 +224,7 @@ JavaObjectWeakGlobalRef ContentVideoView::CreateJavaObject() { } void ContentVideoView::CreatePowerSaveBlocker() { - if (!CommandLine::ForCurrentProcess()->HasSwitch( + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableOverlayFullscreenVideoSubtitle)) { return; } diff --git a/content/browser/android/content_view_core_impl.cc b/content/browser/android/content_view_core_impl.cc index 2ce8774f4bba1..c84e2b2dae42c 100644 --- a/content/browser/android/content_view_core_impl.cc +++ b/content/browser/android/content_view_core_impl.cc @@ -9,7 +9,6 @@ #include "base/android/jni_string.h" #include "base/android/scoped_java_ref.h" #include "base/command_line.h" -#include "base/json/json_writer.h" #include "base/logging.h" #include "base/metrics/histogram.h" #include "base/strings/utf_string_conversions.h" @@ -36,7 +35,6 @@ #include "content/browser/renderer_host/render_widget_host_impl.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" #include "content/browser/screen_orientation/screen_orientation_dispatcher_host.h" -#include "content/browser/ssl/ssl_host_state.h" #include "content/browser/transition_request_manager.h" #include "content/browser/web_contents/web_contents_view_android.h" #include "content/common/frame_messages.h" @@ -47,6 +45,7 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/favicon_status.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/ssl_host_state_delegate.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" @@ -67,7 +66,6 @@ using base::android::ConvertJavaStringToUTF16; using base::android::ConvertJavaStringToUTF8; using base::android::ConvertUTF16ToJavaString; using base::android::ConvertUTF8ToJavaString; -using base::android::ScopedJavaGlobalRef; using base::android::ScopedJavaLocalRef; using blink::WebGestureEvent; using blink::WebInputEvent; @@ -622,9 +620,9 @@ void ContentViewCoreImpl::ShowPastePopup(int x_dip, int y_dip) { ScopedJavaLocalRef obj = java_ref_.get(env); if (obj.is_null()) return; - Java_ContentViewCore_showPastePopup(env, obj.obj(), - static_cast(x_dip), - static_cast(y_dip)); + Java_ContentViewCore_showPastePopupWithFeedback(env, obj.obj(), + static_cast(x_dip), + static_cast(y_dip)); } void ContentViewCoreImpl::GetScaledContentBitmap( @@ -825,7 +823,8 @@ scoped_refptr ContentViewCoreImpl::GetLayer() const { // Methods called from Java via JNI // ---------------------------------------------------------------------------- -void ContentViewCoreImpl::SelectPopupMenuItems(JNIEnv* env, jobject obj, +void ContentViewCoreImpl::SelectPopupMenuItems(JNIEnv* env, + jobject obj, jintArray indices) { RenderViewHostImpl* rvhi = static_cast( web_contents_->GetRenderViewHost()); @@ -901,15 +900,6 @@ void ContentViewCoreImpl::LoadUrl( LoadUrl(params); } -ScopedJavaLocalRef ContentViewCoreImpl::GetURL( - JNIEnv* env, jobject) const { - return ConvertUTF8ToJavaString(env, GetWebContents()->GetURL().spec()); -} - -jboolean ContentViewCoreImpl::IsIncognito(JNIEnv* env, jobject obj) { - return GetWebContents()->GetBrowserContext()->IsOffTheRecord(); -} - WebContents* ContentViewCoreImpl::GetWebContents() const { return web_contents_; } @@ -1317,53 +1307,6 @@ long ContentViewCoreImpl::GetNativeImeAdapter(JNIEnv* env, jobject obj) { return rwhva->GetNativeImeAdapter(); } -namespace { -void JavaScriptResultCallback(const ScopedJavaGlobalRef& callback, - const base::Value* result) { - JNIEnv* env = base::android::AttachCurrentThread(); - std::string json; - base::JSONWriter::Write(result, &json); - ScopedJavaLocalRef j_json = ConvertUTF8ToJavaString(env, json); - Java_ContentViewCore_onEvaluateJavaScriptResult(env, - j_json.obj(), - callback.obj()); -} -} // namespace - -void ContentViewCoreImpl::EvaluateJavaScript(JNIEnv* env, - jobject obj, - jstring script, - jobject callback, - jboolean start_renderer) { - RenderViewHost* rvh = web_contents_->GetRenderViewHost(); - DCHECK(rvh); - - if (start_renderer && !rvh->IsRenderViewLive()) { - if (!web_contents_->CreateRenderViewForInitialEmptyDocument()) { - LOG(ERROR) << "Failed to create RenderView in EvaluateJavaScript"; - return; - } - } - - if (!callback) { - // No callback requested. - web_contents_->GetMainFrame()->ExecuteJavaScript( - ConvertJavaStringToUTF16(env, script)); - return; - } - - // Secure the Java callback in a scoped object and give ownership of it to the - // base::Callback. - ScopedJavaGlobalRef j_callback; - j_callback.Reset(env, callback); - content::RenderFrameHost::JavaScriptResultCallback c_callback = - base::Bind(&JavaScriptResultCallback, j_callback); - - web_contents_->GetMainFrame()->ExecuteJavaScript( - ConvertJavaStringToUTF16(env, script), - c_callback); -} - // TODO(sgurun) add support for posting a frame whose name is known (only // main frame is supported at this time, see crbug.com/389721) // TODO(sgurun) add support for passing message ports @@ -1392,6 +1335,7 @@ bool ContentViewCoreImpl::GetUseDesktopUserAgent( void ContentViewCoreImpl::UpdateImeAdapter(long native_ime_adapter, int text_input_type, + int text_input_flags, const std::string& text, int selection_start, int selection_end, @@ -1405,18 +1349,28 @@ void ContentViewCoreImpl::UpdateImeAdapter(long native_ime_adapter, return; ScopedJavaLocalRef jstring_text = ConvertUTF8ToJavaString(env, text); - Java_ContentViewCore_updateImeAdapter(env, obj.obj(), - native_ime_adapter, text_input_type, + Java_ContentViewCore_updateImeAdapter(env, + obj.obj(), + native_ime_adapter, + text_input_type, + text_input_flags, jstring_text.obj(), - selection_start, selection_end, - composition_start, composition_end, - show_ime_if_needed, is_non_ime_change); + selection_start, + selection_end, + composition_start, + composition_end, + show_ime_if_needed, + is_non_ime_change); } void ContentViewCoreImpl::ClearSslPreferences(JNIEnv* env, jobject obj) { - SSLHostState* state = SSLHostState::GetFor( - web_contents_->GetController().GetBrowserContext()); - state->Clear(); + content::SSLHostStateDelegate* delegate = + web_contents_-> + GetController(). + GetBrowserContext()-> + GetSSLHostStateDelegate(); + if (delegate) + delegate->Clear(); } void ContentViewCoreImpl::SetUseDesktopUserAgent( @@ -1505,28 +1459,6 @@ void ContentViewCoreImpl::ExtractSmartClipData(JNIEnv* env, GetWebContents()->GetRoutingID(), rect)); } -void ContentViewCoreImpl::ResumeResponseDeferredAtStart(JNIEnv* env, - jobject obj) { - static_cast(GetWebContents())-> - ResumeResponseDeferredAtStart(); -} - -void ContentViewCoreImpl::SetHasPendingNavigationTransitionForTesting( - JNIEnv* env, - jobject obj) { - RenderFrameHost* frame = static_cast(GetWebContents())-> - GetMainFrame(); - BrowserThread::PostTask( - BrowserThread::IO, - FROM_HERE, - base::Bind( - &TransitionRequestManager::SetHasPendingTransitionRequest, - base::Unretained(TransitionRequestManager::GetInstance()), - frame->GetProcess()->GetID(), - frame->GetRoutingID(), - true)); -} - jint ContentViewCoreImpl::GetCurrentRenderProcessId(JNIEnv* env, jobject obj) { return GetRenderProcessIdFromRenderViewHost( web_contents_->GetRenderViewHost()); @@ -1555,58 +1487,6 @@ void ContentViewCoreImpl::RequestTextSurroundingSelection( } } -void ContentViewCoreImpl::DidDeferAfterResponseStarted( - const scoped_refptr& headers, - const GURL& url) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj(java_ref_.get(env)); - if (obj.is_null()) - return; - - std::vector entering_stylesheets; - std::string transition_color; - if (headers) { - TransitionRequestManager::ParseTransitionStylesheetsFromHeaders( - headers, entering_stylesheets, url); - - headers->EnumerateHeader( - NULL, "X-Transition-Entering-Color", &transition_color); - } - - ScopedJavaLocalRef jstring_transition_color(ConvertUTF8ToJavaString( - env, transition_color)); - - Java_ContentViewCore_didDeferAfterResponseStarted( - env, obj.obj(), jstring_transition_color.obj()); - - std::vector::const_iterator iter = entering_stylesheets.begin(); - for (; iter != entering_stylesheets.end(); ++iter) { - ScopedJavaLocalRef jstring_url(ConvertUTF8ToJavaString( - env, iter->spec())); - Java_ContentViewCore_addEnteringStylesheetToTransition( - env, obj.obj(), jstring_url.obj()); - } -} - -bool ContentViewCoreImpl::WillHandleDeferAfterResponseStarted() { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj = java_ref_.get(env); - if (obj.is_null()) - return false; - - return Java_ContentViewCore_willHandleDeferAfterResponseStarted(env, - obj.obj()); -} - -void ContentViewCoreImpl::DidStartNavigationTransitionForFrame(int64 frame_id) { - JNIEnv* env = AttachCurrentThread(); - ScopedJavaLocalRef obj(java_ref_.get(env)); - if (obj.is_null()) - return; - Java_ContentViewCore_didStartNavigationTransitionForFrame( - env, obj.obj(), frame_id); -} - void ContentViewCoreImpl::OnSmartClipDataExtracted( const base::string16& text, const base::string16& html, diff --git a/content/browser/android/content_view_core_impl.h b/content/browser/android/content_view_core_impl.h index 7624c74887479..a389d466d25a1 100644 --- a/content/browser/android/content_view_core_impl.h +++ b/content/browser/android/content_view_core_impl.h @@ -94,8 +94,6 @@ class ContentViewCoreImpl : public ContentViewCore, jstring virtual_url_for_data_url, jboolean can_load_local_resources, jboolean is_renderer_initiated); - base::android::ScopedJavaLocalRef GetURL(JNIEnv* env, jobject) const; - jboolean IsIncognito(JNIEnv* env, jobject obj); void SendOrientationChangeEvent(JNIEnv* env, jobject obj, jint orientation); jboolean OnTouchEvent(JNIEnv* env, jobject obj, @@ -161,11 +159,6 @@ class ContentViewCoreImpl : public ContentViewCore, jboolean enabled); void ClearHistory(JNIEnv* env, jobject obj); - void EvaluateJavaScript(JNIEnv* env, - jobject obj, - jstring script, - jobject callback, - jboolean start_renderer); void PostMessageToFrame(JNIEnv* env, jobject obj, jstring frame_id, jstring message, jstring source_origin, jstring target_origin); long GetNativeImeAdapter(JNIEnv* env, jobject obj); @@ -209,31 +202,12 @@ class ContentViewCoreImpl : public ContentViewCore, void SetBackgroundOpaque(JNIEnv* env, jobject jobj, jboolean opaque); - // Notifies the main frame that it can continue navigation (if it was deferred - // immediately at first response). - void ResumeResponseDeferredAtStart(JNIEnv* env, jobject obj); - - void SetHasPendingNavigationTransitionForTesting(JNIEnv* env, jobject obj); - jint GetCurrentRenderProcessId(JNIEnv* env, jobject obj); // -------------------------------------------------------------------------- // Public methods that call to Java via JNI // -------------------------------------------------------------------------- - // This method is invoked when the request is deferred immediately after - // receiving response headers. - void DidDeferAfterResponseStarted( - const scoped_refptr& headers, - const GURL& url); - - // This method is invoked when a navigation transition is detected, to - // determine if the embedder intends to handle it. - bool WillHandleDeferAfterResponseStarted(); - - // This method is invoked when a navigation transition has started. - void DidStartNavigationTransitionForFrame(int64 frame_id); - void OnSmartClipDataExtracted(const base::string16& text, const base::string16& html, const gfx::Rect& clip_rect); @@ -259,11 +233,16 @@ class ContentViewCoreImpl : public ContentViewCore, const gfx::Vector2dF& content_offset, float overdraw_bottom_height); - void UpdateImeAdapter(long native_ime_adapter, int text_input_type, + void UpdateImeAdapter(long native_ime_adapter, + int text_input_type, + int text_input_flags, const std::string& text, - int selection_start, int selection_end, - int composition_start, int composition_end, - bool show_ime_if_needed, bool is_non_ime_change); + int selection_start, + int selection_end, + int composition_start, + int composition_end, + bool show_ime_if_needed, + bool is_non_ime_change); void SetTitle(const base::string16& title); void OnBackgroundColorChanged(SkColor color); diff --git a/content/browser/android/content_view_render_view.cc b/content/browser/android/content_view_render_view.cc index 06590fa7513c7..00d9ad0c7b4ff 100644 --- a/content/browser/android/content_view_render_view.cc +++ b/content/browser/android/content_view_render_view.cc @@ -16,6 +16,7 @@ #include "content/public/browser/android/compositor.h" #include "content/public/browser/android/content_view_layer_renderer.h" #include "content/public/browser/android/layer_tree_build_helper.h" +#include "content/public/browser/android/ui_resource_provider.h" #include "jni/ContentViewRenderView_jni.h" #include "ui/gfx/android/java_bitmap.h" #include "ui/gfx/size.h" @@ -70,6 +71,7 @@ void ContentViewRenderView::SetLayerTreeBuildHelper(JNIEnv* env, LayerTreeBuildHelper* build_helper = reinterpret_cast(native_build_helper); layer_tree_build_helper_.reset(build_helper); + InitCompositor(); } // static static jlong Init(JNIEnv* env, @@ -120,6 +122,12 @@ void ContentViewRenderView::SurfaceChanged(JNIEnv* env, jobject obj, void ContentViewRenderView::SetOverlayVideoMode( JNIEnv* env, jobject obj, bool enabled) { compositor_->SetHasTransparentBackground(enabled); + SetNeedsComposite(env, obj); +} + +void ContentViewRenderView::SetNeedsComposite(JNIEnv* env, jobject obj) { + if (compositor_) + compositor_->SetNeedsComposite(); } void ContentViewRenderView::Layout() { @@ -136,4 +144,11 @@ void ContentViewRenderView::InitCompositor() { if (!compositor_) compositor_.reset(Compositor::Create(this, root_window_)); } + +jlong ContentViewRenderView::GetUIResourceProvider(JNIEnv* env, + jobject obj) { + if (!compositor_) + return 0; + return reinterpret_cast(&compositor_->GetUIResourceProvider()); +} } // namespace content diff --git a/content/browser/android/content_view_render_view.h b/content/browser/android/content_view_render_view.h index 2f1bcc1e7c783..cc8fe80a9f8f6 100644 --- a/content/browser/android/content_view_render_view.h +++ b/content/browser/android/content_view_render_view.h @@ -19,6 +19,7 @@ class Layer; namespace content { class Compositor; class LayerTreeBuildHelper; +class UIResourceProvider; class ContentViewRenderView : public CompositorClient { public: @@ -41,6 +42,11 @@ class ContentViewRenderView : public CompositorClient { jint format, jint width, jint height, jobject surface); jboolean Composite(JNIEnv* env, jobject obj); void SetOverlayVideoMode(JNIEnv* env, jobject obj, bool enabled); + void SetNeedsComposite(JNIEnv* env, jobject obj); + + // TODO(yusufo): Remove this once the compositor code is + // refactored to use a unified system. + jlong GetUIResourceProvider(JNIEnv* env, jobject obj); // CompositorClient implementation virtual void Layout() OVERRIDE; diff --git a/content/browser/android/devtools_auth.cc b/content/browser/android/devtools_auth.cc index 777a01e00b0b6..46314c4476848 100644 --- a/content/browser/android/devtools_auth.cc +++ b/content/browser/android/devtools_auth.cc @@ -4,23 +4,28 @@ #include "content/public/browser/android/devtools_auth.h" -#include +#include #include +#include #include "base/logging.h" namespace content { -bool CanUserConnectToDevTools(uid_t uid, gid_t gid) { - struct passwd* creds = getpwuid(uid); +bool CanUserConnectToDevTools( + const net::UnixDomainServerSocket::Credentials& credentials) { + struct passwd* creds = getpwuid(credentials.user_id); if (!creds || !creds->pw_name) { - LOG(WARNING) << "DevTools: can't obtain creds for uid " << uid; + LOG(WARNING) << "DevTools: can't obtain creds for uid " + << credentials.user_id; return false; } - if (gid == uid && + if (credentials.group_id == credentials.user_id && (strcmp("root", creds->pw_name) == 0 || // For rooted devices strcmp("shell", creds->pw_name) == 0 || // For non-rooted devices - uid == getuid())) { // From processes signed with the same key + + // From processes signed with the same key + credentials.user_id == getuid())) { return true; } LOG(WARNING) << "DevTools: connection attempt from " << creds->pw_name; diff --git a/content/browser/android/edge_effect.cc b/content/browser/android/edge_effect.cc index 2979872723eaf..00c1aa51c0cea 100644 --- a/content/browser/android/edge_effect.cc +++ b/content/browser/android/edge_effect.cc @@ -12,22 +12,14 @@ namespace content { namespace { -enum State { - STATE_IDLE = 0, - STATE_PULL, - STATE_ABSORB, - STATE_RECEDE, - STATE_PULL_DECAY -}; - // Time it will take the effect to fully recede in ms -const int kRecedeTime = 1000; +const int kRecedeTimeMs = 1000; // Time it will take before a pulled glow begins receding in ms -const int kPullTime = 167; +const int kPullTimeMs = 167; // Time it will take in ms for a pulled glow to decay before release -const int kPullDecayTime = 1000; +const int kPullDecayTimeMs = 1000; const float kMaxAlpha = 1.f; const float kHeldEdgeScaleY = .5f; @@ -43,7 +35,7 @@ const float kMaxVelocity = 10000.f; const float kEpsilon = 0.001f; -const float kGlowHeightToWidthRatio = 0.25f; +const float kGlowHeightWidthRatio = 0.25f; // How much dragging should effect the height of the edge image. // Number determined by user testing. @@ -57,6 +49,9 @@ const float kPullDistanceAlphaGlowFactor = 1.1f; const int kVelocityEdgeFactor = 8; const int kVelocityGlowFactor = 12; +const float kEdgeHeightAtMdpi = 12.f; +const float kGlowHeightAtMdpi = 128.f; + template T Lerp(T a, T b, T t) { return a + (b - a) * t; @@ -64,7 +59,7 @@ T Lerp(T a, T b, T t) { template T Clamp(T value, T low, T high) { - return value < low ? low : (value > high ? high : value); + return value < low ? low : (value > high ? high : value); } template @@ -78,48 +73,6 @@ T Damp(T input, T factor) { return result; } -gfx::Transform ComputeTransform(EdgeEffect::Edge edge, - const gfx::SizeF& window_size, - int offset, - int height) { - // Edge effects that require rotation are translated to the center about which - // the layer should be rotated to align with the corresponding edge. - switch (edge) { - case EdgeEffect::EDGE_TOP: - return gfx::Transform(1, 0, 0, 1, 0, offset); - case EdgeEffect::EDGE_LEFT: - return gfx::Transform(0, 1, -1, 0, - (-window_size.height() + height) / 2.f + offset, - (window_size.height() - height) / 2.f); - case EdgeEffect::EDGE_BOTTOM: - return gfx::Transform(-1, 0, 0, -1, - 0, window_size.height() - height + offset); - case EdgeEffect::EDGE_RIGHT: - return gfx::Transform(0, -1, 1, 0, - (-window_size.height() - height) / 2.f + window_size.width() + offset, - (window_size.height() - height) / 2.f); - default: - NOTREACHED() << "Invalid edge: " << edge; - return gfx::Transform(); - }; -} - -gfx::Size ComputeBounds(EdgeEffect::Edge edge, - const gfx::SizeF& window_size, - int height) { - switch (edge) { - case EdgeEffect::EDGE_TOP: - case EdgeEffect::EDGE_BOTTOM: - return gfx::Size(window_size.width(), height); - case EdgeEffect::EDGE_LEFT: - case EdgeEffect::EDGE_RIGHT: - return gfx::Size(window_size.height(), height); - default: - NOTREACHED() << "Invalid edge: " << edge; - return gfx::Size(); - }; -} - } // namespace class EdgeEffect::EffectLayer { @@ -141,20 +94,16 @@ class EdgeEffect::EffectLayer { void Disable() { ui_resource_layer_->SetIsDrawable(false); } - void Update(EdgeEffect::Edge edge, - const gfx::SizeF& window_size, - int offset, - int height, + void Update(const gfx::Size& size, + const gfx::Transform& transform, float opacity) { ui_resource_layer_->SetUIResourceId( resource_manager_->GetUIResourceId(resource_type_)); ui_resource_layer_->SetIsDrawable(true); - gfx::Size bounds = ComputeBounds(edge, window_size, height); ui_resource_layer_->SetTransformOrigin( - gfx::Point3F(bounds.width() * 0.5f, bounds.height() * 0.5f, 0)); - ui_resource_layer_->SetTransform( - ComputeTransform(edge, window_size, offset, height)); - ui_resource_layer_->SetBounds(bounds); + gfx::Point3F(size.width() * 0.5f, 0, 0)); + ui_resource_layer_->SetTransform(transform); + ui_resource_layer_->SetBounds(size); ui_resource_layer_->SetOpacity(Clamp(opacity, 0.f, 1.f)); } @@ -165,11 +114,14 @@ class EdgeEffect::EffectLayer { DISALLOW_COPY_AND_ASSIGN(EffectLayer); }; -EdgeEffect::EdgeEffect(ui::SystemUIResourceManager* resource_manager) +EdgeEffect::EdgeEffect(ui::SystemUIResourceManager* resource_manager, + float device_scale_factor) : edge_(new EffectLayer(ui::SystemUIResourceManager::OVERSCROLL_EDGE, resource_manager)), glow_(new EffectLayer(ui::SystemUIResourceManager::OVERSCROLL_GLOW, resource_manager)), + base_edge_height_(kEdgeHeightAtMdpi * device_scale_factor), + base_glow_height_(kGlowHeightAtMdpi * device_scale_factor), edge_alpha_(0), edge_scale_y_(0), glow_alpha_(0), @@ -186,7 +138,8 @@ EdgeEffect::EdgeEffect(ui::SystemUIResourceManager* resource_manager) pull_distance_(0) { } -EdgeEffect::~EdgeEffect() { } +EdgeEffect::~EdgeEffect() { +} bool EdgeEffect::IsFinished() const { return state_ == STATE_IDLE; @@ -199,7 +152,9 @@ void EdgeEffect::Finish() { state_ = STATE_IDLE; } -void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) { +void EdgeEffect::Pull(base::TimeTicks current_time, + float delta_distance, + float displacement) { if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) { return; } @@ -209,15 +164,15 @@ void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) { state_ = STATE_PULL; start_time_ = current_time; - duration_ = base::TimeDelta::FromMilliseconds(kPullTime); + duration_ = base::TimeDelta::FromMilliseconds(kPullTimeMs); float abs_delta_distance = std::abs(delta_distance); pull_distance_ += delta_distance; float distance = std::abs(pull_distance_); edge_alpha_ = edge_alpha_start_ = Clamp(distance, kPullEdgeBegin, kMaxAlpha); - edge_scale_y_ = edge_scale_y_start_ - = Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f); + edge_scale_y_ = edge_scale_y_start_ = + Clamp(distance * kPullDistanceEdgeFactor, kHeldEdgeScaleY, 1.f); glow_alpha_ = glow_alpha_start_ = std::min(kMaxAlpha, @@ -232,7 +187,8 @@ void EdgeEffect::Pull(base::TimeTicks current_time, float delta_distance) { // Do not allow glow to get larger than kMaxGlowHeight. glow_scale_y_ = glow_scale_y_start_ = Clamp(glow_scale_y_ + glow_change * kPullDistanceGlowFactor, - 0.f, kMaxGlowHeight); + 0.f, + kMaxGlowHeight); edge_alpha_finish_ = edge_alpha_; edge_scale_y_finish_ = edge_scale_y_; @@ -258,7 +214,7 @@ void EdgeEffect::Release(base::TimeTicks current_time) { glow_scale_y_finish_ = 0.f; start_time_ = current_time; - duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime); + duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs); } void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) { @@ -282,19 +238,18 @@ void EdgeEffect::Absorb(base::TimeTicks current_time, float velocity) { // reflect the strength of the user's scrolling. edge_alpha_finish_ = Clamp(velocity * kVelocityEdgeFactor, 0.f, 1.f); // Edge should never get larger than the size of its asset. - edge_scale_y_finish_ = Clamp(velocity * kVelocityEdgeFactor, - kHeldEdgeScaleY, 1.f); + edge_scale_y_finish_ = + Clamp(velocity * kVelocityEdgeFactor, kHeldEdgeScaleY, 1.f); // Growth for the size of the glow should be quadratic to properly // respond // to a user's scrolling speed. The faster the scrolling speed, the more // intense the effect should be for both the size and the saturation. - glow_scale_y_finish_ = std::min( - 0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f); + glow_scale_y_finish_ = + std::min(0.025f + (velocity * (velocity / 100) * 0.00015f), 1.75f); // Alpha should change for the glow as well as size. - glow_alpha_finish_ = Clamp(glow_alpha_start_, - velocity * kVelocityGlowFactor * .00001f, - kMaxAlpha); + glow_alpha_finish_ = Clamp( + glow_alpha_start_, velocity * kVelocityGlowFactor * .00001f, kMaxAlpha); } bool EdgeEffect::Update(base::TimeTicks current_time) { @@ -315,7 +270,7 @@ bool EdgeEffect::Update(base::TimeTicks current_time) { case STATE_ABSORB: state_ = STATE_RECEDE; start_time_ = current_time; - duration_ = base::TimeDelta::FromMilliseconds(kRecedeTime); + duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs); edge_alpha_start_ = edge_alpha_; edge_scale_y_start_ = edge_scale_y_; @@ -331,7 +286,7 @@ bool EdgeEffect::Update(base::TimeTicks current_time) { case STATE_PULL: state_ = STATE_PULL_DECAY; start_time_ = current_time; - duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTime); + duration_ = base::TimeDelta::FromMilliseconds(kPullDecayTimeMs); edge_alpha_start_ = edge_alpha_; edge_scale_y_start_ = edge_scale_y_; @@ -347,10 +302,12 @@ bool EdgeEffect::Update(base::TimeTicks current_time) { case STATE_PULL_DECAY: { // When receding, we want edge to decrease more slowly // than the glow. - const float factor = glow_scale_y_finish_ != 0 ? - 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) : - std::numeric_limits::max(); - edge_scale_y_ = edge_scale_y_start_ + + const float factor = + glow_scale_y_finish_ + ? 1 / (glow_scale_y_finish_ * glow_scale_y_finish_) + : std::numeric_limits::max(); + edge_scale_y_ = + edge_scale_y_start_ + (edge_scale_y_finish_ - edge_scale_y_start_) * interp * factor; state_ = STATE_RECEDE; } break; @@ -368,17 +325,14 @@ bool EdgeEffect::Update(base::TimeTicks current_time) { return !IsFinished(); } -void EdgeEffect::ApplyToLayers(gfx::SizeF window_size, - Edge edge, - float edge_height, - float glow_height, - float offset) { +void EdgeEffect::ApplyToLayers(const gfx::SizeF& size, + const gfx::Transform& transform) { if (IsFinished()) return; // An empty window size, while meaningless, is also relatively harmless, and // will simply prevent any drawing of the layers. - if (window_size.IsEmpty()) { + if (size.IsEmpty()) { edge_->Disable(); glow_->Disable(); return; @@ -386,13 +340,17 @@ void EdgeEffect::ApplyToLayers(gfx::SizeF window_size, // Glow const int scaled_glow_height = static_cast( - std::min(glow_height * glow_scale_y_ * kGlowHeightToWidthRatio * 0.6f, - glow_height * kMaxGlowHeight) + 0.5f); - glow_->Update(edge, window_size, offset, scaled_glow_height, glow_alpha_); + std::min(base_glow_height_ * glow_scale_y_ * kGlowHeightWidthRatio * 0.6f, + base_glow_height_ * kMaxGlowHeight) + + 0.5f); + const gfx::Size glow_size(size.width(), scaled_glow_height); + glow_->Update(glow_size, transform, glow_alpha_); // Edge - const int scaled_edge_height = static_cast(edge_height * edge_scale_y_); - edge_->Update(edge, window_size, offset, scaled_edge_height, edge_alpha_); + const int scaled_edge_height = + static_cast(base_edge_height_ * edge_scale_y_); + const gfx::Size edge_size(size.width(), scaled_edge_height); + edge_->Update(edge_size, transform, edge_alpha_); } void EdgeEffect::SetParent(cc::Layer* parent) { @@ -410,4 +368,4 @@ void EdgeEffect::PreloadResources( ui::SystemUIResourceManager::OVERSCROLL_GLOW); } -} // namespace content +} // namespace content diff --git a/content/browser/android/edge_effect.h b/content/browser/android/edge_effect.h index a8f944c67acad..ced6a3245761a 100644 --- a/content/browser/android/edge_effect.h +++ b/content/browser/android/edge_effect.h @@ -5,10 +5,8 @@ #ifndef CONTENT_BROWSER_ANDROID_EDGE_EFFECT_H_ #define CONTENT_BROWSER_ANDROID_EDGE_EFFECT_H_ -#include "base/basictypes.h" #include "base/memory/scoped_ptr.h" -#include "base/time/time.h" -#include "ui/gfx/size_f.h" +#include "content/browser/android/edge_effect_base.h" namespace cc { class Layer; @@ -20,57 +18,41 @@ class SystemUIResourceManager; namespace content { -/* |EdgeEffect| mirrors its Android counterpart, EdgeEffect.java. - * The primary difference is ownership; the Android version manages render - * resources directly, while this version simply applies the effect to - * existing resources. Conscious tradeoffs were made to align this as closely - * as possible with the original Android java version. - * All coordinates and dimensions are in device pixels. - */ -class EdgeEffect { +// |EdgeEffect| mirrors its Android counterpart, EdgeEffect.java. +// Conscious tradeoffs were made to align this as closely as possible with the +// the original Android java version. +// All coordinates and dimensions are in device pixels. +class EdgeEffect : public EdgeEffectBase { public: - enum Edge { - EDGE_TOP = 0, - EDGE_LEFT, - EDGE_BOTTOM, - EDGE_RIGHT, - EDGE_COUNT - }; + explicit EdgeEffect(ui::SystemUIResourceManager* resource_manager, + float device_scale_factor); + virtual ~EdgeEffect(); - explicit EdgeEffect(ui::SystemUIResourceManager* resource_manager); - ~EdgeEffect(); + virtual void Pull(base::TimeTicks current_time, + float delta_distance, + float displacement) OVERRIDE; + virtual void Absorb(base::TimeTicks current_time, float velocity) OVERRIDE; + virtual bool Update(base::TimeTicks current_time) OVERRIDE; + virtual void Release(base::TimeTicks current_time) OVERRIDE; - void Pull(base::TimeTicks current_time, float delta_distance); - void Absorb(base::TimeTicks current_time, float velocity); - bool Update(base::TimeTicks current_time); - void Release(base::TimeTicks current_time); + virtual void Finish() OVERRIDE; + virtual bool IsFinished() const OVERRIDE; - void Finish(); - bool IsFinished() const; - - void ApplyToLayers(gfx::SizeF window_size, - Edge edge, - float edge_height, - float glow_height, - float offset); - - void SetParent(cc::Layer* parent); + virtual void ApplyToLayers(const gfx::SizeF& size, + const gfx::Transform& transform) OVERRIDE; + virtual void SetParent(cc::Layer* parent) OVERRIDE; + // Thread-safe trigger to load resources. static void PreloadResources(ui::SystemUIResourceManager* resource_manager); private: - enum State { - STATE_IDLE = 0, - STATE_PULL, - STATE_ABSORB, - STATE_RECEDE, - STATE_PULL_DECAY - }; - class EffectLayer; scoped_ptr edge_; scoped_ptr glow_; + float base_edge_height_; + float base_glow_height_; + float edge_alpha_; float edge_scale_y_; float glow_alpha_; diff --git a/content/browser/android/edge_effect_base.h b/content/browser/android/edge_effect_base.h new file mode 100644 index 0000000000000..f7df20dfe16c7 --- /dev/null +++ b/content/browser/android/edge_effect_base.h @@ -0,0 +1,49 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_EDGE_EFFECT_BASE_H_ +#define CONTENT_BROWSER_ANDROID_EDGE_EFFECT_BASE_H_ + +#include "base/basictypes.h" +#include "base/time/time.h" +#include "ui/gfx/geometry/size_f.h" +#include "ui/gfx/transform.h" + +namespace cc { +class Layer; +} + +namespace content { + +// A base class for overscroll-related Android effects. +class EdgeEffectBase { + public: + enum State { + STATE_IDLE = 0, + STATE_PULL, + STATE_ABSORB, + STATE_RECEDE, + STATE_PULL_DECAY + }; + + virtual ~EdgeEffectBase() {} + + virtual void Pull(base::TimeTicks current_time, + float delta_distance, + float displacement) = 0; + virtual void Absorb(base::TimeTicks current_time, float velocity) = 0; + virtual bool Update(base::TimeTicks current_time) = 0; + virtual void Release(base::TimeTicks current_time) = 0; + + virtual void Finish() = 0; + virtual bool IsFinished() const = 0; + + virtual void ApplyToLayers(const gfx::SizeF& size, + const gfx::Transform& transform) = 0; + virtual void SetParent(cc::Layer* parent) = 0; +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_EDGE_EFFECT_BASE_H_ diff --git a/content/browser/android/edge_effect_l.cc b/content/browser/android/edge_effect_l.cc new file mode 100644 index 0000000000000..4bf264651455f --- /dev/null +++ b/content/browser/android/edge_effect_l.cc @@ -0,0 +1,268 @@ +// 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 "content/browser/android/edge_effect_l.h" + +#include "cc/layers/ui_resource_layer.h" +#include "ui/base/android/system_ui_resource_manager.h" + +namespace content { + +namespace { + +// Time it will take the effect to fully recede in ms +const int kRecedeTimeMs = 1000; + +// Time it will take before a pulled glow begins receding in ms +const int kPullTimeMs = 167; + +const float kMaxAlpha = 1.f; + +const float kPullGlowBegin = 0.f; + +// Min/max velocity that will be absorbed +const float kMinVelocity = 100.f; +const float kMaxVelocity = 10000.f; + +const float kEpsilon = 0.001f; + +const float kSin = 0.5f; // sin(PI / 6) +const float kCos = 0.866f; // cos(PI / 6); + +// How much dragging should effect the height of the glow image. +// Number determined by user testing. +const float kPullDistanceAlphaGlowFactor = 1.1f; + +const int kVelocityGlowFactor = 12; + +const ui::SystemUIResourceManager::ResourceType kResourceType = + ui::SystemUIResourceManager::OVERSCROLL_GLOW_L; + +template +T Lerp(T a, T b, T t) { + return a + (b - a) * t; +} + +template +T Clamp(T value, T low, T high) { + return value < low ? low : (value > high ? high : value); +} + +template +T Damp(T input, T factor) { + T result; + if (factor == 1) { + result = 1 - (1 - input) * (1 - input); + } else { + result = 1 - std::pow(1 - input, 2 * factor); + } + return result; +} + +} // namespace + +EdgeEffectL::EdgeEffectL(ui::SystemUIResourceManager* resource_manager) + : resource_manager_(resource_manager), + glow_(cc::UIResourceLayer::Create()), + glow_alpha_(0), + glow_scale_y_(0), + glow_alpha_start_(0), + glow_alpha_finish_(0), + glow_scale_y_start_(0), + glow_scale_y_finish_(0), + displacement_(0.5f), + target_displacement_(0.5f), + state_(STATE_IDLE), + pull_distance_(0) { + // Prevent the provided layers from drawing until the effect is activated. + glow_->SetIsDrawable(false); +} + +EdgeEffectL::~EdgeEffectL() { + glow_->RemoveFromParent(); +} + +bool EdgeEffectL::IsFinished() const { + return state_ == STATE_IDLE; +} + +void EdgeEffectL::Finish() { + glow_->SetIsDrawable(false); + pull_distance_ = 0; + state_ = STATE_IDLE; +} + +void EdgeEffectL::Pull(base::TimeTicks current_time, + float delta_distance, + float displacement) { + target_displacement_ = displacement; + if (state_ == STATE_PULL_DECAY && current_time - start_time_ < duration_) { + return; + } + if (state_ != STATE_PULL) { + glow_scale_y_ = std::max(kPullGlowBegin, glow_scale_y_); + } + state_ = STATE_PULL; + + start_time_ = current_time; + duration_ = base::TimeDelta::FromMilliseconds(kPullTimeMs); + + float abs_delta_distance = std::abs(delta_distance); + pull_distance_ += delta_distance; + + glow_alpha_ = glow_alpha_start_ = std::min( + kMaxAlpha, + glow_alpha_ + (abs_delta_distance * kPullDistanceAlphaGlowFactor)); + + if (pull_distance_ == 0) { + glow_scale_y_ = glow_scale_y_start_ = 0; + } else { + float scale = 1.f - + 1.f / std::sqrt(std::abs(pull_distance_) * bounds_.height()) - + 0.3f; + glow_scale_y_ = glow_scale_y_start_ = std::max(0.f, scale) / 0.7f; + } + + glow_alpha_finish_ = glow_alpha_; + glow_scale_y_finish_ = glow_scale_y_; +} + +void EdgeEffectL::Release(base::TimeTicks current_time) { + pull_distance_ = 0; + + if (state_ != STATE_PULL && state_ != STATE_PULL_DECAY) + return; + + state_ = STATE_RECEDE; + glow_alpha_start_ = glow_alpha_; + glow_scale_y_start_ = glow_scale_y_; + + glow_alpha_finish_ = 0.f; + glow_scale_y_finish_ = 0.f; + + start_time_ = current_time; + duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs); +} + +void EdgeEffectL::Absorb(base::TimeTicks current_time, float velocity) { + state_ = STATE_ABSORB; + + velocity = Clamp(std::abs(velocity), kMinVelocity, kMaxVelocity); + + start_time_ = current_time; + // This should never be less than 1 millisecond. + duration_ = base::TimeDelta::FromMilliseconds(0.15f + (velocity * 0.02f)); + + // The glow depends more on the velocity, and therefore starts out + // nearly invisible. + glow_alpha_start_ = 0.3f; + glow_scale_y_start_ = std::max(glow_scale_y_, 0.f); + + // Growth for the size of the glow should be quadratic to properly respond + // to a user's scrolling speed. The faster the scrolling speed, the more + // intense the effect should be for both the size and the saturation. + glow_scale_y_finish_ = + std::min(0.025f + (velocity * (velocity / 100) * 0.00015f) / 2.f, 1.f); + // Alpha should change for the glow as well as size. + glow_alpha_finish_ = Clamp( + glow_alpha_start_, velocity * kVelocityGlowFactor * .00001f, kMaxAlpha); + target_displacement_ = 0.5; +} + +bool EdgeEffectL::Update(base::TimeTicks current_time) { + if (IsFinished()) + return false; + + const double dt = (current_time - start_time_).InMilliseconds(); + const double t = std::min(dt / duration_.InMilliseconds(), 1.); + const float interp = static_cast(Damp(t, 1.)); + + glow_alpha_ = Lerp(glow_alpha_start_, glow_alpha_finish_, interp); + glow_scale_y_ = Lerp(glow_scale_y_start_, glow_scale_y_finish_, interp); + displacement_ = (displacement_ + target_displacement_) / 2.f; + + if (t >= 1.f - kEpsilon) { + switch (state_) { + case STATE_ABSORB: + state_ = STATE_RECEDE; + start_time_ = current_time; + duration_ = base::TimeDelta::FromMilliseconds(kRecedeTimeMs); + + glow_alpha_start_ = glow_alpha_; + glow_scale_y_start_ = glow_scale_y_; + + glow_alpha_finish_ = 0.f; + glow_scale_y_finish_ = 0.f; + break; + case STATE_PULL: + // Hold in this state until explicitly released. + break; + case STATE_PULL_DECAY: + state_ = STATE_RECEDE; + break; + case STATE_RECEDE: + Finish(); + break; + default: + break; + } + } + + bool one_last_frame = false; + if (state_ == STATE_RECEDE && glow_scale_y_ <= 0) { + Finish(); + one_last_frame = true; + } + + return !IsFinished() || one_last_frame; +} + +void EdgeEffectL::ApplyToLayers(const gfx::SizeF& size, + const gfx::Transform& transform) { + if (IsFinished()) + return; + + // An empty window size, while meaningless, is also relatively harmless, and + // will simply prevent any drawing of the layers. + if (size.IsEmpty()) { + glow_->SetIsDrawable(false); + return; + } + + const float r = size.width() * 0.75f / kSin; + const float y = kCos * r; + const float h = r - y; + bounds_ = gfx::Size(size.width(), (int)std::min(size.height(), h)); + gfx::Size image_bounds(r, std::min(1.f, glow_scale_y_) * bounds_.height()); + + glow_->SetIsDrawable(true); + glow_->SetUIResourceId(resource_manager_->GetUIResourceId(kResourceType)); + glow_->SetTransformOrigin(gfx::Point3F(bounds_.width() * 0.5f, 0, 0)); + glow_->SetBounds(image_bounds); + glow_->SetContentsOpaque(false); + glow_->SetOpacity(Clamp(glow_alpha_, 0.f, 1.f)); + + const float displacement = Clamp(displacement_, 0.f, 1.f) - 0.5f; + const float displacement_offset_x = bounds_.width() * displacement * 0.5f; + const float image_offset_x = (bounds_.width() - image_bounds.width()) * 0.5f; + gfx::Transform offset_transform; + offset_transform.Translate(image_offset_x - displacement_offset_x, 0); + offset_transform.ConcatTransform(transform); + glow_->SetTransform(offset_transform); +} + +void EdgeEffectL::SetParent(cc::Layer* parent) { + if (glow_->parent() != parent) + parent->AddChild(glow_); + glow_->SetUIResourceId(resource_manager_->GetUIResourceId(kResourceType)); +} + +// static +void EdgeEffectL::PreloadResources( + ui::SystemUIResourceManager* resource_manager) { + DCHECK(resource_manager); + resource_manager->PreloadResource(kResourceType); +} + +} // namespace content diff --git a/content/browser/android/edge_effect_l.h b/content/browser/android/edge_effect_l.h new file mode 100644 index 0000000000000..029418024e238 --- /dev/null +++ b/content/browser/android/edge_effect_l.h @@ -0,0 +1,81 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_ANDROID_EDGE_EFFECT_L_H_ +#define CONTENT_BROWSER_ANDROID_EDGE_EFFECT_L_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "content/browser/android/edge_effect_base.h" +#include "ui/gfx/geometry/rect_f.h" +#include "ui/gfx/geometry/size.h" + +namespace cc { +class Layer; +class UIResourceLayer; +} + +namespace ui { +class SystemUIResourceManager; +} + +namespace content { + +// |EdgeEffectL| mirrors its Android L counterpart, EdgeEffect.java. +// Conscious tradeoffs were made to align this as closely as possible with the +// the original Android java version. +// All coordinates and dimensions are in device pixels. +class EdgeEffectL : public EdgeEffectBase { + public: + explicit EdgeEffectL(ui::SystemUIResourceManager* resource_manager); + virtual ~EdgeEffectL(); + + virtual void Pull(base::TimeTicks current_time, + float delta_distance, + float displacement) OVERRIDE; + virtual void Absorb(base::TimeTicks current_time, float velocity) OVERRIDE; + virtual bool Update(base::TimeTicks current_time) OVERRIDE; + virtual void Release(base::TimeTicks current_time) OVERRIDE; + + virtual void Finish() OVERRIDE; + virtual bool IsFinished() const OVERRIDE; + + virtual void ApplyToLayers(const gfx::SizeF& size, + const gfx::Transform& transform) OVERRIDE; + virtual void SetParent(cc::Layer* parent) OVERRIDE; + + // Thread-safe trigger to load resources. + static void PreloadResources(ui::SystemUIResourceManager* resource_manager); + + private: + ui::SystemUIResourceManager* const resource_manager_; + + scoped_refptr glow_; + + float glow_alpha_; + float glow_scale_y_; + + float glow_alpha_start_; + float glow_alpha_finish_; + float glow_scale_y_start_; + float glow_scale_y_finish_; + + gfx::RectF arc_rect_; + gfx::Size bounds_; + float displacement_; + float target_displacement_; + + base::TimeTicks start_time_; + base::TimeDelta duration_; + + State state_; + + float pull_distance_; + + DISALLOW_COPY_AND_ASSIGN(EdgeEffectL); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_ANDROID_EDGE_EFFECT_L_H_ diff --git a/content/browser/android/in_process/synchronous_compositor_factory_impl.cc b/content/browser/android/in_process/synchronous_compositor_factory_impl.cc index 45e5aa14d4621..e624e9b53f01d 100644 --- a/content/browser/android/in_process/synchronous_compositor_factory_impl.cc +++ b/content/browser/android/in_process/synchronous_compositor_factory_impl.cc @@ -8,6 +8,7 @@ #include "content/public/browser/browser_thread.h" #include "content/renderer/gpu/frame_swap_message_queue.h" #include "gpu/command_buffer/client/gl_in_process_context.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "ui/gl/android/surface_texture.h" #include "ui/gl/gl_surface.h" #include "ui/gl/gl_surface_stub.h" @@ -32,48 +33,52 @@ blink::WebGraphicsContext3D::Attributes GetDefaultAttribs() { } using webkit::gpu::WebGraphicsContext3DInProcessCommandBufferImpl; +using webkit::gpu::WebGraphicsContext3DImpl; scoped_ptr CreateOffscreenContext( const blink::WebGraphicsContext3D::Attributes& attributes) { const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; - gpu::GLInProcessContextAttribs in_process_attribs; - WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes( + gpu::gles2::ContextCreationAttribHelper in_process_attribs; + WebGraphicsContext3DImpl::ConvertAttributes( attributes, &in_process_attribs); - in_process_attribs.lose_context_when_out_of_memory = 1; - - scoped_ptr context( - gpu::GLInProcessContext::Create(NULL /* service */, - NULL /* surface */, - true /* is_offscreen */, - gfx::kNullAcceleratedWidget, - gfx::Size(1, 1), - NULL /* share_context */, - false /* share_resources */, - in_process_attribs, - gpu_preference)); + in_process_attribs.lose_context_when_out_of_memory = true; + + scoped_ptr context(gpu::GLInProcessContext::Create( + NULL /* service */, + NULL /* surface */, + true /* is_offscreen */, + gfx::kNullAcceleratedWidget, + gfx::Size(1, 1), + NULL /* share_context */, + false /* share_resources */, + in_process_attribs, + gpu_preference, + gpu::GLInProcessContextSharedMemoryLimits())); return context.Pass(); } scoped_ptr CreateContext( scoped_refptr service, - gpu::GLInProcessContext* share_context) { + gpu::GLInProcessContext* share_context, + const gpu::GLInProcessContextSharedMemoryLimits& mem_limits) { const gfx::GpuPreference gpu_preference = gfx::PreferDiscreteGpu; - gpu::GLInProcessContextAttribs in_process_attribs; - WebGraphicsContext3DInProcessCommandBufferImpl::ConvertAttributes( + gpu::gles2::ContextCreationAttribHelper in_process_attribs; + WebGraphicsContext3DImpl::ConvertAttributes( GetDefaultAttribs(), &in_process_attribs); - in_process_attribs.lose_context_when_out_of_memory = 1; - - scoped_ptr context( - gpu::GLInProcessContext::Create(service, - NULL /* surface */, - false /* is_offscreen */, - gfx::kNullAcceleratedWidget, - gfx::Size(1, 1), - share_context, - false /* share_resources */, - in_process_attribs, - gpu_preference)); + in_process_attribs.lose_context_when_out_of_memory = true; + + scoped_ptr context(gpu::GLInProcessContext::Create( + service, + NULL /* surface */, + false /* is_offscreen */, + gfx::kNullAcceleratedWidget, + gfx::Size(1, 1), + share_context, + false /* share_resources */, + in_process_attribs, + gpu_preference, + mem_limits)); return context.Pass(); } @@ -168,35 +173,30 @@ SynchronousCompositorFactoryImpl::GetInputHandlerManagerClient() { return synchronous_input_event_filter(); } -scoped_refptr SynchronousCompositorFactoryImpl:: - GetSharedOffscreenContextProviderForMainThread() { - bool failed = false; - if ((!offscreen_context_for_main_thread_.get() || - offscreen_context_for_main_thread_->DestroyedOnMainThread())) { - scoped_ptr context = - CreateOffscreenContext(GetDefaultAttribs()); - offscreen_context_for_main_thread_ = - webkit::gpu::ContextProviderInProcess::Create( - WrapContext(context.Pass()), - "Compositor-Offscreen-main-thread"); - failed = !offscreen_context_for_main_thread_.get() || - !offscreen_context_for_main_thread_->BindToCurrentThread(); - } - - if (failed) { - offscreen_context_for_main_thread_ = NULL; - } - return offscreen_context_for_main_thread_; +scoped_refptr +SynchronousCompositorFactoryImpl::CreateOffscreenContextProvider( + const blink::WebGraphicsContext3D::Attributes& attributes, + const std::string& debug_name) { + scoped_ptr context = + CreateOffscreenContext(attributes); + return webkit::gpu::ContextProviderInProcess::Create( + WrapContext(context.Pass()), debug_name); } scoped_refptr SynchronousCompositorFactoryImpl:: CreateOnscreenContextProviderForCompositorThread() { DCHECK(service_); - if (!share_context_.get()) - share_context_ = CreateContext(service_, NULL); + if (!share_context_.get()) { + share_context_ = CreateContext( + service_, NULL, gpu::GLInProcessContextSharedMemoryLimits()); + } + gpu::GLInProcessContextSharedMemoryLimits mem_limits; + // This is half of what RenderWidget uses because synchronous compositor + // pipeline is only one frame deep. + mem_limits.mapped_memory_reclaim_limit = 6 * 1024 * 1024; return webkit::gpu::ContextProviderInProcess::Create( - WrapContext(CreateContext(service_, share_context_.get())), + WrapContext(CreateContext(service_, share_context_.get(), mem_limits)), "Child-Compositor"); } @@ -241,18 +241,23 @@ bool SynchronousCompositorFactoryImpl::CanCreateMainThreadContext() { scoped_refptr SynchronousCompositorFactoryImpl::TryCreateStreamTextureFactory() { - scoped_refptr - context_provider; - // This check only guarantees the main thread context is created after - // a compositor did successfully initialize hardware draw in the past. - // In particular this does not guarantee that the main thread context - // will fail creation when all compositors release hardware draw. - if (CanCreateMainThreadContext() && !video_context_provider_) { + // Always fail creation even if |video_context_provider_| is not NULL. + // This is to avoid synchronous calls that may deadlock. Setting + // |video_context_provider_| to null is also not safe since it makes + // synchronous destruction uncontrolled and possibly deadlock. + if (!CanCreateMainThreadContext()) { + return + scoped_refptr(); + } + + if (!video_context_provider_) { DCHECK(service_); DCHECK(share_context_.get()); video_context_provider_ = new VideoContextProvider( - CreateContext(service_, share_context_.get())); + CreateContext(service_, + share_context_.get(), + gpu::GLInProcessContextSharedMemoryLimits())); } return video_context_provider_; } diff --git a/content/browser/android/in_process/synchronous_compositor_factory_impl.h b/content/browser/android/in_process/synchronous_compositor_factory_impl.h index 63eba9dbe1783..0c3cb7c6f150c 100644 --- a/content/browser/android/in_process/synchronous_compositor_factory_impl.h +++ b/content/browser/android/in_process/synchronous_compositor_factory_impl.h @@ -39,7 +39,9 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory { OVERRIDE; virtual InputHandlerManagerClient* GetInputHandlerManagerClient() OVERRIDE; virtual scoped_refptr - GetSharedOffscreenContextProviderForMainThread() OVERRIDE; + CreateOffscreenContextProvider( + const blink::WebGraphicsContext3D::Attributes& attributes, + const std::string& debug_name) OVERRIDE; virtual scoped_refptr CreateStreamTextureFactory( int view_id) OVERRIDE; virtual blink::WebGraphicsContext3D* CreateOffscreenGraphicsContext3D( @@ -67,9 +69,6 @@ class SynchronousCompositorFactoryImpl : public SynchronousCompositorFactory { SynchronousInputEventFilter synchronous_input_event_filter_; - scoped_refptr - offscreen_context_for_main_thread_; - scoped_refptr service_; scoped_ptr share_context_; scoped_refptr diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc index 65263812a549c..ca63276b5b051 100644 --- a/content/browser/android/java/gin_java_bridge_dispatcher_host.cc +++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.cc @@ -56,6 +56,7 @@ GinJavaBridgeDispatcherHost::GinJavaBridgeDispatcherHost( } GinJavaBridgeDispatcherHost::~GinJavaBridgeDispatcherHost() { + DCHECK(pending_replies_.empty()); } void GinJavaBridgeDispatcherHost::RenderFrameCreated( @@ -70,6 +71,15 @@ void GinJavaBridgeDispatcherHost::RenderFrameCreated( void GinJavaBridgeDispatcherHost::RenderFrameDeleted( RenderFrameHost* render_frame_host) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + IPC::Message* reply_msg = TakePendingReply(render_frame_host); + if (reply_msg != NULL) { + base::ListValue result; + result.Append(base::Value::CreateNullValue()); + IPC::WriteParam(reply_msg, result); + IPC::WriteParam(reply_msg, kGinJavaBridgeRenderFrameDeleted); + render_frame_host->Send(reply_msg); + } RemoveHolder(render_frame_host, GinJavaBoundObject::ObjectMap::iterator(&objects_), objects_.size()); @@ -192,6 +202,10 @@ void GinJavaBridgeDispatcherHost::RemoveNamedObject( if (iter == named_objects_.end()) return; + // |name| may come from |named_objects_|. Make a copy of name so that if + // |name| is from |named_objects_| it'll be valid after the remove below. + const std::string copied_name(name); + scoped_refptr object(*objects_.Lookup(iter->second)); named_objects_.erase(iter); object->RemoveName(); @@ -210,7 +224,7 @@ void GinJavaBridgeDispatcherHost::RemoveNamedObject( } web_contents()->SendToAllFrames( - new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, name)); + new GinJavaBridgeMsg_RemoveNamedObject(MSG_ROUTING_NONE, copied_name)); } void GinJavaBridgeDispatcherHost::SetAllowObjectContentsInspection(bool allow) { @@ -348,17 +362,6 @@ bool GinJavaBridgeDispatcherHost::IsValidRenderFrameHost( return helper->rfh_found(); } -void GinJavaBridgeDispatcherHost::SendReply( - RenderFrameHost* render_frame_host, - IPC::Message* reply_msg) { - DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (IsValidRenderFrameHost(render_frame_host)) { - render_frame_host->Send(reply_msg); - } else { - delete reply_msg; - } -} - void GinJavaBridgeDispatcherHost::OnGetMethods( RenderFrameHost* render_frame_host, GinJavaBoundObject::ObjectID object_id, @@ -377,22 +380,26 @@ void GinJavaBridgeDispatcherHost::OnGetMethods( render_frame_host->Send(reply_msg); return; } + DCHECK(!HasPendingReply(render_frame_host)); + pending_replies_[render_frame_host] = reply_msg; base::PostTaskAndReplyWithResult( g_background_thread.Get().message_loop()->message_loop_proxy(), FROM_HERE, base::Bind(&GinJavaBoundObject::GetMethodNames, object), base::Bind(&GinJavaBridgeDispatcherHost::SendMethods, AsWeakPtr(), - render_frame_host, - reply_msg)); + render_frame_host)); } void GinJavaBridgeDispatcherHost::SendMethods( RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, const std::set& method_names) { + IPC::Message* reply_msg = TakePendingReply(render_frame_host); + if (!reply_msg) { + return; + } IPC::WriteParam(reply_msg, method_names); - SendReply(render_frame_host, reply_msg); + render_frame_host->Send(reply_msg); } void GinJavaBridgeDispatcherHost::OnHasMethod( @@ -409,22 +416,26 @@ void GinJavaBridgeDispatcherHost::OnHasMethod( render_frame_host->Send(reply_msg); return; } + DCHECK(!HasPendingReply(render_frame_host)); + pending_replies_[render_frame_host] = reply_msg; base::PostTaskAndReplyWithResult( g_background_thread.Get().message_loop()->message_loop_proxy(), FROM_HERE, base::Bind(&GinJavaBoundObject::HasMethod, object, method_name), base::Bind(&GinJavaBridgeDispatcherHost::SendHasMethodReply, AsWeakPtr(), - render_frame_host, - reply_msg)); + render_frame_host)); } void GinJavaBridgeDispatcherHost::SendHasMethodReply( RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, bool result) { + IPC::Message* reply_msg = TakePendingReply(render_frame_host); + if (!reply_msg) { + return; + } IPC::WriteParam(reply_msg, result); - SendReply(render_frame_host, reply_msg); + render_frame_host->Send(reply_msg); } void GinJavaBridgeDispatcherHost::OnInvokeMethod( @@ -445,6 +456,8 @@ void GinJavaBridgeDispatcherHost::OnInvokeMethod( render_frame_host->Send(reply_msg); return; } + DCHECK(!HasPendingReply(render_frame_host)); + pending_replies_[render_frame_host] = reply_msg; scoped_refptr result = new GinJavaMethodInvocationHelper( make_scoped_ptr(new GinJavaBoundObjectDelegate(object)) @@ -462,32 +475,37 @@ void GinJavaBridgeDispatcherHost::OnInvokeMethod( &GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult, AsWeakPtr(), render_frame_host, - reply_msg, result)); } void GinJavaBridgeDispatcherHost::ProcessMethodInvocationResult( RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, scoped_refptr result) { if (result->HoldsPrimitiveResult()) { + IPC::Message* reply_msg = TakePendingReply(render_frame_host); + if (!reply_msg) { + return; + } IPC::WriteParam(reply_msg, result->GetPrimitiveResult()); IPC::WriteParam(reply_msg, result->GetInvocationError()); - SendReply(render_frame_host, reply_msg); + render_frame_host->Send(reply_msg); } else { - ProcessMethodInvocationObjectResult(render_frame_host, reply_msg, result); + ProcessMethodInvocationObjectResult(render_frame_host, result); } } void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult( RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, scoped_refptr result) { DCHECK_CURRENTLY_ON(BrowserThread::UI); + if (!IsValidRenderFrameHost(render_frame_host)) { - delete reply_msg; + // In this case, we must've already sent the reply when the render frame + // was destroyed. + DCHECK(!HasPendingReply(render_frame_host)); return; } + base::ListValue wrapped_result; if (!result->GetObjectResult().is_null()) { GinJavaBoundObject::ObjectID returned_object_id; @@ -500,10 +518,15 @@ void GinJavaBridgeDispatcherHost::ProcessMethodInvocationObjectResult( render_frame_host); } wrapped_result.Append( - GinJavaBridgeValue::CreateObjectIDValue(returned_object_id).release()); + GinJavaBridgeValue::CreateObjectIDValue( + returned_object_id).release()); } else { wrapped_result.Append(base::Value::CreateNullValue()); } + IPC::Message* reply_msg = TakePendingReply(render_frame_host); + if (!reply_msg) { + return; + } IPC::WriteParam(reply_msg, wrapped_result); IPC::WriteParam(reply_msg, result->GetInvocationError()); render_frame_host->Send(reply_msg); @@ -523,4 +546,28 @@ void GinJavaBridgeDispatcherHost::OnObjectWrapperDeleted( } } +IPC::Message* GinJavaBridgeDispatcherHost::TakePendingReply( + RenderFrameHost* render_frame_host) { + if (!IsValidRenderFrameHost(render_frame_host)) { + DCHECK(!HasPendingReply(render_frame_host)); + return NULL; + } + + PendingReplyMap::iterator it = pending_replies_.find(render_frame_host); + // There may be no pending reply if we're called from RenderFrameDeleted and + // we already sent the reply through the regular route. + if (it == pending_replies_.end()) { + return NULL; + } + + IPC::Message* reply_msg = it->second; + pending_replies_.erase(it); + return reply_msg; +} + +bool GinJavaBridgeDispatcherHost::HasPendingReply( + RenderFrameHost* render_frame_host) const { + return pending_replies_.find(render_frame_host) != pending_replies_.end(); +} + } // namespace content diff --git a/content/browser/android/java/gin_java_bridge_dispatcher_host.h b/content/browser/android/java/gin_java_bridge_dispatcher_host.h index 615c2b03770af..48fcbb588bbfa 100644 --- a/content/browser/android/java/gin_java_bridge_dispatcher_host.h +++ b/content/browser/android/java/gin_java_bridge_dispatcher_host.h @@ -76,20 +76,15 @@ class GinJavaBridgeDispatcherHost GinJavaBoundObject::ObjectID object_id); bool IsValidRenderFrameHost(RenderFrameHost* render_frame_host); - void SendReply(RenderFrameHost* render_frame_host, IPC::Message* reply_msg); void SendMethods(RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, const std::set& method_names); void SendHasMethodReply(RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, bool result); void ProcessMethodInvocationResult( RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, scoped_refptr result); void ProcessMethodInvocationObjectResult( RenderFrameHost* render_frame_host, - IPC::Message* reply_msg, scoped_refptr result); GinJavaBoundObject::ObjectID AddObject( const base::android::JavaRef& object, @@ -101,6 +96,8 @@ class GinJavaBridgeDispatcherHost void RemoveHolder(RenderFrameHost* holder, const GinJavaBoundObject::ObjectMap::iterator& from, size_t count); + bool HasPendingReply(RenderFrameHost* render_frame_host) const; + IPC::Message* TakePendingReply(RenderFrameHost* render_frame_host); // Every time a GinJavaBoundObject backed by a real Java object is // created/destroyed, we insert/remove a strong ref to that Java object into @@ -114,6 +111,13 @@ class GinJavaBridgeDispatcherHost typedef std::map NamedObjectMap; NamedObjectMap named_objects_; + // Keep track of pending calls out to Java such that we can send a synchronous + // reply to the renderer waiting on the response should the RenderFrame be + // destroyed while the reply is pending. + // Only used on the UI thread. + typedef std::map PendingReplyMap; + PendingReplyMap pending_replies_; + DISALLOW_COPY_AND_ASSIGN(GinJavaBridgeDispatcherHost); }; diff --git a/content/browser/android/java/jni_helper.cc b/content/browser/android/java/jni_helper.cc index 09026e77952a3..f83b30835393a 100644 --- a/content/browser/android/java/jni_helper.cc +++ b/content/browser/android/java/jni_helper.cc @@ -48,6 +48,16 @@ base::LazyInstance g_method_id_map = LAZY_INSTANCE_INITIALIZER; } // namespace +jfieldID GetFieldID(JNIEnv* env, + const base::android::JavaRef& clazz, + const char* field_name, + const char* jni_signature) { + jfieldID field_id = env->GetFieldID(clazz.obj(), field_name, jni_signature); + CHECK(!base::android::ClearException(env) && field_id) << + "Failed to find field " << field_name << " " << jni_signature; + return field_id; +} + jmethodID GetMethodIDFromClassName(JNIEnv* env, const char* class_name, const char* method, diff --git a/content/browser/android/java/jni_helper.h b/content/browser/android/java/jni_helper.h index 25c851598fb20..84f837d98bf5d 100644 --- a/content/browser/android/java/jni_helper.h +++ b/content/browser/android/java/jni_helper.h @@ -7,6 +7,7 @@ #include +#include "base/android/scoped_java_ref.h" #include "content/common/content_export.h" namespace content { @@ -22,6 +23,13 @@ CONTENT_EXPORT jmethodID GetMethodIDFromClassName(JNIEnv* env, const char* method, const char* jni_signature); +// Gets the field ID for a class field. +// This method triggers a fatal assertion if the field could not be found. +CONTENT_EXPORT jfieldID GetFieldID(JNIEnv* env, + const base::android::JavaRef& clazz, + const char* field_name, + const char* jni_signature); + } // namespace content #endif // CONTENT_BROWSER_ANDROID_JAVA_JNI_HELPER_H_ diff --git a/content/browser/android/overscroll_glow.cc b/content/browser/android/overscroll_glow.cc index 3f316d8aee622..b3a969ff85f74 100644 --- a/content/browser/android/overscroll_glow.cc +++ b/content/browser/android/overscroll_glow.cc @@ -4,10 +4,8 @@ #include "content/browser/android/overscroll_glow.h" -#include "base/debug/trace_event.h" -#include "base/lazy_instance.h" #include "cc/layers/layer.h" -#include "content/browser/android/edge_effect.h" +#include "content/browser/android/edge_effect_base.h" using std::max; using std::min; @@ -17,8 +15,6 @@ namespace content { namespace { const float kEpsilon = 1e-3f; -const float kEdgeHeightAtMdpi = 12.f; -const float kGlowHeightAtMdpi = 128.f; bool IsApproxZero(float value) { return std::abs(value) < kEpsilon; @@ -32,17 +28,58 @@ gfx::Vector2dF ZeroSmallComponents(gfx::Vector2dF vector) { return vector; } -} // namespace +gfx::Transform ComputeTransform(OverscrollGlow::Edge edge, + const gfx::SizeF& window_size, + float offset) { + // Transforms assume the edge layers are anchored to their *top center point*. + switch (edge) { + case OverscrollGlow::EDGE_TOP: + return gfx::Transform(1, 0, 0, 1, 0, offset); + case OverscrollGlow::EDGE_LEFT: + return gfx::Transform(0, + 1, + -1, + 0, + -window_size.height() / 2.f + offset, + window_size.height() / 2.f); + case OverscrollGlow::EDGE_BOTTOM: + return gfx::Transform(-1, 0, 0, -1, 0, window_size.height() + offset); + case OverscrollGlow::EDGE_RIGHT: + return gfx::Transform( + 0, + -1, + 1, + 0, + -window_size.height() / 2.f + window_size.width() + offset, + window_size.height() / 2.f); + default: + NOTREACHED() << "Invalid edge: " << edge; + return gfx::Transform(); + }; +} -scoped_ptr OverscrollGlow::Create( - ui::SystemUIResourceManager* resource_manager) { - return make_scoped_ptr(new OverscrollGlow(resource_manager)); +gfx::SizeF ComputeSize(OverscrollGlow::Edge edge, + const gfx::SizeF& window_size) { + switch (edge) { + case OverscrollGlow::EDGE_TOP: + case OverscrollGlow::EDGE_BOTTOM: + return window_size; + case OverscrollGlow::EDGE_LEFT: + case OverscrollGlow::EDGE_RIGHT: + return gfx::SizeF(window_size.height(), window_size.width()); + default: + NOTREACHED() << "Invalid edge: " << edge; + return gfx::SizeF(); + }; } -OverscrollGlow::OverscrollGlow(ui::SystemUIResourceManager* resource_manager) - : enabled_(true), initialized_(false), resource_manager_(resource_manager) { - DCHECK(resource_manager_); - EdgeEffect::PreloadResources(resource_manager_); +} // namespace + +OverscrollGlow::OverscrollGlow(const EdgeEffectProvider& edge_effect_provider) + : edge_effect_provider_(edge_effect_provider), + enabled_(true), + initialized_(false) { + DCHECK(!edge_effect_provider_.is_null()); } OverscrollGlow::~OverscrollGlow() { @@ -59,7 +96,7 @@ void OverscrollGlow::Disable() { enabled_ = false; if (!enabled_ && initialized_) { Detach(); - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) + for (size_t i = 0; i < EDGE_COUNT; ++i) edge_effects_[i]->Finish(); } } @@ -68,7 +105,8 @@ bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer, base::TimeTicks current_time, gfx::Vector2dF accumulated_overscroll, gfx::Vector2dF overscroll_delta, - gfx::Vector2dF velocity) { + gfx::Vector2dF velocity, + gfx::Vector2dF displacement) { DCHECK(overscrolling_layer); if (!enabled_) @@ -98,16 +136,11 @@ bool OverscrollGlow::OnOverscrolled(cc::Layer* overscrolling_layer, bool y_overscroll_started = !IsApproxZero(overscroll_delta.y()) && IsApproxZero(old_overscroll.y()); - if (x_overscroll_started) - ReleaseAxis(AXIS_X, current_time); - if (y_overscroll_started) - ReleaseAxis(AXIS_Y, current_time); - velocity = ZeroSmallComponents(velocity); if (!velocity.IsZero()) Absorb(current_time, velocity, x_overscroll_started, y_overscroll_started); else - Pull(current_time, overscroll_delta); + Pull(current_time, overscroll_delta, displacement); UpdateLayerAttachment(overscrolling_layer); return NeedsAnimate(); @@ -119,14 +152,13 @@ bool OverscrollGlow::Animate(base::TimeTicks current_time) { return false; } - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { + for (size_t i = 0; i < EDGE_COUNT; ++i) { if (edge_effects_[i]->Update(current_time)) { + Edge edge = static_cast(i); edge_effects_[i]->ApplyToLayers( - display_params_.size, - static_cast(i), - kEdgeHeightAtMdpi * display_params_.device_scale_factor, - kGlowHeightAtMdpi * display_params_.device_scale_factor, - display_params_.edge_offsets[i]); + ComputeSize(edge, display_params_.size), + ComputeTransform( + edge, display_params_.size, display_params_.edge_offsets[i])); } } @@ -145,7 +177,7 @@ void OverscrollGlow::UpdateDisplayParameters(const DisplayParameters& params) { bool OverscrollGlow::NeedsAnimate() const { if (!enabled_ || !initialized_) return false; - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { + for (size_t i = 0; i < EDGE_COUNT; ++i) { if (!edge_effects_[i]->IsFinished()) return true; } @@ -165,7 +197,7 @@ void OverscrollGlow::UpdateLayerAttachment(cc::Layer* parent) { if (root_layer_->parent() != parent) parent->AddChild(root_layer_); - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) + for (size_t i = 0; i < EDGE_COUNT; ++i) edge_effects_[i]->SetParent(root_layer_); } @@ -181,57 +213,69 @@ bool OverscrollGlow::InitializeIfNecessary() { DCHECK(!root_layer_); root_layer_ = cc::Layer::Create(); - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) - edge_effects_[i] = make_scoped_ptr(new EdgeEffect(resource_manager_)); + for (size_t i = 0; i < EDGE_COUNT; ++i) { + edge_effects_[i] = edge_effect_provider_.Run(); + DCHECK(edge_effects_[i]); + } initialized_ = true; return true; } void OverscrollGlow::Pull(base::TimeTicks current_time, - gfx::Vector2dF overscroll_delta) { + const gfx::Vector2dF& overscroll_delta, + const gfx::Vector2dF& overscroll_location) { DCHECK(enabled_ && initialized_); - overscroll_delta = ZeroSmallComponents(overscroll_delta); - if (overscroll_delta.IsZero()) - return; + DCHECK(!overscroll_delta.IsZero()); + const float inv_width = 1.f / display_params_.size.width(); + const float inv_height = 1.f / display_params_.size.height(); gfx::Vector2dF overscroll_pull = - gfx::ScaleVector2d(overscroll_delta, - 1.f / display_params_.size.width(), - 1.f / display_params_.size.height()); - float edge_overscroll_pull[EdgeEffect::EDGE_COUNT] = { + gfx::ScaleVector2d(overscroll_delta, inv_width, inv_height); + const float edge_pull[EDGE_COUNT] = { min(overscroll_pull.y(), 0.f), // Top min(overscroll_pull.x(), 0.f), // Left max(overscroll_pull.y(), 0.f), // Bottom max(overscroll_pull.x(), 0.f) // Right }; - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { - if (!edge_overscroll_pull[i]) + gfx::Vector2dF displacement = + gfx::ScaleVector2d(overscroll_location, inv_width, inv_height); + displacement.set_x(max(0.f, min(1.f, displacement.x()))); + displacement.set_y(max(0.f, min(1.f, displacement.y()))); + const float edge_displacement[EDGE_COUNT] = { + 1.f - displacement.x(), // Top + displacement.y(), // Left + displacement.x(), // Bottom + 1.f - displacement.y() // Right + }; + + for (size_t i = 0; i < EDGE_COUNT; ++i) { + if (!edge_pull[i]) continue; - edge_effects_[i]->Pull(current_time, std::abs(edge_overscroll_pull[i])); + edge_effects_[i]->Pull( + current_time, std::abs(edge_pull[i]), edge_displacement[i]); GetOppositeEdge(i)->Release(current_time); } } void OverscrollGlow::Absorb(base::TimeTicks current_time, - gfx::Vector2dF velocity, + const gfx::Vector2dF& velocity, bool x_overscroll_started, bool y_overscroll_started) { DCHECK(enabled_ && initialized_); - if (velocity.IsZero()) - return; + DCHECK(!velocity.IsZero()); // Only trigger on initial overscroll at a non-zero velocity - const float overscroll_velocities[EdgeEffect::EDGE_COUNT] = { + const float overscroll_velocities[EDGE_COUNT] = { y_overscroll_started ? min(velocity.y(), 0.f) : 0, // Top x_overscroll_started ? min(velocity.x(), 0.f) : 0, // Left y_overscroll_started ? max(velocity.y(), 0.f) : 0, // Bottom x_overscroll_started ? max(velocity.x(), 0.f) : 0 // Right }; - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) { + for (size_t i = 0; i < EDGE_COUNT; ++i) { if (!overscroll_velocities[i]) continue; @@ -242,31 +286,16 @@ void OverscrollGlow::Absorb(base::TimeTicks current_time, void OverscrollGlow::Release(base::TimeTicks current_time) { DCHECK(initialized_); - for (size_t i = 0; i < EdgeEffect::EDGE_COUNT; ++i) + for (size_t i = 0; i < EDGE_COUNT; ++i) edge_effects_[i]->Release(current_time); } -void OverscrollGlow::ReleaseAxis(Axis axis, base::TimeTicks current_time) { - DCHECK(initialized_); - switch (axis) { - case AXIS_X: - edge_effects_[EdgeEffect::EDGE_LEFT]->Release(current_time); - edge_effects_[EdgeEffect::EDGE_RIGHT]->Release(current_time); - break; - case AXIS_Y: - edge_effects_[EdgeEffect::EDGE_TOP]->Release(current_time); - edge_effects_[EdgeEffect::EDGE_BOTTOM]->Release(current_time); - break; - }; -} - -EdgeEffect* OverscrollGlow::GetOppositeEdge(int edge_index) { +EdgeEffectBase* OverscrollGlow::GetOppositeEdge(int edge_index) { DCHECK(initialized_); - return edge_effects_[(edge_index + 2) % EdgeEffect::EDGE_COUNT].get(); + return edge_effects_[(edge_index + 2) % EDGE_COUNT].get(); } -OverscrollGlow::DisplayParameters::DisplayParameters() - : device_scale_factor(1) { +OverscrollGlow::DisplayParameters::DisplayParameters() { edge_offsets[0] = edge_offsets[1] = edge_offsets[2] = edge_offsets[3] = 0.f; } diff --git a/content/browser/android/overscroll_glow.h b/content/browser/android/overscroll_glow.h index 7500940538873..878d39aec1288 100644 --- a/content/browser/android/overscroll_glow.h +++ b/content/browser/android/overscroll_glow.h @@ -5,32 +5,36 @@ #ifndef CONTENT_BROWSER_ANDROID_OVERSCROLL_GLOW_H_ #define CONTENT_BROWSER_ANDROID_OVERSCROLL_GLOW_H_ +#include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" -#include "content/browser/android/edge_effect.h" #include "ui/gfx/size_f.h" #include "ui/gfx/vector2d_f.h" -class SkBitmap; - -namespace ui { -class SystemUIResourceManager; +namespace cc { +class Layer; } namespace content { +class EdgeEffectBase; + /* |OverscrollGlow| mirrors its Android counterpart, OverscrollGlow.java. * Conscious tradeoffs were made to align this as closely as possible with the * original Android Java version. */ class OverscrollGlow { public: - // Create a new effect. |resource_manager| provides the resource for the - // effect. |resource_manager| must outlive the effect. The effect is - // activated by default. - static scoped_ptr Create( - ui::SystemUIResourceManager* resource_manager); + enum Edge { EDGE_TOP = 0, EDGE_LEFT, EDGE_BOTTOM, EDGE_RIGHT, EDGE_COUNT }; + + // Allows lazy creation of the edge effects. + typedef base::Callback(void)> EdgeEffectProvider; + + // |edge_effect_provider| must be valid for the duration of the effect's + // lifetime. The effect is enabled by default, but will remain dormant until + // the first overscroll event. + explicit OverscrollGlow(const EdgeEffectProvider& edge_effect_provider); ~OverscrollGlow(); @@ -50,7 +54,8 @@ class OverscrollGlow { base::TimeTicks current_time, gfx::Vector2dF accumulated_overscroll, gfx::Vector2dF overscroll_delta, - gfx::Vector2dF velocity); + gfx::Vector2dF velocity, + gfx::Vector2dF overscroll_location); // Returns true if the effect still needs animation ticks. // Note: The effect will detach itself when no further animation is required. @@ -61,40 +66,37 @@ class OverscrollGlow { struct DisplayParameters { DisplayParameters(); gfx::SizeF size; - float edge_offsets[EdgeEffect::EDGE_COUNT]; - float device_scale_factor; + float edge_offsets[EDGE_COUNT]; }; void UpdateDisplayParameters(const DisplayParameters& params); - private: enum Axis { AXIS_X, AXIS_Y }; - explicit OverscrollGlow(ui::SystemUIResourceManager* resource_manager); - // Returns whether the effect is initialized. bool InitializeIfNecessary(); bool NeedsAnimate() const; void UpdateLayerAttachment(cc::Layer* parent); void Detach(); - void Pull(base::TimeTicks current_time, gfx::Vector2dF overscroll_delta); + void Pull(base::TimeTicks current_time, + const gfx::Vector2dF& overscroll_delta, + const gfx::Vector2dF& overscroll_location); void Absorb(base::TimeTicks current_time, - gfx::Vector2dF velocity, + const gfx::Vector2dF& velocity, bool x_overscroll_started, bool y_overscroll_started); void Release(base::TimeTicks current_time); - void ReleaseAxis(Axis axis, base::TimeTicks current_time); - EdgeEffect* GetOppositeEdge(int edge_index); + EdgeEffectBase* GetOppositeEdge(int edge_index); - scoped_ptr edge_effects_[EdgeEffect::EDGE_COUNT]; + EdgeEffectProvider edge_effect_provider_; + scoped_ptr edge_effects_[EDGE_COUNT]; DisplayParameters display_params_; bool enabled_; bool initialized_; scoped_refptr root_layer_; - ui::SystemUIResourceManager* resource_manager_; DISALLOW_COPY_AND_ASSIGN(OverscrollGlow); }; diff --git a/content/browser/android/system_ui_resource_manager_impl.cc b/content/browser/android/system_ui_resource_manager_impl.cc index 6da221b888a53..81f12fc3a70d5 100644 --- a/content/browser/android/system_ui_resource_manager_impl.cc +++ b/content/browser/android/system_ui_resource_manager_impl.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/debug/trace_event.h" #include "base/location.h" #include "base/observer_list.h" #include "base/threading/worker_pool.h" @@ -13,9 +14,69 @@ #include "content/public/browser/android/ui_resource_client_android.h" #include "content/public/browser/android/ui_resource_provider.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/effects/SkPorterDuff.h" #include "ui/gfx/android/java_bitmap.h" +#include "ui/gfx/screen.h" namespace content { +namespace { + +SkBitmap CreateOverscrollGlowLBitmap(const gfx::Size& screen_size) { + const float kSin = 0.5f; // sin(PI / 6) + const float kCos = 0.866f; // cos(PI / 6); + + SkPaint paint; + paint.setAntiAlias(true); + paint.setAlpha(0x33); + paint.setStyle(SkPaint::kFill_Style); + + const float arc_width = + std::min(screen_size.width(), screen_size.height()) * 0.5f / kSin; + const float y = kCos * arc_width; + const float height = arc_width - y; + gfx::Size bounds(arc_width, height); + SkRect arc_rect = SkRect::MakeXYWH( + -arc_width / 2.f, -arc_width - y, arc_width * 2.f, arc_width * 2.f); + SkBitmap glow_bitmap; + if (!glow_bitmap.allocPixels( + SkImageInfo::MakeA8(bounds.width(), bounds.height()))) { + LOG(FATAL) << " Failed to allocate bitmap of size " << bounds.width() << "x" + << bounds.height(); + } + glow_bitmap.eraseColor(SK_ColorTRANSPARENT); + + SkCanvas canvas(glow_bitmap); + canvas.clipRect(SkRect::MakeXYWH(0, 0, bounds.width(), bounds.height())); + canvas.drawArc(arc_rect, 45, 90, true, paint); + return glow_bitmap; +} + +void LoadBitmap(ui::SystemUIResourceManager::ResourceType type, + SkBitmap* bitmap_holder, + const gfx::Size& screen_size) { + TRACE_EVENT1( + "browser", "SystemUIResourceManagerImpl::LoadBitmap", "type", type); + SkBitmap bitmap; + switch (type) { + case ui::SystemUIResourceManager::OVERSCROLL_EDGE: + bitmap = gfx::CreateSkBitmapFromAndroidResource( + "android:drawable/overscroll_edge", gfx::Size(128, 12)); + break; + case ui::SystemUIResourceManager::OVERSCROLL_GLOW: + bitmap = gfx::CreateSkBitmapFromAndroidResource( + "android:drawable/overscroll_glow", gfx::Size(128, 64)); + break; + case ui::SystemUIResourceManager::OVERSCROLL_GLOW_L: + bitmap = CreateOverscrollGlowLBitmap(screen_size); + break; + } + bitmap.setImmutable(); + *bitmap_holder = bitmap; +} + +} // namespace class SystemUIResourceManagerImpl::Entry : public content::UIResourceClientAndroid { @@ -31,8 +92,8 @@ class SystemUIResourceManagerImpl::Entry } void SetBitmap(const SkBitmap& bitmap) { - DCHECK(bitmap_.empty()); DCHECK(!bitmap.empty()); + DCHECK(bitmap_.empty()); DCHECK(!id_); bitmap_ = bitmap; } @@ -99,8 +160,10 @@ void SystemUIResourceManagerImpl::BuildResource(ResourceType type) { // Instead of blocking the main thread, we post a task to load the bitmap. SkBitmap* bitmap = new SkBitmap(); + gfx::Size screen_size = + gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().GetSizeInPixel(); base::Closure load_bitmap = - base::Bind(&SystemUIResourceManagerImpl::LoadBitmap, type, bitmap); + base::Bind(&LoadBitmap, type, bitmap, screen_size); base::Closure finished_load = base::Bind(&SystemUIResourceManagerImpl::OnFinishedLoadBitmap, weak_factory_.GetWeakPtr(), @@ -110,23 +173,6 @@ void SystemUIResourceManagerImpl::BuildResource(ResourceType type) { FROM_HERE, load_bitmap, finished_load, true); } -void SystemUIResourceManagerImpl::LoadBitmap(ResourceType type, - SkBitmap* bitmap_holder) { - SkBitmap bitmap; - switch (type) { - case ui::SystemUIResourceManager::OVERSCROLL_EDGE: - bitmap = gfx::CreateSkBitmapFromAndroidResource( - "android:drawable/overscroll_edge", gfx::Size(128, 12)); - break; - case ui::SystemUIResourceManager::OVERSCROLL_GLOW: - bitmap = gfx::CreateSkBitmapFromAndroidResource( - "android:drawable/overscroll_glow", gfx::Size(128, 64)); - break; - } - bitmap.setImmutable(); - *bitmap_holder = bitmap; -} - void SystemUIResourceManagerImpl::OnFinishedLoadBitmap( ResourceType type, SkBitmap* bitmap_holder) { diff --git a/content/browser/android/system_ui_resource_manager_impl.h b/content/browser/android/system_ui_resource_manager_impl.h index c5792a19f7180..277f9f0956831 100644 --- a/content/browser/android/system_ui_resource_manager_impl.h +++ b/content/browser/android/system_ui_resource_manager_impl.h @@ -39,7 +39,6 @@ class CONTENT_EXPORT SystemUIResourceManagerImpl virtual void BuildResource(ResourceType type); Entry* GetEntry(ResourceType type); - static void LoadBitmap(ResourceType, SkBitmap* bitmap_holder); void OnFinishedLoadBitmap(ResourceType, SkBitmap* bitmap_holder); scoped_ptr resource_map_[RESOURCE_TYPE_LAST + 1]; diff --git a/content/browser/appcache/appcache_database.cc b/content/browser/appcache/appcache_database.cc index 546c1dcef8a4a..0783efa335036 100644 --- a/content/browser/appcache/appcache_database.cc +++ b/content/browser/appcache/appcache_database.cc @@ -177,7 +177,8 @@ bool CreateIndex(sql::Connection* db, const IndexInfo& info) { } std::string GetActiveExperimentFlags() { - if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers)) + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + kEnableExecutableHandlers)) return std::string("executableHandlersEnabled"); return std::string(); } @@ -712,7 +713,7 @@ bool AppCacheDatabase::InsertNamespace( int type_with_executable_bit = record->namespace_.type; if (record->namespace_.is_executable) { type_with_executable_bit |= 0x8000000; - DCHECK(CommandLine::ForCurrentProcess()->HasSwitch( + DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch( kEnableExecutableHandlers)); } @@ -976,7 +977,8 @@ void AppCacheDatabase::ReadNamespaceRecord( record->namespace_.is_executable = (type_with_executable_bit & 0x80000000) != 0; DCHECK(!record->namespace_.is_executable || - CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers)); + base::CommandLine::ForCurrentProcess()->HasSwitch( + kEnableExecutableHandlers)); } void AppCacheDatabase::ReadOnlineWhiteListRecord( diff --git a/content/browser/appcache/appcache_storage.h b/content/browser/appcache/appcache_storage.h index f557bbd72c164..edc059b9381f8 100644 --- a/content/browser/appcache/appcache_storage.h +++ b/content/browser/appcache/appcache_storage.h @@ -214,7 +214,8 @@ class CONTENT_EXPORT AppCacheStorage { // Helper used to manage multiple references to a 'delegate' and to // allow all pending callbacks to that delegate to be easily cancelled. - struct DelegateReference : public base::RefCounted { + struct CONTENT_EXPORT DelegateReference : + public base::RefCounted { Delegate* delegate; AppCacheStorage* storage; diff --git a/content/browser/appcache/appcache_update_job.cc b/content/browser/appcache/appcache_update_job.cc index 347d4b5c84853..351bd80aade6b 100644 --- a/content/browser/appcache/appcache_update_job.cc +++ b/content/browser/appcache/appcache_update_job.cc @@ -140,7 +140,9 @@ void AppCacheUpdateJob::URLFetcher::Start() { } void AppCacheUpdateJob::URLFetcher::OnReceivedRedirect( - net::URLRequest* request, const GURL& new_url, bool* defer_redirect) { + net::URLRequest* request, + const net::RedirectInfo& redirect_info, + bool* defer_redirect) { DCHECK(request_ == request); // Redirect is not allowed by the update process. job_->MadeProgress(); diff --git a/content/browser/appcache/appcache_update_job.h b/content/browser/appcache/appcache_update_job.h index a2c4df850610f..4da4ac2dac7c6 100644 --- a/content/browser/appcache/appcache_update_job.h +++ b/content/browser/appcache/appcache_update_job.h @@ -135,7 +135,7 @@ class CONTENT_EXPORT AppCacheUpdateJob private: // URLRequest::Delegate overrides virtual void OnReceivedRedirect(net::URLRequest* request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE; virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE; virtual void OnReadCompleted(net::URLRequest* request, diff --git a/content/browser/appcache/manifest_parser.cc b/content/browser/appcache/manifest_parser.cc index fb63f22b173fe..2ca3ce85286e5 100644 --- a/content/browser/appcache/manifest_parser.cc +++ b/content/browser/appcache/manifest_parser.cc @@ -272,7 +272,7 @@ bool ParseManifest(const GURL& manifest_url, const char* data, int length, if (type == L"return") { verb = RETURN; } else if (type == L"execute" && - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( kEnableExecutableHandlers)) { verb = EXECUTE; } diff --git a/content/browser/battery_status/battery_status_manager_linux.cc b/content/browser/battery_status/battery_status_manager_linux.cc new file mode 100644 index 0000000000000..e6abf88ed4eb1 --- /dev/null +++ b/content/browser/battery_status/battery_status_manager_linux.cc @@ -0,0 +1,374 @@ +// 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 "content/browser/battery_status/battery_status_manager_linux.h" + +#include "base/macros.h" +#include "base/threading/thread.h" +#include "base/values.h" +#include "content/browser/battery_status/battery_status_manager.h" +#include "content/public/browser/browser_thread.h" +#include "dbus/bus.h" +#include "dbus/message.h" +#include "dbus/object_path.h" +#include "dbus/object_proxy.h" +#include "dbus/property.h" +#include "dbus/values_util.h" + +namespace content { + +namespace { + +const char kUPowerServiceName[] = "org.freedesktop.UPower"; +const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device"; +const char kUPowerPath[] = "/org/freedesktop/UPower"; +const char kUPowerDeviceSignalChanged[] = "Changed"; +const char kUPowerEnumerateDevices[] = "EnumerateDevices"; +const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier"; + +// UPowerDeviceType reflects the possible UPower.Device.Type values, +// see upower.freedesktop.org/docs/Device.html#Device:Type. +enum UPowerDeviceType { + UPOWER_DEVICE_TYPE_UNKNOWN = 0, + UPOWER_DEVICE_TYPE_LINE_POWER = 1, + UPOWER_DEVICE_TYPE_BATTERY = 2, + UPOWER_DEVICE_TYPE_UPS = 3, + UPOWER_DEVICE_TYPE_MONITOR = 4, + UPOWER_DEVICE_TYPE_MOUSE = 5, + UPOWER_DEVICE_TYPE_KEYBOARD = 6, + UPOWER_DEVICE_TYPE_PDA = 7, + UPOWER_DEVICE_TYPE_PHONE = 8, +}; + +typedef std::vector PathsVector; + +double GetPropertyAsDouble(const base::DictionaryValue& dictionary, + const std::string& property_name, + double default_value) { + double value = default_value; + return dictionary.GetDouble(property_name, &value) ? value : default_value; +} + +bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary, + const std::string& property_name, + bool default_value) { + bool value = default_value; + return dictionary.GetBoolean(property_name, &value) ? value : default_value; +} + +scoped_ptr GetPropertiesAsDictionary( + dbus::ObjectProxy* proxy) { + dbus::MethodCall method_call(dbus::kPropertiesInterface, + dbus::kPropertiesGetAll); + dbus::MessageWriter builder(&method_call); + builder.AppendString(kUPowerDeviceName); + + scoped_ptr response( + proxy->CallMethodAndBlock(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + if (response) { + dbus::MessageReader reader(response.get()); + scoped_ptr value(dbus::PopDataAsValue(&reader)); + base::DictionaryValue* dictionary_value = NULL; + if (value && value->GetAsDictionary(&dictionary_value)) { + ignore_result(value.release()); + return scoped_ptr(dictionary_value); + } + } + return scoped_ptr(); +} + +scoped_ptr GetPowerSourcesPaths(dbus::ObjectProxy* proxy) { + scoped_ptr paths(new PathsVector()); + if (!proxy) + return paths.Pass(); + + dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices); + scoped_ptr response( + proxy->CallMethodAndBlock(&method_call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); + + if (response) { + dbus::MessageReader reader(response.get()); + reader.PopArrayOfObjectPaths(paths.get()); + } + return paths.Pass();; +} + +// Class that represents a dedicated thread which communicates with DBus to +// obtain battery information and receives battery change notifications. +class BatteryStatusNotificationThread : public base::Thread { + public: + BatteryStatusNotificationThread( + const BatteryStatusService::BatteryUpdateCallback& callback) + : base::Thread(kBatteryNotifierThreadName), + callback_(callback), + battery_proxy_(NULL) {} + + virtual ~BatteryStatusNotificationThread() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // Make sure to shutdown the dbus connection if it is still open in the very + // end. It needs to happen on the BatteryStatusNotificationThread. + message_loop()->PostTask( + FROM_HERE, + base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection, + base::Unretained(this))); + + // Drain the message queue of the BatteryStatusNotificationThread and stop. + Stop(); + } + + void StartListening() { + DCHECK(OnWatcherThread()); + + if (system_bus_) + return; + + InitDBus(); + dbus::ObjectProxy* power_proxy = + system_bus_->GetObjectProxy(kUPowerServiceName, + dbus::ObjectPath(kUPowerPath)); + scoped_ptr device_paths = GetPowerSourcesPaths(power_proxy); + + for (size_t i = 0; i < device_paths->size(); ++i) { + const dbus::ObjectPath& device_path = device_paths->at(i); + dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy( + kUPowerServiceName, device_path); + scoped_ptr dictionary = + GetPropertiesAsDictionary(device_proxy); + + if (!dictionary) + continue; + + bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false); + uint32 type = static_cast( + GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN)); + + if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) { + system_bus_->RemoveObjectProxy(kUPowerServiceName, + device_path, + base::Bind(&base::DoNothing)); + continue; + } + + if (battery_proxy_) { + // TODO(timvolodine): add support for multiple batteries. Currently we + // only collect information from the first battery we encounter + // (crbug.com/400780). + // TODO(timvolodine): add UMA logging for this case. + LOG(WARNING) << "multiple batteries found, " + << "using status data of the first battery only."; + } else { + battery_proxy_ = device_proxy; + } + } + + if (!battery_proxy_) { + callback_.Run(blink::WebBatteryStatus()); + return; + } + + battery_proxy_->ConnectToSignal( + kUPowerDeviceName, + kUPowerDeviceSignalChanged, + base::Bind(&BatteryStatusNotificationThread::BatteryChanged, + base::Unretained(this)), + base::Bind(&BatteryStatusNotificationThread::OnSignalConnected, + base::Unretained(this))); + } + + void StopListening() { + DCHECK(OnWatcherThread()); + ShutdownDBusConnection(); + } + + private: + bool OnWatcherThread() { + return task_runner()->BelongsToCurrentThread(); + } + + void InitDBus() { + DCHECK(OnWatcherThread()); + + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SYSTEM; + options.connection_type = dbus::Bus::PRIVATE; + system_bus_ = new dbus::Bus(options); + } + + void ShutdownDBusConnection() { + DCHECK(OnWatcherThread()); + + if (!system_bus_) + return; + + // Shutdown DBus connection later because there may be pending tasks on + // this thread. + message_loop()->PostTask(FROM_HERE, + base::Bind(&dbus::Bus::ShutdownAndBlock, + system_bus_)); + system_bus_ = NULL; + battery_proxy_ = NULL; + } + + void OnSignalConnected(const std::string& interface_name, + const std::string& signal_name, + bool success) { + DCHECK(OnWatcherThread()); + + if (interface_name != kUPowerDeviceName || + signal_name != kUPowerDeviceSignalChanged) { + return; + } + + if (!system_bus_) + return; + + if (success) { + BatteryChanged(NULL); + } else { + // Failed to register for "Changed" signal, execute callback with the + // default values. + callback_.Run(blink::WebBatteryStatus()); + } + } + + void BatteryChanged(dbus::Signal* signal /* unsused */) { + DCHECK(OnWatcherThread()); + + if (!system_bus_) + return; + + scoped_ptr dictionary = + GetPropertiesAsDictionary(battery_proxy_); + if (dictionary) + callback_.Run(ComputeWebBatteryStatus(*dictionary)); + else + callback_.Run(blink::WebBatteryStatus()); + } + + BatteryStatusService::BatteryUpdateCallback callback_; + scoped_refptr system_bus_; + dbus::ObjectProxy* battery_proxy_; // owned by the bus + + DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); +}; + +// Runs on IO thread and creates a notification thread and delegates Start/Stop +// calls to it. +class BatteryStatusManagerLinux : public BatteryStatusManager { + public: + explicit BatteryStatusManagerLinux( + const BatteryStatusService::BatteryUpdateCallback& callback) + : callback_(callback) {} + + virtual ~BatteryStatusManagerLinux() {} + + private: + // BatteryStatusManager: + virtual bool StartListeningBatteryChange() OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!StartNotifierThreadIfNecessary()) + return false; + + notifier_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&BatteryStatusNotificationThread::StartListening, + base::Unretained(notifier_thread_.get()))); + return true; + } + + virtual void StopListeningBatteryChange() OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!notifier_thread_) + return; + + notifier_thread_->message_loop()->PostTask( + FROM_HERE, + base::Bind(&BatteryStatusNotificationThread::StopListening, + base::Unretained(notifier_thread_.get()))); + } + + // Starts the notifier thread if not already started and returns true on + // success. + bool StartNotifierThreadIfNecessary() { + if (notifier_thread_) + return true; + + base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0); + notifier_thread_.reset(new BatteryStatusNotificationThread(callback_)); + if (!notifier_thread_->StartWithOptions(thread_options)) { + notifier_thread_.reset(); + LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName + << " thread"; + return false; + } + return true; + } + + BatteryStatusService::BatteryUpdateCallback callback_; + scoped_ptr notifier_thread_; + + DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux); +}; + +} // namespace + +blink::WebBatteryStatus ComputeWebBatteryStatus( + const base::DictionaryValue& dictionary) { + blink::WebBatteryStatus status; + if (!dictionary.HasKey("State")) + return status; + + uint32 state = static_cast( + GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN)); + status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING && + state != UPOWER_DEVICE_STATE_EMPTY; + double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100); + // Convert percentage to a value between 0 and 1 with 2 digits of precision. + // This is to bring it in line with other platforms like Mac and Android where + // we report level with 1% granularity. It also serves the purpose of reducing + // the possibility of fingerprinting and triggers less level change events on + // the blink side. + // TODO(timvolodine): consider moving this rounding to the blink side. + status.level = round(percentage) / 100.f; + + switch (state) { + case UPOWER_DEVICE_STATE_CHARGING : { + double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0); + status.chargingTime = + (time_to_full > 0) ? time_to_full + : std::numeric_limits::infinity(); + break; + } + case UPOWER_DEVICE_STATE_DISCHARGING : { + double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0); + // Set dischargingTime if it's available. Otherwise leave the default + // value which is +infinity. + if (time_to_empty > 0) + status.dischargingTime = time_to_empty; + status.chargingTime = std::numeric_limits::infinity(); + break; + } + case UPOWER_DEVICE_STATE_FULL : { + break; + } + default: { + status.chargingTime = std::numeric_limits::infinity(); + } + } + return status; +} + +// static +scoped_ptr BatteryStatusManager::Create( + const BatteryStatusService::BatteryUpdateCallback& callback) { + return scoped_ptr( + new BatteryStatusManagerLinux(callback)); +} + +} // namespace content diff --git a/content/browser/battery_status/battery_status_manager_linux.h b/content/browser/battery_status/battery_status_manager_linux.h new file mode 100644 index 0000000000000..5bce494743e35 --- /dev/null +++ b/content/browser/battery_status/battery_status_manager_linux.h @@ -0,0 +1,34 @@ +// 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 CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_LINUX_H_ +#define CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_LINUX_H_ + +#include "content/common/content_export.h" +#include "third_party/WebKit/public/platform/WebBatteryStatus.h" + +namespace base { +class DictionaryValue; +} + +namespace content { + +// UPowerDeviceState reflects the possible UPower.Device.State values, +// see upower.freedesktop.org/docs/Device.html#Device:State. +enum UPowerDeviceState { + UPOWER_DEVICE_STATE_UNKNOWN = 0, + UPOWER_DEVICE_STATE_CHARGING = 1, + UPOWER_DEVICE_STATE_DISCHARGING = 2, + UPOWER_DEVICE_STATE_EMPTY = 3, + UPOWER_DEVICE_STATE_FULL = 4, + UPOWER_DEVICE_STATE_PENDING_CHARGE = 5, + UPOWER_DEVICE_STATE_PENDING_DISCHARGE = 6, +}; + +// Returns the WebBatteryStatus computed using the provided dictionary. +CONTENT_EXPORT blink::WebBatteryStatus ComputeWebBatteryStatus( + const base::DictionaryValue& dictionary); + +} // namespace content + +#endif // CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_LINUX_H_ diff --git a/content/browser/battery_status/battery_status_manager_linux_unittest.cc b/content/browser/battery_status/battery_status_manager_linux_unittest.cc new file mode 100644 index 0000000000000..79148b56678fe --- /dev/null +++ b/content/browser/battery_status/battery_status_manager_linux_unittest.cc @@ -0,0 +1,143 @@ +// 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 "content/browser/battery_status/battery_status_manager_linux.h" + +#include "base/values.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +TEST(BatteryStatusManagerLinuxTest, EmptyDictionary) { + base::DictionaryValue dictionary; + blink::WebBatteryStatus default_status; + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_EQ(default_status.charging, status.charging); + EXPECT_EQ(default_status.chargingTime, status.chargingTime); + EXPECT_EQ(default_status.dischargingTime, status.dischargingTime); + EXPECT_EQ(default_status.level, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, ChargingHalfFull) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_CHARGING); + dictionary.SetDouble("TimeToFull", 0); + dictionary.SetDouble("Percentage", 50); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_TRUE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(0.5, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, ChargingTimeToFull) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_CHARGING); + dictionary.SetDouble("TimeToFull", 100.f); + dictionary.SetDouble("Percentage", 1); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_TRUE(status.charging); + EXPECT_EQ(100, status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(.01, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, FullyCharged) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_FULL); + dictionary.SetDouble("TimeToFull", 100); + dictionary.SetDouble("TimeToEmpty", 200); + dictionary.SetDouble("Percentage", 100); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_TRUE(status.charging); + EXPECT_EQ(0, status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(1, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, Discharging) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING); + dictionary.SetDouble("TimeToFull", 0); + dictionary.SetDouble("TimeToEmpty", 200); + dictionary.SetDouble("Percentage", 90); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_FALSE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(200, status.dischargingTime); + EXPECT_EQ(.9, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, DischargingTimeToEmptyUnknown) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING); + dictionary.SetDouble("TimeToFull", 0); + dictionary.SetDouble("TimeToEmpty", 0); + dictionary.SetDouble("Percentage", 90); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_FALSE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(.9, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, DeviceStateUnknown) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_UNKNOWN); + dictionary.SetDouble("TimeToFull", 0); + dictionary.SetDouble("TimeToEmpty", 0); + dictionary.SetDouble("Percentage", 50); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_TRUE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(.5, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, DeviceStateEmpty) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_EMPTY); + dictionary.SetDouble("TimeToFull", 0); + dictionary.SetDouble("TimeToEmpty", 0); + dictionary.SetDouble("Percentage", 0); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_FALSE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(0, status.level); +} + +TEST(BatteryStatusManagerLinuxTest, LevelRoundedToThreeSignificantDigits) { + base::DictionaryValue dictionary; + dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING); + dictionary.SetDouble("Percentage", 14.56); + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary); + + EXPECT_FALSE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(0.15, status.level); +} + +} // namespace + +} // namespace content diff --git a/content/browser/battery_status/battery_status_manager_win.cc b/content/browser/battery_status/battery_status_manager_win.cc new file mode 100644 index 0000000000000..d998d5aca0e2a --- /dev/null +++ b/content/browser/battery_status/battery_status_manager_win.cc @@ -0,0 +1,206 @@ +// 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 "content/browser/battery_status/battery_status_manager_win.h" + +#include "base/memory/ref_counted.h" +#include "base/strings/string16.h" +#include "base/win/message_window.h" +#include "base/win/windows_version.h" +#include "content/browser/battery_status/battery_status_manager.h" +#include "content/public/browser/browser_thread.h" + +namespace content { + +namespace { + +typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback; + +const wchar_t kWindowClassName[] = L"BatteryStatusMessageWindow"; + +// Message-only window for handling battery changes on Windows. +class BatteryStatusObserver + : public base::RefCountedThreadSafe { + public: + explicit BatteryStatusObserver(const BatteryCallback& callback) + : power_handle_(NULL), + battery_change_handle_(NULL), + callback_(callback) { + } + + virtual ~BatteryStatusObserver() { DCHECK(!window_); } + + void Start() { + // Need to start on the UI thread to receive battery status notifications. + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&BatteryStatusObserver::StartOnUI, this)); + } + + void Stop() { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&BatteryStatusObserver::StopOnUI, this)); + } + + private: + void StartOnUI() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (window_) + return; + + if (CreateMessageWindow()) { + BatteryChanged(); + // RegisterPowerSettingNotification function work from Windows Vista + // onwards. However even without them we will receive notifications, + // e.g. when a power source is connected. + // TODO(timvolodine) : consider polling for battery changes on windows + // versions prior to Vista, see crbug.com/402466. + power_handle_ = + RegisterNotification(&GUID_ACDC_POWER_SOURCE); + battery_change_handle_ = + RegisterNotification(&GUID_BATTERY_PERCENTAGE_REMAINING); + } else { + // Could not create a message window, execute callback with the default + // values. + callback_.Run(blink::WebBatteryStatus()); + } + } + + void StopOnUI() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (!window_) + return; + + if (power_handle_) + UnregisterNotification(power_handle_); + if (battery_change_handle_) + UnregisterNotification(battery_change_handle_); + window_.reset(); + } + + void BatteryChanged() { + SYSTEM_POWER_STATUS win_status; + if (GetSystemPowerStatus(&win_status)) + callback_.Run(ComputeWebBatteryStatus(win_status)); + else + callback_.Run(blink::WebBatteryStatus()); + } + + bool HandleMessage(UINT message, + WPARAM wparam, + LPARAM lparam, + LRESULT* result) { + switch(message) { + case WM_POWERBROADCAST: + if (wparam == PBT_APMPOWERSTATUSCHANGE || + wparam == PBT_POWERSETTINGCHANGE) { + BatteryChanged(); + } + *result = NULL; + return true; + default: + return false; + } + } + + HPOWERNOTIFY RegisterNotification(LPCGUID power_setting) { + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return NULL; + + return RegisterPowerSettingNotification(window_->hwnd(), power_setting, + DEVICE_NOTIFY_WINDOW_HANDLE); + } + + BOOL UnregisterNotification(HPOWERNOTIFY handle) { + if (base::win::GetVersion() < base::win::VERSION_VISTA) + return FALSE; + + return UnregisterPowerSettingNotification(handle); + } + + bool CreateMessageWindow() { + // TODO(timvolodine): consider reusing the message window of PowerMonitor. + window_.reset(new base::win::MessageWindow()); + if (!window_->CreateNamed(base::Bind(&BatteryStatusObserver::HandleMessage, + base::Unretained(this)), + base::string16(kWindowClassName))) { + LOG(ERROR) << "Failed to create message window: " << kWindowClassName; + window_.reset(); + return false; + } + return true; + } + + HPOWERNOTIFY power_handle_; + HPOWERNOTIFY battery_change_handle_; + BatteryCallback callback_; + scoped_ptr window_; + + DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); +}; + +class BatteryStatusManagerWin : public BatteryStatusManager { + public: + explicit BatteryStatusManagerWin(const BatteryCallback& callback) + : battery_observer_(new BatteryStatusObserver(callback)) {} + virtual ~BatteryStatusManagerWin() { battery_observer_->Stop(); } + + public: + // BatteryStatusManager: + virtual bool StartListeningBatteryChange() OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + battery_observer_->Start(); + return true; + } + + virtual void StopListeningBatteryChange() OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + battery_observer_->Stop(); + } + + private: + scoped_refptr battery_observer_; + + DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerWin); +}; + +} // namespace + +blink::WebBatteryStatus ComputeWebBatteryStatus( + const SYSTEM_POWER_STATUS& win_status) { + blink::WebBatteryStatus status; + status.charging = win_status.ACLineStatus != WIN_AC_LINE_STATUS_OFFLINE; + + // Set level if available. Otherwise keep the default value which is 1. + if (win_status.BatteryLifePercent != 255) { + // Convert percentage to a value between 0 and 1 with 2 significant digits. + status.level = static_cast(win_status.BatteryLifePercent) / 100.; + } + + if (!status.charging) { + // Set dischargingTime if available otherwise keep the default value, + // which is +Infinity. + if (win_status.BatteryLifeTime != (DWORD)-1) + status.dischargingTime = win_status.BatteryLifeTime; + status.chargingTime = std::numeric_limits::infinity(); + } else { + // Set chargingTime to +Infinity if not fully charged, otherwise leave the + // default value, which is 0. + if (status.level < 1) + status.chargingTime = std::numeric_limits::infinity(); + } + return status; +} + +// static +scoped_ptr BatteryStatusManager::Create( + const BatteryStatusService::BatteryUpdateCallback& callback) { + return scoped_ptr( + new BatteryStatusManagerWin(callback)); +} + +} // namespace content diff --git a/content/browser/battery_status/battery_status_manager_win.h b/content/browser/battery_status/battery_status_manager_win.h new file mode 100644 index 0000000000000..5169e4bf8c8e6 --- /dev/null +++ b/content/browser/battery_status/battery_status_manager_win.h @@ -0,0 +1,26 @@ +// 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 CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_WIN_H_ +#define CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_WIN_H_ + +#include +#include "content/common/content_export.h" +#include "third_party/WebKit/public/platform/WebBatteryStatus.h" + +namespace content { + +enum WinACLineStatus { + WIN_AC_LINE_STATUS_OFFLINE = 0, + WIN_AC_LINE_STATUS_ONLINE = 1, + WIN_AC_LINE_STATUS_UNKNOWN = 255, +}; + +// Returns WebBatteryStatus corresponding to the given SYSTEM_POWER_STATUS. +CONTENT_EXPORT blink::WebBatteryStatus ComputeWebBatteryStatus( + const SYSTEM_POWER_STATUS& win_status); + +} // namespace content + +#endif // CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_WIN_H_ \ No newline at end of file diff --git a/content/browser/battery_status/battery_status_manager_win_unittest.cc b/content/browser/battery_status/battery_status_manager_win_unittest.cc new file mode 100644 index 0000000000000..22d7752246e8a --- /dev/null +++ b/content/browser/battery_status/battery_status_manager_win_unittest.cc @@ -0,0 +1,80 @@ +// 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 "content/browser/battery_status/battery_status_manager_win.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +TEST(BatteryStatusManagerWinTest, ACLineStatusOffline) { + SYSTEM_POWER_STATUS win_status; + win_status.ACLineStatus = WIN_AC_LINE_STATUS_OFFLINE; + win_status.BatteryLifePercent = 100; + win_status.BatteryLifeTime = 200; + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(win_status); + EXPECT_FALSE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(200, status.dischargingTime); + EXPECT_EQ(1, status.level); +} + +TEST(BatteryStatusManagerWinTest, ACLineStatusOfflineDischargingTimeUnknown) { + SYSTEM_POWER_STATUS win_status; + win_status.ACLineStatus = WIN_AC_LINE_STATUS_OFFLINE; + win_status.BatteryLifePercent = 100; + win_status.BatteryLifeTime = (DWORD)-1; + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(win_status); + EXPECT_FALSE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(1, status.level); +} + +TEST(BatteryStatusManagerWinTest, ACLineStatusOnline) { + SYSTEM_POWER_STATUS win_status; + win_status.ACLineStatus = WIN_AC_LINE_STATUS_ONLINE; + win_status.BatteryLifePercent = 50; + win_status.BatteryLifeTime = 200; + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(win_status); + EXPECT_TRUE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(.5, status.level); +} + +TEST(BatteryStatusManagerWinTest, ACLineStatusOnlineFullBattery) { + SYSTEM_POWER_STATUS win_status; + win_status.ACLineStatus = WIN_AC_LINE_STATUS_ONLINE; + win_status.BatteryLifePercent = 100; + win_status.BatteryLifeTime = 200; + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(win_status); + EXPECT_TRUE(status.charging); + EXPECT_EQ(0, status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(1, status.level); +} + +TEST(BatteryStatusManagerWinTest, ACLineStatusUnknown) { + SYSTEM_POWER_STATUS win_status; + win_status.ACLineStatus = WIN_AC_LINE_STATUS_UNKNOWN; + win_status.BatteryLifePercent = 50; + win_status.BatteryLifeTime = 200; + + blink::WebBatteryStatus status = ComputeWebBatteryStatus(win_status); + EXPECT_TRUE(status.charging); + EXPECT_EQ(std::numeric_limits::infinity(), status.chargingTime); + EXPECT_EQ(std::numeric_limits::infinity(), status.dischargingTime); + EXPECT_EQ(.5, status.level); +} + +} // namespace + +} // namespace content diff --git a/content/browser/browser_child_process_host_impl.cc b/content/browser/browser_child_process_host_impl.cc index 7589e5f013472..de94d33dcb825 100644 --- a/content/browser/browser_child_process_host_impl.cc +++ b/content/browser/browser_child_process_host_impl.cc @@ -132,13 +132,14 @@ void BrowserChildProcessHostImpl::TerminateAll() { void BrowserChildProcessHostImpl::Launch( SandboxedProcessLauncherDelegate* delegate, - CommandLine* cmd_line) { + base::CommandLine* cmd_line) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); GetContentClient()->browser()->AppendExtraCommandLineSwitches( cmd_line, data_.id); - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); static const char* kForwardSwitches[] = { switches::kDisableLogging, switches::kEnableLogging, diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc index ce7e2248dab0f..d2a642ca653af 100644 --- a/content/browser/browser_main_loop.cc +++ b/content/browser/browser_main_loop.cc @@ -135,7 +135,7 @@ namespace content { namespace { #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_ANDROID) -void SetupSandbox(const CommandLine& parsed_command_line) { +void SetupSandbox(const base::CommandLine& parsed_command_line) { TRACE_EVENT0("startup", "SetupSandbox"); base::FilePath sandbox_binary; @@ -239,6 +239,47 @@ bool ShouldInitializeBrowserGpuChannelAndTransportSurface() { } #endif +// Disable optimizations for this block of functions so the compiler doesn't +// merge them all together. This makes it possible to tell what thread was +// unresponsive by inspecting the callstack. +MSVC_DISABLE_OPTIMIZE() +MSVC_PUSH_DISABLE_WARNING(4748) + +NOINLINE void ResetThread_DB(scoped_ptr thread) { + thread.reset(); +} + +NOINLINE void ResetThread_FILE(scoped_ptr thread) { + thread.reset(); +} + +NOINLINE void ResetThread_FILE_USER_BLOCKING( + scoped_ptr thread) { + thread.reset(); +} + +NOINLINE void ResetThread_PROCESS_LAUNCHER( + scoped_ptr thread) { + thread.reset(); +} + +NOINLINE void ResetThread_CACHE(scoped_ptr thread) { + thread.reset(); +} + +NOINLINE void ResetThread_IO(scoped_ptr thread) { + thread.reset(); +} + +#if !defined(OS_IOS) +NOINLINE void ResetThread_IndexedDb(scoped_ptr thread) { + thread.reset(); +} +#endif + +MSVC_POP_WARNING() +MSVC_ENABLE_OPTIMIZE(); + } // namespace // The currently-running BrowserMainLoop. There can be one or zero. @@ -807,42 +848,42 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { // - (Not sure why DB stops last.) switch (thread_id) { case BrowserThread::DB: { - TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DBThread"); - db_thread_.reset(); - } - break; - case BrowserThread::FILE_USER_BLOCKING: { - TRACE_EVENT0("shutdown", - "BrowserMainLoop::Subsystem:FileUserBlockingThread"); - file_user_blocking_thread_.reset(); - } + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:DBThread"); + ResetThread_DB(db_thread_.Pass()); break; + } case BrowserThread::FILE: { - TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread"); + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:FileThread"); #if !defined(OS_IOS) - // Clean up state that lives on or uses the file_thread_ before - // it goes away. - if (resource_dispatcher_host_) - resource_dispatcher_host_.get()->save_file_manager()->Shutdown(); + // Clean up state that lives on or uses the file_thread_ before + // it goes away. + if (resource_dispatcher_host_) + resource_dispatcher_host_.get()->save_file_manager()->Shutdown(); #endif // !defined(OS_IOS) - file_thread_.reset(); - } + ResetThread_FILE(file_thread_.Pass()); + break; + } + case BrowserThread::FILE_USER_BLOCKING: { + TRACE_EVENT0("shutdown", + "BrowserMainLoop::Subsystem:FileUserBlockingThread"); + ResetThread_FILE_USER_BLOCKING(file_user_blocking_thread_.Pass()); break; + } case BrowserThread::PROCESS_LAUNCHER: { - TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:LauncherThread"); - process_launcher_thread_.reset(); - } + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:LauncherThread"); + ResetThread_PROCESS_LAUNCHER(process_launcher_thread_.Pass()); break; + } case BrowserThread::CACHE: { - TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:CacheThread"); - cache_thread_.reset(); - } + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:CacheThread"); + ResetThread_CACHE(cache_thread_.Pass()); break; + } case BrowserThread::IO: { - TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IOThread"); - io_thread_.reset(); - } + TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IOThread"); + ResetThread_IO(io_thread_.Pass()); break; + } case BrowserThread::UI: case BrowserThread::ID_COUNT: default: @@ -854,7 +895,7 @@ void BrowserMainLoop::ShutdownThreadsAndCleanUp() { #if !defined(OS_IOS) { TRACE_EVENT0("shutdown", "BrowserMainLoop::Subsystem:IndexedDBThread"); - indexed_db_thread_.reset(); + ResetThread_IndexedDb(indexed_db_thread_.Pass()); } #endif @@ -1135,7 +1176,8 @@ base::FilePath BrowserMainLoop::GetStartupTraceFileName( return trace_file; } -void BrowserMainLoop::InitStartupTracing(const CommandLine& command_line) { +void BrowserMainLoop::InitStartupTracing( + const base::CommandLine& command_line) { DCHECK(is_tracing_startup_); startup_trace_file_ = GetStartupTraceFileName(parsed_command_line_); diff --git a/content/browser/browser_main_runner.cc b/content/browser/browser_main_runner.cc index 8701c07cb0aa0..a06f18af30dfe 100644 --- a/content/browser/browser_main_runner.cc +++ b/content/browser/browser_main_runner.cc @@ -146,7 +146,8 @@ class BrowserMainRunnerImpl : public BrowserMainRunner { // The shutdown tracing got enabled in AttemptUserExit earlier, but someone // needs to write the result to disc. For that a dumper needs to get created // which will dump the traces to disc when it gets destroyed. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); scoped_ptr shutdown_profiler; if (command_line.HasSwitch(switches::kTraceShutdown)) { shutdown_profiler.reset(new BrowserShutdownProfileDumper( diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc index a3a86f8b7381f..4c754ffab6b20 100644 --- a/content/browser/browser_plugin/browser_plugin_guest.cc +++ b/content/browser/browser_plugin/browser_plugin_guest.cc @@ -215,7 +215,10 @@ void BrowserPluginGuest::Initialize( new BrowserPluginMsg_GuestContentWindowReady(instance_id_, guest_routing_id)); - WebPreferences prefs = GetWebContents()->GetWebkitPrefs(); + // TODO(chrishtr): this code is wrong. The navigate_on_drag_drop field will + // be reset again the next time preferences are updated. + WebPreferences prefs = + GetWebContents()->GetRenderViewHost()->GetWebkitPreferences(); prefs.navigate_on_drag_drop = false; GetWebContents()->GetRenderViewHost()->UpdateWebkitPreferences(prefs); diff --git a/content/browser/browser_shutdown_profile_dumper.cc b/content/browser/browser_shutdown_profile_dumper.cc index a8b9e952e1304..1cb5694cc1613 100644 --- a/content/browser/browser_shutdown_profile_dumper.cc +++ b/content/browser/browser_shutdown_profile_dumper.cc @@ -75,7 +75,8 @@ void BrowserShutdownProfileDumper::EndTraceAndFlush( // static base::FilePath BrowserShutdownProfileDumper::GetShutdownProfileFileName() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); base::FilePath trace_file = command_line.GetSwitchValuePath(switches::kTraceShutdownFile); diff --git a/content/browser/child_process_launcher.cc b/content/browser/child_process_launcher.cc index 5e65042301c9a..152cd5c8aee6e 100644 --- a/content/browser/child_process_launcher.cc +++ b/content/browser/child_process_launcher.cc @@ -76,7 +76,7 @@ class ChildProcessLauncher::Context void Launch( SandboxedProcessLauncherDelegate* delegate, - CommandLine* cmd_line, + base::CommandLine* cmd_line, int child_process_id, Client* client) { client_ = client; @@ -172,7 +172,7 @@ class ChildProcessLauncher::Context BrowserThread::ID client_thread_id, int child_process_id, SandboxedProcessLauncherDelegate* delegate, - CommandLine* cmd_line) { + base::CommandLine* cmd_line) { scoped_ptr delegate_deleter(delegate); #if defined(OS_WIN) bool launch_elevated = delegate->ShouldLaunchElevated(); @@ -186,7 +186,7 @@ class ChildProcessLauncher::Context base::EnvironmentMap env = delegate->GetEnvironment(); int ipcfd = delegate->GetIpcFd(); #endif - scoped_ptr cmd_line_deleter(cmd_line); + scoped_ptr cmd_line_deleter(cmd_line); base::TimeTicks begin_launch_time = base::TimeTicks::Now(); #if defined(OS_WIN) @@ -436,7 +436,7 @@ class ChildProcessLauncher::Context ChildProcessLauncher::ChildProcessLauncher( SandboxedProcessLauncherDelegate* delegate, - CommandLine* cmd_line, + base::CommandLine* cmd_line, int child_process_id, Client* client) { context_ = new Context(); diff --git a/content/browser/child_process_security_policy_impl.cc b/content/browser/child_process_security_policy_impl.cc index 3f8e21611f472..6ffe46f185e26 100644 --- a/content/browser/child_process_security_policy_impl.cc +++ b/content/browser/child_process_security_policy_impl.cc @@ -241,7 +241,8 @@ class ChildProcessSecurityPolicyImpl::SecurityState { // compatibility with many sites. The similar --site-per-process flag only // blocks JavaScript access to cross-site cookies (in // CanAccessCookiesForOrigin). - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (!command_line.HasSwitch(switches::kEnableStrictSiteIsolation)) return true; @@ -585,7 +586,8 @@ bool ChildProcessSecurityPolicyImpl::CanLoadPage(int child_id, ResourceType resource_type) { // If --site-per-process flag is passed, we should enforce // stronger security restrictions on page navigation. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess) && + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess) && IsResourceTypeFrame(resource_type)) { // TODO(nasko): Do the proper check for site-per-process, once // out-of-process iframes is ready to go. diff --git a/content/browser/compositor/browser_compositor_view_mac.h b/content/browser/compositor/browser_compositor_view_mac.h index 4512d3714b0a7..e6019c3521af3 100644 --- a/content/browser/compositor/browser_compositor_view_mac.h +++ b/content/browser/compositor/browser_compositor_view_mac.h @@ -70,7 +70,8 @@ class BrowserCompositorViewMac { gfx::AcceleratedWidget widget, uint64 surface_handle, int surface_id, const std::vector& latency_info, - gfx::Size pixel_size, float scale_factor); + gfx::Size pixel_size, float scale_factor, + int gpu_host_id, int gpu_route_id); static void GotSoftwareFrame( gfx::AcceleratedWidget widget, diff --git a/content/browser/compositor/browser_compositor_view_mac.mm b/content/browser/compositor/browser_compositor_view_mac.mm index 32e8bfab8c856..9feeefaa09248 100644 --- a/content/browser/compositor/browser_compositor_view_mac.mm +++ b/content/browser/compositor/browser_compositor_view_mac.mm @@ -6,7 +6,9 @@ #include "base/debug/trace_event.h" #include "base/lazy_instance.h" +#include "content/browser/gpu/gpu_process_host_ui_shim.h" #include "content/browser/compositor/browser_compositor_view_private_mac.h" +#include "content/common/gpu/gpu_messages.h" //////////////////////////////////////////////////////////////////////////////// // BrowserCompositorViewMac @@ -76,12 +78,25 @@ gfx::AcceleratedWidget widget, uint64 surface_handle, int surface_id, const std::vector& latency_info, - gfx::Size pixel_size, float scale_factor) { + gfx::Size pixel_size, float scale_factor, + int gpu_host_id, int gpu_route_id) { BrowserCompositorViewMacInternal* internal_view = BrowserCompositorViewMacInternal::FromAcceleratedWidget(widget); + int renderer_id = 0; if (internal_view) { internal_view->GotAcceleratedFrame( surface_handle, surface_id, latency_info, pixel_size, scale_factor); + renderer_id = internal_view->GetRendererID(); + } + + // Acknowledge the swap, now that it has been processed. + AcceleratedSurfaceMsg_BufferPresented_Params ack_params; + ack_params.sync_point = 0; + ack_params.renderer_id = renderer_id; + GpuProcessHostUIShim* ui_shim = GpuProcessHostUIShim::FromID(gpu_host_id); + if (ui_shim) { + ui_shim->Send(new AcceleratedSurfaceMsg_BufferPresented( + gpu_route_id, ack_params)); } } diff --git a/content/browser/compositor/browser_compositor_view_private_mac.h b/content/browser/compositor/browser_compositor_view_private_mac.h index e84fc9c001b1d..23fdc8ba5347e 100644 --- a/content/browser/compositor/browser_compositor_view_private_mac.h +++ b/content/browser/compositor/browser_compositor_view_private_mac.h @@ -33,6 +33,9 @@ class BrowserCompositorViewMacInternal // Return true if the last frame swapped has a size in DIP of |dip_size|. bool HasFrameOfSize(const gfx::Size& dip_size) const; + // Return the CGL renderer ID for the surface, if one is available. + int GetRendererID() const; + // Mark a bracket in which new frames are being pumped in a restricted nested // run loop. void BeginPumpingFrames(); @@ -49,7 +52,8 @@ class BrowserCompositorViewMacInternal private: // CompositingIOSurfaceLayerClient implementation: virtual bool AcceleratedLayerShouldAckImmediately() const OVERRIDE; - virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE; + virtual void AcceleratedLayerDidDrawFrame() OVERRIDE; + virtual void AcceleratedLayerHitError() OVERRIDE; void GotAcceleratedCAContextFrame( CAContextID ca_context_id, gfx::Size pixel_size, float scale_factor); @@ -57,6 +61,16 @@ class BrowserCompositorViewMacInternal void GotAcceleratedIOSurfaceFrame( IOSurfaceID io_surface_id, gfx::Size pixel_size, float scale_factor); + // Remove a layer from the heirarchy and destroy it. Because the accelerated + // layer types may be replaced by a layer of the same type, the layer to + // destroy is parameterized, and, if it is the current layer, the current + // layer is reset. + void DestroyCAContextLayer( + base::scoped_nsobject ca_context_layer); + void DestroyIOSurfaceLayer( + base::scoped_nsobject io_surface_layer); + void DestroySoftwareLayer(); + // The client of the BrowserCompositorViewMac that is using this as its // internals. BrowserCompositorViewMacClient* client_; diff --git a/content/browser/compositor/browser_compositor_view_private_mac.mm b/content/browser/compositor/browser_compositor_view_private_mac.mm index cd6ed66cc6d53..90c834054313d 100644 --- a/content/browser/compositor/browser_compositor_view_private_mac.mm +++ b/content/browser/compositor/browser_compositor_view_private_mac.mm @@ -91,15 +91,11 @@ ScopedCAActionDisabler disabler; [flipped_layer_ removeFromSuperlayer]; + DestroyIOSurfaceLayer(io_surface_layer_); + DestroyCAContextLayer(ca_context_layer_); + DestroySoftwareLayer(); - [io_surface_layer_ removeFromSuperlayer]; - [io_surface_layer_ resetClient]; - io_surface_layer_.reset(); accelerated_output_surface_id_ = 0; - - [software_layer_ removeFromSuperlayer]; - software_layer_.reset(); - last_swap_size_dip_ = gfx::Size(); compositor_->SetScaleAndSize(1.0, gfx::Size(0, 0)); @@ -112,6 +108,12 @@ return last_swap_size_dip_ == dip_size; } +int BrowserCompositorViewMacInternal::GetRendererID() const { + if (io_surface_layer_) + return [io_surface_layer_ iosurface]->GetRendererID(); + return 0; +} + void BrowserCompositorViewMacInternal::BeginPumpingFrames() { [io_surface_layer_ beginPumpingFrames]; } @@ -132,7 +134,7 @@ // If there is no client and therefore no superview to draw into, early-out. if (!client_) { - AcceleratedLayerDidDrawFrame(true); + AcceleratedLayerDidDrawFrame(); return; } @@ -178,23 +180,16 @@ // Acknowledge the frame to unblock the compositor immediately (the GPU // process will do any required throttling). - AcceleratedLayerDidDrawFrame(true); + AcceleratedLayerDidDrawFrame(); // If this replacing a same-type layer, remove it now that the new layer is // in the hierarchy. if (old_ca_context_layer != ca_context_layer_) - [old_ca_context_layer removeFromSuperlayer]; + DestroyCAContextLayer(old_ca_context_layer); // Remove any different-type layers that this is replacing. - if (io_surface_layer_) { - [io_surface_layer_ resetClient]; - [io_surface_layer_ removeFromSuperlayer]; - io_surface_layer_.reset(); - } - if (software_layer_) { - [software_layer_ removeFromSuperlayer]; - software_layer_.reset(); - } + DestroyIOSurfaceLayer(io_surface_layer_); + DestroySoftwareLayer(); } void BrowserCompositorViewMacInternal::GotAcceleratedIOSurfaceFrame( @@ -216,51 +211,64 @@ if (needs_new_layer) { scoped_refptr iosurface = content::CompositingIOSurfaceMac::Create(); - io_surface_layer_.reset([[CompositingIOSurfaceLayer alloc] - initWithIOSurface:iosurface - withScaleFactor:scale_factor - withClient:this]); - [flipped_layer_ addSublayer:io_surface_layer_]; + if (!iosurface) { + LOG(ERROR) << "Failed to create CompositingIOSurfaceMac"; + } else { + io_surface_layer_.reset([[CompositingIOSurfaceLayer alloc] + initWithIOSurface:iosurface + withScaleFactor:scale_factor + withClient:this]); + if (io_surface_layer_) + [flipped_layer_ addSublayer:io_surface_layer_]; + else + LOG(ERROR) << "Failed to create CompositingIOSurfaceLayer"; + } } // Open the provided IOSurface. - { + if (io_surface_layer_) { bool result = true; gfx::ScopedCGLSetCurrentContext scoped_set_current_context( [io_surface_layer_ context]->cgl_context()); result = [io_surface_layer_ iosurface]->SetIOSurfaceWithContextCurrent( [io_surface_layer_ context], io_surface_id, pixel_size, scale_factor); - if (!result) - LOG(ERROR) << "Failed SetIOSurface on CompositingIOSurfaceMac"; + if (!result) { + DestroyIOSurfaceLayer(io_surface_layer_); + LOG(ERROR) << "Failed open IOSurface in CompositingIOSurfaceLayer"; + } + } + + // Give a final complaint if anything with the layer's creation went wrong. + // This frame will appear blank, the compositor will try to create another, + // and maybe that will go better. + if (!io_surface_layer_) { + LOG(ERROR) << "CompositingIOSurfaceLayer is nil, tab will be blank"; + AcceleratedLayerHitError(); + } + + // Make the CALayer draw and set its size appropriately. + if (io_surface_layer_) { + [io_surface_layer_ gotNewFrame]; + + // Set the bounds of the accelerated layer to match the size of the frame. + // If the bounds changed, force the content to be displayed immediately. + CGRect new_layer_bounds = CGRectMake( + 0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height()); + bool bounds_changed = !CGRectEqualToRect( + new_layer_bounds, [io_surface_layer_ bounds]); + [io_surface_layer_ setBounds:new_layer_bounds]; + if (bounds_changed) + [io_surface_layer_ setNeedsDisplayAndDisplayAndAck]; } - [io_surface_layer_ gotNewFrame]; - - // Set the bounds of the accelerated layer to match the size of the frame. - // If the bounds changed, force the content to be displayed immediately. - CGRect new_layer_bounds = CGRectMake( - 0, 0, last_swap_size_dip_.width(), last_swap_size_dip_.height()); - bool bounds_changed = !CGRectEqualToRect( - new_layer_bounds, [io_surface_layer_ bounds]); - [io_surface_layer_ setBounds:new_layer_bounds]; - if (bounds_changed) - [io_surface_layer_ setNeedsDisplayAndDisplayAndAck]; // If this replacing a same-type layer, remove it now that the new layer is // in the hierarchy. - if (old_io_surface_layer != io_surface_layer_) { - [old_io_surface_layer resetClient]; - [old_io_surface_layer removeFromSuperlayer]; - } + if (old_io_surface_layer != io_surface_layer_) + DestroyIOSurfaceLayer(old_io_surface_layer); // Remove any different-type layers that this is replacing. - if (ca_context_layer_) { - [ca_context_layer_ removeFromSuperlayer]; - ca_context_layer_.reset(); - } - if (software_layer_) { - [software_layer_ removeFromSuperlayer]; - software_layer_.reset(); - } + DestroyCAContextLayer(ca_context_layer_); + DestroySoftwareLayer(); } void BrowserCompositorViewMacInternal::GotSoftwareFrame( @@ -291,15 +299,34 @@ last_swap_size_dip_ = ConvertSizeToDIP(scale_factor, pixel_size); // Remove any different-type layers that this is replacing. - if (ca_context_layer_) { - [ca_context_layer_ removeFromSuperlayer]; + DestroyCAContextLayer(ca_context_layer_); + DestroyIOSurfaceLayer(io_surface_layer_); +} + +void BrowserCompositorViewMacInternal::DestroyCAContextLayer( + base::scoped_nsobject ca_context_layer) { + if (!ca_context_layer) + return; + [ca_context_layer removeFromSuperlayer]; + if (ca_context_layer == ca_context_layer_) ca_context_layer_.reset(); - } - if (io_surface_layer_) { - [io_surface_layer_ resetClient]; - [io_surface_layer_ removeFromSuperlayer]; +} + +void BrowserCompositorViewMacInternal::DestroyIOSurfaceLayer( + base::scoped_nsobject io_surface_layer) { + if (!io_surface_layer) + return; + [io_surface_layer resetClient]; + [io_surface_layer removeFromSuperlayer]; + if (io_surface_layer == io_surface_layer_) io_surface_layer_.reset(); - } +} + +void BrowserCompositorViewMacInternal::DestroySoftwareLayer() { + if (!software_layer_) + return; + [software_layer_ removeFromSuperlayer]; + software_layer_.reset(); } bool BrowserCompositorViewMacInternal::AcceleratedLayerShouldAckImmediately() @@ -311,8 +338,7 @@ return client_->BrowserCompositorViewShouldAckImmediately(); } -void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame( - bool succeeded) { +void BrowserCompositorViewMacInternal::AcceleratedLayerDidDrawFrame() { if (accelerated_output_surface_id_) { content::ImageTransportFactory::GetInstance()->OnSurfaceDisplayed( accelerated_output_surface_id_); @@ -323,12 +349,17 @@ client_->BrowserCompositorViewFrameSwapped(accelerated_latency_info_); accelerated_latency_info_.clear(); +} - if (!succeeded) { - if (io_surface_layer_) - [io_surface_layer_ context]->PoisonContextAndSharegroup(); - compositor_->ScheduleFullRedraw(); - } +void BrowserCompositorViewMacInternal::AcceleratedLayerHitError() { + // Perform all acks that would have been done if the frame had succeeded, to + // un-block the compositor and renderer. + AcceleratedLayerDidDrawFrame(); + + // Poison the context being used and request a mulligan. + if (io_surface_layer_) + [io_surface_layer_ context]->PoisonContextAndSharegroup(); + compositor_->ScheduleFullRedraw(); } // static diff --git a/content/browser/compositor/delegated_frame_host.cc b/content/browser/compositor/delegated_frame_host.cc index 09b1c27ff62de..4f1d43a39699e 100644 --- a/content/browser/compositor/delegated_frame_host.cc +++ b/content/browser/compositor/delegated_frame_host.cc @@ -60,7 +60,7 @@ DelegatedFrameHost::DelegatedFrameHost(DelegatedFrameHostClient* client) ImageTransportFactory::GetInstance()->AddObserver(this); } -void DelegatedFrameHost::WasShown() { +void DelegatedFrameHost::WasShown(const ui::LatencyInfo& latency_info) { delegated_frame_evictor_->SetVisible(true); if (surface_id_.is_null() && !frame_provider_ && @@ -69,6 +69,15 @@ void DelegatedFrameHost::WasShown() { if (compositor) released_front_lock_ = compositor->GetCompositorLock(); } + + ui::Compositor* compositor = client_->GetCompositor(); + if (compositor) { + compositor->SetLatencyInfo(latency_info); + } +} + +bool DelegatedFrameHost::HasSavedFrame() { + return delegated_frame_evictor_->HasFrame(); } void DelegatedFrameHost::WasHidden() { @@ -174,9 +183,7 @@ void DelegatedFrameHost::CopyFromCompositingSurfaceToVideoFrame( subscriber_texture, target, callback)); - gfx::Rect src_subrect_in_pixel = - ConvertRectToPixel(client_->CurrentDeviceScaleFactor(), src_subrect); - request->set_area(src_subrect_in_pixel); + request->set_area(src_subrect); if (subscriber_texture.get()) { request->SetTextureMailbox( cc::TextureMailbox(subscriber_texture->mailbox(), @@ -729,7 +736,8 @@ void DelegatedFrameHost::CopyFromCompositingSurfaceHasResultForVideo( quality_switch = switches::kTabCaptureUpscaleQuality; std::string switch_value = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII(quality_switch); + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + quality_switch); if (switch_value == "fast") quality = GLHelper::SCALER_QUALITY_FAST; else if (switch_value == "good") diff --git a/content/browser/compositor/delegated_frame_host.h b/content/browser/compositor/delegated_frame_host.h index 493b2b2c75eba..ac9d90ffd5ea5 100644 --- a/content/browser/compositor/delegated_frame_host.h +++ b/content/browser/compositor/delegated_frame_host.h @@ -92,8 +92,9 @@ class CONTENT_EXPORT DelegatedFrameHost float frame_device_scale_factor, const std::vector& latency_info); void WasHidden(); - void WasShown(); + void WasShown(const ui::LatencyInfo& latency_info); void WasResized(); + bool HasSavedFrame(); gfx::Size GetRequestedRendererSize() const; void AddedToWindow(); void RemovingFromWindow(); diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc index 17b05485e2100..ab5317bc39c3a 100644 --- a/content/browser/compositor/gpu_process_transport_factory.cc +++ b/content/browser/compositor/gpu_process_transport_factory.cc @@ -72,7 +72,7 @@ GpuProcessTransportFactory::GpuProcessTransportFactory() output_surface_proxy_ = new BrowserCompositorOutputSurfaceProxy( &output_surface_map_); #if defined(OS_CHROMEOS) - bool use_thread = !CommandLine::ForCurrentProcess()->HasSwitch( + bool use_thread = !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUIDisableThreadedCompositing); #else bool use_thread = false; @@ -124,8 +124,9 @@ scoped_ptr CreateOverlayCandidateValidator( #if defined(USE_OZONE) ui::OverlayCandidatesOzone* overlay_candidates = ui::SurfaceFactoryOzone::GetInstance()->GetOverlayCandidates(widget); - if (overlay_candidates && CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableHardwareOverlays)) { + if (overlay_candidates && + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableHardwareOverlays)) { return scoped_ptr( new OverlayCandidateValidatorOzone(widget, overlay_candidates)); } diff --git a/content/browser/compositor/image_transport_factory.cc b/content/browser/compositor/image_transport_factory.cc index 7087d22848644..6f06a97316801 100644 --- a/content/browser/compositor/image_transport_factory.cc +++ b/content/browser/compositor/image_transport_factory.cc @@ -37,7 +37,8 @@ void ImageTransportFactory::InitializeForUnitTests( DCHECK(!g_initialized_for_unit_tests); g_initialized_for_unit_tests = true; - CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kEnablePixelOutputInTests)) g_disable_null_draw = new gfx::DisableNullDrawGLBindings; diff --git a/content/browser/compositor/overlay_candidate_validator_ozone.cc b/content/browser/compositor/overlay_candidate_validator_ozone.cc index b4a3cfeace00a..a92dc1011ded7 100644 --- a/content/browser/compositor/overlay_candidate_validator_ozone.cc +++ b/content/browser/compositor/overlay_candidate_validator_ozone.cc @@ -15,6 +15,7 @@ static ui::SurfaceFactoryOzone::BufferFormat GetOzoneFormat( return ui::SurfaceFactoryOzone::RGBA_8888; case cc::RGBA_4444: case cc::BGRA_8888: + case cc::ALPHA_8: case cc::LUMINANCE_8: case cc::RGB_565: case cc::ETC1: diff --git a/content/browser/cross_site_request_manager.cc b/content/browser/cross_site_request_manager.cc index 2fa38b185ab37..5ce71bf8f3053 100644 --- a/content/browser/cross_site_request_manager.cc +++ b/content/browser/cross_site_request_manager.cc @@ -9,24 +9,24 @@ namespace content { bool CrossSiteRequestManager::HasPendingCrossSiteRequest(int renderer_id, - int render_view_id) { + int render_frame_id) { base::AutoLock lock(lock_); - std::pair key(renderer_id, render_view_id); - return pending_cross_site_views_.find(key) != - pending_cross_site_views_.end(); + std::pair key(renderer_id, render_frame_id); + return pending_cross_site_frames_.find(key) != + pending_cross_site_frames_.end(); } void CrossSiteRequestManager::SetHasPendingCrossSiteRequest(int renderer_id, - int render_view_id, + int render_frame_id, bool has_pending) { base::AutoLock lock(lock_); - std::pair key(renderer_id, render_view_id); + std::pair key(renderer_id, render_frame_id); if (has_pending) { - pending_cross_site_views_.insert(key); + pending_cross_site_frames_.insert(key); } else { - pending_cross_site_views_.erase(key); + pending_cross_site_frames_.erase(key); } } diff --git a/content/browser/cross_site_request_manager.h b/content/browser/cross_site_request_manager.h index 29417d7a86863..bf04c8a56557d 100644 --- a/content/browser/cross_site_request_manager.h +++ b/content/browser/cross_site_request_manager.h @@ -17,7 +17,7 @@ namespace content { // CrossSiteRequestManager is used to handle bookkeeping for cross-site // requests and responses between the UI and IO threads. Such requests involve -// a transition from one RenderViewHost to another within WebContentsImpl, and +// a transition from one RenderFrameHost to another within WebContentsImpl, and // involve coordination with ResourceDispatcherHost. // // CrossSiteRequestManager is a singleton that may be used on any thread. @@ -27,22 +27,22 @@ class CrossSiteRequestManager { // Returns the singleton instance. static CrossSiteRequestManager* GetInstance(); - // Returns whether the RenderViewHost specified by the given IDs currently + // Returns whether the RenderFrameHost specified by the given IDs currently // has a pending cross-site request. If so, we will have to delay the - // response until the previous RenderViewHost runs its onunload handler. - // Called by ResourceDispatcherHost on the IO thread and RenderViewHost on + // response until the previous RenderFrameHost runs its onunload handler. + // Called by ResourceDispatcherHost on the IO thread and RenderFrameHost on // the UI thread. - bool HasPendingCrossSiteRequest(int renderer_id, int render_view_id); + bool HasPendingCrossSiteRequest(int renderer_id, int render_frame_id); - // Sets whether the RenderViewHost specified by the given IDs currently has a - // pending cross-site request. Called by RenderViewHost on the UI thread. + // Sets whether the RenderFrameHost specified by the given IDs currently has a + // pending cross-site request. Called by RenderFrameHost on the UI thread. void SetHasPendingCrossSiteRequest(int renderer_id, - int render_view_id, + int render_frame_id, bool has_pending); private: friend struct DefaultSingletonTraits; - typedef std::set > RenderViewSet; + typedef std::set > RenderFrameSet; CrossSiteRequestManager(); ~CrossSiteRequestManager(); @@ -51,10 +51,10 @@ class CrossSiteRequestManager { // class. You must not block while holding this lock. base::Lock lock_; - // Set of (render_process_host_id, render_view_id) pairs of all - // RenderViewHosts that have pending cross-site requests. Used to pass - // information about the RenderViewHosts between the UI and IO threads. - RenderViewSet pending_cross_site_views_; + // Set of (render_process_host_id, render_frame_id) pairs of all + // RenderFrameHosts that have pending cross-site requests. Used to pass + // information about the RenderFrameHosts between the UI and IO threads. + RenderFrameSet pending_cross_site_frames_; DISALLOW_COPY_AND_ASSIGN(CrossSiteRequestManager); }; diff --git a/content/browser/devtools/devtools_agent_host_impl.cc b/content/browser/devtools/devtools_agent_host_impl.cc index bcb60b55b7832..9d4e707165c79 100644 --- a/content/browser/devtools/devtools_agent_host_impl.cc +++ b/content/browser/devtools/devtools_agent_host_impl.cc @@ -60,13 +60,15 @@ std::string DevToolsAgentHostImpl::GetId() { return id_; } -RenderViewHost* DevToolsAgentHostImpl::GetRenderViewHost() { +WebContents* DevToolsAgentHostImpl::GetWebContents() { return NULL; } -void DevToolsAgentHostImpl::DisconnectRenderViewHost() {} +void DevToolsAgentHostImpl::DisconnectWebContents() { +} -void DevToolsAgentHostImpl::ConnectRenderViewHost(RenderViewHost* rvh) {} +void DevToolsAgentHostImpl::ConnectWebContents(WebContents* wc) { +} bool DevToolsAgentHostImpl::IsWorker() const { return false; diff --git a/content/browser/devtools/devtools_agent_host_impl.h b/content/browser/devtools/devtools_agent_host_impl.h index d0fad668b4925..d20a8b7935051 100644 --- a/content/browser/devtools/devtools_agent_host_impl.h +++ b/content/browser/devtools/devtools_agent_host_impl.h @@ -47,11 +47,11 @@ class CONTENT_EXPORT DevToolsAgentHostImpl : public DevToolsAgentHost { virtual std::string GetId() OVERRIDE; - virtual RenderViewHost* GetRenderViewHost() OVERRIDE; + virtual WebContents* GetWebContents() OVERRIDE; - virtual void DisconnectRenderViewHost() OVERRIDE; + virtual void DisconnectWebContents() OVERRIDE; - virtual void ConnectRenderViewHost(RenderViewHost* rvh) OVERRIDE; + virtual void ConnectWebContents(WebContents* rvh) OVERRIDE; virtual bool IsWorker() const OVERRIDE; diff --git a/content/browser/devtools/devtools_manager_unittest.cc b/content/browser/devtools/devtools_manager_unittest.cc index 75c527388b4fa..7690a96a4881e 100644 --- a/content/browser/devtools/devtools_manager_unittest.cc +++ b/content/browser/devtools/devtools_manager_unittest.cc @@ -101,7 +101,7 @@ TEST_F(DevToolsManagerTest, OpenAndManuallyCloseDevToolsClientHost) { DevToolsManager* manager = DevToolsManager::GetInstance(); scoped_refptr agent( - DevToolsAgentHost::GetOrCreateFor(rvh())); + DevToolsAgentHost::GetOrCreateFor(web_contents())); EXPECT_FALSE(agent->IsAttached()); TestDevToolsClientHost client_host; @@ -121,12 +121,12 @@ TEST_F(DevToolsManagerTest, ForwardMessageToClient) { TestDevToolsClientHost client_host; scoped_refptr agent_host( - DevToolsAgentHost::GetOrCreateFor(rvh())); + DevToolsAgentHost::GetOrCreateFor(web_contents())); manager->RegisterDevToolsClientHostFor(agent_host.get(), &client_host); EXPECT_EQ(0, TestDevToolsClientHost::close_counter); std::string m = "test message"; - agent_host = DevToolsAgentHost::GetOrCreateFor(rvh()); + agent_host = DevToolsAgentHost::GetOrCreateFor(web_contents()); manager->DispatchOnInspectorFrontend(agent_host.get(), m); EXPECT_TRUE(&m == client_host.last_sent_message); @@ -142,8 +142,8 @@ TEST_F(DevToolsManagerTest, NoUnresponsiveDialogInInspectedContents) { contents()->SetDelegate(&delegate); TestDevToolsClientHost client_host; - scoped_refptr agent_host( - DevToolsAgentHost::GetOrCreateFor(inspected_rvh)); + scoped_refptr agent_host(DevToolsAgentHost::GetOrCreateFor( + WebContents::FromRenderViewHost(inspected_rvh))); DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( agent_host.get(), &client_host); @@ -177,13 +177,14 @@ TEST_F(DevToolsManagerTest, ReattachOnCancelPendingNavigation) { const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(rvh(), 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate( + contents()->GetMainFrame(), 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); TestDevToolsClientHost client_host; DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); devtools_manager->RegisterDevToolsClientHostFor( - DevToolsAgentHost::GetOrCreateFor(rvh()).get(), &client_host); + DevToolsAgentHost::GetOrCreateFor(web_contents()).get(), &client_host); // Navigate to new site which should get a new RenderViewHost. const GURL url2("http://www.yahoo.com"); @@ -191,15 +192,16 @@ TEST_F(DevToolsManagerTest, ReattachOnCancelPendingNavigation) { url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(devtools_manager->GetDevToolsAgentHostFor(&client_host), - DevToolsAgentHost::GetOrCreateFor(pending_rvh())); + DevToolsAgentHost::GetOrCreateFor(web_contents())); // Interrupt pending navigation and navigate back to the original site. controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(rvh(), 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate( + contents()->GetMainFrame(), 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); EXPECT_EQ(devtools_manager->GetDevToolsAgentHostFor(&client_host), - DevToolsAgentHost::GetOrCreateFor(rvh())); + DevToolsAgentHost::GetOrCreateFor(web_contents())); client_host.Close(DevToolsManager::GetInstance()); } diff --git a/content/browser/devtools/embedded_worker_devtools_agent_host.cc b/content/browser/devtools/embedded_worker_devtools_agent_host.cc new file mode 100644 index 0000000000000..9f604001134b6 --- /dev/null +++ b/content/browser/devtools/embedded_worker_devtools_agent_host.cc @@ -0,0 +1,159 @@ +// 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 "content/browser/devtools/embedded_worker_devtools_agent_host.h" + +#include "content/browser/devtools/devtools_manager_impl.h" +#include "content/browser/devtools/devtools_protocol.h" +#include "content/browser/devtools/devtools_protocol_constants.h" +#include "content/common/devtools_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" + +namespace content { + +EmbeddedWorkerDevToolsAgentHost::EmbeddedWorkerDevToolsAgentHost( + WorkerId worker_id, + const SharedWorkerInstance& shared_worker) + : shared_worker_(new SharedWorkerInstance(shared_worker)), + state_(WORKER_UNINSPECTED), + worker_id_(worker_id) { + WorkerCreated(); +} + +EmbeddedWorkerDevToolsAgentHost::EmbeddedWorkerDevToolsAgentHost( + WorkerId worker_id, + const ServiceWorkerIdentifier& service_worker, + bool debug_service_worker_on_start) + : service_worker_(new ServiceWorkerIdentifier(service_worker)), + state_(WORKER_UNINSPECTED), + worker_id_(worker_id) { + if (debug_service_worker_on_start) + state_ = WORKER_PAUSED_FOR_DEBUG_ON_START; + WorkerCreated(); +} + +bool EmbeddedWorkerDevToolsAgentHost::IsWorker() const { + return true; +} + +void EmbeddedWorkerDevToolsAgentHost::SendMessageToAgent( + IPC::Message* message_raw) { + scoped_ptr message(message_raw); + if (state_ != WORKER_INSPECTED) + return; + if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) { + message->set_routing_id(worker_id_.second); + host->Send(message.release()); + } +} + +void EmbeddedWorkerDevToolsAgentHost::Attach() { + if (state_ != WORKER_INSPECTED) { + state_ = WORKER_INSPECTED; + AttachToWorker(); + } + IPCDevToolsAgentHost::Attach(); +} + +void EmbeddedWorkerDevToolsAgentHost::OnClientDetached() { + if (state_ == WORKER_INSPECTED) { + state_ = WORKER_UNINSPECTED; + DetachFromWorker(); + } else if (state_ == WORKER_PAUSED_FOR_REATTACH) { + state_ = WORKER_UNINSPECTED; + } +} + +bool EmbeddedWorkerDevToolsAgentHost::OnMessageReceived( + const IPC::Message& msg) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost, msg) + IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, + OnDispatchOnInspectorFrontend) + IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState, + OnSaveAgentRuntimeState) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void EmbeddedWorkerDevToolsAgentHost::WorkerContextStarted() { + if (state_ == WORKER_PAUSED_FOR_DEBUG_ON_START) { + RenderProcessHost* rph = RenderProcessHost::FromID(worker_id_.first); + DevToolsManagerImpl::GetInstance()->Inspect(rph->GetBrowserContext(), this); + } else if (state_ == WORKER_PAUSED_FOR_REATTACH) { + DCHECK(IsAttached()); + state_ = WORKER_INSPECTED; + AttachToWorker(); + Reattach(saved_agent_state_); + } +} + +void EmbeddedWorkerDevToolsAgentHost::WorkerRestarted(WorkerId worker_id) { + DCHECK_EQ(WORKER_TERMINATED, state_); + state_ = IsAttached() ? WORKER_PAUSED_FOR_REATTACH : WORKER_UNINSPECTED; + worker_id_ = worker_id; + WorkerCreated(); +} + +void EmbeddedWorkerDevToolsAgentHost::WorkerDestroyed() { + DCHECK_NE(WORKER_TERMINATED, state_); + if (state_ == WORKER_INSPECTED) { + DCHECK(IsAttached()); + // Client host is debugging this worker agent host. + std::string notification = + DevToolsProtocol::CreateNotification( + devtools::Worker::disconnectedFromWorker::kName, NULL)->Serialize(); + DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend( + this, notification); + DetachFromWorker(); + } + state_ = WORKER_TERMINATED; + Release(); // Balanced in WorkerCreated() +} + +bool EmbeddedWorkerDevToolsAgentHost::Matches( + const SharedWorkerInstance& other) { + return shared_worker_ && shared_worker_->Matches(other); +} + +bool EmbeddedWorkerDevToolsAgentHost::Matches( + const ServiceWorkerIdentifier& other) { + return service_worker_ && service_worker_->Matches(other); +} + +EmbeddedWorkerDevToolsAgentHost::~EmbeddedWorkerDevToolsAgentHost() { + DCHECK_EQ(WORKER_TERMINATED, state_); + EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData( + worker_id_); +} + +void EmbeddedWorkerDevToolsAgentHost::AttachToWorker() { + if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) + host->AddRoute(worker_id_.second, this); +} + +void EmbeddedWorkerDevToolsAgentHost::DetachFromWorker() { + if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) + host->RemoveRoute(worker_id_.second); +} + +void EmbeddedWorkerDevToolsAgentHost::WorkerCreated() { + AddRef(); // Balanced in WorkerDestroyed() +} + +void EmbeddedWorkerDevToolsAgentHost::OnDispatchOnInspectorFrontend( + const std::string& message) { + DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend( + this, message); +} + +void EmbeddedWorkerDevToolsAgentHost::OnSaveAgentRuntimeState( + const std::string& state) { + saved_agent_state_ = state; +} + +} // namespace content diff --git a/content/browser/devtools/embedded_worker_devtools_agent_host.h b/content/browser/devtools/embedded_worker_devtools_agent_host.h new file mode 100644 index 0000000000000..0b186c9b504ec --- /dev/null +++ b/content/browser/devtools/embedded_worker_devtools_agent_host.h @@ -0,0 +1,77 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_AGENT_HOST_H_ +#define CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_AGENT_HOST_H_ + +#include "content/browser/devtools/embedded_worker_devtools_manager.h" +#include "content/browser/devtools/ipc_devtools_agent_host.h" +#include "ipc/ipc_listener.h" + +namespace content { + +class SharedWorkerInstance; + +class EmbeddedWorkerDevToolsAgentHost : public IPCDevToolsAgentHost, + public IPC::Listener { + public: + typedef EmbeddedWorkerDevToolsManager::WorkerId WorkerId; + typedef EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier + ServiceWorkerIdentifier; + + EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id, + const SharedWorkerInstance& shared_worker); + + EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id, + const ServiceWorkerIdentifier& service_worker, + bool debug_service_worker_on_start); + + // DevToolsAgentHost override. + virtual bool IsWorker() const OVERRIDE; + + // IPCDevToolsAgentHost implementation. + virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE; + virtual void Attach() OVERRIDE; + virtual void OnClientAttached() OVERRIDE {} + virtual void OnClientDetached() OVERRIDE; + + // IPC::Listener implementation. + virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE; + + void WorkerContextStarted(); + void WorkerRestarted(WorkerId worker_id); + void WorkerDestroyed(); + bool Matches(const SharedWorkerInstance& other); + bool Matches(const ServiceWorkerIdentifier& other); + + private: + friend class EmbeddedWorkerDevToolsManagerTest; + + virtual ~EmbeddedWorkerDevToolsAgentHost(); + + enum WorkerState { + WORKER_UNINSPECTED, + WORKER_INSPECTED, + WORKER_TERMINATED, + WORKER_PAUSED_FOR_DEBUG_ON_START, + WORKER_PAUSED_FOR_REATTACH, + }; + + void AttachToWorker(); + void DetachFromWorker(); + void WorkerCreated(); + void OnDispatchOnInspectorFrontend(const std::string& message); + void OnSaveAgentRuntimeState(const std::string& state); + + scoped_ptr shared_worker_; + scoped_ptr service_worker_; + WorkerState state_; + WorkerId worker_id_; + std::string saved_agent_state_; + DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_AGENT_HOST_H_ diff --git a/content/browser/devtools/embedded_worker_devtools_manager.cc b/content/browser/devtools/embedded_worker_devtools_manager.cc index 0d9b7e9cd3444..54e90522f9435 100644 --- a/content/browser/devtools/embedded_worker_devtools_manager.cc +++ b/content/browser/devtools/embedded_worker_devtools_manager.cc @@ -7,6 +7,7 @@ #include "content/browser/devtools/devtools_manager_impl.h" #include "content/browser/devtools/devtools_protocol.h" #include "content/browser/devtools/devtools_protocol_constants.h" +#include "content/browser/devtools/embedded_worker_devtools_agent_host.h" #include "content/browser/devtools/ipc_devtools_agent_host.h" #include "content/browser/shared_worker/shared_worker_instance.h" #include "content/common/devtools_messages.h" @@ -26,23 +27,6 @@ scoped_refptr DevToolsAgentHost::GetForWorker( ->GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id); } -namespace { - -bool SendMessageToWorker( - const EmbeddedWorkerDevToolsManager::WorkerId& worker_id, - IPC::Message* message) { - RenderProcessHost* host = RenderProcessHost::FromID(worker_id.first); - if (!host) { - delete message; - return false; - } - message->set_routing_id(worker_id.second); - host->Send(message); - return true; -} - -} // namespace - EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::ServiceWorkerIdentifier( const ServiceWorkerContextCore* const service_worker_context, int64 service_worker_version_id) @@ -62,126 +46,6 @@ bool EmbeddedWorkerDevToolsManager::ServiceWorkerIdentifier::Matches( service_worker_version_id_ == other.service_worker_version_id_; } -EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo( - const SharedWorkerInstance& instance) - : shared_worker_instance_(new SharedWorkerInstance(instance)), - state_(WORKER_UNINSPECTED), - agent_host_(NULL) { -} - -EmbeddedWorkerDevToolsManager::WorkerInfo::WorkerInfo( - const ServiceWorkerIdentifier& service_worker_id) - : service_worker_id_(new ServiceWorkerIdentifier(service_worker_id)), - state_(WORKER_UNINSPECTED), - agent_host_(NULL) { -} - -bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches( - const SharedWorkerInstance& other) { - if (!shared_worker_instance_) - return false; - return shared_worker_instance_->Matches(other); -} - -bool EmbeddedWorkerDevToolsManager::WorkerInfo::Matches( - const ServiceWorkerIdentifier& other) { - if (!service_worker_id_) - return false; - return service_worker_id_->Matches(other); -} - -EmbeddedWorkerDevToolsManager::WorkerInfo::~WorkerInfo() { -} - -class EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsAgentHost - : public IPCDevToolsAgentHost, - public IPC::Listener { - public: - explicit EmbeddedWorkerDevToolsAgentHost(WorkerId worker_id) - : worker_id_(worker_id), worker_attached_(false) { - AttachToWorker(); - } - - // DevToolsAgentHost override. - virtual bool IsWorker() const OVERRIDE { return true; } - - // IPCDevToolsAgentHost implementation. - virtual void SendMessageToAgent(IPC::Message* message) OVERRIDE { - if (worker_attached_) - SendMessageToWorker(worker_id_, message); - else - delete message; - } - virtual void Attach() OVERRIDE { - AttachToWorker(); - IPCDevToolsAgentHost::Attach(); - } - virtual void OnClientAttached() OVERRIDE {} - virtual void OnClientDetached() OVERRIDE { DetachFromWorker(); } - - // IPC::Listener implementation. - virtual bool OnMessageReceived(const IPC::Message& msg) OVERRIDE { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - bool handled = true; - IPC_BEGIN_MESSAGE_MAP(EmbeddedWorkerDevToolsAgentHost, msg) - IPC_MESSAGE_HANDLER(DevToolsClientMsg_DispatchOnInspectorFrontend, - OnDispatchOnInspectorFrontend) - IPC_MESSAGE_HANDLER(DevToolsHostMsg_SaveAgentRuntimeState, - OnSaveAgentRuntimeState) - IPC_MESSAGE_UNHANDLED(handled = false) - IPC_END_MESSAGE_MAP() - return handled; - } - - void ReattachToWorker(WorkerId worker_id) { - CHECK(!worker_attached_); - worker_id_ = worker_id; - if (!IsAttached()) - return; - AttachToWorker(); - Reattach(state_); - } - - void DetachFromWorker() { - if (!worker_attached_) - return; - worker_attached_ = false; - if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) - host->RemoveRoute(worker_id_.second); - Release(); - } - - WorkerId worker_id() const { return worker_id_; } - - private: - virtual ~EmbeddedWorkerDevToolsAgentHost() { - CHECK(!worker_attached_); - EmbeddedWorkerDevToolsManager::GetInstance()->RemoveInspectedWorkerData( - this); - } - - void OnDispatchOnInspectorFrontend(const std::string& message) { - DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend(this, - message); - } - - void OnSaveAgentRuntimeState(const std::string& state) { state_ = state; } - - void AttachToWorker() { - if (worker_attached_) - return; - worker_attached_ = true; - AddRef(); - if (RenderProcessHost* host = RenderProcessHost::FromID(worker_id_.first)) - host->AddRoute(worker_id_.second, this); - } - - WorkerId worker_id_; - bool worker_attached_; - std::string state_; - DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerDevToolsAgentHost); -}; - // static EmbeddedWorkerDevToolsManager* EmbeddedWorkerDevToolsManager::GetInstance() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); @@ -191,32 +55,9 @@ EmbeddedWorkerDevToolsManager* EmbeddedWorkerDevToolsManager::GetInstance() { DevToolsAgentHost* EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForWorker( int worker_process_id, int worker_route_id) { - WorkerId id(worker_process_id, worker_route_id); - - WorkerInfoMap::iterator it = workers_.find(id); - if (it == workers_.end()) - return NULL; - - WorkerInfo* info = it->second; - if (info->state() != WORKER_UNINSPECTED && - info->state() != WORKER_PAUSED_FOR_DEBUG_ON_START) { - return info->agent_host(); - } - - EmbeddedWorkerDevToolsAgentHost* agent_host = - new EmbeddedWorkerDevToolsAgentHost(id); - info->set_agent_host(agent_host); - info->set_state(WORKER_INSPECTED); - return agent_host; -} - -DevToolsAgentHost* -EmbeddedWorkerDevToolsManager::GetDevToolsAgentHostForServiceWorker( - const ServiceWorkerIdentifier& service_worker_id) { - WorkerInfoMap::iterator it = FindExistingServiceWorkerInfo(service_worker_id); - if (it == workers_.end()) - return NULL; - return GetDevToolsAgentHostForWorker(it->first.first, it->first.second); + AgentHostMap::iterator it = workers_.find( + WorkerId(worker_process_id, worker_route_id)); + return it == workers_.end() ? NULL : it->second; } EmbeddedWorkerDevToolsManager::EmbeddedWorkerDevToolsManager() @@ -232,13 +73,12 @@ bool EmbeddedWorkerDevToolsManager::SharedWorkerCreated( const SharedWorkerInstance& instance) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const WorkerId id(worker_process_id, worker_route_id); - WorkerInfoMap::iterator it = FindExistingSharedWorkerInfo(instance); + AgentHostMap::iterator it = FindExistingSharedWorkerAgentHost(instance); if (it == workers_.end()) { - scoped_ptr info(new WorkerInfo(instance)); - workers_.set(id, info.Pass()); + workers_[id] = new EmbeddedWorkerDevToolsAgentHost(id, instance); return false; } - MoveToPausedState(id, it); + WorkerRestarted(id, it); return true; } @@ -248,15 +88,14 @@ bool EmbeddedWorkerDevToolsManager::ServiceWorkerCreated( const ServiceWorkerIdentifier& service_worker_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const WorkerId id(worker_process_id, worker_route_id); - WorkerInfoMap::iterator it = FindExistingServiceWorkerInfo(service_worker_id); + AgentHostMap::iterator it = + FindExistingServiceWorkerAgentHost(service_worker_id); if (it == workers_.end()) { - scoped_ptr info(new WorkerInfo(service_worker_id)); - if (debug_service_worker_on_start_) - info->set_state(WORKER_PAUSED_FOR_DEBUG_ON_START); - workers_.set(id, info.Pass()); + workers_[id] = new EmbeddedWorkerDevToolsAgentHost( + id, service_worker_id, debug_service_worker_on_start_); return debug_service_worker_on_start_; } - MoveToPausedState(id, it); + WorkerRestarted(id, it); return true; } @@ -264,96 +103,29 @@ void EmbeddedWorkerDevToolsManager::WorkerDestroyed(int worker_process_id, int worker_route_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const WorkerId id(worker_process_id, worker_route_id); - WorkerInfoMap::iterator it = workers_.find(id); + AgentHostMap::iterator it = workers_.find(id); DCHECK(it != workers_.end()); - WorkerInfo* info = it->second; - switch (info->state()) { - case WORKER_UNINSPECTED: - case WORKER_PAUSED_FOR_DEBUG_ON_START: - workers_.erase(it); - break; - case WORKER_INSPECTED: { - EmbeddedWorkerDevToolsAgentHost* agent_host = info->agent_host(); - info->set_state(WORKER_TERMINATED); - if (!agent_host->IsAttached()) { - agent_host->DetachFromWorker(); - return; - } - // Client host is debugging this worker agent host. - std::string notification = - DevToolsProtocol::CreateNotification( - devtools::Worker::disconnectedFromWorker::kName, NULL) - ->Serialize(); - DevToolsManagerImpl::GetInstance()->DispatchOnInspectorFrontend( - agent_host, notification); - agent_host->DetachFromWorker(); - break; - } - case WORKER_TERMINATED: - NOTREACHED(); - break; - case WORKER_PAUSED_FOR_REATTACH: { - scoped_ptr worker_info = workers_.take_and_erase(it); - worker_info->set_state(WORKER_TERMINATED); - const WorkerId old_id = worker_info->agent_host()->worker_id(); - workers_.set(old_id, worker_info.Pass()); - break; - } - } + it->second->WorkerDestroyed(); } void EmbeddedWorkerDevToolsManager::WorkerContextStarted(int worker_process_id, int worker_route_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); const WorkerId id(worker_process_id, worker_route_id); - WorkerInfoMap::iterator it = workers_.find(id); + AgentHostMap::iterator it = workers_.find(id); DCHECK(it != workers_.end()); - WorkerInfo* info = it->second; - if (info->state() == WORKER_PAUSED_FOR_DEBUG_ON_START) { - RenderProcessHost* rph = RenderProcessHost::FromID(worker_process_id); - scoped_refptr agent_host( - GetDevToolsAgentHostForWorker(worker_process_id, worker_route_id)); - DevToolsManagerImpl::GetInstance()->Inspect(rph->GetBrowserContext(), - agent_host.get()); - } else if (info->state() == WORKER_PAUSED_FOR_REATTACH) { - info->agent_host()->ReattachToWorker(id); - info->set_state(WORKER_INSPECTED); - } + it->second->WorkerContextStarted(); } -void EmbeddedWorkerDevToolsManager::RemoveInspectedWorkerData( - EmbeddedWorkerDevToolsAgentHost* agent_host) { +void EmbeddedWorkerDevToolsManager::RemoveInspectedWorkerData(WorkerId id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - const WorkerId id(agent_host->worker_id()); - scoped_ptr worker_info = workers_.take_and_erase(id); - if (worker_info) { - DCHECK_EQ(worker_info->agent_host(), agent_host); - if (worker_info->state() == WORKER_TERMINATED) - return; - DCHECK_EQ(worker_info->state(), WORKER_INSPECTED); - worker_info->set_agent_host(NULL); - worker_info->set_state(WORKER_UNINSPECTED); - workers_.set(id, worker_info.Pass()); - return; - } - for (WorkerInfoMap::iterator it = workers_.begin(); it != workers_.end(); - ++it) { - if (it->second->agent_host() == agent_host) { - DCHECK_EQ(WORKER_PAUSED_FOR_REATTACH, it->second->state()); - SendMessageToWorker( - it->first, - new DevToolsAgentMsg_ResumeWorkerContext(it->first.second)); - it->second->set_agent_host(NULL); - it->second->set_state(WORKER_UNINSPECTED); - return; - } - } + workers_.erase(id); } -EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator -EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerInfo( +EmbeddedWorkerDevToolsManager::AgentHostMap::iterator +EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerAgentHost( const SharedWorkerInstance& instance) { - WorkerInfoMap::iterator it = workers_.begin(); + AgentHostMap::iterator it = workers_.begin(); for (; it != workers_.end(); ++it) { if (it->second->Matches(instance)) break; @@ -361,10 +133,10 @@ EmbeddedWorkerDevToolsManager::FindExistingSharedWorkerInfo( return it; } -EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator -EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerInfo( +EmbeddedWorkerDevToolsManager::AgentHostMap::iterator +EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerAgentHost( const ServiceWorkerIdentifier& service_worker_id) { - WorkerInfoMap::iterator it = workers_.begin(); + AgentHostMap::iterator it = workers_.begin(); for (; it != workers_.end(); ++it) { if (it->second->Matches(service_worker_id)) break; @@ -372,13 +144,13 @@ EmbeddedWorkerDevToolsManager::FindExistingServiceWorkerInfo( return it; } -void EmbeddedWorkerDevToolsManager::MoveToPausedState( +void EmbeddedWorkerDevToolsManager::WorkerRestarted( const WorkerId& id, - const WorkerInfoMap::iterator& it) { - DCHECK_EQ(WORKER_TERMINATED, it->second->state()); - scoped_ptr info = workers_.take_and_erase(it); - info->set_state(WORKER_PAUSED_FOR_REATTACH); - workers_.set(id, info.Pass()); + const AgentHostMap::iterator& it) { + EmbeddedWorkerDevToolsAgentHost* agent_host = it->second; + agent_host->WorkerRestarted(id); + workers_.erase(it); + workers_[id] = agent_host; } void EmbeddedWorkerDevToolsManager::ResetForTesting() { diff --git a/content/browser/devtools/embedded_worker_devtools_manager.h b/content/browser/devtools/embedded_worker_devtools_manager.h index 3b15c8f209967..34e2ebce93e11 100644 --- a/content/browser/devtools/embedded_worker_devtools_manager.h +++ b/content/browser/devtools/embedded_worker_devtools_manager.h @@ -5,8 +5,9 @@ #ifndef CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_MANAGER_H_ #define CONTENT_BROWSER_DEVTOOLS_EMBEDDED_WORKER_DEVTOOLS_MANAGER_H_ +#include + #include "base/basictypes.h" -#include "base/containers/scoped_ptr_hash_map.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_vector.h" #include "base/memory/singleton.h" @@ -17,6 +18,7 @@ namespace content { class DevToolsAgentHost; +class EmbeddedWorkerDevToolsAgentHost; class ServiceWorkerContextCore; // EmbeddedWorkerDevToolsManager is used instead of WorkerDevToolsManager when @@ -25,7 +27,6 @@ class ServiceWorkerContextCore; class CONTENT_EXPORT EmbeddedWorkerDevToolsManager { public: typedef std::pair WorkerId; - class EmbeddedWorkerDevToolsAgentHost; class ServiceWorkerIdentifier { public: @@ -47,8 +48,6 @@ class CONTENT_EXPORT EmbeddedWorkerDevToolsManager { DevToolsAgentHost* GetDevToolsAgentHostForWorker(int worker_process_id, int worker_route_id); - DevToolsAgentHost* GetDevToolsAgentHostForServiceWorker( - const ServiceWorkerIdentifier& service_worker_id); // Returns true when the worker must be paused on start because a DevTool // window for the same former SharedWorkerInstance is still opened. @@ -73,60 +72,29 @@ class CONTENT_EXPORT EmbeddedWorkerDevToolsManager { private: friend struct DefaultSingletonTraits; + friend class EmbeddedWorkerDevToolsAgentHost; friend class EmbeddedWorkerDevToolsManagerTest; FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerDevToolsManagerTest, BasicTest); FRIEND_TEST_ALL_PREFIXES(EmbeddedWorkerDevToolsManagerTest, AttachTest); - enum WorkerState { - WORKER_UNINSPECTED, - WORKER_INSPECTED, - WORKER_TERMINATED, - WORKER_PAUSED_FOR_DEBUG_ON_START, - WORKER_PAUSED_FOR_REATTACH, - }; - - class WorkerInfo { - public: - // Creates WorkerInfo for SharedWorker. - explicit WorkerInfo(const SharedWorkerInstance& instance); - // Creates WorkerInfo for ServiceWorker. - explicit WorkerInfo(const ServiceWorkerIdentifier& service_worker_id); - ~WorkerInfo(); - - WorkerState state() { return state_; } - void set_state(WorkerState new_state) { state_ = new_state; } - EmbeddedWorkerDevToolsAgentHost* agent_host() { return agent_host_; } - void set_agent_host(EmbeddedWorkerDevToolsAgentHost* agent_host) { - agent_host_ = agent_host; - } - bool Matches(const SharedWorkerInstance& other); - bool Matches(const ServiceWorkerIdentifier& other); - - private: - scoped_ptr shared_worker_instance_; - scoped_ptr service_worker_id_; - WorkerState state_; - EmbeddedWorkerDevToolsAgentHost* agent_host_; - }; - - typedef base::ScopedPtrHashMap WorkerInfoMap; + typedef std::map AgentHostMap; EmbeddedWorkerDevToolsManager(); virtual ~EmbeddedWorkerDevToolsManager(); - void RemoveInspectedWorkerData(EmbeddedWorkerDevToolsAgentHost* agent_host); + void RemoveInspectedWorkerData(WorkerId id); - WorkerInfoMap::iterator FindExistingSharedWorkerInfo( + AgentHostMap::iterator FindExistingSharedWorkerAgentHost( const SharedWorkerInstance& instance); - WorkerInfoMap::iterator FindExistingServiceWorkerInfo( + AgentHostMap::iterator FindExistingServiceWorkerAgentHost( const ServiceWorkerIdentifier& service_worker_id); - void MoveToPausedState(const WorkerId& id, const WorkerInfoMap::iterator& it); + void WorkerRestarted(const WorkerId& id, const AgentHostMap::iterator& it); // Resets to its initial state as if newly created. void ResetForTesting(); - WorkerInfoMap workers_; + AgentHostMap workers_; bool debug_service_worker_on_start_; diff --git a/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc b/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc index f9fa31af0c117..347b6f39f4db0 100644 --- a/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc +++ b/content/browser/devtools/embedded_worker_devtools_manager_unittest.cc @@ -9,6 +9,7 @@ #include "base/run_loop.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/devtools/devtools_manager_impl.h" +#include "content/browser/devtools/embedded_worker_devtools_agent_host.h" #include "content/browser/shared_worker/shared_worker_instance.h" #include "content/browser/shared_worker/worker_storage_partition.h" #include "content/public/browser/devtools_agent_host.h" @@ -35,6 +36,8 @@ class TestDevToolsClientHost : public DevToolsClientHost { class EmbeddedWorkerDevToolsManagerTest : public testing::Test { public: + typedef EmbeddedWorkerDevToolsAgentHost::WorkerState WorkerState; + EmbeddedWorkerDevToolsManagerTest() : ui_thread_(BrowserThread::UI, &message_loop_), browser_context_(new TestBrowserContext()), @@ -59,13 +62,13 @@ class EmbeddedWorkerDevToolsManagerTest : public testing::Test { void CheckWorkerState(int worker_process_id, int worker_route_id, - EmbeddedWorkerDevToolsManager::WorkerState state) { + WorkerState state) { const EmbeddedWorkerDevToolsManager::WorkerId id(worker_process_id, worker_route_id); - EmbeddedWorkerDevToolsManager::WorkerInfoMap::iterator it = + EmbeddedWorkerDevToolsManager::AgentHostMap::iterator it = manager_->workers_.find(id); EXPECT_TRUE(manager_->workers_.end() != it); - EXPECT_EQ(state, it->second->state()); + EXPECT_EQ(state, it->second->state_); } void CheckWorkerNotExist(int worker_process_id, int worker_route_id) { @@ -112,67 +115,67 @@ TEST_F(EmbeddedWorkerDevToolsManagerTest, BasicTest) { // Created -> Started -> Destroyed CheckWorkerNotExist(1, 1); manager_->SharedWorkerCreated(1, 1, instance1); - CheckWorkerState(1, 1, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 1, WorkerState::WORKER_UNINSPECTED); manager_->WorkerContextStarted(1, 1); - CheckWorkerState(1, 1, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 1, WorkerState::WORKER_UNINSPECTED); manager_->WorkerDestroyed(1, 1); CheckWorkerNotExist(1, 1); // Created -> GetDevToolsAgentHost -> Started -> Destroyed CheckWorkerNotExist(1, 2); manager_->SharedWorkerCreated(1, 2, instance1); - CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 2, WorkerState::WORKER_UNINSPECTED); agent_host = manager_->GetDevToolsAgentHostForWorker(1, 2); EXPECT_TRUE(agent_host.get()); - CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(1, 2, WorkerState::WORKER_UNINSPECTED); EXPECT_EQ(agent_host.get(), manager_->GetDevToolsAgentHostForWorker(1, 2)); manager_->WorkerContextStarted(1, 2); - CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(1, 2, WorkerState::WORKER_UNINSPECTED); manager_->WorkerDestroyed(1, 2); - CheckWorkerState(1, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(1, 2, WorkerState::WORKER_TERMINATED); agent_host = NULL; CheckWorkerNotExist(1, 2); // Created -> Started -> GetDevToolsAgentHost -> Destroyed CheckWorkerNotExist(1, 3); manager_->SharedWorkerCreated(1, 3, instance1); - CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 3, WorkerState::WORKER_UNINSPECTED); manager_->WorkerContextStarted(1, 3); - CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 3, WorkerState::WORKER_UNINSPECTED); agent_host = manager_->GetDevToolsAgentHostForWorker(1, 3); EXPECT_TRUE(agent_host.get()); - CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(1, 3, WorkerState::WORKER_UNINSPECTED); manager_->WorkerDestroyed(1, 3); - CheckWorkerState(1, 3, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(1, 3, WorkerState::WORKER_TERMINATED); agent_host = NULL; CheckWorkerNotExist(1, 3); // Created -> Destroyed CheckWorkerNotExist(1, 4); manager_->SharedWorkerCreated(1, 4, instance1); - CheckWorkerState(1, 4, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 4, WorkerState::WORKER_UNINSPECTED); manager_->WorkerDestroyed(1, 4); CheckWorkerNotExist(1, 4); // Created -> GetDevToolsAgentHost -> Destroyed CheckWorkerNotExist(1, 5); manager_->SharedWorkerCreated(1, 5, instance1); - CheckWorkerState(1, 5, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 5, WorkerState::WORKER_UNINSPECTED); agent_host = manager_->GetDevToolsAgentHostForWorker(1, 5); EXPECT_TRUE(agent_host.get()); - CheckWorkerState(1, 5, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(1, 5, WorkerState::WORKER_UNINSPECTED); manager_->WorkerDestroyed(1, 5); - CheckWorkerState(1, 5, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(1, 5, WorkerState::WORKER_TERMINATED); agent_host = NULL; CheckWorkerNotExist(1, 5); // Created -> GetDevToolsAgentHost -> Free agent_host -> Destroyed CheckWorkerNotExist(1, 6); manager_->SharedWorkerCreated(1, 6, instance1); - CheckWorkerState(1, 6, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(1, 6, WorkerState::WORKER_UNINSPECTED); agent_host = manager_->GetDevToolsAgentHostForWorker(1, 6); EXPECT_TRUE(agent_host.get()); - CheckWorkerState(1, 6, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(1, 6, WorkerState::WORKER_UNINSPECTED); agent_host = NULL; manager_->WorkerDestroyed(1, 6); CheckWorkerNotExist(1, 6); @@ -199,67 +202,64 @@ TEST_F(EmbeddedWorkerDevToolsManagerTest, AttachTest) { scoped_ptr client_host1(new TestDevToolsClientHost()); CheckWorkerNotExist(2, 1); manager_->SharedWorkerCreated(2, 1, instance1); - CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(2, 1, WorkerState::WORKER_UNINSPECTED); agent_host1 = manager_->GetDevToolsAgentHostForWorker(2, 1); EXPECT_TRUE(agent_host1.get()); - CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(2, 1, WorkerState::WORKER_UNINSPECTED); EXPECT_EQ(agent_host1.get(), manager_->GetDevToolsAgentHostForWorker(2, 1)); RegisterDevToolsClientHostFor(agent_host1.get(), client_host1.get()); - CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(2, 1, WorkerState::WORKER_INSPECTED); manager_->WorkerContextStarted(2, 1); - CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(2, 1, WorkerState::WORKER_INSPECTED); manager_->WorkerDestroyed(2, 1); - CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(2, 1, WorkerState::WORKER_TERMINATED); EXPECT_EQ(agent_host1.get(), manager_->GetDevToolsAgentHostForWorker(2, 1)); // Created -> Started -> GetDevToolsAgentHost -> Register -> Destroyed scoped_ptr client_host2(new TestDevToolsClientHost()); manager_->SharedWorkerCreated(2, 2, instance2); - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(2, 2, WorkerState::WORKER_UNINSPECTED); manager_->WorkerContextStarted(2, 2); - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_UNINSPECTED); + CheckWorkerState(2, 2, WorkerState::WORKER_UNINSPECTED); agent_host2 = manager_->GetDevToolsAgentHostForWorker(2, 2); EXPECT_TRUE(agent_host2.get()); EXPECT_NE(agent_host1.get(), agent_host2.get()); EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 2)); - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(2, 2, WorkerState::WORKER_UNINSPECTED); RegisterDevToolsClientHostFor(agent_host2.get(), client_host2.get()); - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(2, 2, WorkerState::WORKER_INSPECTED); manager_->WorkerDestroyed(2, 2); - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(2, 2, WorkerState::WORKER_TERMINATED); EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 2)); // Re-created -> Started -> ClientHostClosing -> Destroyed - CheckWorkerState(2, 1, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(2, 1, WorkerState::WORKER_TERMINATED); manager_->SharedWorkerCreated(2, 3, instance1); CheckWorkerNotExist(2, 1); - CheckWorkerState( - 2, 3, EmbeddedWorkerDevToolsManager::WORKER_PAUSED_FOR_REATTACH); + CheckWorkerState(2, 3, WorkerState::WORKER_PAUSED_FOR_REATTACH); EXPECT_EQ(agent_host1.get(), manager_->GetDevToolsAgentHostForWorker(2, 3)); manager_->WorkerContextStarted(2, 3); - CheckWorkerState(2, 3, EmbeddedWorkerDevToolsManager::WORKER_INSPECTED); + CheckWorkerState(2, 3, WorkerState::WORKER_INSPECTED); ClientHostClosing(client_host1.get()); manager_->WorkerDestroyed(2, 3); - CheckWorkerState(2, 3, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(2, 3, WorkerState::WORKER_TERMINATED); agent_host1 = NULL; CheckWorkerNotExist(2, 3); // Re-created -> Destroyed - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerState(2, 2, WorkerState::WORKER_TERMINATED); manager_->SharedWorkerCreated(2, 4, instance2); CheckWorkerNotExist(2, 2); - CheckWorkerState( - 2, 4, EmbeddedWorkerDevToolsManager::WORKER_PAUSED_FOR_REATTACH); + CheckWorkerState(2, 4, WorkerState::WORKER_PAUSED_FOR_REATTACH); EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 4)); manager_->WorkerDestroyed(2, 4); - CheckWorkerNotExist(2, 4); - CheckWorkerState(2, 2, EmbeddedWorkerDevToolsManager::WORKER_TERMINATED); + CheckWorkerNotExist(2, 2); + CheckWorkerState(2, 4, WorkerState::WORKER_TERMINATED); // Re-created -> ClientHostClosing -> Destroyed manager_->SharedWorkerCreated(2, 5, instance2); CheckWorkerNotExist(2, 2); - CheckWorkerState( - 2, 5, EmbeddedWorkerDevToolsManager::WORKER_PAUSED_FOR_REATTACH); + CheckWorkerState(2, 5, WorkerState::WORKER_PAUSED_FOR_REATTACH); EXPECT_EQ(agent_host2.get(), manager_->GetDevToolsAgentHostForWorker(2, 5)); ClientHostClosing(client_host2.get()); CheckWorkerCount(1); @@ -269,4 +269,35 @@ TEST_F(EmbeddedWorkerDevToolsManagerTest, AttachTest) { CheckWorkerCount(0); } +TEST_F(EmbeddedWorkerDevToolsManagerTest, ReattachTest) { + SharedWorkerInstance instance(GURL("http://example.com/w3.js"), + base::string16(), + base::string16(), + blink::WebContentSecurityPolicyTypeReport, + browser_context_->GetResourceContext(), + partition_id_); + scoped_ptr client_host(new TestDevToolsClientHost()); + // Created -> GetDevToolsAgentHost -> Register -> Destroyed + manager_->SharedWorkerCreated(3, 1, instance); + CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED); + scoped_refptr agent_host( + manager_->GetDevToolsAgentHostForWorker(3, 1)); + EXPECT_TRUE(agent_host.get()); + CheckWorkerState(3, 1, WorkerState::WORKER_UNINSPECTED); + RegisterDevToolsClientHostFor(agent_host.get(), client_host.get()); + CheckWorkerState(3, 1, WorkerState::WORKER_INSPECTED); + manager_->WorkerDestroyed(3, 1); + CheckWorkerState(3, 1, WorkerState::WORKER_TERMINATED); + // ClientHostClosing -> Re-created -> release agent_host -> Destroyed + ClientHostClosing(client_host.get()); + CheckWorkerState(3, 1, WorkerState::WORKER_TERMINATED); + manager_->SharedWorkerCreated(3, 2, instance); + CheckWorkerState(3, 2, WorkerState::WORKER_UNINSPECTED); + agent_host = NULL; + CheckWorkerState(3, 2, WorkerState::WORKER_UNINSPECTED); + manager_->WorkerDestroyed(3, 2); + CheckWorkerNotExist(3, 2); + CheckWorkerCount(0); +} + } // namespace content diff --git a/content/browser/devtools/render_view_devtools_agent_host.cc b/content/browser/devtools/render_view_devtools_agent_host.cc index d58d168bd6fc5..86c0867a71d82 100644 --- a/content/browser/devtools/render_view_devtools_agent_host.cc +++ b/content/browser/devtools/render_view_devtools_agent_host.cc @@ -42,23 +42,9 @@ base::LazyInstance::Leaky g_instances = LAZY_INSTANCE_INITIALIZER; static RenderViewDevToolsAgentHost* FindAgentHost(WebContents* web_contents) { if (g_instances == NULL) return NULL; - RenderViewHostDelegate* delegate = - static_cast(web_contents); for (Instances::iterator it = g_instances.Get().begin(); it != g_instances.Get().end(); ++it) { - RenderViewHost* rvh = (*it)->render_view_host(); - if (rvh && rvh->GetDelegate() == delegate) - return *it; - } - return NULL; -} - -static RenderViewDevToolsAgentHost* FindAgentHost(RenderViewHost* rvh) { - if (g_instances == NULL) - return NULL; - for (Instances::iterator it = g_instances.Get().begin(); - it != g_instances.Get().end(); ++it) { - if (rvh == (*it)->render_view_host()) + if ((*it)->GetWebContents() == web_contents) return *it; } return NULL; @@ -75,42 +61,19 @@ DevToolsAgentHost::GetOrCreateFor(WebContents* web_contents) { } // static -scoped_refptr -DevToolsAgentHost::GetOrCreateFor(RenderViewHost* rvh) { - RenderViewDevToolsAgentHost* result = FindAgentHost(rvh); - if (!result) - result = new RenderViewDevToolsAgentHost(rvh); - return result; -} - -// static -bool DevToolsAgentHost::HasFor(RenderViewHost* rvh) { - return FindAgentHost(rvh) != NULL; +bool DevToolsAgentHost::HasFor(WebContents* web_contents) { + return FindAgentHost(web_contents) != NULL; } // static bool DevToolsAgentHost::IsDebuggerAttached(WebContents* web_contents) { - if (g_instances == NULL) - return false; - DevToolsManager* devtools_manager = DevToolsManager::GetInstance(); - if (!devtools_manager) - return false; - RenderViewHostDelegate* delegate = - static_cast(web_contents); - for (Instances::iterator it = g_instances.Get().begin(); - it != g_instances.Get().end(); ++it) { - RenderViewHost* rvh = (*it)->render_view_host_; - if (rvh && rvh->GetDelegate() != delegate) - continue; - if ((*it)->IsAttached()) - return true; - } - return false; + RenderViewDevToolsAgentHost* agent_host = FindAgentHost(web_contents); + return agent_host && agent_host->IsAttached(); } //static -std::vector DevToolsAgentHost::GetValidRenderViewHosts() { - std::vector result; +std::vector DevToolsAgentHost::GetInspectableWebContents() { + std::set set; scoped_ptr widgets( RenderWidgetHost::GetRenderWidgetHosts()); while (RenderWidgetHost* widget = widgets->GetNextHost()) { @@ -122,23 +85,11 @@ std::vector DevToolsAgentHost::GetValidRenderViewHosts() { RenderViewHost* rvh = RenderViewHost::From(widget); WebContents* web_contents = WebContents::FromRenderViewHost(rvh); - if (!web_contents) - continue; - - // Don't report a RenderViewHost if it is not the current RenderViewHost - // for some WebContents (this filters out pre-render RVHs and similar). - // However report a RenderViewHost created for an out of process iframe. - // TODO (kaznacheev): Revisit this when it is clear how OOP iframes - // interact with pre-rendering. - // TODO (kaznacheev): GetMainFrame() call is a temporary hack. Iterate over - // all RenderFrameHost instances when multiple OOP frames are supported. - if (rvh != web_contents->GetRenderViewHost() && - !rvh->GetMainFrame()->IsCrossProcessSubframe()) { - continue; - } - - result.push_back(rvh); + if (web_contents) + set.insert(web_contents); } + std::vector result(set.size()); + std::copy(set.begin(), set.end(), result.begin()); return result; } @@ -146,24 +97,17 @@ std::vector DevToolsAgentHost::GetValidRenderViewHosts() { void RenderViewDevToolsAgentHost::OnCancelPendingNavigation( RenderViewHost* pending, RenderViewHost* current) { - RenderViewDevToolsAgentHost* agent_host = FindAgentHost(pending); + WebContents* web_contents = WebContents::FromRenderViewHost(pending); + RenderViewDevToolsAgentHost* agent_host = FindAgentHost(web_contents); if (!agent_host) return; agent_host->DisconnectRenderViewHost(); agent_host->ConnectRenderViewHost(current); } -// static -bool RenderViewDevToolsAgentHost::DispatchIPCMessage( - RenderViewHost* source, - const IPC::Message& message) { - RenderViewDevToolsAgentHost* agent_host = FindAgentHost(source); - return agent_host && agent_host->DispatchIPCMessage(message); -} - RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh) : render_view_host_(NULL), - overrides_handler_(new RendererOverridesHandler(this)), + overrides_handler_(new RendererOverridesHandler()), tracing_handler_( new DevToolsTracingHandler(DevToolsTracingHandler::Renderer)), power_handler_(new DevToolsPowerHandler()), @@ -179,8 +123,8 @@ RenderViewDevToolsAgentHost::RenderViewDevToolsAgentHost(RenderViewHost* rvh) AddRef(); // Balanced in RenderViewHostDestroyed. } -RenderViewHost* RenderViewDevToolsAgentHost::GetRenderViewHost() { - return render_view_host_; +WebContents* RenderViewDevToolsAgentHost::GetWebContents() { + return web_contents(); } void RenderViewDevToolsAgentHost::DispatchOnInspectorBackend( @@ -264,6 +208,11 @@ void RenderViewDevToolsAgentHost::OnClientDetached() { tracing_handler_->OnClientDetached(); power_handler_->OnClientDetached(); ClientDetachedFromRenderer(); + + // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when + // extensions::ProcessManager no longer relies on this notification. + if (!reattaching_) + DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false); } void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() { @@ -271,11 +220,6 @@ void RenderViewDevToolsAgentHost::ClientDetachedFromRenderer() { return; InnerClientDetachedFromRenderer(); - - // TODO(kaznacheev): Move this call back to DevToolsManagerImpl when - // extensions::ProcessManager no longer relies on this notification. - if (!reattaching_) - DevToolsManagerImpl::GetInstance()->NotifyObservers(this, false); } void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() { @@ -285,7 +229,7 @@ void RenderViewDevToolsAgentHost::InnerClientDetachedFromRenderer() { it != g_instances.Get().end(); ++it) { if (*it == this || !(*it)->IsAttached()) continue; - RenderViewHost* rvh = (*it)->render_view_host(); + RenderViewHost* rvh = (*it)->render_view_host_; if (rvh && rvh->GetProcess() == render_process_host) process_has_agents = true; } @@ -310,8 +254,8 @@ void RenderViewDevToolsAgentHost::AboutToNavigateRenderView( if (!render_view_host_) return; - if (render_view_host_ == dest_rvh && static_cast( - render_view_host_)->render_view_termination_status() == + if (render_view_host_ == dest_rvh && + render_view_host_->render_view_termination_status() == base::TERMINATION_STATUS_STILL_RUNNING) return; ReattachToRenderViewHost(dest_rvh); @@ -363,6 +307,17 @@ void RenderViewDevToolsAgentHost::RenderProcessGone( } } +bool RenderViewDevToolsAgentHost::OnMessageReceived( + const IPC::Message& message, + RenderFrameHost* render_frame_host) { + return DispatchIPCMessage(message); +} + +bool RenderViewDevToolsAgentHost::OnMessageReceived( + const IPC::Message& message) { + return DispatchIPCMessage(message); +} + void RenderViewDevToolsAgentHost::DidAttachInterstitialPage() { if (!render_view_host_) return; @@ -387,10 +342,10 @@ void RenderViewDevToolsAgentHost::Observe(int type, void RenderViewDevToolsAgentHost::SetRenderViewHost(RenderViewHost* rvh) { DCHECK(!render_view_host_); - render_view_host_ = rvh; + render_view_host_ = static_cast(rvh); WebContentsObserver::Observe(WebContents::FromRenderViewHost(rvh)); - overrides_handler_->OnRenderViewHostChanged(); + overrides_handler_->SetRenderViewHost(render_view_host_); registrar_.Add( this, @@ -405,6 +360,15 @@ void RenderViewDevToolsAgentHost::ClearRenderViewHost() { content::NOTIFICATION_RENDER_WIDGET_VISIBILITY_CHANGED, content::Source(render_view_host_)); render_view_host_ = NULL; + overrides_handler_->ClearRenderViewHost(); +} + +void RenderViewDevToolsAgentHost::DisconnectWebContents() { + DisconnectRenderViewHost(); +} + +void RenderViewDevToolsAgentHost::ConnectWebContents(WebContents* wc) { + ConnectRenderViewHost(wc->GetRenderViewHost()); } void RenderViewDevToolsAgentHost::ConnectRenderViewHost(RenderViewHost* rvh) { diff --git a/content/browser/devtools/render_view_devtools_agent_host.h b/content/browser/devtools/render_view_devtools_agent_host.h index 93fca3d8f2444..5a9d5b464cd22 100644 --- a/content/browser/devtools/render_view_devtools_agent_host.h +++ b/content/browser/devtools/render_view_devtools_agent_host.h @@ -26,6 +26,7 @@ class DevToolsPowerHandler; class DevToolsTracingHandler; class RendererOverridesHandler; class RenderViewHost; +class RenderViewHostImpl; #if defined(OS_ANDROID) class PowerSaveBlockerImpl; @@ -39,26 +40,20 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost static void OnCancelPendingNavigation(RenderViewHost* pending, RenderViewHost* current); - static bool DispatchIPCMessage(RenderViewHost* source, - const IPC::Message& message); - RenderViewDevToolsAgentHost(RenderViewHost*); - RenderViewHost* render_view_host() { return render_view_host_; } - void SynchronousSwapCompositorFrame( const cc::CompositorFrameMetadata& frame_metadata); + // DevTooolsAgentHost overrides. + virtual void DisconnectWebContents() OVERRIDE; + virtual void ConnectWebContents(WebContents* web_contents) OVERRIDE; + virtual WebContents* GetWebContents() OVERRIDE; + private: friend class DevToolsAgentHost; - virtual ~RenderViewDevToolsAgentHost(); - // DevTooolsAgentHost overrides. - virtual void DisconnectRenderViewHost() OVERRIDE; - virtual void ConnectRenderViewHost(RenderViewHost* rvh) OVERRIDE; - virtual RenderViewHost* GetRenderViewHost() OVERRIDE; - // IPCDevToolsAgentHost overrides. virtual void DispatchOnInspectorBackend(const std::string& message) OVERRIDE; virtual void SendMessageToAgent(IPC::Message* msg) OVERRIDE; @@ -71,6 +66,9 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost RenderViewHost* new_host) OVERRIDE; virtual void RenderViewDeleted(RenderViewHost* rvh) OVERRIDE; virtual void RenderProcessGone(base::TerminationStatus status) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message, + RenderFrameHost* render_frame_host) OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; virtual void DidAttachInterstitialPage() OVERRIDE; // NotificationObserver overrides: @@ -78,6 +76,8 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost const NotificationSource& source, const NotificationDetails& details) OVERRIDE; + void DisconnectRenderViewHost(); + void ConnectRenderViewHost(RenderViewHost* rvh); void ReattachToRenderViewHost(RenderViewHost* rvh); bool DispatchIPCMessage(const IPC::Message& message); @@ -97,7 +97,7 @@ class CONTENT_EXPORT RenderViewDevToolsAgentHost void InnerOnClientAttached(); void InnerClientDetachedFromRenderer(); - RenderViewHost* render_view_host_; + RenderViewHostImpl* render_view_host_; scoped_ptr overrides_handler_; scoped_ptr tracing_handler_; scoped_ptr power_handler_; diff --git a/content/browser/devtools/renderer_overrides_handler.cc b/content/browser/devtools/renderer_overrides_handler.cc index 9d61ce81af107..b068a85f084a5 100644 --- a/content/browser/devtools/renderer_overrides_handler.cc +++ b/content/browser/devtools/renderer_overrides_handler.cc @@ -67,9 +67,8 @@ static int kCaptureRetryLimit = 2; } // namespace -RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent) - : agent_(agent), - has_last_compositor_frame_metadata_(false), +RendererOverridesHandler::RendererOverridesHandler() + : has_last_compositor_frame_metadata_(false), capture_retry_count_(0), weak_factory_(this) { RegisterCommandHandler( @@ -151,10 +150,8 @@ RendererOverridesHandler::RendererOverridesHandler(DevToolsAgentHost* agent) RendererOverridesHandler::~RendererOverridesHandler() {} void RendererOverridesHandler::OnClientDetached() { - RenderViewHostImpl* host = static_cast( - agent_->GetRenderViewHost()); - if (screencast_command_ && host) - host->SetTouchEventEmulationEnabled(false, false); + if (screencast_command_ && host_) + host_->SetTouchEventEmulationEnabled(false, false); screencast_command_ = NULL; } @@ -173,13 +170,17 @@ void RendererOverridesHandler::OnVisibilityChanged(bool visible) { NotifyScreencastVisibility(visible); } -void RendererOverridesHandler::OnRenderViewHostChanged() { - RenderViewHostImpl* host = static_cast( - agent_->GetRenderViewHost()); +void RendererOverridesHandler::SetRenderViewHost( + RenderViewHostImpl* host) { + host_ = host; if (screencast_command_ && host) host->SetTouchEventEmulationEnabled(true, true); } +void RendererOverridesHandler::ClearRenderViewHost() { + host_ = NULL; +} + bool RendererOverridesHandler::OnSetTouchEventEmulationEnabled() { return screencast_command_.get() != NULL; } @@ -190,14 +191,13 @@ void RendererOverridesHandler::InnerSwapCompositorFrame() { return; } - RenderViewHost* host = agent_->GetRenderViewHost(); - if (!host->GetView()) + if (!host_ || !host_->GetView()) return; last_frame_time_ = base::TimeTicks::Now(); RenderWidgetHostViewBase* view = static_cast( - host->GetView()); + host_->GetView()); // TODO(vkuzkokov): do not use previous frame metadata. cc::CompositorFrameMetadata& metadata = last_compositor_frame_metadata_; @@ -272,8 +272,7 @@ RendererOverridesHandler::GrantPermissionsForSetFileInputFiles( devtools::DOM::setFileInputFiles::kParamFiles; if (!params || !params->GetList(param, &file_list)) return command->InvalidParamResponse(param); - RenderViewHost* host = agent_->GetRenderViewHost(); - if (!host) + if (!host_) return NULL; for (size_t i = 0; i < file_list->GetSize(); ++i) { @@ -281,7 +280,7 @@ RendererOverridesHandler::GrantPermissionsForSetFileInputFiles( if (!file_list->GetString(i, &file)) return command->InvalidParamResponse(param); ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( - host->GetProcess()->GetID(), base::FilePath(file)); + host_->GetProcess()->GetID(), base::FilePath(file)); } return NULL; } @@ -292,14 +291,14 @@ RendererOverridesHandler::GrantPermissionsForSetFileInputFiles( scoped_refptr RendererOverridesHandler::ClearBrowserCache( scoped_refptr command) { - GetContentClient()->browser()->ClearCache(agent_->GetRenderViewHost()); + GetContentClient()->browser()->ClearCache(host_); return command->SuccessResponse(NULL); } scoped_refptr RendererOverridesHandler::ClearBrowserCookies( scoped_refptr command) { - GetContentClient()->browser()->ClearCookies(agent_->GetRenderViewHost()); + GetContentClient()->browser()->ClearCookies(host_); return command->SuccessResponse(NULL); } @@ -309,10 +308,8 @@ RendererOverridesHandler::ClearBrowserCookies( scoped_refptr RendererOverridesHandler::PageDisable( scoped_refptr command) { - RenderViewHostImpl* host = static_cast( - agent_->GetRenderViewHost()); - if (screencast_command_ && host) - host->SetTouchEventEmulationEnabled(false, false); + if (screencast_command_ && host_) + host_->SetTouchEventEmulationEnabled(false, false); screencast_command_ = NULL; return NULL; } @@ -334,16 +331,16 @@ RendererOverridesHandler::PageHandleJavaScriptDialog( prompt_override_ptr = NULL; } - RenderViewHost* host = agent_->GetRenderViewHost(); - if (host) { - WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); - if (web_contents) { - JavaScriptDialogManager* manager = - web_contents->GetDelegate()->GetJavaScriptDialogManager(); - if (manager && manager->HandleJavaScriptDialog( - web_contents, accept, prompt_override_ptr)) { - return command->SuccessResponse(new base::DictionaryValue()); - } + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + + WebContents* web_contents = WebContents::FromRenderViewHost(host_); + if (web_contents) { + JavaScriptDialogManager* manager = + web_contents->GetDelegate()->GetJavaScriptDialogManager(); + if (manager && manager->HandleJavaScriptDialog( + web_contents, accept, prompt_override_ptr)) { + return command->SuccessResponse(new base::DictionaryValue()); } } return command->InternalErrorResponse("No JavaScript dialog to handle"); @@ -357,37 +354,39 @@ RendererOverridesHandler::PageNavigate( const char* param = devtools::Page::navigate::kParamUrl; if (!params || !params->GetString(param, &url)) return command->InvalidParamResponse(param); + GURL gurl(url); - if (!gurl.is_valid()) { + if (!gurl.is_valid()) return command->InternalErrorResponse("Cannot navigate to invalid URL"); + + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + + WebContents* web_contents = WebContents::FromRenderViewHost(host_); + if (web_contents) { + web_contents->GetController() + .LoadURL(gurl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); + // Fall through into the renderer. + return NULL; } - RenderViewHost* host = agent_->GetRenderViewHost(); - if (host) { - WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); - if (web_contents) { - web_contents->GetController() - .LoadURL(gurl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - // Fall through into the renderer. - return NULL; - } - } + return command->InternalErrorResponse("No WebContents to navigate"); } scoped_refptr RendererOverridesHandler::PageReload( scoped_refptr command) { - RenderViewHost* host = agent_->GetRenderViewHost(); - if (host) { - WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); - if (web_contents) { - // Override only if it is crashed. - if (!web_contents->IsCrashed()) - return NULL; - - web_contents->GetController().Reload(false); - return command->SuccessResponse(NULL); - } + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + + WebContents* web_contents = WebContents::FromRenderViewHost(host_); + if (web_contents) { + // Override only if it is crashed. + if (!web_contents->IsCrashed()) + return NULL; + + web_contents->GetController().Reload(false); + return command->SuccessResponse(NULL); } return command->InternalErrorResponse("No WebContents to reload"); } @@ -395,35 +394,34 @@ RendererOverridesHandler::PageReload( scoped_refptr RendererOverridesHandler::PageGetNavigationHistory( scoped_refptr command) { - RenderViewHost* host = agent_->GetRenderViewHost(); - if (host) { - WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); - if (web_contents) { - base::DictionaryValue* result = new base::DictionaryValue(); - NavigationController& controller = web_contents->GetController(); - result->SetInteger( - devtools::Page::getNavigationHistory::kResponseCurrentIndex, - controller.GetCurrentEntryIndex()); - base::ListValue* entries = new base::ListValue(); - for (int i = 0; i != controller.GetEntryCount(); ++i) { - const NavigationEntry* entry = controller.GetEntryAtIndex(i); - base::DictionaryValue* entry_value = new base::DictionaryValue(); - entry_value->SetInteger( - devtools::Page::NavigationEntry::kParamId, - entry->GetUniqueID()); - entry_value->SetString( - devtools::Page::NavigationEntry::kParamUrl, - entry->GetURL().spec()); - entry_value->SetString( - devtools::Page::NavigationEntry::kParamTitle, - entry->GetTitle()); - entries->Append(entry_value); - } - result->Set( - devtools::Page::getNavigationHistory::kResponseEntries, - entries); - return command->SuccessResponse(result); + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + WebContents* web_contents = WebContents::FromRenderViewHost(host_); + if (web_contents) { + base::DictionaryValue* result = new base::DictionaryValue(); + NavigationController& controller = web_contents->GetController(); + result->SetInteger( + devtools::Page::getNavigationHistory::kResponseCurrentIndex, + controller.GetCurrentEntryIndex()); + base::ListValue* entries = new base::ListValue(); + for (int i = 0; i != controller.GetEntryCount(); ++i) { + const NavigationEntry* entry = controller.GetEntryAtIndex(i); + base::DictionaryValue* entry_value = new base::DictionaryValue(); + entry_value->SetInteger( + devtools::Page::NavigationEntry::kParamId, + entry->GetUniqueID()); + entry_value->SetString( + devtools::Page::NavigationEntry::kParamUrl, + entry->GetURL().spec()); + entry_value->SetString( + devtools::Page::NavigationEntry::kParamTitle, + entry->GetTitle()); + entries->Append(entry_value); } + result->Set( + devtools::Page::getNavigationHistory::kResponseEntries, + entries); + return command->SuccessResponse(result); } return command->InternalErrorResponse("No WebContents to navigate"); } @@ -438,19 +436,19 @@ RendererOverridesHandler::PageNavigateToHistoryEntry( return command->InvalidParamResponse(param); } - RenderViewHost* host = agent_->GetRenderViewHost(); - if (host) { - WebContents* web_contents = host->GetDelegate()->GetAsWebContents(); - if (web_contents) { - NavigationController& controller = web_contents->GetController(); - for (int i = 0; i != controller.GetEntryCount(); ++i) { - if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) { - controller.GoToIndex(i); - return command->SuccessResponse(new base::DictionaryValue()); - } + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + + WebContents* web_contents = WebContents::FromRenderViewHost(host_); + if (web_contents) { + NavigationController& controller = web_contents->GetController(); + for (int i = 0; i != controller.GetEntryCount(); ++i) { + if (controller.GetEntryAtIndex(i)->GetUniqueID() == entry_id) { + controller.GoToIndex(i); + return command->SuccessResponse(new base::DictionaryValue()); } - return command->InvalidParamResponse(param); } + return command->InvalidParamResponse(param); } return command->InternalErrorResponse("No WebContents to navigate"); } @@ -458,12 +456,10 @@ RendererOverridesHandler::PageNavigateToHistoryEntry( scoped_refptr RendererOverridesHandler::PageCaptureScreenshot( scoped_refptr command) { - RenderViewHostImpl* host = static_cast( - agent_->GetRenderViewHost()); - if (!host->GetView()) - return command->InternalErrorResponse("Unable to access the view"); + if (!host_ || !host_->GetView()) + return command->InternalErrorResponse("Could not connect to view"); - host->GetSnapshotFromBrowser( + host_->GetSnapshotFromBrowser( base::Bind(&RendererOverridesHandler::ScreenshotCaptured, weak_factory_.GetWeakPtr(), command)); return command->AsyncResponsePromise(); @@ -507,16 +503,16 @@ scoped_refptr RendererOverridesHandler::PageStartScreencast( scoped_refptr command) { screencast_command_ = command; - RenderViewHostImpl* host = static_cast( - agent_->GetRenderViewHost()); - host->SetTouchEventEmulationEnabled(true, true); - bool visible = !host->is_hidden(); + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + host_->SetTouchEventEmulationEnabled(true, true); + bool visible = !host_->is_hidden(); NotifyScreencastVisibility(visible); if (visible) { if (has_last_compositor_frame_metadata_) InnerSwapCompositorFrame(); else - host->Send(new ViewMsg_ForceRedraw(host->GetRoutingID(), 0)); + host_->Send(new ViewMsg_ForceRedraw(host_->GetRoutingID(), 0)); } return command->SuccessResponse(NULL); } @@ -526,10 +522,8 @@ RendererOverridesHandler::PageStopScreencast( scoped_refptr command) { last_frame_time_ = base::TimeTicks(); screencast_command_ = NULL; - RenderViewHostImpl* host = static_cast( - agent_->GetRenderViewHost()); - if (host) - host->SetTouchEventEmulationEnabled(false, false); + if (host_) + host_->SetTouchEventEmulationEnabled(false, false); return command->SuccessResponse(NULL); } @@ -820,9 +814,11 @@ RendererOverridesHandler::PageQueryUsageAndQuota( weak_factory_.GetWeakPtr(), command); + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + scoped_refptr quota_manager = - agent_->GetRenderViewHost()->GetProcess()-> - GetStoragePartition()->GetQuotaManager(); + host_->GetProcess()->GetStoragePartition()->GetQuotaManager(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, @@ -863,8 +859,6 @@ RendererOverridesHandler::InputEmulateTouchFromMouseEvent( if (!params) return command->NoSuchMethodErrorResponse(); - RenderViewHost* host = agent_->GetRenderViewHost(); - std::string type; if (!params->GetString( devtools::Input::emulateTouchFromMouseEvent::kParamType, @@ -974,10 +968,13 @@ RendererOverridesHandler::InputEmulateTouchFromMouseEvent( devtools::Input::emulateTouchFromMouseEvent::kParamButton); } + if (!host_) + return command->InternalErrorResponse("Could not connect to view"); + if (event->type == WebInputEvent::MouseWheel) - host->ForwardWheelEvent(wheel_event); + host_->ForwardWheelEvent(wheel_event); else - host->ForwardMouseEvent(mouse_event); + host_->ForwardMouseEvent(mouse_event); return command->SuccessResponse(NULL); } diff --git a/content/browser/devtools/renderer_overrides_handler.h b/content/browser/devtools/renderer_overrides_handler.h index 08ccf4e063af0..774a6838cb815 100644 --- a/content/browser/devtools/renderer_overrides_handler.h +++ b/content/browser/devtools/renderer_overrides_handler.h @@ -23,8 +23,8 @@ class Message; namespace content { -class DevToolsAgentHost; class DevToolsTracingHandler; +class RenderViewHostImpl; // Overrides Inspector commands before they are sent to the renderer. // May override the implementation completely, ignore it, or handle @@ -32,13 +32,14 @@ class DevToolsTracingHandler; class CONTENT_EXPORT RendererOverridesHandler : public DevToolsProtocol::Handler { public: - explicit RendererOverridesHandler(DevToolsAgentHost* agent); + RendererOverridesHandler(); virtual ~RendererOverridesHandler(); void OnClientDetached(); void OnSwapCompositorFrame(const cc::CompositorFrameMetadata& frame_metadata); void OnVisibilityChanged(bool visible); - void OnRenderViewHostChanged(); + void SetRenderViewHost(RenderViewHostImpl* host); + void ClearRenderViewHost(); bool OnSetTouchEventEmulationEnabled(); private: @@ -101,7 +102,7 @@ class CONTENT_EXPORT RendererOverridesHandler scoped_refptr InputEmulateTouchFromMouseEvent( scoped_refptr command); - DevToolsAgentHost* agent_; + RenderViewHostImpl* host_; scoped_refptr screencast_command_; bool has_last_compositor_frame_metadata_; cc::CompositorFrameMetadata last_compositor_frame_metadata_; diff --git a/content/browser/devtools/renderer_overrides_handler_browsertest.cc b/content/browser/devtools/renderer_overrides_handler_browsertest.cc index 2a3a667c03ccf..a6cd973bc1a34 100644 --- a/content/browser/devtools/renderer_overrides_handler_browsertest.cc +++ b/content/browser/devtools/renderer_overrides_handler_browsertest.cc @@ -59,9 +59,7 @@ class RendererOverridesHandlerTest : public ContentBrowserTest, private: virtual void SetUpOnMainThread() OVERRIDE { DevToolsManager::GetInstance()->RegisterDevToolsClientHostFor( - DevToolsAgentHost::GetOrCreateFor( - shell()->web_contents()->GetRenderViewHost()).get(), - this); + DevToolsAgentHost::GetOrCreateFor(shell()->web_contents()).get(), this); } virtual void TearDownOnMainThread() OVERRIDE { diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc index 8f35b63e5761b..a768c44bbde1a 100644 --- a/content/browser/download/download_browsertest.cc +++ b/content/browser/download/download_browsertest.cc @@ -1070,7 +1070,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ShutdownAtRelease) { } IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownload) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1137,7 +1137,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownload) { // Confirm restart fallback happens if a range request is bounced. IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownloadNoRange) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1186,7 +1186,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownloadNoRange) { // Confirm restart fallback happens if a precondition is failed. IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownloadBadPrecondition) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1238,7 +1238,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, // Confirm we don't try to resume if we don't have a verifier. IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeInterruptedDownloadNoVerifiers) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1282,7 +1282,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, } IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithDeletedFile) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1333,7 +1333,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithDeletedFile) { } IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileInitError) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); base::FilePath file(FILE_PATH_LITERAL("download-test.lib")); GURL url(URLRequestMockHTTPJob::GetMockUrl(file)); @@ -1384,7 +1384,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileInitError) { IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileIntermediateRenameError) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); base::FilePath file(FILE_PATH_LITERAL("download-test.lib")); GURL url(URLRequestMockHTTPJob::GetMockUrl(file)); @@ -1436,7 +1436,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, } IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileFinalRenameError) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); base::FilePath file(FILE_PATH_LITERAL("download-test.lib")); GURL url(URLRequestMockHTTPJob::GetMockUrl(file)); @@ -1489,7 +1489,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, ResumeWithFileFinalRenameError) { // An interrupted download should remove the intermediate file when it is // cancelled. IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelInterruptedDownload) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1519,7 +1519,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelInterruptedDownload) { } IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveDownload) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1574,7 +1574,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveDownload) { IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumingDownload) { SetupEnsureNoPendingDownloads(); - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); @@ -1621,7 +1621,7 @@ IN_PROC_BROWSER_TEST_F(DownloadContentTest, RemoveResumingDownload) { IN_PROC_BROWSER_TEST_F(DownloadContentTest, CancelResumingDownload) { SetupEnsureNoPendingDownloads(); - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); ASSERT_TRUE(test_server()->Start()); diff --git a/content/browser/download/download_item_impl.cc b/content/browser/download/download_item_impl.cc index 15e95129a4e5b..733df1c91e98c 100644 --- a/content/browser/download/download_item_impl.cc +++ b/content/browser/download/download_item_impl.cc @@ -93,7 +93,7 @@ static void DownloadFileCancel(scoped_ptr download_file) { } bool IsDownloadResumptionEnabled() { - return CommandLine::ForCurrentProcess()->HasSwitch( + return base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableDownloadResumption); } @@ -1682,7 +1682,8 @@ void DownloadItemImpl::ResumeInterruptedDownload() { // If the flag for downloads resumption isn't enabled, ignore // this request. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (!command_line.HasSwitch(switches::kEnableDownloadResumption)) return; diff --git a/content/browser/download/download_item_impl_unittest.cc b/content/browser/download/download_item_impl_unittest.cc index 1291ce11350ca..86588ba2a2759 100644 --- a/content/browser/download/download_item_impl_unittest.cc +++ b/content/browser/download/download_item_impl_unittest.cc @@ -409,7 +409,7 @@ TEST_F(DownloadItemTest, NotificationAfterDestroyed) { } TEST_F(DownloadItemTest, ContinueAfterInterrupted) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); DownloadItemImpl* item = CreateDownloadItem(); @@ -437,7 +437,7 @@ TEST_F(DownloadItemTest, ContinueAfterInterrupted) { // Same as above, but with a non-continuable interrupt. TEST_F(DownloadItemTest, RestartAfterInterrupted) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); DownloadItemImpl* item = CreateDownloadItem(); @@ -461,7 +461,7 @@ TEST_F(DownloadItemTest, RestartAfterInterrupted) { // Check we do correct cleanup for RESUME_MODE_INVALID interrupts. TEST_F(DownloadItemTest, UnresumableInterrupt) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); DownloadItemImpl* item = CreateDownloadItem(); @@ -491,7 +491,7 @@ TEST_F(DownloadItemTest, UnresumableInterrupt) { } TEST_F(DownloadItemTest, LimitRestartsAfterInterrupted) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); DownloadItemImpl* item = CreateDownloadItem(); @@ -813,7 +813,7 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Restart) { // intermediate path should be retained when the download is interrupted after // the intermediate rename succeeds. TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; @@ -848,7 +848,7 @@ TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Continue) { // As above. If the intermediate rename fails, then the interrupt reason should // be set to the destination error and the intermediate path should be empty. TEST_F(DownloadItemTest, InterruptedBeforeIntermediateRename_Failed) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); DownloadItemImpl* item = CreateDownloadItem(); DownloadItemImplDelegate::DownloadTargetCallback callback; @@ -1248,7 +1248,7 @@ TEST_F(DownloadItemTest, StealDangerousDownload) { } TEST_F(DownloadItemTest, StealInterruptedDangerousDownload) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); base::FilePath returned_path; DownloadItemImpl* item = CreateDownloadItem(); @@ -1274,7 +1274,7 @@ TEST_F(DownloadItemTest, StealInterruptedDangerousDownload) { } TEST_F(DownloadItemTest, StealInterruptedNonResumableDangerousDownload) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableDownloadResumption); base::FilePath returned_path; DownloadItemImpl* item = CreateDownloadItem(); diff --git a/content/browser/download/download_resource_handler.cc b/content/browser/download/download_resource_handler.cc index 073e25ce16a55..f2fb2a5b267f6 100644 --- a/content/browser/download/download_resource_handler.cc +++ b/content/browser/download/download_resource_handler.cc @@ -141,12 +141,9 @@ bool DownloadResourceHandler::OnUploadProgress(uint64 position, } bool DownloadResourceHandler::OnRequestRedirected( - const GURL& url, + const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { - // We treat a download as a main frame load, and thus update the policy URL - // on redirects. - request()->set_first_party_for_cookies(url); return true; } diff --git a/content/browser/download/download_resource_handler.h b/content/browser/download/download_resource_handler.h index de6efef2fe64f..68169bce24edd 100644 --- a/content/browser/download/download_resource_handler.h +++ b/content/browser/download/download_resource_handler.h @@ -48,7 +48,7 @@ class CONTENT_EXPORT DownloadResourceHandler virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; diff --git a/content/browser/download/save_file_resource_handler.cc b/content/browser/download/save_file_resource_handler.cc index e1fc32b7fc476..17c29ed6593ae 100644 --- a/content/browser/download/save_file_resource_handler.cc +++ b/content/browser/download/save_file_resource_handler.cc @@ -11,6 +11,7 @@ #include "content/browser/download/save_file_manager.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request_status.h" namespace content { @@ -37,10 +38,10 @@ bool SaveFileResourceHandler::OnUploadProgress(uint64 position, uint64 size) { } bool SaveFileResourceHandler::OnRequestRedirected( - const GURL& url, + const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { - final_url_ = url; + final_url_ = redirect_info.new_url; return true; } diff --git a/content/browser/download/save_file_resource_handler.h b/content/browser/download/save_file_resource_handler.h index 5adbc5c229a8b..beca8dc721600 100644 --- a/content/browser/download/save_file_resource_handler.h +++ b/content/browser/download/save_file_resource_handler.h @@ -33,7 +33,7 @@ class SaveFileResourceHandler : public ResourceHandler { // Saves the redirected URL to final_url_, we need to use the original // URL to match original request. - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; diff --git a/content/browser/frame_host/debug_urls.cc b/content/browser/frame_host/debug_urls.cc index b3840984e79ac..091171a5b912c 100644 --- a/content/browser/frame_host/debug_urls.cc +++ b/content/browser/frame_host/debug_urls.cc @@ -110,9 +110,10 @@ bool HandleAsanDebugURL(const GURL& url) { bool HandleDebugURL(const GURL& url, PageTransition transition) { // Ensure that the user explicitly navigated to this URL, unless // kEnableGpuBenchmarking is enabled by Telemetry. - bool is_telemetry_navigation = CommandLine::ForCurrentProcess()->HasSwitch( - cc::switches::kEnableGpuBenchmarking) && - (transition & PAGE_TRANSITION_TYPED); + bool is_telemetry_navigation = + base::CommandLine::ForCurrentProcess()->HasSwitch( + cc::switches::kEnableGpuBenchmarking) && + (transition & PAGE_TRANSITION_TYPED); if (!(transition & PAGE_TRANSITION_FROM_ADDRESS_BAR) && !is_telemetry_navigation) diff --git a/content/browser/frame_host/frame_tree.cc b/content/browser/frame_host/frame_tree.cc index 992d7e52e5a63..c8b5d2da80235 100644 --- a/content/browser/frame_host/frame_tree.cc +++ b/content/browser/frame_host/frame_tree.cc @@ -8,18 +8,29 @@ #include "base/bind.h" #include "base/callback.h" +#include "base/containers/hash_tables.h" +#include "base/lazy_instance.h" #include "content/browser/frame_host/frame_tree_node.h" #include "content/browser/frame_host/navigator.h" #include "content/browser/frame_host/render_frame_host_factory.h" #include "content/browser/frame_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_view_host_factory.h" #include "content/browser/renderer_host/render_view_host_impl.h" +#include "content/public/browser/browser_thread.h" namespace content { namespace { + +// This is a global map between frame_tree_node_ids and pointer to +// FrameTreeNodes. +typedef base::hash_map FrameTreeNodeIDMap; + +base::LazyInstance g_frame_tree_node_id_map = + LAZY_INSTANCE_INITIALIZER; + // Used with FrameTree::ForEach() to search for the FrameTreeNode -// corresponding to |frame_tree_node_id|. +// corresponding to |frame_tree_node_id| whithin a specific FrameTree. bool FrameTreeNodeForId(int64 frame_tree_node_id, FrameTreeNode** out_node, FrameTreeNode* node) { @@ -84,9 +95,22 @@ FrameTree::FrameTree(Navigator* navigator, manager_delegate, std::string())), focused_frame_tree_node_id_(-1) { + std::pair result = + g_frame_tree_node_id_map.Get().insert( + std::make_pair(root_->frame_tree_node_id(), root_.get())); + CHECK(result.second); } FrameTree::~FrameTree() { + g_frame_tree_node_id_map.Get().erase(root_->frame_tree_node_id()); +} + +// static +FrameTreeNode* FrameTree::GloballyFindByID(int64 frame_tree_node_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + FrameTreeNodeIDMap* nodes = g_frame_tree_node_id_map.Pointer(); + FrameTreeNodeIDMap::iterator it = nodes->find(frame_tree_node_id); + return it == nodes->end() ? NULL : it->second; } FrameTreeNode* FrameTree::FindByID(int64 frame_tree_node_id) { @@ -124,6 +148,10 @@ RenderFrameHostImpl* FrameTree::AddFrame(FrameTreeNode* parent, scoped_ptr node(new FrameTreeNode( this, parent->navigator(), render_frame_delegate_, render_view_delegate_, render_widget_delegate_, manager_delegate_, frame_name)); + std::pair result = + g_frame_tree_node_id_map.Get().insert( + std::make_pair(node->frame_tree_node_id(), node.get())); + CHECK(result.second); FrameTreeNode* node_ptr = node.get(); // AddChild is what creates the RenderFrameHost. parent->AddChild(node.Pass(), new_routing_id); @@ -142,7 +170,7 @@ void FrameTree::RemoveFrame(FrameTreeNode* child) { if (!on_frame_removed_.is_null()) { on_frame_removed_.Run(render_frame_host); } - + g_frame_tree_node_id_map.Get().erase(child->frame_tree_node_id()); parent->RemoveChild(child); } diff --git a/content/browser/frame_host/frame_tree.h b/content/browser/frame_host/frame_tree.h index 5e58562a87912..bd1f19aa25b87 100644 --- a/content/browser/frame_host/frame_tree.h +++ b/content/browser/frame_host/frame_tree.h @@ -50,9 +50,13 @@ class CONTENT_EXPORT FrameTree { RenderFrameHostManager::Delegate* manager_delegate); ~FrameTree(); + // Returns the FrameTreeNode with the given |frame_tree_node_id|. + static FrameTreeNode* GloballyFindByID(int64 frame_tree_node_id); + FrameTreeNode* root() const { return root_.get(); } - // Returns the FrameTreeNode with the given |frame_tree_node_id|. + // Returns the FrameTreeNode with the given |frame_tree_node_id| if it is part + // of this FrameTree. FrameTreeNode* FindByID(int64 frame_tree_node_id); // Returns the FrameTreeNode with the given renderer-specific |routing_id|. diff --git a/content/browser/frame_host/frame_tree_browsertest.cc b/content/browser/frame_host/frame_tree_browsertest.cc index 287506c50e61d..d96ee59d10e77 100644 --- a/content/browser/frame_host/frame_tree_browsertest.cc +++ b/content/browser/frame_host/frame_tree_browsertest.cc @@ -187,6 +187,10 @@ IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, FrameTreeNode* root = static_cast(shell()->web_contents()) ->GetFrameTree()->root(); + // There should not be a proxy for the root's own SiteInstance. + SiteInstance* root_instance = root->current_frame_host()->GetSiteInstance(); + EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance)); + // Load same-site page into iframe. GURL http_url(test_server()->GetURL("files/title1.html")); NavigateFrameToURL(root->child_at(0), http_url); @@ -204,20 +208,24 @@ IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, // Ensure that we have created a new process for the subframe. ASSERT_EQ(1U, root->child_count()); FrameTreeNode* child = root->child_at(0); - SiteInstance* site_instance = child->current_frame_host()->GetSiteInstance(); + SiteInstance* child_instance = child->current_frame_host()->GetSiteInstance(); RenderViewHost* rvh = child->current_frame_host()->render_view_host(); RenderProcessHost* rph = child->current_frame_host()->GetProcess(); EXPECT_NE(shell()->web_contents()->GetRenderViewHost(), rvh); - EXPECT_NE(shell()->web_contents()->GetSiteInstance(), site_instance); + EXPECT_NE(shell()->web_contents()->GetSiteInstance(), child_instance); EXPECT_NE(shell()->web_contents()->GetRenderProcessHost(), rph); // Ensure that the root node has a proxy for the child node's SiteInstance. - EXPECT_TRUE(root->render_manager()->proxy_hosts_[site_instance->GetId()]); + EXPECT_TRUE(root->render_manager()->GetRenderFrameProxyHost(child_instance)); // Also ensure that the child has a proxy for the root node's SiteInstance. - EXPECT_TRUE(child->render_manager()->proxy_hosts_[ - root->current_frame_host()->GetSiteInstance()->GetId()]); + EXPECT_TRUE(child->render_manager()->GetRenderFrameProxyHost(root_instance)); + + // The nodes should not have proxies for their own SiteInstance. + EXPECT_FALSE(root->render_manager()->GetRenderFrameProxyHost(root_instance)); + EXPECT_FALSE( + child->render_manager()->GetRenderFrameProxyHost(child_instance)); } } // namespace content diff --git a/content/browser/frame_host/frame_tree_unittest.cc b/content/browser/frame_host/frame_tree_unittest.cc index f6479249ac042..8936bfa2d5492 100644 --- a/content/browser/frame_host/frame_tree_unittest.cc +++ b/content/browser/frame_host/frame_tree_unittest.cc @@ -16,6 +16,7 @@ #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" #include "testing/gtest/include/gtest/gtest.h" diff --git a/content/browser/frame_host/interstitial_page_impl.cc b/content/browser/frame_host/interstitial_page_impl.cc index 1a1ed43dc658b..f2cac9c96e774 100644 --- a/content/browser/frame_host/interstitial_page_impl.cc +++ b/content/browser/frame_host/interstitial_page_impl.cc @@ -509,11 +509,11 @@ RendererPreferences InterstitialPageImpl::GetRendererPrefs( return renderer_preferences_; } -WebPreferences InterstitialPageImpl::GetWebkitPrefs() { +WebPreferences InterstitialPageImpl::ComputeWebkitPrefs() { if (!enabled()) return WebPreferences(); - return render_view_host_->GetWebkitPrefs(url_); + return render_view_host_->ComputeWebkitPrefs(url_); } void InterstitialPageImpl::RenderWidgetDeleted( diff --git a/content/browser/frame_host/interstitial_page_impl.h b/content/browser/frame_host/interstitial_page_impl.h index 7f2d4d79b3623..1305eea170cc0 100644 --- a/content/browser/frame_host/interstitial_page_impl.h +++ b/content/browser/frame_host/interstitial_page_impl.h @@ -131,7 +131,7 @@ class CONTENT_EXPORT InterstitialPageImpl int error_code) OVERRIDE; virtual RendererPreferences GetRendererPrefs( BrowserContext* browser_context) const OVERRIDE; - virtual WebPreferences GetWebkitPrefs() OVERRIDE; + virtual WebPreferences ComputeWebkitPrefs() OVERRIDE; virtual gfx::Rect GetRootWindowResizerRect() const OVERRIDE; virtual void CreateNewWindow( int render_process_id, diff --git a/content/browser/frame_host/navigation_controller_delegate.h b/content/browser/frame_host/navigation_controller_delegate.h index 49dfc60bef076..52567ef3c6219 100644 --- a/content/browser/frame_host/navigation_controller_delegate.h +++ b/content/browser/frame_host/navigation_controller_delegate.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_FRAME_HOST_NAVIGATION_CONTROLLER_DELEGATE_H_ #include +#include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/navigation_details.h" @@ -34,7 +35,7 @@ class NavigationControllerDelegate { virtual RenderViewHost* GetRenderViewHost() const = 0; virtual InterstitialPage* GetInterstitialPage() const = 0; virtual const std::string& GetContentsMimeType() const = 0; - virtual void NotifyNavigationStateChanged(unsigned changed_flags) = 0; + virtual void NotifyNavigationStateChanged(InvalidateTypes changed_flags) = 0; virtual void Stop() = 0; virtual SiteInstance* GetPendingSiteInstance() const = 0; virtual int32 GetMaxPageID() = 0; diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc index 3997cae599152..7219831781da7 100644 --- a/content/browser/frame_host/navigation_controller_impl.cc +++ b/content/browser/frame_host/navigation_controller_impl.cc @@ -46,8 +46,6 @@ namespace content { namespace { -const unsigned kInvalidateAll = 0xFFFFFFFF; - // Invoked when entries have been pruned, or removed. For example, if the // current entries are [google, digg, yahoo], with the current entry google, // and the user types in cnet, then digg and yahoo are pruned. @@ -1075,9 +1073,11 @@ void NavigationControllerImpl::RendererDidNavigateToNewPage( // history.pushState() is classified as a navigation to a new page, but // sets was_within_same_page to true. In this case, we already have the - // title available, so set it immediately. - if (params.was_within_same_page && GetLastCommittedEntry()) + // title and favicon available, so set them immediately. + if (params.was_within_same_page && GetLastCommittedEntry()) { new_entry->SetTitle(GetLastCommittedEntry()->GetTitle()); + new_entry->GetFavicon() = GetLastCommittedEntry()->GetFavicon(); + } DCHECK(!params.history_list_was_cleared || !replace_entry); // The browser requested to clear the session history when it initiated the @@ -1525,7 +1525,7 @@ void NavigationControllerImpl::DiscardNonCommittedEntries() { // If there was a transient entry, invalidate everything so the new active // entry state is shown. if (transient) { - delegate_->NotifyNavigationStateChanged(kInvalidateAll); + delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL); } } @@ -1659,7 +1659,7 @@ void NavigationControllerImpl::NotifyNavigationEntryCommitted( // when it wants to draw. See http://crbug.com/11157 ssl_manager_.DidCommitProvisionalLoad(*details); - delegate_->NotifyNavigationStateChanged(kInvalidateAll); + delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL); delegate_->NotifyNavigationEntryCommitted(*details); // TODO(avi): Remove. http://crbug.com/170921 @@ -1768,7 +1768,7 @@ void NavigationControllerImpl::SetTransientEntry(NavigationEntry* entry) { entries_.begin() + index, linked_ptr( NavigationEntryImpl::FromNavigationEntry(entry))); transient_entry_index_ = index; - delegate_->NotifyNavigationStateChanged(kInvalidateAll); + delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL); } void NavigationControllerImpl::InsertEntriesFrom( diff --git a/content/browser/frame_host/navigation_controller_impl_unittest.cc b/content/browser/frame_host/navigation_controller_impl_unittest.cc index e7cbceb5dd847..65759bd8ed4e2 100644 --- a/content/browser/frame_host/navigation_controller_impl_unittest.cc +++ b/content/browser/frame_host/navigation_controller_impl_unittest.cc @@ -31,6 +31,7 @@ #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_notification_tracker.h" #include "content/public/test/test_utils.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" #include "net/base/net_util.h" @@ -246,7 +247,7 @@ class TestWebContentsDelegate : public WebContentsDelegate { // Keep track of whether the tab has notified us of a navigation state change. virtual void NavigationStateChanged(const WebContents* source, - unsigned changed_flags) OVERRIDE { + InvalidateTypes changed_flags) OVERRIDE { navigation_state_change_count_++; } @@ -415,8 +416,7 @@ TEST_F(NavigationControllerTest, LoadURL) { // Simulate the beforeunload ack for the cross-site transition, and then the // commit. test_rvh()->SendBeforeUnloadACK(true); - static_cast( - contents()->GetPendingRenderViewHost())->SendNavigate(1, url2); + contents()->GetPendingMainFrame()->SendNavigate(1, url2); EXPECT_EQ(1U, navigation_entry_committed_counter_); navigation_entry_committed_counter_ = 0; @@ -747,8 +747,7 @@ TEST_F(NavigationControllerTest, LoadURL_NewPending) { // After the beforeunload but before it commits, do a new navigation. test_rvh()->SendBeforeUnloadACK(true); const GURL kNewURL("http://see"); - static_cast( - contents()->GetPendingRenderViewHost())->SendNavigate(3, kNewURL); + contents()->GetPendingMainFrame()->SendNavigate(3, kNewURL); // There should no longer be any pending entry, and the third navigation we // just made should be committed. @@ -826,16 +825,15 @@ TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) { controller.LoadURL( kExistingURL2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); test_rvh()->SendBeforeUnloadACK(true); - TestRenderViewHost* foo_rvh = static_cast( - contents()->GetPendingRenderViewHost()); - foo_rvh->SendNavigate(1, kExistingURL2); + TestRenderFrameHost* foo_rfh = contents()->GetPendingMainFrame(); + foo_rfh->SendNavigate(1, kExistingURL2); EXPECT_EQ(1U, navigation_entry_committed_counter_); navigation_entry_committed_counter_ = 0; // Now make a pending back/forward navigation to a privileged entry. // The zeroth entry should be pending. controller.GoBack(); - foo_rvh->SendBeforeUnloadACK(true); + foo_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); EXPECT_EQ(0U, notifications.size()); EXPECT_EQ(0, controller.GetPendingEntryIndex()); EXPECT_EQ(1, controller.GetLastCommittedEntryIndex()); @@ -845,7 +843,7 @@ TEST_F(NavigationControllerTest, LoadURL_PrivilegedPending) { // Before that commits, do a new navigation. const GURL kNewURL("http://foo/bee"); LoadCommittedDetails details; - foo_rvh->SendNavigate(3, kNewURL); + foo_rfh->SendNavigate(3, kNewURL); // There should no longer be any pending entry, and the third navigation we // just made should be committed. @@ -1004,7 +1002,7 @@ TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) { const GURL kExistingURL("http://foo/eh"); controller.LoadURL(kExistingURL, content::Referrer(), content::PAGE_TRANSITION_TYPED, std::string()); - main_test_rfh()->SendNavigate(1, kExistingURL); + main_test_rfh()->SendNavigate(0, kExistingURL); EXPECT_EQ(1U, navigation_entry_committed_counter_); navigation_entry_committed_counter_ = 0; @@ -1032,7 +1030,7 @@ TEST_F(NavigationControllerTest, LoadURL_RedirectAbortDoesntShowPendingURL) { const GURL kRedirectURL("http://foo/see"); main_test_rfh()->OnMessageReceived( FrameHostMsg_DidRedirectProvisionalLoad(0, // routing_id - 1, // pending page_id + -1, // pending page_id kNewURL, // old url kRedirectURL)); // new url @@ -1083,33 +1081,32 @@ TEST_F(NavigationControllerTest, LoadURL_WithBindings) { controller.GetPendingEntry())->bindings()); // Commit. - TestRenderViewHost* orig_rvh = static_cast(test_rvh()); - orig_rvh->SendNavigate(0, url1); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); + orig_rfh->SendNavigate(0, url1); EXPECT_EQ(controller.GetEntryCount(), 1); EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry( controller.GetLastCommittedEntry())->bindings()); // Manually increase the number of active views in the SiteInstance - // that orig_rvh belongs to, to prevent it from being destroyed when - // it gets swapped out, so that we can reuse orig_rvh when the + // that orig_rfh belongs to, to prevent it from being destroyed when + // it gets swapped out, so that we can reuse orig_rfh when the // controller goes back. - static_cast(orig_rvh->GetSiteInstance())-> + static_cast(orig_rfh->GetSiteInstance())-> increment_active_view_count(); // Navigate to a second URL, simulate the beforeunload ack for the cross-site // transition, run the unload handler, and set bindings on the pending // RenderViewHost to simulate a privileged url. controller.LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - orig_rvh->SendBeforeUnloadACK(true); + orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( - contents()->GetRenderManagerForTesting()->pending_frame_host(), + contents()->GetPendingMainFrame(), GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); - TestRenderViewHost* new_rvh = - static_cast(contents()->GetPendingRenderViewHost()); - new_rvh->AllowBindings(1); - new_rvh->SendNavigate(1, url2); + TestRenderFrameHost* new_rfh = contents()->GetPendingMainFrame(); + new_rfh->GetRenderViewHost()->AllowBindings(1); + new_rfh->SendNavigate(1, url2); // The second load should be committed, and bindings should be remembered. EXPECT_EQ(controller.GetEntryCount(), 2); @@ -1120,12 +1117,12 @@ TEST_F(NavigationControllerTest, LoadURL_WithBindings) { // Going back, the first entry should still appear unprivileged. controller.GoBack(); - new_rvh->SendBeforeUnloadACK(true); + new_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( - contents()->GetRenderManagerForTesting()->pending_frame_host(), + contents()->GetPendingMainFrame(), GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); - orig_rvh->SendNavigate(0, url1); + orig_rfh->SendNavigate(0, url1); EXPECT_EQ(0, controller.GetLastCommittedEntryIndex()); EXPECT_EQ(0, NavigationEntryImpl::FromNavigationEntry( controller.GetLastCommittedEntry())->bindings()); @@ -4235,14 +4232,17 @@ TEST_F(NavigationControllerTest, MAYBE_PurgeScreenshot) { } } -TEST_F(NavigationControllerTest, PushStateUpdatesTitle) { - - // Navigate +TEST_F(NavigationControllerTest, PushStateUpdatesTitleAndFavicon) { + // Navigate. test_rvh()->SendNavigate(1, GURL("http://foo")); - // Set title + // Set title and favicon. base::string16 title(base::ASCIIToUTF16("Title")); + FaviconStatus favicon; + favicon.valid = true; + favicon.url = GURL("http://foo/favicon.ico"); controller().GetLastCommittedEntry()->SetTitle(title); + controller().GetLastCommittedEntry()->GetFavicon() = favicon; // history.pushState() is called. FrameHostMsg_DidCommitProvisionalLoad_Params params; @@ -4257,6 +4257,10 @@ TEST_F(NavigationControllerTest, PushStateUpdatesTitle) { base::string16 new_title = controller().GetLastCommittedEntry()->GetTitleForDisplay(std::string()); EXPECT_EQ(title, new_title); + FaviconStatus new_favicon = + controller().GetLastCommittedEntry()->GetFavicon(); + EXPECT_EQ(favicon.valid, new_favicon.valid); + EXPECT_EQ(favicon.url, new_favicon.url); } // Test that the navigation controller clears its session history when a @@ -4293,7 +4297,7 @@ TEST_F(NavigationControllerTest, ClearHistoryList) { EXPECT_TRUE(entry->should_clear_history_list()); // Assume that the RV correctly cleared its history and commit the navigation. - static_cast(contents()->GetPendingRenderViewHost())-> + contents()->GetPendingMainFrame()->GetRenderViewHost()-> set_simulate_history_list_was_cleared(true); contents()->CommitPendingNavigation(); diff --git a/content/browser/frame_host/navigation_entry_screenshot_manager.cc b/content/browser/frame_host/navigation_entry_screenshot_manager.cc index a7cae65601347..4c52117605802 100644 --- a/content/browser/frame_host/navigation_entry_screenshot_manager.cc +++ b/content/browser/frame_host/navigation_entry_screenshot_manager.cc @@ -83,7 +83,7 @@ NavigationEntryScreenshotManager::~NavigationEntryScreenshotManager() { } void NavigationEntryScreenshotManager::TakeScreenshot() { - static bool overscroll_enabled = CommandLine::ForCurrentProcess()-> + static bool overscroll_enabled = base::CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0"; if (!overscroll_enabled) return; diff --git a/content/browser/frame_host/navigator_impl.cc b/content/browser/frame_host/navigator_impl.cc index 020404fbbfdbc..0d6bf9dfa5c51 100644 --- a/content/browser/frame_host/navigator_impl.cc +++ b/content/browser/frame_host/navigator_impl.cc @@ -127,7 +127,8 @@ void MakeNavigateParams(const NavigationEntryImpl& entry, } RenderFrameHostManager* GetRenderManager(RenderFrameHostImpl* rfh) { - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) return rfh->frame_tree_node()->render_manager(); return rfh->frame_tree_node()->frame_tree()->root()->render_manager(); @@ -418,8 +419,8 @@ void NavigatorImpl::DidNavigate( const FrameHostMsg_DidCommitProvisionalLoad_Params& input_params) { FrameHostMsg_DidCommitProvisionalLoad_Params params(input_params); FrameTree* frame_tree = render_frame_host->frame_tree_node()->frame_tree(); - bool use_site_per_process = - CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess); + bool use_site_per_process = base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess); if (use_site_per_process) { // TODO(creis): Until we mirror the frame tree in the subframe's process, @@ -604,7 +605,8 @@ void NavigatorImpl::RequestTransferURL( } int64 frame_tree_node_id = -1; - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) { frame_tree_node_id = render_frame_host->frame_tree_node()->frame_tree_node_id(); } diff --git a/content/browser/frame_host/render_frame_host_delegate.h b/content/browser/frame_host/render_frame_host_delegate.h index 7f6897138912c..40af1dba20c27 100644 --- a/content/browser/frame_host/render_frame_host_delegate.h +++ b/content/browser/frame_host/render_frame_host_delegate.h @@ -30,6 +30,7 @@ class RenderFrameHost; class WebContents; struct AXEventNotificationDetails; struct ContextMenuParams; +struct TransitionLayerData; // An interface implemented by an object interested in knowing about the state // of the RenderFrameHost. @@ -68,8 +69,7 @@ class CONTENT_EXPORT RenderFrameHostDelegate { // Notification that the navigation on the main frame is blocked waiting // for transition to occur. virtual void DidDeferAfterResponseStarted( - const scoped_refptr& headers, - const GURL& url) {} + const TransitionLayerData& transition_data) {} // Used to query whether the navigation transition will be handled. virtual bool WillHandleDeferAfterResponseStarted(); diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc index dde456aada68e..832911c8a939a 100644 --- a/content/browser/frame_host/render_frame_host_impl.cc +++ b/content/browser/frame_host/render_frame_host_impl.cc @@ -13,6 +13,7 @@ #include "content/browser/accessibility/browser_accessibility_manager.h" #include "content/browser/accessibility/browser_accessibility_state_impl.h" #include "content/browser/child_process_security_policy_impl.h" +#include "content/browser/cross_site_request_manager.h" #include "content/browser/frame_host/cross_process_frame_connector.h" #include "content/browser/frame_host/cross_site_transferring_request.h" #include "content/browser/frame_host/frame_tree.h" @@ -166,6 +167,7 @@ RenderFrameHostImpl::RenderFrameHostImpl(RenderViewHostImpl* render_view_host, routing_id_(routing_id), is_swapped_out_(is_swapped_out), renderer_initialized_(false), + navigations_suspended_(false), weak_ptr_factory_(this) { frame_tree_->RegisterRenderFrameHost(this); GetProcess()->AddRoute(routing_id_, this); @@ -188,6 +190,10 @@ RenderFrameHostImpl::~RenderFrameHostImpl() { GetProcess()->RemoveRoute(routing_id_); g_routing_id_frame_map.Get().erase( RenderFrameHostID(GetProcess()->GetID(), routing_id_)); + // Clean up any leftover state from cross-site requests. + CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest( + GetProcess()->GetID(), routing_id_, false); + if (delegate_) delegate_->RenderFrameDeleted(this); @@ -552,9 +558,8 @@ void RenderFrameHostImpl::OnDidRedirectProvisionalLoad( int32 page_id, const GURL& source_url, const GURL& target_url) { - CHECK_EQ(render_view_host_->page_id_, page_id); frame_tree_node_->navigator()->DidRedirectProvisionalLoad( - this, render_view_host_->page_id_, source_url, target_url); + this, page_id, source_url, target_url); } // Called when the renderer navigates. For every frame loaded, we'll get this @@ -574,10 +579,6 @@ void RenderFrameHostImpl::OnNavigate(const IPC::Message& msg) { Read(&msg, &iter, &validated_params)) return; - // Update the RVH's current page ID so that future IPCs from the renderer - // correspond to the new page. - render_view_host_->page_id_ = validated_params.page_id; - // If we're waiting for a cross-site beforeunload ack from this renderer and // we receive a Navigate message from the main frame, then the renderer was // navigating already and sent it before hearing the ViewMsg_Stop message. @@ -662,15 +663,14 @@ void RenderFrameHostImpl::OnCrossSiteResponse( void RenderFrameHostImpl::OnDeferredAfterResponseStarted( const GlobalRequestID& global_request_id, - const scoped_refptr& headers, - const GURL& url) { + const TransitionLayerData& transition_data) { frame_tree_node_->render_manager()->OnDeferredAfterResponseStarted( global_request_id, this); if (GetParent() || !delegate_->WillHandleDeferAfterResponseStarted()) frame_tree_node_->render_manager()->ResumeResponseDeferredAtStart(); else - delegate_->DidDeferAfterResponseStarted(headers, url); + delegate_->DidDeferAfterResponseStarted(transition_data); } void RenderFrameHostImpl::SwapOut(RenderFrameProxyHost* proxy) { @@ -890,7 +890,6 @@ void RenderFrameHostImpl::OnUpdateTitle( int32 page_id, const base::string16& title, blink::WebTextDirection title_direction) { - CHECK_EQ(render_view_host_->page_id_, page_id); // This message is only sent for top-level frames. TODO(avi): when frame tree // mirroring works correctly, add a check here to enforce it. if (title.length() > kMaxTitleChars) { @@ -898,7 +897,7 @@ void RenderFrameHostImpl::OnUpdateTitle( return; } - delegate_->UpdateTitle(this, render_view_host_->page_id_, title, + delegate_->UpdateTitle(this, page_id, title, WebTextDirectionToChromeTextDirection( title_direction)); } @@ -1018,14 +1017,13 @@ void RenderFrameHostImpl::Navigate(const FrameMsg_Navigate_Params& params) { // Only send the message if we aren't suspended at the start of a cross-site // request. - if (render_view_host_->navigations_suspended_) { + if (navigations_suspended_) { // Shouldn't be possible to have a second navigation while suspended, since // navigations will only be suspended during a cross-site request. If a // second navigation occurs, RenderFrameHostManager will cancel this pending // RFH and create a new pending RFH. - DCHECK(!render_view_host_->suspended_nav_params_.get()); - render_view_host_->suspended_nav_params_.reset( - new FrameMsg_Navigate_Params(params)); + DCHECK(!suspended_nav_params_.get()); + suspended_nav_params_.reset(new FrameMsg_Navigate_Params(params)); } else { // Get back to a clean state, in case we start a new navigation without // completing a RVH swap or unload handler. @@ -1148,6 +1146,17 @@ void RenderFrameHostImpl::NotificationClosed(int notification_id) { cancel_notification_callbacks_.erase(notification_id); } +bool RenderFrameHostImpl::HasPendingCrossSiteRequest() { + return CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest( + GetProcess()->GetID(), routing_id_); +} + +void RenderFrameHostImpl::SetHasPendingCrossSiteRequest( + bool has_pending_request) { + CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest( + GetProcess()->GetID(), routing_id_, has_pending_request); +} + void RenderFrameHostImpl::PlatformNotificationPermissionRequestDone( int request_id, blink::WebNotificationPermission permission) { Send(new PlatformNotificationMsg_PermissionRequestComplete( @@ -1194,17 +1203,42 @@ RenderFrameHostImpl::GetParentNativeViewAccessible() const { } #endif // defined(OS_WIN) -void RenderFrameHostImpl::SetHasPendingTransitionRequest( - bool has_pending_request) { +void RenderFrameHostImpl::ClearPendingTransitionRequestData() { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind( - &TransitionRequestManager::SetHasPendingTransitionRequest, + &TransitionRequestManager::ClearPendingTransitionRequestData, base::Unretained(TransitionRequestManager::GetInstance()), GetProcess()->GetID(), - routing_id_, - has_pending_request)); + routing_id_)); +} + +void RenderFrameHostImpl::SetNavigationsSuspended( + bool suspend, + const base::TimeTicks& proceed_time) { + // This should only be called to toggle the state. + DCHECK(navigations_suspended_ != suspend); + + navigations_suspended_ = suspend; + if (!suspend && suspended_nav_params_) { + // There's navigation message params waiting to be sent. Now that we're not + // suspended anymore, resume navigation by sending them. If we were swapped + // out, we should also stop filtering out the IPC messages now. + render_view_host_->SetState(RenderViewHostImpl::STATE_DEFAULT); + + DCHECK(!proceed_time.is_null()); + suspended_nav_params_->browser_navigation_start = proceed_time; + Send(new FrameMsg_Navigate(routing_id_, *suspended_nav_params_)); + suspended_nav_params_.reset(); + } +} + +void RenderFrameHostImpl::CancelSuspendedNavigations() { + // Clear any state if a pending navigation is canceled or preempted. + if (suspended_nav_params_) + suspended_nav_params_.reset(); + navigations_suspended_ = false; } } // namespace content diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h index 683c34ab909fa..de6a58e9ea633 100644 --- a/content/browser/frame_host/render_frame_host_impl.h +++ b/content/browser/frame_host/render_frame_host_impl.h @@ -53,6 +53,7 @@ struct ContextMenuParams; struct GlobalRequestID; struct Referrer; struct ShowDesktopNotificationHostMsgParams; +struct TransitionLayerData; class CONTENT_EXPORT RenderFrameHostImpl : public RenderFrameHost, @@ -156,8 +157,7 @@ class CONTENT_EXPORT RenderFrameHostImpl // receieved. void OnDeferredAfterResponseStarted( const GlobalRequestID& global_request_id, - const scoped_refptr& headers, - const GURL& url); + const TransitionLayerData& transition_data); // Tells the renderer that this RenderFrame is being swapped out for one in a // different renderer process. It should run its unload handler, move to @@ -189,6 +189,30 @@ class CONTENT_EXPORT RenderFrameHostImpl // Load the specified URL; this is a shortcut for Navigate(). void NavigateToURL(const GURL& url); + // Returns whether navigation messages are currently suspended for this + // RenderFrameHost. Only true during a cross-site navigation, while waiting + // for the onbeforeunload handler. + bool are_navigations_suspended() const { return navigations_suspended_; } + + // Suspends (or unsuspends) any navigation messages from being sent from this + // RenderFrameHost. This is called when a pending RenderFrameHost is created + // for a cross-site navigation, because we must suspend any navigations until + // we hear back from the old renderer's onbeforeunload handler. Note that it + // is important that only one navigation event happen after calling this + // method with |suspend| equal to true. If |suspend| is false and there is a + // suspended_nav_message_, this will send the message. This function should + // only be called to toggle the state; callers should check + // are_navigations_suspended() first. If |suspend| is false, the time that the + // user decided the navigation should proceed should be passed as + // |proceed_time|. + void SetNavigationsSuspended(bool suspend, + const base::TimeTicks& proceed_time); + + // Clears any suspended navigation state after a cross-site navigation is + // canceled or suspended. This is important if we later return to this + // RenderFrameHost. + void CancelSuspendedNavigations(); + // Runs the beforeunload handler for this frame. |for_cross_site_transition| // indicates whether this call is for the current frame during a cross-process // navigation. False means we're closing the entire tab. @@ -208,10 +232,9 @@ class CONTENT_EXPORT RenderFrameHostImpl // Called when an HTML5 notification is closed. void NotificationClosed(int notification_id); - // Sets whether there is an outstanding transition request. This is called at - // the start of a provisional load for the main frame, and cleared when we - // hear the response or commit. - void SetHasPendingTransitionRequest(bool has_pending_request); + // Clears any outstanding transition request. This is called when we hear the + // response or commit. + void ClearPendingTransitionRequestData(); // Send a message to the renderer process to change the accessibility mode. void SetAccessibilityMode(AccessibilityMode AccessibilityMode); @@ -244,6 +267,17 @@ class CONTENT_EXPORT RenderFrameHostImpl gfx::NativeViewAccessible GetParentNativeViewAccessible() const; #endif + // Returns whether this RenderFrameHost has an outstanding cross-site request. + // Cleared when we hear the response and start to swap out the old + // RenderFrameHost, or if we hear a commit here without a network request. + bool HasPendingCrossSiteRequest(); + + // Sets whether this RenderFrameHost has an outstanding cross-site request, + // for which another renderer will need to run an onunload event handler. + // This is called before the first navigation event for this RenderFrameHost, + // and cleared when we hear the response or commit. + void SetHasPendingCrossSiteRequest(bool has_pending_request); + protected: friend class RenderFrameHostFactory; @@ -375,6 +409,19 @@ class CONTENT_EXPORT RenderFrameHostImpl bool is_swapped_out_; bool renderer_initialized_; + // Whether we should buffer outgoing Navigate messages rather than sending + // them. This will be true when a RenderFrameHost is created for a cross-site + // request, until we hear back from the onbeforeunload handler of the old + // RenderFrameHost. + bool navigations_suspended_; + + // We only buffer the params for a suspended navigation while this RFH is the + // pending RenderFrameHost of a RenderFrameHostManager. There will only ever + // be one suspended navigation, because RenderFrameHostManager will destroy + // the pending RenderFrameHost and create a new one if a second navigation + // occurs. + scoped_ptr suspended_nav_params_; + // When the last BeforeUnload message was sent. base::TimeTicks send_before_unload_start_time_; diff --git a/content/browser/frame_host/render_frame_host_manager.cc b/content/browser/frame_host/render_frame_host_manager.cc index cb8148ca3d948..9da1897e6b79d 100644 --- a/content/browser/frame_host/render_frame_host_manager.cc +++ b/content/browser/frame_host/render_frame_host_manager.cc @@ -273,9 +273,8 @@ bool RenderFrameHostManager::ShouldCloseTabOnUnresponsiveRenderer() { // that the beforeunload handler will later finish and possibly return // false (meaning the navigation should not proceed), but we'll ignore it // in this case because it took too long. - if (pending_render_frame_host_->render_view_host()-> - are_navigations_suspended()) { - pending_render_frame_host_->render_view_host()->SetNavigationsSuspended( + if (pending_render_frame_host_->are_navigations_suspended()) { + pending_render_frame_host_->SetNavigationsSuspended( false, base::TimeTicks::Now()); } } @@ -298,10 +297,9 @@ void RenderFrameHostManager::OnBeforeUnloadACK( // already made by ShouldCloseTabOnUnresponsiveRenderer. In that case, it // is ok to do nothing here. if (pending_render_frame_host_ && - pending_render_frame_host_->render_view_host()-> - are_navigations_suspended()) { - pending_render_frame_host_->render_view_host()-> - SetNavigationsSuspended(false, proceed_time); + pending_render_frame_host_->are_navigations_suspended()) { + pending_render_frame_host_->SetNavigationsSuspended(false, + proceed_time); } } else { // Current page says to cancel. @@ -373,7 +371,7 @@ void RenderFrameHostManager::ResumeResponseDeferredAtStart() { static_cast(render_frame_host_->GetProcess()); process->ResumeResponseDeferredAtStart(*response_started_id_); - render_frame_host_->SetHasPendingTransitionRequest(false); + render_frame_host_->ClearPendingTransitionRequestData(); response_started_id_.reset(); } @@ -458,8 +456,7 @@ void RenderFrameHostManager::DidNavigateFrame( // then we still need to swap out the old RFH first and run its unload // handler, only if it hasn't happened yet. OK for that to happen in the // background. - if (pending_render_frame_host_->render_view_host()-> - HasPendingCrossSiteRequest() && + if (pending_render_frame_host_->HasPendingCrossSiteRequest() && pending_render_frame_host_->render_view_host()->rvh_state() == RenderViewHostImpl::STATE_DEFAULT) { SwapOutOldPage(); @@ -529,12 +526,7 @@ void RenderFrameHostManager::SwapOutOldPage() { // Create the RenderFrameProxyHost that will replace the // RenderFrameHost which is swapping out. If one exists, ensure it is deleted // from the map and not leaked. - RenderFrameProxyHostMap::iterator iter = proxy_hosts_.find( - render_frame_host_->GetSiteInstance()->GetId()); - if (iter != proxy_hosts_.end()) { - delete iter->second; - proxy_hosts_.erase(iter); - } + DeleteRenderFrameProxyHost(render_frame_host_->GetSiteInstance()); RenderFrameProxyHost* proxy = new RenderFrameProxyHost( render_frame_host_->GetSiteInstance(), frame_tree_node_); @@ -554,8 +546,7 @@ void RenderFrameHostManager::SwapOutOldPage() { // navigation. Thus, we no longer need to remember that the RenderFrameHost // is part of a pending cross-site request. if (pending_render_frame_host_) { - pending_render_frame_host_->render_view_host()-> - SetHasPendingCrossSiteRequest(false); + pending_render_frame_host_->SetHasPendingCrossSiteRequest(false); } } @@ -654,62 +645,53 @@ bool RenderFrameHostManager::ShouldTransitionCrossSite() { } bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation( - const NavigationEntry* current_entry, - const NavigationEntryImpl* new_entry) const { - DCHECK(new_entry); - + const GURL& current_effective_url, + bool current_is_view_source_mode, + SiteInstance* new_site_instance, + const GURL& new_effective_url, + bool new_is_view_source_mode) const { // If new_entry already has a SiteInstance, assume it is correct. We only // need to force a swap if it is in a different BrowsingInstance. - if (new_entry->site_instance()) { - return !new_entry->site_instance()->IsRelatedSiteInstance( + if (new_site_instance) { + return !new_site_instance->IsRelatedSiteInstance( render_frame_host_->GetSiteInstance()); } // Check for reasons to swap processes even if we are in a process model that // doesn't usually swap (e.g., process-per-tab). Any time we return true, // the new_entry will be rendered in a new SiteInstance AND BrowsingInstance. - - // We use the effective URL here, since that's what is used in the - // SiteInstance's site and when we later call IsSameWebSite. If there is no - // current_entry, check the current SiteInstance's site, which might already - // be committed to a Web UI URL (such as the NTP). BrowserContext* browser_context = delegate_->GetControllerForRenderManager().GetBrowserContext(); - const GURL& current_url = (current_entry) ? - SiteInstanceImpl::GetEffectiveURL(browser_context, - current_entry->GetURL()) : - render_frame_host_->GetSiteInstance()->GetSiteURL(); - const GURL& new_url = SiteInstanceImpl::GetEffectiveURL(browser_context, - new_entry->GetURL()); // Don't force a new BrowsingInstance for debug URLs that are handled in the // renderer process, like javascript: or chrome://crash. - if (IsRendererDebugURL(new_url)) + if (IsRendererDebugURL(new_effective_url)) return false; // For security, we should transition between processes when one is a Web UI // page and one isn't. if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL( - browser_context, current_url)) { + browser_context, current_effective_url)) { // If so, force a swap if destination is not an acceptable URL for Web UI. // Here, data URLs are never allowed. if (!WebUIControllerFactoryRegistry::GetInstance()->IsURLAcceptableForWebUI( - browser_context, new_url)) { + browser_context, new_effective_url)) { return true; } } else { // Force a swap if it's a Web UI URL. if (WebUIControllerFactoryRegistry::GetInstance()->UseWebUIForURL( - browser_context, new_url)) { + browser_context, new_effective_url)) { return true; } } - // Check with the content client as well. Important to pass current_url here, - // which uses the SiteInstance's site if there is no current_entry. + // Check with the content client as well. Important to pass + // current_effective_url here, which uses the SiteInstance's site if there is + // no current_entry. if (GetContentClient()->browser()->ShouldSwapBrowsingInstancesForNavigation( render_frame_host_->GetSiteInstance(), - current_url, new_url)) { + current_effective_url, new_effective_url)) { return true; } @@ -717,8 +699,7 @@ bool RenderFrameHostManager::ShouldSwapBrowsingInstancesForNavigation( // without screwing up the session history sometimes (when navigating between // "view-source:http://foo.com/" and "http://foo.com/", Blink doesn't treat // it as a new navigation). So require a BrowsingInstance switch. - if (current_entry && - current_entry->IsViewSourceMode() != new_entry->IsViewSourceMode()) + if (current_is_view_source_mode != new_is_view_source_mode) return true; return false; @@ -736,24 +717,26 @@ bool RenderFrameHostManager::ShouldReuseWebUI( controller.GetBrowserContext(), new_entry->GetURL())); } -SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( - const NavigationEntryImpl& entry, +SiteInstance* RenderFrameHostManager::GetSiteInstanceForURL( + const GURL& dest_url, + SiteInstance* dest_instance, + PageTransition dest_transition, + bool dest_is_restore, + bool dest_is_view_source_mode, SiteInstance* current_instance, bool force_browsing_instance_swap) { - // Determine which SiteInstance to use for navigating to |entry|. - const GURL& dest_url = entry.GetURL(); NavigationControllerImpl& controller = delegate_->GetControllerForRenderManager(); BrowserContext* browser_context = controller.GetBrowserContext(); // If the entry has an instance already we should use it. - if (entry.site_instance()) { + if (dest_instance) { // If we are forcing a swap, this should be in a different BrowsingInstance. if (force_browsing_instance_swap) { - CHECK(!entry.site_instance()->IsRelatedSiteInstance( + CHECK(!dest_instance->IsRelatedSiteInstance( render_frame_host_->GetSiteInstance())); } - return entry.site_instance(); + return dest_instance; } // If a swap is required, we need to force the SiteInstance AND @@ -772,8 +755,7 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( // RenderViews in response to a link click. // if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessPerSite) && - PageTransitionCoreTypeIs(entry.GetTransitionType(), - PAGE_TRANSITION_GENERATED)) { + PageTransitionCoreTypeIs(dest_transition, PAGE_TRANSITION_GENERATED)) { return current_instance; } @@ -815,7 +797,7 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( // TODO(nasko): This is the same condition as later in the function. This // should be taken into account when refactoring this method as part of // http://crbug.com/123007. - if (entry.IsViewSourceMode()) + if (dest_is_view_source_mode) return SiteInstance::CreateForURL(browser_context, dest_url); // If we are navigating from a blank SiteInstance to a WebUI, make sure we @@ -839,7 +821,7 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( // renderers created for particular chrome urls (e.g. the chrome-native:// // scheme) can be reused for subsequent navigations in the same WebContents. // See http://crbug.com/386542. - if (entry.restore_type() != NavigationEntryImpl::RESTORE_NONE && + if (dest_is_restore && GetContentClient()->browser()->ShouldAssignSiteForURL(dest_url)) { current_site_instance->SetSite(dest_url); } @@ -882,7 +864,7 @@ SiteInstance* RenderFrameHostManager::GetSiteInstanceForEntry( // TODO(creis): Refactor this method so this duplicated code isn't needed. // See http://crbug.com/123007. if (current_entry && - current_entry->IsViewSourceMode() != entry.IsViewSourceMode() && + current_entry->IsViewSourceMode() != dest_is_view_source_mode && !IsRendererDebugURL(dest_url)) { return SiteInstance::CreateForURL(browser_context, dest_url); } @@ -1341,10 +1323,31 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( // process-per-tab model, such as WebUI pages. const NavigationEntry* current_entry = delegate_->GetLastCommittedNavigationEntryForRenderManager(); + BrowserContext* browser_context = + delegate_->GetControllerForRenderManager().GetBrowserContext(); + const GURL& current_effective_url = current_entry ? + SiteInstanceImpl::GetEffectiveURL(browser_context, + current_entry->GetURL()) : + render_frame_host_->GetSiteInstance()->GetSiteURL(); + bool current_is_view_source_mode = current_entry ? + current_entry->IsViewSourceMode() : entry.IsViewSourceMode(); bool force_swap = !is_guest_scheme && - ShouldSwapBrowsingInstancesForNavigation(current_entry, &entry); - if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap)) - new_instance = GetSiteInstanceForEntry(entry, current_instance, force_swap); + ShouldSwapBrowsingInstancesForNavigation( + current_effective_url, + current_is_view_source_mode, + entry.site_instance(), + SiteInstanceImpl::GetEffectiveURL(browser_context, entry.GetURL()), + entry.IsViewSourceMode()); + if (!is_guest_scheme && (ShouldTransitionCrossSite() || force_swap)) { + new_instance = GetSiteInstanceForURL( + entry.GetURL(), + entry.site_instance(), + entry.GetTransitionType(), + entry.restore_type() != NavigationEntryImpl::RESTORE_NONE, + entry.IsViewSourceMode(), + current_instance, + force_swap); + } // If force_swap is true, we must use a different SiteInstance. If we didn't, // we would have two RenderFrameHosts in the same SiteInstance and the same @@ -1414,8 +1417,7 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( // Navigate message) until we hear back from the old renderer's // beforeunload handler. If the handler returns false, we'll have to // cancel the request. - DCHECK(!pending_render_frame_host_->render_view_host()-> - are_navigations_suspended()); + DCHECK(!pending_render_frame_host_->are_navigations_suspended()); bool is_transfer = entry.transferred_global_request_id() != GlobalRequestID(); if (is_transfer) { @@ -1430,15 +1432,13 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( render_frame_host_->render_view_host()->Send(new ViewMsg_Stop( render_frame_host_->render_view_host()->GetRoutingID())); - pending_render_frame_host_->render_view_host()->SetNavigationsSuspended( - true, base::TimeTicks()); + pending_render_frame_host_->SetNavigationsSuspended(true, + base::TimeTicks()); - // Tell the CrossSiteRequestManager that this RVH has a pending cross-site + // Tell the CrossSiteRequestManager that this RFH has a pending cross-site // request, so that ResourceDispatcherHost will know to tell us to run the // old page's unload handler before it sends the response. - // TODO(creis): This needs to be on the RFH. - pending_render_frame_host_->render_view_host()-> - SetHasPendingCrossSiteRequest(true); + pending_render_frame_host_->SetHasPendingCrossSiteRequest(true); } // We now have a pending RFH. @@ -1457,6 +1457,14 @@ RenderFrameHostImpl* RenderFrameHostManager::UpdateStateForNavigate( // Otherwise the same SiteInstance can be used. Navigate render_frame_host_. DCHECK(!cross_navigation_pending_); + + // It's possible to swap out the current RFH and then decide to navigate in it + // anyway (e.g., a cross-process navigation that redirects back to the + // original site). In that case, we have a proxy for the current RFH but + // haven't deleted it yet. The new navigation will swap it back in, so we can + // delete the proxy. + DeleteRenderFrameProxyHost(new_instance); + if (ShouldReuseWebUI(current_entry, &entry)) { pending_web_ui_.reset(); pending_and_current_web_ui_ = web_ui_->AsWeakPtr(); @@ -1505,7 +1513,7 @@ void RenderFrameHostManager::CancelPending() { pending_render_frame_host->GetSiteInstance()); if (site_instance->active_view_count() > 1) { // Any currently suspended navigations are no longer needed. - pending_render_frame_host->render_view_host()->CancelSuspendedNavigations(); + pending_render_frame_host->CancelSuspendedNavigations(); RenderFrameProxyHost* proxy = new RenderFrameProxyHost(site_instance, frame_tree_node_); @@ -1591,4 +1599,13 @@ RenderFrameProxyHost* RenderFrameHostManager::GetRenderFrameProxyHost( return NULL; } +void RenderFrameHostManager::DeleteRenderFrameProxyHost( + SiteInstance* instance) { + RenderFrameProxyHostMap::iterator iter = proxy_hosts_.find(instance->GetId()); + if (iter != proxy_hosts_.end()) { + delete iter->second; + proxy_hosts_.erase(iter); + } +} + } // namespace content diff --git a/content/browser/frame_host/render_frame_host_manager.h b/content/browser/frame_host/render_frame_host_manager.h index 38914e86aaad9..b9449c279958b 100644 --- a/content/browser/frame_host/render_frame_host_manager.h +++ b/content/browser/frame_host/render_frame_host_manager.h @@ -386,15 +386,25 @@ class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver { // switch. Can be overridden in unit tests. bool ShouldTransitionCrossSite(); - // Returns true if for the navigation from |current_entry| to |new_entry|, - // a new SiteInstance and BrowsingInstance should be created (even if we are - // in a process model that doesn't usually swap). This forces a process swap - // and severs script connections with existing tabs. Cases where this can - // happen include transitions between WebUI and regular web pages. - // Either of the entries may be NULL. + // Returns true if for the navigation from |current_effective_url| to + // |new_effective_url|, a new SiteInstance and BrowsingInstance should be + // created (even if we are in a process model that doesn't usually swap). + // This forces a process swap and severs script connections with existing + // tabs. Cases where this can happen include transitions between WebUI and + // regular web pages. |new_site_instance| may be null. + // If there is no current NavigationEntry, then |current_is_view_source_mode| + // should be the same as |new_is_view_source_mode|. + // + // We use the effective URL here, since that's what is used in the + // SiteInstance's site and when we later call IsSameWebSite. If there is no + // current NavigationEntry, check the current SiteInstance's site, which might + // already be committed to a Web UI URL (such as the NTP). bool ShouldSwapBrowsingInstancesForNavigation( - const NavigationEntry* current_entry, - const NavigationEntryImpl* new_entry) const; + const GURL& current_effective_url, + bool current_is_view_source_mode, + SiteInstance* new_site_instance, + const GURL& new_effective_url, + bool new_is_view_source_mode) const; // Returns true if it is safe to reuse the current WebUI when navigating from // |current_entry| to |new_entry|. @@ -402,12 +412,16 @@ class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver { const NavigationEntry* current_entry, const NavigationEntryImpl* new_entry) const; - // Returns an appropriate SiteInstance object for the given NavigationEntry, + // Returns an appropriate SiteInstance object for the given |dest_url|, // possibly reusing the current SiteInstance. If --process-per-tab is used, // this is only called when ShouldSwapBrowsingInstancesForNavigation returns - // true. - SiteInstance* GetSiteInstanceForEntry( - const NavigationEntryImpl& entry, + // true. |dest_instance| will be used if it is not null. + SiteInstance* GetSiteInstanceForURL( + const GURL& dest_url, + SiteInstance* dest_instance, + PageTransition dest_transition, + bool dest_is_restore, + bool dest_is_view_source_mode, SiteInstance* current_instance, bool force_browsing_instance_swap); @@ -459,6 +473,10 @@ class CONTENT_EXPORT RenderFrameHostManager : public NotificationObserver { // schedule new navigations in its swapped out RenderFrameHosts after this. void RendererProcessClosing(RenderProcessHost* render_process_host); + // Helper method to delete a RenderFrameProxyHost from the list, if one exists + // for the given |instance|. + void DeleteRenderFrameProxyHost(SiteInstance* instance); + // For use in creating RenderFrameHosts. FrameTreeNode* frame_tree_node_; 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 1a76c36d7c2a5..eac088a9e7d94 100644 --- a/content/browser/frame_host/render_frame_host_manager_unittest.cc +++ b/content/browser/frame_host/render_frame_host_manager_unittest.cc @@ -31,6 +31,7 @@ #include "content/public/test/test_notification_tracker.h" #include "content/test/test_content_browser_client.h" #include "content/test/test_content_client.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" #include "testing/gtest/include/gtest/gtest.h" @@ -275,8 +276,7 @@ class RenderFrameHostManagerTest GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); EXPECT_TRUE(contents->cross_navigation_pending()); - RenderViewHostImpl* rvh = static_cast( - contents->GetRenderViewHost()); + RenderViewHostImpl* rvh = contents->GetRenderViewHost(); EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, rvh->rvh_state()); } @@ -323,8 +323,21 @@ class RenderFrameHostManagerTest bool ShouldSwapProcesses(RenderFrameHostManager* manager, const NavigationEntryImpl* current_entry, const NavigationEntryImpl* new_entry) const { - return manager->ShouldSwapBrowsingInstancesForNavigation(current_entry, - new_entry); + CHECK(new_entry); + BrowserContext* browser_context = + manager->delegate_->GetControllerForRenderManager().GetBrowserContext(); + const GURL& current_effective_url = current_entry ? + SiteInstanceImpl::GetEffectiveURL(browser_context, + current_entry->GetURL()) : + manager->render_frame_host_->GetSiteInstance()->GetSiteURL(); + bool current_is_view_source_mode = current_entry ? + current_entry->IsViewSourceMode() : new_entry->IsViewSourceMode(); + return manager->ShouldSwapBrowsingInstancesForNavigation( + current_effective_url, + current_is_view_source_mode, + new_entry->site_instance(), + SiteInstanceImpl::GetEffectiveURL(browser_context, new_entry->GetURL()), + new_entry->IsViewSourceMode()); } // Creates a test RenderViewHost that's swapped out. @@ -334,8 +347,7 @@ class RenderFrameHostManagerTest // Navigate our first tab to a chrome url and then to the destination. NavigateActiveAndCommit(kChromeURL); - TestRenderViewHost* ntp_rvh = static_cast( - contents()->GetRenderManagerForTesting()->current_host()); + TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame(); // Navigate to a cross-site URL. contents()->GetController().LoadURL( @@ -343,24 +355,23 @@ class RenderFrameHostManagerTest EXPECT_TRUE(contents()->cross_navigation_pending()); // Manually increase the number of active views in the - // SiteInstance that ntp_rvh belongs to, to prevent it from being + // SiteInstance that ntp_rfh belongs to, to prevent it from being // destroyed when it gets swapped out. - static_cast(ntp_rvh->GetSiteInstance())-> + static_cast(ntp_rfh->GetSiteInstance())-> increment_active_view_count(); - TestRenderViewHost* dest_rvh = static_cast( - contents()->GetRenderManagerForTesting()->pending_render_view_host()); - CHECK(dest_rvh); - EXPECT_NE(ntp_rvh, dest_rvh); + TestRenderFrameHost* dest_rfh = contents()->GetPendingMainFrame(); + CHECK(dest_rfh); + EXPECT_NE(ntp_rfh, dest_rfh); // BeforeUnload finishes. - ntp_rvh->SendBeforeUnloadACK(true); + ntp_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); - dest_rvh->SendNavigate(101, kDestUrl); - ntp_rvh->OnSwappedOut(false); + dest_rfh->SendNavigate(101, kDestUrl); + ntp_rfh->OnSwappedOut(false); - EXPECT_TRUE(ntp_rvh->IsSwappedOut()); - return ntp_rvh; + EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->IsSwappedOut()); + return ntp_rfh->GetRenderViewHost(); } NavigationRequest* NavigationRequestForRenderFrameManager( @@ -388,54 +399,55 @@ TEST_F(RenderFrameHostManagerTest, NewTabPageProcesses) { EXPECT_TRUE(active_rvh()->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); NavigateActiveAndCommit(kDestUrl); + EXPECT_FALSE(contents()->GetPendingMainFrame()); + // Make a second tab. scoped_ptr contents2( TestWebContents::Create(browser_context(), NULL)); // Load the two URLs in the second tab. Note that the first navigation creates - // a RVH that's not pending (since there is no cross-site transition), so + // a RFH that's not pending (since there is no cross-site transition), so // we use the committed one. contents2->GetController().LoadURL( kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); - TestRenderViewHost* ntp_rvh2 = static_cast( - contents2->GetRenderManagerForTesting()->current_host()); + TestRenderFrameHost* ntp_rfh2 = contents2->GetMainFrame(); EXPECT_FALSE(contents2->cross_navigation_pending()); - ntp_rvh2->SendNavigate(100, kChromeUrl); + ntp_rfh2->SendNavigate(100, kChromeUrl); // The second one is the opposite, creating a cross-site transition and // requiring a beforeunload ack. contents2->GetController().LoadURL( kDestUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); EXPECT_TRUE(contents2->cross_navigation_pending()); - TestRenderViewHost* dest_rvh2 = static_cast( - contents2->GetRenderManagerForTesting()->pending_render_view_host()); - ASSERT_TRUE(dest_rvh2); + TestRenderFrameHost* dest_rfh2 = contents2->GetPendingMainFrame(); + ASSERT_TRUE(dest_rfh2); - ntp_rvh2->SendBeforeUnloadACK(true); + ntp_rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true); StartCrossSiteTransition(contents2.get()); - dest_rvh2->SendNavigate(101, kDestUrl); + dest_rfh2->SendNavigate(101, kDestUrl); - // The two RVH's should be different in every way. - EXPECT_NE(active_rvh()->GetProcess(), dest_rvh2->GetProcess()); - EXPECT_NE(active_rvh()->GetSiteInstance(), dest_rvh2->GetSiteInstance()); - EXPECT_FALSE(active_rvh()->GetSiteInstance()->IsRelatedSiteInstance( - dest_rvh2->GetSiteInstance())); + // The two RFH's should be different in every way. + EXPECT_NE(contents()->GetMainFrame()->GetProcess(), dest_rfh2->GetProcess()); + EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(), + dest_rfh2->GetSiteInstance()); + EXPECT_FALSE(dest_rfh2->GetSiteInstance()->IsRelatedSiteInstance( + contents()->GetMainFrame()->GetSiteInstance())); // Navigate both to the new tab page, and verify that they share a // RenderProcessHost (not a SiteInstance). NavigateActiveAndCommit(kChromeUrl); + EXPECT_FALSE(contents()->GetPendingMainFrame()); contents2->GetController().LoadURL( kChromeUrl, Referrer(), PAGE_TRANSITION_LINK, std::string()); - dest_rvh2->SendBeforeUnloadACK(true); + dest_rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true); StartCrossSiteTransition(contents2.get()); - static_cast(contents2->GetRenderManagerForTesting()-> - pending_render_view_host())->SendNavigate(102, kChromeUrl); + contents2->GetPendingMainFrame()->SendNavigate(102, kChromeUrl); - EXPECT_NE(active_rvh()->GetSiteInstance(), - contents2->GetRenderViewHost()->GetSiteInstance()); - EXPECT_EQ(active_rvh()->GetSiteInstance()->GetProcess(), - contents2->GetRenderViewHost()->GetSiteInstance()->GetProcess()); + EXPECT_NE(contents()->GetMainFrame()->GetSiteInstance(), + contents2->GetMainFrame()->GetSiteInstance()); + EXPECT_EQ(contents()->GetMainFrame()->GetSiteInstance()->GetProcess(), + contents2->GetMainFrame()->GetSiteInstance()->GetProcess()); } // Ensure that the browser ignores most IPC messages that arrive from a @@ -450,50 +462,50 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) { // Navigate our first tab to a chrome url and then to the destination. NavigateActiveAndCommit(kChromeURL); - TestRenderViewHost* ntp_rvh = static_cast( - contents()->GetRenderManagerForTesting()->current_host()); + 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_rvh->OnMessageReceived( + EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->OnMessageReceived( ViewHostMsg_UpdateFaviconURL( - rvh()->GetRoutingID(), icons))); + ntp_rfh->GetRenderViewHost()->GetRoutingID(), icons))); EXPECT_TRUE(observer.favicon_received()); } - // Create one more view in the same SiteInstance where ntp_rvh + // Create one more view in the same SiteInstance where ntp_rfh // exists so that it doesn't get deleted on navigation to another // site. - static_cast(ntp_rvh->GetSiteInstance())-> + static_cast(ntp_rfh->GetSiteInstance())-> increment_active_view_count(); // Navigate to a cross-site URL. NavigateActiveAndCommit(kDestUrl); - TestRenderViewHost* dest_rvh = static_cast( - contents()->GetRenderViewHost()); - ASSERT_TRUE(dest_rvh); - EXPECT_NE(ntp_rvh, dest_rvh); + TestRenderFrameHost* dest_rfh = contents()->GetMainFrame(); + ASSERT_TRUE(dest_rfh); + 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( - dest_rvh->OnMessageReceived( - ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), icons))); + dest_rfh->GetRenderViewHost()->OnMessageReceived( + ViewHostMsg_UpdateFaviconURL( + dest_rfh->GetRenderViewHost()->GetRoutingID(), icons))); EXPECT_TRUE(observer.favicon_received()); } // The old renderer, being slow, now updates the favicon. It should be // filtered out and not take effect. - EXPECT_TRUE(ntp_rvh->IsSwappedOut()); + EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->IsSwappedOut()); { PluginFaviconMessageObserver observer(contents()); EXPECT_TRUE( - ntp_rvh->OnMessageReceived( - ViewHostMsg_UpdateFaviconURL(rvh()->GetRoutingID(), icons))); + ntp_rfh->GetRenderViewHost()->OnMessageReceived( + ViewHostMsg_UpdateFaviconURL( + dest_rfh->GetRenderViewHost()->GetRoutingID(), icons))); EXPECT_FALSE(observer.favicon_received()); } @@ -502,11 +514,9 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) { // if the IPC message is allowed through or not. { PluginFaviconMessageObserver observer(contents()); - // TODO(nasko): Check that the RFH is in swapped out when the state moves - // from RVH to RFH. - EXPECT_TRUE(ntp_rvh->main_render_frame_host()->OnMessageReceived( + EXPECT_TRUE(ntp_rfh->OnMessageReceived( FrameHostMsg_PluginCrashed( - main_rfh()->GetRoutingID(), base::FilePath(), 0))); + ntp_rfh->GetRoutingID(), base::FilePath(), 0))); EXPECT_FALSE(observer.plugin_crashed()); } @@ -515,9 +525,8 @@ TEST_F(RenderFrameHostManagerTest, FilterMessagesWhileSwappedOut) { // that can run easily within a unit test, and that needs to receive a reply // without showing an actual dialog. MockRenderProcessHost* ntp_process_host = - static_cast(ntp_rvh->GetProcess()); + static_cast(ntp_rfh->GetProcess()); ntp_process_host->sink().ClearMessages(); - RenderFrameHost* ntp_rfh = ntp_rvh->GetMainFrame(); const base::string16 msg = base::ASCIIToUTF16("Message"); bool result = false; base::string16 unused; @@ -685,20 +694,20 @@ TEST_F(RenderFrameHostManagerTest, // Navigate our first tab to a chrome url and then to the destination. NavigateActiveAndCommit(kChromeURL); - TestRenderViewHost* ntp_rvh = static_cast( - contents()->GetRenderManagerForTesting()->current_host()); + TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame(); // Create one more tab and navigate to kUrl1. web_contents is not // wrapped as scoped_ptr since it intentionally deleted by destroyer // below as part of this test. TestWebContents* web_contents = - TestWebContents::Create(browser_context(), ntp_rvh->GetSiteInstance()); + TestWebContents::Create(browser_context(), ntp_rfh->GetSiteInstance()); web_contents->NavigateAndCommit(kUrl1); - RenderViewHostDestroyer destroyer(ntp_rvh, web_contents); + RenderViewHostDestroyer destroyer(ntp_rfh->GetRenderViewHost(), + web_contents); // This causes the first tab to navigate to kUrl2, which destroys - // the ntp_rvh in ShutdownRenderViewHostsInSiteInstance(). When - // ntp_rvh is destroyed, it also destroys the RVHs in web_contents + // the ntp_rfh in ShutdownRenderViewHostsInSiteInstance(). When + // ntp_rfh is destroyed, it also destroys the RVHs in web_contents // too. This can test whether // SiteInstanceImpl::ShutdownRenderViewHostsInSiteInstance() can // touch any object freed in this way or not while iterating through @@ -727,13 +736,14 @@ TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) { kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); // Simulate response from RenderFrame for DispatchBeforeUnload. base::TimeTicks now = base::TimeTicks::Now(); - main_test_rfh()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK( - main_test_rfh()->GetRoutingID(), true, now, now)); - ASSERT_TRUE(pending_rvh()); // New pending RenderViewHost will be created. - RenderViewHost* last_rvh = pending_rvh(); - int32 new_id = contents()->GetMaxPageIDForSiteInstance( - active_rvh()->GetSiteInstance()) + 1; - pending_test_rvh()->SendNavigate(new_id, kUrl); + contents()->GetMainFrame()->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK( + contents()->GetMainFrame()->GetRoutingID(), true, now, now)); + ASSERT_TRUE(contents()->GetPendingMainFrame()) + << "Expected new pending RenderFrameHost to be created."; + RenderFrameHost* last_rfh = contents()->GetPendingMainFrame(); + int32 new_id = + contents()->GetMaxPageIDForSiteInstance(last_rfh->GetSiteInstance()) + 1; + contents()->GetPendingMainFrame()->SendNavigate(new_id, kUrl); EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1); ASSERT_TRUE(controller().GetLastCommittedEntry()); EXPECT_TRUE(kUrl == controller().GetLastCommittedEntry()->GetURL()); @@ -748,9 +758,10 @@ TEST_F(RenderFrameHostManagerTest, AlwaysSendEnableViewSourceMode) { controller().LoadURL( kUrl, Referrer(), PAGE_TRANSITION_TYPED, std::string()); // The same RenderViewHost should be reused. - EXPECT_FALSE(pending_rvh()); - EXPECT_TRUE(last_rvh == rvh()); - test_rvh()->SendNavigate(new_id, kUrl); // The same page_id returned. + EXPECT_FALSE(contents()->GetPendingMainFrame()); + EXPECT_TRUE(last_rfh == contents()->GetMainFrame()); + // Navigate using the returned page_id. + contents()->GetMainFrame()->SendNavigate(new_id, kUrl); EXPECT_EQ(controller().GetLastCommittedEntryIndex(), 1); EXPECT_FALSE(controller().GetPendingEntry()); // New message should be sent out to make sure to enter view-source mode. @@ -925,7 +936,7 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) { // Check that the navigation is still suspended because the old RVH // is not swapped out, yet. - EXPECT_TRUE(host2->render_view_host()->are_navigations_suspended()); + EXPECT_TRUE(host2->are_navigations_suspended()); MockRenderProcessHost* test_process_host2 = static_cast(host2->GetProcess()); test_process_host2->sink().ClearMessages(); @@ -975,9 +986,8 @@ TEST_F(RenderFrameHostManagerTest, NavigateWithEarlyReNavigation) { EXPECT_NE(host3, host); EXPECT_NE(host3->GetProcess()->GetID(), host2_process_id); - // Navigations in the new RVH should be suspended. - EXPECT_TRUE(static_cast( - host3->render_view_host())->are_navigations_suspended()); + // Navigations in the new RFH should be suspended. + EXPECT_TRUE(host3->are_navigations_suspended()); EXPECT_EQ(host, manager->current_frame_host()); EXPECT_FALSE(manager->current_frame_host()->is_swapped_out()); @@ -1048,10 +1058,10 @@ TEST_F(RenderFrameHostManagerTest, NewCrossNavigationBetweenSwapOutAndCommit) { // Pending rvh2 is already deleted. contents()->ProceedWithCrossSiteNavigation(); - TestRenderViewHost* rvh3 = pending_test_rvh(); - EXPECT_TRUE(rvh3); + TestRenderFrameHost* rfh3 = contents()->GetPendingMainFrame(); + EXPECT_TRUE(rfh3); // Navigation should be already unblocked by rvh1. - EXPECT_FALSE(rvh3->are_navigations_suspended()); + EXPECT_FALSE(rfh3->are_navigations_suspended()); } // Tests WebUI creation. @@ -1168,22 +1178,23 @@ TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) { const GURL kUrl2("http://www.evil-site.com/"); // Navigate to a safe site, then an evil site. - // This will switch RenderViewHosts. We cannot assert that the first and - // second RVHs are different, though, because the first one may be promptly + // This will switch RenderFrameHosts. We cannot assert that the first and + // second RFHs are different, though, because the first one may be promptly // deleted. contents()->NavigateAndCommit(kUrl1); contents()->NavigateAndCommit(kUrl2); - RenderViewHost* evil_rvh = contents()->GetRenderViewHost(); + TestRenderFrameHost* evil_rfh = contents()->GetMainFrame(); // Now let's simulate the evil page calling history.back(). contents()->OnGoToEntryAtOffset(-1); - // We should have a new pending RVH. - // Note that in this case, the navigation has not committed, so evil_rvh will + // We should have a new pending RFH. + // Note that in this case, the navigation has not committed, so evil_rfh will // not be deleted yet. - EXPECT_NE(evil_rvh, contents()->GetRenderManagerForTesting()-> - pending_render_view_host()); + EXPECT_NE(evil_rfh, contents()->GetPendingMainFrame()); + EXPECT_NE(evil_rfh->GetRenderViewHost(), + contents()->GetPendingMainFrame()->GetRenderViewHost()); - // Before that RVH has committed, the evil page reloads itself. + // Before that RFH has committed, the evil page reloads itself. FrameHostMsg_DidCommitProvisionalLoad_Params params; params.page_id = 1; params.url = kUrl2; @@ -1194,16 +1205,19 @@ TEST_F(RenderFrameHostManagerTest, PageDoesBackAndReload) { params.is_post = false; params.page_state = PageState::CreateFromURL(kUrl2); - RenderViewHostImpl* rvh = static_cast(evil_rvh); - RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID( - rvh->GetProcess()->GetID(), rvh->main_frame_routing_id()); - contents()->GetFrameTree()->root()->navigator()->DidNavigate(rfh, params); + contents()->GetFrameTree()->root()->navigator()->DidNavigate(evil_rfh, + params); - // That should have cancelled the pending RVH, and the evil RVH should be the + // That should have cancelled the pending RFH, and the evil RFH should be the // current one. EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> pending_render_view_host() == NULL); - EXPECT_EQ(evil_rvh, contents()->GetRenderManagerForTesting()->current_host()); + EXPECT_TRUE(contents()->GetRenderManagerForTesting()->pending_frame_host() == + NULL); + EXPECT_EQ(evil_rfh, + contents()->GetRenderManagerForTesting()->current_frame_host()); + EXPECT_EQ(evil_rfh->GetRenderViewHost(), + contents()->GetRenderManagerForTesting()->current_host()); // Also we should not have a pending navigation entry. EXPECT_TRUE(contents()->GetController().GetPendingEntry() == NULL); @@ -1554,45 +1568,46 @@ TEST_F(RenderFrameHostManagerTest, // Navigate to the first page. contents()->NavigateAndCommit(kUrl1); - TestRenderViewHost* rvh1 = test_rvh(); - RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + TestRenderFrameHost* rfh1 = contents()->GetMainFrame(); + RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh1->GetRenderViewHost()->rvh_state()); // Navigate to new site, simulating onbeforeunload approval. controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); base::TimeTicks now = base::TimeTicks::Now(); - main_test_rfh()->OnMessageReceived( - FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); + rfh1->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* rvh2 = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame(); - // Simulate rvh2's response, which leads to an unload request being sent to - // rvh1. + // Simulate rfh2's response, which leads to an unload request being sent to + // rfh1. std::vector url_chain; url_chain.push_back(GURL()); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( - contents()->GetRenderManagerForTesting()->pending_frame_host(), + rfh2, GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, - rvh1->rvh_state()); + rfh1->GetRenderViewHost()->rvh_state()); // Simulate the swap out ack. - rvh1->OnSwappedOut(false); - EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); + rfh1->OnSwappedOut(false); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, + rfh1->GetRenderViewHost()->rvh_state()); // The new page commits. - contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(rvh2, rvh()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + EXPECT_EQ(rfh2, contents()->GetMainFrame()); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh2->GetRenderViewHost()->rvh_state()); - // rvh1 should have been deleted. + // rfh1's rvh should have been deleted. EXPECT_TRUE(rvh_deleted_observer.deleted()); - rvh1 = NULL; + rfh1 = NULL; } // Tests that the RenderViewHost is properly swapped out when the SwapOutACK is @@ -1604,50 +1619,52 @@ TEST_F(RenderFrameHostManagerTest, // Navigate to the first page. contents()->NavigateAndCommit(kUrl1); - TestRenderViewHost* rvh1 = test_rvh(); - RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + TestRenderFrameHost* rfh1 = contents()->GetMainFrame(); + RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh1->GetRenderViewHost()->rvh_state()); - // Increment the number of active views in SiteInstanceImpl so that rvh2 is + // Increment the number of active views in SiteInstanceImpl so that rfh2 is // not deleted on swap out. static_cast( - rvh1->GetSiteInstance())->increment_active_view_count(); + rfh1->GetSiteInstance())->increment_active_view_count(); // Navigate to new site, simulating onbeforeunload approval. controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); base::TimeTicks now = base::TimeTicks::Now(); - main_test_rfh()->OnMessageReceived( + contents()->GetMainFrame()->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* rvh2 = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame(); - // Simulate rvh2's response, which leads to an unload request being sent to - // rvh1. + // Simulate rfh2's response, which leads to an unload request being sent to + // rfh1. std::vector url_chain; url_chain.push_back(GURL()); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( - contents()->GetRenderManagerForTesting()->pending_frame_host(), + rfh2, GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, - rvh1->rvh_state()); + rfh1->GetRenderViewHost()->rvh_state()); // Simulate the swap out ack. - rvh1->OnSwappedOut(false); - EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, rvh1->rvh_state()); + rfh1->OnSwappedOut(false); + EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_COMMIT, + rfh1->GetRenderViewHost()->rvh_state()); // The new page commits. - contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(rvh2, rvh()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); + EXPECT_EQ(rfh2, contents()->GetMainFrame()); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh2->GetRenderViewHost()->rvh_state()); - // rvh1 should be swapped out. + // rfh1 should be swapped out. EXPECT_FALSE(rvh_deleted_observer.deleted()); - EXPECT_TRUE(rvh1->IsSwappedOut()); + EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut()); } // Tests that the RenderViewHost is properly deleted when the new @@ -1659,45 +1676,47 @@ TEST_F(RenderFrameHostManagerTest, // Navigate to the first page. contents()->NavigateAndCommit(kUrl1); - TestRenderViewHost* rvh1 = test_rvh(); - RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + TestRenderFrameHost* rfh1 = contents()->GetMainFrame(); + RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh1->GetRenderViewHost()->rvh_state()); // Navigate to new site, simulating onbeforeunload approval. controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); base::TimeTicks now = base::TimeTicks::Now(); - main_test_rfh()->OnMessageReceived( + contents()->GetMainFrame()->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* rvh2 = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame(); - // Simulate rvh2's response, which leads to an unload request being sent to - // rvh1. + // Simulate rfh2's response, which leads to an unload request being sent to + // rfh1. std::vector url_chain; url_chain.push_back(GURL()); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( - contents()->GetRenderManagerForTesting()->pending_frame_host(), + rfh2, GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, - rvh1->rvh_state()); + rfh1->GetRenderViewHost()->rvh_state()); // The new page commits. - contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(rvh2, rvh()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); - EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, rvh1->rvh_state()); + EXPECT_EQ(rfh2, contents()->GetMainFrame()); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh2->GetRenderViewHost()->rvh_state()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SHUTDOWN, + rfh1->GetRenderViewHost()->rvh_state()); // Simulate the swap out ack. - rvh1->OnSwappedOut(false); + rfh1->OnSwappedOut(false); - // rvh1 should have been deleted. + // rfh1 should have been deleted. EXPECT_TRUE(rvh_deleted_observer.deleted()); - rvh1 = NULL; + rfh1 = NULL; } // Tests that the RenderViewHost is properly swapped out when the new page @@ -1709,50 +1728,52 @@ TEST_F(RenderFrameHostManagerTest, // Navigate to the first page. contents()->NavigateAndCommit(kUrl1); - TestRenderViewHost* rvh1 = test_rvh(); - RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + TestRenderFrameHost* rfh1 = contents()->GetMainFrame(); + RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh1->GetRenderViewHost()->rvh_state()); - // Increment the number of active views in SiteInstanceImpl so that rvh1 is + // Increment the number of active views in SiteInstanceImpl so that rfh1 is // not deleted on swap out. static_cast( - rvh1->GetSiteInstance())->increment_active_view_count(); + rfh1->GetSiteInstance())->increment_active_view_count(); // Navigate to new site, simulating onbeforeunload approval. controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); base::TimeTicks now = base::TimeTicks::Now(); - main_test_rfh()->OnMessageReceived( + contents()->GetMainFrame()->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* rvh2 = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame(); - // Simulate rvh2's response, which leads to an unload request being sent to - // rvh1. + // Simulate rfh2's response, which leads to an unload request being sent to + // rfh1. std::vector url_chain; url_chain.push_back(GURL()); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( - contents()->GetRenderManagerForTesting()->pending_frame_host(), + rfh2, GlobalRequestID(0, 0), scoped_ptr(), url_chain, Referrer(), PAGE_TRANSITION_TYPED, false); EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(RenderViewHostImpl::STATE_WAITING_FOR_UNLOAD_ACK, - rvh1->rvh_state()); + rfh1->GetRenderViewHost()->rvh_state()); // The new page commits. - contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(rvh2, rvh()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); - EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); + EXPECT_EQ(rfh2, contents()->GetMainFrame()); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh2->GetRenderViewHost()->rvh_state()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, + rfh1->GetRenderViewHost()->rvh_state()); // Simulate the swap out ack. - rvh1->OnSwappedOut(false); + rfh1->OnSwappedOut(false); - // rvh1 should be swapped out. + // rfh1 should be swapped out. EXPECT_FALSE(rvh_deleted_observer.deleted()); - EXPECT_TRUE(rvh1->IsSwappedOut()); + EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut()); } // Test that the RenderViewHost is properly swapped out if a navigation in the @@ -1766,38 +1787,40 @@ TEST_F(RenderFrameHostManagerTest, // Navigate to the first page. contents()->NavigateAndCommit(kUrl1); - TestRenderViewHost* rvh1 = test_rvh(); - RenderViewHostDeletedObserver rvh_deleted_observer(rvh1); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); + TestRenderFrameHost* rfh1 = contents()->GetMainFrame(); + RenderViewHostDeletedObserver rvh_deleted_observer(rfh1->GetRenderViewHost()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh1->GetRenderViewHost()->rvh_state()); - // Increment the number of active views in SiteInstanceImpl so that rvh1 is + // Increment the number of active views in SiteInstanceImpl so that rfh1 is // not deleted on swap out. static_cast( - rvh1->GetSiteInstance())->increment_active_view_count(); + rfh1->GetSiteInstance())->increment_active_view_count(); // Navigate to new site, simulating onbeforeunload approval. controller().LoadURL(kUrl2, Referrer(), PAGE_TRANSITION_LINK, std::string()); base::TimeTicks now = base::TimeTicks::Now(); - main_test_rfh()->OnMessageReceived( + rfh1->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* rvh2 = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* rfh2 = contents()->GetPendingMainFrame(); // The new page commits. - contents()->TestDidNavigate(rvh2, 1, kUrl2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(rfh2, 1, kUrl2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(rvh2, rvh()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh2->rvh_state()); - EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, rvh1->rvh_state()); + EXPECT_EQ(rfh2, contents()->GetMainFrame()); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + rfh2->GetRenderViewHost()->rvh_state()); + EXPECT_EQ(RenderViewHostImpl::STATE_PENDING_SWAP_OUT, + rfh1->GetRenderViewHost()->rvh_state()); // Simulate the swap out ack. - rvh1->OnSwappedOut(false); + rfh1->OnSwappedOut(false); - // rvh1 should be swapped out. + // rfh1 should be swapped out. EXPECT_FALSE(rvh_deleted_observer.deleted()); - EXPECT_TRUE(rvh1->IsSwappedOut()); + EXPECT_TRUE(rfh1->GetRenderViewHost()->IsSwappedOut()); } // Test that a RenderFrameHost is properly deleted or swapped out when a @@ -1822,7 +1845,7 @@ TEST_F(RenderFrameHostManagerTest, RenderFrameHostDeletedObserver rvh_deleted_observer(pending_rfh); // Cancel the navigation by simulating a declined beforeunload dialog. - main_test_rfh()->OnMessageReceived( + contents()->GetMainFrame()->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); EXPECT_FALSE(contents()->cross_navigation_pending()); @@ -1843,7 +1866,7 @@ TEST_F(RenderFrameHostManagerTest, static_cast( pending_rfh->GetSiteInstance())->increment_active_view_count(); - main_test_rfh()->OnMessageReceived( + contents()->GetMainFrame()->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); EXPECT_FALSE(contents()->cross_navigation_pending()); EXPECT_FALSE(rvh_deleted_observer.deleted()); @@ -1859,8 +1882,6 @@ TEST_F(RenderFrameHostManagerTest, BrowserSideNavigationBeginNavigation) { // Navigate to the first page. contents()->NavigateAndCommit(kUrl1); - TestRenderViewHost* rvh1 = test_rvh(); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, rvh1->rvh_state()); // Add a subframe. TestRenderFrameHost* subframe_rfh = static_cast( @@ -1881,10 +1902,9 @@ TEST_F(RenderFrameHostManagerTest, BrowserSideNavigationBeginNavigation) { EXPECT_TRUE(subframe_request->info_for_testing().parent_is_main_frame); // Simulate a BeginNavigation IPC on the main frame. - main_test_rfh()->SendBeginNavigationWithURL(kUrl3); - NavigationRequest* main_request = - NavigationRequestForRenderFrameManager( - main_test_rfh()->frame_tree_node()->render_manager()); + contents()->GetMainFrame()->SendBeginNavigationWithURL(kUrl3); + NavigationRequest* main_request = NavigationRequestForRenderFrameManager( + contents()->GetMainFrame()->frame_tree_node()->render_manager()); ASSERT_TRUE(main_request); EXPECT_EQ(kUrl3, main_request->info_for_testing().navigation_params.url); EXPECT_EQ(kUrl3, main_request->info_for_testing().first_party_for_cookies); diff --git a/content/browser/frame_host/render_widget_host_view_child_frame.cc b/content/browser/frame_host/render_widget_host_view_child_frame.cc index 5c4199c533863..94e7e137f67e7 100644 --- a/content/browser/frame_host/render_widget_host_view_child_frame.cc +++ b/content/browser/frame_host/render_widget_host_view_child_frame.cc @@ -122,7 +122,7 @@ void RenderWidgetHostViewChildFrame::ImeCompositionRangeChanged( void RenderWidgetHostViewChildFrame::WasShown() { if (!host_->is_hidden()) return; - host_->WasShown(); + host_->WasShown(ui::LatencyInfo()); } void RenderWidgetHostViewChildFrame::WasHidden() { diff --git a/content/browser/frame_host/render_widget_host_view_guest.cc b/content/browser/frame_host/render_widget_host_view_guest.cc index cc27520709725..51b3d6717f8ab 100644 --- a/content/browser/frame_host/render_widget_host_view_guest.cc +++ b/content/browser/frame_host/render_widget_host_view_guest.cc @@ -74,7 +74,7 @@ void RenderWidgetHostViewGuest::WasShown() { // |guest_| is NULL during test. if ((guest_ && guest_->is_in_destruction()) || !host_->is_hidden()) return; - host_->WasShown(); + host_->WasShown(ui::LatencyInfo()); } void RenderWidgetHostViewGuest::WasHidden() { @@ -109,8 +109,12 @@ void RenderWidgetHostViewGuest::ProcessAckedTouchEvent( INPUT_EVENT_ACK_STATE_CONSUMED) ? ui::ER_HANDLED : ui::ER_UNHANDLED; for (ScopedVector::iterator iter = events.begin(), end = events.end(); iter != end; ++iter) { + if (!ui::GestureRecognizer::Get()->ProcessTouchEventPreDispatch(*(*iter), + this)) { + continue; + } scoped_ptr gestures; - gestures.reset(gesture_recognizer_->ProcessTouchEventForGesture( + gestures.reset(ui::GestureRecognizer::Get()->ProcessTouchEventPostDispatch( *(*iter), result, this)); ProcessGestures(gestures.get()); } diff --git a/content/browser/geolocation/geolocation_provider_impl_unittest.cc b/content/browser/geolocation/geolocation_provider_impl_unittest.cc new file mode 100644 index 0000000000000..3575c3f905a25 --- /dev/null +++ b/content/browser/geolocation/geolocation_provider_impl_unittest.cc @@ -0,0 +1,258 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/strings/string16.h" +#include "base/time/time.h" +#include "content/browser/geolocation/geolocation_provider_impl.h" +#include "content/browser/geolocation/mock_location_arbitrator.h" +#include "content/public/browser/access_token_store.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_browser_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::MakeMatcher; +using testing::Matcher; +using testing::MatcherInterface; +using testing::MatchResultListener; + +namespace content { + +class LocationProviderForTestArbitrator : public GeolocationProviderImpl { + public: + LocationProviderForTestArbitrator() : mock_arbitrator_(NULL) {} + virtual ~LocationProviderForTestArbitrator() {} + + // Only valid for use on the geolocation thread. + MockLocationArbitrator* mock_arbitrator() const { + return mock_arbitrator_; + } + + protected: + // GeolocationProviderImpl implementation: + virtual LocationArbitrator* CreateArbitrator() OVERRIDE; + + private: + MockLocationArbitrator* mock_arbitrator_; +}; + +LocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() { + DCHECK(mock_arbitrator_ == NULL); + mock_arbitrator_ = new MockLocationArbitrator; + return mock_arbitrator_; +} + +class GeolocationObserver { + public: + virtual ~GeolocationObserver() {} + virtual void OnLocationUpdate(const Geoposition& position) = 0; +}; + +class MockGeolocationObserver : public GeolocationObserver { + public: + MOCK_METHOD1(OnLocationUpdate, void(const Geoposition& position)); +}; + +class AsyncMockGeolocationObserver : public MockGeolocationObserver { + public: + virtual void OnLocationUpdate(const Geoposition& position) OVERRIDE { + MockGeolocationObserver::OnLocationUpdate(position); + base::MessageLoop::current()->Quit(); + } +}; + +class MockGeolocationCallbackWrapper { + public: + MOCK_METHOD1(Callback, void(const Geoposition& position)); +}; + +class GeopositionEqMatcher + : public MatcherInterface { + public: + explicit GeopositionEqMatcher(const Geoposition& expected) + : expected_(expected) {} + + virtual bool MatchAndExplain(const Geoposition& actual, + MatchResultListener* listener) const OVERRIDE { + return actual.latitude == expected_.latitude && + actual.longitude == expected_.longitude && + actual.altitude == expected_.altitude && + actual.accuracy == expected_.accuracy && + actual.altitude_accuracy == expected_.altitude_accuracy && + actual.heading == expected_.heading && + actual.speed == expected_.speed && + actual.timestamp == expected_.timestamp && + actual.error_code == expected_.error_code && + actual.error_message == expected_.error_message; + } + + virtual void DescribeTo(::std::ostream* os) const OVERRIDE { + *os << "which matches the expected position"; + } + + virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE { + *os << "which does not match the expected position"; + } + + private: + Geoposition expected_; + + DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher); +}; + +Matcher GeopositionEq(const Geoposition& expected) { + return MakeMatcher(new GeopositionEqMatcher(expected)); +} + +class GeolocationProviderTest : public testing::Test { + protected: + GeolocationProviderTest() + : message_loop_(), + ui_thread_(BrowserThread::UI, &message_loop_), + provider_(new LocationProviderForTestArbitrator) { + } + + virtual ~GeolocationProviderTest() {} + + LocationProviderForTestArbitrator* provider() { return provider_.get(); } + + // Called on test thread. + bool ProvidersStarted(); + void SendMockLocation(const Geoposition& position); + + private: + // Called on provider thread. + void GetProvidersStarted(bool* started); + + base::MessageLoop message_loop_; + TestBrowserThread ui_thread_; + scoped_ptr provider_; +}; + + +bool GeolocationProviderTest::ProvidersStarted() { + DCHECK(provider_->IsRunning()); + DCHECK(base::MessageLoop::current() == &message_loop_); + bool started; + provider_->message_loop_proxy()->PostTaskAndReply( + FROM_HERE, + base::Bind(&GeolocationProviderTest::GetProvidersStarted, + base::Unretained(this), + &started), + base::MessageLoop::QuitClosure()); + message_loop_.Run(); + return started; +} + +void GeolocationProviderTest::GetProvidersStarted(bool* started) { + DCHECK(base::MessageLoop::current() == provider_->message_loop()); + *started = provider_->mock_arbitrator()->providers_started(); +} + +void GeolocationProviderTest::SendMockLocation(const Geoposition& position) { + DCHECK(provider_->IsRunning()); + DCHECK(base::MessageLoop::current() == &message_loop_); + provider_->message_loop() + ->PostTask(FROM_HERE, + base::Bind(&GeolocationProviderImpl::OnLocationUpdate, + base::Unretained(provider_.get()), + position)); +} + +// Regression test for http://crbug.com/59377 +TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) { + EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing()); + provider()->UserDidOptIntoLocationServices(); + EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing()); +} + +void DummyFunction(const Geoposition& position) { +} + +TEST_F(GeolocationProviderTest, StartStop) { + EXPECT_FALSE(provider()->IsRunning()); + GeolocationProviderImpl::LocationUpdateCallback callback = + base::Bind(&DummyFunction); + scoped_ptr subscription = + provider()->AddLocationUpdateCallback(callback, false); + EXPECT_TRUE(provider()->IsRunning()); + EXPECT_TRUE(ProvidersStarted()); + + subscription.reset(); + + EXPECT_FALSE(ProvidersStarted()); + EXPECT_TRUE(provider()->IsRunning()); +} + +TEST_F(GeolocationProviderTest, StalePositionNotSent) { + Geoposition first_position; + first_position.latitude = 12; + first_position.longitude = 34; + first_position.accuracy = 56; + first_position.timestamp = base::Time::Now(); + + AsyncMockGeolocationObserver first_observer; + GeolocationProviderImpl::LocationUpdateCallback first_callback = base::Bind( + &MockGeolocationObserver::OnLocationUpdate, + base::Unretained(&first_observer)); + EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position))); + scoped_ptr subscription = + provider()->AddLocationUpdateCallback(first_callback, false); + SendMockLocation(first_position); + base::MessageLoop::current()->Run(); + + subscription.reset(); + + Geoposition second_position; + second_position.latitude = 13; + second_position.longitude = 34; + second_position.accuracy = 56; + second_position.timestamp = base::Time::Now(); + + AsyncMockGeolocationObserver second_observer; + + // After adding a second observer, check that no unexpected position update + // is sent. + EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0); + GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind( + &MockGeolocationObserver::OnLocationUpdate, + base::Unretained(&second_observer)); + scoped_ptr subscription2 = + provider()->AddLocationUpdateCallback(second_callback, false); + base::MessageLoop::current()->RunUntilIdle(); + + // The second observer should receive the new position now. + EXPECT_CALL(second_observer, + OnLocationUpdate(GeopositionEq(second_position))); + SendMockLocation(second_position); + base::MessageLoop::current()->Run(); + + subscription2.reset(); + EXPECT_FALSE(ProvidersStarted()); +} + +TEST_F(GeolocationProviderTest, OverrideLocationForTesting) { + Geoposition position; + position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + provider()->OverrideLocationForTesting(position); + // Adding an observer when the location is overridden should synchronously + // update the observer with our overridden position. + MockGeolocationObserver mock_observer; + EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position))); + GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind( + &MockGeolocationObserver::OnLocationUpdate, + base::Unretained(&mock_observer)); + scoped_ptr subscription = + provider()->AddLocationUpdateCallback(callback, false); + subscription.reset(); + // Wait for the providers to be stopped now that all clients are gone. + EXPECT_FALSE(ProvidersStarted()); +} + +} // namespace content diff --git a/content/browser/geolocation/geolocation_provider_unittest.cc b/content/browser/geolocation/geolocation_provider_unittest.cc deleted file mode 100644 index 95fa1b69145ed..0000000000000 --- a/content/browser/geolocation/geolocation_provider_unittest.cc +++ /dev/null @@ -1,258 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/strings/string16.h" -#include "base/time/time.h" -#include "content/browser/geolocation/geolocation_provider_impl.h" -#include "content/browser/geolocation/mock_location_arbitrator.h" -#include "content/public/browser/access_token_store.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/test/test_browser_thread.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using testing::MakeMatcher; -using testing::Matcher; -using testing::MatcherInterface; -using testing::MatchResultListener; - -namespace content { - -class LocationProviderForTestArbitrator : public GeolocationProviderImpl { - public: - LocationProviderForTestArbitrator() : mock_arbitrator_(NULL) {} - virtual ~LocationProviderForTestArbitrator() {} - - // Only valid for use on the geolocation thread. - MockLocationArbitrator* mock_arbitrator() const { - return mock_arbitrator_; - } - - protected: - // GeolocationProviderImpl implementation: - virtual LocationArbitrator* CreateArbitrator() OVERRIDE; - - private: - MockLocationArbitrator* mock_arbitrator_; -}; - -LocationArbitrator* LocationProviderForTestArbitrator::CreateArbitrator() { - DCHECK(mock_arbitrator_ == NULL); - mock_arbitrator_ = new MockLocationArbitrator; - return mock_arbitrator_; -} - -class GeolocationObserver { - public: - virtual ~GeolocationObserver() {} - virtual void OnLocationUpdate(const Geoposition& position) = 0; -}; - -class MockGeolocationObserver : public GeolocationObserver { - public: - MOCK_METHOD1(OnLocationUpdate, void(const Geoposition& position)); -}; - -class AsyncMockGeolocationObserver : public MockGeolocationObserver { - public: - virtual void OnLocationUpdate(const Geoposition& position) OVERRIDE { - MockGeolocationObserver::OnLocationUpdate(position); - base::MessageLoop::current()->Quit(); - } -}; - -class MockGeolocationCallbackWrapper { - public: - MOCK_METHOD1(Callback, void(const Geoposition& position)); -}; - -class GeopositionEqMatcher - : public MatcherInterface { - public: - explicit GeopositionEqMatcher(const Geoposition& expected) - : expected_(expected) {} - - virtual bool MatchAndExplain(const Geoposition& actual, - MatchResultListener* listener) const OVERRIDE { - return actual.latitude == expected_.latitude && - actual.longitude == expected_.longitude && - actual.altitude == expected_.altitude && - actual.accuracy == expected_.accuracy && - actual.altitude_accuracy == expected_.altitude_accuracy && - actual.heading == expected_.heading && - actual.speed == expected_.speed && - actual.timestamp == expected_.timestamp && - actual.error_code == expected_.error_code && - actual.error_message == expected_.error_message; - } - - virtual void DescribeTo(::std::ostream* os) const OVERRIDE { - *os << "which matches the expected position"; - } - - virtual void DescribeNegationTo(::std::ostream* os) const OVERRIDE { - *os << "which does not match the expected position"; - } - - private: - Geoposition expected_; - - DISALLOW_COPY_AND_ASSIGN(GeopositionEqMatcher); -}; - -Matcher GeopositionEq(const Geoposition& expected) { - return MakeMatcher(new GeopositionEqMatcher(expected)); -} - -class GeolocationProviderTest : public testing::Test { - protected: - GeolocationProviderTest() - : message_loop_(), - ui_thread_(BrowserThread::UI, &message_loop_), - provider_(new LocationProviderForTestArbitrator) { - } - - virtual ~GeolocationProviderTest() {} - - LocationProviderForTestArbitrator* provider() { return provider_.get(); } - - // Called on test thread. - bool ProvidersStarted(); - void SendMockLocation(const Geoposition& position); - - private: - // Called on provider thread. - void GetProvidersStarted(bool* started); - - base::MessageLoop message_loop_; - TestBrowserThread ui_thread_; - scoped_ptr provider_; -}; - - -bool GeolocationProviderTest::ProvidersStarted() { - DCHECK(provider_->IsRunning()); - DCHECK(base::MessageLoop::current() == &message_loop_); - bool started; - provider_->message_loop_proxy()->PostTaskAndReply( - FROM_HERE, - base::Bind(&GeolocationProviderTest::GetProvidersStarted, - base::Unretained(this), - &started), - base::MessageLoop::QuitClosure()); - message_loop_.Run(); - return started; -} - -void GeolocationProviderTest::GetProvidersStarted(bool* started) { - DCHECK(base::MessageLoop::current() == provider_->message_loop()); - *started = provider_->mock_arbitrator()->providers_started(); -} - -void GeolocationProviderTest::SendMockLocation(const Geoposition& position) { - DCHECK(provider_->IsRunning()); - DCHECK(base::MessageLoop::current() == &message_loop_); - provider_->message_loop() - ->PostTask(FROM_HERE, - base::Bind(&GeolocationProviderImpl::OnLocationUpdate, - base::Unretained(provider_.get()), - position)); -} - -// Regression test for http://crbug.com/59377 -TEST_F(GeolocationProviderTest, OnPermissionGrantedWithoutObservers) { - EXPECT_FALSE(provider()->user_did_opt_into_location_services_for_testing()); - provider()->UserDidOptIntoLocationServices(); - EXPECT_TRUE(provider()->user_did_opt_into_location_services_for_testing()); -} - -void DummyFunction(const Geoposition& position) { -} - -TEST_F(GeolocationProviderTest, StartStop) { - EXPECT_FALSE(provider()->IsRunning()); - GeolocationProviderImpl::LocationUpdateCallback callback = - base::Bind(&DummyFunction); - scoped_ptr subscription = - provider()->AddLocationUpdateCallback(callback, false); - EXPECT_TRUE(provider()->IsRunning()); - EXPECT_TRUE(ProvidersStarted()); - - subscription.reset(); - - EXPECT_FALSE(ProvidersStarted()); - EXPECT_TRUE(provider()->IsRunning()); -} - -TEST_F(GeolocationProviderTest, StalePositionNotSent) { - Geoposition first_position; - first_position.latitude = 12; - first_position.longitude = 34; - first_position.accuracy = 56; - first_position.timestamp = base::Time::Now(); - - AsyncMockGeolocationObserver first_observer; - GeolocationProviderImpl::LocationUpdateCallback first_callback = base::Bind( - &MockGeolocationObserver::OnLocationUpdate, - base::Unretained(&first_observer)); - EXPECT_CALL(first_observer, OnLocationUpdate(GeopositionEq(first_position))); - scoped_ptr subscription = - provider()->AddLocationUpdateCallback(first_callback, false); - SendMockLocation(first_position); - base::MessageLoop::current()->Run(); - - subscription.reset(); - - Geoposition second_position; - second_position.latitude = 13; - second_position.longitude = 34; - second_position.accuracy = 56; - second_position.timestamp = base::Time::Now(); - - AsyncMockGeolocationObserver second_observer; - - // After adding a second observer, check that no unexpected position update - // is sent. - EXPECT_CALL(second_observer, OnLocationUpdate(testing::_)).Times(0); - GeolocationProviderImpl::LocationUpdateCallback second_callback = base::Bind( - &MockGeolocationObserver::OnLocationUpdate, - base::Unretained(&second_observer)); - scoped_ptr subscription2 = - provider()->AddLocationUpdateCallback(second_callback, false); - base::MessageLoop::current()->RunUntilIdle(); - - // The second observer should receive the new position now. - EXPECT_CALL(second_observer, - OnLocationUpdate(GeopositionEq(second_position))); - SendMockLocation(second_position); - base::MessageLoop::current()->Run(); - - subscription2.reset(); - EXPECT_FALSE(ProvidersStarted()); -} - -TEST_F(GeolocationProviderTest, OverrideLocationForTesting) { - Geoposition position; - position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; - provider()->OverrideLocationForTesting(position); - // Adding an observer when the location is overridden should synchronously - // update the observer with our overridden position. - MockGeolocationObserver mock_observer; - EXPECT_CALL(mock_observer, OnLocationUpdate(GeopositionEq(position))); - GeolocationProviderImpl::LocationUpdateCallback callback = base::Bind( - &MockGeolocationObserver::OnLocationUpdate, - base::Unretained(&mock_observer)); - scoped_ptr subscription = - provider()->AddLocationUpdateCallback(callback, false); - subscription.reset(); - // Wait for the providers to be stopped now that all clients are gone. - EXPECT_FALSE(ProvidersStarted()); -} - -} // namespace content diff --git a/content/browser/geolocation/location_arbitrator_impl.cc b/content/browser/geolocation/location_arbitrator_impl.cc index b8ed8c49bf51b..09b6c10167050 100644 --- a/content/browser/geolocation/location_arbitrator_impl.cc +++ b/content/browser/geolocation/location_arbitrator_impl.cc @@ -28,9 +28,9 @@ const int64 LocationArbitratorImpl::kFixStaleTimeoutMilliseconds = LocationArbitratorImpl::LocationArbitratorImpl( const LocationUpdateCallback& callback) - : callback_(callback), - provider_callback_( - base::Bind(&LocationArbitratorImpl::LocationUpdateAvailable, + : arbitrator_update_callback_(callback), + provider_update_callback_( + base::Bind(&LocationArbitratorImpl::OnLocationUpdate, base::Unretained(this))), position_provider_(NULL), is_permission_granted_(false), @@ -116,15 +116,14 @@ void LocationArbitratorImpl::RegisterProvider( LocationProvider* provider) { if (!provider) return; - provider->SetUpdateCallback(provider_callback_); + provider->SetUpdateCallback(provider_update_callback_); if (is_permission_granted_) provider->OnPermissionGranted(); providers_.push_back(provider); } -void LocationArbitratorImpl::LocationUpdateAvailable( - const LocationProvider* provider, - const Geoposition& new_position) { +void LocationArbitratorImpl::OnLocationUpdate(const LocationProvider* provider, + const Geoposition& new_position) { DCHECK(new_position.Validate() || new_position.error_code != Geoposition::ERROR_CODE_NONE); if (!IsNewPositionBetter(position_, new_position, @@ -132,7 +131,7 @@ void LocationArbitratorImpl::LocationUpdateAvailable( return; position_provider_ = provider; position_ = new_position; - callback_.Run(position_); + arbitrator_update_callback_.Run(position_); } AccessTokenStore* LocationArbitratorImpl::NewAccessTokenStore() { @@ -150,7 +149,7 @@ LocationProvider* LocationArbitratorImpl::NewNetworkLocationProvider( net::URLRequestContextGetter* context, const GURL& url, const base::string16& access_token) { -#if defined(OS_ANDROID) +#if defined(OS_ANDROID) || defined(OS_TIZEN) // Android uses its own SystemLocationProvider. return NULL; #else @@ -160,7 +159,9 @@ LocationProvider* LocationArbitratorImpl::NewNetworkLocationProvider( } LocationProvider* LocationArbitratorImpl::NewSystemLocationProvider() { -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) +#if defined(OS_TIZEN) + return content::NewSystemLocationProvider(); +#elif defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) return NULL; #else return content::NewSystemLocationProvider(); diff --git a/content/browser/geolocation/location_arbitrator_impl.h b/content/browser/geolocation/location_arbitrator_impl.h index da20e7b1889dd..21ba90e98e394 100644 --- a/content/browser/geolocation/location_arbitrator_impl.h +++ b/content/browser/geolocation/location_arbitrator_impl.h @@ -70,9 +70,9 @@ class CONTENT_EXPORT LocationArbitratorImpl : public LocationArbitrator { net::URLRequestContextGetter* context_getter); void DoStartProviders(); - // The providers call this function when a new position is available. - void LocationUpdateAvailable(const LocationProvider* provider, - const Geoposition& new_position); + // Gets called when a provider has a new position. + void OnLocationUpdate(const LocationProvider* provider, + const Geoposition& new_position); // Returns true if |new_position| is an improvement over |old_position|. // Set |from_same_provider| to true if both the positions came from the same @@ -82,8 +82,8 @@ class CONTENT_EXPORT LocationArbitratorImpl : public LocationArbitrator { bool from_same_provider) const; scoped_refptr access_token_store_; - LocationUpdateCallback callback_; - LocationProvider::LocationProviderUpdateCallback provider_callback_; + LocationUpdateCallback arbitrator_update_callback_; + LocationProvider::LocationProviderUpdateCallback provider_update_callback_; ScopedVector providers_; bool use_high_accuracy_; // The provider which supplied the current |position_| diff --git a/content/browser/geolocation/network_location_provider.cc b/content/browser/geolocation/network_location_provider.cc index 2a9d7c8a3b871..0cc7f8fc6853c 100644 --- a/content/browser/geolocation/network_location_provider.cc +++ b/content/browser/geolocation/network_location_provider.cc @@ -110,7 +110,7 @@ NetworkLocationProvider::NetworkLocationProvider( : access_token_store_(access_token_store), wifi_data_provider_(NULL), wifi_data_update_callback_( - base::Bind(&NetworkLocationProvider::WifiDataUpdateAvailable, + base::Bind(&NetworkLocationProvider::OnWifiDataUpdate, base::Unretained(this))), is_wifi_data_complete_(false), access_token_(access_token), @@ -120,10 +120,11 @@ NetworkLocationProvider::NetworkLocationProvider( // Create the position cache. position_cache_.reset(new PositionCache()); - NetworkLocationRequest::LocationResponseCallback callback = - base::Bind(&NetworkLocationProvider::LocationResponseAvailable, - base::Unretained(this)); - request_.reset(new NetworkLocationRequest(url_context_getter, url, callback)); + request_.reset(new NetworkLocationRequest( + url_context_getter, + url, + base::Bind(&NetworkLocationProvider::OnLocationResponse, + base::Unretained(this)))); } NetworkLocationProvider::~NetworkLocationProvider() { @@ -153,14 +154,13 @@ void NetworkLocationProvider::OnPermissionGranted() { } } -void NetworkLocationProvider::WifiDataUpdateAvailable( - WifiDataProvider* provider) { +void NetworkLocationProvider::OnWifiDataUpdate(WifiDataProvider* provider) { DCHECK(provider == wifi_data_provider_); is_wifi_data_complete_ = wifi_data_provider_->GetData(&wifi_data_); OnWifiDataUpdated(); } -void NetworkLocationProvider::LocationResponseAvailable( +void NetworkLocationProvider::OnLocationResponse( const Geoposition& position, bool server_error, const base::string16& access_token, diff --git a/content/browser/geolocation/network_location_provider.h b/content/browser/geolocation/network_location_provider.h index 710fd3f4fa6d9..daf0c8b4417e5 100644 --- a/content/browser/geolocation/network_location_provider.h +++ b/content/browser/geolocation/network_location_provider.h @@ -82,18 +82,18 @@ class NetworkLocationProvider // Satisfies a position request from cache or network. void RequestPosition(); - // Called from a callback when new wifi data is available. - void WifiDataUpdateAvailable(WifiDataProvider* provider); + // Gets called when new wifi data is available. + void OnWifiDataUpdate(WifiDataProvider* provider); - // Internal helper used by WifiDataUpdateAvailable. + // Internal helper used by OnWifiDataUpdate. void OnWifiDataUpdated(); bool IsStarted() const; - void LocationResponseAvailable(const Geoposition& position, - bool server_error, - const base::string16& access_token, - const WifiData& wifi_data); + void OnLocationResponse(const Geoposition& position, + bool server_error, + const base::string16& access_token, + const WifiData& wifi_data); scoped_refptr access_token_store_; diff --git a/content/browser/geolocation/network_location_provider_unittest.cc b/content/browser/geolocation/network_location_provider_unittest.cc index c574b7a7b96be..23bb75483e8d5 100644 --- a/content/browser/geolocation/network_location_provider_unittest.cc +++ b/content/browser/geolocation/network_location_provider_unittest.cc @@ -35,8 +35,8 @@ class MessageLoopQuitListener { CHECK(client_message_loop_); } - void LocationUpdateAvailable(const LocationProvider* provider, - const Geoposition& position) { + void OnLocationUpdate(const LocationProvider* provider, + const Geoposition& position) { EXPECT_EQ(client_message_loop_, base::MessageLoop::current()); updated_provider_ = provider; client_message_loop_->Quit(); @@ -473,9 +473,8 @@ TEST_F(GeolocationNetworkProviderTest, NoRequestOnStartupUntilWifiData) { scoped_ptr provider(CreateProvider(true)); EXPECT_TRUE(provider->StartProvider(false)); - provider->SetUpdateCallback( - base::Bind(&MessageLoopQuitListener::LocationUpdateAvailable, - base::Unretained(&listener))); + provider->SetUpdateCallback(base::Bind( + &MessageLoopQuitListener::OnLocationUpdate, base::Unretained(&listener))); main_message_loop_.RunUntilIdle(); EXPECT_FALSE(get_url_fetcher_and_advance_id()) diff --git a/content/browser/geolocation/network_location_request.cc b/content/browser/geolocation/network_location_request.cc index 75c7c8434f4d2..84ba5b05edcc5 100644 --- a/content/browser/geolocation/network_location_request.cc +++ b/content/browser/geolocation/network_location_request.cc @@ -103,9 +103,7 @@ NetworkLocationRequest::NetworkLocationRequest( net::URLRequestContextGetter* context, const GURL& url, LocationResponseCallback callback) - : url_context_(context), - callback_(callback), - url_(url) { + : url_context_(context), location_response_callback_(callback), url_(url) { } NetworkLocationRequest::~NetworkLocationRequest() { @@ -122,7 +120,7 @@ bool NetworkLocationRequest::MakeRequest(const base::string16& access_token, url_fetcher_.reset(); } wifi_data_ = wifi_data; - timestamp_ = timestamp; + wifi_data_timestamp_ = timestamp; GURL request_url = FormRequestURL(url_); url_fetcher_.reset(net::URLFetcher::Create( @@ -136,7 +134,7 @@ bool NetworkLocationRequest::MakeRequest(const base::string16& access_token, net::LOAD_DO_NOT_SAVE_COOKIES | net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SEND_AUTH_DATA); - start_time_ = base::TimeTicks::Now(); + request_start_time_ = base::TimeTicks::Now(); url_fetcher_->Start(); return true; } @@ -156,7 +154,7 @@ void NetworkLocationRequest::OnURLFetchComplete( GetLocationFromResponse(status.is_success(), response_code, data, - timestamp_, + wifi_data_timestamp_, source->GetURL(), &position, &access_token); @@ -165,7 +163,8 @@ void NetworkLocationRequest::OnURLFetchComplete( url_fetcher_.reset(); if (!server_error) { - const base::TimeDelta request_time = base::TimeTicks::Now() - start_time_; + const base::TimeDelta request_time = + base::TimeTicks::Now() - request_start_time_; UMA_HISTOGRAM_CUSTOM_TIMES( "Net.Wifi.LbsLatency", @@ -176,7 +175,8 @@ void NetworkLocationRequest::OnURLFetchComplete( } DVLOG(1) << "NetworkLocationRequest::OnURLFetchComplete() : run callback."; - callback_.Run(position, server_error, access_token, wifi_data_); + location_response_callback_.Run( + position, server_error, access_token, wifi_data_); } // Local functions. diff --git a/content/browser/geolocation/network_location_request.h b/content/browser/geolocation/network_location_request.h index 6fea08b8bd5d8..23bffb92aeedf 100644 --- a/content/browser/geolocation/network_location_request.h +++ b/content/browser/geolocation/network_location_request.h @@ -57,17 +57,17 @@ class NetworkLocationRequest : private net::URLFetcherDelegate { virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; scoped_refptr url_context_; - LocationResponseCallback callback_; + LocationResponseCallback location_response_callback_; const GURL url_; scoped_ptr url_fetcher_; // Keep a copy of the data sent in the request, so we can refer back to it // when the response arrives. WifiData wifi_data_; - base::Time timestamp_; // Timestamp of the above data, not of the request. + base::Time wifi_data_timestamp_; // The start time for the request. - base::TimeTicks start_time_; + base::TimeTicks request_start_time_; DISALLOW_COPY_AND_ASSIGN(NetworkLocationRequest); }; diff --git a/content/browser/geolocation/wifi_data_provider_common_unittest.cc b/content/browser/geolocation/wifi_data_provider_common_unittest.cc index 9c55dfd333567..7b583f2f4e7d7 100644 --- a/content/browser/geolocation/wifi_data_provider_common_unittest.cc +++ b/content/browser/geolocation/wifi_data_provider_common_unittest.cc @@ -62,12 +62,12 @@ class MessageLoopQuitter { public: explicit MessageLoopQuitter(base::MessageLoop* message_loop) : message_loop_to_quit_(message_loop), - callback_(base::Bind(&MessageLoopQuitter::WifiDataUpdateAvailable, + callback_(base::Bind(&MessageLoopQuitter::OnWifiDataUpdate, base::Unretained(this))) { CHECK(message_loop_to_quit_ != NULL); } - void WifiDataUpdateAvailable(WifiDataProvider* provider) { + void OnWifiDataUpdate(WifiDataProvider* provider) { // Provider should call back on client's thread. EXPECT_EQ(base::MessageLoop::current(), message_loop_to_quit_); message_loop_to_quit_->QuitNow(); diff --git a/content/browser/gpu/compositor_util.cc b/content/browser/gpu/compositor_util.cc index 844f63deb9776..028de132859f5 100644 --- a/content/browser/gpu/compositor_util.cc +++ b/content/browser/gpu/compositor_util.cc @@ -48,7 +48,8 @@ struct GpuFeatureInfo { }; const GpuFeatureInfo GetGpuFeatureInfo(size_t index, bool* eof) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); GpuDataManagerImpl* manager = GpuDataManagerImpl::GetInstance(); const GpuFeatureInfo kGpuFeatureInfo[] = { @@ -163,7 +164,8 @@ const GpuFeatureInfo GetGpuFeatureInfo(size_t index, bool* eof) { } // namespace bool IsPinchVirtualViewportEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); // Command line switches take precedence over platform default. if (command_line.HasSwitch(cc::switches::kDisablePinchVirtualViewport)) @@ -179,7 +181,8 @@ bool IsPinchVirtualViewportEnabled() { } bool IsDelegatedRendererEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); bool enabled = false; #if defined(USE_AURA) || defined(OS_MACOSX) @@ -194,7 +197,8 @@ bool IsDelegatedRendererEnabled() { } bool IsImplSidePaintingEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kDisableImplSidePainting)) return false; @@ -212,7 +216,8 @@ bool IsImplSidePaintingEnabled() { } bool IsGpuRasterizationEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (!IsImplSidePaintingEnabled()) return false; @@ -230,7 +235,8 @@ bool IsGpuRasterizationEnabled() { } bool IsForceGpuRasterizationEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (!IsImplSidePaintingEnabled()) return false; diff --git a/content/browser/gpu/gpu_data_manager_impl_private.cc b/content/browser/gpu/gpu_data_manager_impl_private.cc index f4c6c9c9b1619..bbf7231c95485 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private.cc @@ -113,7 +113,8 @@ void UpdateStats(const gpu::GPUInfo& gpu_info, return; } - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); bool disabled = false; // Use entry 0 to capture the total number of times that data @@ -265,7 +266,7 @@ void GpuDataManagerImplPrivate::InitializeForTesting( bool GpuDataManagerImplPrivate::IsFeatureBlacklisted(int feature) const { #if defined(OS_CHROMEOS) if (feature == gpu::GPU_FEATURE_TYPE_PANEL_FITTING && - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisablePanelFitting)) { return true; } @@ -323,7 +324,7 @@ bool GpuDataManagerImplPrivate::GpuAccessAllowed( if (card_blacklisted_) { if (reason) { *reason = "GPU access is disabled "; - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDisableGpu)) *reason += "through commandline switch --disable-gpu."; else @@ -477,7 +478,8 @@ void GpuDataManagerImplPrivate::Initialize() { return; } - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kSkipGpuDataLoading)) return; @@ -535,7 +537,7 @@ void GpuDataManagerImplPrivate::UpdateGpuInfoHelper() { gpu::GpuControlList::kOsAny, std::string(), gpu_info_); } gpu::GpuDriverBugList::AppendWorkaroundsFromCommandLine( - &gpu_driver_bugs_, *CommandLine::ForCurrentProcess()); + &gpu_driver_bugs_, *base::CommandLine::ForCurrentProcess()); // We have to update GpuFeatureType before notify all the observers. NotifyGpuInfoUpdate(); @@ -561,7 +563,7 @@ void GpuDataManagerImplPrivate::UpdateVideoMemoryUsageStats( } void GpuDataManagerImplPrivate::AppendRendererCommandLine( - CommandLine* command_line) const { + base::CommandLine* command_line) const { DCHECK(command_line); if (IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) && @@ -580,13 +582,14 @@ void GpuDataManagerImplPrivate::AppendRendererCommandLine( } void GpuDataManagerImplPrivate::AppendGpuCommandLine( - CommandLine* command_line) const { + base::CommandLine* command_line) const { DCHECK(command_line); std::string use_gl = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kUseGL); + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kUseGL); base::FilePath swiftshader_path = - CommandLine::ForCurrentProcess()->GetSwitchValuePath( + base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kSwiftShaderPath); if (gpu_driver_bugs_.find(gpu::DISABLE_D3D11) != gpu_driver_bugs_.end()) command_line->AppendSwitch(switches::kDisableD3D11); @@ -645,7 +648,7 @@ void GpuDataManagerImplPrivate::AppendGpuCommandLine( } void GpuDataManagerImplPrivate::AppendPluginCommandLine( - CommandLine* command_line) const { + base::CommandLine* command_line) const { DCHECK(command_line); #if defined(OS_MACOSX) @@ -692,7 +695,7 @@ void GpuDataManagerImplPrivate::UpdateRendererWebPrefs( #endif if (!IsFeatureBlacklisted(gpu::GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE) && - !CommandLine::ForCurrentProcess()->HasSwitch( + !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableAcceleratedVideoDecode)) { prefs->pepper_accelerated_video_decode_enabled = true; } @@ -871,7 +874,8 @@ GpuDataManagerImplPrivate::GpuDataManagerImplPrivate( gpu_process_accessible_(true), finalized_(false) { DCHECK(owner_); - CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDisableGpu)) DisableHardwareAcceleration(); @@ -897,7 +901,7 @@ void GpuDataManagerImplPrivate::InitializeImpl( const std::string& gpu_driver_bug_list_json, const gpu::GPUInfo& gpu_info) { const bool log_gpu_control_list_decisions = - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kLogGpuControlListDecisions); if (!gpu_blacklist_json.empty()) { @@ -962,7 +966,7 @@ void GpuDataManagerImplPrivate::EnableSwiftShaderIfNecessary() { if (!GpuAccessAllowed(NULL) || blacklisted_features_.count(gpu::GPU_FEATURE_TYPE_WEBGL)) { if (!swiftshader_path_.empty() && - !CommandLine::ForCurrentProcess()->HasSwitch( + !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableSoftwareRasterizer)) use_swiftshader_ = true; } diff --git a/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc b/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc index 8955ce1f88d75..41458edb5e92e 100644 --- a/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc +++ b/content/browser/gpu/gpu_data_manager_impl_private_unittest.cc @@ -158,10 +158,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuSideBlacklisting) { }, { "id": 2, - "gl_renderer": { - "op": "contains", - "value": "GeForce" - }, + "gl_renderer": ".*GeForce.*", "features": [ "accelerated_2d_canvas" ] @@ -205,10 +202,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuSideExceptions) { "id": 1, "exceptions": [ { - "gl_renderer": { - "op": "contains", - "value": "GeForce" - } + "gl_renderer": ".*GeForce.*" } ], "features": [ @@ -601,7 +595,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuDriverBugListSingle) { ScopedGpuDataManagerImplPrivate manager; manager->gpu_driver_bugs_.insert(5); - CommandLine command_line(0, NULL); + base::CommandLine command_line(0, NULL); manager->AppendGpuCommandLine(&command_line); EXPECT_TRUE(command_line.HasSwitch(switches::kGpuDriverBugWorkarounds)); @@ -615,7 +609,7 @@ TEST_F(GpuDataManagerImplPrivateTest, GpuDriverBugListMultiple) { manager->gpu_driver_bugs_.insert(5); manager->gpu_driver_bugs_.insert(7); - CommandLine command_line(0, NULL); + base::CommandLine command_line(0, NULL); manager->AppendGpuCommandLine(&command_line); EXPECT_TRUE(command_line.HasSwitch(switches::kGpuDriverBugWorkarounds)); diff --git a/content/browser/gpu/gpu_internals_ui.cc b/content/browser/gpu/gpu_internals_ui.cc index 88e4b05fbfb31..d2c3ca5698aad 100644 --- a/content/browser/gpu/gpu_internals_ui.cc +++ b/content/browser/gpu/gpu_internals_ui.cc @@ -328,7 +328,7 @@ base::Value* GpuMessageHandler::OnRequestClientInfo( dict->SetString("version", GetContentClient()->GetProduct()); dict->SetString("command_line", - CommandLine::ForCurrentProcess()->GetCommandLineString()); + base::CommandLine::ForCurrentProcess()->GetCommandLineString()); dict->SetString("operating_system", base::SysInfo::OperatingSystemName() + " " + base::SysInfo::OperatingSystemVersion()); diff --git a/content/browser/gpu/gpu_process_host.cc b/content/browser/gpu/gpu_process_host.cc index 27496f526e00d..dbbd7bb405905 100644 --- a/content/browser/gpu/gpu_process_host.cc +++ b/content/browser/gpu/gpu_process_host.cc @@ -40,15 +40,10 @@ #include "ipc/ipc_switches.h" #include "ipc/message_filter.h" #include "media/base/media_switches.h" +#include "ui/base/ui_base_switches.h" #include "ui/events/latency_info.h" #include "ui/gl/gl_switches.h" -#if defined(OS_MACOSX) -#include -#include "base/mac/scoped_cftyperef.h" -#include "content/common/gpu/surface_handle_types_mac.h" -#endif - #if defined(OS_WIN) #include "base/win/windows_version.h" #include "content/common/sandbox_win.h" @@ -104,7 +99,7 @@ void SendGpuProcessMessage(GpuProcessHost::GpuProcessKind kind, class GpuSandboxedProcessLauncherDelegate : public SandboxedProcessLauncherDelegate { public: - GpuSandboxedProcessLauncherDelegate(CommandLine* cmd_line, + GpuSandboxedProcessLauncherDelegate(base::CommandLine* cmd_line, ChildProcessHost* host) #if defined(OS_WIN) : cmd_line_(cmd_line) {} @@ -216,7 +211,7 @@ class GpuSandboxedProcessLauncherDelegate private: #if defined(OS_WIN) - CommandLine* cmd_line_; + base::CommandLine* cmd_line_; #elif defined(OS_POSIX) int ipc_fd_; #endif // OS_WIN @@ -228,8 +223,10 @@ class GpuSandboxedProcessLauncherDelegate bool GpuProcessHost::ValidateHost(GpuProcessHost* host) { // The Gpu process is invalid if it's not using SwiftShader, the card is // blacklisted, and we can kill it and start over. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) || - CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU) || + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess) || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kInProcessGPU) || (host->valid_ && (host->swiftshader_rendering_ || !GpuDataManagerImpl::GetInstance()->ShouldUseSwiftShader()))) { @@ -337,8 +334,10 @@ GpuProcessHost::GpuProcessHost(int host_id, GpuProcessKind kind) initialized_(false), gpu_crash_recorded_(false), uma_memory_stats_received_(false) { - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess) || - CommandLine::ForCurrentProcess()->HasSwitch(switches::kInProcessGPU)) { + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess) || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kInProcessGPU)) { in_process_ = true; } @@ -459,7 +458,7 @@ bool GpuProcessHost::Init() { if (in_process_) { DCHECK(g_gpu_main_thread_factory); - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitch(switches::kDisableGpuWatchdog); GpuDataManagerImpl* gpu_data_manager = GpuDataManagerImpl::GetInstance(); @@ -572,7 +571,7 @@ void GpuProcessHost::EstablishGpuChannel( callback.Run(IPC::ChannelHandle(), gpu::GPUInfo()); } - if (!CommandLine::ForCurrentProcess()->HasSwitch( + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableGpuShaderDiskCache)) { CreateChannelCache(client_id); } @@ -758,44 +757,9 @@ void GpuProcessHost::OnGpuMemoryUmaStatsReceived( } #if defined(OS_MACOSX) -namespace { -void HoldIOSurfaceReference(base::ScopedCFTypeRef io_surface) {} -} // namespace - void GpuProcessHost::OnAcceleratedSurfaceBuffersSwapped( const IPC::Message& message) { RenderWidgetResizeHelper::Get()->PostGpuProcessMsg(host_id_, message); - - if (!IsDelegatedRendererEnabled()) - return; - - GpuHostMsg_AcceleratedSurfaceBuffersSwapped::Param param; - if (!GpuHostMsg_AcceleratedSurfaceBuffersSwapped::Read(&message, ¶m)) - return; - const GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params& params = param.a; - - if (GetSurfaceHandleType(params.surface_handle) == - kSurfaceHandleTypeIOSurface) { - // As soon as the frame is acked, the IOSurface may be thrown away by the - // GPU process. Open the IOSurface and post a task referencing it to the UI - // thread. This will keep the IOSurface from being thrown away until the UI - // thread can open another reference to it, if needed. - base::ScopedCFTypeRef io_surface(IOSurfaceLookup( - IOSurfaceIDFromSurfaceHandle(params.surface_handle))); - BrowserThread::PostTask(BrowserThread::UI, - FROM_HERE, - base::Bind(HoldIOSurfaceReference, io_surface)); - } - - // If delegated rendering is enabled, then immediately acknowledge this frame - // on the IO thread instead of the UI thread. The UI thread will wait on the - // GPU process. If the UI thread were to be responsible for acking swaps, - // then there would be a cycle and a potential deadlock. Back-pressure from - // the GPU is provided through the compositor's output surface. - AcceleratedSurfaceMsg_BufferPresented_Params ack_params; - ack_params.sync_point = 0; - ack_params.renderer_id = 0; - Send(new AcceleratedSurfaceMsg_BufferPresented(params.route_id, ack_params)); } #endif @@ -842,9 +806,10 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) { return false; } - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); - CommandLine::StringType gpu_launcher = + base::CommandLine::StringType gpu_launcher = browser_command_line.GetSwitchValueNative(switches::kGpuLauncher); #if defined(OS_LINUX) @@ -858,7 +823,7 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) { if (exe_path.empty()) return false; - CommandLine* cmd_line = new CommandLine(exe_path); + base::CommandLine* cmd_line = new base::CommandLine(exe_path); cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kGpuProcess); cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); @@ -879,7 +844,7 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) { switches::kEnableLogging, switches::kEnableShareGroupAsyncTextureUpload, #if defined(OS_CHROMEOS) - switches::kEnableVaapiAcceleratedVideoEncode, + switches::kDisableVaapiAcceleratedVideoEncode, #endif switches::kGpuStartupDialog, switches::kGpuSandboxAllowSysVShm, @@ -895,6 +860,7 @@ bool GpuProcessHost::LaunchGpuProcess(const std::string& channel_id) { switches::kV, switches::kVModule, #if defined(OS_MACOSX) + switches::kEnableRemoteCoreAnimation, switches::kEnableSandboxLogging, #endif #if defined(USE_AURA) @@ -982,7 +948,7 @@ void GpuProcessHost::RecordProcessCrash() { // Last time the GPU process crashed. static base::Time last_gpu_crash_time; - bool disable_crash_limit = CommandLine::ForCurrentProcess()->HasSwitch( + bool disable_crash_limit = base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableGpuProcessCrashLimit); // Ending only acts as a failure if the GPU process was actually started and diff --git a/content/browser/gpu/gpu_process_host_ui_shim.cc b/content/browser/gpu/gpu_process_host_ui_shim.cc index 3e078639921a0..741368f0ff909 100644 --- a/content/browser/gpu/gpu_process_host_ui_shim.cc +++ b/content/browser/gpu/gpu_process_host_ui_shim.cc @@ -270,7 +270,8 @@ void GpuProcessHostUIShim::OnAcceleratedSurfaceBuffersSwapped( params.surface_id); BrowserCompositorViewMac::GotAcceleratedFrame( native_widget, params.surface_handle, params.surface_id, - params.latency_info, params.size, params.scale_factor); + params.latency_info, params.size, params.scale_factor, + host_id_, params.route_id); return; } #endif diff --git a/content/browser/histogram_message_filter.cc b/content/browser/histogram_message_filter.cc index 8a58fff55f61e..8eb7c7231baeb 100644 --- a/content/browser/histogram_message_filter.cc +++ b/content/browser/histogram_message_filter.cc @@ -45,7 +45,7 @@ void HistogramMessageFilter::OnGetBrowserHistogram( // Security: Only allow access to browser histograms when running in the // context of a test. bool using_stats_collection_controller = - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kStatsCollectionController); if (!using_stats_collection_controller) { LOG(ERROR) << "Attempt at reading browser histogram without specifying " diff --git a/content/browser/indexed_db/indexed_db_backing_store.cc b/content/browser/indexed_db/indexed_db_backing_store.cc index 95639f5af7d03..b60a55f703f95 100644 --- a/content/browser/indexed_db/indexed_db_backing_store.cc +++ b/content/browser/indexed_db/indexed_db_backing_store.cc @@ -1167,6 +1167,7 @@ std::vector IndexedDBBackingStore::GetDatabaseNames( for (*s = it->Seek(start_key); s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0; *s = it->Next()) { + // Decode database name (in iterator key). StringPiece slice(it->Key()); DatabaseNameKey database_name_key; if (!DatabaseNameKey::Decode(&slice, &database_name_key) || @@ -1174,7 +1175,31 @@ std::vector IndexedDBBackingStore::GetDatabaseNames( INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES); continue; } - found_names.push_back(database_name_key.database_name()); + + // Decode database id (in iterator value). + int64 database_id = 0; + StringPiece valueSlice(it->Value()); + if (!DecodeInt(&valueSlice, &database_id) || !valueSlice.empty()) { + INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES); + continue; + } + + // Look up version by id. + bool found = false; + int64 database_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION; + *s = GetVarInt(db_.get(), + DatabaseMetaDataKey::Encode( + database_id, DatabaseMetaDataKey::USER_INT_VERSION), + &database_version, + &found); + if (!s->ok() || !found) { + INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES); + continue; + } + + // Ignore stale metadata from failed initial opens. + if (database_version != IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) + found_names.push_back(database_name_key.database_name()); } if (!s->ok()) @@ -1289,6 +1314,7 @@ leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData( const base::string16& version, int64 int_version, int64* row_id) { + // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472 scoped_refptr transaction = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get()); diff --git a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc index 1fea813e6591b..c6628ff6f1f31 100644 --- a/content/browser/indexed_db/indexed_db_backing_store_unittest.cc +++ b/content/browser/indexed_db/indexed_db_backing_store_unittest.cc @@ -970,6 +970,35 @@ TEST_F(IndexedDBBackingStoreTest, CreateDatabase) { } } +TEST_F(IndexedDBBackingStoreTest, GetDatabaseNames) { + const base::string16 string_version(ASCIIToUTF16("string_version")); + + const base::string16 db1_name(ASCIIToUTF16("db1")); + const int64 db1_version = 1LL; + int64 db1_id; + + // Database records with DEFAULT_INT_VERSION represent stale data, + // and should not be enumerated. + const base::string16 db2_name(ASCIIToUTF16("db2")); + const int64 db2_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION; + int64 db2_id; + + leveldb::Status s = backing_store_->CreateIDBDatabaseMetaData( + db1_name, string_version, db1_version, &db1_id); + EXPECT_TRUE(s.ok()); + EXPECT_GT(db1_id, 0LL); + + s = backing_store_->CreateIDBDatabaseMetaData( + db2_name, string_version, db2_version, &db2_id); + EXPECT_TRUE(s.ok()); + EXPECT_GT(db2_id, db1_id); + + std::vector names = backing_store_->GetDatabaseNames(&s); + EXPECT_TRUE(s.ok()); + EXPECT_EQ(names.size(), 1ULL); + EXPECT_EQ(names[0], db1_name); +} + } // namespace } // namespace content diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc index 6ff55f67dcd63..4ca77fb67c807 100644 --- a/content/browser/indexed_db/indexed_db_browsertest.cc +++ b/content/browser/indexed_db/indexed_db_browsertest.cc @@ -597,7 +597,7 @@ IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest, const GURL& origin_url = embedded_test_server()->base_url(); embedded_test_server()->RegisterRequestHandler( base::Bind(&CorruptDBRequestHandler, - base::ConstRef(GetContext()), + base::Unretained(GetContext()), origin_url, s_corrupt_db_test_prefix, this)); diff --git a/content/browser/indexed_db/indexed_db_callbacks.cc b/content/browser/indexed_db/indexed_db_callbacks.cc index cee89b1fb75b9..643889d65e71f 100644 --- a/content/browser/indexed_db/indexed_db_callbacks.cc +++ b/content/browser/indexed_db/indexed_db_callbacks.cc @@ -218,6 +218,7 @@ static std::string CreateBlobData( uuid = base::GenerateGUID(); scoped_refptr blob_data = new webkit_blob::BlobData(uuid); + blob_data->set_content_type(base::UTF16ToUTF8(blob_info.type())); blob_data->AppendFile( blob_info.file_path(), 0, blob_info.size(), blob_info.last_modified()); scoped_ptr blob_data_handle( diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/content/browser/indexed_db/indexed_db_leveldb_coding.cc index a493d52fc1f07..dcb1081b315ea 100644 --- a/content/browser/indexed_db/indexed_db_leveldb_coding.cc +++ b/content/browser/indexed_db/indexed_db_leveldb_coding.cc @@ -54,7 +54,7 @@ // <0, 0, 0, 100, database id> // => Existence implies the database id is in the free list // [DatabaseFreeListKey] -// <0, 0, 0, 201, origin, database name> => Database id [DatabaseNameKey] +// <0, 0, 0, 201, origin, database name> => Database id (int) [DatabaseNameKey] // // // Database metadata: [DatabaseMetaDataKey] diff --git a/content/browser/indexed_db/leveldb/leveldb_database.cc b/content/browser/indexed_db/leveldb/leveldb_database.cc index 9e70f463856ad..218a71b019016 100644 --- a/content/browser/indexed_db/leveldb/leveldb_database.cc +++ b/content/browser/indexed_db/leveldb/leveldb_database.cc @@ -264,6 +264,8 @@ leveldb::Status LevelDBDatabase::Open(const base::FilePath& file_name, const LevelDBComparator* comparator, scoped_ptr* result, bool* is_disk_full) { + base::TimeTicks begin_time = base::TimeTicks::Now(); + scoped_ptr comparator_adapter( new ComparatorAdapter(comparator)); @@ -284,6 +286,9 @@ leveldb::Status LevelDBDatabase::Open(const base::FilePath& file_name, return s; } + UMA_HISTOGRAM_MEDIUM_TIMES("WebCore.IndexedDB.LevelDB.OpenTime", + base::TimeTicks::Now() - begin_time); + CheckFreeSpace("Success", file_name); (*result).reset(new LevelDBDatabase); @@ -320,6 +325,8 @@ scoped_ptr LevelDBDatabase::OpenInMemory( leveldb::Status LevelDBDatabase::Put(const StringPiece& key, std::string* value) { + base::TimeTicks begin_time = base::TimeTicks::Now(); + leveldb::WriteOptions write_options; write_options.sync = kSyncWrites; @@ -327,6 +334,9 @@ leveldb::Status LevelDBDatabase::Put(const StringPiece& key, db_->Put(write_options, MakeSlice(key), MakeSlice(*value)); if (!s.ok()) LOG(ERROR) << "LevelDB put failed: " << s.ToString(); + else + UMA_HISTOGRAM_TIMES("WebCore.IndexedDB.LevelDB.PutTime", + base::TimeTicks::Now() - begin_time); return s; } @@ -363,6 +373,7 @@ leveldb::Status LevelDBDatabase::Get(const StringPiece& key, } leveldb::Status LevelDBDatabase::Write(const LevelDBWriteBatch& write_batch) { + base::TimeTicks begin_time = base::TimeTicks::Now(); leveldb::WriteOptions write_options; write_options.sync = kSyncWrites; @@ -371,6 +382,9 @@ leveldb::Status LevelDBDatabase::Write(const LevelDBWriteBatch& write_batch) { if (!s.ok()) { HistogramLevelDBError("WebCore.IndexedDB.LevelDBWriteErrors", s); LOG(ERROR) << "LevelDB write failed: " << s.ToString(); + } else { + UMA_HISTOGRAM_TIMES("WebCore.IndexedDB.LevelDB.WriteTime", + base::TimeTicks::Now() - begin_time); } return s; } diff --git a/content/browser/indexed_db/leveldb/leveldb_transaction.cc b/content/browser/indexed_db/leveldb/leveldb_transaction.cc index e20d404c3c5d0..40e7a502fb418 100644 --- a/content/browser/indexed_db/leveldb/leveldb_transaction.cc +++ b/content/browser/indexed_db/leveldb/leveldb_transaction.cc @@ -5,6 +5,8 @@ #include "content/browser/indexed_db/leveldb/leveldb_transaction.h" #include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/time/time.h" #include "content/browser/indexed_db/leveldb/leveldb_database.h" #include "content/browser/indexed_db/leveldb/leveldb_write_batch.h" #include "third_party/leveldatabase/src/include/leveldb/db.h" @@ -93,6 +95,7 @@ leveldb::Status LevelDBTransaction::Commit() { return leveldb::Status::OK(); } + base::TimeTicks begin_time = base::TimeTicks::Now(); scoped_ptr write_batch = LevelDBWriteBatch::Create(); for (DataType::iterator iterator = data_.begin(); iterator != data_.end(); @@ -107,6 +110,8 @@ leveldb::Status LevelDBTransaction::Commit() { if (s.ok()) { Clear(); finished_ = true; + UMA_HISTOGRAM_TIMES("WebCore.IndexedDB.LevelDB.Transaction.CommitTime", + base::TimeTicks::Now() - begin_time); } return s; } diff --git a/content/browser/loader/async_resource_handler.cc b/content/browser/loader/async_resource_handler.cc index 825fc2f4a5678..87d82ca1424f6 100644 --- a/content/browser/loader/async_resource_handler.cc +++ b/content/browser/loader/async_resource_handler.cc @@ -29,6 +29,7 @@ #include "net/base/load_flags.h" #include "net/base/net_log.h" #include "net/base/net_util.h" +#include "net/url_request/redirect_info.h" using base::TimeTicks; @@ -41,7 +42,7 @@ static int kMaxAllocationSize = 1024 * 32; void GetNumericArg(const std::string& name, int* result) { const std::string& value = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name); + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(name); if (!value.empty()) base::StringToInt(value, result); } @@ -134,9 +135,10 @@ bool AsyncResourceHandler::OnUploadProgress(uint64 position, new ResourceMsg_UploadProgress(GetRequestID(), position, size)); } -bool AsyncResourceHandler::OnRequestRedirected(const GURL& new_url, - ResourceResponse* response, - bool* defer) { +bool AsyncResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + bool* defer) { const ResourceRequestInfoImpl* info = GetRequestInfo(); if (!info->filter()) return false; @@ -146,7 +148,7 @@ bool AsyncResourceHandler::OnRequestRedirected(const GURL& new_url, if (rdh_->delegate()) { rdh_->delegate()->OnRequestRedirected( - new_url, request(), info->GetContext(), response); + redirect_info.new_url, request(), info->GetContext(), response); } DevToolsNetLogObserver::PopulateResponseInfo(request(), response); @@ -159,8 +161,7 @@ bool AsyncResourceHandler::OnRequestRedirected(const GURL& new_url, // and hopefully those will eventually all be owned by the browser. It's // possible this is still needed while renderer-owned ones exist. return info->filter()->Send(new ResourceMsg_ReceivedRedirect( - GetRequestID(), new_url, request()->first_party_for_cookies(), - response->head)); + GetRequestID(), redirect_info, response->head)); } bool AsyncResourceHandler::OnResponseStarted(ResourceResponse* response, diff --git a/content/browser/loader/async_resource_handler.h b/content/browser/loader/async_resource_handler.h index b0821191e0ddf..6842b192ccadf 100644 --- a/content/browser/loader/async_resource_handler.h +++ b/content/browser/loader/async_resource_handler.h @@ -36,7 +36,7 @@ class AsyncResourceHandler : public ResourceHandler, // ResourceHandler implementation: virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnRequestRedirected(const GURL& new_url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; virtual bool OnResponseStarted(ResourceResponse* response, diff --git a/content/browser/loader/certificate_resource_handler.cc b/content/browser/loader/certificate_resource_handler.cc index 9b3a607eb46c0..eb1976bef0566 100644 --- a/content/browser/loader/certificate_resource_handler.cc +++ b/content/browser/loader/certificate_resource_handler.cc @@ -12,6 +12,7 @@ #include "net/base/mime_sniffer.h" #include "net/base/mime_util.h" #include "net/http/http_response_headers.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_status.h" @@ -34,10 +35,10 @@ bool CertificateResourceHandler::OnUploadProgress(uint64 position, return true; } -bool CertificateResourceHandler::OnRequestRedirected(const GURL& url, - ResourceResponse* resp, - bool* defer) { - url_ = url; +bool CertificateResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* resp, + bool* defer) { return true; } diff --git a/content/browser/loader/certificate_resource_handler.h b/content/browser/loader/certificate_resource_handler.h index 9c67860eb7eff..b91e495699fd1 100644 --- a/content/browser/loader/certificate_resource_handler.h +++ b/content/browser/loader/certificate_resource_handler.h @@ -14,7 +14,6 @@ #include "base/memory/scoped_ptr.h" #include "content/browser/loader/resource_handler.h" #include "net/base/mime_util.h" -#include "url/gurl.h" namespace net { class IOBuffer; @@ -37,7 +36,7 @@ class CertificateResourceHandler : public ResourceHandler { virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; // Not needed, as this event handler ought to be the final resource. - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* resp, bool* defer) OVERRIDE; @@ -73,7 +72,6 @@ class CertificateResourceHandler : public ResourceHandler { void AssembleResource(); - GURL url_; size_t content_length_; ContentVector buffer_; scoped_refptr read_buffer_; diff --git a/content/browser/loader/cross_site_resource_handler.cc b/content/browser/loader/cross_site_resource_handler.cc index f8a5e371e1036..6efe97d297bf7 100644 --- a/content/browser/loader/cross_site_resource_handler.cc +++ b/content/browser/loader/cross_site_resource_handler.cc @@ -88,12 +88,11 @@ void OnCrossSiteResponseHelper(const CrossSiteResponseParams& params) { void OnDeferredAfterResponseStartedHelper( const GlobalRequestID& global_request_id, int render_frame_id, - const scoped_refptr& headers, - const GURL& url) { + const TransitionLayerData& transition_data) { RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID(global_request_id.child_id, render_frame_id); if (rfh) - rfh->OnDeferredAfterResponseStarted(global_request_id, headers, url); + rfh->OnDeferredAfterResponseStarted(global_request_id, transition_data); } bool CheckNavigationPolicyOnUI(GURL url, int process_id, int render_frame_id) { @@ -129,17 +128,12 @@ CrossSiteResourceHandler::~CrossSiteResourceHandler() { } bool CrossSiteResourceHandler::OnRequestRedirected( - const GURL& new_url, + const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { - // Top-level requests change their cookie first-party URL on redirects, while - // subframes retain the parent's value. - if (GetRequestInfo()->GetResourceType() == RESOURCE_TYPE_MAIN_FRAME) - request()->set_first_party_for_cookies(new_url); - // We should not have started the transition before being redirected. DCHECK(!in_cross_site_transition_); - return next_handler_->OnRequestRedirected(new_url, response, defer); + return next_handler_->OnRequestRedirected(redirect_info, response, defer); } bool CrossSiteResourceHandler::OnResponseStarted( @@ -153,14 +147,22 @@ bool CrossSiteResourceHandler::OnResponseStarted( ResourceRequestInfoImpl* info = GetRequestInfo(); info->set_cross_site_handler(this); + TransitionLayerData transition_data; bool is_navigation_transition = TransitionRequestManager::GetInstance()->HasPendingTransitionRequest( - info->GetChildID(), info->GetRenderFrameID()); + info->GetChildID(), info->GetRenderFrameID(), request()->url(), + &transition_data); - if (is_navigation_transition) - return OnNavigationTransitionResponseStarted(response, defer); - else + if (is_navigation_transition) { + if (response_) + transition_data.response_headers = response_->head.headers; + transition_data.request_url = request()->url(); + + return OnNavigationTransitionResponseStarted(response, defer, + transition_data); + } else { return OnNormalResponseStarted(response, defer); + } } bool CrossSiteResourceHandler::OnNormalResponseStarted( @@ -187,15 +189,17 @@ bool CrossSiteResourceHandler::OnNormalResponseStarted( // or for WebUI processes for now, since pages like the NTP host multiple // cross-site WebUI iframes. if (!should_transfer && - CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess) && + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess) && !ChildProcessSecurityPolicyImpl::GetInstance()->HasWebUIBindings( info->GetChildID())) { return DeferForNavigationPolicyCheck(info, response, defer); } - bool swap_needed = should_transfer || - CrossSiteRequestManager::GetInstance()-> - HasPendingCrossSiteRequest(info->GetChildID(), info->GetRouteID()); + bool swap_needed = + should_transfer || + CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest( + info->GetChildID(), info->GetRenderFrameID()); // If this is a download, just pass the response through without doing a // cross-site check. The renderer will see it is a download and abort the @@ -231,14 +235,10 @@ bool CrossSiteResourceHandler::OnNormalResponseStarted( bool CrossSiteResourceHandler::OnNavigationTransitionResponseStarted( ResourceResponse* response, - bool* defer) { + bool* defer, + const TransitionLayerData& transition_data) { ResourceRequestInfoImpl* info = GetRequestInfo(); - scoped_refptr headers; - if (response_) - headers = response_->head.headers; - GURL url = request()->url(); - GlobalRequestID global_id(info->GetChildID(), info->GetRequestID()); int render_frame_id = info->GetRenderFrameID(); BrowserThread::PostTask( @@ -248,8 +248,7 @@ bool CrossSiteResourceHandler::OnNavigationTransitionResponseStarted( &OnDeferredAfterResponseStartedHelper, global_id, render_frame_id, - headers, - url)); + transition_data)); *defer = true; OnDidDefer(); @@ -290,7 +289,7 @@ void CrossSiteResourceHandler::OnResponseCompleted( if (has_started_response_ || status.status() != net::URLRequestStatus::FAILED || !CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest( - info->GetChildID(), info->GetRouteID())) { + info->GetChildID(), info->GetRenderFrameID())) { next_handler_->OnResponseCompleted(status, security_info, defer); return; } diff --git a/content/browser/loader/cross_site_resource_handler.h b/content/browser/loader/cross_site_resource_handler.h index f51f6163be253..ef29d90fa2e4b 100644 --- a/content/browser/loader/cross_site_resource_handler.h +++ b/content/browser/loader/cross_site_resource_handler.h @@ -17,6 +17,8 @@ class URLRequest; namespace content { +struct TransitionLayerData; + // Ensures that cross-site responses are delayed until the onunload handler of // the previous page is allowed to run. This handler wraps an // AsyncEventHandler, and it sits inside SafeBrowsing and Buffered event @@ -29,7 +31,7 @@ class CrossSiteResourceHandler : public LayeredResourceHandler { virtual ~CrossSiteResourceHandler(); // ResourceHandler implementation: - virtual bool OnRequestRedirected(const GURL& new_url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; virtual bool OnResponseStarted(ResourceResponse* response, @@ -69,8 +71,10 @@ class CrossSiteResourceHandler : public LayeredResourceHandler { ResourceResponse* response, bool* defer); - bool OnNavigationTransitionResponseStarted(ResourceResponse* response, - bool* defer); + bool OnNavigationTransitionResponseStarted( + ResourceResponse* response, + bool* defer, + const TransitionLayerData& transition_data); bool OnNormalResponseStarted(ResourceResponse* response, bool* defer); diff --git a/content/browser/loader/detachable_resource_handler.cc b/content/browser/loader/detachable_resource_handler.cc index b8ef55d4fff50..1fe9f0624d065 100644 --- a/content/browser/loader/detachable_resource_handler.cc +++ b/content/browser/loader/detachable_resource_handler.cc @@ -89,15 +89,17 @@ bool DetachableResourceHandler::OnUploadProgress(uint64 position, uint64 size) { return next_handler_->OnUploadProgress(position, size); } -bool DetachableResourceHandler::OnRequestRedirected(const GURL& url, - ResourceResponse* response, - bool* defer) { +bool DetachableResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + bool* defer) { DCHECK(!is_deferred_); if (!next_handler_) return true; - bool ret = next_handler_->OnRequestRedirected(url, response, &is_deferred_); + bool ret = next_handler_->OnRequestRedirected( + redirect_info, response, &is_deferred_); *defer = is_deferred_; return ret; } diff --git a/content/browser/loader/detachable_resource_handler.h b/content/browser/loader/detachable_resource_handler.h index 9a685701a9d71..60181add811c4 100644 --- a/content/browser/loader/detachable_resource_handler.h +++ b/content/browser/loader/detachable_resource_handler.h @@ -49,7 +49,7 @@ class DetachableResourceHandler : public ResourceHandler, // ResourceHandler implementation: virtual void SetController(ResourceController* controller) OVERRIDE; virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; virtual bool OnResponseStarted(ResourceResponse* response, diff --git a/content/browser/loader/layered_resource_handler.cc b/content/browser/loader/layered_resource_handler.cc index 1c65820f610cd..fa15b0de40f75 100644 --- a/content/browser/loader/layered_resource_handler.cc +++ b/content/browser/loader/layered_resource_handler.cc @@ -35,11 +35,12 @@ bool LayeredResourceHandler::OnUploadProgress(uint64 position, return next_handler_->OnUploadProgress(position, size); } -bool LayeredResourceHandler::OnRequestRedirected(const GURL& url, - ResourceResponse* response, - bool* defer) { +bool LayeredResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + bool* defer) { DCHECK(next_handler_.get()); - return next_handler_->OnRequestRedirected(url, response, defer); + return next_handler_->OnRequestRedirected(redirect_info, response, defer); } bool LayeredResourceHandler::OnResponseStarted(ResourceResponse* response, diff --git a/content/browser/loader/layered_resource_handler.h b/content/browser/loader/layered_resource_handler.h index dc6de2314dc98..3a092c3d748c5 100644 --- a/content/browser/loader/layered_resource_handler.h +++ b/content/browser/loader/layered_resource_handler.h @@ -26,7 +26,7 @@ class CONTENT_EXPORT LayeredResourceHandler : public ResourceHandler { // ResourceHandler implementation: virtual void SetController(ResourceController* controller) OVERRIDE; virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; virtual bool OnResponseStarted(ResourceResponse* response, diff --git a/content/browser/loader/resource_dispatcher_host_impl.cc b/content/browser/loader/resource_dispatcher_host_impl.cc index 2123008fcd914..f63b7cf8679ba 100644 --- a/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/content/browser/loader/resource_dispatcher_host_impl.cc @@ -27,7 +27,6 @@ #include "content/browser/appcache/chrome_appcache_service.h" #include "content/browser/cert_store_impl.h" #include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/cross_site_request_manager.h" #include "content/browser/download/download_resource_handler.h" #include "content/browser/download/save_file_manager.h" #include "content/browser/download/save_file_resource_handler.h" @@ -171,7 +170,8 @@ void AbortRequestBeforeItStarts(ResourceMessageFilter* filter, void SetReferrerForRequest(net::URLRequest* request, const Referrer& referrer) { if (!referrer.url.is_valid() || - CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoReferrers)) { + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kNoReferrers)) { request->SetReferrer(std::string()); } else { request->SetReferrer(referrer.url.spec()); @@ -517,6 +517,15 @@ DownloadInterruptReason ResourceDispatcherHostImpl::BeginDownload( } request->SetLoadFlags(request->load_flags() | extra_load_flags); + // We treat a download as a main frame load, and thus update the policy URL on + // redirects. + // + // TODO(davidben): Is this correct? If this came from a + // ViewHostMsg_DownloadUrl in a frame, should it have first-party URL set + // appropriately? + request->set_first_party_url_policy( + net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); + // Check if the renderer is permitted to request the requested URL. if (!ChildProcessSecurityPolicyImpl::GetInstance()-> CanRequestURL(child_id, url)) { @@ -1034,6 +1043,13 @@ void ResourceDispatcherHostImpl::BeginRequest( new_request->set_first_party_for_cookies( request_data.first_party_for_cookies); + // If the request is a MAIN_FRAME request, the first-party URL gets updated on + // redirects. + if (request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME) { + new_request->set_first_party_url_policy( + net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); + } + const Referrer referrer(request_data.referrer, request_data.referrer_policy); SetReferrerForRequest(new_request.get(), referrer); @@ -1075,6 +1091,7 @@ void ResourceDispatcherHostImpl::BeginRequest( false, // is stream allow_download, request_data.has_user_gesture, + request_data.enable_load_timing, request_data.referrer_policy, request_data.visiblity_state, resource_context, @@ -1160,7 +1177,8 @@ scoped_ptr ResourceDispatcherHostImpl::CreateResourceHandler( request_data.resource_type == RESOURCE_TYPE_MAIN_FRAME; // If we are using --site-per-process, install it for subframes as well. if (!is_swappable_navigation && - CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) { + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) { is_swappable_navigation = request_data.resource_type == RESOURCE_TYPE_SUB_FRAME; } @@ -1295,6 +1313,7 @@ ResourceRequestInfoImpl* ResourceDispatcherHostImpl::CreateRequestInfo( false, // is_stream download, // allow_download false, // has_user_gesture + false, // enable_load_timing blink::WebReferrerPolicyDefault, blink::WebPageVisibilityStateVisible, context, diff --git a/content/browser/loader/resource_handler.h b/content/browser/loader/resource_handler.h index a9d8a38aaa9d1..306a672616cfb 100644 --- a/content/browser/loader/resource_handler.h +++ b/content/browser/loader/resource_handler.h @@ -25,6 +25,7 @@ namespace net { class IOBuffer; class URLRequest; class URLRequestStatus; +struct RedirectInfo; } // namespace net namespace content { @@ -51,7 +52,7 @@ class CONTENT_EXPORT ResourceHandler // false. Set |*defer| to true to defer the redirect. The redirect may be // followed later on via ResourceDispatcherHost::FollowDeferredRedirect. If // the handler returns false, then the request is cancelled. - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) = 0; diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc index d82cf07302dd6..a0713ebaab780 100644 --- a/content/browser/loader/resource_loader.cc +++ b/content/browser/loader/resource_loader.cc @@ -30,6 +30,7 @@ #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" #include "net/ssl/client_cert_store.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request_status.h" using base::TimeDelta; @@ -38,7 +39,8 @@ using base::TimeTicks; namespace content { namespace { -void PopulateResourceResponse(net::URLRequest* request, +void PopulateResourceResponse(ResourceRequestInfoImpl* info, + net::URLRequest* request, ResourceResponse* response) { response->head.error_code = request->status().error(); response->head.request_time = request->request_time(); @@ -65,8 +67,7 @@ void PopulateResourceResponse(net::URLRequest* request, request, &response->head.appcache_id, &response->head.appcache_manifest_url); - // TODO(mmenke): Figure out if LOAD_ENABLE_LOAD_TIMING is safe to remove. - if (request->load_flags() & net::LOAD_ENABLE_LOAD_TIMING) + if (info->is_load_timing_enabled()) request->GetLoadTimingInfo(&response->head.load_timing); } @@ -199,7 +200,7 @@ void ResourceLoader::OnUploadProgressACK() { } void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer) { DCHECK_EQ(request_.get(), unused); @@ -210,27 +211,27 @@ void ResourceLoader::OnReceivedRedirect(net::URLRequest* unused, if (info->GetProcessType() != PROCESS_TYPE_PLUGIN && !ChildProcessSecurityPolicyImpl::GetInstance()-> - CanRequestURL(info->GetChildID(), new_url)) { + CanRequestURL(info->GetChildID(), redirect_info.new_url)) { VLOG(1) << "Denied unauthorized request for " - << new_url.possibly_invalid_spec(); + << redirect_info.new_url.possibly_invalid_spec(); // Tell the renderer that this request was disallowed. Cancel(); return; } - delegate_->DidReceiveRedirect(this, new_url); + delegate_->DidReceiveRedirect(this, redirect_info.new_url); - if (delegate_->HandleExternalProtocol(this, new_url)) { + if (delegate_->HandleExternalProtocol(this, redirect_info.new_url)) { // The request is complete so we can remove it. CancelAndIgnore(); return; } scoped_refptr response(new ResourceResponse()); - PopulateResourceResponse(request_.get(), response.get()); + PopulateResourceResponse(info, request_.get(), response.get()); - if (!handler_->OnRequestRedirected(new_url, response.get(), defer)) { + if (!handler_->OnRequestRedirected(redirect_info, response.get(), defer)) { Cancel(); } else if (*defer) { deferred_stage_ = DEFERRED_REDIRECT; // Follow redirect when resumed. @@ -527,7 +528,7 @@ void ResourceLoader::CompleteResponseStarted() { ResourceRequestInfoImpl* info = GetRequestInfo(); scoped_refptr response(new ResourceResponse()); - PopulateResourceResponse(request_.get(), response.get()); + PopulateResourceResponse(info, request_.get(), response.get()); if (request_->ssl_info().cert.get()) { int cert_id = CertStore::GetInstance()->StoreCert( diff --git a/content/browser/loader/resource_loader.h b/content/browser/loader/resource_loader.h index d06f58ca6179d..21f800e496614 100644 --- a/content/browser/loader/resource_loader.h +++ b/content/browser/loader/resource_loader.h @@ -57,7 +57,7 @@ class CONTENT_EXPORT ResourceLoader : public net::URLRequest::Delegate, // net::URLRequest::Delegate implementation: virtual void OnReceivedRedirect(net::URLRequest* request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer) OVERRIDE; virtual void OnAuthRequired(net::URLRequest* request, net::AuthChallengeInfo* info) OVERRIDE; diff --git a/content/browser/loader/resource_loader_unittest.cc b/content/browser/loader/resource_loader_unittest.cc index 047e404fc1977..1e489e8570c36 100644 --- a/content/browser/loader/resource_loader_unittest.cc +++ b/content/browser/loader/resource_loader_unittest.cc @@ -132,7 +132,7 @@ class ResourceHandlerStub : public ResourceHandler { return true; } - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE { NOTREACHED(); diff --git a/content/browser/loader/resource_request_info_impl.cc b/content/browser/loader/resource_request_info_impl.cc index 0e5778aa170b4..16a64bb4194d5 100644 --- a/content/browser/loader/resource_request_info_impl.cc +++ b/content/browser/loader/resource_request_info_impl.cc @@ -48,6 +48,7 @@ void ResourceRequestInfo::AllocateForTesting(net::URLRequest* request, false, // is_stream true, // allow_download false, // has_user_gesture + false, // enable load timing blink::WebReferrerPolicyDefault, // referrer_policy blink::WebPageVisibilityStateVisible, // visibility_state context, // context @@ -102,6 +103,7 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( bool is_stream, bool allow_download, bool has_user_gesture, + bool enable_load_timing, blink::WebReferrerPolicy referrer_policy, blink::WebPageVisibilityState visibility_state, ResourceContext* context, @@ -123,6 +125,7 @@ ResourceRequestInfoImpl::ResourceRequestInfoImpl( is_stream_(is_stream), allow_download_(allow_download), has_user_gesture_(has_user_gesture), + enable_load_timing_(enable_load_timing), was_ignored_by_handler_(false), resource_type_(resource_type), transition_type_(transition_type), diff --git a/content/browser/loader/resource_request_info_impl.h b/content/browser/loader/resource_request_info_impl.h index 70465f7b10b43..54dd85dbaa814 100644 --- a/content/browser/loader/resource_request_info_impl.h +++ b/content/browser/loader/resource_request_info_impl.h @@ -56,6 +56,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, bool is_stream, bool allow_download, bool has_user_gesture, + bool enable_load_timing, blink::WebReferrerPolicy referrer_policy, blink::WebPageVisibilityState visibility_state, ResourceContext* context, @@ -152,6 +153,8 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, int memory_cost() const { return memory_cost_; } void set_memory_cost(int cost) { memory_cost_ = cost; } + bool is_load_timing_enabled() const { return enable_load_timing_; } + private: FRIEND_TEST_ALL_PREFIXES(ResourceDispatcherHostTest, DeletedFilterDetached); @@ -175,6 +178,7 @@ class ResourceRequestInfoImpl : public ResourceRequestInfo, bool is_stream_; bool allow_download_; bool has_user_gesture_; + bool enable_load_timing_; bool was_ignored_by_handler_; ResourceType resource_type_; PageTransition transition_type_; diff --git a/content/browser/loader/resource_scheduler_unittest.cc b/content/browser/loader/resource_scheduler_unittest.cc index ea4fc023fa0fb..3de46c5ceca92 100644 --- a/content/browser/loader/resource_scheduler_unittest.cc +++ b/content/browser/loader/resource_scheduler_unittest.cc @@ -182,6 +182,7 @@ class ResourceSchedulerTest : public testing::Test { false, // is_stream true, // allow_download false, // has_user_gesture + false, // enable_load_timing blink::WebReferrerPolicyDefault, // referrer_policy blink::WebPageVisibilityStateVisible, // visibility_state NULL, // context diff --git a/content/browser/loader/stream_resource_handler.cc b/content/browser/loader/stream_resource_handler.cc index 46713fc67fad6..0a2d6e83afbe2 100644 --- a/content/browser/loader/stream_resource_handler.cc +++ b/content/browser/loader/stream_resource_handler.cc @@ -36,9 +36,10 @@ bool StreamResourceHandler::OnUploadProgress(uint64 position, return true; } -bool StreamResourceHandler::OnRequestRedirected(const GURL& url, - ResourceResponse* resp, - bool* defer) { +bool StreamResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* resp, + bool* defer) { return true; } diff --git a/content/browser/loader/stream_resource_handler.h b/content/browser/loader/stream_resource_handler.h index 3b8bc217890ea..75a987daf5f7e 100644 --- a/content/browser/loader/stream_resource_handler.h +++ b/content/browser/loader/stream_resource_handler.h @@ -34,7 +34,7 @@ class StreamResourceHandler : public StreamWriteObserver, virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; // Not needed, as this event handler ought to be the final resource. - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* resp, bool* defer) OVERRIDE; diff --git a/content/browser/loader/sync_resource_handler.cc b/content/browser/loader/sync_resource_handler.cc index 090b9391056d4..1da8887daa9a4 100644 --- a/content/browser/loader/sync_resource_handler.cc +++ b/content/browser/loader/sync_resource_handler.cc @@ -14,6 +14,7 @@ #include "content/public/browser/resource_request_info.h" #include "net/base/io_buffer.h" #include "net/http/http_response_headers.h" +#include "net/url_request/redirect_info.h" namespace content { @@ -45,23 +46,24 @@ bool SyncResourceHandler::OnUploadProgress(uint64 position, uint64 size) { } bool SyncResourceHandler::OnRequestRedirected( - const GURL& new_url, + const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) { if (rdh_->delegate()) { rdh_->delegate()->OnRequestRedirected( - new_url, request(), GetRequestInfo()->GetContext(), response); + redirect_info.new_url, request(), GetRequestInfo()->GetContext(), + response); } DevToolsNetLogObserver::PopulateResponseInfo(request(), response); // TODO(darin): It would be much better if this could live in WebCore, but // doing so requires API changes at all levels. Similar code exists in // WebCore/platform/network/cf/ResourceHandleCFNet.cpp :-( - if (new_url.GetOrigin() != result_.final_url.GetOrigin()) { + if (redirect_info.new_url.GetOrigin() != result_.final_url.GetOrigin()) { LOG(ERROR) << "Cross origin redirect denied"; return false; } - result_.final_url = new_url; + result_.final_url = redirect_info.new_url; total_transfer_size_ += request()->GetTotalReceivedBytes(); return true; diff --git a/content/browser/loader/sync_resource_handler.h b/content/browser/loader/sync_resource_handler.h index 693b48f3b378c..bc26e4bd25cf2 100644 --- a/content/browser/loader/sync_resource_handler.h +++ b/content/browser/loader/sync_resource_handler.h @@ -34,7 +34,7 @@ class SyncResourceHandler : public ResourceHandler { virtual ~SyncResourceHandler(); virtual bool OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnRequestRedirected(const GURL& new_url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; virtual bool OnResponseStarted(ResourceResponse* response, diff --git a/content/browser/loader/throttling_resource_handler.cc b/content/browser/loader/throttling_resource_handler.cc index 9b1df5421e5d5..1f17d336b6d62 100644 --- a/content/browser/loader/throttling_resource_handler.cc +++ b/content/browser/loader/throttling_resource_handler.cc @@ -31,22 +31,23 @@ ThrottlingResourceHandler::ThrottlingResourceHandler( ThrottlingResourceHandler::~ThrottlingResourceHandler() { } -bool ThrottlingResourceHandler::OnRequestRedirected(const GURL& new_url, - ResourceResponse* response, - bool* defer) { +bool ThrottlingResourceHandler::OnRequestRedirected( + const net::RedirectInfo& redirect_info, + ResourceResponse* response, + bool* defer) { DCHECK(!cancelled_by_resource_throttle_); *defer = false; while (next_index_ < throttles_.size()) { int index = next_index_; - throttles_[index]->WillRedirectRequest(new_url, defer); + throttles_[index]->WillRedirectRequest(redirect_info.new_url, defer); next_index_++; if (cancelled_by_resource_throttle_) return false; if (*defer) { OnRequestDefered(index); deferred_stage_ = DEFERRED_REDIRECT; - deferred_url_ = new_url; + deferred_redirect_ = redirect_info; deferred_response_ = response; return true; // Do not cancel. } @@ -54,7 +55,7 @@ bool ThrottlingResourceHandler::OnRequestRedirected(const GURL& new_url, next_index_ = 0; // Reset for next time. - return next_handler_->OnRequestRedirected(new_url, response, defer); + return next_handler_->OnRequestRedirected(redirect_info, response, defer); } bool ThrottlingResourceHandler::OnWillStart(const GURL& url, bool* defer) { @@ -199,13 +200,13 @@ void ThrottlingResourceHandler::ResumeNetworkStart() { void ThrottlingResourceHandler::ResumeRedirect() { DCHECK(!cancelled_by_resource_throttle_); - GURL new_url = deferred_url_; - deferred_url_ = GURL(); + net::RedirectInfo redirect_info = deferred_redirect_; + deferred_redirect_ = net::RedirectInfo(); scoped_refptr response; deferred_response_.swap(response); bool defer = false; - if (!OnRequestRedirected(new_url, response.get(), &defer)) { + if (!OnRequestRedirected(redirect_info, response.get(), &defer)) { controller()->Cancel(); } else if (!defer) { controller()->Resume(); diff --git a/content/browser/loader/throttling_resource_handler.h b/content/browser/loader/throttling_resource_handler.h index fd284dbba2c22..4e1b81411dc18 100644 --- a/content/browser/loader/throttling_resource_handler.h +++ b/content/browser/loader/throttling_resource_handler.h @@ -9,6 +9,7 @@ #include "base/memory/scoped_vector.h" #include "content/browser/loader/layered_resource_handler.h" #include "content/public/browser/resource_controller.h" +#include "net/url_request/redirect_info.h" #include "url/gurl.h" namespace net { @@ -31,7 +32,7 @@ class ThrottlingResourceHandler : public LayeredResourceHandler, virtual ~ThrottlingResourceHandler(); // LayeredResourceHandler overrides: - virtual bool OnRequestRedirected(const GURL& url, + virtual bool OnRequestRedirected(const net::RedirectInfo& redirect_info, ResourceResponse* response, bool* defer) OVERRIDE; virtual bool OnResponseStarted(ResourceResponse* response, @@ -68,6 +69,7 @@ class ThrottlingResourceHandler : public LayeredResourceHandler, size_t next_index_; GURL deferred_url_; + net::RedirectInfo deferred_redirect_; scoped_refptr deferred_response_; bool cancelled_by_resource_throttle_; diff --git a/content/browser/mach_broker_mac.mm b/content/browser/mach_broker_mac.mm index e4ee38e09b2f4..d526d6ea8750e 100644 --- a/content/browser/mach_broker_mac.mm +++ b/content/browser/mach_broker_mac.mm @@ -276,7 +276,8 @@ virtual void ThreadMain() OVERRIDE { // static std::string MachBroker::GetMachPortName() { - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); const bool is_child = command_line->HasSwitch(switches::kProcessType); // In non-browser (child) processes, use the parent's pid. diff --git a/content/browser/media/android/browser_media_player_manager.cc b/content/browser/media/android/browser_media_player_manager.cc index 53f1a4180b379..030be1ce3f94c 100644 --- a/content/browser/media/android/browser_media_player_manager.cc +++ b/content/browser/media/android/browser_media_player_manager.cc @@ -85,7 +85,8 @@ MediaPlayerAndroid* BrowserMediaPlayerManager::CreateMediaPlayer( weak_ptr_factory_.GetWeakPtr()), base::Bind(&BrowserMediaPlayerManager::OnMediaResourcesReleased, weak_ptr_factory_.GetWeakPtr()), - media_player_params.frame_url); + media_player_params.frame_url, + media_player_params.allow_credentials); BrowserMediaPlayerManager* browser_media_player_manager = static_cast(manager); ContentViewCoreImpl* content_view_core_impl = @@ -264,6 +265,15 @@ void BrowserMediaPlayerManager::PauseVideo() { Send(new MediaPlayerMsg_PauseVideo(RoutingID())); } +void BrowserMediaPlayerManager::ReleaseAllMediaPlayers() { + for (ScopedVector::iterator it = players_.begin(); + it != players_.end(); ++it) { + if ((*it)->player_id() == fullscreen_player_id_) + fullscreen_player_is_released_ = true; + (*it)->Release(); + } +} + void BrowserMediaPlayerManager::OnSeekComplete( int player_id, const base::TimeDelta& current_time) { diff --git a/content/browser/media/android/browser_media_player_manager.h b/content/browser/media/android/browser_media_player_manager.h index 5f3fae57565fa..fe78b8ec16c89 100644 --- a/content/browser/media/android/browser_media_player_manager.h +++ b/content/browser/media/android/browser_media_player_manager.h @@ -70,6 +70,9 @@ class CONTENT_EXPORT BrowserMediaPlayerManager // Pauses all video players manages by this class. void PauseVideo(); + // Stops and releases every media managed by this class. + void ReleaseAllMediaPlayers(); + // media::MediaPlayerManager overrides. virtual void OnTimeUpdate( int player_id, base::TimeDelta current_time) OVERRIDE; diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc index 930b752159408..246ab1b5d3965 100644 --- a/content/browser/media/capture/desktop_capture_device.cc +++ b/content/browser/media/capture/desktop_capture_device.cc @@ -15,6 +15,7 @@ #include "content/browser/media/capture/desktop_capture_device_uma_types.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/desktop_media_id.h" +#include "content/public/browser/power_save_blocker.h" #include "media/base/video_util.h" #include "third_party/libyuv/include/libyuv/scale_argb.h" #include "third_party/webrtc/modules/desktop_capture/desktop_and_cursor_composer.h" @@ -124,6 +125,10 @@ class DesktopCaptureDevice::Core : public webrtc::DesktopCapturer::Callback { scoped_ptr black_frame_; + // TODO(jiayl): Remove power_save_blocker_ when there is an API to keep the + // screen from sleeping for the drive-by web. + scoped_ptr power_save_blocker_; + DISALLOW_COPY_AND_ASSIGN(Core); }; @@ -164,6 +169,10 @@ void DesktopCaptureDevice::Core::AllocateAndStart( // This capturer always outputs ARGB, non-interlaced. capture_format_.pixel_format = media::PIXEL_FORMAT_ARGB; + power_save_blocker_.reset(PowerSaveBlocker::Create( + PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, + "DesktopCaptureDevice is running").release()); + desktop_capturer_->Start(this); CaptureFrameAndScheduleNext(); diff --git a/content/browser/media/capture/desktop_capture_device_aura.cc b/content/browser/media/capture/desktop_capture_device_aura.cc index c99ad01e2bcf9..700b027347aa4 100644 --- a/content/browser/media/capture/desktop_capture_device_aura.cc +++ b/content/browser/media/capture/desktop_capture_device_aura.cc @@ -14,6 +14,7 @@ #include "content/browser/media/capture/desktop_capture_device_uma_types.h" #include "content/common/gpu/client/gl_helper.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/power_save_blocker.h" #include "media/base/video_util.h" #include "media/video/capture/video_capture_types.h" #include "skia/ext/image_operations.h" @@ -174,6 +175,10 @@ class DesktopVideoCaptureMachine gfx::Point cursor_hot_point_; SkBitmap scaled_cursor_bitmap_; + // TODO(jiayl): Remove power_save_blocker_ when there is an API to keep the + // screen from sleeping for the drive-by web. + scoped_ptr power_save_blocker_; + DISALLOW_COPY_AND_ASSIGN(DesktopVideoCaptureMachine); }; @@ -213,6 +218,10 @@ bool DesktopVideoCaptureMachine::Start( if (desktop_window_->GetHost()) desktop_window_->GetHost()->compositor()->AddObserver(this); + power_save_blocker_.reset(PowerSaveBlocker::Create( + PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep, + "DesktopCaptureDevice is running").release()); + // Starts timer. timer_.Start(FROM_HERE, oracle_proxy_->min_capture_period(), base::Bind(&DesktopVideoCaptureMachine::Capture, AsWeakPtr(), @@ -224,6 +233,7 @@ bool DesktopVideoCaptureMachine::Start( void DesktopVideoCaptureMachine::Stop(const base::Closure& callback) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + power_save_blocker_.reset(); // Stop observing compositor and window events. if (desktop_window_) { @@ -245,8 +255,15 @@ void DesktopVideoCaptureMachine::UpdateCaptureSize() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (oracle_proxy_ && desktop_window_) { ui::Layer* layer = desktop_window_->layer(); - oracle_proxy_->UpdateCaptureSize(ui::ConvertSizeToPixel( - layer, layer->bounds().size())); + gfx::Size capture_size = + ui::ConvertSizeToPixel(layer, layer->bounds().size()); +#if defined(OS_CHROMEOS) + // Pad desktop capture size to multiples of 16 pixels to accommodate HW + // encoder. TODO(hshi): remove this hack. See http://crbug.com/402151 + capture_size.SetSize((capture_size.width() + 15) & ~15, + (capture_size.height() + 15) & ~15); +#endif + oracle_proxy_->UpdateCaptureSize(capture_size); } ClearCursorState(); } @@ -271,10 +288,8 @@ void DesktopVideoCaptureMachine::Capture(bool dirty) { cc::CopyOutputRequest::CreateRequest( base::Bind(&DesktopVideoCaptureMachine::DidCopyOutput, AsWeakPtr(), frame, start_time, capture_frame_cb)); - gfx::Rect window_rect = - ui::ConvertRectToPixel(desktop_window_->layer(), - gfx::Rect(desktop_window_->bounds().width(), - desktop_window_->bounds().height())); + gfx::Rect window_rect = gfx::Rect(desktop_window_->bounds().width(), + desktop_window_->bounds().height()); request->set_area(window_rect); desktop_window_->layer()->RequestCopyOfOutput(request.Pass()); } diff --git a/content/browser/media/capture/web_contents_video_capture_device.cc b/content/browser/media/capture/web_contents_video_capture_device.cc index 2789634ea4eb3..2ddeb2b9eedbd 100644 --- a/content/browser/media/capture/web_contents_video_capture_device.cc +++ b/content/browser/media/capture/web_contents_video_capture_device.cc @@ -80,6 +80,10 @@ #include "skia/ext/image_operations.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/screen.h" namespace content { @@ -273,11 +277,14 @@ class WebContentsCaptureMachine virtual void WebContentsDestroyed() OVERRIDE; private: + // Computes the preferred size of the target RenderWidget for optimal capture. + gfx::Size ComputeOptimalTargetSize() const; + // Starts observing the web contents, returning false if lookup fails. bool StartObservingWebContents(); // Helper function to determine the view that we are currently tracking. - RenderWidgetHost* GetTarget(); + RenderWidgetHost* GetTarget() const; // Response callback for RenderWidgetHost::CopyFromBackingStore(). void DidCopyFromBackingStore( @@ -675,7 +682,40 @@ void WebContentsCaptureMachine::Capture( } } +gfx::Size WebContentsCaptureMachine::ComputeOptimalTargetSize() const { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + gfx::Size optimal_size = oracle_proxy_->GetCaptureSize(); + + // If the ratio between physical and logical pixels is greater than 1:1, + // shrink |optimal_size| by that amount. Then, when external code resizes the + // render widget to the "preferred size," the widget will be physically + // rendered at the exact capture size, thereby eliminating unnecessary scaling + // operations in the graphics pipeline. + RenderWidgetHost* const rwh = GetTarget(); + RenderWidgetHostView* const rwhv = rwh ? rwh->GetView() : NULL; + if (rwhv) { + const gfx::NativeView view = rwhv->GetNativeView(); + gfx::Screen* const screen = gfx::Screen::GetScreenFor(view); + if (screen->IsDIPEnabled()) { + const gfx::Display display = screen->GetDisplayNearestWindow(view); + const float scale = display.device_scale_factor(); + if (scale > 1.0f) { + const gfx::Size shrunk_size( + gfx::ToFlooredSize(gfx::ScaleSize(optimal_size, 1.0f / scale))); + if (shrunk_size.width() > 0 && shrunk_size.height() > 0) + optimal_size = shrunk_size; + } + } + } + + VLOG(1) << "Computed optimal target size: " << optimal_size.ToString(); + return optimal_size; +} + bool WebContentsCaptureMachine::StartObservingWebContents() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Look-up the RenderFrameHost and, from that, the WebContents that wraps it. // If successful, begin observing the WebContents instance. // @@ -694,7 +734,7 @@ bool WebContentsCaptureMachine::StartObservingWebContents() { WebContentsImpl* contents = static_cast(web_contents()); if (contents) { - contents->IncrementCapturerCount(oracle_proxy_->GetCaptureSize()); + contents->IncrementCapturerCount(ComputeOptimalTargetSize()); fullscreen_widget_id_ = contents->GetFullscreenWidgetRoutingID(); RenewFrameSubscription(); return true; @@ -710,7 +750,7 @@ void WebContentsCaptureMachine::WebContentsDestroyed() { oracle_proxy_->ReportError("WebContentsDestroyed()"); } -RenderWidgetHost* WebContentsCaptureMachine::GetTarget() { +RenderWidgetHost* WebContentsCaptureMachine::GetTarget() const { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!web_contents()) return NULL; 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 162df4bd06302..4723c79febed7 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 @@ -32,6 +32,8 @@ #include "skia/ext/platform_canvas.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" namespace content { namespace { @@ -39,6 +41,7 @@ namespace { const int kTestWidth = 320; const int kTestHeight = 240; const int kTestFramesPerSecond = 20; +const float kTestDeviceScaleFactor = 2.0f; const SkColor kNothingYet = 0xdeadbeef; const SkColor kNotInterested = ~kNothingYet; @@ -466,6 +469,49 @@ class StubClientObserver { DISALLOW_COPY_AND_ASSIGN(StubClientObserver); }; +// A dummy implementation of gfx::Screen, since WebContentsVideoCaptureDevice +// needs access to a gfx::Display's device scale factor. +class FakeScreen : public gfx::Screen { + public: + FakeScreen() : the_one_display_(0x1337, gfx::Rect(0, 0, 2560, 1440)) { + the_one_display_.set_device_scale_factor(kTestDeviceScaleFactor); + } + virtual ~FakeScreen() {} + + // gfx::Screen implementation (only what's needed for testing). + virtual bool IsDIPEnabled() OVERRIDE { return true; } + virtual gfx::Point GetCursorScreenPoint() OVERRIDE { return gfx::Point(); } + virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE { return NULL; } + virtual gfx::NativeWindow GetWindowAtScreenPoint( + const gfx::Point& point) OVERRIDE { return NULL; } + virtual int GetNumDisplays() const OVERRIDE { return 1; } + virtual std::vector GetAllDisplays() const OVERRIDE { + return std::vector(1, the_one_display_); + } + virtual gfx::Display GetDisplayNearestWindow( + gfx::NativeView view) const OVERRIDE { + return the_one_display_; + } + virtual gfx::Display GetDisplayNearestPoint( + const gfx::Point& point) const OVERRIDE { + return the_one_display_; + } + virtual gfx::Display GetDisplayMatching( + const gfx::Rect& match_rect) const OVERRIDE { + return the_one_display_; + } + virtual gfx::Display GetPrimaryDisplay() const OVERRIDE { + return the_one_display_; + } + virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE {} + virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE {} + + private: + gfx::Display the_one_display_; + + DISALLOW_COPY_AND_ASSIGN(FakeScreen); +}; + // Test harness that sets up a minimal environment with necessary stubs. class WebContentsVideoCaptureDeviceTest : public testing::Test { public: @@ -477,6 +523,9 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { protected: virtual void SetUp() { + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, &fake_screen_); + ASSERT_EQ(&fake_screen_, gfx::Screen::GetNativeScreen()); + // TODO(nick): Sadness and woe! Much "mock-the-world" boilerplate could be // eliminated here, if only we could use RenderViewHostTestHarness. The // catch is that we need our TestRenderViewHost to support a @@ -530,10 +579,13 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { SiteInstanceImpl::set_render_process_host_factory(NULL); render_view_host_factory_.reset(); render_process_host_factory_.reset(); + + gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE, NULL); } // Accessors. CaptureTestSourceController* source() { return &controller_; } + WebContents* web_contents() const { return web_contents_.get(); } media::VideoCaptureDevice* device() { return device_.get(); } void SimulateDrawEvent() { @@ -558,6 +610,8 @@ class WebContentsVideoCaptureDeviceTest : public testing::Test { } private: + FakeScreen fake_screen_; + StubClientObserver client_observer_; // The controller controls which pixel patterns to produce. @@ -597,6 +651,11 @@ TEST_F(WebContentsVideoCaptureDeviceTest, InvalidInitialWebContentsError) { } TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { + const gfx::Size capture_preferred_size( + static_cast(kTestWidth / kTestDeviceScaleFactor), + static_cast(kTestHeight / kTestDeviceScaleFactor)); + ASSERT_NE(capture_preferred_size, web_contents()->GetPreferredSize()); + // We'll simulate the tab being closed after the capture pipeline is up and // running. media::VideoCaptureParams capture_params; @@ -612,6 +671,10 @@ TEST_F(WebContentsVideoCaptureDeviceTest, WebContentsDestroyed) { base::RunLoop().RunUntilIdle(); + // Check that the preferred size of the WebContents matches the one provided + // by WebContentsVideoCaptureDevice. + EXPECT_EQ(capture_preferred_size, web_contents()->GetPreferredSize()); + // Post a task to close the tab. We should see an error reported to the // consumer. BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, diff --git a/content/browser/media/encrypted_media_browsertest.cc b/content/browser/media/encrypted_media_browsertest.cc index 9f5e1377049c6..c049d307cad0e 100644 --- a/content/browser/media/encrypted_media_browsertest.cc +++ b/content/browser/media/encrypted_media_browsertest.cc @@ -175,6 +175,9 @@ IN_PROC_BROWSER_TEST_P(EncryptedMediaTest, FrameSizeChangeVideo) { #if defined(OS_WIN) if (base::win::GetVersion() < base::win::VERSION_VISTA) return; +#elif defined(__aarch64__) + // Times out on arm64 currently due to http://crbug.com/403308 + return; #endif TestFrameSizeChange(); } diff --git a/content/browser/media/webrtc_browsertest.cc b/content/browser/media/webrtc_browsertest.cc index f7d7a1217f236..9e4de3859adff 100644 --- a/content/browser/media/webrtc_browsertest.cc +++ b/content/browser/media/webrtc_browsertest.cc @@ -84,7 +84,7 @@ class WebRtcBrowserTest : public WebRtcContentBrowserTest { return; } - ASSERT_TRUE(CommandLine::ForCurrentProcess()->HasSwitch( + ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kUseFakeDeviceForMediaStream)) << "Must run with fake devices since the test will explicitly look " << "for the fake device signal."; @@ -188,7 +188,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest, MAYBE_CanForwardRemoteStream) { // This test fails on Nexus 5 devices. // TODO(henrika): see http://crbug.com/362437 and http://crbug.com/359389 // for details. - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kDisableWebRtcHWDecoding); #endif MakeTypicalPeerConnectionCall( @@ -200,7 +200,7 @@ IN_PROC_BROWSER_TEST_F(WebRtcBrowserTest, MAYBE_CanForwardRemoteStream720p) { // This test fails on Nexus 5 devices. // TODO(henrika): see http://crbug.com/362437 and http://crbug.com/359389 // for details. - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kDisableWebRtcHWDecoding); #endif const std::string javascript = GenerateGetUserMediaCall( diff --git a/content/browser/media/webrtc_getusermedia_browsertest.cc b/content/browser/media/webrtc_getusermedia_browsertest.cc index 9efa23e9b2911..653e0edde56f0 100644 --- a/content/browser/media/webrtc_getusermedia_browsertest.cc +++ b/content/browser/media/webrtc_getusermedia_browsertest.cc @@ -503,8 +503,8 @@ IN_PROC_BROWSER_TEST_F(WebRtcGetUserMediaBrowserTest, large_value); NavigateToURL(shell(), url); - // TODO(perkj): A proper error code should be returned by gUM. - EXPECT_EQ("TrackStartError", ExecuteJavascriptAndReturnResult(call)); + EXPECT_EQ("ConstraintNotSatisfiedError", + ExecuteJavascriptAndReturnResult(call)); } // This test will make a simple getUserMedia page, verify that video is playing diff --git a/content/browser/media/webrtc_internals.cc b/content/browser/media/webrtc_internals.cc index 09f0e5a0ed42f..92ca83cc2f4b3 100644 --- a/content/browser/media/webrtc_internals.cc +++ b/content/browser/media/webrtc_internals.cc @@ -12,6 +12,7 @@ #include "content/public/browser/content_browser_client.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/browser/power_save_blocker.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" @@ -82,6 +83,7 @@ void WebRTCInternals::OnAddPeerConnection(int render_process_id, dict->SetString("constraints", constraints); dict->SetString("url", url); peer_connection_data_.Append(dict); + CreateOrReleasePowerSaveBlocker(); if (observers_.might_have_observers()) SendUpdate("addPeerConnection", dict); @@ -102,6 +104,7 @@ void WebRTCInternals::OnRemovePeerConnection(ProcessId pid, int lid) { continue; peer_connection_data_.Remove(i, NULL); + CreateOrReleasePowerSaveBlocker(); if (observers_.might_have_observers()) { base::DictionaryValue id; @@ -263,6 +266,7 @@ void WebRTCInternals::ResetForTesting() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); observers_.Clear(); peer_connection_data_.Clear(); + CreateOrReleasePowerSaveBlocker(); get_user_media_requests_.Clear(); aec_dump_enabled_ = false; } @@ -299,6 +303,8 @@ void WebRTCInternals::FileSelectionCanceled(void* params) { } void WebRTCInternals::OnRendererExit(int render_process_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + // Iterates from the end of the list to remove the PeerConnections created // by the exitting renderer. for (int i = peer_connection_data_.GetSize() - 1; i >= 0; --i) { @@ -322,6 +328,7 @@ void WebRTCInternals::OnRendererExit(int render_process_id) { peer_connection_data_.Remove(i, NULL); } } + CreateOrReleasePowerSaveBlocker(); bool found_any = false; // Iterates from the end of the list to remove the getUserMedia requests @@ -357,4 +364,20 @@ void WebRTCInternals::EnableAecDumpOnAllRenderProcessHosts() { } #endif +void WebRTCInternals::CreateOrReleasePowerSaveBlocker() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (peer_connection_data_.empty() && power_save_blocker_) { + DVLOG(1) << ("Releasing the block on application suspension since no " + "PeerConnections are active anymore."); + power_save_blocker_.reset(); + } else if (!peer_connection_data_.empty() && !power_save_blocker_) { + DVLOG(1) << ("Preventing the application from being suspended while one or " + "more PeerConnections are active."); + power_save_blocker_ = content::PowerSaveBlocker::Create( + content::PowerSaveBlocker::kPowerSaveBlockPreventAppSuspension, + "WebRTC has active PeerConnections.").Pass(); + } +} + } // namespace content diff --git a/content/browser/media/webrtc_internals.h b/content/browser/media/webrtc_internals.h index 6f22a999106e4..1f30ce8f069b7 100644 --- a/content/browser/media/webrtc_internals.h +++ b/content/browser/media/webrtc_internals.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_MEDIA_WEBRTC_INTERNALS_H_ #include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/singleton.h" #include "base/observer_list.h" #include "base/process/process.h" @@ -16,6 +17,8 @@ #include "ui/shell_dialogs/select_file_dialog.h" namespace content { + +class PowerSaveBlocker; class WebContents; class WebRTCInternalsUIObserver; @@ -131,6 +134,11 @@ class CONTENT_EXPORT WebRTCInternals : public NotificationObserver, void EnableAecDumpOnAllRenderProcessHosts(); #endif + // Called whenever an element is added to or removed from + // |peer_connection_data_| to impose/release a block on suspending the current + // application for power-saving. + void CreateOrReleasePowerSaveBlocker(); + ObserverList observers_; // |peer_connection_data_| is a list containing all the PeerConnection @@ -166,6 +174,11 @@ class CONTENT_EXPORT WebRTCInternals : public NotificationObserver, // AEC dump (diagnostic echo canceller recording) state. bool aec_dump_enabled_; base::FilePath aec_dump_file_path_; + + // While |peer_connection_data_| is non-empty, hold an instance of + // PowerSaveBlocker. This prevents the application from being suspended while + // remoting. + scoped_ptr power_save_blocker_; }; } // namespace content diff --git a/content/browser/mojo/mojo_application_host.cc b/content/browser/mojo/mojo_application_host.cc index 2491252891316..3af797499e0b8 100644 --- a/content/browser/mojo/mojo_application_host.cc +++ b/content/browser/mojo/mojo_application_host.cc @@ -59,4 +59,8 @@ bool MojoApplicationHost::Activate(IPC::Sender* sender, return did_activate_; } +void MojoApplicationHost::WillDestroySoon() { + channel_init_.WillDestroySoon(); +} + } // namespace content diff --git a/content/browser/mojo/mojo_application_host.h b/content/browser/mojo/mojo_application_host.h index ae571045bd68c..27a43f89c2dcd 100644 --- a/content/browser/mojo/mojo_application_host.h +++ b/content/browser/mojo/mojo_application_host.h @@ -32,6 +32,8 @@ class MojoApplicationHost { bool Init(); bool Activate(IPC::Sender* sender, base::ProcessHandle process_handle); + void WillDestroySoon(); + bool did_activate() const { return did_activate_; } ServiceRegistry* service_registry() { return &service_registry_; } diff --git a/content/browser/net/sqlite_persistent_cookie_store.cc b/content/browser/net/sqlite_persistent_cookie_store.cc index 967101f4dbb2b..420ad92d83782 100644 --- a/content/browser/net/sqlite_persistent_cookie_store.cc +++ b/content/browser/net/sqlite_persistent_cookie_store.cc @@ -1325,8 +1325,8 @@ net::CookieStore* CreateCookieStore(const CookieStoreConfig& config) { // // TODO(ajwong): Remove the InitializedForCurrentProcess() check // once http://crbug.com/331424 is resolved. - if (CommandLine::InitializedForCurrentProcess() && - CommandLine::ForCurrentProcess()->HasSwitch( + if (base::CommandLine::InitializedForCurrentProcess() && + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableFileCookies)) { cookie_monster->SetEnableFileScheme(true); } diff --git a/content/browser/plugin_browsertest.cc b/content/browser/plugin_browsertest.cc index d2c66fd0a5ee3..67b606c0789dd 100644 --- a/content/browser/plugin_browsertest.cc +++ b/content/browser/plugin_browsertest.cc @@ -222,10 +222,12 @@ IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ManyPlugins)) { LoadAndWait(GetURL("many_plugins.html")); } +#if !defined(OS_MACOSX) // http://crbug.com/402164 // Test various calls to GetURL from a plugin. IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetURL)) { LoadAndWait(GetURL("geturl.html")); } +#endif // Test various calls to GetURL for javascript URLs with // non NULL targets from a plugin. @@ -441,7 +443,7 @@ IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_FlashSecurity) { // TODO(port) Port the following tests to platforms that have the required // plugins. // Flaky: http://crbug.com/55915 -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Quicktime)) { +IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_Quicktime) { TestPlugin("quicktime.html"); } @@ -480,7 +482,8 @@ IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_Java) { TestPlugin("Java.html"); } -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Silverlight)) { +// Flaky: http://crbug.com/55915 +IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_Silverlight) { TestPlugin("silverlight.html"); } #endif // defined(OS_WIN) diff --git a/content/browser/plugin_process_host.cc b/content/browser/plugin_process_host.cc index 2a68eaf1cb2bd..040fafe88aaba 100644 --- a/content/browser/plugin_process_host.cc +++ b/content/browser/plugin_process_host.cc @@ -160,8 +160,9 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) { // Build command line for plugin. When we have a plugin launcher, we can't // allow "self" on linux and we need the real file path. - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); - CommandLine::StringType plugin_launcher = + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringType plugin_launcher = browser_command_line.GetSwitchValueNative(switches::kPluginLauncher); #if defined(OS_MACOSX) @@ -180,7 +181,7 @@ bool PluginProcessHost::Init(const WebPluginInfo& info) { if (exe_path.empty()) return false; - CommandLine* cmd_line = new CommandLine(exe_path); + base::CommandLine* cmd_line = new base::CommandLine(exe_path); // Put the process type and plugin path first so they're easier to see // in process listings using native process management tools. cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kPluginProcess); diff --git a/content/browser/plugin_service_impl.cc b/content/browser/plugin_service_impl.cc index a2c3170baf5bf..00326e39fe4f8 100644 --- a/content/browser/plugin_service_impl.cc +++ b/content/browser/plugin_service_impl.cc @@ -178,7 +178,8 @@ void PluginServiceImpl::Init() { RegisterPepperPlugins(); // Load any specified on the command line as well. - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); base::FilePath path = command_line->GetSwitchValuePath(switches::kLoadPlugin); if (!path.empty()) diff --git a/content/browser/power_save_blocker_x11.cc b/content/browser/power_save_blocker_x11.cc index e5fb4e5f3df43..f1d028c7f459e 100644 --- a/content/browser/power_save_blocker_x11.cc +++ b/content/browser/power_save_blocker_x11.cc @@ -200,7 +200,7 @@ void PowerSaveBlockerImpl::Delegate::ApplyBlock(DBusAPI api) { // reason: The reason for the inhibit // flags: Flags that spefify what should be inhibited message_writer->AppendString( - CommandLine::ForCurrentProcess()->GetProgram().value()); + base::CommandLine::ForCurrentProcess()->GetProgram().value()); message_writer->AppendUint32(0); // should be toplevel_xid message_writer->AppendString(reason_); { @@ -228,7 +228,7 @@ void PowerSaveBlockerImpl::Delegate::ApplyBlock(DBusAPI api) { // app_id: The application identifier // reason: The reason for the inhibit message_writer->AppendString( - CommandLine::ForCurrentProcess()->GetProgram().value()); + base::CommandLine::ForCurrentProcess()->GetProgram().value()); message_writer->AppendString(reason_); break; } diff --git a/content/browser/ppapi_plugin_process_host.cc b/content/browser/ppapi_plugin_process_host.cc index fc6a75ebed701..49d301719ff90 100644 --- a/content/browser/ppapi_plugin_process_host.cc +++ b/content/browser/ppapi_plugin_process_host.cc @@ -70,8 +70,9 @@ class PpapiPluginSandboxedProcessLauncherDelegate #elif defined(OS_POSIX) virtual bool ShouldUseZygote() OVERRIDE { - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); - CommandLine::StringType plugin_launcher = browser_command_line + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringType plugin_launcher = browser_command_line .GetSwitchValueNative(switches::kPpapiPluginLauncher); return !is_broker_ && plugin_launcher.empty() && info_.is_sandboxed; } @@ -288,8 +289,9 @@ bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) { return false; } - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); - CommandLine::StringType plugin_launcher = + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringType plugin_launcher = browser_command_line.GetSwitchValueNative(switches::kPpapiPluginLauncher); #if defined(OS_LINUX) @@ -304,7 +306,7 @@ bool PpapiPluginProcessHost::Init(const PepperPluginInfo& info) { return false; } - CommandLine* cmd_line = new CommandLine(exe_path); + base::CommandLine* cmd_line = new base::CommandLine(exe_path); cmd_line->AppendSwitchASCII(switches::kProcessType, is_broker_ ? switches::kPpapiBrokerProcess : switches::kPpapiPluginProcess); diff --git a/content/browser/renderer_host/OWNERS b/content/browser/renderer_host/OWNERS index 9d25787925e29..2bcb60654b611 100644 --- a/content/browser/renderer_host/OWNERS +++ b/content/browser/renderer_host/OWNERS @@ -2,7 +2,7 @@ per-file *aura*=ben@chromium.org # for *mac* -thakis@chromium.org +asvitkine@chromium.org # for GPU-related stuff in *mac* kbr@chromium.org diff --git a/content/browser/renderer_host/compositing_iosurface_layer_mac.h b/content/browser/renderer_host/compositing_iosurface_layer_mac.h index b9d19da0ef4b8..a01b6fc125ce6 100644 --- a/content/browser/renderer_host/compositing_iosurface_layer_mac.h +++ b/content/browser/renderer_host/compositing_iosurface_layer_mac.h @@ -22,8 +22,17 @@ class CompositingIOSurfaceContext; // BrowserCompositorViewMac). class CompositingIOSurfaceLayerClient { public: + // Used to indicate that the layer should attempt to draw immediately and + // should (even if the draw is elided by the system), ack the frame + // immediately. virtual bool AcceleratedLayerShouldAckImmediately() const = 0; - virtual void AcceleratedLayerDidDrawFrame(bool succeeded) = 0; + + // Called when a frame is drawn or when, because the layer is not visible, it + // is known that the frame will never drawn. + virtual void AcceleratedLayerDidDrawFrame() = 0; + + // Called when an error prevents the frame from being drawn. + virtual void AcceleratedLayerHitError() = 0; }; // CompositingIOSurfaceLayerHelper provides C++ functionality needed for the diff --git a/content/browser/renderer_host/compositing_iosurface_layer_mac.mm b/content/browser/renderer_host/compositing_iosurface_layer_mac.mm index 38054c855a5e6..aad9f7aac1db1 100644 --- a/content/browser/renderer_host/compositing_iosurface_layer_mac.mm +++ b/content/browser/renderer_host/compositing_iosurface_layer_mac.mm @@ -102,7 +102,10 @@ if (!has_pending_frame_) return; has_pending_frame_ = false; - client_->AcceleratedLayerDidDrawFrame(success); + if (success) + client_->AcceleratedLayerDidDrawFrame(); + else + client_->AcceleratedLayerHitError(); // A trace value of 0 indicates that there is no longer a pending swap ack. TRACE_COUNTER_ID1("browser", "PendingSwapAck", this, 0); } @@ -143,7 +146,7 @@ } void CompositingIOSurfaceLayerHelper::TimerFired() { - SetNeedsDisplayAndDisplayAndAck(); + DisplayIfNeededAndAck(); } void CompositingIOSurfaceLayerHelper::BeginPumpingFrames() { @@ -174,13 +177,19 @@ - (id)initWithIOSurface:(scoped_refptr) iosurface withScaleFactor:(float)scale_factor withClient:(content::CompositingIOSurfaceLayerClient*)client { + DCHECK(iosurface); if (self = [super init]) { helper_.reset(new content::CompositingIOSurfaceLayerHelper(client, self)); iosurface_ = iosurface; context_ = content::CompositingIOSurfaceContext::Get( content::CompositingIOSurfaceContext::kCALayerContextWindowNumber); - DCHECK(context_); + if (!context_) { + LOG(ERROR) << "Failed create CompositingIOSurfaceContext"; + [self resetClient]; + [self release]; + return nil; + } [self setBackgroundColor:CGColorGetConstantColor(kCGColorWhite)]; [self setAnchorPoint:CGPointMake(0, 0)]; diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc index d704f2860c25c..59272bea30fb3 100644 --- a/content/browser/renderer_host/compositor_impl_android.cc +++ b/content/browser/renderer_host/compositor_impl_android.cc @@ -436,7 +436,7 @@ void CompositorImpl::SetVisible(bool visible) { settings.top_controls_height = 0.f; settings.highp_threshold_min = 2048; - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); settings.initial_debug_state.SetRecordRenderingStats( command_line->HasSwitch(cc::switches::kEnableGpuBenchmarking)); settings.initial_debug_state.show_fps_counter = @@ -559,7 +559,6 @@ scoped_ptr CompositorImpl::CreateOutputSurface( void CompositorImpl::OnLostResources() { client_->DidLoseResources(); - ui_resource_provider_.UIResourcesAreInvalid(); } void CompositorImpl::ScheduleComposite() { diff --git a/content/browser/renderer_host/ime_adapter_android.cc b/content/browser/renderer_host/ime_adapter_android.cc index fe1c04ee0abf2..077e625f0ca95 100644 --- a/content/browser/renderer_host/ime_adapter_android.cc +++ b/content/browser/renderer_host/ime_adapter_android.cc @@ -29,6 +29,7 @@ #include "jni/ImeAdapter_jni.h" #include "third_party/WebKit/public/web/WebCompositionUnderline.h" #include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebTextInputType.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF16; @@ -87,6 +88,14 @@ bool RegisterImeAdapter(JNIEnv* env) { ui::TEXT_INPUT_TYPE_TELEPHONE, ui::TEXT_INPUT_TYPE_NUMBER, ui::TEXT_INPUT_TYPE_CONTENT_EDITABLE); + Java_ImeAdapter_initializeTextInputFlags( + env, + blink::WebTextInputFlagAutocompleteOn, + blink::WebTextInputFlagAutocompleteOff, + blink::WebTextInputFlagAutocorrectOn, + blink::WebTextInputFlagAutocorrectOff, + blink::WebTextInputFlagSpellcheckOn, + blink::WebTextInputFlagSpellcheckOff); return true; } diff --git a/content/browser/renderer_host/input/gesture_text_selector_unittest.cc b/content/browser/renderer_host/input/gesture_text_selector_unittest.cc index cf1e3b93d4e13..1ca90d1bdb61f 100644 --- a/content/browser/renderer_host/input/gesture_text_selector_unittest.cc +++ b/content/browser/renderer_host/input/gesture_text_selector_unittest.cc @@ -57,6 +57,22 @@ class GestureTextSelectorTest : public testing::Test, } protected: + static GestureEventData CreateGesture(ui::EventType type, + base::TimeTicks event_time, + float x, + float y) { + return GestureEventData(GestureEventDetails(type, 0, 0), + 0, + MotionEvent::TOOL_TYPE_FINGER, + event_time, + x, + y, + x, + y, + 1, + gfx::RectF(0, 0, 0, 0)); + } + scoped_ptr selector_; std::vector event_log_; }; @@ -130,25 +146,22 @@ TEST_F(GestureTextSelectorTest, PenDragging) { // 3. DOUBLE TAP // Suppress most gesture events when in text selection mode. event_time += base::TimeDelta::FromMilliseconds(10); - const GestureEventData double_tap( - GestureEventDetails(ui::ET_GESTURE_DOUBLE_TAP, 0, 0), 0, event_time, - x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0)); + const GestureEventData double_tap = + CreateGesture(ui::ET_GESTURE_DOUBLE_TAP, event_time, x2, y2); EXPECT_TRUE(selector_->OnGestureEvent(double_tap)); EXPECT_TRUE(event_log_.empty()); // 4. ET_GESTURE_SCROLL_BEGIN event_time += base::TimeDelta::FromMilliseconds(10); - const GestureEventData scroll_begin( - GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 0, 0), 0, event_time, - x1, y1, x1, y1, 1, gfx::RectF(0, 0, 0, 0)); + const GestureEventData scroll_begin = + CreateGesture(ui::ET_GESTURE_SCROLL_BEGIN, event_time, x1, y1); EXPECT_TRUE(selector_->OnGestureEvent(scroll_begin)); EXPECT_EQ(1u, event_log_.size()); // Unselect // 5. ET_GESTURE_SCROLL_UPDATE event_time += base::TimeDelta::FromMilliseconds(10); - const GestureEventData scroll_update( - GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 0, 0), 0, event_time, - x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0)); + const GestureEventData scroll_update = + CreateGesture(ui::ET_GESTURE_SCROLL_UPDATE, event_time, x2, y2); EXPECT_TRUE(selector_->OnGestureEvent(scroll_update)); EXPECT_EQ(3u, event_log_.size()); // Unselect, Show, SelectRange EXPECT_STREQ("SelectRange", event_log_.back().c_str()); @@ -163,9 +176,8 @@ TEST_F(GestureTextSelectorTest, PenDragging) { // 7. ET_GESTURE_SCROLL_END event_time += base::TimeDelta::FromMilliseconds(10); - const GestureEventData scroll_end( - GestureEventDetails(ui::ET_GESTURE_SCROLL_END, 0, 0), 0, event_time, - x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0)); + const GestureEventData scroll_end = + CreateGesture(ui::ET_GESTURE_SCROLL_END, event_time, x2, y2); EXPECT_TRUE(selector_->OnGestureEvent(scroll_end)); EXPECT_EQ(3u, event_log_.size()); // NO CHANGE } @@ -186,9 +198,8 @@ TEST_F(GestureTextSelectorTest, TapToSelectWord) { // 5. TAP_DOWN event_time += base::TimeDelta::FromMilliseconds(10); - const GestureEventData tap_down( - GestureEventDetails(ui::ET_GESTURE_TAP_DOWN, 0, 0), 0, event_time, - x2, y2, x2, y2, 1, gfx::RectF(0, 0, 0, 0)); + const GestureEventData tap_down = + CreateGesture(ui::ET_GESTURE_TAP_DOWN, event_time, x2, y2); EXPECT_TRUE(selector_->OnGestureEvent(tap_down)); EXPECT_TRUE(event_log_.empty()); @@ -210,9 +221,8 @@ TEST_F(GestureTextSelectorTest, TapToSelectWord) { // 4. TAP event_time += base::TimeDelta::FromMilliseconds(10); - const GestureEventData tap( - GestureEventDetails(ui::ET_GESTURE_TAP, 0, 0), 0, event_time, - x1, y1, x1, y1, 1, gfx::RectF(0, 0, 0, 0)); + const GestureEventData tap = + CreateGesture(ui::ET_GESTURE_TAP, event_time, x1, y1); EXPECT_TRUE(selector_->OnGestureEvent(tap)); EXPECT_EQ(1u, event_log_.size()); // LongPress EXPECT_STREQ("LongPress", event_log_.back().c_str()); diff --git a/content/browser/renderer_host/input/input_router_config_helper.cc b/content/browser/renderer_host/input/input_router_config_helper.cc index 31b17cfab70fd..268b055ebce9a 100644 --- a/content/browser/renderer_host/input/input_router_config_helper.cc +++ b/content/browser/renderer_host/input/input_router_config_helper.cc @@ -114,7 +114,7 @@ TouchEventQueue::Config GetTouchEventQueueConfig() { TouchEventQueue::TouchScrollingMode GetTouchScrollingMode() { std::string modeString = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kTouchScrollingMode); if (modeString == switches::kTouchScrollingModeAsyncTouchmove) return TouchEventQueue::TOUCH_SCROLLING_MODE_ASYNC_TOUCHMOVE; diff --git a/content/browser/renderer_host/input/input_router_impl.h b/content/browser/renderer_host/input/input_router_impl.h index e06113b3e5819..ed648f50ce622 100644 --- a/content/browser/renderer_host/input/input_router_impl.h +++ b/content/browser/renderer_host/input/input_router_impl.h @@ -33,7 +33,6 @@ namespace content { class InputAckHandler; class InputRouterClient; class OverscrollController; -class RenderWidgetHostImpl; struct DidOverscrollParams; // A default implementation for browser input event routing. diff --git a/content/browser/renderer_host/input/input_router_impl_unittest.cc b/content/browser/renderer_host/input/input_router_impl_unittest.cc index 1fa1a7d305ac5..713ae5f84577f 100644 --- a/content/browser/renderer_host/input/input_router_impl_unittest.cc +++ b/content/browser/renderer_host/input/input_router_impl_unittest.cc @@ -151,7 +151,7 @@ class InputRouterImplTest : public testing::Test { process_.reset(new MockRenderProcessHost(browser_context_.get())); client_.reset(new MockInputRouterClient()); ack_handler_.reset(new MockInputAckHandler()); - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitch(switches::kValidateInputEventStream); input_router_.reset(new InputRouterImpl(process_.get(), client_.get(), diff --git a/content/browser/renderer_host/input/motion_event_android.cc b/content/browser/renderer_host/input/motion_event_android.cc index 7316a405f4993..6688a3d24dd7c 100644 --- a/content/browser/renderer_host/input/motion_event_android.cc +++ b/content/browser/renderer_host/input/motion_event_android.cc @@ -64,6 +64,8 @@ MotionEventAndroid::ToolType FromAndroidToolType(int android_tool_type) { return MotionEventAndroid::TOOL_TYPE_STYLUS; case TOOL_TYPE_MOUSE: return MotionEventAndroid::TOOL_TYPE_MOUSE; + case TOOL_TYPE_ERASER: + return MotionEventAndroid::TOOL_TYPE_ERASER; default: NOTREACHED() << "Invalid Android MotionEvent tool type: " << android_tool_type; diff --git a/content/browser/renderer_host/input/motion_event_android.h b/content/browser/renderer_host/input/motion_event_android.h index aa3e08241aac2..032c08321cd98 100644 --- a/content/browser/renderer_host/input/motion_event_android.h +++ b/content/browser/renderer_host/input/motion_event_android.h @@ -10,6 +10,7 @@ #include "base/android/scoped_java_ref.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" +#include "content/common/content_export.h" #include "ui/events/gesture_detection/motion_event.h" #include "ui/gfx/geometry/point_f.h" @@ -18,7 +19,7 @@ namespace content { // Implementation of ui::MotionEvent wrapping a native Android MotionEvent. // All *input* coordinates are in device pixels (as with Android MotionEvent), // while all *output* coordinates are in DIPs (as with WebTouchEvent). -class MotionEventAndroid : public ui::MotionEvent { +class CONTENT_EXPORT MotionEventAndroid : public ui::MotionEvent { public: // Forcing the caller to provide all cached values upon construction // eliminates the need to perform a JNI call to retrieve values individually. diff --git a/content/browser/renderer_host/input/motion_event_android_unittest.cc b/content/browser/renderer_host/input/motion_event_android_unittest.cc new file mode 100644 index 0000000000000..f1c13308f38d6 --- /dev/null +++ b/content/browser/renderer_host/input/motion_event_android_unittest.cc @@ -0,0 +1,179 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/android/jni_android.h" +#include "content/browser/renderer_host/input/motion_event_android.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ui::MotionEvent; + +namespace content { +namespace { +const float kPixToDip = 0.5f; + +// Corresponds to ACTION_DOWN, see +// developer.android.com/reference/android/view/MotionEvent.html#ACTION_DOWN. +int kAndroidActionDown = 0; + +// Corresponds to TOOL_TYPE_FINGER, see +// developer.android.com/reference/android/view/MotionEvent.html +// #TOOL_TYPE_FINGER. +int kAndroidToolTypeFinger = 1; + +// Corresponds to BUTTON_PRIMARY, see +// developer.android.com/reference/android/view/MotionEvent.html#BUTTON_PRIMARY. +int kAndroidButtonPrimary = 1; + +} // namespace + +TEST(MotionEventAndroidTest, Constructor) { + int event_time_ms = 5; + base::TimeTicks event_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(event_time_ms); + float x0 = 13.7; + float y0 = -7.13; + float x1 = -13.7; + float y1 = 7.13; + float raw_offset = 10.1f; + float touch_major0 = 5.3f; + float touch_major1 = 3.5f; + int p0 = 1; + int p1 = 2; + int pointer_count = 2; + int history_size = 0; + int action_index = -1; + base::android::ScopedJavaLocalRef base_event_obj = + MotionEventAndroid::Obtain( + event_time, event_time, MotionEvent::ACTION_DOWN, x0, y0); + ASSERT_TRUE(base_event_obj.obj()); + + MotionEventAndroid event(kPixToDip, + base::android::AttachCurrentThread(), + base_event_obj.obj(), + event_time_ms, + kAndroidActionDown, + pointer_count, + history_size, + action_index, + x0, + y0, + x1, + y1, + p0, + p1, + touch_major0, + touch_major1, + x0 + raw_offset, + y0 - raw_offset, + kAndroidToolTypeFinger, + kAndroidToolTypeFinger, + kAndroidButtonPrimary); + + EXPECT_EQ(MotionEvent::ACTION_DOWN, event.GetAction()); + EXPECT_EQ(event_time, event.GetEventTime()); + EXPECT_EQ(x0 * kPixToDip, event.GetX(0)); + EXPECT_EQ(y0 * kPixToDip, event.GetY(0)); + EXPECT_EQ(x1 * kPixToDip, event.GetX(1)); + EXPECT_EQ(y1 * kPixToDip, event.GetY(1)); + EXPECT_FLOAT_EQ((x0 + raw_offset) * kPixToDip, event.GetRawX(0)); + EXPECT_FLOAT_EQ((y0 - raw_offset) * kPixToDip, event.GetRawY(0)); + EXPECT_FLOAT_EQ((x1 + raw_offset) * kPixToDip, event.GetRawX(1)); + EXPECT_FLOAT_EQ((y1 - raw_offset) * kPixToDip, event.GetRawY(1)); + EXPECT_EQ(touch_major0 * kPixToDip, event.GetTouchMajor(0)); + EXPECT_EQ(touch_major1 * kPixToDip, event.GetTouchMajor(1)); + EXPECT_EQ(p0, event.GetPointerId(0)); + EXPECT_EQ(p1, event.GetPointerId(1)); + EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(0)); + EXPECT_EQ(MotionEvent::TOOL_TYPE_FINGER, event.GetToolType(1)); + EXPECT_EQ(MotionEvent::BUTTON_PRIMARY, event.GetButtonState()); + EXPECT_EQ(static_cast(pointer_count), event.GetPointerCount()); + EXPECT_EQ(static_cast(history_size), event.GetHistorySize()); + EXPECT_EQ(action_index, event.GetActionIndex()); +} + +TEST(MotionEventAndroidTest, Clone) { + int event_time_ms = 5; + base::TimeTicks event_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(event_time_ms); + float x = 13.7; + float y = -7.13; + float touch_major = 5.3f; + int pointer_count = 1; + int pointer_id = 1; + base::android::ScopedJavaLocalRef event_obj = + MotionEventAndroid::Obtain( + event_time, event_time, MotionEvent::ACTION_DOWN, x, y); + ASSERT_TRUE(event_obj.obj()); + + MotionEventAndroid event(kPixToDip, + base::android::AttachCurrentThread(), + event_obj.obj(), + event_time_ms, + kAndroidActionDown, + pointer_count, + 0, + 0, + x, + y, + 0, + 0, + pointer_id, + 0, + touch_major, + 0.f, + x, + y, + 0, + 0, + 0); + + scoped_ptr clone = event.Clone(); + EXPECT_EQ(event, *clone); +} + +TEST(MotionEventAndroidTest, Cancel) { + int event_time_ms = 5; + base::TimeTicks event_time = + base::TimeTicks() + base::TimeDelta::FromMilliseconds(event_time_ms); + int pointer_count = 1; + float x = 13.7; + float y = -7.13; + base::android::ScopedJavaLocalRef event_obj = + MotionEventAndroid::Obtain( + event_time, event_time, MotionEvent::ACTION_DOWN, x, y); + ASSERT_TRUE(event_obj.obj()); + + MotionEventAndroid event(kPixToDip, + base::android::AttachCurrentThread(), + event_obj.obj(), + event_time_ms, + kAndroidActionDown, + pointer_count, + 0, + 0, + x, + y, + 0, + 0, + 0, + 0, + 0.f, + 0.f, + x, + y, + 0, + 0, + 0); + + scoped_ptr cancel_event = event.Cancel(); + EXPECT_EQ(MotionEvent::ACTION_CANCEL, cancel_event->GetAction()); + EXPECT_EQ(event_time, cancel_event->GetEventTime()); + EXPECT_EQ(x * kPixToDip, cancel_event->GetX(0)); + EXPECT_EQ(y * kPixToDip, cancel_event->GetY(0)); + EXPECT_EQ(static_cast(pointer_count), + cancel_event->GetPointerCount()); + EXPECT_EQ(0U, cancel_event->GetHistorySize()); +} + +} // namespace content diff --git a/content/browser/renderer_host/input/motion_event_web.cc b/content/browser/renderer_host/input/motion_event_web.cc index c85d9c42fc951..6e53773a68e8c 100644 --- a/content/browser/renderer_host/input/motion_event_web.cc +++ b/content/browser/renderer_host/input/motion_event_web.cc @@ -119,7 +119,8 @@ base::TimeTicks MotionEventWeb::GetEventTime() const { ui::MotionEvent::ToolType MotionEventWeb::GetToolType( size_t pointer_index) const { - NOTIMPLEMENTED(); + // TODO(jdduke): Plumb tool type from the platform event, crbug.com/404128. + DCHECK_LT(pointer_index, GetPointerCount()); return TOOL_TYPE_UNKNOWN; } diff --git a/content/browser/renderer_host/input/selection_event_type_list.h b/content/browser/renderer_host/input/selection_event_type_list.h index 89ceb0d6c25bb..b4f3852a99915 100644 --- a/content/browser/renderer_host/input/selection_event_type_list.h +++ b/content/browser/renderer_host/input/selection_event_type_list.h @@ -14,7 +14,10 @@ DEFINE_SELECTION_EVENT_TYPE(SELECTION_SHOWN, 0) DEFINE_SELECTION_EVENT_TYPE(SELECTION_CLEARED, 1) -DEFINE_SELECTION_EVENT_TYPE(INSERTION_SHOWN, 2) -DEFINE_SELECTION_EVENT_TYPE(INSERTION_MOVED, 3) -DEFINE_SELECTION_EVENT_TYPE(INSERTION_TAPPED, 4) -DEFINE_SELECTION_EVENT_TYPE(INSERTION_CLEARED, 5) +DEFINE_SELECTION_EVENT_TYPE(SELECTION_DRAG_STARTED, 2) +DEFINE_SELECTION_EVENT_TYPE(SELECTION_DRAG_STOPPED, 3) +DEFINE_SELECTION_EVENT_TYPE(INSERTION_SHOWN, 4) +DEFINE_SELECTION_EVENT_TYPE(INSERTION_MOVED, 5) +DEFINE_SELECTION_EVENT_TYPE(INSERTION_TAPPED, 6) +DEFINE_SELECTION_EVENT_TYPE(INSERTION_CLEARED, 7) +DEFINE_SELECTION_EVENT_TYPE(INSERTION_DRAG_STARTED, 8) diff --git a/content/browser/renderer_host/input/touch_handle.cc b/content/browser/renderer_host/input/touch_handle.cc index 2636179e68e66..d5bda485dca02 100644 --- a/content/browser/renderer_host/input/touch_handle.cc +++ b/content/browser/renderer_host/input/touch_handle.cc @@ -17,11 +17,15 @@ const double kFadeDurationMs = 200; // when the handle is moving rapidly while the fade is active. const double kFadeDistanceSquared = 20.f * 20.f; +// Avoid using an empty touch rect, as it may fail the intersection test event +// if it lies within the other rect's bounds. +const float kMinTouchMajorForHitTesting = 1.f; + // The maximum touch size to use when computing whether a touch point is // targetting a touch handle. This is necessary for devices that misreport // touch radii, preventing inappropriately largely touch sizes from completely // breaking handle dragging behavior. -const float kMaxTouchMajorForHitTesting = 48.f; +const float kMaxTouchMajorForHitTesting = 36.f; } // namespace @@ -120,8 +124,9 @@ bool TouchHandle::WillHandleTouchEvent(const ui::MotionEvent& event) { case ui::MotionEvent::ACTION_DOWN: { if (!is_visible_) return false; - const float touch_size = - std::min(event.GetTouchMajor(), kMaxTouchMajorForHitTesting); + const float touch_size = std::max( + kMinTouchMajorForHitTesting, + std::min(kMaxTouchMajorForHitTesting, event.GetTouchMajor())); const gfx::RectF touch_rect(event.GetX() - touch_size * .5f, event.GetY() - touch_size * .5f, touch_size, diff --git a/content/browser/renderer_host/input/touch_handle_unittest.cc b/content/browser/renderer_host/input/touch_handle_unittest.cc index 4c50c2c6b9b79..87d3a8c68a376 100644 --- a/content/browser/renderer_host/input/touch_handle_unittest.cc +++ b/content/browser/renderer_host/input/touch_handle_unittest.cc @@ -424,6 +424,15 @@ TEST_F(TouchHandleTest, DragTargettingUsesTouchSize) { event.SetTouchMajor(kTouchSize * 2.f); EXPECT_TRUE(handle.WillHandleTouchEvent(event)); EXPECT_TRUE(IsDragging()); + + // Ensure a touch size of 0 can still register a hit. + event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, + event_time, + kDefaultDrawableSize / 2.f, + kDefaultDrawableSize / 2.f); + event.SetTouchMajor(0); + EXPECT_TRUE(handle.WillHandleTouchEvent(event)); + EXPECT_TRUE(IsDragging()); } TEST_F(TouchHandleTest, Tap) { diff --git a/content/browser/renderer_host/input/touch_selection_controller.cc b/content/browser/renderer_host/input/touch_selection_controller.cc index a7ebb4f429619..d5380487e255b 100644 --- a/content/browser/renderer_host/input/touch_selection_controller.cc +++ b/content/browser/renderer_host/input/touch_selection_controller.cc @@ -4,6 +4,7 @@ #include "content/browser/renderer_host/input/touch_selection_controller.h" +#include "base/auto_reset.h" #include "base/logging.h" #include "third_party/WebKit/public/web/WebInputEvent.h" @@ -12,7 +13,7 @@ namespace content { TouchSelectionController::TouchSelectionController( TouchSelectionControllerClient* client) : client_(client), - last_input_event_type_(INPUT_EVENT_TYPE_NONE), + response_pending_input_event_(INPUT_EVENT_TYPE_NONE), start_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), start_visible_(false), end_orientation_(TOUCH_HANDLE_ORIENTATION_UNDEFINED), @@ -38,8 +39,11 @@ void TouchSelectionController::OnSelectionBoundsChanged( const gfx::RectF& end_rect, TouchHandleOrientation end_orientation, bool end_visible) { - if (!activate_selection_automatically_ && !activate_insertion_automatically_) + if (!activate_selection_automatically_ && + !activate_insertion_automatically_) { + DCHECK_EQ(INPUT_EVENT_TYPE_NONE, response_pending_input_event_); return; + } if (start_rect_ == start_rect && end_rect_ == end_rect && start_orientation_ == start_orientation && @@ -54,6 +58,14 @@ void TouchSelectionController::OnSelectionBoundsChanged( end_orientation_ = end_orientation; end_visible_ = end_visible; + // Ensure that |response_pending_input_event_| is cleared after the method + // completes, while also making its current value available for the duration + // of the call. + InputEventType causal_input_event = response_pending_input_event_; + response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; + base::AutoReset auto_reset_response_pending_input_event( + &response_pending_input_event_, causal_input_event); + const bool is_selection_dragging = is_selection_active_ && (start_selection_handle_->is_dragging() || end_selection_handle_->is_dragging()); @@ -117,14 +129,14 @@ bool TouchSelectionController::WillHandleTouchEvent( } void TouchSelectionController::OnLongPressEvent() { - last_input_event_type_ = LONG_PRESS; + response_pending_input_event_ = LONG_PRESS; ShowSelectionHandlesAutomatically(); ShowInsertionHandleAutomatically(); ResetCachedValuesIfInactive(); } void TouchSelectionController::OnTapEvent() { - last_input_event_type_ = TAP; + response_pending_input_event_ = TAP; activate_selection_automatically_ = false; DeactivateSelection(); ShowInsertionHandleAutomatically(); @@ -132,7 +144,7 @@ void TouchSelectionController::OnTapEvent() { } void TouchSelectionController::HideAndDisallowShowingAutomatically() { - last_input_event_type_ = INPUT_EVENT_TYPE_NONE; + response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; DeactivateInsertion(); DeactivateSelection(); activate_insertion_automatically_ = false; @@ -183,8 +195,10 @@ bool TouchSelectionController::Animate(base::TimeTicks frame_time) { } void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) { - if (&handle == insertion_handle_.get()) + if (&handle == insertion_handle_.get()) { + client_->OnSelectionEvent(INSERTION_DRAG_STARTED, handle.position()); return; + } if (&handle == start_selection_handle_.get()) { fixed_handle_position_ = end_selection_handle_->position() - @@ -193,6 +207,7 @@ void TouchSelectionController::OnHandleDragBegin(const TouchHandle& handle) { fixed_handle_position_ = start_selection_handle_->position() - gfx::Vector2dF(0, GetStartLineHeight() / 2.f); } + client_->OnSelectionEvent(SELECTION_DRAG_STARTED, handle.position()); } void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle, @@ -211,11 +226,13 @@ void TouchSelectionController::OnHandleDragUpdate(const TouchHandle& handle, } void TouchSelectionController::OnHandleDragEnd(const TouchHandle& handle) { + if (&handle != insertion_handle_.get()) + client_->OnSelectionEvent(SELECTION_DRAG_STOPPED, handle.position()); } void TouchSelectionController::OnHandleTapped(const TouchHandle& handle) { if (insertion_handle_ && &handle == insertion_handle_.get()) - client_->OnSelectionEvent(INSERTION_TAPPED, GetStartPosition()); + client_->OnSelectionEvent(INSERTION_TAPPED, handle.position()); } void TouchSelectionController::SetNeedsAnimate() { @@ -243,7 +260,7 @@ void TouchSelectionController::ShowSelectionHandlesAutomatically() { void TouchSelectionController::OnInsertionChanged() { DeactivateSelection(); - if (last_input_event_type_ == TAP && selection_empty_) { + if (response_pending_input_event_ == TAP && selection_empty_) { HideAndDisallowShowingAutomatically(); return; } @@ -319,8 +336,12 @@ void TouchSelectionController::ActivateSelection() { end_selection_handle_->SetOrientation(end_orientation_); } - if (!is_selection_active_) { + // As a long press received while a selection is already active may trigger + // an entirely new selection, notify the client but avoid sending an + // intervening SELECTION_CLEARED update to avoid unnecessary state changes. + if (!is_selection_active_ || response_pending_input_event_ == LONG_PRESS) { is_selection_active_ = true; + response_pending_input_event_ = INPUT_EVENT_TYPE_NONE; client_->OnSelectionEvent(SELECTION_SHOWN, GetStartPosition()); } } diff --git a/content/browser/renderer_host/input/touch_selection_controller.h b/content/browser/renderer_host/input/touch_selection_controller.h index 4ef75bd914597..c2b2059d6415c 100644 --- a/content/browser/renderer_host/input/touch_selection_controller.h +++ b/content/browser/renderer_host/input/touch_selection_controller.h @@ -117,7 +117,7 @@ class CONTENT_EXPORT TouchSelectionController : public TouchHandleClient { TouchSelectionControllerClient* const client_; - InputEventType last_input_event_type_; + InputEventType response_pending_input_event_; gfx::RectF start_rect_; TouchHandleOrientation start_orientation_; diff --git a/content/browser/renderer_host/input/touch_selection_controller_unittest.cc b/content/browser/renderer_host/input/touch_selection_controller_unittest.cc index 55e393af01975..f14860c4891cb 100644 --- a/content/browser/renderer_host/input/touch_selection_controller_unittest.cc +++ b/content/browser/renderer_host/input/touch_selection_controller_unittest.cc @@ -374,7 +374,8 @@ TEST_F(TouchSelectionControllerTest, InsertionTapped) { MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); - EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); + //TODO(AKV): this test case has to be modified once crbug.com/394093 is fixed. + EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); @@ -394,7 +395,7 @@ TEST_F(TouchSelectionControllerTest, InsertionTapped) { 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); - EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); + EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); // Reset the insertion. ClearInsertion(); @@ -407,7 +408,7 @@ TEST_F(TouchSelectionControllerTest, InsertionTapped) { EXPECT_TRUE(controller().WillHandleTouchEvent(event)); event = MockMotionEvent(MockMotionEvent::ACTION_CANCEL, event_time, 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); - EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); + EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); } TEST_F(TouchSelectionControllerTest, InsertionNotResetByRepeatedTapOrPress) { @@ -426,7 +427,7 @@ TEST_F(TouchSelectionControllerTest, InsertionNotResetByRepeatedTapOrPress) { controller().OnTapEvent(); MockMotionEvent event(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); - EXPECT_EQ(INSERTION_SHOWN, GetLastEventType()); + EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); @@ -444,7 +445,7 @@ TEST_F(TouchSelectionControllerTest, InsertionNotResetByRepeatedTapOrPress) { controller().OnSelectionEmpty(true); event = MockMotionEvent(MockMotionEvent::ACTION_DOWN, event_time, 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); - EXPECT_EQ(INSERTION_MOVED, GetLastEventType()); + EXPECT_EQ(INSERTION_DRAG_STARTED, GetLastEventType()); EXPECT_EQ(anchor_rect.bottom_left(), GetLastEventAnchor()); event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); @@ -477,6 +478,25 @@ TEST_F(TouchSelectionControllerTest, SelectionBasic) { EXPECT_EQ(gfx::PointF(), GetLastEventAnchor()); } +TEST_F(TouchSelectionControllerTest, SelectionRepeatedLongPress) { + gfx::RectF start_rect(5, 5, 0, 10); + gfx::RectF end_rect(50, 5, 0, 10); + bool visible = true; + + controller().OnLongPressEvent(); + ChangeSelection(start_rect, visible, end_rect, visible); + EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); + EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); + + // A long press triggering a new selection should re-send the SELECTION_SHOWN + // event notification. + start_rect.Offset(10, 10); + controller().OnLongPressEvent(); + ChangeSelection(start_rect, visible, end_rect, visible); + EXPECT_EQ(SELECTION_SHOWN, GetLastEventType()); + EXPECT_EQ(start_rect.bottom_left(), GetLastEventAnchor()); +} + TEST_F(TouchSelectionControllerTest, SelectionDragged) { base::TimeTicks event_time = base::TimeTicks::Now(); controller().OnLongPressEvent(); @@ -508,6 +528,7 @@ TEST_F(TouchSelectionControllerTest, SelectionDragged) { gfx::PointF start_offset = start_rect.CenterPoint(); event = MockMotionEvent(MockMotionEvent::ACTION_MOVE, event_time, 0, 5); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType()); EXPECT_TRUE(GetAndResetSelectionMoved()); EXPECT_EQ(fixed_offset, GetLastSelectionStart()); EXPECT_EQ(start_offset + gfx::Vector2dF(0, 5), GetLastSelectionEnd()); @@ -526,6 +547,7 @@ TEST_F(TouchSelectionControllerTest, SelectionDragged) { event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 10, 5); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType()); EXPECT_FALSE(GetAndResetSelectionMoved()); // Once the drag is complete, no more touch events should be consumed until @@ -553,6 +575,7 @@ TEST_F(TouchSelectionControllerTest, SelectionDraggedWithOverlap) { MockMotionEvent::ACTION_DOWN, event_time, touch_down_x, 0); SetDraggingEnabled(true); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(SELECTION_DRAG_STARTED, GetLastEventType()); EXPECT_FALSE(GetAndResetSelectionMoved()); // Even though the ACTION_MOVE is over the start handle, it should continue @@ -566,6 +589,7 @@ TEST_F(TouchSelectionControllerTest, SelectionDraggedWithOverlap) { event = MockMotionEvent(MockMotionEvent::ACTION_UP, event_time, 0, 0); EXPECT_TRUE(controller().WillHandleTouchEvent(event)); + EXPECT_EQ(SELECTION_DRAG_STOPPED, GetLastEventType()); EXPECT_FALSE(GetAndResetSelectionMoved()); } diff --git a/content/browser/renderer_host/media/audio_input_renderer_host.cc b/content/browser/renderer_host/media/audio_input_renderer_host.cc index 2dcc56d091809..1f9c26d7950e9 100644 --- a/content/browser/renderer_host/media/audio_input_renderer_host.cc +++ b/content/browser/renderer_host/media/audio_input_renderer_host.cc @@ -19,6 +19,20 @@ #include "media/audio/audio_manager_base.h" #include "media/base/audio_bus.h" +namespace { + +void LogMessage(int stream_id, const std::string& msg, bool add_prefix) { + std::ostringstream oss; + oss << "[stream_id=" << stream_id << "] "; + if (add_prefix) + oss << "AIRH::"; + oss << msg; + content::MediaStreamManager::SendMessageToNativeLog(oss.str()); + DVLOG(1) << oss.str(); +} + +} + namespace content { struct AudioInputRendererHost::AudioEntry { @@ -133,8 +147,10 @@ void AudioInputRendererHost::DoCompleteCreation( DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntry* entry = LookupByController(controller); - if (!entry) + if (!entry) { + NOTREACHED() << "AudioInputController is invalid."; return; + } if (!PeerHandle()) { NOTREACHED() << "Renderer process handle is invalid."; @@ -176,6 +192,10 @@ void AudioInputRendererHost::DoCompleteCreation( return; } + LogMessage(entry->stream_id, + "DoCompleteCreation => IPC channel and stream are now open", + true); + Send(new AudioInputMsg_NotifyStreamCreated(entry->stream_id, foreign_memory_handle, foreign_socket_handle, entry->shared_memory.requested_size(), @@ -187,29 +207,40 @@ void AudioInputRendererHost::DoSendRecordingMessage( DCHECK_CURRENTLY_ON(BrowserThread::IO); // TODO(henrika): See crbug.com/115262 for details on why this method // should be implemented. + AudioEntry* entry = LookupByController(controller); + if (!entry) { + NOTREACHED() << "AudioInputController is invalid."; + return; + } + LogMessage(entry->stream_id, + "DoSendRecordingMessage => stream is now started", + true); } void AudioInputRendererHost::DoHandleError( media::AudioInputController* controller, media::AudioInputController::ErrorCode error_code) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - // Log all errors even it is ignored later. - MediaStreamManager::SendMessageToNativeLog( - base::StringPrintf("AudioInputController error: %d", error_code)); + AudioEntry* entry = LookupByController(controller); + if (!entry) { + NOTREACHED() << "AudioInputController is invalid."; + return; + } // This is a fix for crbug.com/357501. The error can be triggered when closing // the lid on Macs, which causes more problems than it fixes. // Also, in crbug.com/357569, the goal is to remove usage of the error since // it was added to solve a crash on Windows that no longer can be reproduced. if (error_code == media::AudioInputController::NO_DATA_ERROR) { - DVLOG(1) << "AudioInputRendererHost@" << this << "::DoHandleError: " - << "NO_DATA_ERROR ignored."; + // TODO(henrika): it might be possible to do something other than just + // logging when we detect many NO_DATA_ERROR calls for a stream. + LogMessage(entry->stream_id, "AIC => NO_DATA_ERROR", false); return; } - AudioEntry* entry = LookupByController(controller); - if (!entry) - return; + std::ostringstream oss; + oss << "AIC reports error_code=" << error_code; + LogMessage(entry->stream_id, oss.str(), false); audio_log_->OnError(entry->stream_id); DeleteEntryOnError(entry, AUDIO_INPUT_CONTROLLER_ERROR); @@ -219,15 +250,13 @@ void AudioInputRendererHost::DoLog(media::AudioInputController* controller, const std::string& message) { DCHECK_CURRENTLY_ON(BrowserThread::IO); AudioEntry* entry = LookupByController(controller); - if (!entry) + if (!entry) { + NOTREACHED() << "AudioInputController is invalid."; return; + } // Add stream ID and current audio level reported by AIC to native log. - std::string log_string = - base::StringPrintf("[stream_id=%d] ", entry->stream_id); - log_string += message; - MediaStreamManager::SendMessageToNativeLog(log_string); - DVLOG(1) << log_string; + LogMessage(entry->stream_id, message, false); } bool AudioInputRendererHost::OnMessageReceived(const IPC::Message& message) { @@ -250,10 +279,10 @@ void AudioInputRendererHost::OnCreateStream( const AudioInputHostMsg_CreateStream_Config& config) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DVLOG(1) << "AudioInputRendererHost@" << this - << "::OnCreateStream(stream_id=" << stream_id - << ", render_view_id=" << render_view_id - << ", session_id=" << session_id << ")"; + std::ostringstream oss; + oss << "[stream_id=" << stream_id << "] " + << "AIRH::OnCreateStream(render_view_id=" << render_view_id + << ", session_id=" << session_id << ")"; DCHECK_GT(render_view_id, 0); // media::AudioParameters is validated in the deserializer. @@ -287,6 +316,7 @@ void AudioInputRendererHost::OnCreateStream( device_id = info->device.id; device_name = info->device.name; + oss << ": device_name=" << device_name; } // Create a new AudioEntry structure. @@ -350,21 +380,24 @@ void AudioInputRendererHost::OnCreateStream( // Set the initial AGC state for the audio input stream. Note that, the AGC // is only supported in AUDIO_PCM_LOW_LATENCY mode. - if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) + if (config.params.format() == media::AudioParameters::AUDIO_PCM_LOW_LATENCY) { entry->controller->SetAutomaticGainControl(config.automatic_gain_control); + oss << ", AGC=" << config.automatic_gain_control; + } + + MediaStreamManager::SendMessageToNativeLog(oss.str()); + DVLOG(1) << oss.str(); // Since the controller was created successfully, create an entry and add it // to the map. entry->stream_id = stream_id; audio_entries_.insert(std::make_pair(stream_id, entry.release())); - - MediaStreamManager::SendMessageToNativeLog( - "Audio input stream created successfully. Device name: " + device_name); audio_log_->OnCreated(stream_id, audio_params, device_id); } void AudioInputRendererHost::OnRecordStream(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + LogMessage(stream_id, "OnRecordStream", true); AudioEntry* entry = LookupById(stream_id); if (!entry) { @@ -378,6 +411,7 @@ void AudioInputRendererHost::OnRecordStream(int stream_id) { void AudioInputRendererHost::OnCloseStream(int stream_id) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + LogMessage(stream_id, "OnCloseStream", true); AudioEntry* entry = LookupById(stream_id); @@ -400,8 +434,10 @@ void AudioInputRendererHost::OnSetVolume(int stream_id, double volume) { void AudioInputRendererHost::SendErrorMessage( int stream_id, ErrorCode error_code) { - MediaStreamManager::SendMessageToNativeLog( - base::StringPrintf("AudioInputRendererHost error: %d", error_code)); + std::string err_msg = + base::StringPrintf("SendErrorMessage(error_code=%d)", error_code); + LogMessage(stream_id, err_msg, true); + Send(new AudioInputMsg_NotifyStreamStateChanged( stream_id, media::AudioInputIPCDelegate::kError)); } @@ -419,6 +455,7 @@ void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) { DCHECK_CURRENTLY_ON(BrowserThread::IO); if (!entry->pending_close) { + LogMessage(entry->stream_id, "CloseAndDeleteStream", true); entry->controller->Close(base::Bind(&AudioInputRendererHost::DeleteEntry, this, entry)); entry->pending_close = true; @@ -428,6 +465,7 @@ void AudioInputRendererHost::CloseAndDeleteStream(AudioEntry* entry) { void AudioInputRendererHost::DeleteEntry(AudioEntry* entry) { DCHECK_CURRENTLY_ON(BrowserThread::IO); + LogMessage(entry->stream_id, "DeleteEntry => stream is now closed", true); // Delete the entry when this method goes out of scope. scoped_ptr entry_deleter(entry); diff --git a/content/browser/renderer_host/media/audio_input_sync_writer.cc b/content/browser/renderer_host/media/audio_input_sync_writer.cc index 117fbe8fedbbe..6a78f6cc784aa 100644 --- a/content/browser/renderer_host/media/audio_input_sync_writer.cc +++ b/content/browser/renderer_host/media/audio_input_sync_writer.cc @@ -60,18 +60,20 @@ void AudioInputSyncWriter::Write(const media::AudioBus* data, if (last_write_time_.is_null()) { // This is the first time Write is called. base::TimeDelta interval = base::Time::Now() - creation_time_; - oss << "Audio input data received for the first time: delay = " - << interval.InMilliseconds() << "ms."; + oss << "AISW::Write => audio input data received for the first time: delay " + "= " << interval.InMilliseconds() << "ms"; } else { base::TimeDelta interval = base::Time::Now() - last_write_time_; if (interval > kLogDelayThreadhold) { - oss << "Audio input data delay unexpectedly long: delay = " - << interval.InMilliseconds() << "ms."; + oss << "AISW::Write => audio input data delay unexpectedly long: delay = " + << interval.InMilliseconds() << "ms"; } } - if (!oss.str().empty()) + if (!oss.str().empty()) { MediaStreamManager::SendMessageToNativeLog(oss.str()); + DVLOG(1) << oss.str(); + } last_write_time_ = base::Time::Now(); #endif diff --git a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc index 343c9890810ed..9dc99f20d0e0f 100644 --- a/content/browser/renderer_host/media/audio_renderer_host_unittest.cc +++ b/content/browser/renderer_host/media/audio_renderer_host_unittest.cc @@ -157,7 +157,7 @@ class AudioRendererHostTest : public testing::Test { public: AudioRendererHostTest() { audio_manager_.reset(media::AudioManager::CreateForTesting()); - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kUseFakeDeviceForMediaStream); media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get())); host_ = new MockAudioRendererHost(audio_manager_.get(), diff --git a/content/browser/renderer_host/media/audio_sync_reader.cc b/content/browser/renderer_host/media/audio_sync_reader.cc index 3daacca566ea4..ffc906ec1e4f9 100644 --- a/content/browser/renderer_host/media/audio_sync_reader.cc +++ b/content/browser/renderer_host/media/audio_sync_reader.cc @@ -9,12 +9,32 @@ #include "base/command_line.h" #include "base/memory/shared_memory.h" #include "base/metrics/histogram.h" +#include "base/strings/stringprintf.h" +#include "content/browser/renderer_host/media/media_stream_manager.h" #include "content/public/common/content_switches.h" #include "media/audio/audio_buffers_state.h" #include "media/audio/audio_parameters.h" using media::AudioBus; +namespace { + +// Used to log if any audio glitches have been detected during an audio session. +// Elements in this enum should not be added, deleted or rearranged. +enum AudioGlitchResult { + AUDIO_RENDERER_NO_AUDIO_GLITCHES = 0, + AUDIO_RENDERER_AUDIO_GLITCHES = 1, + AUDIO_RENDERER_AUDIO_GLITCHES_MAX = AUDIO_RENDERER_AUDIO_GLITCHES +}; + +void LogAudioGlitchResult(AudioGlitchResult result) { + UMA_HISTOGRAM_ENUMERATION("Media.AudioRendererAudioGlitches", + result, + AUDIO_RENDERER_AUDIO_GLITCHES_MAX + 1); +} + +} // namespace + namespace content { AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory, @@ -47,6 +67,18 @@ AudioSyncReader::~AudioSyncReader() { 100.0 * renderer_missed_callback_count_ / renderer_callback_count_; UMA_HISTOGRAM_PERCENTAGE( "Media.AudioRendererMissedDeadline", percentage_missed); + + // Add more detailed information regarding detected audio glitches where + // a non-zero value of |renderer_missed_callback_count_| is added to the + // AUDIO_RENDERER_AUDIO_GLITCHES bin. + renderer_missed_callback_count_ > 0 ? + LogAudioGlitchResult(AUDIO_RENDERER_AUDIO_GLITCHES) : + LogAudioGlitchResult(AUDIO_RENDERER_NO_AUDIO_GLITCHES); + std::string log_string = + base::StringPrintf("ASR: number of detected audio glitches=%d", + static_cast(renderer_missed_callback_count_)); + MediaStreamManager::SendMessageToNativeLog(log_string); + DVLOG(1) << log_string; } // media::AudioOutputController::SyncReader implementations. diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc index 327efce6e20b3..929cf198fb4b2 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.cc @@ -169,8 +169,7 @@ void MediaStreamDispatcherHost::OnEnumerateDevices( int render_frame_id, int page_request_id, MediaStreamType type, - const GURL& security_origin, - bool hide_labels_if_no_access) { + const GURL& security_origin) { DVLOG(1) << "MediaStreamDispatcherHost::OnEnumerateDevices(" << render_frame_id << ", " << page_request_id << ", " @@ -183,14 +182,10 @@ void MediaStreamDispatcherHost::OnEnumerateDevices( DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || type == MEDIA_DEVICE_VIDEO_CAPTURE || type == MEDIA_DEVICE_AUDIO_OUTPUT); - bool have_permission = true; - if (hide_labels_if_no_access) { - bool audio_type = type == MEDIA_DEVICE_AUDIO_CAPTURE || - type == MEDIA_DEVICE_AUDIO_OUTPUT; - have_permission = audio_type ? - resource_context_->AllowMicAccess(security_origin) : - resource_context_->AllowCameraAccess(security_origin); - } + bool have_permission = + type == MEDIA_DEVICE_AUDIO_CAPTURE || type == MEDIA_DEVICE_AUDIO_OUTPUT ? + resource_context_->AllowMicAccess(security_origin) : + resource_context_->AllowCameraAccess(security_origin); media_stream_manager_->EnumerateDevices( this, render_process_id_, render_frame_id, salt_callback_, diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host.h b/content/browser/renderer_host/media/media_stream_dispatcher_host.h index d467718bff975..0ef6defdcba7e 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host.h +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host.h @@ -79,8 +79,7 @@ class CONTENT_EXPORT MediaStreamDispatcherHost : public BrowserMessageFilter, void OnEnumerateDevices(int render_frame_id, int page_request_id, MediaStreamType type, - const GURL& security_origin, - bool hide_labels_if_no_access); + const GURL& security_origin); void OnCancelEnumerateDevices(int render_frame_id, int page_request_id); diff --git a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc index 24366a5a37d60..4e9df5b5803ef 100644 --- a/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_dispatcher_host_unittest.cc @@ -100,12 +100,10 @@ class MockMediaStreamDispatcherHost : public MediaStreamDispatcherHost, int page_request_id, MediaStreamType type, const GURL& security_origin, - bool hide_labels_if_no_access, const base::Closure& quit_closure) { quit_closures_.push(quit_closure); MediaStreamDispatcherHost::OnEnumerateDevices( - render_frame_id, page_request_id, type, security_origin, - hide_labels_if_no_access); + render_frame_id, page_request_id, type, security_origin); } std::string label_; @@ -225,7 +223,7 @@ class MediaStreamDispatcherHostTest : public testing::Test { audio_manager_.reset( new media::MockAudioManager(base::MessageLoopProxy::current())); // Make sure we use fake devices to avoid long delays. - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kUseFakeDeviceForMediaStream); // Create our own MediaStreamManager. media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get())); @@ -331,11 +329,10 @@ class MediaStreamDispatcherHostTest : public testing::Test { void EnumerateDevicesAndWaitForResult(int render_frame_id, int page_request_id, - MediaStreamType type, - bool hide_labels_if_no_access) { + MediaStreamType type) { base::RunLoop run_loop; host_->OnEnumerateDevices(render_frame_id, page_request_id, type, origin_, - hide_labels_if_no_access, run_loop.QuitClosure()); + run_loop.QuitClosure()); run_loop.Run(); ASSERT_FALSE(host_->enumerated_devices_.empty()); EXPECT_FALSE(DoesContainRawIds(host_->enumerated_devices_)); @@ -886,52 +883,32 @@ TEST_F(MediaStreamDispatcherHostTest, VideoDeviceUnplugged) { TEST_F(MediaStreamDispatcherHostTest, EnumerateAudioDevices) { EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId, - MEDIA_DEVICE_AUDIO_CAPTURE, true); + MEDIA_DEVICE_AUDIO_CAPTURE); EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_)); } TEST_F(MediaStreamDispatcherHostTest, EnumerateVideoDevices) { EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId, - MEDIA_DEVICE_VIDEO_CAPTURE, true); + MEDIA_DEVICE_VIDEO_CAPTURE); EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_)); } -TEST_F(MediaStreamDispatcherHostTest, EnumerateAudioDevicesNoAccessHideLabels) { +TEST_F(MediaStreamDispatcherHostTest, EnumerateAudioDevicesNoAccess) { MockResourceContext* mock_resource_context = static_cast(browser_context_.GetResourceContext()); mock_resource_context->set_mic_access(false); EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId, - MEDIA_DEVICE_AUDIO_CAPTURE, true); + MEDIA_DEVICE_AUDIO_CAPTURE); EXPECT_TRUE(DoesNotContainLabels(host_->enumerated_devices_)); } -TEST_F(MediaStreamDispatcherHostTest, EnumerateVideoDevicesNoAccessHideLabels) { +TEST_F(MediaStreamDispatcherHostTest, EnumerateVideoDevicesNoAccess) { MockResourceContext* mock_resource_context = static_cast(browser_context_.GetResourceContext()); mock_resource_context->set_camera_access(false); EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId, - MEDIA_DEVICE_VIDEO_CAPTURE, true); + MEDIA_DEVICE_VIDEO_CAPTURE); EXPECT_TRUE(DoesNotContainLabels(host_->enumerated_devices_)); } -TEST_F(MediaStreamDispatcherHostTest, - EnumerateAudioDevicesNoAccessNoHideLabels) { - MockResourceContext* mock_resource_context = - static_cast(browser_context_.GetResourceContext()); - mock_resource_context->set_mic_access(false); - EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId, - MEDIA_DEVICE_AUDIO_CAPTURE, false); - EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_)); -} - -TEST_F(MediaStreamDispatcherHostTest, - EnumerateVideoDevicesNoAccessNoHideLabels) { - MockResourceContext* mock_resource_context = - static_cast(browser_context_.GetResourceContext()); - mock_resource_context->set_camera_access(false); - EnumerateDevicesAndWaitForResult(kRenderId, kPageRequestId, - MEDIA_DEVICE_VIDEO_CAPTURE, false); - EXPECT_TRUE(DoesContainLabels(host_->enumerated_devices_)); -} - }; // namespace content diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc index 21d2aa4ce2e1c..cdb71b1a6a56a 100644 --- a/content/browser/renderer_host/media/media_stream_manager.cc +++ b/content/browser/renderer_host/media/media_stream_manager.cc @@ -118,6 +118,18 @@ void ParseStreamType(const StreamOptions& options, } } +// Turns off available audio effects (removes the flag) if the options +// explicitly turn them off. +void FilterAudioEffects(const StreamOptions& options, int* effects) { + DCHECK(effects); + // TODO(ajm): Should we also handle ECHO_CANCELLER here? + std::string value; + if (options.GetFirstAudioConstraintByName( + kMediaStreamAudioDucking, &value, NULL) && value == "false") { + *effects &= ~media::AudioParameters::DUCKING; + } +} + // Private helper method for SendMessageToNativeLog() that obtains the global // MediaStreamManager instance on the UI thread before sending |message| to the // webrtcLoggingPrivate API. @@ -1386,8 +1398,12 @@ bool MediaStreamManager::FindExistingRequestedDeviceInfo( device_it != request->devices.end(); ++device_it) { if (device_it->device.id == source_id && device_it->device.type == new_device_info.type) { - *existing_device_info = *device_it; - *existing_request_state = request->state(device_it->device.type); + *existing_device_info = *device_it; + // Make sure that the audio |effects| reflect what the request + // is set to and not what the capabilities are. + FilterAudioEffects(request->options, + &existing_device_info->device.input.effects); + *existing_request_state = request->state(device_it->device.type); return true; } } @@ -1568,6 +1584,14 @@ void MediaStreamManager::Opened(MediaStreamType stream_type, audio_input_device_manager_->GetOpenedDeviceInfoById( device_it->session_id); device_it->device.input = info->device.input; + + // Since the audio input device manager will set the input + // parameters to the default settings (including supported effects), + // we need to adjust those settings here according to what the + // request asks for. + FilterAudioEffects(request->options, + &device_it->device.input.effects); + device_it->device.matched_output = info->device.matched_output; } } diff --git a/content/browser/renderer_host/media/media_stream_manager_unittest.cc b/content/browser/renderer_host/media/media_stream_manager_unittest.cc index cc19af2af4c37..cfaaf94ec024f 100644 --- a/content/browser/renderer_host/media/media_stream_manager_unittest.cc +++ b/content/browser/renderer_host/media/media_stream_manager_unittest.cc @@ -79,7 +79,7 @@ class MediaStreamManagerTest : public ::testing::Test { : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), message_loop_(base::MessageLoopProxy::current()) { // Create our own MediaStreamManager. Use fake devices to run on the bots. - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kUseFakeDeviceForMediaStream); audio_manager_.reset(new MockAudioManager()); media_stream_manager_.reset(new MediaStreamManager(audio_manager_.get())); diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc index 41bafde91bc35..71af3fc9bfa6a 100644 --- a/content/browser/renderer_host/media/video_capture_manager.cc +++ b/content/browser/renderer_host/media/video_capture_manager.cc @@ -75,6 +75,22 @@ void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) { const int kMaxNumberOfBuffers = 3; const int kMaxNumberOfBuffersForTabCapture = 5; +// Used for logging capture events. +// Elements in this enum should not be deleted or rearranged; the only +// permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT. +enum VideoCaptureEvent { + VIDEO_CAPTURE_EVENT_START_CAPTURE = 0, + VIDEO_CAPTURE_EVENT_STOP_CAPTURE_NORMAL = 1, + VIDEO_CAPTURE_EVENT_STOP_CAPTURE_DUE_TO_ERROR = 2, + NUM_VIDEO_CAPTURE_EVENT +}; + +void LogVideoCaptureEvent(VideoCaptureEvent event) { + UMA_HISTOGRAM_ENUMERATION("Media.VideoCaptureManager.Event", + event, + NUM_VIDEO_CAPTURE_EVENT); +} + } // namespace namespace content { @@ -287,6 +303,8 @@ void VideoCaptureManager::StartCaptureForClient( DCHECK(entry->video_capture_controller); + LogVideoCaptureEvent(VIDEO_CAPTURE_EVENT_START_CAPTURE); + // First client starts the device. if (entry->video_capture_controller->GetClientCount() == 0) { DVLOG(1) << "VideoCaptureManager starting device (type = " @@ -317,6 +335,10 @@ void VideoCaptureManager::StopCaptureForClient( DCHECK(controller); DCHECK(client_handler); + LogVideoCaptureEvent(aborted_due_to_error ? + VIDEO_CAPTURE_EVENT_STOP_CAPTURE_DUE_TO_ERROR : + VIDEO_CAPTURE_EVENT_STOP_CAPTURE_NORMAL); + DeviceEntry* entry = GetDeviceEntryForController(controller); if (!entry) { NOTREACHED(); diff --git a/content/browser/renderer_host/overscroll_controller.cc b/content/browser/renderer_host/overscroll_controller.cc index 3a6e7576563ec..86dc692fea28d 100644 --- a/content/browser/renderer_host/overscroll_controller.cc +++ b/content/browser/renderer_host/overscroll_controller.cc @@ -15,7 +15,7 @@ using blink::WebInputEvent; namespace { bool IsScrollEndEffectEnabled() { - return CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kScrollEndEffect) == "1"; } @@ -212,20 +212,18 @@ bool OverscrollController::ProcessEventForOverscroll( static_cast(event); if (!wheel.hasPreciseScrollingDeltas) break; - - ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX, - wheel.deltaY * wheel.accelerationRatioY, - wheel.type); - event_processed = true; + event_processed = + ProcessOverscroll(wheel.deltaX * wheel.accelerationRatioX, + wheel.deltaY * wheel.accelerationRatioY, + wheel.type); break; } case blink::WebInputEvent::GestureScrollUpdate: { const blink::WebGestureEvent& gesture = static_cast(event); - ProcessOverscroll(gesture.data.scrollUpdate.deltaX, - gesture.data.scrollUpdate.deltaY, - gesture.type); - event_processed = true; + event_processed = ProcessOverscroll(gesture.data.scrollUpdate.deltaX, + gesture.data.scrollUpdate.deltaY, + gesture.type); break; } case blink::WebInputEvent::GestureFlingStart: { @@ -263,7 +261,7 @@ bool OverscrollController::ProcessEventForOverscroll( return event_processed; } -void OverscrollController::ProcessOverscroll(float delta_x, +bool OverscrollController::ProcessOverscroll(float delta_x, float delta_y, blink::WebInputEvent::Type type) { if (scroll_state_ != STATE_CONTENT_SCROLLING) @@ -279,7 +277,7 @@ void OverscrollController::ProcessOverscroll(float delta_x, if (fabs(overscroll_delta_x_) <= horiz_threshold && fabs(overscroll_delta_y_) <= vert_threshold) { SetOverscrollMode(OVERSCROLL_NONE); - return; + return true; } // Compute the current overscroll direction. If the direction is different @@ -306,7 +304,7 @@ void OverscrollController::ProcessOverscroll(float delta_x, SetOverscrollMode(OVERSCROLL_NONE); if (overscroll_mode_ == OVERSCROLL_NONE) - return; + return false; // Tell the delegate about the overscroll update so that it can update // the display accordingly (e.g. show history preview etc.). @@ -332,8 +330,9 @@ void OverscrollController::ProcessOverscroll(float delta_x, } else { delegate_delta_y = 0.f; } - delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y); + return delegate_->OnOverscrollUpdate(delegate_delta_x, delegate_delta_y); } + return false; } void OverscrollController::CompleteAction() { diff --git a/content/browser/renderer_host/overscroll_controller.h b/content/browser/renderer_host/overscroll_controller.h index 5577ce97f3d58..a6287efd3bd75 100644 --- a/content/browser/renderer_host/overscroll_controller.h +++ b/content/browser/renderer_host/overscroll_controller.h @@ -91,8 +91,9 @@ class OverscrollController { // Processes horizontal overscroll. This can update both the overscroll mode // and the over scroll amount (i.e. |overscroll_mode_|, |overscroll_delta_x_| - // and |overscroll_delta_y_|). - void ProcessOverscroll(float delta_x, + // and |overscroll_delta_y_|). Returns true if overscroll was handled by the + // delegate. + bool ProcessOverscroll(float delta_x, float delta_y, blink::WebInputEvent::Type event_type); diff --git a/content/browser/renderer_host/overscroll_controller_delegate.h b/content/browser/renderer_host/overscroll_controller_delegate.h index 59295ce412772..54b5da1885727 100644 --- a/content/browser/renderer_host/overscroll_controller_delegate.h +++ b/content/browser/renderer_host/overscroll_controller_delegate.h @@ -23,8 +23,9 @@ class OverscrollControllerDelegate { // events will only be processed if the visible bounds are non-empty. virtual gfx::Rect GetVisibleBounds() const = 0; - // This is called for each update in the overscroll amount. - virtual void OnOverscrollUpdate(float delta_x, float delta_y) = 0; + // This is called for each update in the overscroll amount. Returns true if + // the delegate consumed the event. + virtual bool OnOverscrollUpdate(float delta_x, float delta_y) = 0; // This is called when the overscroll completes. virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) = 0; diff --git a/content/browser/renderer_host/p2p/socket_host_tcp.cc b/content/browser/renderer_host/p2p/socket_host_tcp.cc index 2955b6182d4dc..1ee1627b5d72d 100644 --- a/content/browser/renderer_host/p2p/socket_host_tcp.cc +++ b/content/browser/renderer_host/p2p/socket_host_tcp.cc @@ -186,16 +186,16 @@ void P2PSocketHostTcpBase::StartTls() { // Default ssl config. const net::SSLConfig ssl_config; net::HostPortPair dest_host_port_pair; - if (remote_address_.ip_address.address().empty()) { - DCHECK(!remote_address_.hostname.empty()); - dest_host_port_pair = net::HostPortPair::FromString( - remote_address_.hostname); + + // Calling net::HostPortPair::FromIPEndPoint will crash if the IP address is + // empty. + if (!remote_address_.ip_address.address().empty()) { + net::HostPortPair::FromIPEndPoint(remote_address_.ip_address); } else { - dest_host_port_pair = net::HostPortPair::FromIPEndPoint( - remote_address_.ip_address); - if (!remote_address_.hostname.empty()) - dest_host_port_pair.set_host(remote_address_.hostname); + dest_host_port_pair.set_port(remote_address_.ip_address.port()); } + if (!remote_address_.hostname.empty()) + dest_host_port_pair.set_host(remote_address_.hostname); net::ClientSocketFactory* socket_factory = net::ClientSocketFactory::GetDefaultFactory(); diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc index 9174d12fad402..13ce2f8190814 100644 --- a/content/browser/renderer_host/render_message_filter.cc +++ b/content/browser/renderer_host/render_message_filter.cc @@ -443,8 +443,8 @@ bool RenderMessageFilter::OnMessageReceived(const IPC::Message& message) { #if defined(OS_ANDROID) IPC_MESSAGE_HANDLER(ViewHostMsg_RunWebAudioMediaCodec, OnWebAudioMediaCodec) #endif - IPC_MESSAGE_HANDLER(FrameHostMsg_SetHasPendingTransitionRequest, - OnSetHasPendingTransitionRequest) + IPC_MESSAGE_HANDLER(FrameHostMsg_AddNavigationTransitionData, + OnAddNavigationTransitionData) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -986,24 +986,10 @@ void RenderMessageFilter::OnFreeTransportDIB( } #endif -bool RenderMessageFilter::CheckPreparsedJsCachingEnabled() const { - static bool checked = false; - static bool result = false; - if (!checked) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - result = command_line.HasSwitch(switches::kEnablePreparsedJsCaching); - checked = true; - } - return result; -} - void RenderMessageFilter::OnCacheableMetadataAvailable( const GURL& url, double expected_response_time, const std::vector& data) { - if (!CheckPreparsedJsCachingEnabled()) - return; - net::HttpCache* cache = request_context_->GetURLRequestContext()-> http_transaction_factory()->GetCache(); DCHECK(cache); @@ -1229,10 +1215,15 @@ void RenderMessageFilter::OnWebAudioMediaCodec( } #endif -void RenderMessageFilter::OnSetHasPendingTransitionRequest(int render_frame_id, - bool is_transition) { - TransitionRequestManager::GetInstance()->SetHasPendingTransitionRequest( - render_process_id_, render_frame_id, is_transition); + +void RenderMessageFilter::OnAddNavigationTransitionData( + int render_frame_id, + const std::string& allowed_destination_host_pattern, + const std::string& selector, + const std::string& markup) { + TransitionRequestManager::GetInstance()->AddPendingTransitionRequestData( + render_process_id_, render_frame_id, allowed_destination_host_pattern, + selector, markup); } } // namespace content diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h index 9764c7beb6b72..b89778527ff85 100644 --- a/content/browser/renderer_host/render_message_filter.h +++ b/content/browser/renderer_host/render_message_filter.h @@ -270,8 +270,11 @@ class RenderMessageFilter : public BrowserMessageFilter { uint32_t data_size); #endif - void OnSetHasPendingTransitionRequest(int render_frame_id, - bool is_transition); + void OnAddNavigationTransitionData( + int render_frame_id, + const std::string& allowed_destination_host_pattern, + const std::string& selector, + const std::string& markup); // Cached resource request dispatcher host and plugin service, guaranteed to // be non-null if Init succeeds. We do not own the objects, they are managed diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index bbd594953831e..516af832c2f5b 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -69,6 +69,7 @@ #include "content/browser/media/midi_host.h" #include "content/browser/message_port_message_filter.h" #include "content/browser/mime_registry_message_filter.h" +#include "content/browser/mojo/mojo_application_host.h" #include "content/browser/plugin_service_impl.h" #include "content/browser/profiler_message_filter.h" #include "content/browser/push_messaging_message_filter.h" @@ -152,6 +153,7 @@ #if defined(OS_ANDROID) #include "content/browser/media/android/browser_demuxer_android.h" #include "content/browser/renderer_host/compositor_impl_android.h" +#include "content/browser/screen_orientation/screen_orientation_message_filter_android.h" #include "content/common/gpu/client/gpu_memory_buffer_impl_surface_texture.h" #endif @@ -319,8 +321,9 @@ class RendererSandboxedProcessLauncherDelegate #elif defined(OS_POSIX) virtual bool ShouldUseZygote() OVERRIDE { - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); - CommandLine::StringType renderer_prefix = + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); + base::CommandLine::StringType renderer_prefix = browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); return renderer_prefix.empty(); } @@ -480,7 +483,7 @@ RenderProcessHostImpl::RenderProcessHostImpl( mark_child_process_activity_time(); if (!GetBrowserContext()->IsOffTheRecord() && - !CommandLine::ForCurrentProcess()->HasSwitch( + !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableGpuShaderDiskCache)) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&CacheShaderInfo, GetID(), @@ -549,7 +552,7 @@ RenderProcessHostImpl::~RenderProcessHostImpl() { UnregisterHost(GetID()); - if (!CommandLine::ForCurrentProcess()->HasSwitch( + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableGpuShaderDiskCache)) { BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, base::Bind(&RemoveShaderInfo, GetID())); @@ -570,11 +573,12 @@ bool RenderProcessHostImpl::Init() { if (channel_) return true; - CommandLine::StringType renderer_prefix; + base::CommandLine::StringType renderer_prefix; #if defined(OS_POSIX) // A command prefix is something prepended to the command line of the spawned // process. It is supported only on POSIX systems. - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); renderer_prefix = browser_command_line.GetSwitchValueNative(switches::kRendererCmdPrefix); #endif // defined(OS_POSIX) @@ -632,7 +636,7 @@ bool RenderProcessHostImpl::Init() { } else { // Build command line for renderer. We call AppendRendererCommandLine() // first so the process type argument will appear first. - CommandLine* cmd_line = new CommandLine(renderer_path); + base::CommandLine* cmd_line = new base::CommandLine(renderer_path); if (!renderer_prefix.empty()) cmd_line->PrependWrapper(renderer_prefix); AppendRendererCommandLine(cmd_line); @@ -676,7 +680,8 @@ void RenderProcessHostImpl::MaybeActivateMojo() { } bool RenderProcessHostImpl::ShouldUseMojoChannel() const { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); return command_line.HasSwitch(switches::kEnableRendererMojoChannel); } @@ -885,7 +890,7 @@ void RenderProcessHostImpl::CreateMessageFilters() { AddFilter(new ProfilerMessageFilter(PROCESS_TYPE_RENDERER)); AddFilter(new HistogramMessageFilter()); #if defined(USE_TCMALLOC) && (defined(OS_LINUX) || defined(OS_ANDROID)) - if (CommandLine::ForCurrentProcess()->HasSwitch( + if (base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kEnableMemoryBenchmarking)) AddFilter(new MemoryBenchmarkMessageFilter()); #endif @@ -893,6 +898,9 @@ void RenderProcessHostImpl::CreateMessageFilters() { AddFilter(new PushMessagingMessageFilter( GetID(), storage_partition_impl_->GetServiceWorkerContext())); AddFilter(new BatteryStatusMessageFilter()); +#if defined(OS_ANDROID) + AddFilter(new ScreenOrientationMessageFilterAndroid()); +#endif } int RenderProcessHostImpl::GetNextRoutingID() { @@ -915,8 +923,7 @@ void RenderProcessHostImpl::NotifyTimezoneChange() { } ServiceRegistry* RenderProcessHostImpl::GetServiceRegistry() { - if (!mojo_application_host_) - return NULL; + DCHECK(mojo_application_host_); return mojo_application_host_->service_registry(); } @@ -932,8 +939,8 @@ void RenderProcessHostImpl::RemoveRoute(int32 routing_id) { #if defined(OS_WIN) // Dump the handle table if handle auditing is enabled. - const CommandLine& browser_command_line = - *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); if (browser_command_line.HasSwitch(switches::kAuditHandles) || browser_command_line.HasSwitch(switches::kAuditAllHandles)) { DumpHandles(); @@ -958,7 +965,7 @@ void RenderProcessHostImpl::RemoveObserver( } void RenderProcessHostImpl::ReceivedBadMessage() { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kDisableKillAfterBadIPC)) return; @@ -1006,7 +1013,7 @@ StoragePartition* RenderProcessHostImpl::GetStoragePartition() const { return storage_partition_impl_; } -static void AppendCompositorCommandLineFlags(CommandLine* command_line) { +static void AppendCompositorCommandLineFlags(base::CommandLine* command_line) { if (IsPinchVirtualViewportEnabled()) command_line->AppendSwitch(cc::switches::kEnablePinchVirtualViewport); @@ -1029,13 +1036,14 @@ static void AppendCompositorCommandLineFlags(CommandLine* command_line) { } void RenderProcessHostImpl::AppendRendererCommandLine( - CommandLine* command_line) const { + base::CommandLine* command_line) const { // Pass the process type first, so it shows first in process listings. command_line->AppendSwitchASCII(switches::kProcessType, switches::kRendererProcess); // Now send any options from our own command line we want to propagate. - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); PropagateBrowserCommandLineToRenderer(browser_command_line, command_line); // Pass on the browser locale. @@ -1068,8 +1076,8 @@ void RenderProcessHostImpl::AppendRendererCommandLine( } void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( - const CommandLine& browser_cmd, - CommandLine* renderer_cmd) const { + const base::CommandLine& browser_cmd, + base::CommandLine* renderer_cmd) const { // Propagate the following switches to the renderer command line (along // with any associated values) if present in the browser command line. static const char* const kSwitchNames[] = { @@ -1145,7 +1153,6 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kEnableOverscrollNotifications, switches::kEnablePinch, switches::kEnablePreciseMemoryInfo, - switches::kEnablePreparsedJsCaching, switches::kEnableRendererMojoChannel, switches::kEnableSeccompFilterSandbox, switches::kEnableSkiaBenchmarking, @@ -1231,6 +1238,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableWebRtcHWDecoding, switches::kDisableWebRtcHWEncoding, switches::kEnableWebRtcHWVp8Encoding, + switches::kEnableWebRtcHWH264Encoding, #endif switches::kLowEndDeviceMode, #if defined(OS_ANDROID) @@ -1249,7 +1257,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kDisableDirectWrite, #endif #if defined(OS_CHROMEOS) - switches::kEnableVaapiAcceleratedVideoEncode, + switches::kDisableVaapiAcceleratedVideoEncode, #endif }; renderer_cmd->CopySwitchesFrom(browser_cmd, kSwitchNames, @@ -1723,7 +1731,7 @@ bool RenderProcessHost::run_renderer_in_process() { void RenderProcessHost::SetRunRendererInProcess(bool value) { g_run_renderer_in_process_ = value; - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (value) { if (!command_line->HasSwitch(switches::kLang)) { // Modify the current process' command line to include the browser locale, @@ -1760,7 +1768,8 @@ bool RenderProcessHost::ShouldTryToUseExistingProcessHost( // from the same site to share, if we knew what the given process was // dedicated to. Allowing no sharing is simpler for now.) This may cause // resource exhaustion issues if too many sites are open at once. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation) || command_line.HasSwitch(switches::kSitePerProcess)) return false; @@ -1817,7 +1826,8 @@ bool RenderProcessHost::ShouldUseProcessPerSite( // the case if the --process-per-site switch is specified, or in // process-per-site-instance for particular sites (e.g., WebUI). // Note that --single-process is handled in ShouldTryToUseExistingProcessHost. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kProcessPerSite)) return true; @@ -1912,6 +1922,8 @@ void RenderProcessHostImpl::ProcessDied(bool already_dead) { RenderProcessExited(this, GetHandle(), status, exit_code)); within_process_died_observer_ = false; + mojo_application_host_->WillDestroySoon(); + child_process_launcher_.reset(); channel_.reset(); gpu_message_filter_ = NULL; @@ -2014,6 +2026,8 @@ void RenderProcessHostImpl::OnShutdownRequest() { Source(this), NotificationService::NoDetails()); + mojo_application_host_->WillDestroySoon(); + Send(new ChildProcessMsg_Shutdown()); } @@ -2141,7 +2155,7 @@ void RenderProcessHostImpl::OnGpuSwitching() { continue; RenderViewHost* rvh = RenderViewHost::From(widget); - rvh->UpdateWebkitPreferences(rvh->GetWebkitPreferences()); + rvh->OnWebkitPreferencesChanged(); } } diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h index 18dac77d98ce8..7d1db32b28434 100644 --- a/content/browser/renderer_host/render_process_host_impl.h +++ b/content/browser/renderer_host/render_process_host_impl.h @@ -15,7 +15,6 @@ #include "base/timer/timer.h" #include "content/browser/child_process_launcher.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" -#include "content/browser/mojo/mojo_application_host.h" #include "content/browser/power_monitor_message_broadcaster.h" #include "content/common/content_export.h" #include "content/common/mojo/service_registry_impl.h" @@ -46,6 +45,7 @@ class AudioRendererHost; class BrowserDemuxerAndroid; class GpuMessageFilter; class MessagePortMessageFilter; +class MojoApplicationHost; #if defined(ENABLE_WEBRTC) class P2PSocketDispatcherHost; #endif diff --git a/content/browser/renderer_host/render_view_host_delegate.cc b/content/browser/renderer_host/render_view_host_delegate.cc index 9c0f17f6c771d..41a110e3f5c2a 100644 --- a/content/browser/renderer_host/render_view_host_delegate.cc +++ b/content/browser/renderer_host/render_view_host_delegate.cc @@ -22,7 +22,7 @@ WebContents* RenderViewHostDelegate::GetAsWebContents() { return NULL; } -WebPreferences RenderViewHostDelegate::GetWebkitPrefs() { +WebPreferences RenderViewHostDelegate::ComputeWebkitPrefs() { return WebPreferences(); } diff --git a/content/browser/renderer_host/render_view_host_delegate.h b/content/browser/renderer_host/render_view_host_delegate.h index 4a441236ce4f1..8c453a3f58fc4 100644 --- a/content/browser/renderer_host/render_view_host_delegate.h +++ b/content/browser/renderer_host/render_view_host_delegate.h @@ -139,9 +139,9 @@ class CONTENT_EXPORT RenderViewHostDelegate { virtual RendererPreferences GetRendererPrefs( BrowserContext* browser_context) const = 0; - // Returns a WebPreferences object that will be used by the renderer + // Computes a WebPreferences object that will be used by the renderer // associated with the owning render view host. - virtual WebPreferences GetWebkitPrefs(); + virtual WebPreferences ComputeWebkitPrefs(); // Notification the user has made a gesture while focus was on the // page. This is used to avoid uninitiated user downloads (aka carpet diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index b03d28fa685ca..ad11596f5cd38 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc @@ -24,7 +24,6 @@ #include "base/values.h" #include "cc/base/switches.h" #include "content/browser/child_process_security_policy_impl.h" -#include "content/browser/cross_site_request_manager.h" #include "content/browser/dom_storage/session_storage_namespace_impl.h" #include "content/browser/frame_host/frame_tree.h" #include "content/browser/gpu/compositor_util.h" @@ -94,6 +93,10 @@ #include "content/browser/media/media_web_contents_observer.h" #endif +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) +#include "xwalk/tizen/browser/media/media_webcontents_observer.h" +#endif + using base::TimeDelta; using blink::WebConsoleMessage; using blink::WebDragOperation; @@ -189,8 +192,6 @@ RenderViewHostImpl::RenderViewHostImpl( instance_(static_cast(instance)), waiting_for_drag_context_response_(false), enabled_bindings_(0), - navigations_suspended_(false), - page_id_(-1), main_frame_routing_id_(main_frame_routing_id), run_modal_reply_msg_(NULL), run_modal_opener_id_(MSG_ROUTING_NONE), @@ -200,7 +201,8 @@ RenderViewHostImpl::RenderViewHostImpl( render_view_termination_status_(base::TERMINATION_STATUS_STILL_RUNNING), virtual_keyboard_requested_(false), weak_factory_(this), - is_focused_element_editable_(false) { + is_focused_element_editable_(false), + updating_web_preferences_(false) { DCHECK(instance_.get()); CHECK(delegate_); // http://crbug.com/82827 @@ -225,6 +227,10 @@ RenderViewHostImpl::RenderViewHostImpl( media_web_contents_observer_.reset(new MediaWebContentsObserver(this)); #endif +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) + media_webcontents_observer_.reset(new tizen::MediaWebContentsObserver(this)); +#endif + unload_event_monitor_timeout_.reset(new TimeoutMonitor(base::Bind( &RenderViewHostImpl::OnSwappedOut, weak_factory_.GetWeakPtr(), true))); } @@ -240,10 +246,6 @@ RenderViewHostImpl::~RenderViewHostImpl() { delegate_->RenderViewDeleted(this); - // Be sure to clean up any leftover state from cross-site requests. - CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest( - GetProcess()->GetID(), GetRoutingID(), false); - // If this was swapped out, it already decremented the active view // count of the SiteInstance it belongs to. if (IsRVHStateActive(rvh_state_)) @@ -284,15 +286,13 @@ bool RenderViewHostImpl::CreateRenderView( // Ensure the RenderView starts with a next_page_id larger than any existing // page ID it might be asked to render. int32 next_page_id = 1; - if (max_page_id > -1) { + if (max_page_id > -1) next_page_id = max_page_id + 1; - page_id_ = max_page_id; - } ViewMsg_New_Params params; params.renderer_preferences = delegate_->GetRendererPrefs(GetProcess()->GetBrowserContext()); - params.web_preferences = delegate_->GetWebkitPrefs(); + params.web_preferences = GetWebkitPreferences(); params.view_id = GetRoutingID(); params.main_frame_routing_id = main_frame_routing_id_; params.surface_id = surface_id(); @@ -332,11 +332,12 @@ void RenderViewHostImpl::SyncRendererPrefs() { GetProcess()->GetBrowserContext()))); } -WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) { +WebPreferences RenderViewHostImpl::ComputeWebkitPrefs(const GURL& url) { TRACE_EVENT0("browser", "RenderViewHostImpl::GetWebkitPrefs"); WebPreferences prefs; - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); prefs.javascript_enabled = !command_line.HasSwitch(switches::kDisableJavaScript); @@ -384,8 +385,6 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) { GpuProcessHost::gpu_enabled() && !command_line.HasSwitch(switches::kDisableFlashStage3d); - prefs.site_specific_quirks_enabled = - !command_line.HasSwitch(switches::kDisableSiteSpecificQuirks); prefs.allow_file_access_from_file_urls = command_line.HasSwitch(switches::kAllowFileAccessFromFiles); @@ -476,6 +475,18 @@ WebPreferences RenderViewHostImpl::GetWebkitPrefs(const GURL& url) { prefs.spatial_navigation_enabled = command_line.HasSwitch( switches::kEnableSpatialNavigation); + if (command_line.HasSwitch(switches::kV8CacheOptions)) { + const std::string v8_cache_options = + command_line.GetSwitchValueASCII(switches::kV8CacheOptions); + if (v8_cache_options == "parse") { + prefs.v8_cache_options = V8_CACHE_OPTIONS_PARSE; + } else if (v8_cache_options == "code") { + prefs.v8_cache_options = V8_CACHE_OPTIONS_CODE; + } else { + prefs.v8_cache_options = V8_CACHE_OPTIONS_OFF; + } + } + GetContentClient()->browser()->OverrideWebkitPrefs(this, url, &prefs); return prefs; } @@ -489,34 +500,6 @@ void RenderViewHostImpl::NavigateToURL(const GURL& url) { delegate_->GetFrameTree()->GetMainFrame()->NavigateToURL(url); } -void RenderViewHostImpl::SetNavigationsSuspended( - bool suspend, - const base::TimeTicks& proceed_time) { - // This should only be called to toggle the state. - DCHECK(navigations_suspended_ != suspend); - - navigations_suspended_ = suspend; - if (!suspend && suspended_nav_params_) { - // There's navigation message params waiting to be sent. Now that we're not - // suspended anymore, resume navigation by sending them. If we were swapped - // out, we should also stop filtering out the IPC messages now. - SetState(STATE_DEFAULT); - - DCHECK(!proceed_time.is_null()); - suspended_nav_params_->browser_navigation_start = proceed_time; - Send(new FrameMsg_Navigate( - main_frame_routing_id_, *suspended_nav_params_.get())); - suspended_nav_params_.reset(); - } -} - -void RenderViewHostImpl::CancelSuspendedNavigations() { - // Clear any state if a pending navigation is canceled or pre-empted. - if (suspended_nav_params_) - suspended_nav_params_.reset(); - navigations_suspended_ = false; -} - void RenderViewHostImpl::SuppressDialogsUntilSwapOut() { Send(new ViewMsg_SuppressDialogsUntilSwapOut(GetRoutingID())); } @@ -637,17 +620,6 @@ void RenderViewHostImpl::ClosePageIgnoringUnloadEvents() { delegate_->Close(this); } -bool RenderViewHostImpl::HasPendingCrossSiteRequest() { - return CrossSiteRequestManager::GetInstance()->HasPendingCrossSiteRequest( - GetProcess()->GetID(), GetRoutingID()); -} - -void RenderViewHostImpl::SetHasPendingCrossSiteRequest( - bool has_pending_request) { - CrossSiteRequestManager::GetInstance()->SetHasPendingCrossSiteRequest( - GetProcess()->GetID(), GetRoutingID(), has_pending_request); -} - #if defined(OS_ANDROID) void RenderViewHostImpl::ActivateNearestFindResult(int request_id, float x, @@ -810,7 +782,8 @@ void RenderViewHostImpl::AllowBindings(int bindings_flags) { static_cast(GetProcess()); // --single-process only has one renderer. if (process->GetActiveViewCount() > 1 && - !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) return; } @@ -1114,7 +1087,6 @@ void RenderViewHostImpl::OnRenderProcessGone(int status, int exit_code) { } void RenderViewHostImpl::OnUpdateState(int32 page_id, const PageState& state) { - CHECK_EQ(page_id_, page_id); // Without this check, the renderer can trick the browser into using // filenames it can't access in a future session restore. if (!CanAccessFilesOfPageState(state)) { @@ -1122,13 +1094,12 @@ void RenderViewHostImpl::OnUpdateState(int32 page_id, const PageState& state) { return; } - delegate_->UpdateState(this, page_id_, state); + delegate_->UpdateState(this, page_id, state); } void RenderViewHostImpl::OnUpdateTargetURL(int32 page_id, const GURL& url) { - CHECK_EQ(page_id_, page_id); if (IsRVHStateActive(rvh_state_)) - delegate_->UpdateTargetURL(page_id_, url); + delegate_->UpdateTargetURL(page_id, url); // Send a notification back to the renderer that we are ready to // receive more target urls. @@ -1423,7 +1394,10 @@ void RenderViewHostImpl::ExitFullscreen() { } WebPreferences RenderViewHostImpl::GetWebkitPreferences() { - return delegate_->GetWebkitPrefs(); + if (!web_preferences_.get()) { + OnWebkitPreferencesChanged(); + } + return *web_preferences_; } void RenderViewHostImpl::DisownOpener() { @@ -1434,9 +1408,21 @@ void RenderViewHostImpl::DisownOpener() { } void RenderViewHostImpl::UpdateWebkitPreferences(const WebPreferences& prefs) { + web_preferences_.reset(new WebPreferences(prefs)); Send(new ViewMsg_UpdateWebPreferences(GetRoutingID(), prefs)); } +void RenderViewHostImpl::OnWebkitPreferencesChanged() { + // This is defensive code to avoid infinite loops due to code run inside + // UpdateWebkitPreferences() accidentally updating more preferences and thus + // calling back into this code. See crbug.com/398751 for one past example. + if (updating_web_preferences_) + return; + updating_web_preferences_ = true; + UpdateWebkitPreferences(delegate_->ComputeWebkitPrefs()); + updating_web_preferences_ = false; +} + void RenderViewHostImpl::GetAudioOutputControllers( const GetAudioOutputControllersCallback& callback) const { AudioRendererHost* audio_host = diff --git a/content/browser/renderer_host/render_view_host_impl.h b/content/browser/renderer_host/render_view_host_impl.h index b099a7b280f63..1a28abfa413a9 100644 --- a/content/browser/renderer_host/render_view_host_impl.h +++ b/content/browser/renderer_host/render_view_host_impl.h @@ -35,6 +35,12 @@ struct ViewHostMsg_ShowPopup_Params; struct FrameMsg_Navigate_Params; struct ViewMsg_PostMessage_Params; +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) +namespace tizen { +class MediaWebContentsObserver; +} +#endif + namespace base { class ListValue; } @@ -204,6 +210,7 @@ class CONTENT_EXPORT RenderViewHostImpl virtual WebPreferences GetWebkitPreferences() OVERRIDE; virtual void UpdateWebkitPreferences( const WebPreferences& prefs) OVERRIDE; + virtual void OnWebkitPreferencesChanged() OVERRIDE; virtual void GetAudioOutputControllers( const GetAudioOutputControllersCallback& callback) const OVERRIDE; virtual void SelectWordAroundCaret() OVERRIDE; @@ -241,7 +248,7 @@ class CONTENT_EXPORT RenderViewHostImpl } // Returns the content specific prefs for this RenderViewHost. - WebPreferences GetWebkitPrefs(const GURL& url); + WebPreferences ComputeWebkitPrefs(const GURL& url); // Sends the given navigation message. Use this rather than sending it // yourself since this does the internal bookkeeping described below. This @@ -259,30 +266,6 @@ class CONTENT_EXPORT RenderViewHostImpl // RenderFrameHostImpl. void NavigateToURL(const GURL& url); - // Returns whether navigation messages are currently suspended for this - // RenderViewHost. Only true during a cross-site navigation, while waiting - // for the onbeforeunload handler. - bool are_navigations_suspended() const { return navigations_suspended_; } - - // Suspends (or unsuspends) any navigation messages from being sent from this - // RenderViewHost. This is called when a pending RenderViewHost is created - // for a cross-site navigation, because we must suspend any navigations until - // we hear back from the old renderer's onbeforeunload handler. Note that it - // is important that only one navigation event happen after calling this - // method with |suspend| equal to true. If |suspend| is false and there is - // a suspended_nav_message_, this will send the message. This function - // should only be called to toggle the state; callers should check - // are_navigations_suspended() first. If |suspend| is false, the time that the - // user decided the navigation should proceed should be passed as - // |proceed_time|. - void SetNavigationsSuspended(bool suspend, - const base::TimeTicks& proceed_time); - - // Clears any suspended navigation state after a cross-site navigation is - // canceled or suspended. This is important if we later return to this - // RenderViewHost. - void CancelSuspendedNavigations(); - // Whether this RenderViewHost has been swapped out to be displayed by a // different process. bool IsSwappedOut() const { return rvh_state_ == STATE_SWAPPED_OUT; } @@ -315,17 +298,6 @@ class CONTENT_EXPORT RenderViewHostImpl // and the user has agreed to continue with closing the page. void ClosePageIgnoringUnloadEvents(); - // Returns whether this RenderViewHost has an outstanding cross-site request. - // Cleared when we hear the response and start to swap out the old - // RenderViewHost, or if we hear a commit here without a network request. - bool HasPendingCrossSiteRequest(); - - // Sets whether this RenderViewHost has an outstanding cross-site request, - // for which another renderer will need to run an onunload event handler. - // This is called before the first navigation event for this RenderViewHost, - // and cleared when we hear the response or commit. - void SetHasPendingCrossSiteRequest(bool has_pending_request); - // Tells the renderer view to focus the first (last if reverse is true) node. void SetInitialFocus(bool reverse); @@ -533,24 +505,6 @@ class CONTENT_EXPORT RenderViewHostImpl // See BindingsPolicy for details. int enabled_bindings_; - // Whether we should buffer outgoing Navigate messages rather than sending - // them. This will be true when a RenderViewHost is created for a cross-site - // request, until we hear back from the onbeforeunload handler of the old - // RenderViewHost. - // TODO(nasko): Move to RenderFrameHost, as this is per-frame state. - bool navigations_suspended_; - - // We only buffer the params for a suspended navigation while we have a - // pending RVH for a WebContentsImpl. There will only ever be one suspended - // navigation, because WebContentsImpl will destroy the pending RVH and create - // a new one if a second navigation occurs. - // TODO(nasko): Move to RenderFrameHost, as this is per-frame state. - scoped_ptr suspended_nav_params_; - - // The most recent page ID we've heard from the renderer process. This is - // used as context when other session history related IPCs arrive. - // TODO(creis): Allocate this in WebContents/NavigationController instead. - int32 page_id_; // The current state of this RVH. // TODO(nasko): Move to RenderFrameHost, as this is per-frame state. @@ -596,6 +550,11 @@ class CONTENT_EXPORT RenderViewHostImpl scoped_ptr media_web_contents_observer_; #endif +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) + // Manages all the media player managers and forwards IPCs to them. + scoped_ptr media_webcontents_observer_; +#endif + // Used to swap out or shutdown this RVH when the unload event is taking too // long to execute, depending on the number of active views in the // SiteInstance. @@ -612,6 +571,13 @@ class CONTENT_EXPORT RenderViewHostImpl // True if the current focused element is editable. bool is_focused_element_editable_; + // This is updated every time UpdateWebkitPreferences is called. That method + // is in turn called when any of the settings change that the WebPreferences + // values depend on. + scoped_ptr web_preferences_; + + bool updating_web_preferences_; + DISALLOW_COPY_AND_ASSIGN(RenderViewHostImpl); }; diff --git a/content/browser/renderer_host/render_view_host_unittest.cc b/content/browser/renderer_host/render_view_host_unittest.cc index d765481705636..6889cd6f0bd12 100644 --- a/content/browser/renderer_host/render_view_host_unittest.cc +++ b/content/browser/renderer_host/render_view_host_unittest.cc @@ -214,12 +214,12 @@ TEST_F(RenderViewHostTest, MessageWithBadHistoryItemFiles) { EXPECT_TRUE(PathService::Get(base::DIR_TEMP, &file_path)); file_path = file_path.AppendASCII("foo"); EXPECT_EQ(0, process()->bad_msg_count()); - test_rvh()->TestOnUpdateStateWithFile(-1, file_path); + test_rvh()->TestOnUpdateStateWithFile(process()->GetID(), file_path); EXPECT_EQ(1, process()->bad_msg_count()); ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile( process()->GetID(), file_path); - test_rvh()->TestOnUpdateStateWithFile(-1, file_path); + test_rvh()->TestOnUpdateStateWithFile(process()->GetID(), file_path); EXPECT_EQ(1, process()->bad_msg_count()); } diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 67afdf557d907..4b361260b777c 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -226,7 +226,7 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, RenderViewHostImpl* rvh = static_cast( IsRenderView() ? RenderViewHost::From(this) : NULL); if (BrowserPluginGuest::IsGuest(rvh) || - !CommandLine::ForCurrentProcess()->HasSwitch( + !base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDisableHangMonitor)) { hang_monitor_timeout_.reset(new TimeoutMonitor( base::Bind(&RenderWidgetHostImpl::RendererIsUnresponsive, @@ -235,6 +235,8 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, } RenderWidgetHostImpl::~RenderWidgetHostImpl() { + if (view_weak_) + view_weak_->RenderWidgetHostGone(); SetView(NULL); GpuSurfaceTracker::Get()->RemoveSurface(surface_id_); @@ -310,6 +312,10 @@ RenderWidgetHostImpl* RenderWidgetHostImpl::From(RenderWidgetHost* rwh) { } void RenderWidgetHostImpl::SetView(RenderWidgetHostViewBase* view) { + if (view) + view_weak_ = view->GetWeakPtr(); + else + view_weak_.reset(); view_ = view; GpuSurfaceTracker::Get()->SetSurfaceHandle( @@ -512,7 +518,7 @@ void RenderWidgetHostImpl::WasHidden() { Details(&is_visible)); } -void RenderWidgetHostImpl::WasShown() { +void RenderWidgetHostImpl::WasShown(const ui::LatencyInfo& latency_info) { if (!is_hidden_) return; is_hidden_ = false; @@ -522,7 +528,7 @@ void RenderWidgetHostImpl::WasShown() { // Always repaint on restore. bool needs_repainting = true; needs_repainting_on_restore_ = false; - Send(new ViewMsg_WasShown(routing_id_, needs_repainting)); + Send(new ViewMsg_WasShown(routing_id_, needs_repainting, latency_info)); process_->WidgetRestored(); @@ -1198,6 +1204,18 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status, // Reset some fields in preparation for recovering from a crash. ResetSizeAndRepaintPendingFlags(); current_size_.SetSize(0, 0); + // After the renderer crashes, the view is destroyed and so the + // RenderWidgetHost cannot track its visibility anymore. We assume such + // RenderWidgetHost to be visible for the sake of internal accounting - be + // careful about changing this - see http://crbug.com/401859. + // + // We need to at least make sure that the RenderProcessHost is notified about + // the |is_hidden_| change, so that the renderer will have correct visibility + // set when respawned. + if (is_hidden_) { + process_->WidgetRestored(); + is_hidden_ = false; + } // Reset this to ensure the hung renderer mechanism is working properly. in_flight_event_count_ = 0; @@ -1206,7 +1224,8 @@ void RenderWidgetHostImpl::RendererExited(base::TerminationStatus status, GpuSurfaceTracker::Get()->SetSurfaceHandle(surface_id_, gfx::GLSurfaceHandle()); view_->RenderProcessGone(status, exit_code); - view_ = NULL; // The View should be deleted by RenderProcessGone. + view_ = NULL; // The View should be deleted by RenderProcessGone. + view_weak_.reset(); } // Reconstruct the input router to ensure that it has fresh state for a new @@ -1562,7 +1581,7 @@ void RenderWidgetHostImpl::DidUpdateBackingStore( void RenderWidgetHostImpl::OnQueueSyntheticGesture( const SyntheticGesturePacket& gesture_packet) { // Only allow untrustworthy gestures if explicitly enabled. - if (!CommandLine::ForCurrentProcess()->HasSwitch( + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( cc::switches::kEnableGpuBenchmarking)) { RecordAction(base::UserMetricsAction("BadMessageTerminate_RWH7")); GetProcess()->ReceivedBadMessage(); @@ -2093,14 +2112,28 @@ void RenderWidgetHostImpl::FrameSwapped(const ui::LatencyInfo& latency_info) { #endif } - ui::LatencyInfo::LatencyComponent rwh_component; ui::LatencyInfo::LatencyComponent swap_component; + if (!latency_info.FindLatency( + ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, + 0, + &swap_component)) { + return; + } + ui::LatencyInfo::LatencyComponent tab_switch_component; + if (latency_info.FindLatency(ui::TAB_SHOW_COMPONENT, + GetLatencyComponentId(), + &tab_switch_component)) { + base::TimeDelta delta = + swap_component.event_time - tab_switch_component.event_time; + for (size_t i = 0; i < tab_switch_component.event_count; i++) { + UMA_HISTOGRAM_TIMES("MPArch.RWH_TabSwitchPaintDuration", delta); + } + } + + ui::LatencyInfo::LatencyComponent rwh_component; if (!latency_info.FindLatency(ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT, GetLatencyComponentId(), - &rwh_component) || - !latency_info.FindLatency( - ui::INPUT_EVENT_LATENCY_TERMINATED_FRAME_SWAP_COMPONENT, - 0, &swap_component)) { + &rwh_component)) { return; } @@ -2152,7 +2185,8 @@ void RenderWidgetHostImpl::WindowOldSnapshotReachedScreen(int snapshot_id) { // This feature is behind the kEnableGpuBenchmarking command line switch // because it poses security concerns and should only be used for testing. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (!command_line.HasSwitch(cc::switches::kEnableGpuBenchmarking)) { Send(new ViewMsg_WindowSnapshotCompleted( GetRoutingID(), snapshot_id, gfx::Size(), png)); @@ -2239,7 +2273,8 @@ void RenderWidgetHostImpl::CompositorFrameDrawn( ++b) { if (b->first.first == ui::INPUT_EVENT_LATENCY_BEGIN_RWH_COMPONENT || b->first.first == ui::WINDOW_SNAPSHOT_FRAME_NUMBER_COMPONENT || - b->first.first == ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT) { + b->first.first == ui::WINDOW_OLD_SNAPSHOT_FRAME_NUMBER_COMPONENT || + b->first.first == ui::TAB_SHOW_COMPONENT) { // Matches with GetLatencyComponentId int routing_id = b->first.second & 0xffffffff; int process_id = (b->first.second >> 32) & 0xffffffff; diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 7077315c9d7a4..a47e9d7813f69 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h @@ -208,7 +208,7 @@ class CONTENT_EXPORT RenderWidgetHostImpl // Called to notify the RenderWidget that it has been hidden or restored from // having been hidden. void WasHidden(); - void WasShown(); + void WasShown(const ui::LatencyInfo& latency_info); // Returns true if the RenderWidget is hidden. bool is_hidden() const { return is_hidden_; } @@ -562,6 +562,12 @@ class CONTENT_EXPORT RenderWidgetHostImpl // doing so). RenderWidgetHostViewBase* view_; + // A weak pointer to the view. The above pointer should be weak, but changing + // that to be weak causes crashes on Android. + // TODO(ccameron): Fix this. + // http://crbug.com/404828 + base::WeakPtr view_weak_; + // true if a renderer has once been valid. We use this flag to display a sad // tab only when we lose our renderer and not if a paint occurs during // initialization. @@ -696,7 +702,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl // Indicates whether a page is loading or not. bool is_loading_; - // Indicates whether a page is hidden or not. + // Indicates whether a page is hidden or not. It has to stay in sync with the + // most recent call to process_->WidgetRestored() / WidgetHidden(). bool is_hidden_; // Indicates whether a page is fullscreen or not. diff --git a/content/browser/renderer_host/render_widget_host_unittest.cc b/content/browser/renderer_host/render_widget_host_unittest.cc index afbd298cc52cd..c65276a1bcf05 100644 --- a/content/browser/renderer_host/render_widget_host_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_unittest.cc @@ -431,7 +431,7 @@ class RenderWidgetHostTest : public testing::Test { protected: // testing::Test virtual void SetUp() { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitch(switches::kValidateInputEventStream); browser_context_.reset(new TestBrowserContext()); @@ -808,14 +808,14 @@ TEST_F(RenderWidgetHostTest, HiddenPaint) { // Now unhide. process_->sink().ClearMessages(); - host_->WasShown(); + host_->WasShown(ui::LatencyInfo()); EXPECT_FALSE(host_->is_hidden_); // It should have sent out a restored message with a request to paint. const IPC::Message* restored = process_->sink().GetUniqueMessageMatching( ViewMsg_WasShown::ID); ASSERT_TRUE(restored); - Tuple1 needs_repaint; + Tuple2 needs_repaint; ViewMsg_WasShown::Read(restored, &needs_repaint); EXPECT_TRUE(needs_repaint.a); } @@ -1420,4 +1420,18 @@ TEST_F(RenderWidgetHostTest, RendererExitedResetsInputRouter) { ASSERT_FALSE(host_->input_router()->HasPendingEvents()); } +// Regression test for http://crbug.com/401859. +TEST_F(RenderWidgetHostTest, RendererExitedResetsIsHidden) { + // RendererExited will delete the view. + host_->SetView(new TestView(host_.get())); + host_->WasHidden(); + + ASSERT_TRUE(host_->is_hidden()); + host_->RendererExited(base::TERMINATION_STATUS_PROCESS_CRASHED, -1); + ASSERT_FALSE(host_->is_hidden()); + + // Make sure the input router is in a fresh state. + ASSERT_FALSE(host_->input_router()->HasPendingEvents()); +} + } // namespace content diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc index 5f595ae6484e6..49ae253d76655 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.cc +++ b/content/browser/renderer_host/render_widget_host_view_android.cc @@ -6,6 +6,7 @@ #include +#include "base/android/build_info.h" #include "base/basictypes.h" #include "base/bind.h" #include "base/callback_helpers.h" @@ -29,6 +30,8 @@ #include "content/browser/accessibility/browser_accessibility_manager_android.h" #include "content/browser/android/composited_touch_handle_drawable.h" #include "content/browser/android/content_view_core_impl.h" +#include "content/browser/android/edge_effect.h" +#include "content/browser/android/edge_effect_l.h" #include "content/browser/android/in_process/synchronous_compositor_impl.h" #include "content/browser/android/overscroll_glow.h" #include "content/browser/devtools/render_view_devtools_agent_host.h" @@ -83,6 +86,9 @@ const int kUndefinedOutputSurfaceId = -1; // V1 saw errors of ~0.065 between computed window and content widths. const float kMobileViewportWidthEpsilon = 0.15f; +// Used for conditional creation of EdgeEffect types for overscroll. +const int kKitKatMR2SDKVersion = 19; + static const char kAsyncReadBackString[] = "Compositing.CopyFromSurfaceTime"; // Sends an acknowledgement to the renderer of a processed IME event. @@ -100,7 +106,14 @@ void CopyFromCompositingSurfaceFinished( TRACE_EVENT0( "cc", "RenderWidgetHostViewAndroid::CopyFromCompositingSurfaceFinished"); bitmap_pixels_lock.reset(); - release_callback->Run(0, false); + uint32 sync_point = 0; + if (result) { + GLHelper* gl_helper = + ImageTransportFactoryAndroid::GetInstance()->GetGLHelper(); + sync_point = gl_helper->InsertSyncPoint(); + } + bool lost_resource = sync_point == 0; + release_callback->Run(sync_point, lost_resource); UMA_HISTOGRAM_TIMES(kAsyncReadBackString, base::TimeTicks::Now() - start_time); callback.Run(result, *bitmap); @@ -133,27 +146,66 @@ OverscrollGlow::DisplayParameters CreateOverscrollDisplayParameters( OverscrollGlow::DisplayParameters params; params.size = gfx::ScaleSize( frame_metadata.scrollable_viewport_size, scale_factor); - params.edge_offsets[EdgeEffect::EDGE_TOP] = + params.edge_offsets[OverscrollGlow::EDGE_TOP] = -frame_metadata.root_scroll_offset.y() * scale_factor; - params.edge_offsets[EdgeEffect::EDGE_LEFT] = + params.edge_offsets[OverscrollGlow::EDGE_LEFT] = -frame_metadata.root_scroll_offset.x() * scale_factor; - params.edge_offsets[EdgeEffect::EDGE_BOTTOM] = + params.edge_offsets[OverscrollGlow::EDGE_BOTTOM] = (frame_metadata.root_layer_size.height() - frame_metadata.root_scroll_offset.y() - - frame_metadata.scrollable_viewport_size.height()) * scale_factor; - params.edge_offsets[EdgeEffect::EDGE_RIGHT] = + frame_metadata.scrollable_viewport_size.height()) * + scale_factor; + params.edge_offsets[OverscrollGlow::EDGE_RIGHT] = (frame_metadata.root_layer_size.width() - frame_metadata.root_scroll_offset.x() - - frame_metadata.scrollable_viewport_size.width()) * scale_factor; - params.device_scale_factor = frame_metadata.device_scale_factor; + frame_metadata.scrollable_viewport_size.width()) * + scale_factor; return params; } +bool UseEdgeEffectL() { + static bool use_edge_effect_l = + base::android::BuildInfo::GetInstance()->sdk_int() > kKitKatMR2SDKVersion; + return use_edge_effect_l; +} + +scoped_ptr CreateEdgeEffect( + ui::SystemUIResourceManager* resource_manager, + float device_scale_factor) { + DCHECK(resource_manager); + if (UseEdgeEffectL()) + return scoped_ptr(new EdgeEffectL(resource_manager)); + + return scoped_ptr( + new EdgeEffect(resource_manager, device_scale_factor)); +} + +scoped_ptr CreateOverscrollEffect( + ContentViewCore* content_view_core) { + DCHECK(content_view_core); + ui::WindowAndroidCompositor* compositor = + content_view_core->GetWindowAndroid()->GetCompositor(); + DCHECK(compositor); + ui::SystemUIResourceManager* system_resource_manager = + &compositor->GetSystemUIResourceManager(); + + if (UseEdgeEffectL()) + EdgeEffectL::PreloadResources(system_resource_manager); + else + EdgeEffect::PreloadResources(system_resource_manager); + + return make_scoped_ptr( + new OverscrollGlow(base::Bind(&CreateEdgeEffect, + system_resource_manager, + content_view_core->GetDpiScale()))); +} + ui::GestureProvider::Config CreateGestureProviderConfig() { ui::GestureProvider::Config config = ui::DefaultGestureProviderConfig(); config.disable_click_delay = - CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableClickDelay); + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableClickDelay); return config; } @@ -183,20 +235,20 @@ RenderWidgetHostViewAndroid::RenderWidgetHostViewAndroid( RenderWidgetHostImpl* widget_host, ContentViewCoreImpl* content_view_core) : host_(widget_host), - needs_begin_frame_(false), + outstanding_vsync_requests_(0), is_showing_(!widget_host->is_hidden()), content_view_core_(NULL), ime_adapter_android_(this), cached_background_color_(SK_ColorWHITE), last_output_surface_id_(kUndefinedOutputSurfaceId), weak_ptr_factory_(this), - overscroll_effect_enabled_(!CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableOverscrollEdgeEffect)), + overscroll_effect_enabled_( + !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableOverscrollEdgeEffect)), gesture_provider_(CreateGestureProviderConfig(), this), gesture_text_selector_(this), touch_scrolling_(false), potentially_active_fling_count_(0), - flush_input_requested_(false), accelerated_surface_route_id_(0), using_synchronous_compositor_(SynchronousCompositorImpl::FromID( widget_host->GetProcess()->GetID(), @@ -257,12 +309,11 @@ void RenderWidgetHostViewAndroid::WasShown() { if (!host_ || !host_->is_hidden()) return; - host_->WasShown(); + host_->WasShown(ui::LatencyInfo()); - if (content_view_core_ && !using_synchronous_compositor_) { - content_view_core_->GetWindowAndroid()->AddObserver(this); - content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); - observing_root_window_ = true; + if (content_view_core_) { + StartObservingRootWindow(); + RequestVSyncUpdate(BEGIN_FRAME); } } @@ -276,10 +327,7 @@ void RenderWidgetHostViewAndroid::WasHidden() { // utilization. host_->WasHidden(); - if (content_view_core_ && !using_synchronous_compositor_) { - content_view_core_->GetWindowAndroid()->RemoveObserver(this); - observing_root_window_ = false; - } + StopObservingRootWindow(); } void RenderWidgetHostViewAndroid::WasResized() { @@ -534,7 +582,7 @@ void RenderWidgetHostViewAndroid::TextInputStateChanged( content_view_core_->UpdateImeAdapter( GetNativeImeAdapter(), - static_cast(params.type), + static_cast(params.type), params.flags, params.value, params.selection_start, params.selection_end, params.composition_start, params.composition_end, params.show_ime_if_needed, params.is_non_ime_change); @@ -551,15 +599,13 @@ void RenderWidgetHostViewAndroid::OnDidChangeBodyBackgroundColor( } void RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame(bool enabled) { - if (enabled == needs_begin_frame_) - return; - + DCHECK(!using_synchronous_compositor_); TRACE_EVENT1("cc", "RenderWidgetHostViewAndroid::OnSetNeedsBeginFrame", "enabled", enabled); - if (content_view_core_ && enabled) - content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); - - needs_begin_frame_ = enabled; + if (enabled) + RequestVSyncUpdate(PERSISTENT_BEGIN_FRAME); + else + outstanding_vsync_requests_ &= ~PERSISTENT_BEGIN_FRAME; } void RenderWidgetHostViewAndroid::OnStartContentIntent( @@ -1007,10 +1053,10 @@ void RenderWidgetHostViewAndroid::SynchronousFrameMetadata( ComputeContentsSize(frame_metadata); // DevTools ScreenCast support for Android WebView. - if (DevToolsAgentHost::HasFor(RenderViewHost::From(GetRenderWidgetHost()))) { + WebContents* web_contents = content_view_core_->GetWebContents(); + if (DevToolsAgentHost::HasFor(web_contents)) { scoped_refptr dtah = - DevToolsAgentHost::GetOrCreateFor( - RenderViewHost::From(GetRenderWidgetHost())); + DevToolsAgentHost::GetOrCreateFor(web_contents); // Unblock the compositor. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -1162,6 +1208,64 @@ void RenderWidgetHostViewAndroid::RemoveLayers() { overscroll_effect_->Disable(); } +void RenderWidgetHostViewAndroid::RequestVSyncUpdate(uint32 requests) { + // The synchronous compositor does not requre BeginFrame messages. + if (using_synchronous_compositor_) + requests &= FLUSH_INPUT; + + bool should_request_vsync = !outstanding_vsync_requests_ && requests; + outstanding_vsync_requests_ |= requests; + // Note that if we're not currently observing the root window, outstanding + // vsync requests will be pushed if/when we resume observing in + // |StartObservingRootWindow()|. + if (observing_root_window_ && should_request_vsync) + content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); +} + +void RenderWidgetHostViewAndroid::StartObservingRootWindow() { + DCHECK(content_view_core_); + if (observing_root_window_) + return; + + observing_root_window_ = true; + content_view_core_->GetWindowAndroid()->AddObserver(this); + + // Clear existing vsync requests to allow a request to the new window. + uint32 outstanding_vsync_requests = outstanding_vsync_requests_; + outstanding_vsync_requests_ = 0; + RequestVSyncUpdate(outstanding_vsync_requests); +} + +void RenderWidgetHostViewAndroid::StopObservingRootWindow() { + if (!content_view_core_) { + DCHECK(!observing_root_window_); + return; + } + + if (!observing_root_window_) + return; + + observing_root_window_ = false; + content_view_core_->GetWindowAndroid()->RemoveObserver(this); +} + +void RenderWidgetHostViewAndroid::SendBeginFrame(base::TimeTicks frame_time, + base::TimeDelta vsync_period) { + TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame"); + base::TimeTicks display_time = frame_time + vsync_period; + + // TODO(brianderson): Use adaptive draw-time estimation. + base::TimeDelta estimated_browser_composite_time = + base::TimeDelta::FromMicroseconds( + (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60)); + + base::TimeTicks deadline = display_time - estimated_browser_composite_time; + + host_->Send(new ViewMsg_BeginFrame( + host_->GetRoutingID(), + cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period))); +} + bool RenderWidgetHostViewAndroid::Animate(base::TimeTicks frame_time) { bool needs_animate = overscroll_effect_ ? overscroll_effect_->Animate(frame_time) : false; @@ -1235,6 +1339,13 @@ void RenderWidgetHostViewAndroid::ProcessAckedTouchEvent( void RenderWidgetHostViewAndroid::GestureEventAck( const blink::WebGestureEvent& event, InputEventAckState ack_result) { + // The overscroll effect requires an explicit release signal that may not be + // sent from the renderer compositor. + if (event.type == blink::WebInputEvent::GestureScrollEnd || + event.type == blink::WebInputEvent::GestureFlingStart) { + DidOverscroll(DidOverscrollParams()); + } + switch (event.type) { case blink::WebInputEvent::GestureScrollBegin: touch_scrolling_ = true; @@ -1300,11 +1411,8 @@ InputEventAckState RenderWidgetHostViewAndroid::FilterInputEvent( } void RenderWidgetHostViewAndroid::OnSetNeedsFlushInput() { - if (flush_input_requested_ || !content_view_core_) - return; TRACE_EVENT0("input", "RenderWidgetHostViewAndroid::OnSetNeedsFlushInput"); - flush_input_requested_ = true; - content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); + RequestVSyncUpdate(FLUSH_INPUT); } BrowserAccessibilityManager* @@ -1345,9 +1453,8 @@ void RenderWidgetHostViewAndroid::SendTouchEvent( // This is good enough as long as the first touch event has Begin semantics // and the actual scroll happens on the next vsync. // TODO: Is this actually still needed? - if (content_view_core_) { - content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); - } + if (observing_root_window_) + RequestVSyncUpdate(BEGIN_FRAME); } void RenderWidgetHostViewAndroid::SendMouseEvent( @@ -1422,7 +1529,10 @@ void RenderWidgetHostViewAndroid::DidOverscroll( gfx::ScaleVector2d(params.latest_overscroll_delta, device_scale_factor), gfx::ScaleVector2d(params.current_fling_velocity, - device_scale_factor))) { + device_scale_factor), + gfx::ScaleVector2d( + params.causal_event_viewport_point.OffsetFromOrigin(), + device_scale_factor))) { SetNeedsAnimate(); } } @@ -1440,13 +1550,11 @@ void RenderWidgetHostViewAndroid::DidStopFlinging() { void RenderWidgetHostViewAndroid::SetContentViewCore( ContentViewCoreImpl* content_view_core) { RemoveLayers(); - if (observing_root_window_ && content_view_core_) { - content_view_core_->GetWindowAndroid()->RemoveObserver(this); - observing_root_window_ = false; - } + StopObservingRootWindow(); bool resize = false; if (content_view_core != content_view_core_) { + overscroll_effect_.reset(); selection_controller_.reset(); ReleaseLocksOnSurface(); resize = true; @@ -1469,12 +1577,7 @@ void RenderWidgetHostViewAndroid::SetContentViewCore( if (!content_view_core_) return; - if (!using_synchronous_compositor_) { - content_view_core_->GetWindowAndroid()->AddObserver(this); - observing_root_window_ = true; - if (needs_begin_frame_) - content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); - } + StartObservingRootWindow(); if (resize) WasResized(); @@ -1482,15 +1585,9 @@ void RenderWidgetHostViewAndroid::SetContentViewCore( if (!selection_controller_) selection_controller_.reset(new TouchSelectionController(this)); - if (!content_view_core_) { - overscroll_effect_.reset(); - } else if (overscroll_effect_enabled_ && !overscroll_effect_) { - DCHECK(content_view_core_->GetWindowAndroid()->GetCompositor()); - overscroll_effect_ = - OverscrollGlow::Create(&content_view_core_->GetWindowAndroid() - ->GetCompositor() - ->GetSystemUIResourceManager()); - } + if (overscroll_effect_enabled_ && !overscroll_effect_ && + content_view_core_->GetWindowAndroid()->GetCompositor()) + overscroll_effect_ = CreateOverscrollEffect(content_view_core_); } void RenderWidgetHostViewAndroid::RunAckCallbacks() { @@ -1512,10 +1609,18 @@ void RenderWidgetHostViewAndroid::OnCompositingDidCommit() { RunAckCallbacks(); } + +void RenderWidgetHostViewAndroid::OnAttachCompositor() { + DCHECK(content_view_core_); + if (overscroll_effect_enabled_ && !overscroll_effect_) + overscroll_effect_ = CreateOverscrollEffect(content_view_core_); +} + void RenderWidgetHostViewAndroid::OnDetachCompositor() { DCHECK(content_view_core_); DCHECK(!using_synchronous_compositor_); RunAckCallbacks(); + overscroll_effect_.reset(); } void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time, @@ -1524,27 +1629,19 @@ void RenderWidgetHostViewAndroid::OnVSync(base::TimeTicks frame_time, if (!host_) return; - if (flush_input_requested_) { - flush_input_requested_ = false; - host_->FlushInput(); - } - - TRACE_EVENT0("cc", "RenderWidgetHostViewAndroid::SendBeginFrame"); - base::TimeTicks display_time = frame_time + vsync_period; - - // TODO(brianderson): Use adaptive draw-time estimation. - base::TimeDelta estimated_browser_composite_time = - base::TimeDelta::FromMicroseconds( - (1.0f * base::Time::kMicrosecondsPerSecond) / (3.0f * 60)); + const uint32 current_vsync_requests = outstanding_vsync_requests_; + outstanding_vsync_requests_ = 0; - base::TimeTicks deadline = display_time - estimated_browser_composite_time; + if (current_vsync_requests & FLUSH_INPUT) + host_->FlushInput(); - host_->Send(new ViewMsg_BeginFrame( - host_->GetRoutingID(), - cc::BeginFrameArgs::Create(frame_time, deadline, vsync_period))); + if (current_vsync_requests & BEGIN_FRAME || + current_vsync_requests & PERSISTENT_BEGIN_FRAME) { + SendBeginFrame(frame_time, vsync_period); + } - if (needs_begin_frame_) - content_view_core_->GetWindowAndroid()->RequestVSyncUpdate(); + if (current_vsync_requests & PERSISTENT_BEGIN_FRAME) + RequestVSyncUpdate(PERSISTENT_BEGIN_FRAME); } void RenderWidgetHostViewAndroid::OnAnimate(base::TimeTicks begin_frame_time) { diff --git a/content/browser/renderer_host/render_widget_host_view_android.h b/content/browser/renderer_host/render_widget_host_view_android.h index 91f9d483731a9..6f5963b3d9144 100644 --- a/content/browser/renderer_host/render_widget_host_view_android.h +++ b/content/browser/renderer_host/render_widget_host_view_android.h @@ -177,7 +177,7 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid // ui::WindowAndroidObserver implementation. virtual void OnCompositingDidCommit() OVERRIDE; - virtual void OnAttachCompositor() OVERRIDE {} + virtual void OnAttachCompositor() OVERRIDE; virtual void OnDetachCompositor() OVERRIDE; virtual void OnVSync(base::TimeTicks frame_time, base::TimeDelta vsync_period) OVERRIDE; @@ -310,6 +310,15 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid void InternalSwapCompositorFrame(uint32 output_surface_id, scoped_ptr frame); + enum VSyncRequestType { + FLUSH_INPUT = 1 << 0, + BEGIN_FRAME = 1 << 1, + PERSISTENT_BEGIN_FRAME = 1 << 2 + }; + void RequestVSyncUpdate(uint32 requests); + void StartObservingRootWindow(); + void StopObservingRootWindow(); + void SendBeginFrame(base::TimeTicks frame_time, base::TimeDelta vsync_period); bool Animate(base::TimeTicks frame_time); void OnContentScrollingChange(); @@ -320,8 +329,8 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid // The model object. RenderWidgetHostImpl* host_; - // Used to track whether this render widget needs a BeginFrame. - bool needs_begin_frame_; + // Used to control action dispatch at the next |OnVSync()| call. + uint32 outstanding_vsync_requests_; bool is_showing_; @@ -343,9 +352,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid // The most recent content size that was pushed to the texture layer. gfx::Size content_size_in_layer_; - // The device scale of the last received frame. - float device_scale_factor_; - // The output surface id of the last received frame. uint32_t last_output_surface_id_; @@ -370,8 +376,6 @@ class CONTENT_EXPORT RenderWidgetHostViewAndroid bool touch_scrolling_; size_t potentially_active_fling_count_; - bool flush_input_requested_; - int accelerated_surface_route_id_; // Size to use if we have no backing ContentViewCore diff --git a/content/browser/renderer_host/render_widget_host_view_aura.cc b/content/browser/renderer_host/render_widget_host_view_aura.cc index eee2df69b7675..0d41c2a5218cb 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura.cc @@ -462,7 +462,7 @@ RenderWidgetHostViewAura::RenderWidgetHostViewAura(RenderWidgetHost* host) window_->set_layer_owner_delegate(delegated_frame_host_.get()); gfx::Screen::GetScreenFor(window_)->AddObserver(this); - bool overscroll_enabled = CommandLine::ForCurrentProcess()-> + bool overscroll_enabled = base::CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0"; SetOverscrollControllerEnabled(overscroll_enabled); } @@ -559,7 +559,17 @@ void RenderWidgetHostViewAura::WasShown() { DCHECK(host_); if (!host_->is_hidden()) return; - host_->WasShown(); + + bool has_saved_frame = delegated_frame_host_->HasSavedFrame(); + ui::LatencyInfo renderer_latency_info, browser_latency_info; + if (has_saved_frame) { + browser_latency_info.AddLatencyNumber( + ui::TAB_SHOW_COMPONENT, host_->GetLatencyComponentId(), 0); + } else { + renderer_latency_info.AddLatencyNumber( + ui::TAB_SHOW_COMPONENT, host_->GetLatencyComponentId(), 0); + } + host_->WasShown(renderer_latency_info); aura::Window* root = window_->GetRootWindow(); if (root) { @@ -569,7 +579,7 @@ void RenderWidgetHostViewAura::WasShown() { NotifyRendererOfCursorVisibilityState(cursor_client->IsCursorVisible()); } - delegated_frame_host_->WasShown(); + delegated_frame_host_->WasShown(browser_latency_info); #if defined(OS_WIN) if (legacy_render_widget_host_HWND_) { diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc index 49f017879e028..506b02e6722a1 100644 --- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc +++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc @@ -123,9 +123,10 @@ class TestOverscrollDelegate : public OverscrollControllerDelegate { return view_->IsShowing() ? view_->GetViewBounds() : gfx::Rect(); } - virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE { + virtual bool OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE { delta_x_ = delta_x; delta_y_ = delta_y; + return true; } virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE { @@ -1159,7 +1160,8 @@ scoped_ptr MakeDelegatedFrame(float scale_factor, } // Resizing in fullscreen mode should send the up-to-date screen info. -TEST_F(RenderWidgetHostViewAuraTest, FullscreenResize) { +// http://crbug.com/324350 +TEST_F(RenderWidgetHostViewAuraTest, DISABLED_FullscreenResize) { aura::Window* root_window = aura_test_helper_->root_window(); root_window->SetLayoutManager(new FullscreenLayoutManager(root_window)); view_->InitAsFullscreen(parent_view_); @@ -2533,9 +2535,10 @@ TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) { EXPECT_EQ(0U, sink_->message_count()); // Send another update event, but in the reverse direction. The overscroll - // controller will consume the event, and reset the overscroll mode. + // controller will not consume the event, because it is not triggering + // gesture-nav. SimulateGestureScrollUpdateEvent(-260, 0, 0); - EXPECT_EQ(0U, sink_->message_count()); + EXPECT_EQ(1U, sink_->message_count()); EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); // Since the overscroll mode has been reset, the next scroll update events @@ -2545,6 +2548,33 @@ TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollDirectionChange) { EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); } +TEST_F(RenderWidgetHostViewAuraOverscrollTest, + OverscrollDirectionChangeMouseWheel) { + SetUpOverscrollEnvironment(); + + // Send wheel event and receive ack as not consumed. + SimulateWheelEvent(125, -5, 0, true); + EXPECT_EQ(1U, GetSentMessageCountAndResetSink()); + SendInputEventACK(WebInputEvent::MouseWheel, + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_mode()); + EXPECT_EQ(OVERSCROLL_EAST, overscroll_delegate()->current_mode()); + EXPECT_EQ(0U, sink_->message_count()); + + // Send another wheel event, but in the reverse direction. The overscroll + // controller will not consume the event, because it is not triggering + // gesture-nav. + SimulateWheelEvent(-260, 0, 0, true); + EXPECT_EQ(1U, sink_->message_count()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); + + // Since the overscroll mode has been reset, the next wheel event should reach + // the renderer. + SimulateWheelEvent(-20, 0, 0, true); + EXPECT_EQ(1U, sink_->message_count()); + EXPECT_EQ(OVERSCROLL_NONE, overscroll_mode()); +} + // Tests that if a mouse-move event completes the overscroll gesture, future // move events do reach the renderer. TEST_F(RenderWidgetHostViewAuraOverscrollTest, OverscrollMouseMoveCompletion) { diff --git a/content/browser/renderer_host/render_widget_host_view_base.cc b/content/browser/renderer_host/render_widget_host_view_base.cc index 9e38f0e170096..f12faf32dcaf7 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.cc +++ b/content/browser/renderer_host/render_widget_host_view_base.cc @@ -374,7 +374,8 @@ RenderWidgetHostViewBase::RenderWidgetHostViewBase() current_device_scale_factor_(0), current_display_rotation_(gfx::Display::ROTATE_0), pinch_zoom_enabled_(content::IsPinchToZoomEnabled()), - renderer_frame_number_(0) { + renderer_frame_number_(0), + weak_factory_(this) { } RenderWidgetHostViewBase::~RenderWidgetHostViewBase() { @@ -535,6 +536,10 @@ bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) { return true; } +base::WeakPtr RenderWidgetHostViewBase::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + scoped_ptr RenderWidgetHostViewBase::CreateSyntheticGestureTarget() { RenderWidgetHostImpl* host = diff --git a/content/browser/renderer_host/render_widget_host_view_base.h b/content/browser/renderer_host/render_widget_host_view_base.h index 15a055d2e03f1..0fdfe5c668bb2 100644 --- a/content/browser/renderer_host/render_widget_host_view_base.h +++ b/content/browser/renderer_host/render_widget_host_view_base.h @@ -104,6 +104,8 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView, // changed since the last time. bool HasDisplayPropertyChanged(gfx::NativeView view); + base::WeakPtr GetWeakPtr(); + //---------------------------------------------------------------------------- // The following methods can be overridden by derived classes. @@ -226,6 +228,13 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView, virtual void RenderProcessGone(base::TerminationStatus status, int error_code) = 0; + // Notifies the View that the renderer's host has ceased to exist. + // The default implementation of this is a no-op. This hack exists to fix + // a crash on the branch. + // TODO(ccameron): Clean this up. + // http://crbug.com/404828 + virtual void RenderWidgetHostGone() {} + // Tells the View to destroy itself. virtual void Destroy() = 0; @@ -243,27 +252,27 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView, // Notifies the view that the scroll offset has changed. virtual void ScrollOffsetChanged() = 0; - // Copies the contents of the compositing surface into the given - // (uninitialized) PlatformCanvas if any. - // The rectangle region specified with |src_subrect| is copied from the - // contents, scaled to |dst_size|, and written to |output|. - // |callback| is invoked with true on success, false otherwise. |output| can - // be initialized even on failure. - // A smaller region than |src_subrect| may be copied if the underlying surface - // is smaller than |src_subrect|. - // NOTE: |callback| is called asynchronously. + // Copies the contents of the compositing surface, providing a new SkBitmap + // result via an asynchronously-run |callback|. |src_subrect| is specified in + // layer space coordinates for the current platform (e.g., DIP for Aura/Mac, + // physical for Android), and is the region to be copied from this view. The + // copy is then scaled to a SkBitmap of size |dst_size|. |callback| is run + // with true on success, false otherwise. A smaller region than |src_subrect| + // may be copied if the underlying surface is smaller than |src_subrect|. virtual void CopyFromCompositingSurface( const gfx::Rect& src_subrect, const gfx::Size& dst_size, const base::Callback& callback, const SkColorType color_type) = 0; - // Copies a given subset of the compositing surface's content into a YV12 - // VideoFrame, and invokes a callback with a success/fail parameter. |target| - // must contain an allocated, YV12 video frame of the intended size. If the - // copy rectangle does not match |target|'s size, the copied content will be - // scaled and letterboxed with black borders. The copy will happen - // asynchronously. This operation will fail if there is no available + // Copies the contents of the compositing surface, populating the given + // |target| with YV12 image data. |src_subrect| is specified in layer space + // coordinates for the current platform (e.g., DIP for Aura/Mac, physical for + // Android), and is the region to be copied from this view. The copy is then + // scaled and letterboxed with black borders to fit |target|. Finally, + // |callback| is asynchronously run with true/false for + // success/failure. |target| must point to an allocated, YV12 video frame of + // the intended size. This operation will fail if there is no available // compositing surface. virtual void CopyFromCompositingSurfaceToVideoFrame( const gfx::Rect& src_subrect, @@ -428,6 +437,8 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView, base::OneShotTimer flush_input_timer_; + base::WeakPtrFactory weak_factory_; + DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewBase); }; diff --git a/content/browser/renderer_host/render_widget_host_view_browsertest.cc b/content/browser/renderer_host/render_widget_host_view_browsertest.cc index a1ffa149078c0..25482945f3cb5 100644 --- a/content/browser/renderer_host/render_widget_host_view_browsertest.cc +++ b/content/browser/renderer_host/render_widget_host_view_browsertest.cc @@ -29,7 +29,7 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "ui/base/layout.h" #include "ui/base/ui_base_switches.h" -#include "ui/gfx/size_conversions.h" +#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/switches.h" #include "ui/gl/gl_switches.h" @@ -491,13 +491,9 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture SkBitmap bitmap; bitmap.allocN32Pixels(video_frame->visible_rect().width(), video_frame->visible_rect().height()); - bitmap.eraseColor(SK_ColorTRANSPARENT); + // Don't clear the canvas because drawing a video frame by Src mode. SkCanvas canvas(bitmap); - - video_renderer.Paint(video_frame.get(), - &canvas, - video_frame->visible_rect(), - 0xff); + video_renderer.Copy(video_frame.get(), &canvas); CopyFromCompositingSurfaceCallback(quit_callback, result, @@ -522,15 +518,12 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture // Loads a page two boxes side-by-side, each half the width of // |html_rect_size|, and with different background colors. The test then // copies from |copy_rect| region of the page into a bitmap of size - // |output_size|, and compares that with a bitmap of size - // |expected_bitmap_size|. + // |output_size|, and examines the resulting bitmap/VideoFrame. // Note that |output_size| may not have the same size as |copy_rect| (e.g. - // when the output is scaled). Also note that |expected_bitmap_size| may not - // be the same as |output_size| (e.g. when the device scale factor is not 1). + // when the output is scaled). void PerformTestWithLeftRightRects(const gfx::Size& html_rect_size, const gfx::Rect& copy_rect, const gfx::Size& output_size, - const gfx::Size& expected_bitmap_size, bool video_frame) { const gfx::Size box_size(html_rect_size.width() / 2, html_rect_size.height()); @@ -591,7 +584,7 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture GiveItSomeTime(); SkBitmap expected_bitmap; - SetupLeftRightBitmap(expected_bitmap_size, &expected_bitmap); + SetupLeftRightBitmap(output_size, &expected_bitmap); SetExpectedCopyFromCompositingSurfaceResult(true, expected_bitmap); base::RunLoop run_loop; @@ -604,9 +597,9 @@ class CompositingRenderWidgetHostViewBrowserTestTabCapture scoped_refptr video_frame = media::VideoFrame::CreateFrame(media::VideoFrame::YV12, - expected_bitmap_size, - gfx::Rect(expected_bitmap_size), - expected_bitmap_size, + output_size, + gfx::Rect(output_size), + output_size, base::TimeDelta()); base::Callback callback = @@ -678,13 +671,11 @@ IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, CopyFromCompositingSurface_Origin_Unscaled) { gfx::Rect copy_rect(400, 300); gfx::Size output_size = copy_rect.size(); - gfx::Size expected_bitmap_size = output_size; gfx::Size html_rect_size(400, 300); bool video_frame = false; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } @@ -692,13 +683,11 @@ IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, CopyFromCompositingSurface_Origin_Scaled) { gfx::Rect copy_rect(400, 300); gfx::Size output_size(200, 100); - gfx::Size expected_bitmap_size = output_size; gfx::Size html_rect_size(400, 300); bool video_frame = false; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } @@ -709,13 +698,11 @@ IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30), gfx::Size(60, 60)); gfx::Size output_size = copy_rect.size(); - gfx::Size expected_bitmap_size = output_size; gfx::Size html_rect_size(400, 300); bool video_frame = false; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } @@ -726,13 +713,11 @@ IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(30, 30), gfx::Size(60, 60)); gfx::Size output_size(20, 10); - gfx::Size expected_bitmap_size = output_size; gfx::Size html_rect_size(400, 300); bool video_frame = false; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } @@ -743,13 +728,11 @@ IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, copy_rect = gfx::Rect(copy_rect.CenterPoint() - gfx::Vector2d(45, 30), gfx::Size(90, 60)); gfx::Size output_size = copy_rect.size(); - gfx::Size expected_bitmap_size = output_size; gfx::Size html_rect_size(400, 300); bool video_frame = true; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } @@ -761,101 +744,174 @@ IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewBrowserTestTabCapture, gfx::Size(90, 60)); // Scale to 30 x 20 (preserve aspect ratio). gfx::Size output_size(30, 20); - gfx::Size expected_bitmap_size = output_size; gfx::Size html_rect_size(400, 300); bool video_frame = true; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } -class CompositingRenderWidgetHostViewTabCaptureHighDPI +class CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI : public CompositingRenderWidgetHostViewBrowserTestTabCapture { public: - CompositingRenderWidgetHostViewTabCaptureHighDPI() : kScale(2.f) {} + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI() {} - virtual void SetUpOnMainThread() OVERRIDE { - CommandLine* cmd = CommandLine::ForCurrentProcess(); + protected: + virtual void SetUpCommandLine(base::CommandLine* cmd) OVERRIDE { + CompositingRenderWidgetHostViewBrowserTestTabCapture::SetUpCommandLine(cmd); cmd->AppendSwitchASCII(switches::kForceDeviceScaleFactor, base::StringPrintf("%f", scale())); -#if defined(OS_WIN) - gfx::ForceHighDPISupportForTesting(scale()); - gfx::EnableHighDPISupport(); -#endif } - float scale() const { return kScale; } - - private: virtual bool ShouldContinueAfterTestURLLoad() OVERRIDE { // Short-circuit a pass for platforms where setting up high-DPI fails. - if (ui::GetScaleForScaleFactor(ui::GetSupportedScaleFactor( - GetScaleFactorForView(GetRenderWidgetHostView()))) != scale()) { - LOG(WARNING) << "Blindly passing this test: failed to set up " - "scale factor: " << scale(); + const float actual_scale_factor = + GetScaleFactorForView(GetRenderWidgetHostView()); + if (actual_scale_factor != scale()) { + LOG(WARNING) << "Blindly passing this test; unable to force device scale " + << "factor: seems to be " << actual_scale_factor + << " but expected " << scale(); return false; } + VLOG(1) << ("Successfully forced device scale factor. Moving forward with " + "this test! :-)"); return true; } - const float kScale; + static float scale() { return 2.0f; } - DISALLOW_COPY_AND_ASSIGN(CompositingRenderWidgetHostViewTabCaptureHighDPI); + private: + DISALLOW_COPY_AND_ASSIGN( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI); }; -IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewTabCaptureHighDPI, - CopyFromCompositingSurface) { +// ImageSkia (related to ResourceBundle) implementation crashes the process on +// Windows when this content_browsertest forces a device scale factor. +// http://crbug.com/399349 +#if defined(OS_WIN) +#define MAYBE_CopyToBitmap_EntireRegion DISABLED_CopyToBitmap_EntireRegion +#define MAYBE_CopyToBitmap_CenterRegion DISABLED_CopyToBitmap_CenterRegion +#define MAYBE_CopyToBitmap_ScaledResult DISABLED_CopyToBitmap_ScaledResult +#define MAYBE_CopyToVideoFrame_EntireRegion \ + DISABLED_CopyToVideoFrame_EntireRegion +#define MAYBE_CopyToVideoFrame_CenterRegion \ + DISABLED_CopyToVideoFrame_CenterRegion +#define MAYBE_CopyToVideoFrame_ScaledResult \ + DISABLED_CopyToVideoFrame_ScaledResult +#else +#define MAYBE_CopyToBitmap_EntireRegion CopyToBitmap_EntireRegion +#define MAYBE_CopyToBitmap_CenterRegion CopyToBitmap_CenterRegion +#define MAYBE_CopyToBitmap_ScaledResult CopyToBitmap_ScaledResult +#define MAYBE_CopyToVideoFrame_EntireRegion CopyToVideoFrame_EntireRegion +#define MAYBE_CopyToVideoFrame_CenterRegion CopyToVideoFrame_CenterRegion +#define MAYBE_CopyToVideoFrame_ScaledResult CopyToVideoFrame_ScaledResult +#endif + +IN_PROC_BROWSER_TEST_P( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + MAYBE_CopyToBitmap_EntireRegion) { + gfx::Size html_rect_size(200, 150); gfx::Rect copy_rect(200, 150); - gfx::Size output_size = copy_rect.size(); - gfx::Size expected_bitmap_size = - gfx::ToFlooredSize(gfx::ScaleSize(output_size, scale(), scale())); + // Scale the output size so that, internally, scaling is not occurring. + gfx::Size output_size = + gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); + bool video_frame = false; + PerformTestWithLeftRightRects(html_rect_size, + copy_rect, + output_size, + video_frame); +} + +IN_PROC_BROWSER_TEST_P( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + MAYBE_CopyToBitmap_CenterRegion) { gfx::Size html_rect_size(200, 150); + // Grab 90x60 pixels from the center of the tab contents. + gfx::Rect copy_rect = + gfx::Rect(gfx::Rect(html_rect_size).CenterPoint() - gfx::Vector2d(45, 30), + gfx::Size(90, 60)); + // Scale the output size so that, internally, scaling is not occurring. + gfx::Size output_size = + gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); bool video_frame = false; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } -IN_PROC_BROWSER_TEST_P(CompositingRenderWidgetHostViewTabCaptureHighDPI, - CopyFromCompositingSurfaceVideoFrame) { +IN_PROC_BROWSER_TEST_P( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + MAYBE_CopyToBitmap_ScaledResult) { + gfx::Size html_rect_size(200, 100); + gfx::Rect copy_rect(200, 100); + // Output is being down-scaled since output_size is in phyiscal pixels. + gfx::Size output_size(200, 100); + bool video_frame = false; + PerformTestWithLeftRightRects(html_rect_size, + copy_rect, + output_size, + video_frame); +} + +IN_PROC_BROWSER_TEST_P( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + MAYBE_CopyToVideoFrame_EntireRegion) { + gfx::Size html_rect_size(200, 150); + gfx::Rect copy_rect(200, 150); + // Scale the output size so that, internally, scaling is not occurring. + gfx::Size output_size = + gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); + bool video_frame = true; + PerformTestWithLeftRightRects(html_rect_size, + copy_rect, + output_size, + video_frame); +} + +IN_PROC_BROWSER_TEST_P( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + MAYBE_CopyToVideoFrame_CenterRegion) { gfx::Size html_rect_size(200, 150); // Grab 90x60 pixels from the center of the tab contents. gfx::Rect copy_rect = gfx::Rect(gfx::Rect(html_rect_size).CenterPoint() - gfx::Vector2d(45, 30), gfx::Size(90, 60)); - gfx::Size output_size = copy_rect.size(); - gfx::Size expected_bitmap_size = - gfx::ToFlooredSize(gfx::ScaleSize(output_size, scale(), scale())); + // Scale the output size so that, internally, scaling is not occurring. + gfx::Size output_size = + gfx::ToRoundedSize(gfx::ScaleSize(copy_rect.size(), scale())); bool video_frame = true; PerformTestWithLeftRightRects(html_rect_size, copy_rect, output_size, - expected_bitmap_size, video_frame); } -#if !defined(USE_AURA) && !defined(OS_MACOSX) -// TODO(danakj): Remove this case when GTK linux is no more and move the -// values inline to testing::Values() below. -static const CompositingMode kAllCompositingModes[] = {GL_COMPOSITING}; -#else -static const CompositingMode kAllCompositingModes[] = {GL_COMPOSITING, - SOFTWARE_COMPOSITING}; -#endif +IN_PROC_BROWSER_TEST_P( + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + MAYBE_CopyToVideoFrame_ScaledResult) { + gfx::Size html_rect_size(200, 100); + gfx::Rect copy_rect(200, 100); + // Output is being down-scaled since output_size is in phyiscal pixels. + gfx::Size output_size(200, 100); + bool video_frame = true; + PerformTestWithLeftRightRects(html_rect_size, + copy_rect, + output_size, + video_frame); +} INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing, CompositingRenderWidgetHostViewBrowserTest, - testing::ValuesIn(kAllCompositingModes)); + testing::Values(GL_COMPOSITING, SOFTWARE_COMPOSITING)); INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing, CompositingRenderWidgetHostViewBrowserTestTabCapture, - testing::ValuesIn(kAllCompositingModes)); -INSTANTIATE_TEST_CASE_P(GLAndSoftwareCompositing, - CompositingRenderWidgetHostViewTabCaptureHighDPI, - testing::ValuesIn(kAllCompositingModes)); + testing::Values(GL_COMPOSITING, SOFTWARE_COMPOSITING)); +INSTANTIATE_TEST_CASE_P( + GLAndSoftwareCompositing, + CompositingRenderWidgetHostViewBrowserTestTabCaptureHighDPI, + testing::Values(GL_COMPOSITING, SOFTWARE_COMPOSITING)); #endif // !defined(OS_ANDROID) && !defined(OS_IOS) diff --git a/content/browser/renderer_host/render_widget_host_view_mac.h b/content/browser/renderer_host/render_widget_host_view_mac.h index 16368ff58b4cd..630f76eb3d585 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.h +++ b/content/browser/renderer_host/render_widget_host_view_mac.h @@ -287,6 +287,7 @@ class CONTENT_EXPORT RenderWidgetHostViewMac const std::vector& character_bounds) OVERRIDE; virtual void RenderProcessGone(base::TerminationStatus status, int error_code) OVERRIDE; + virtual void RenderWidgetHostGone() OVERRIDE; virtual void Destroy() OVERRIDE; virtual void SetTooltipText(const base::string16& tooltip_text) OVERRIDE; virtual void SelectionChanged(const base::string16& text, @@ -351,7 +352,8 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // CompositingIOSurfaceLayerClient implementation. virtual bool AcceleratedLayerShouldAckImmediately() const OVERRIDE; - virtual void AcceleratedLayerDidDrawFrame(bool succeeded) OVERRIDE; + virtual void AcceleratedLayerDidDrawFrame() OVERRIDE; + virtual void AcceleratedLayerHitError() OVERRIDE; // gfx::DisplayObserver implementation. virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE; @@ -550,6 +552,10 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // invoke it from the message loop. void ShutdownHost(); + // Tear down all components of the browser compositor in an order that will + // ensure no dangling references. + void ShutdownBrowserCompositor(); + void EnsureBrowserCompositorView(); void DestroyBrowserCompositorView(); @@ -577,6 +583,7 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // IPC message handlers. void OnPluginFocusChanged(bool focused, int plugin_id); void OnStartPluginIme(); + void OnGetRenderedTextCompleted(const std::string& text); // Convert |rect| from the views coordinate (upper-left origin) into // the OpenGL coordinate (lower-left origin) and scale for HiDPI displays. @@ -585,6 +592,9 @@ class CONTENT_EXPORT RenderWidgetHostViewMac // Send updated vsync parameters to the renderer. void SendVSyncParametersToRenderer(); + // Dispatches a TTS session. + void SpeakText(const std::string& text); + // The associated view. This is weak and is inserted into the view hierarchy // to own this RenderWidgetHostViewMac object. Set to nil at the start of the // destructor. 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 f80585a0f84bd..8143dfa4a8538 100644 --- a/content/browser/renderer_host/render_widget_host_view_mac.mm +++ b/content/browser/renderer_host/render_widget_host_view_mac.mm @@ -558,6 +558,9 @@ void RemoveLayerFromSuperlayer( UnlockMouse(); + // Ensure that the browser compositor is destroyed in a safe order. + ShutdownBrowserCompositor(); + // Make sure that the layer doesn't reach into the now-invalid object. DestroyCompositedIOSurfaceAndLayer(); DestroySoftwareLayer(); @@ -651,7 +654,7 @@ void RemoveLayerFromSuperlayer( browser_compositor_view_.reset(new BrowserCompositorViewMac(this)); delegated_frame_host_->AddedToWindow(); - delegated_frame_host_->WasShown(); + delegated_frame_host_->WasShown(ui::LatencyInfo()); } void RenderWidgetHostViewMac::DestroyBrowserCompositorView() { @@ -660,6 +663,8 @@ void RemoveLayerFromSuperlayer( if (!browser_compositor_view_) return; + // Marking the DelegatedFrameHost as removed from the window hierarchy is + // necessary to remove all connections to its old ui::Compositor. delegated_frame_host_->WasHidden(); delegated_frame_host_->RemovingFromWindow(); browser_compositor_view_.reset(); @@ -734,6 +739,8 @@ void RemoveLayerFromSuperlayer( IPC_BEGIN_MESSAGE_MAP(RenderWidgetHostViewMac, message) IPC_MESSAGE_HANDLER(ViewHostMsg_PluginFocusChanged, OnPluginFocusChanged) IPC_MESSAGE_HANDLER(ViewHostMsg_StartPluginIme, OnStartPluginIme) + IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedTextCompleted, + OnGetRenderedTextCompleted) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() return handled; @@ -844,7 +851,8 @@ void RemoveLayerFromSuperlayer( void RenderWidgetHostViewMac::UpdateDisplayLink() { static bool is_vsync_disabled = - CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync); + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableGpuVsync); if (is_vsync_disabled) return; @@ -874,6 +882,10 @@ void RemoveLayerFromSuperlayer( render_widget_host_->UpdateVSyncParameters(vsync_timebase_, vsync_interval_); } +void RenderWidgetHostViewMac::SpeakText(const std::string& text) { + [NSApp speakString:base::SysUTF8ToNSString(text)]; +} + void RenderWidgetHostViewMac::UpdateBackingStoreScaleFactor() { if (!render_widget_host_) return; @@ -894,7 +906,24 @@ void RemoveLayerFromSuperlayer( if (!render_widget_host_->is_hidden()) return; - render_widget_host_->WasShown(); + ui::LatencyInfo renderer_latency_info; + if ((compositing_iosurface_ && compositing_iosurface_->HasIOSurface()) || + software_frame_manager_->HasCurrentFrame() || + (delegated_frame_host_ && delegated_frame_host_->HasSavedFrame())) { + ui::LatencyInfo browser_latency_info; + browser_latency_info.AddLatencyNumber( + ui::TAB_SHOW_COMPONENT, + render_widget_host_->GetLatencyComponentId(), + 0); + pending_latency_info_.push_back(browser_latency_info); + } else { + renderer_latency_info.AddLatencyNumber( + ui::TAB_SHOW_COMPONENT, + render_widget_host_->GetLatencyComponentId(), + 0); + } + + render_widget_host_->WasShown(renderer_latency_info); software_frame_manager_->SetVisibility(true); // If there is not a frame being currently drawn, kick one, so that the below @@ -1100,6 +1129,13 @@ void RemoveLayerFromSuperlayer( Destroy(); } +void RenderWidgetHostViewMac::RenderWidgetHostGone() { + // Destroy the DelegatedFrameHost, to prevent crashes when Destroy is never + // called on the view. + // http://crbug.com/404828 + ShutdownBrowserCompositor(); +} + void RenderWidgetHostViewMac::Destroy() { [[NSNotificationCenter defaultCenter] removeObserver:cocoa_view_ @@ -1125,10 +1161,7 @@ void RemoveLayerFromSuperlayer( // Delete the delegated frame state, which will reach back into // render_widget_host_. - DestroyBrowserCompositorView(); - delegated_frame_host_.reset(); - root_layer_.reset(); - browser_compositor_view_placeholder_.reset(); + ShutdownBrowserCompositor(); // We get this call just before |render_widget_host_| deletes // itself. But we are owned by |cocoa_view_|, which may be retained @@ -1165,8 +1198,19 @@ void RemoveLayerFromSuperlayer( } void RenderWidgetHostViewMac::SpeakSelection() { - if ([NSApp respondsToSelector:@selector(speakString:)]) - [NSApp speakString:base::SysUTF8ToNSString(selected_text_)]; + if (![NSApp respondsToSelector:@selector(speakString:)]) + return; + + if (selected_text_.empty() && render_widget_host_) { + // If there's no selection, speak all text. Send an asynchronous IPC + // request for fetching all the text for a webcontent. + // ViewMsg_GetRenderedTextCompleted is sent back to IPC Message receiver. + render_widget_host_->Send(new ViewMsg_GetRenderedText( + render_widget_host_->GetRoutingID())); + return; + } + + SpeakText(selected_text_); } bool RenderWidgetHostViewMac::IsSpeaking() const { @@ -2012,6 +2056,13 @@ void RemoveLayerFromSuperlayer( // Do not touch any members at this point, |this| has been deleted. } +void RenderWidgetHostViewMac::ShutdownBrowserCompositor() { + DestroyBrowserCompositorView(); + delegated_frame_host_.reset(); + root_layer_.reset(); + browser_compositor_view_placeholder_.reset(); +} + void RenderWidgetHostViewMac::GotAcceleratedFrame() { EnsureCompositedIOSurfaceLayer(); SendVSyncParametersToRenderer(); @@ -2142,6 +2193,11 @@ void RemoveLayerFromSuperlayer( [cocoa_view_ setPluginImeActive:YES]; } +void RenderWidgetHostViewMac::OnGetRenderedTextCompleted( + const std::string& text) { + SpeakText(text); +} + gfx::Rect RenderWidgetHostViewMac::GetScaledOpenGLPixelRect( const gfx::Rect& rect) { gfx::Rect src_gl_subrect = rect; @@ -2272,7 +2328,8 @@ void RemoveLayerFromSuperlayer( bool RenderWidgetHostViewMac::AcceleratedLayerShouldAckImmediately() const { // If vsync is disabled, then always draw and ack frames immediately. static bool is_vsync_disabled = - CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableGpuVsync); + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableGpuVsync); if (is_vsync_disabled) return true; @@ -2314,14 +2371,21 @@ void RemoveLayerFromSuperlayer( return false; } -void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame(bool succeeded) { +void RenderWidgetHostViewMac::AcceleratedLayerDidDrawFrame() { if (!render_widget_host_) return; SendPendingLatencyInfoToHost(); SendPendingSwapAck(); - if (!succeeded) - GotAcceleratedCompositingError(); +} + +void RenderWidgetHostViewMac::AcceleratedLayerHitError() { + if (!render_widget_host_) + return; + // Perform all acks that would have been done if the frame had succeeded, to + // un-block the renderer. + AcceleratedLayerDidDrawFrame(); + GotAcceleratedCompositingError(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/content/browser/renderer_host/ui_events_helper.cc b/content/browser/renderer_host/ui_events_helper.cc index ec17ec65d8427..7208b76c36323 100644 --- a/content/browser/renderer_host/ui_events_helper.cc +++ b/content/browser/renderer_host/ui_events_helper.cc @@ -310,13 +310,6 @@ blink::WebTouchPoint* UpdateWebTouchEventFromUIEvent( // Update the location and state of the point. point->state = TouchPointStateFromEvent(event); - if (point->state == blink::WebTouchPoint::StateMoved) { - // It is possible for badly written touch drivers to emit Move events even - // when the touch location hasn't changed. In such cases, consume the event - // and pretend nothing happened. - if (point->position.x == event.x() && point->position.y == event.y()) - return NULL; - } point->position.x = event.x(); point->position.y = event.y(); diff --git a/content/browser/resources/indexed_db/OWNERS b/content/browser/resources/indexed_db/OWNERS index b106dad853f93..957249073989d 100644 --- a/content/browser/resources/indexed_db/OWNERS +++ b/content/browser/resources/indexed_db/OWNERS @@ -1,4 +1,3 @@ dgrogan@chromium.org michaeln@chromium.org jsbell@chromium.org -alecflett@chromium.org diff --git a/content/browser/screen_orientation/screen_orientation_browsertest.cc b/content/browser/screen_orientation/screen_orientation_browsertest.cc index 8216106ace3da..90fe72943d889 100644 --- a/content/browser/screen_orientation/screen_orientation_browsertest.cc +++ b/content/browser/screen_orientation/screen_orientation_browsertest.cc @@ -21,6 +21,10 @@ #include "third_party/WebKit/public/platform/WebScreenInfo.h" #include "ui/compositor/compositor_switches.h" +#if defined(OS_WIN) +#include "base/win/windows_version.h" +#endif // OS_WIN + namespace content { class ScreenOrientationBrowserTest : public ContentBrowserTest { @@ -86,6 +90,13 @@ class ScreenOrientationBrowserTest : public ContentBrowserTest { return type; } + bool ScreenOrientationSupported() { + bool support; + ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(), + "'orientation' in screen")->GetAsBoolean(&support); + return support; + } + bool WindowOrientationSupported() { bool support; ExecuteScriptAndGetValue(shell()->web_contents()->GetMainFrame(), @@ -123,6 +134,15 @@ IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest, ScreenOrientationChange) { WaitForResizeComplete(shell()->web_contents()); #endif // USE_AURA +#if defined(OS_WIN) + // Screen Orientation is currently disabled on Windows 8. + // This test will break, requiring an update when the API will be enabled. + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_WIN8) { + EXPECT_EQ(false, ScreenOrientationSupported()); + return; + } +#endif // defined(OS_WIN) + int angle = GetOrientationAngle(); for (int i = 0; i < 4; ++i) { @@ -170,6 +190,16 @@ IN_PROC_BROWSER_TEST_F(ScreenOrientationBrowserTest, LockSmoke) { TestNavigationObserver navigation_observer(shell()->web_contents(), 2); shell()->LoadURL(test_url); + +#if defined(OS_WIN) + // Screen Orientation is currently disabled on Windows 8. + // This test will break, requiring an update when the API will be enabled. + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_WIN8) { + EXPECT_EQ(false, ScreenOrientationSupported()); + return; + } +#endif // defined(OS_WIN) + navigation_observer.Wait(); #if USE_AURA WaitForResizeComplete(shell()->web_contents()); diff --git a/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc b/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc index 5d77d6c88958a..310b54b3f9047 100644 --- a/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc +++ b/content/browser/screen_orientation/screen_orientation_dispatcher_host.cc @@ -100,6 +100,11 @@ void ScreenOrientationDispatcherHost::OnOrientationChange() { provider_->OnOrientationChange(); } +void ScreenOrientationDispatcherHost::SetProvider( + ScreenOrientationProvider* provider) { + provider_.reset(provider); +} + void ScreenOrientationDispatcherHost::OnLockRequest( RenderFrameHost* render_frame_host, blink::WebScreenOrientationLockType orientation, diff --git a/content/browser/screen_orientation/screen_orientation_dispatcher_host.h b/content/browser/screen_orientation/screen_orientation_dispatcher_host.h index 3357b17ae04ca..8782a015b8a15 100644 --- a/content/browser/screen_orientation/screen_orientation_dispatcher_host.h +++ b/content/browser/screen_orientation/screen_orientation_dispatcher_host.h @@ -43,6 +43,8 @@ class CONTENT_EXPORT ScreenOrientationDispatcherHost void OnOrientationChange(); + void SetProvider(ScreenOrientationProvider* provider); + private: void OnLockRequest(RenderFrameHost* render_frame_host, blink::WebScreenOrientationLockType orientation, diff --git a/content/browser/screen_orientation/screen_orientation_message_filter_android.cc b/content/browser/screen_orientation/screen_orientation_message_filter_android.cc new file mode 100644 index 0000000000000..3a7831b2df038 --- /dev/null +++ b/content/browser/screen_orientation/screen_orientation_message_filter_android.cc @@ -0,0 +1,49 @@ +// 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 "content/browser/screen_orientation/screen_orientation_message_filter_android.h" + +#include "content/browser/screen_orientation/screen_orientation_provider_android.h" +#include "content/common/screen_orientation_messages.h" + +namespace content { + +ScreenOrientationMessageFilterAndroid::ScreenOrientationMessageFilterAndroid() + : BrowserMessageFilter(ScreenOrientationMsgStart) + , listeners_count_(0) { +} + +ScreenOrientationMessageFilterAndroid::~ScreenOrientationMessageFilterAndroid() +{ + if (listeners_count_ > 0) + ScreenOrientationProviderAndroid::StopAccurateListening(); +} + +bool ScreenOrientationMessageFilterAndroid::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ScreenOrientationMessageFilterAndroid, message) + IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_StartListening, + OnStartListening) + IPC_MESSAGE_HANDLER(ScreenOrientationHostMsg_StopListening, + OnStopListening) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void ScreenOrientationMessageFilterAndroid::OnStartListening() { + ++listeners_count_; + if (listeners_count_ == 1) + ScreenOrientationProviderAndroid::StartAccurateListening(); +} + +void ScreenOrientationMessageFilterAndroid::OnStopListening() { + DCHECK(listeners_count_ > 0); + --listeners_count_; + if (listeners_count_ == 0) + ScreenOrientationProviderAndroid::StopAccurateListening(); +} + +} // namespace content diff --git a/content/browser/screen_orientation/screen_orientation_message_filter_android.h b/content/browser/screen_orientation/screen_orientation_message_filter_android.h new file mode 100644 index 0000000000000..9e19673758118 --- /dev/null +++ b/content/browser/screen_orientation/screen_orientation_message_filter_android.h @@ -0,0 +1,32 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_MESSAGE_FILTER_ANDROID_H_ +#define CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_MESSAGE_FILTER_ANDROID_H_ + +#include "content/public/browser/browser_message_filter.h" + +namespace content { + +class ScreenOrientationMessageFilterAndroid : public BrowserMessageFilter { + public: + ScreenOrientationMessageFilterAndroid(); + + // BrowserMessageFilter implementation. + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + private: + virtual ~ScreenOrientationMessageFilterAndroid(); + + void OnStartListening(); + void OnStopListening(); + + int listeners_count_; + + DISALLOW_COPY_AND_ASSIGN(ScreenOrientationMessageFilterAndroid); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_MESSAGE_FILTER_ANDROID_H_ diff --git a/content/browser/screen_orientation/screen_orientation_provider.cc b/content/browser/screen_orientation/screen_orientation_provider.cc new file mode 100644 index 0000000000000..4f1998de7b851 --- /dev/null +++ b/content/browser/screen_orientation/screen_orientation_provider.cc @@ -0,0 +1,18 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/screen_orientation/screen_orientation_provider.h" + +namespace content { + +#if !defined(OS_ANDROID) +// static +ScreenOrientationProvider* ScreenOrientationProvider::Create( + ScreenOrientationDispatcherHost* dispatcher_host, + WebContents* web_contents) { + return NULL; +} +#endif // !defined(OS_ANDROID) + +} // namespace content diff --git a/content/browser/screen_orientation/screen_orientation_provider.h b/content/browser/screen_orientation/screen_orientation_provider.h index 8b7c0fbb52a58..f4c8e6c6b8602 100644 --- a/content/browser/screen_orientation/screen_orientation_provider.h +++ b/content/browser/screen_orientation/screen_orientation_provider.h @@ -43,15 +43,6 @@ class ScreenOrientationProvider { DISALLOW_COPY_AND_ASSIGN(ScreenOrientationProvider); }; -#if !defined(OS_ANDROID) -// static -ScreenOrientationProvider* ScreenOrientationProvider::Create( - ScreenOrientationDispatcherHost* dispatcher_host, - WebContents* web_contents) { - return NULL; -} -#endif // !defined(OS_ANDROID) - } // namespace content #endif // CONTENT_BROWSER_SCREEN_ORIENTATION_SCREEN_ORIENTATION_PROVIDER_H_ diff --git a/content/browser/screen_orientation/screen_orientation_provider_android.cc b/content/browser/screen_orientation/screen_orientation_provider_android.cc index 36064a69371e5..72940624841a4 100644 --- a/content/browser/screen_orientation/screen_orientation_provider_android.cc +++ b/content/browser/screen_orientation/screen_orientation_provider_android.cc @@ -202,6 +202,18 @@ ScreenOrientationProviderAndroid::GetNaturalLockType() const { return blink::WebScreenOrientationLockDefault; } +// static +void ScreenOrientationProviderAndroid::StartAccurateListening() { + Java_ScreenOrientationProvider_startAccurateListening( + base::android::AttachCurrentThread()); +} + +// static +void ScreenOrientationProviderAndroid::StopAccurateListening() { + Java_ScreenOrientationProvider_stopAccurateListening( + base::android::AttachCurrentThread()); +} + // static ScreenOrientationProvider* ScreenOrientationProvider::Create( ScreenOrientationDispatcherHost* dispatcher, diff --git a/content/browser/screen_orientation/screen_orientation_provider_android.h b/content/browser/screen_orientation/screen_orientation_provider_android.h index eac7cce8537cb..9b88386afab1e 100644 --- a/content/browser/screen_orientation/screen_orientation_provider_android.h +++ b/content/browser/screen_orientation/screen_orientation_provider_android.h @@ -33,6 +33,16 @@ class ScreenOrientationProviderAndroid : public ScreenOrientationProvider, // WebContentsObserver virtual void DidToggleFullscreenModeForTab(bool entered_fullscreen) OVERRIDE; + // Ask the ScreenOrientationListener (Java) to start accurately listening to + // the screen orientation. It keep track of the number of start request if it + // is already running an accurate listening. + static void StartAccurateListening(); + + // Ask the ScreenOrientationListener (Java) to stop accurately listening to + // the screen orientation. It will actually stop only if the number of stop + // requests matches the number of start requests. + static void StopAccurateListening(); + private: WebContentsImpl* web_contents_impl(); diff --git a/content/browser/service_worker/BUILD.gn b/content/browser/service_worker/BUILD.gn index d9f206615fb57..43a2ff1b43595 100644 --- a/content/browser/service_worker/BUILD.gn +++ b/content/browser/service_worker/BUILD.gn @@ -4,8 +4,9 @@ import("//third_party/protobuf/proto_library.gni") -proto_library("database_proto") { +proto_library("proto") { sources = [ + "service_worker_cache.proto", "service_worker_database.proto", ] } diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc index 37ff625d3a265..b67c78283b066 100644 --- a/content/browser/service_worker/service_worker_browsertest.cc +++ b/content/browser/service_worker/service_worker_browsertest.cc @@ -25,12 +25,16 @@ #include "content/public/browser/storage_partition.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_switches.h" +#include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "net/test/embedded_test_server/embedded_test_server.h" #include "net/test/embedded_test_server/http_request.h" #include "net/test/embedded_test_server/http_response.h" +#include "net/url_request/url_request_filter.h" +#include "net/url_request/url_request_interceptor.h" +#include "net/url_request/url_request_test_job.h" #include "webkit/browser/blob/blob_data_handle.h" #include "webkit/browser/blob/blob_storage_context.h" #include "webkit/common/blob/blob_data.h" @@ -171,13 +175,86 @@ scoped_ptr VerifyServiceWorkerHeaderInRequest( return http_response.PassAs(); } +// The ImportsBustMemcache test requires that the imported script +// would naturally be cached in blink's memcache, but the embedded +// test server doesn't produce headers that allow the blink's memcache +// to do that. This interceptor injects headers that give the import +// an experiration far in the future. +class LongLivedResourceInterceptor : public net::URLRequestInterceptor { + public: + LongLivedResourceInterceptor(const std::string& body) + : body_(body) {} + virtual ~LongLivedResourceInterceptor() {} + + // net::URLRequestInterceptor implementation + virtual net::URLRequestJob* MaybeInterceptRequest( + net::URLRequest* request, + net::NetworkDelegate* network_delegate) const OVERRIDE { + const char kHeaders[] = + "HTTP/1.1 200 OK\0" + "Content-Type: text/javascript\0" + "Expires: Thu, 1 Jan 2100 20:00:00 GMT\0" + "\0"; + std::string headers(kHeaders, arraysize(kHeaders)); + return new net::URLRequestTestJob( + request, network_delegate, headers, body_, true); + } + + private: + std::string body_; + DISALLOW_COPY_AND_ASSIGN(LongLivedResourceInterceptor); +}; + +void CreateLongLivedResourceInterceptors( + const GURL& worker_url, const GURL& import_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + scoped_ptr interceptor; + + interceptor.reset(new LongLivedResourceInterceptor( + "importScripts('long_lived_import.js');")); + net::URLRequestFilter::GetInstance()->AddUrlInterceptor( + worker_url, interceptor.Pass()); + + interceptor.reset(new LongLivedResourceInterceptor( + "// the imported script does nothing")); + net::URLRequestFilter::GetInstance()->AddUrlInterceptor( + import_url, interceptor.Pass()); +} + +void CountScriptResources( + ServiceWorkerContextWrapper* wrapper, + const GURL& scope, + int* num_resources) { + *num_resources = -1; + + std::vector infos = + wrapper->context()->GetAllLiveRegistrationInfo(); + if (infos.empty()) + return; + + int version_id; + size_t index = infos.size() - 1; + if (!infos[index].installing_version.is_null) + version_id = infos[index].installing_version.version_id; + else if (!infos[index].waiting_version.is_null) + version_id = infos[1].waiting_version.version_id; + else if (!infos[index].active_version.is_null) + version_id = infos[index].active_version.version_id; + else + return; + + ServiceWorkerVersion* version = + wrapper->context()->GetLiveVersion(version_id); + *num_resources = static_cast(version->script_cache_map()->size()); +} + } // namespace class ServiceWorkerBrowserTest : public ContentBrowserTest { protected: typedef ServiceWorkerBrowserTest self; - virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + virtual void SetUpCommandLine(base::CommandLine* command_line) OVERRIDE { command_line->AppendSwitch( switches::kEnableExperimentalWebPlatformFeatures); } @@ -641,7 +718,7 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, } IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); command_line->AppendSwitch(switches::kEnableServiceWorkerSync); RunOnIOThread(base::Bind( @@ -670,7 +747,13 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, SyncEventHandled) { EXPECT_EQ(200, response.status_code); } -IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, DISABLED_Reload) { +// ServiceWorkerBrowserTest.Reload is flaky on Android crbug.com/393486 +#if defined(OS_ANDROID) +#define MAYBE_Reload DISABLED_Reload +#else +#define MAYBE_Reload Reload +#endif +IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, MAYBE_Reload) { const std::string kPageUrl = "/service_worker/reload.html"; const std::string kWorkerUrl = "/service_worker/fetch_event_reload.js"; { @@ -683,12 +766,18 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, DISABLED_Reload) { base::Bind(&ExpectResultAndRun, true, base::Bind(&base::DoNothing))); observer->Wait(); } - NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl)); - EXPECT_EQ(base::UTF8ToUTF16("reload=false"), - shell()->web_contents()->GetTitle()); - ReloadBlockUntilNavigationsComplete(shell(), 1); - EXPECT_EQ(base::UTF8ToUTF16("reload=true"), - shell()->web_contents()->GetTitle()); + { + const base::string16 title = base::ASCIIToUTF16("reload=false"); + TitleWatcher title_watcher(shell()->web_contents(), title); + NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl)); + EXPECT_EQ(title, title_watcher.WaitAndGetTitle()); + } + { + const base::string16 title = base::ASCIIToUTF16("reload=true"); + TitleWatcher title_watcher(shell()->web_contents(), title); + ReloadBlockUntilNavigationsComplete(shell(), 1); + EXPECT_EQ(title, title_watcher.WaitAndGetTitle()); + } shell()->Close(); { base::RunLoop run_loop; @@ -699,6 +788,36 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, DISABLED_Reload) { } } +IN_PROC_BROWSER_TEST_F(ServiceWorkerBrowserTest, ImportsBustMemcache) { + const std::string kScopeUrl = "/service_worker/imports_bust_memcache_scope/"; + const std::string kPageUrl = "/service_worker/imports_bust_memcache.html"; + const std::string kScriptUrl = "/service_worker/worker_with_one_import.js"; + const std::string kImportUrl = "/service_worker/long_lived_import.js"; + const base::string16 kOKTitle(base::ASCIIToUTF16("OK")); + const base::string16 kFailTitle(base::ASCIIToUTF16("FAIL")); + + RunOnIOThread( + base::Bind(&CreateLongLivedResourceInterceptors, + embedded_test_server()->GetURL(kScriptUrl), + embedded_test_server()->GetURL(kImportUrl))); + + TitleWatcher title_watcher(shell()->web_contents(), kOKTitle); + title_watcher.AlsoWaitForTitle(kFailTitle); + NavigateToURL(shell(), embedded_test_server()->GetURL(kPageUrl)); + base::string16 title = title_watcher.WaitAndGetTitle(); + EXPECT_EQ(kOKTitle, title); + + // Verify the number of resources in the implicit script cache is correct. + const int kExpectedNumResources = 2; + int num_resources = 0; + RunOnIOThread( + base::Bind(&CountScriptResources, + base::Unretained(wrapper()), + embedded_test_server()->GetURL(kScopeUrl), + &num_resources)); + EXPECT_EQ(kExpectedNumResources, num_resources); +} + class ServiceWorkerBlackBoxBrowserTest : public ServiceWorkerBrowserTest { public: typedef ServiceWorkerBlackBoxBrowserTest self; diff --git a/content/browser/service_worker/service_worker_cache.cc b/content/browser/service_worker/service_worker_cache.cc new file mode 100644 index 0000000000000..d5028f00854f1 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache.cc @@ -0,0 +1,59 @@ +// 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 "content/browser/service_worker/service_worker_cache.h" + +#include + +#include "base/files/file_path.h" +#include "net/url_request/url_request_context.h" +#include "webkit/browser/blob/blob_storage_context.h" + +namespace content { + +// static +scoped_ptr ServiceWorkerCache::CreateMemoryCache( + const std::string& name, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) { + return make_scoped_ptr(new ServiceWorkerCache( + base::FilePath(), name, request_context, blob_context)); +} + +// static +scoped_ptr ServiceWorkerCache::CreatePersistentCache( + const base::FilePath& path, + const std::string& name, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) { + return make_scoped_ptr( + new ServiceWorkerCache(path, name, request_context, blob_context)); +} + +void ServiceWorkerCache::CreateBackend( + const base::Callback& callback) { + callback.Run(true); +} + +base::WeakPtr ServiceWorkerCache::AsWeakPtr() { + return weak_ptr_factory_.GetWeakPtr(); +} + +ServiceWorkerCache::ServiceWorkerCache( + const base::FilePath& path, + const std::string& name, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) + : path_(path), + name_(name), + request_context_(request_context), + blob_storage_context_(blob_context), + id_(0), + weak_ptr_factory_(this) { +} + +ServiceWorkerCache::~ServiceWorkerCache() { +} + +} // namespace content diff --git a/content/browser/service_worker/service_worker_cache.h b/content/browser/service_worker/service_worker_cache.h new file mode 100644 index 0000000000000..d5ee7071e5cc0 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache.h @@ -0,0 +1,77 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_H_ + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/memory/weak_ptr.h" + +namespace net { +class URLRequestContext; +} + +namespace webkit_blob { +class BlobStorageContext; +} + +namespace content { + +// TODO(jkarlin): Fill this in with a real Cache implementation as +// specified in +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html. +// TODO(jkarlin): Unload cache backend from memory once the cache object is no +// longer referenced in javascript. + +// Represents a ServiceWorker Cache as seen in +// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html. +// InitializeIfNeeded must be called before calling the other public members. +class ServiceWorkerCache { + public: + static scoped_ptr CreateMemoryCache( + const std::string& name, + net::URLRequestContext* request_context, + base::WeakPtr blob_context); + static scoped_ptr CreatePersistentCache( + const base::FilePath& path, + const std::string& name, + net::URLRequestContext* request_context, + base::WeakPtr blob_context); + + virtual ~ServiceWorkerCache(); + + // Loads the backend and calls the callback with the result (true for + // success). This must be called before member functions that require a + // backend are called. + void CreateBackend(const base::Callback& callback); + + void set_name(const std::string& name) { name_ = name; } + const std::string& name() const { return name_; } + int32 id() const { return id_; } + void set_id(int32 id) { id_ = id; } + + base::WeakPtr AsWeakPtr(); + + private: + ServiceWorkerCache( + const base::FilePath& path, + const std::string& name, + net::URLRequestContext* request_context, + base::WeakPtr blob_context); + + base::FilePath path_; + std::string name_; + net::URLRequestContext* request_context_; + base::WeakPtr blob_storage_context_; + int32 id_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCache); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_H_ diff --git a/content/browser/service_worker/service_worker_cache.proto b/content/browser/service_worker/service_worker_cache.proto new file mode 100644 index 0000000000000..b271df63b0ca1 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache.proto @@ -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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package content; + +message ServiceWorkerCacheStorageIndex { + message Cache { + required string name = 1; + required int32 size = 2; + } + repeated Cache cache = 1; +} diff --git a/content/browser/service_worker/service_worker_cache_listener.cc b/content/browser/service_worker/service_worker_cache_listener.cc new file mode 100644 index 0000000000000..0a4b66bbee9cf --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_listener.cc @@ -0,0 +1,207 @@ +// 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 "content/browser/service_worker/service_worker_cache_listener.h" + +#include "base/bind.h" +#include "base/strings/utf_string_conversions.h" +#include "content/browser/service_worker/service_worker_cache_storage_manager.h" +#include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_version.h" +#include "content/common/service_worker/service_worker_messages.h" +#include "third_party/WebKit/public/platform/WebServiceWorkerCacheError.h" + +namespace content { + +using blink::WebServiceWorkerCacheError; + +namespace { + +WebServiceWorkerCacheError ToWebServiceWorkerCacheError( + ServiceWorkerCacheStorage::CacheStorageError err) { + switch (err) { + case ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR: + NOTREACHED(); + return WebServiceWorkerCacheError:: + WebServiceWorkerCacheErrorNotImplemented; + case ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NOT_IMPLEMENTED: + return WebServiceWorkerCacheError:: + WebServiceWorkerCacheErrorNotImplemented; + case ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NOT_FOUND: + return WebServiceWorkerCacheError::WebServiceWorkerCacheErrorNotFound; + case ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EXISTS: + return WebServiceWorkerCacheError::WebServiceWorkerCacheErrorExists; + case ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_STORAGE: + // TODO(jkarlin): Changethis to CACHE_STORAGE_ERROR_STORAGE once that's + // added. + return WebServiceWorkerCacheError::WebServiceWorkerCacheErrorNotFound; + case ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EMPTY_KEY: + // TODO(jkarlin): Update this to CACHE_STORAGE_ERROR_EMPTY_KEY once that's + // added. + return WebServiceWorkerCacheError::WebServiceWorkerCacheErrorNotFound; + } + NOTREACHED(); + return WebServiceWorkerCacheError::WebServiceWorkerCacheErrorNotImplemented; +} + +} // namespace + +ServiceWorkerCacheListener::ServiceWorkerCacheListener( + ServiceWorkerVersion* version, + base::WeakPtr context) + : version_(version), context_(context), weak_factory_(this) { +} + +ServiceWorkerCacheListener::~ServiceWorkerCacheListener() { +} + +bool ServiceWorkerCacheListener::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ServiceWorkerCacheListener, message) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_CacheStorageGet, + OnCacheStorageGet) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_CacheStorageHas, + OnCacheStorageGet) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_CacheStorageCreate, + OnCacheStorageCreate) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_CacheStorageDelete, + OnCacheStorageDelete) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_CacheStorageKeys, + OnCacheStorageKeys) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void ServiceWorkerCacheListener::OnCacheStorageGet( + int request_id, + const base::string16& cache_name) { + context_->cache_manager()->GetCache( + version_->scope().GetOrigin(), + base::UTF16ToUTF8(cache_name), + base::Bind(&ServiceWorkerCacheListener::OnCacheStorageGetCallback, + weak_factory_.GetWeakPtr(), + request_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageHas( + int request_id, + const base::string16& cache_name) { + context_->cache_manager()->HasCache( + version_->scope().GetOrigin(), + base::UTF16ToUTF8(cache_name), + base::Bind(&ServiceWorkerCacheListener::OnCacheStorageHasCallback, + weak_factory_.GetWeakPtr(), + request_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageCreate( + int request_id, + const base::string16& cache_name) { + context_->cache_manager()->CreateCache( + version_->scope().GetOrigin(), + base::UTF16ToUTF8(cache_name), + base::Bind(&ServiceWorkerCacheListener::OnCacheStorageCreateCallback, + weak_factory_.GetWeakPtr(), + request_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageDelete( + int request_id, + const base::string16& cache_name) { + context_->cache_manager()->DeleteCache( + version_->scope().GetOrigin(), + base::UTF16ToUTF8(cache_name), + base::Bind(&ServiceWorkerCacheListener::OnCacheStorageDeleteCallback, + weak_factory_.GetWeakPtr(), + request_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageKeys(int request_id) { + context_->cache_manager()->EnumerateCaches( + version_->scope().GetOrigin(), + base::Bind(&ServiceWorkerCacheListener::OnCacheStorageKeysCallback, + weak_factory_.GetWeakPtr(), + request_id)); +} + +void ServiceWorkerCacheListener::Send(const IPC::Message& message) { + version_->embedded_worker()->SendMessage(message); +} + +void ServiceWorkerCacheListener::OnCacheStorageGetCallback( + int request_id, + int cache_id, + ServiceWorkerCacheStorage::CacheStorageError error) { + if (error != ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR) { + Send(ServiceWorkerMsg_CacheStorageGetError( + request_id, ToWebServiceWorkerCacheError(error))); + return; + } + Send(ServiceWorkerMsg_CacheStorageGetSuccess(request_id, cache_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageHasCallback( + int request_id, + bool has_cache, + ServiceWorkerCacheStorage::CacheStorageError error) { + if (error != ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR) { + Send(ServiceWorkerMsg_CacheStorageHasError( + request_id, ToWebServiceWorkerCacheError(error))); + return; + } + if (!has_cache) { + Send(ServiceWorkerMsg_CacheStorageHasError( + request_id, + WebServiceWorkerCacheError::WebServiceWorkerCacheErrorNotFound)); + return; + } + Send(ServiceWorkerMsg_CacheStorageHasSuccess(request_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageCreateCallback( + int request_id, + int cache_id, + ServiceWorkerCacheStorage::CacheStorageError error) { + if (error != ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR) { + Send(ServiceWorkerMsg_CacheStorageCreateError( + request_id, ToWebServiceWorkerCacheError(error))); + return; + } + Send(ServiceWorkerMsg_CacheStorageCreateSuccess(request_id, cache_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageDeleteCallback( + int request_id, + bool deleted, + ServiceWorkerCacheStorage::CacheStorageError error) { + if (!deleted || + error != ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR) { + Send(ServiceWorkerMsg_CacheStorageDeleteError( + request_id, ToWebServiceWorkerCacheError(error))); + return; + } + Send(ServiceWorkerMsg_CacheStorageDeleteSuccess(request_id)); +} + +void ServiceWorkerCacheListener::OnCacheStorageKeysCallback( + int request_id, + const std::vector& strings, + ServiceWorkerCacheStorage::CacheStorageError error) { + if (error != ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR) { + Send(ServiceWorkerMsg_CacheStorageKeysError( + request_id, ToWebServiceWorkerCacheError(error))); + return; + } + + std::vector string16s; + for (size_t i = 0, max = strings.size(); i < max; ++i) { + string16s.push_back(base::UTF8ToUTF16(strings[i])); + } + Send(ServiceWorkerMsg_CacheStorageKeysSuccess(request_id, string16s)); +} + +} // namespace content diff --git a/content/browser/service_worker/service_worker_cache_listener.h b/content/browser/service_worker/service_worker_cache_listener.h new file mode 100644 index 0000000000000..27a6089019946 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_listener.h @@ -0,0 +1,75 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_LISTENER_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_LISTENER_H_ + +#include "base/memory/weak_ptr.h" +#include "base/strings/string16.h" +#include "content/browser/service_worker/embedded_worker_instance.h" +#include "content/browser/service_worker/service_worker_cache_storage.h" + +namespace content { + +class ServiceWorkerVersion; + +// This class listens for requests on the Cache APIs, and sends response +// messages to the renderer process. There is one instance per +// ServiceWorkerVersion instance. +class ServiceWorkerCacheListener : public EmbeddedWorkerInstance::Listener { + public: + ServiceWorkerCacheListener(ServiceWorkerVersion* version, + base::WeakPtr context); + virtual ~ServiceWorkerCacheListener(); + + // From EmbeddedWorkerInstance::Listener: + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + + // Message receiver functions for CacheStorage API. + void OnCacheStorageGet(int request_id, const base::string16& cache_name); + void OnCacheStorageHas(int request_id, const base::string16& cache_name); + void OnCacheStorageCreate(int request_id, + const base::string16& cache_name); + void OnCacheStorageDelete(int request_id, + const base::string16& cache_name); + void OnCacheStorageKeys(int request_id); + + private: + void Send(const IPC::Message& message); + + void OnCacheStorageGetCallback( + int request_id, + int cache_id, + ServiceWorkerCacheStorage::CacheStorageError error); + void OnCacheStorageHasCallback( + int request_id, + bool has_cache, + ServiceWorkerCacheStorage::CacheStorageError error); + void OnCacheStorageCreateCallback( + int request_id, + int cache_id, + ServiceWorkerCacheStorage::CacheStorageError error); + void OnCacheStorageDeleteCallback( + int request_id, + bool deleted, + ServiceWorkerCacheStorage::CacheStorageError error); + void OnCacheStorageKeysCallback( + int request_id, + const std::vector& strings, + ServiceWorkerCacheStorage::CacheStorageError error); + + // The ServiceWorkerVersion to use for messaging back to the renderer thread. + ServiceWorkerVersion* version_; + + // The ServiceWorkerContextCore should always outlive this. + base::WeakPtr context_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCacheListener); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_LISTENER_H_ diff --git a/content/browser/service_worker/service_worker_cache_storage.cc b/content/browser/service_worker/service_worker_cache_storage.cc new file mode 100644 index 0000000000000..8a9c6c4d266b0 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_storage.cc @@ -0,0 +1,718 @@ +// 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 "content/browser/service_worker/service_worker_cache_storage.h" + +#include + +#include "base/file_util.h" +#include "base/files/memory_mapped_file.h" +#include "base/memory/ref_counted.h" +#include "base/sha1.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "content/browser/service_worker/service_worker_cache.h" +#include "content/browser/service_worker/service_worker_cache.pb.h" +#include "content/public/browser/browser_thread.h" +#include "net/base/directory_lister.h" +#include "net/base/net_errors.h" +#include "webkit/browser/blob/blob_storage_context.h" + +namespace content { + +// Handles the loading and clean up of ServiceWorkerCache objects. +class ServiceWorkerCacheStorage::CacheLoader + : public base::RefCountedThreadSafe< + ServiceWorkerCacheStorage::CacheLoader> { + public: + typedef base::Callback)> CacheCallback; + typedef base::Callback BoolCallback; + typedef base::Callback >)> + StringsCallback; + + CacheLoader( + base::SequencedTaskRunner* cache_task_runner, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) + : cache_task_runner_(cache_task_runner), + request_context_(request_context), + blob_context_(blob_context) {} + + // Loads the given cache_name, the cache is NULL if it fails. If the cache + // doesn't exist a new one is created. + virtual void LoadCache(const std::string& cache_name, + const CacheCallback& callback) = 0; + + // Deletes any pre-existing cache of the same name and then loads it. + virtual void CreateCache(const std::string& cache_name, + const CacheCallback& callback) = 0; + + // After the backend has been deleted, do any extra house keeping such as + // removing the cache's directory. + virtual void CleanUpDeletedCache(const std::string& key, + const BoolCallback& callback) = 0; + + // Writes the cache names (and sizes) to disk if applicable. + virtual void WriteIndex(CacheMap* caches, const BoolCallback& callback) = 0; + + // Loads the cache names from disk if applicable. + virtual void LoadIndex(scoped_ptr > cache_names, + const StringsCallback& callback) = 0; + + protected: + friend class base::RefCountedThreadSafe< + ServiceWorkerCacheStorage::CacheLoader>; + + virtual ~CacheLoader() {}; + virtual void LoadCacheImpl(const std::string&) {} + + scoped_refptr cache_task_runner_; + net::URLRequestContext* request_context_; + base::WeakPtr blob_context_; +}; + +class ServiceWorkerCacheStorage::MemoryLoader + : public ServiceWorkerCacheStorage::CacheLoader { + public: + MemoryLoader( + base::SequencedTaskRunner* cache_task_runner, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) + : CacheLoader(cache_task_runner, request_context, blob_context) {} + virtual void LoadCache(const std::string& cache_name, + const CacheCallback& callback) OVERRIDE { + NOTREACHED(); + } + + virtual void CreateCache(const std::string& cache_name, + const CacheCallback& callback) OVERRIDE { + scoped_ptr cache = + ServiceWorkerCache::CreateMemoryCache( + cache_name, request_context_, blob_context_); + callback.Run(cache.Pass()); + } + + virtual void CleanUpDeletedCache(const std::string& cache_name, + const BoolCallback& callback) OVERRIDE { + callback.Run(true); + } + + virtual void WriteIndex(CacheMap* caches, + const BoolCallback& callback) OVERRIDE { + callback.Run(false); + } + + virtual void LoadIndex(scoped_ptr > cache_names, + const StringsCallback& callback) OVERRIDE { + callback.Run(cache_names.Pass()); + } + + private: + virtual ~MemoryLoader() {} +}; + +class ServiceWorkerCacheStorage::SimpleCacheLoader + : public ServiceWorkerCacheStorage::CacheLoader { + public: + SimpleCacheLoader(const base::FilePath& origin_path, + base::SequencedTaskRunner* cache_task_runner, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) + : CacheLoader(cache_task_runner, request_context, blob_context), + origin_path_(origin_path) {} + + virtual void LoadCache(const std::string& cache_name, + const CacheCallback& callback) OVERRIDE { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // 1. Create the cache's directory if necessary. (LoadCreateDirectoryInPool) + // 2. Create the cache object. (LoadDidCreateDirectory) + + cache_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::LoadCreateDirectoryInPool, + this, + CreatePersistentCachePath(origin_path_, cache_name), + cache_name, + callback, + base::MessageLoopProxy::current())); + } + + void LoadCreateDirectoryInPool( + const base::FilePath& path, + const std::string& cache_name, + const CacheCallback& callback, + const scoped_refptr& original_loop) { + DCHECK(cache_task_runner_->RunsTasksOnCurrentThread()); + + bool rv = base::CreateDirectory(path); + original_loop->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::LoadDidCreateDirectory, + this, + cache_name, + callback, + rv)); + } + + void LoadDidCreateDirectory(const std::string& cache_name, + const CacheCallback& callback, + bool dir_rv) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!dir_rv) { + callback.Run(scoped_ptr()); + return; + } + + scoped_ptr cache = + ServiceWorkerCache::CreatePersistentCache( + CreatePersistentCachePath(origin_path_, cache_name), + cache_name, + request_context_, + blob_context_); + callback.Run(cache.Pass()); + } + + virtual void CreateCache(const std::string& cache_name, + const CacheCallback& callback) OVERRIDE { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // 1. Delete the cache's directory if it exists. + // (CreateCacheDeleteFilesInPool) + // 2. Load the cache. (LoadCreateDirectoryInPool) + + base::FilePath cache_path = + CreatePersistentCachePath(origin_path_, cache_name); + + cache_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::CreateCacheDeleteFilesInPool, + this, + cache_path, + cache_name, + callback, + base::MessageLoopProxy::current())); + } + + void CreateCacheDeleteFilesInPool( + const base::FilePath& cache_path, + const std::string& cache_name, + const CacheCallback& callback, + const scoped_refptr& original_loop) { + DCHECK(cache_task_runner_->RunsTasksOnCurrentThread()); + + base::FilePath path(cache_path); + if (base::PathExists(path)) + base::DeleteFile(path, /* recursive */ true); + + // Jump straight into LoadCache on the same thread. + cache_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::LoadCreateDirectoryInPool, + this, + cache_path, + cache_name, + callback, + original_loop)); + } + + virtual void CleanUpDeletedCache(const std::string& cache_name, + const BoolCallback& callback) OVERRIDE { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // 1. Delete the cache's directory. (CleanUpDeleteCacheDirInPool) + + base::FilePath cache_path = + CreatePersistentCachePath(origin_path_, cache_name); + cache_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::CleanUpDeleteCacheDirInPool, + this, + cache_path, + callback, + base::MessageLoopProxy::current())); + } + + void CleanUpDeleteCacheDirInPool( + const base::FilePath& cache_path, + const BoolCallback& callback, + const scoped_refptr& original_loop) { + DCHECK(cache_task_runner_->RunsTasksOnCurrentThread()); + + bool rv = base::DeleteFile(cache_path, true); + original_loop->PostTask(FROM_HERE, base::Bind(callback, rv)); + } + + virtual void WriteIndex(CacheMap* caches, + const BoolCallback& callback) OVERRIDE { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // 1. Create the index file as a string. (WriteIndex) + // 2. Write the file to disk. (WriteIndexWriteToFileInPool) + + ServiceWorkerCacheStorageIndex index; + + for (CacheMap::const_iterator iter(caches); !iter.IsAtEnd(); + iter.Advance()) { + const ServiceWorkerCache* cache = iter.GetCurrentValue(); + ServiceWorkerCacheStorageIndex::Cache* index_cache = index.add_cache(); + index_cache->set_name(cache->name()); + index_cache->set_size(0); // TODO(jkarlin): Make this real. + } + + std::string serialized; + bool success = index.SerializeToString(&serialized); + DCHECK(success); + + base::FilePath tmp_path = origin_path_.AppendASCII("index.txt.tmp"); + base::FilePath index_path = origin_path_.AppendASCII("index.txt"); + + cache_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::WriteIndexWriteToFileInPool, + this, + tmp_path, + index_path, + serialized, + caches, + callback, + base::MessageLoopProxy::current())); + } + + void WriteIndexWriteToFileInPool( + const base::FilePath& tmp_path, + const base::FilePath& index_path, + const std::string& data, + CacheMap* caches, + const BoolCallback& callback, + const scoped_refptr& original_loop) { + DCHECK(cache_task_runner_->RunsTasksOnCurrentThread()); + + int bytes_written = base::WriteFile(tmp_path, data.c_str(), data.size()); + if (bytes_written != implicit_cast(data.size())) { + base::DeleteFile(tmp_path, /* recursive */ false); + original_loop->PostTask(FROM_HERE, base::Bind(callback, false)); + } + + // Atomically rename the temporary index file to become the real one. + bool rv = base::ReplaceFile(tmp_path, index_path, NULL); + original_loop->PostTask(FROM_HERE, base::Bind(callback, rv)); + } + + virtual void LoadIndex(scoped_ptr > names, + const StringsCallback& callback) OVERRIDE { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + // 1. Read the file from disk. (LoadIndexReadFileInPool) + // 2. Parse file and return the names of the caches (LoadIndexDidReadFile) + + base::FilePath index_path = origin_path_.AppendASCII("index.txt"); + + cache_task_runner_->PostTask( + FROM_HERE, + base::Bind(&SimpleCacheLoader::LoadIndexReadFileInPool, + this, + index_path, + base::Passed(names.Pass()), + callback, + base::MessageLoopProxy::current())); + } + + void LoadIndexReadFileInPool( + const base::FilePath& index_path, + scoped_ptr > names, + const StringsCallback& callback, + const scoped_refptr& original_loop) { + DCHECK(cache_task_runner_->RunsTasksOnCurrentThread()); + + std::string body; + base::ReadFileToString(index_path, &body); + + original_loop->PostTask(FROM_HERE, + base::Bind(&SimpleCacheLoader::LoadIndexDidReadFile, + this, + base::Passed(names.Pass()), + callback, + body)); + } + + void LoadIndexDidReadFile(scoped_ptr > names, + const StringsCallback& callback, + const std::string& serialized) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCacheStorageIndex index; + index.ParseFromString(serialized); + + for (int i = 0, max = index.cache_size(); i < max; ++i) { + const ServiceWorkerCacheStorageIndex::Cache& cache = index.cache(i); + names->push_back(cache.name()); + } + + // TODO(jkarlin): Delete caches that are in the directory and not returned + // in LoadIndex. + callback.Run(names.Pass()); + } + + private: + virtual ~SimpleCacheLoader() {} + + std::string HexedHash(const std::string& value) { + std::string value_hash = base::SHA1HashString(value); + std::string valued_hexed_hash = base::StringToLowerASCII( + base::HexEncode(value_hash.c_str(), value_hash.length())); + return valued_hexed_hash; + } + + base::FilePath CreatePersistentCachePath(const base::FilePath& origin_path, + const std::string& cache_name) { + return origin_path.AppendASCII(HexedHash(cache_name)); + } + + const base::FilePath origin_path_; +}; + +ServiceWorkerCacheStorage::ServiceWorkerCacheStorage( + const base::FilePath& path, + bool memory_only, + base::SequencedTaskRunner* cache_task_runner, + net::URLRequestContext* request_context, + base::WeakPtr blob_context) + : initialized_(false), + origin_path_(path), + cache_task_runner_(cache_task_runner), + weak_factory_(this) { + if (memory_only) + cache_loader_ = + new MemoryLoader(cache_task_runner_, request_context, blob_context); + else + cache_loader_ = new SimpleCacheLoader( + origin_path_, cache_task_runner_, request_context, blob_context); +} + +ServiceWorkerCacheStorage::~ServiceWorkerCacheStorage() { +} + +void ServiceWorkerCacheStorage::CreateCache( + const std::string& cache_name, + const CacheAndErrorCallback& callback) { + if (!initialized_) { + LazyInit(base::Bind(&ServiceWorkerCacheStorage::CreateCache, + weak_factory_.GetWeakPtr(), + cache_name, + callback)); + return; + } + + if (cache_name.empty()) { + callback.Run(0, CACHE_STORAGE_ERROR_EMPTY_KEY); + return; + } + + if (GetLoadedCache(cache_name)) { + callback.Run(0, CACHE_STORAGE_ERROR_EXISTS); + return; + } + + cache_loader_->CreateCache( + cache_name, + base::Bind(&ServiceWorkerCacheStorage::CreateCacheDidCreateCache, + weak_factory_.GetWeakPtr(), + cache_name, + callback)); +} + +void ServiceWorkerCacheStorage::GetCache( + const std::string& cache_name, + const CacheAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!initialized_) { + LazyInit(base::Bind(&ServiceWorkerCacheStorage::GetCache, + weak_factory_.GetWeakPtr(), + cache_name, + callback)); + return; + } + + if (cache_name.empty()) { + callback.Run(0, CACHE_STORAGE_ERROR_EMPTY_KEY); + return; + } + + ServiceWorkerCache* cache = GetLoadedCache(cache_name); + if (!cache) { + callback.Run(0, CACHE_STORAGE_ERROR_NOT_FOUND); + return; + } + + cache->CreateBackend(base::Bind(&ServiceWorkerCacheStorage::DidCreateBackend, + weak_factory_.GetWeakPtr(), + cache->AsWeakPtr(), + callback)); +} + +void ServiceWorkerCacheStorage::HasCache(const std::string& cache_name, + const BoolAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!initialized_) { + LazyInit(base::Bind(&ServiceWorkerCacheStorage::HasCache, + weak_factory_.GetWeakPtr(), + cache_name, + callback)); + return; + } + + if (cache_name.empty()) { + callback.Run(false, CACHE_STORAGE_ERROR_EMPTY_KEY); + return; + } + + bool has_cache = GetLoadedCache(cache_name) != NULL; + + callback.Run(has_cache, CACHE_STORAGE_ERROR_NO_ERROR); +} + +void ServiceWorkerCacheStorage::DeleteCache( + const std::string& cache_name, + const BoolAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!initialized_) { + LazyInit(base::Bind(&ServiceWorkerCacheStorage::DeleteCache, + weak_factory_.GetWeakPtr(), + cache_name, + callback)); + return; + } + + if (cache_name.empty()) { + callback.Run(false, CACHE_STORAGE_ERROR_EMPTY_KEY); + return; + } + + ServiceWorkerCache* cache = GetLoadedCache(cache_name); + if (!cache) { + callback.Run(false, CACHE_STORAGE_ERROR_NOT_FOUND); + return; + } + + name_map_.erase(cache_name); + cache_map_.Remove(cache->id()); // deletes cache + + // Update the Index + cache_loader_->WriteIndex( + &cache_map_, + base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex, + weak_factory_.GetWeakPtr(), + cache_name, + callback)); +} + +void ServiceWorkerCacheStorage::EnumerateCaches( + const StringsAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!initialized_) { + LazyInit(base::Bind(&ServiceWorkerCacheStorage::EnumerateCaches, + weak_factory_.GetWeakPtr(), + callback)); + return; + } + + std::vector names; + for (NameMap::const_iterator it = name_map_.begin(); it != name_map_.end(); + ++it) { + names.push_back(it->first); + } + + callback.Run(names, CACHE_STORAGE_ERROR_NO_ERROR); +} + +void ServiceWorkerCacheStorage::DidCreateBackend( + base::WeakPtr cache, + const CacheAndErrorCallback& callback, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!success || !cache) { + // TODO(jkarlin): This should delete the directory and try again in case + // the cache is simply corrupt. + callback.Run(0, CACHE_STORAGE_ERROR_STORAGE); + return; + } + callback.Run(cache->id(), CACHE_STORAGE_ERROR_NO_ERROR); +} + +// Init is run lazily so that it is called on the proper MessageLoop. +void ServiceWorkerCacheStorage::LazyInit(const base::Closure& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(!initialized_); + + init_callbacks_.push_back(callback); + + // If this isn't the first call to LazyInit then return as the initialization + // has already started. + if (init_callbacks_.size() > 1u) + return; + + // 1. Get the list of cache names (async call) + // 2. For each cache name, load the cache (async call) + // 3. Once each load is complete, update the map variables. + // 4. Call the list of waiting callbacks. + + scoped_ptr > indexed_cache_names( + new std::vector()); + + cache_loader_->LoadIndex( + indexed_cache_names.Pass(), + base::Bind(&ServiceWorkerCacheStorage::LazyInitDidLoadIndex, + weak_factory_.GetWeakPtr(), + callback)); +} + +void ServiceWorkerCacheStorage::LazyInitDidLoadIndex( + const base::Closure& callback, + scoped_ptr > indexed_cache_names) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (indexed_cache_names->empty()) { + LazyInitDone(); + return; + } + + std::vector::const_iterator iter = indexed_cache_names->begin(); + std::vector::const_iterator iter_next = iter + 1; + + cache_loader_->LoadCache( + *iter, + base::Bind(&ServiceWorkerCacheStorage::LazyInitIterateAndLoadCacheName, + weak_factory_.GetWeakPtr(), + callback, + base::Passed(indexed_cache_names.Pass()), + iter_next)); +} + +void ServiceWorkerCacheStorage::LazyInitIterateAndLoadCacheName( + const base::Closure& callback, + scoped_ptr > indexed_cache_names, + const std::vector::const_iterator& iter, + scoped_ptr cache) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (cache) + AddCacheToMaps(cache.Pass()); + + if (iter == indexed_cache_names->end()) { + LazyInitDone(); + return; + } + + std::vector::const_iterator iter_next = iter + 1; + cache_loader_->LoadCache( + *iter, + base::Bind(&ServiceWorkerCacheStorage::LazyInitIterateAndLoadCacheName, + weak_factory_.GetWeakPtr(), + callback, + base::Passed(indexed_cache_names.Pass()), + iter_next)); +} + +void ServiceWorkerCacheStorage::LazyInitDone() { + initialized_ = true; + for (std::vector::iterator it = init_callbacks_.begin(); + it != init_callbacks_.end(); + ++it) { + it->Run(); + } + init_callbacks_.clear(); +} + +void ServiceWorkerCacheStorage::AddCacheToMaps( + scoped_ptr cache) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCache* cache_ptr = cache.release(); + CacheID id = cache_map_.Add(cache_ptr); + name_map_.insert(std::make_pair(cache_ptr->name(), id)); + cache_ptr->set_id(id); +} + +void ServiceWorkerCacheStorage::CreateCacheDidCreateCache( + const std::string& cache_name, + const CacheAndErrorCallback& callback, + scoped_ptr cache) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (!cache) { + callback.Run(0, CACHE_STORAGE_ERROR_STORAGE); + return; + } + + base::WeakPtr cache_ptr = cache->AsWeakPtr(); + + AddCacheToMaps(cache.Pass()); + + cache_loader_->WriteIndex( + &cache_map_, + base::Bind( + &ServiceWorkerCacheStorage::CreateCacheDidWriteIndex, + weak_factory_.GetWeakPtr(), + callback, + cache_ptr)); // cache is owned by this->CacheMap and won't be deleted +} + +void ServiceWorkerCacheStorage::CreateCacheDidWriteIndex( + const CacheAndErrorCallback& callback, + base::WeakPtr cache, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + if (!cache) { + callback.Run(false, CACHE_STORAGE_ERROR_STORAGE); + return; + } + cache->CreateBackend(base::Bind(&ServiceWorkerCacheStorage::DidCreateBackend, + weak_factory_.GetWeakPtr(), + cache, + callback)); +} + +void ServiceWorkerCacheStorage::DeleteCacheDidWriteIndex( + const std::string& cache_name, + const BoolAndErrorCallback& callback, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + cache_loader_->CleanUpDeletedCache( + cache_name, + base::Bind(&ServiceWorkerCacheStorage::DeleteCacheDidCleanUp, + weak_factory_.GetWeakPtr(), + callback)); +} + +void ServiceWorkerCacheStorage::DeleteCacheDidCleanUp( + const BoolAndErrorCallback& callback, + bool success) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + callback.Run(true, CACHE_STORAGE_ERROR_NO_ERROR); +} + +ServiceWorkerCache* ServiceWorkerCacheStorage::GetLoadedCache( + const std::string& cache_name) const { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(initialized_); + + NameMap::const_iterator it = name_map_.find(cache_name); + if (it == name_map_.end()) + return NULL; + + ServiceWorkerCache* cache = cache_map_.Lookup(it->second); + DCHECK(cache); + return cache; +} + +} // namespace content diff --git a/content/browser/service_worker/service_worker_cache_storage.h b/content/browser/service_worker/service_worker_cache_storage.h new file mode 100644 index 0000000000000..71037b4327e69 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_storage.h @@ -0,0 +1,159 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_H_ + +#include +#include + +#include "base/callback.h" +#include "base/files/file_path.h" +#include "base/id_map.h" +#include "base/memory/weak_ptr.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace net { +class URLRequestContext; +} + +namespace webkit_blob { +class BlobStorageContext; +} + +namespace content { +class ServiceWorkerCache; + +// TODO(jkarlin): Constrain the total bytes used per origin. + +// ServiceWorkerCacheStorage holds the set of caches for a given origin. It is +// owned by the ServiceWorkerCacheStorageManager. This class expects to be run +// on the IO thread. +class ServiceWorkerCacheStorage { + public: + enum CacheStorageError { + CACHE_STORAGE_ERROR_NO_ERROR, + CACHE_STORAGE_ERROR_NOT_IMPLEMENTED, + CACHE_STORAGE_ERROR_NOT_FOUND, + CACHE_STORAGE_ERROR_EXISTS, + CACHE_STORAGE_ERROR_STORAGE, + CACHE_STORAGE_ERROR_EMPTY_KEY, + }; + + typedef base::Callback BoolAndErrorCallback; + typedef base::Callback CacheAndErrorCallback; + typedef base::Callback&, + CacheStorageError)> StringsAndErrorCallback; + + ServiceWorkerCacheStorage( + const base::FilePath& origin_path, + bool memory_only, + base::SequencedTaskRunner* cache_task_runner, + net::URLRequestContext* request_context, + base::WeakPtr blob_context); + + virtual ~ServiceWorkerCacheStorage(); + + // Create a ServiceWorkerCache if it doesn't already exist and call the + // callback with the cache's id. If it already + // exists the callback is called with CACHE_STORAGE_ERROR_EXISTS. + void CreateCache(const std::string& cache_name, + const CacheAndErrorCallback& callback); + + // Get the cache id for the given key. If not found returns + // CACHE_STORAGE_ERROR_NOT_FOUND. + void GetCache(const std::string& cache_name, + const CacheAndErrorCallback& callback); + + // Calls the callback with whether or not the cache exists. + void HasCache(const std::string& cache_name, + const BoolAndErrorCallback& callback); + + // Deletes the cache if it exists. If it doesn't exist, + // CACHE_STORAGE_ERROR_NOT_FOUND is returned. + void DeleteCache(const std::string& cache_name, + const BoolAndErrorCallback& callback); + + // Calls the callback with a vector of cache names (keys) available. + void EnumerateCaches(const StringsAndErrorCallback& callback); + + // TODO(jkarlin): Add match() function. + + private: + class MemoryLoader; + class SimpleCacheLoader; + class CacheLoader; + + typedef IDMap CacheMap; + typedef CacheMap::KeyType CacheID; + typedef std::map NameMap; + + ServiceWorkerCache* GetLoadedCache(const std::string& cache_name) const; + + // Initializer and its callback are below. + void LazyInit(const base::Closure& closure); + void LazyInitDidLoadIndex( + const base::Closure& callback, + scoped_ptr > indexed_cache_names); + void LazyInitIterateAndLoadCacheName( + const base::Closure& callback, + scoped_ptr > indexed_cache_names, + const std::vector::const_iterator& iter, + scoped_ptr cache); + void LazyInitDone(); + + void DidCreateBackend(base::WeakPtr cache, + const CacheAndErrorCallback& callback, + bool success); + + void AddCacheToMaps(scoped_ptr cache); + + // The CreateCache callbacks are below. + void CreateCacheDidCreateCache(const std::string& cache_name, + const CacheAndErrorCallback& callback, + scoped_ptr cache); + void CreateCacheDidWriteIndex(const CacheAndErrorCallback& callback, + base::WeakPtr cache, + bool success); + + // The DeleteCache callbacks are below. + void DeleteCacheDidWriteIndex(const std::string& cache_name, + const BoolAndErrorCallback& callback, + bool success); + void DeleteCacheDidCleanUp(const BoolAndErrorCallback& callback, + bool success); + + // Whether or not we've loaded the list of cache names into memory. + bool initialized_; + + // The list of operations waiting on initialization. + std::vector init_callbacks_; + + // The map of ServiceWorkerCache objects to their integer ids that + // ServiceWorkers reference. Owns the cache objects. + CacheMap cache_map_; + + // The map of cache names to their integer ids. + NameMap name_map_; + + // The file path for this CacheStorage. + base::FilePath origin_path_; + + // The TaskRunner to run file IO on. + scoped_refptr cache_task_runner_; + + // Performs backend specific operations (memory vs disk). + scoped_refptr cache_loader_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCacheStorage); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_H_ diff --git a/content/browser/service_worker/service_worker_cache_storage_manager.cc b/content/browser/service_worker/service_worker_cache_storage_manager.cc new file mode 100644 index 0000000000000..0e75354035afe --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_storage_manager.cc @@ -0,0 +1,170 @@ +// 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 "content/browser/service_worker/service_worker_cache_storage_manager.h" + +#include +#include + +#include "base/bind.h" +#include "base/id_map.h" +#include "base/sha1.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_util.h" +#include "content/browser/service_worker/service_worker_cache_storage.h" +#include "content/browser/service_worker/service_worker_context_core.h" +#include "content/public/browser/browser_thread.h" +#include "url/gurl.h" + +namespace { + +base::FilePath ConstructOriginPath(const base::FilePath& root_path, + const GURL& origin) { + std::string origin_hash = base::SHA1HashString(origin.spec()); + std::string origin_hash_hex = base::StringToLowerASCII( + base::HexEncode(origin_hash.c_str(), origin_hash.length())); + return root_path.AppendASCII(origin_hash_hex); +} + +} // namespace + +namespace content { + +// static +scoped_ptr +ServiceWorkerCacheStorageManager::Create( + const base::FilePath& path, + base::SequencedTaskRunner* cache_task_runner) { + base::FilePath root_path = path; + if (!path.empty()) { + root_path = path.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) + .AppendASCII("CacheStorage"); + } + + return make_scoped_ptr( + new ServiceWorkerCacheStorageManager(root_path, cache_task_runner)); +} + +// static +scoped_ptr +ServiceWorkerCacheStorageManager::Create( + ServiceWorkerCacheStorageManager* old_manager) { + scoped_ptr manager( + new ServiceWorkerCacheStorageManager(old_manager->root_path(), + old_manager->cache_task_runner())); + // These values may be NULL, in which case this will be called again later by + // the dispatcher host per usual. + manager->SetBlobParametersForCache(old_manager->url_request_context(), + old_manager->blob_storage_context()); + return manager.Pass(); +} + +ServiceWorkerCacheStorageManager::~ServiceWorkerCacheStorageManager() { + for (ServiceWorkerCacheStorageMap::iterator it = cache_storage_map_.begin(); + it != cache_storage_map_.end(); + ++it) { + delete it->second; + } +} + +void ServiceWorkerCacheStorageManager::CreateCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::CacheAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCacheStorage* cache_storage = + FindOrCreateServiceWorkerCacheManager(origin); + + cache_storage->CreateCache(cache_name, callback); +} + +void ServiceWorkerCacheStorageManager::GetCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::CacheAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCacheStorage* cache_storage = + FindOrCreateServiceWorkerCacheManager(origin); + + cache_storage->GetCache(cache_name, callback); +} + +void ServiceWorkerCacheStorageManager::HasCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::BoolAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCacheStorage* cache_storage = + FindOrCreateServiceWorkerCacheManager(origin); + cache_storage->HasCache(cache_name, callback); +} + +void ServiceWorkerCacheStorageManager::DeleteCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::BoolAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCacheStorage* cache_storage = + FindOrCreateServiceWorkerCacheManager(origin); + cache_storage->DeleteCache(cache_name, callback); +} + +void ServiceWorkerCacheStorageManager::EnumerateCaches( + const GURL& origin, + const ServiceWorkerCacheStorage::StringsAndErrorCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + ServiceWorkerCacheStorage* cache_storage = + FindOrCreateServiceWorkerCacheManager(origin); + + cache_storage->EnumerateCaches(callback); +} + +void ServiceWorkerCacheStorageManager::SetBlobParametersForCache( + net::URLRequestContext* request_context, + base::WeakPtr blob_storage_context) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(cache_storage_map_.empty()); + DCHECK(!request_context_ || request_context_ == request_context); + DCHECK(!blob_context_ || blob_context_.get() == blob_storage_context.get()); + request_context_ = request_context; + blob_context_ = blob_storage_context; +} + +ServiceWorkerCacheStorageManager::ServiceWorkerCacheStorageManager( + const base::FilePath& path, + base::SequencedTaskRunner* cache_task_runner) + : root_path_(path), + cache_task_runner_(cache_task_runner), + request_context_(NULL) { +} + +ServiceWorkerCacheStorage* +ServiceWorkerCacheStorageManager::FindOrCreateServiceWorkerCacheManager( + const GURL& origin) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + DCHECK(request_context_); + + ServiceWorkerCacheStorageMap::const_iterator it = + cache_storage_map_.find(origin); + if (it == cache_storage_map_.end()) { + bool memory_only = root_path_.empty(); + ServiceWorkerCacheStorage* cache_storage = + new ServiceWorkerCacheStorage(ConstructOriginPath(root_path_, origin), + memory_only, + cache_task_runner_, + request_context_, + blob_context_); + // The map owns fetch_stores. + cache_storage_map_.insert(std::make_pair(origin, cache_storage)); + return cache_storage; + } + return it->second; +} + +} // namespace content diff --git a/content/browser/service_worker/service_worker_cache_storage_manager.h b/content/browser/service_worker/service_worker_cache_storage_manager.h new file mode 100644 index 0000000000000..ff5028c7b96df --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_storage_manager.h @@ -0,0 +1,114 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_MANAGER_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_MANAGER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/files/file_path.h" +#include "content/browser/service_worker/service_worker_cache_storage.h" +#include "content/common/content_export.h" +#include "url/gurl.h" + +namespace base { +class SequencedTaskRunner; +} + +namespace net { +class URLRequestContext; +} + +namespace webkit_blob { +class BlobStorageContext; +} + +namespace content { + +// Keeps track of a ServiceWorkerCacheStorage per origin. There is one +// ServiceWorkerCacheStorageManager per ServiceWorkerContextCore. +// TODO(jkarlin): Remove ServiceWorkerCacheStorage from memory once they're no +// longer in active use. +class CONTENT_EXPORT ServiceWorkerCacheStorageManager { + public: + static scoped_ptr Create( + const base::FilePath& path, + base::SequencedTaskRunner* cache_task_runner); + + static scoped_ptr Create( + ServiceWorkerCacheStorageManager* old_manager); + + virtual ~ServiceWorkerCacheStorageManager(); + + // Methods to support the CacheStorage spec. These methods call the + // corresponding ServiceWorkerCacheStorage method on the appropriate thread. + void CreateCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::CacheAndErrorCallback& callback); + void GetCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::CacheAndErrorCallback& callback); + void HasCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::BoolAndErrorCallback& callback); + void DeleteCache( + const GURL& origin, + const std::string& cache_name, + const ServiceWorkerCacheStorage::BoolAndErrorCallback& callback); + void EnumerateCaches( + const GURL& origin, + const ServiceWorkerCacheStorage::StringsAndErrorCallback& callback); + // TODO(jkarlin): Add match() function. + + // This must be called before creating any of the public *Cache functions + // above. + void SetBlobParametersForCache( + net::URLRequestContext* request_context, + base::WeakPtr blob_storage_context); + + private: + typedef std::map + ServiceWorkerCacheStorageMap; + + ServiceWorkerCacheStorageManager( + const base::FilePath& path, + base::SequencedTaskRunner* cache_task_runner); + + // The returned ServiceWorkerCacheStorage* is owned by + // service_worker_cache_storages_. + ServiceWorkerCacheStorage* FindOrCreateServiceWorkerCacheManager( + const GURL& origin); + + net::URLRequestContext* url_request_context() const { + return request_context_; + } + base::WeakPtr blob_storage_context() const { + return blob_context_; + } + base::FilePath root_path() const { return root_path_; } + scoped_refptr cache_task_runner() const { + return cache_task_runner_; + } + + base::FilePath root_path_; + scoped_refptr cache_task_runner_; + + // The map owns the CacheStorages and the CacheStorages are only accessed on + // |cache_task_runner_|. + ServiceWorkerCacheStorageMap cache_storage_map_; + + net::URLRequestContext* request_context_; + base::WeakPtr blob_context_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCacheStorageManager); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_MANAGER_H_ diff --git a/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc b/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc new file mode 100644 index 0000000000000..d02dc3d9d6f26 --- /dev/null +++ b/content/browser/service_worker/service_worker_cache_storage_manager_unittest.cc @@ -0,0 +1,377 @@ +// 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 "content/browser/service_worker/service_worker_cache_storage_manager.h" + +#include "base/files/file_path.h" +#include "base/files/scoped_temp_dir.h" +#include "base/message_loop/message_loop_proxy.h" +#include "base/run_loop.h" +#include "content/browser/fileapi/chrome_blob_storage_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/test/test_browser_context.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "net/url_request/url_request_context_getter.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/browser/blob/blob_storage_context.h" + +namespace content { + +class ServiceWorkerCacheStorageManagerTest : public testing::Test { + protected: + ServiceWorkerCacheStorageManagerTest() + : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), + callback_bool_(false), + callback_cache_id_(0), + callback_error_( + ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR), + origin1_("http://example1.com"), + origin2_("http://example2.com") {} + + virtual void SetUp() OVERRIDE { + ChromeBlobStorageContext* blob_storage_context( + ChromeBlobStorageContext::GetFor(&browser_context_)); + // Wait for ChromeBlobStorageContext to finish initializing. + base::RunLoop().RunUntilIdle(); + + net::URLRequestContext* url_request_context = + browser_context_.GetRequestContext()->GetURLRequestContext(); + if (MemoryOnly()) { + cache_manager_ = ServiceWorkerCacheStorageManager::Create( + base::FilePath(), base::MessageLoopProxy::current()); + } else { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + cache_manager_ = ServiceWorkerCacheStorageManager::Create( + temp_dir_.path(), base::MessageLoopProxy::current()); + } + + cache_manager_->SetBlobParametersForCache( + url_request_context, blob_storage_context->context()->AsWeakPtr()); + } + + virtual bool MemoryOnly() { return false; } + + void BoolAndErrorCallback( + base::RunLoop* run_loop, + bool value, + ServiceWorkerCacheStorage::CacheStorageError error) { + callback_bool_ = value; + callback_error_ = error; + run_loop->Quit(); + } + + void CacheAndErrorCallback( + base::RunLoop* run_loop, + int cache_id, + ServiceWorkerCacheStorage::CacheStorageError error) { + callback_cache_id_ = cache_id; + callback_error_ = error; + run_loop->Quit(); + } + + void StringsAndErrorCallback( + base::RunLoop* run_loop, + const std::vector& strings, + ServiceWorkerCacheStorage::CacheStorageError error) { + callback_strings_ = strings; + callback_error_ = error; + run_loop->Quit(); + } + + bool CreateCache(const GURL& origin, const std::string& cache_name) { + scoped_ptr loop(new base::RunLoop()); + cache_manager_->CreateCache( + origin, + cache_name, + base::Bind(&ServiceWorkerCacheStorageManagerTest::CacheAndErrorCallback, + base::Unretained(this), + base::Unretained(loop.get()))); + loop->Run(); + + bool error = callback_error_ != + ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR; + if (error) + EXPECT_EQ(0, callback_cache_id_); + else + EXPECT_LT(0, callback_cache_id_); + return !error; + } + + bool Get(const GURL& origin, const std::string& cache_name) { + scoped_ptr loop(new base::RunLoop()); + cache_manager_->GetCache( + origin, + cache_name, + base::Bind(&ServiceWorkerCacheStorageManagerTest::CacheAndErrorCallback, + base::Unretained(this), + base::Unretained(loop.get()))); + loop->Run(); + + bool error = callback_error_ != + ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR; + if (error) + EXPECT_EQ(0, callback_cache_id_); + else + EXPECT_LT(0, callback_cache_id_); + return !error; + } + + bool Has(const GURL& origin, const std::string& cache_name) { + scoped_ptr loop(new base::RunLoop()); + cache_manager_->HasCache( + origin, + cache_name, + base::Bind(&ServiceWorkerCacheStorageManagerTest::BoolAndErrorCallback, + base::Unretained(this), + base::Unretained(loop.get()))); + loop->Run(); + + return callback_bool_; + } + + bool Delete(const GURL& origin, const std::string& cache_name) { + scoped_ptr loop(new base::RunLoop()); + cache_manager_->DeleteCache( + origin, + cache_name, + base::Bind(&ServiceWorkerCacheStorageManagerTest::BoolAndErrorCallback, + base::Unretained(this), + base::Unretained(loop.get()))); + loop->Run(); + + return callback_bool_; + } + + bool Keys(const GURL& origin) { + scoped_ptr loop(new base::RunLoop()); + cache_manager_->EnumerateCaches( + origin, + base::Bind( + &ServiceWorkerCacheStorageManagerTest::StringsAndErrorCallback, + base::Unretained(this), + base::Unretained(loop.get()))); + loop->Run(); + + bool error = callback_error_ != + ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NO_ERROR; + return !error; + } + + bool VerifyKeys(const std::vector& expected_keys) { + if (expected_keys.size() != callback_strings_.size()) + return false; + + std::set found_set; + for (int i = 0, max = callback_strings_.size(); i < max; ++i) + found_set.insert(callback_strings_[i]); + + for (int i = 0, max = expected_keys.size(); i < max; ++i) { + if (found_set.find(expected_keys[i]) == found_set.end()) + return false; + } + return true; + } + + protected: + TestBrowserContext browser_context_; + TestBrowserThreadBundle browser_thread_bundle_; + + base::ScopedTempDir temp_dir_; + scoped_ptr cache_manager_; + + int callback_bool_; + int callback_cache_id_; + ServiceWorkerCacheStorage::CacheStorageError callback_error_; + std::vector callback_strings_; + + const GURL origin1_; + const GURL origin2_; + + private: + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCacheStorageManagerTest); +}; + +class ServiceWorkerCacheStorageManagerMemoryOnlyTest + : public ServiceWorkerCacheStorageManagerTest { + virtual bool MemoryOnly() OVERRIDE { return true; } +}; + +class ServiceWorkerCacheStorageManagerTestP + : public ServiceWorkerCacheStorageManagerTest, + public testing::WithParamInterface { + virtual bool MemoryOnly() OVERRIDE { return !GetParam(); } +}; + +TEST_F(ServiceWorkerCacheStorageManagerTest, TestsRunOnIOThread) { + EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, CreateCache) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, CreateDuplicateCache) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_FALSE(CreateCache(origin1_, "foo")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EXISTS, + callback_error_); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, CreateTwoCaches) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(CreateCache(origin1_, "bar")); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, Create2CachesSameNameDiffSWs) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(CreateCache(origin2_, "foo")); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, GetCache) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + int cache_id = callback_cache_id_; + EXPECT_TRUE(Get(origin1_, "foo")); + EXPECT_EQ(cache_id, callback_cache_id_); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, GetNonExistent) { + EXPECT_FALSE(Get(origin1_, "foo")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NOT_FOUND, + callback_error_); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, HasCache) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(Has(origin1_, "foo")); + EXPECT_TRUE(callback_bool_); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, HasNonExistent) { + EXPECT_FALSE(Has(origin1_, "foo")); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, DeleteCache) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(Delete(origin1_, "foo")); + EXPECT_FALSE(Get(origin1_, "foo")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NOT_FOUND, + callback_error_); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, DeleteTwice) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(Delete(origin1_, "foo")); + EXPECT_FALSE(Delete(origin1_, "foo")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_NOT_FOUND, + callback_error_); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, EmptyKeys) { + EXPECT_TRUE(Keys(origin1_)); + EXPECT_TRUE(callback_strings_.empty()); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, SomeKeys) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(CreateCache(origin1_, "bar")); + EXPECT_TRUE(CreateCache(origin2_, "baz")); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(2u, callback_strings_.size()); + std::vector expected_keys; + expected_keys.push_back("foo"); + expected_keys.push_back("bar"); + EXPECT_TRUE(VerifyKeys(expected_keys)); + EXPECT_TRUE(Keys(origin2_)); + EXPECT_EQ(1u, callback_strings_.size()); + EXPECT_STREQ("baz", callback_strings_[0].c_str()); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, DeletedKeysGone) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(CreateCache(origin1_, "bar")); + EXPECT_TRUE(CreateCache(origin2_, "baz")); + EXPECT_TRUE(Delete(origin1_, "bar")); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(1u, callback_strings_.size()); + EXPECT_STREQ("foo", callback_strings_[0].c_str()); +} + +TEST_P(ServiceWorkerCacheStorageManagerTestP, Chinese) { + EXPECT_TRUE(CreateCache(origin1_, "你好")); + int cache_id = callback_cache_id_; + EXPECT_TRUE(Get(origin1_, "你好")); + EXPECT_EQ(cache_id, callback_cache_id_); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(1u, callback_strings_.size()); + EXPECT_TRUE("你好" == callback_strings_[0]); +} + +TEST_F(ServiceWorkerCacheStorageManagerTest, EmptyKey) { + EXPECT_FALSE(CreateCache(origin1_, "")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EMPTY_KEY, + callback_error_); + + EXPECT_FALSE(Get(origin1_, "")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EMPTY_KEY, + callback_error_); + + EXPECT_FALSE(Has(origin1_, "")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EMPTY_KEY, + callback_error_); + + EXPECT_FALSE(Delete(origin1_, "")); + EXPECT_EQ(ServiceWorkerCacheStorage::CACHE_STORAGE_ERROR_EMPTY_KEY, + callback_error_); +} + +TEST_F(ServiceWorkerCacheStorageManagerTest, DataPersists) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(CreateCache(origin1_, "bar")); + EXPECT_TRUE(CreateCache(origin1_, "baz")); + EXPECT_TRUE(CreateCache(origin2_, "raz")); + EXPECT_TRUE(Delete(origin1_, "bar")); + cache_manager_ = + ServiceWorkerCacheStorageManager::Create(cache_manager_.get()); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(2u, callback_strings_.size()); + std::vector expected_keys; + expected_keys.push_back("foo"); + expected_keys.push_back("baz"); + EXPECT_TRUE(VerifyKeys(expected_keys)); +} + +TEST_F(ServiceWorkerCacheStorageManagerMemoryOnlyTest, DataLostWhenMemoryOnly) { + EXPECT_TRUE(CreateCache(origin1_, "foo")); + EXPECT_TRUE(CreateCache(origin2_, "baz")); + cache_manager_ = + ServiceWorkerCacheStorageManager::Create(cache_manager_.get()); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(0u, callback_strings_.size()); +} + +TEST_F(ServiceWorkerCacheStorageManagerTest, BadCacheName) { + // Since the implementation writes cache names to disk, ensure that we don't + // escape the directory. + const std::string bad_name = "../../../../../../../../../../../../../../foo"; + EXPECT_TRUE(CreateCache(origin1_, bad_name)); + EXPECT_TRUE(Keys(origin1_)); + EXPECT_EQ(1u, callback_strings_.size()); + EXPECT_STREQ(bad_name.c_str(), callback_strings_[0].c_str()); +} + +TEST_F(ServiceWorkerCacheStorageManagerTest, BadOriginName) { + // Since the implementation writes origin names to disk, ensure that we don't + // escape the directory. + GURL bad_origin("../../../../../../../../../../../../../../foo"); + EXPECT_TRUE(CreateCache(bad_origin, "foo")); + EXPECT_TRUE(Keys(bad_origin)); + EXPECT_EQ(1u, callback_strings_.size()); + EXPECT_STREQ("foo", callback_strings_[0].c_str()); +} + +INSTANTIATE_TEST_CASE_P(ServiceWorkerCacheStorageManagerTests, + ServiceWorkerCacheStorageManagerTestP, + ::testing::Values(false, true)); + +} // namespace content diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc index 88872fe632df6..b735cfb0e2ac8 100644 --- a/content/browser/service_worker/service_worker_context_core.cc +++ b/content/browser/service_worker/service_worker_context_core.cc @@ -8,9 +8,9 @@ #include "base/message_loop/message_loop_proxy.h" #include "base/strings/string_util.h" #include "content/browser/service_worker/embedded_worker_registry.h" +#include "content/browser/service_worker/service_worker_cache_storage_manager.h" #include "content/browser/service_worker/service_worker_context_observer.h" #include "content/browser/service_worker/service_worker_context_wrapper.h" -#include "content/browser/service_worker/service_worker_fetch_stores_manager.h" #include "content/browser/service_worker/service_worker_info.h" #include "content/browser/service_worker/service_worker_job_coordinator.h" #include "content/browser/service_worker/service_worker_process_manager.h" @@ -83,7 +83,7 @@ void ServiceWorkerContextCore::ProviderHostIterator::Initialize() { ServiceWorkerContextCore::ServiceWorkerContextCore( const base::FilePath& path, - base::SequencedTaskRunner* stores_task_runner, + base::SequencedTaskRunner* cache_task_runner, base::SequencedTaskRunner* database_task_runner, base::MessageLoopProxy* disk_cache_thread, quota::QuotaManagerProxy* quota_manager_proxy, @@ -92,17 +92,17 @@ ServiceWorkerContextCore::ServiceWorkerContextCore( : weak_factory_(this), wrapper_(wrapper), providers_(new ProcessToProviderMap), - storage_(ServiceWorkerStorage::Create( - path, - AsWeakPtr(), - database_task_runner, - disk_cache_thread, - quota_manager_proxy)), - fetch_stores_manager_( - ServiceWorkerFetchStoresManager::Create(path, stores_task_runner)), + storage_(ServiceWorkerStorage::Create(path, + AsWeakPtr(), + database_task_runner, + disk_cache_thread, + quota_manager_proxy)), + cache_manager_( + ServiceWorkerCacheStorageManager::Create(path, cache_task_runner)), embedded_worker_registry_(EmbeddedWorkerRegistry::Create(AsWeakPtr())), job_coordinator_(new ServiceWorkerJobCoordinator(AsWeakPtr())), next_handle_id_(0), + next_registration_handle_id_(0), observer_list_(observer_list) { } @@ -114,13 +114,14 @@ ServiceWorkerContextCore::ServiceWorkerContextCore( providers_(old_context->providers_.release()), storage_( ServiceWorkerStorage::Create(AsWeakPtr(), old_context->storage())), - fetch_stores_manager_(ServiceWorkerFetchStoresManager::Create( - old_context->fetch_stores_manager())), + cache_manager_(ServiceWorkerCacheStorageManager::Create( + old_context->cache_manager())), embedded_worker_registry_(EmbeddedWorkerRegistry::Create( AsWeakPtr(), old_context->embedded_worker_registry())), job_coordinator_(new ServiceWorkerJobCoordinator(AsWeakPtr())), next_handle_id_(0), + next_registration_handle_id_(0), observer_list_(old_context->observer_list_) { } @@ -318,6 +319,10 @@ int ServiceWorkerContextCore::GetNewServiceWorkerHandleId() { return next_handle_id_++; } +int ServiceWorkerContextCore::GetNewRegistrationHandleId() { + return next_registration_handle_id_++; +} + void ServiceWorkerContextCore::ScheduleDeleteAndStartOver() const { storage_->Disable(); base::MessageLoop::current()->PostTask( @@ -331,6 +336,15 @@ void ServiceWorkerContextCore::DeleteAndStartOver( storage_->DeleteAndStartOver(callback); } +void ServiceWorkerContextCore::SetBlobParametersForCache( + net::URLRequestContext* request_context, + base::WeakPtr blob_storage_context) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + cache_manager_->SetBlobParametersForCache(request_context, + blob_storage_context); +} + void ServiceWorkerContextCore::OnWorkerStarted(ServiceWorkerVersion* version) { if (!observer_list_) return; diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h index 86f43c55ee3f4..11ee722041bf7 100644 --- a/content/browser/service_worker/service_worker_context_core.h +++ b/content/browser/service_worker/service_worker_context_core.h @@ -29,6 +29,10 @@ class MessageLoopProxy; class SequencedTaskRunner; } +namespace net { +class URLRequestContext; +} + namespace quota { class QuotaManagerProxy; } @@ -36,7 +40,7 @@ class QuotaManagerProxy; namespace content { class EmbeddedWorkerRegistry; -class ServiceWorkerFetchStoresManager; +class ServiceWorkerCacheStorageManager; class ServiceWorkerContextObserver; class ServiceWorkerContextWrapper; class ServiceWorkerHandle; @@ -62,7 +66,7 @@ class CONTENT_EXPORT ServiceWorkerContextCore typedef IDMap ProviderMap; typedef IDMap ProcessToProviderMap; - // Directory for ServiceWorkerStorage and ServiceWorkerFetchStores. + // Directory for ServiceWorkerStorage and ServiceWorkerCacheManager. static const base::FilePath::CharType kServiceWorkerDirectory[]; // Iterates over ServiceWorkerProviderHost objects in a ProcessToProviderMap. @@ -93,7 +97,7 @@ class CONTENT_EXPORT ServiceWorkerContextCore // be called on the thread which called AddObserver() of |observer_list|. ServiceWorkerContextCore( const base::FilePath& user_data_directory, - base::SequencedTaskRunner* stores_task_runner, + base::SequencedTaskRunner* cache_task_runner, base::SequencedTaskRunner* database_task_runner, base::MessageLoopProxy* disk_cache_thread, quota::QuotaManagerProxy* quota_manager_proxy, @@ -121,8 +125,8 @@ class CONTENT_EXPORT ServiceWorkerContextCore const GURL& source_url) OVERRIDE; ServiceWorkerStorage* storage() { return storage_.get(); } - ServiceWorkerFetchStoresManager* fetch_stores_manager() { - return fetch_stores_manager_.get(); + ServiceWorkerCacheStorageManager* cache_manager() { + return cache_manager_.get(); } ServiceWorkerProcessManager* process_manager(); EmbeddedWorkerRegistry* embedded_worker_registry() { @@ -164,8 +168,9 @@ class CONTENT_EXPORT ServiceWorkerContextCore std::vector GetAllLiveRegistrationInfo(); std::vector GetAllLiveVersionInfo(); - // Returns new context-local unique ID for ServiceWorkerHandle. + // Returns new context-local unique ID. int GetNewServiceWorkerHandleId(); + int GetNewRegistrationHandleId(); void ScheduleDeleteAndStartOver() const; @@ -173,6 +178,10 @@ class CONTENT_EXPORT ServiceWorkerContextCore // in a disabled state until it's done. void DeleteAndStartOver(const StatusCallback& callback); + void SetBlobParametersForCache( + net::URLRequestContext* request_context, + base::WeakPtr blob_storage_context); + base::WeakPtr AsWeakPtr() { return weak_factory_.GetWeakPtr(); } @@ -202,12 +211,13 @@ class CONTENT_EXPORT ServiceWorkerContextCore ServiceWorkerContextWrapper* wrapper_; scoped_ptr providers_; scoped_ptr storage_; - scoped_ptr fetch_stores_manager_; + scoped_ptr cache_manager_; scoped_refptr embedded_worker_registry_; scoped_ptr job_coordinator_; std::map live_registrations_; std::map live_versions_; int next_handle_id_; + int next_registration_handle_id_; scoped_refptr > observer_list_; diff --git a/content/browser/service_worker/service_worker_context_request_handler.cc b/content/browser/service_worker/service_worker_context_request_handler.cc index 45248930d18cd..1da3233668c4b 100644 --- a/content/browser/service_worker/service_worker_context_request_handler.cc +++ b/content/browser/service_worker/service_worker_context_request_handler.cc @@ -10,6 +10,7 @@ #include "content/browser/service_worker/service_worker_storage.h" #include "content/browser/service_worker/service_worker_version.h" #include "content/browser/service_worker/service_worker_write_to_cache_job.h" +#include "net/base/load_flags.h" #include "net/url_request/url_request.h" namespace content { @@ -51,14 +52,28 @@ net::URLRequestJob* ServiceWorkerContextRequestHandler::MaybeCreateJob( } if (ShouldAddToScriptCache(request->url())) { + ServiceWorkerRegistration* registration = + context_->GetLiveRegistration(version_->registration_id()); + DCHECK(registration); // We're registering or updating so must be there. + int64 response_id = context_->storage()->NewResourceId(); if (response_id == kInvalidServiceWorkerResponseId) return NULL; + + // Bypass the browser cache for initial installs and update + // checks after 24 hours have passed. + int extra_load_flags = 0; + base::TimeDelta time_since_last_check = + base::Time::Now() - registration->last_update_check(); + if (time_since_last_check > base::TimeDelta::FromHours(24)) + extra_load_flags = net::LOAD_BYPASS_CACHE; + return new ServiceWorkerWriteToCacheJob(request, network_delegate, resource_type_, context_, version_, + extra_load_flags, response_id); } diff --git a/content/browser/service_worker/service_worker_context_request_handler_unittest.cc b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc new file mode 100644 index 0000000000000..94a0046b465af --- /dev/null +++ b/content/browser/service_worker/service_worker_context_request_handler_unittest.cc @@ -0,0 +1,141 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/scoped_temp_dir.h" +#include "base/logging.h" +#include "base/run_loop.h" +#include "content/browser/browser_thread_impl.h" +#include "content/browser/fileapi/mock_url_request_delegate.h" +#include "content/browser/service_worker/embedded_worker_test_helper.h" +#include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_context_request_handler.h" +#include "content/browser/service_worker/service_worker_provider_host.h" +#include "content/browser/service_worker/service_worker_registration.h" +#include "content/browser/service_worker/service_worker_utils.h" +#include "content/browser/service_worker/service_worker_write_to_cache_job.h" +#include "content/public/test/test_browser_thread_bundle.h" +#include "net/base/load_flags.h" +#include "net/url_request/url_request_context.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace content { + +namespace { + +int kMockRenderProcessId = 1224; + +void EmptyCallback() {} + +} + +class ServiceWorkerContextRequestHandlerTest : public testing::Test { + public: + ServiceWorkerContextRequestHandlerTest() + : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} + + virtual void SetUp() OVERRIDE { + helper_.reset(new EmbeddedWorkerTestHelper(kMockRenderProcessId)); + + // A new unstored registration/version. + scope_ = GURL("http://host/scope/"); + script_url_ = GURL("http://host/script.js"); + registration_ = new ServiceWorkerRegistration( + scope_, script_url_, 1L, context()->AsWeakPtr()); + version_ = new ServiceWorkerVersion( + registration_, + 1L, context()->AsWeakPtr()); + + // An empty host. + scoped_ptr host(new ServiceWorkerProviderHost( + kMockRenderProcessId, 1 /* provider_id */, + context()->AsWeakPtr(), NULL)); + provider_host_ = host->AsWeakPtr(); + context()->AddProviderHost(host.Pass()); + + context()->storage()->LazyInitialize(base::Bind(&EmptyCallback)); + base::RunLoop().RunUntilIdle(); + } + + virtual void TearDown() OVERRIDE { + version_ = NULL; + registration_ = NULL; + helper_.reset(); + } + + ServiceWorkerContextCore* context() const { return helper_->context(); } + + protected: + TestBrowserThreadBundle browser_thread_bundle_; + scoped_ptr helper_; + scoped_refptr registration_; + scoped_refptr version_; + base::WeakPtr provider_host_; + net::URLRequestContext url_request_context_; + MockURLRequestDelegate url_request_delegate_; + GURL scope_; + GURL script_url_; +}; + +TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateBefore24Hours) { + // Give the registration a very recent last update time and pretend + // we're installing a new version. + registration_->set_last_update_check(base::Time::Now()); + version_->SetStatus(ServiceWorkerVersion::NEW); + provider_host_->running_hosted_version_ = version_; + + // Conduct a resource fetch for the main script. + const GURL kScriptUrl("http://host/script.js"); + scoped_ptr request = url_request_context_.CreateRequest( + kScriptUrl, + net::DEFAULT_PRIORITY, + &url_request_delegate_, + NULL); + scoped_ptr handler( + new ServiceWorkerContextRequestHandler( + context()->AsWeakPtr(), + provider_host_, + base::WeakPtr(), + RESOURCE_TYPE_SERVICE_WORKER)); + scoped_refptr job = + handler->MaybeCreateJob(request.get(), NULL); + ASSERT_TRUE(job); + ServiceWorkerWriteToCacheJob* sw_job = + static_cast(job.get()); + + // Verify the net request is not initialized to bypass the browser cache. + EXPECT_FALSE(sw_job->net_request_->load_flags() & net::LOAD_BYPASS_CACHE); +} + +TEST_F(ServiceWorkerContextRequestHandlerTest, UpdateAfter24Hours) { + // Give the registration a old update time and pretend + // we're installing a new version. + registration_->set_last_update_check( + base::Time::Now() - base::TimeDelta::FromDays(7)); + version_->SetStatus(ServiceWorkerVersion::NEW); + provider_host_->running_hosted_version_ = version_; + + // Conduct a resource fetch for the main script. + const GURL kScriptUrl("http://host/script.js"); + scoped_ptr request = url_request_context_.CreateRequest( + kScriptUrl, + net::DEFAULT_PRIORITY, + &url_request_delegate_, + NULL); + scoped_ptr handler( + new ServiceWorkerContextRequestHandler( + context()->AsWeakPtr(), + provider_host_, + base::WeakPtr(), + RESOURCE_TYPE_SERVICE_WORKER)); + scoped_refptr job = + handler->MaybeCreateJob(request.get(), NULL); + ASSERT_TRUE(job); + ServiceWorkerWriteToCacheJob* sw_job = + static_cast(job.get()); + + // Verify the net request is initialized to bypass the browser cache. + EXPECT_TRUE(sw_job->net_request_->load_flags() & net::LOAD_BYPASS_CACHE); +} + +} // namespace content diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc index 21ecc73c28345..d3f776dd341c1 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.cc +++ b/content/browser/service_worker/service_worker_context_wrapper.cc @@ -4,13 +4,19 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" +#include + #include "base/files/file_path.h" #include "base/logging.h" #include "base/threading/sequenced_worker_pool.h" +#include "content/browser/fileapi/chrome_blob_storage_context.h" #include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_context_observer.h" #include "content/browser/service_worker/service_worker_process_manager.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" +#include "net/url_request/url_request_context_getter.h" +#include "webkit/browser/blob/blob_storage_context.h" #include "webkit/browser/quota/quota_manager_proxy.h" namespace content { @@ -37,13 +43,13 @@ void ServiceWorkerContextWrapper::Init( base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); scoped_refptr disk_cache_thread = BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE); - scoped_refptr stores_task_runner = + scoped_refptr cache_task_runner = BrowserThread::GetBlockingPool() ->GetSequencedTaskRunnerWithShutdownBehavior( BrowserThread::GetBlockingPool()->GetSequenceToken(), base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); InitInternal(user_data_directory, - stores_task_runner, + cache_task_runner, database_task_runner, disk_cache_thread, quota_manager_proxy); @@ -139,6 +145,77 @@ void ServiceWorkerContextWrapper::Terminate() { process_manager_->Shutdown(); } +void ServiceWorkerContextWrapper::GetAllOriginsInfo( + const GetUsageInfoCallback& callback) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + context_core_->storage()->GetAllRegistrations(base::Bind( + &ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins, + this, + callback)); +} + +void ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins( + const GetUsageInfoCallback& callback, + const std::vector& registrations) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + std::vector usage_infos; + + std::map origins; + for (std::vector::const_iterator it = + registrations.begin(); + it != registrations.end(); + ++it) { + const ServiceWorkerRegistrationInfo& registration_info = *it; + GURL origin = registration_info.script_url.GetOrigin(); + + ServiceWorkerUsageInfo& usage_info = origins[origin]; + if (usage_info.origin.is_empty()) + usage_info.origin = origin; + usage_info.scopes.push_back(registration_info.pattern); + } + + for (std::map::const_iterator it = + origins.begin(); + it != origins.end(); + ++it) { + usage_infos.push_back(it->second); + } + + callback.Run(usage_infos); +} + +namespace { + +void EmptySuccessCallback(bool success) { +} + +} // namespace + +void ServiceWorkerContextWrapper::DeleteForOrigin(const GURL& origin_url) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + context_core_->storage()->GetAllRegistrations(base::Bind( + &ServiceWorkerContextWrapper::DidGetAllRegistrationsForDeleteForOrigin, + this, + origin_url)); +} + +void ServiceWorkerContextWrapper::DidGetAllRegistrationsForDeleteForOrigin( + const GURL& origin, + const std::vector& registrations) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + for (std::vector::const_iterator it = + registrations.begin(); + it != registrations.end(); + ++it) { + const ServiceWorkerRegistrationInfo& registration_info = *it; + if (origin == registration_info.script_url.GetOrigin()) { + UnregisterServiceWorker(registration_info.pattern, + base::Bind(&EmptySuccessCallback)); + } + } +} + void ServiceWorkerContextWrapper::AddObserver( ServiceWorkerContextObserver* observer) { observer_list_->AddObserver(observer); @@ -149,6 +226,18 @@ void ServiceWorkerContextWrapper::RemoveObserver( observer_list_->RemoveObserver(observer); } +void ServiceWorkerContextWrapper::SetBlobParametersForCache( + net::URLRequestContextGetter* request_context, + ChromeBlobStorageContext* blob_storage_context) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + + if (context_core_ && request_context && blob_storage_context) { + context_core_->SetBlobParametersForCache( + request_context->GetURLRequestContext(), + blob_storage_context->context()->AsWeakPtr()); + } +} + void ServiceWorkerContextWrapper::InitInternal( const base::FilePath& user_data_directory, base::SequencedTaskRunner* stores_task_runner, diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h index e593b240bf04a..4aa718c1c6b26 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.h +++ b/content/browser/service_worker/service_worker_context_wrapper.h @@ -20,6 +20,10 @@ class MessageLoopProxy; class SequencedTaskRunner; } +namespace net { +class URLRequestContextGetter; +} + namespace quota { class QuotaManagerProxy; } @@ -27,6 +31,7 @@ class QuotaManagerProxy; namespace content { class BrowserContext; +class ChromeBlobStorageContext; class ServiceWorkerContextCore; class ServiceWorkerContextObserver; @@ -68,12 +73,24 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper const ResultCallback& continuation) OVERRIDE; virtual void Terminate() OVERRIDE; + virtual void GetAllOriginsInfo(const GetUsageInfoCallback& callback) OVERRIDE; + virtual void DeleteForOrigin(const GURL& origin_url) OVERRIDE; void AddObserver(ServiceWorkerContextObserver* observer); void RemoveObserver(ServiceWorkerContextObserver* observer); bool is_incognito() const { return is_incognito_; } + // The URLRequestContext doesn't exist until after the StoragePartition is + // made (which is after this object is made). This function must be called + // after this object is created but before any ServiceWorkerCache operations. + // It must be called on the IO thread. If either parameter is NULL the + // function immediately returns without forwarding to the + // ServiceWorkerCacheStorageManager. + void SetBlobParametersForCache( + net::URLRequestContextGetter* request_context, + ChromeBlobStorageContext* blob_storage_context); + private: friend class base::RefCountedThreadSafe; friend class EmbeddedWorkerTestHelper; @@ -89,6 +106,13 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper void DidDeleteAndStartOver(ServiceWorkerStatusCode status); + void DidGetAllRegistrationsForGetAllOrigins( + const GetUsageInfoCallback& callback, + const std::vector& registrations); + void DidGetAllRegistrationsForDeleteForOrigin( + const GURL& origin, + const std::vector& registrations); + const scoped_refptr > observer_list_; const scoped_ptr process_manager_; diff --git a/content/browser/service_worker/service_worker_controllee_request_handler.cc b/content/browser/service_worker/service_worker_controllee_request_handler.cc index bdea9e1f859fc..bd8205b4e90a4 100644 --- a/content/browser/service_worker/service_worker_controllee_request_handler.cc +++ b/content/browser/service_worker/service_worker_controllee_request_handler.cc @@ -114,10 +114,7 @@ void ServiceWorkerControlleeRequestHandler::PrepareForMainResource( DCHECK(context_); // The corresponding provider_host may already have associated a registration // in redirect case, unassociate it now. - provider_host_->SetControllerVersion(NULL); - provider_host_->SetActiveVersion(NULL); - provider_host_->SetWaitingVersion(NULL); - provider_host_->SetInstallingVersion(NULL); + provider_host_->UnassociateRegistration(); GURL stripped_url = net::SimplifyUrlForRequest(url); provider_host_->SetDocumentUrl(stripped_url); @@ -152,7 +149,7 @@ ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource( // Wait until it's activated before firing fetch events. if (active_version && - active_version->status() == ServiceWorkerVersion::ACTIVATING) { + active_version->status() == ServiceWorkerVersion::ACTIVATING) { registration->active_version()->RegisterStatusChangeCallback( base::Bind(&self::OnVersionStatusChanged, weak_factory_.GetWeakPtr(), @@ -167,11 +164,7 @@ ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource( return; } - provider_host_->SetControllerVersion(registration->active_version()); - provider_host_->SetActiveVersion(registration->active_version()); - provider_host_->SetWaitingVersion(registration->waiting_version()); - provider_host_->SetInstallingVersion(registration->installing_version()); - + provider_host_->AssociateRegistration(registration); job_->ForwardToServiceWorker(); } @@ -183,11 +176,8 @@ void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged( job_->FallbackToNetwork(); return; } - provider_host_->SetControllerVersion(registration->active_version()); - provider_host_->SetActiveVersion(registration->active_version()); - provider_host_->SetWaitingVersion(registration->waiting_version()); - provider_host_->SetInstallingVersion(registration->installing_version()); + provider_host_->AssociateRegistration(registration); job_->ForwardToServiceWorker(); } diff --git a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc index 708d00083aea3..78ee95ddc1cae 100644 --- a/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc +++ b/content/browser/service_worker/service_worker_controllee_request_handler_unittest.cc @@ -51,7 +51,7 @@ class ServiceWorkerControlleeRequestHandlerTest : public testing::Test { kMockRenderProcessId, 1 /* provider_id */, context()->AsWeakPtr(), NULL)); provider_host_ = host->AsWeakPtr(); - context()->AddProviderHost(make_scoped_ptr(host.release())); + context()->AddProviderHost(host.Pass()); context()->storage()->LazyInitialize(base::Bind(&EmptyCallback)); base::RunLoop().RunUntilIdle(); diff --git a/content/browser/service_worker/service_worker_database.cc b/content/browser/service_worker/service_worker_database.cc index 51b4cb3414377..cd0d959ef473a 100644 --- a/content/browser/service_worker/service_worker_database.cc +++ b/content/browser/service_worker/service_worker_database.cc @@ -513,6 +513,9 @@ ServiceWorkerDatabase::Status ServiceWorkerDatabase::WriteRegistration( // Delete a resource from the uncommitted list. batch.Delete(CreateResourceIdKey( kUncommittedResIdKeyPrefix, itr->resource_id)); + // Delete from the purgeable list in case this version was once deleted. + batch.Delete( + CreateResourceIdKey(kPurgeableResIdKeyPrefix, itr->resource_id)); } // Retrieve a previous version to sweep purgeable resources. diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc index 2c68c82a3ef59..47885d1db6ce9 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host.cc +++ b/content/browser/service_worker/service_worker_dispatcher_host.cc @@ -13,6 +13,7 @@ #include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_handle.h" #include "content/browser/service_worker/service_worker_registration.h" +#include "content/browser/service_worker/service_worker_registration_handle.h" #include "content/browser/service_worker/service_worker_utils.h" #include "content/common/service_worker/embedded_worker_messages.h" #include "content/common/service_worker/service_worker_messages.h" @@ -135,6 +136,10 @@ bool ServiceWorkerDispatcherHost::OnMessageReceived( OnIncrementServiceWorkerRefCount) IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount, OnDecrementServiceWorkerRefCount) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_IncrementRegistrationRefCount, + OnIncrementRegistrationRefCount) + IPC_MESSAGE_HANDLER(ServiceWorkerHostMsg_DecrementRegistrationRefCount, + OnDecrementRegistrationRefCount) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() @@ -165,6 +170,12 @@ void ServiceWorkerDispatcherHost::RegisterServiceWorkerHandle( handles_.AddWithID(handle.release(), handle_id); } +void ServiceWorkerDispatcherHost::RegisterServiceWorkerRegistrationHandle( + scoped_ptr handle) { + int handle_id = handle->handle_id(); + registration_handles_.AddWithID(handle.release(), handle_id); +} + void ServiceWorkerDispatcherHost::OnRegisterServiceWorker( int thread_id, int request_id, @@ -331,6 +342,23 @@ ServiceWorkerHandle* ServiceWorkerDispatcherHost::FindHandle(int provider_id, return NULL; } +ServiceWorkerRegistrationHandle* +ServiceWorkerDispatcherHost::FindRegistrationHandle(int provider_id, + int64 registration_id) { + for (IDMap::iterator + iter(®istration_handles_); + !iter.IsAtEnd(); + iter.Advance()) { + ServiceWorkerRegistrationHandle* handle = iter.GetCurrentValue(); + DCHECK(handle); + if (handle->provider_id() == provider_id && handle->registration() && + handle->registration()->id() == registration_id) { + return handle; + } + } + return NULL; +} + void ServiceWorkerDispatcherHost::RegistrationComplete( int thread_id, int provider_id, @@ -350,6 +378,7 @@ void ServiceWorkerDispatcherHost::RegistrationComplete( DCHECK(version); DCHECK_EQ(registration_id, version->registration_id()); ServiceWorkerObjectInfo info; + ServiceWorkerHandle* handle = FindHandle(provider_id, version_id); if (handle) { DCHECK_EQ(thread_id, handle->thread_id()); @@ -361,8 +390,27 @@ void ServiceWorkerDispatcherHost::RegistrationComplete( info = new_handle->GetObjectInfo(); RegisterServiceWorkerHandle(new_handle.Pass()); } + + ServiceWorkerRegistration* registration = + GetContext()->GetLiveRegistration(registration_id); + DCHECK(registration); + + ServiceWorkerRegistrationHandle* registration_handle = + FindRegistrationHandle(provider_id, registration_id); + int registration_handle_id = kInvalidServiceWorkerRegistrationHandleId; + if (registration_handle) { + registration_handle->IncrementRefCount(); + registration_handle_id = registration_handle->handle_id(); + } else { + scoped_ptr new_handle( + new ServiceWorkerRegistrationHandle( + GetContext()->AsWeakPtr(), this, provider_id, registration)); + registration_handle_id = new_handle->handle_id(); + RegisterServiceWorkerRegistrationHandle(new_handle.Pass()); + } + Send(new ServiceWorkerMsg_ServiceWorkerRegistered( - thread_id, request_id, info)); + thread_id, request_id, registration_handle_id, info)); } void ServiceWorkerDispatcherHost::OnWorkerScriptLoaded(int embedded_worker_id) { @@ -467,6 +515,30 @@ void ServiceWorkerDispatcherHost::OnDecrementServiceWorkerRefCount( handles_.Remove(handle_id); } +void ServiceWorkerDispatcherHost::OnIncrementRegistrationRefCount( + int registration_handle_id) { + ServiceWorkerRegistrationHandle* handle = + registration_handles_.Lookup(registration_handle_id); + if (!handle) { + BadMessageReceived(); + return; + } + handle->IncrementRefCount(); +} + +void ServiceWorkerDispatcherHost::OnDecrementRegistrationRefCount( + int registration_handle_id) { + ServiceWorkerRegistrationHandle* handle = + registration_handles_.Lookup(registration_handle_id); + if (!handle) { + BadMessageReceived(); + return; + } + handle->DecrementRefCount(); + if (handle->HasNoRefCount()) + registration_handles_.Remove(registration_handle_id); +} + void ServiceWorkerDispatcherHost::UnregistrationComplete( int thread_id, int request_id, diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h index f9cace41ce7be..6a4dd1b36998b 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host.h +++ b/content/browser/service_worker/service_worker_dispatcher_host.h @@ -24,6 +24,7 @@ class ServiceWorkerContextWrapper; class ServiceWorkerHandle; class ServiceWorkerProviderHost; class ServiceWorkerRegistration; +class ServiceWorkerRegistrationHandle; class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter { public: @@ -47,6 +48,8 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter { virtual bool Send(IPC::Message* message) OVERRIDE; void RegisterServiceWorkerHandle(scoped_ptr handle); + void RegisterServiceWorkerRegistrationHandle( + scoped_ptr handle); MessagePortMessageFilter* message_port_message_filter() { return message_port_message_filter_; @@ -92,12 +95,19 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter { const std::vector& sent_message_port_ids); void OnIncrementServiceWorkerRefCount(int handle_id); void OnDecrementServiceWorkerRefCount(int handle_id); + void OnIncrementRegistrationRefCount(int registration_handle_id); + void OnDecrementRegistrationRefCount(int registration_handle_id); void OnPostMessageToWorker(int handle_id, const base::string16& message, const std::vector& sent_message_port_ids); void OnServiceWorkerObjectDestroyed(int handle_id); - ServiceWorkerHandle* FindHandle(int provider_id, int64 version_id); + ServiceWorkerHandle* FindHandle( + int provider_id, + int64 version_id); + ServiceWorkerRegistrationHandle* FindRegistrationHandle( + int provider_id, + int64 registration_id); // Callbacks from ServiceWorkerContextCore void RegistrationComplete(int thread_id, @@ -122,6 +132,7 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter { scoped_refptr context_wrapper_; IDMap handles_; + IDMap registration_handles_; bool channel_ready_; // True after BrowserMessageFilter::sender_ != NULL. ScopedVector pending_messages_; diff --git a/content/browser/service_worker/service_worker_fetch_stores.cc b/content/browser/service_worker/service_worker_fetch_stores.cc deleted file mode 100644 index 643ed44939535..0000000000000 --- a/content/browser/service_worker/service_worker_fetch_stores.cc +++ /dev/null @@ -1,71 +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 "content/browser/service_worker/service_worker_fetch_stores.h" - -#include - -#include "content/public/browser/browser_thread.h" - -namespace content { - -ServiceWorkerFetchStores::ServiceWorkerFetchStores( - const base::FilePath& path, - BackendType backend_type, - const scoped_refptr& callback_loop) - : origin_path_(path), - backend_type_(backend_type), - callback_loop_(callback_loop) { -} - -ServiceWorkerFetchStores::~ServiceWorkerFetchStores() { -} - -void ServiceWorkerFetchStores::CreateStore( - const std::string& key, - const StoreAndErrorCallback& callback) { - // TODO(jkarlin): Implement this. - - callback_loop_->PostTask(FROM_HERE, - base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); - return; -} - -void ServiceWorkerFetchStores::Get(const std::string& key, - const StoreAndErrorCallback& callback) { - // TODO(jkarlin): Implement this. - - callback_loop_->PostTask(FROM_HERE, - base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); - return; -} - -void ServiceWorkerFetchStores::Has(const std::string& key, - const BoolAndErrorCallback& callback) const { - // TODO(jkarlin): Implement this. - - callback_loop_->PostTask( - FROM_HERE, base::Bind(callback, false, FETCH_STORES_ERROR_EXISTS)); - return; -} - -void ServiceWorkerFetchStores::Delete(const std::string& key, - const StoreAndErrorCallback& callback) { - // TODO(jkarlin): Implement this. - - callback_loop_->PostTask(FROM_HERE, - base::Bind(callback, 0, FETCH_STORES_ERROR_EXISTS)); - return; -} - -void ServiceWorkerFetchStores::Keys( - const StringsAndErrorCallback& callback) const { - // TODO(jkarlin): Implement this. - std::vector out; - callback_loop_->PostTask( - FROM_HERE, base::Bind(callback, out, FETCH_STORES_ERROR_EXISTS)); - return; -} - -} // namespace content diff --git a/content/browser/service_worker/service_worker_fetch_stores.h b/content/browser/service_worker/service_worker_fetch_stores.h deleted file mode 100644 index c5be0a8eabdfd..0000000000000 --- a/content/browser/service_worker/service_worker_fetch_stores.h +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_ -#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_ - -#include - -#include "base/callback.h" -#include "base/files/file_path.h" -#include "base/threading/thread_checker.h" - -namespace base { -class MessageLoopProxy; -} - -namespace content { - -class ServiceWorkerFetchStore; - -// The set of stores for a given ServiceWorker. It is owned by the -// ServiceWorkerFetchStoresManager. Provided callbacks are called on the -// |callback_loop| provided in the constructor. -class ServiceWorkerFetchStores { - public: - enum FetchStoresError { - FETCH_STORES_ERROR_NO_ERROR, - FETCH_STORES_ERROR_NOT_IMPLEMENTED, - FETCH_STORES_ERROR_NOT_FOUND, - FETCH_STORES_ERROR_EXISTS, - FETCH_STORES_ERROR_STORAGE - }; - - enum BackendType { BACKEND_TYPE_SIMPLE_CACHE, BACKEND_TYPE_MEMORY }; - - typedef base::Callback BoolAndErrorCallback; - typedef base::Callback StoreAndErrorCallback; - typedef base::Callback&, - FetchStoresError)> StringsAndErrorCallback; - - ServiceWorkerFetchStores( - const base::FilePath& origin_path, - BackendType backend, - const scoped_refptr& callback_loop); - virtual ~ServiceWorkerFetchStores(); - - void CreateStore(const std::string& key, - const StoreAndErrorCallback& callback); - void Get(const std::string& key, const StoreAndErrorCallback& callback); - void Has(const std::string& key, const BoolAndErrorCallback& callback) const; - void Delete(const std::string& key, const StoreAndErrorCallback& callback); - void Keys(const StringsAndErrorCallback& callback) const; - // TODO(jkarlin): Add match() function. - - private: - base::FilePath origin_path_; - BackendType backend_type_; - scoped_refptr callback_loop_; - - DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStores); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_H_ diff --git a/content/browser/service_worker/service_worker_fetch_stores_manager.cc b/content/browser/service_worker/service_worker_fetch_stores_manager.cc deleted file mode 100644 index 88036af71bd3d..0000000000000 --- a/content/browser/service_worker/service_worker_fetch_stores_manager.cc +++ /dev/null @@ -1,169 +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 "content/browser/service_worker/service_worker_fetch_stores_manager.h" - -#include -#include - -#include "base/bind.h" -#include "base/id_map.h" -#include "base/sha1.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "content/browser/service_worker/service_worker_context_core.h" -#include "content/browser/service_worker/service_worker_fetch_stores.h" -#include "content/public/browser/browser_thread.h" -#include "url/gurl.h" - -namespace { - -base::FilePath ConstructOriginPath(const base::FilePath& root_path, - const GURL& origin) { - std::string origin_hash = base::SHA1HashString(origin.spec()); - std::string origin_hash_hex = base::StringToLowerASCII( - base::HexEncode(origin_hash.c_str(), origin_hash.length())); - return root_path.AppendASCII(origin_hash_hex); -} - -} // namespace - -namespace content { - -// static -scoped_ptr -ServiceWorkerFetchStoresManager::Create( - const base::FilePath& path, - base::SequencedTaskRunner* stores_task_runner) { - base::FilePath root_path = path; - if (!path.empty()) { - root_path = path.Append(ServiceWorkerContextCore::kServiceWorkerDirectory) - .AppendASCII("Stores"); - } - - return make_scoped_ptr( - new ServiceWorkerFetchStoresManager(root_path, stores_task_runner)); -} - -// static -scoped_ptr -ServiceWorkerFetchStoresManager::Create( - ServiceWorkerFetchStoresManager* old_manager) { - return make_scoped_ptr(new ServiceWorkerFetchStoresManager( - old_manager->root_path(), old_manager->stores_task_runner())); -} - -ServiceWorkerFetchStoresManager::~ServiceWorkerFetchStoresManager() { - for (ServiceWorkerFetchStoresMap::iterator it = - service_worker_fetch_stores_.begin(); - it != service_worker_fetch_stores_.end(); - ++it) { - stores_task_runner_->DeleteSoon(FROM_HERE, it->second); - } -} - -void ServiceWorkerFetchStoresManager::CreateStore( - const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ServiceWorkerFetchStores* stores = - FindOrCreateServiceWorkerFetchStores(origin); - - stores_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ServiceWorkerFetchStores::CreateStore, - base::Unretained(stores), - key, - callback)); -} - -void ServiceWorkerFetchStoresManager::Get( - const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ServiceWorkerFetchStores* stores = - FindOrCreateServiceWorkerFetchStores(origin); - stores_task_runner_->PostTask(FROM_HERE, - base::Bind(&ServiceWorkerFetchStores::Get, - base::Unretained(stores), - key, - callback)); -} - -void ServiceWorkerFetchStoresManager::Has( - const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::BoolAndErrorCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ServiceWorkerFetchStores* stores = - FindOrCreateServiceWorkerFetchStores(origin); - stores_task_runner_->PostTask(FROM_HERE, - base::Bind(&ServiceWorkerFetchStores::Has, - base::Unretained(stores), - key, - callback)); -} - -void ServiceWorkerFetchStoresManager::Delete( - const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::StoreAndErrorCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ServiceWorkerFetchStores* stores = - FindOrCreateServiceWorkerFetchStores(origin); - stores_task_runner_->PostTask(FROM_HERE, - base::Bind(&ServiceWorkerFetchStores::Delete, - base::Unretained(stores), - key, - callback)); -} - -void ServiceWorkerFetchStoresManager::Keys( - const GURL& origin, - const ServiceWorkerFetchStores::StringsAndErrorCallback& callback) { - DCHECK_CURRENTLY_ON(BrowserThread::IO); - - ServiceWorkerFetchStores* stores = - FindOrCreateServiceWorkerFetchStores(origin); - - stores_task_runner_->PostTask( - FROM_HERE, - base::Bind( - &ServiceWorkerFetchStores::Keys, base::Unretained(stores), callback)); -} - -ServiceWorkerFetchStoresManager::ServiceWorkerFetchStoresManager( - const base::FilePath& path, - base::SequencedTaskRunner* stores_task_runner) - : root_path_(path), stores_task_runner_(stores_task_runner) { -} - -ServiceWorkerFetchStores* -ServiceWorkerFetchStoresManager::FindOrCreateServiceWorkerFetchStores( - const GURL& origin) { - ServiceWorkerFetchStoresMap::const_iterator it = - service_worker_fetch_stores_.find(origin); - if (it == service_worker_fetch_stores_.end()) { - ServiceWorkerFetchStores::BackendType backend = - root_path_.empty() - ? ServiceWorkerFetchStores::BACKEND_TYPE_MEMORY - : ServiceWorkerFetchStores::BACKEND_TYPE_SIMPLE_CACHE; - ServiceWorkerFetchStores* fetch_stores = - new ServiceWorkerFetchStores(ConstructOriginPath(root_path_, origin), - backend, - base::MessageLoopProxy::current()); - // The map owns fetch_stores. - service_worker_fetch_stores_.insert(std::make_pair(origin, fetch_stores)); - return fetch_stores; - } - return it->second; -} - -} // namespace content diff --git a/content/browser/service_worker/service_worker_fetch_stores_manager.h b/content/browser/service_worker/service_worker_fetch_stores_manager.h deleted file mode 100644 index 44c999aeba63f..0000000000000 --- a/content/browser/service_worker/service_worker_fetch_stores_manager.h +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_MANAGER_H_ -#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_MANAGER_H_ - -#include -#include - -#include "base/basictypes.h" -#include "base/files/file_path.h" -#include "content/browser/service_worker/service_worker_fetch_stores.h" -#include "content/common/content_export.h" -#include "url/gurl.h" - -namespace base { -class SequencedTaskRunner; -} // namespace base - -namespace content { - -// Keeps track of a ServiceWorkerFetchStores per origin. There is one -// ServiceWorkerFetchStoresManager per ServiceWorkerContextCore. -class CONTENT_EXPORT ServiceWorkerFetchStoresManager { - public: - static scoped_ptr Create( - const base::FilePath& path, - base::SequencedTaskRunner* stores_task_runner); - - static scoped_ptr Create( - ServiceWorkerFetchStoresManager* old_manager); - - virtual ~ServiceWorkerFetchStoresManager(); - - // Methods to support the FetchStores spec. These methods call the - // corresponding ServiceWorkerFetchStores method on the appropriate thread. - void CreateStore( - const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::StoreAndErrorCallback& callback); - void Get(const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::StoreAndErrorCallback& callback); - void Has(const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::BoolAndErrorCallback& callback); - void Delete(const GURL& origin, - const std::string& key, - const ServiceWorkerFetchStores::StoreAndErrorCallback& callback); - void Keys(const GURL& origin, - const ServiceWorkerFetchStores::StringsAndErrorCallback& callback); - // TODO(jkarlin): Add match() function. - - base::FilePath root_path() const { return root_path_; } - scoped_refptr stores_task_runner() const { - return stores_task_runner_; - } - - private: - typedef std::map ServiceWorkerFetchStoresMap; - - ServiceWorkerFetchStoresManager( - const base::FilePath& path, - base::SequencedTaskRunner* stores_task_runner); - - // The returned ServiceWorkerFetchStores* is owned by - // service_worker_fetch_stores_. - ServiceWorkerFetchStores* FindOrCreateServiceWorkerFetchStores( - const GURL& origin); - - base::FilePath root_path_; - scoped_refptr stores_task_runner_; - - // The map owns the stores and the stores are only accessed on - // |stores_task_runner_|. - ServiceWorkerFetchStoresMap service_worker_fetch_stores_; - - DISALLOW_COPY_AND_ASSIGN(ServiceWorkerFetchStoresManager); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_MANAGER_H_ diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc index f9a2f2e8e6416..0a074c59e712b 100644 --- a/content/browser/service_worker/service_worker_job_unittest.cc +++ b/content/browser/service_worker/service_worker_job_unittest.cc @@ -896,6 +896,10 @@ class UpdateJobTestHelper UpdateJobTestHelper(int mock_render_process_id) : EmbeddedWorkerTestHelper(mock_render_process_id) {} + virtual ~UpdateJobTestHelper() { + if (registration_) + registration_->RemoveListener(this); + } ServiceWorkerStorage* storage() { return context()->storage(); } ServiceWorkerJobCoordinator* job_coordinator() { @@ -917,6 +921,7 @@ class UpdateJobTestHelper EXPECT_TRUE(registration->active_version()); EXPECT_FALSE(registration->installing_version()); EXPECT_FALSE(registration->waiting_version()); + registration_ = registration; return registration; } @@ -963,6 +968,11 @@ class UpdateJobTestHelper attribute_change_log_.push_back(entry); } + virtual void OnRegistrationFailed( + ServiceWorkerRegistration* registration) OVERRIDE { + NOTREACHED(); + } + // ServiceWorkerVersion::Listener overrides virtual void OnVersionStateChanged(ServiceWorkerVersion* version) OVERRIDE { StateChangeLogEntry entry; @@ -971,6 +981,8 @@ class UpdateJobTestHelper state_change_log_.push_back(entry); } + scoped_refptr registration_; + std::vector attribute_change_log_; std::vector state_change_log_; }; diff --git a/content/browser/service_worker/service_worker_proto.gyp b/content/browser/service_worker/service_worker_proto.gyp index b2588fff6a551..aab5e63bb7f3d 100644 --- a/content/browser/service_worker/service_worker_proto.gyp +++ b/content/browser/service_worker/service_worker_proto.gyp @@ -1,10 +1,11 @@ { 'targets': [ { - # GN version: //content/browser/service_worker:database_proto - 'target_name': 'database_proto', + # GN version: //content/browser/service_worker:proto + 'target_name': 'proto', 'type': 'static_library', 'sources': [ + 'service_worker_cache.proto', 'service_worker_database.proto', ], 'variables': { diff --git a/content/browser/service_worker/service_worker_provider_host.cc b/content/browser/service_worker/service_worker_provider_host.cc index 4155af21b5d9d..14e104434ae49 100644 --- a/content/browser/service_worker/service_worker_provider_host.cc +++ b/content/browser/service_worker/service_worker_provider_host.cc @@ -11,6 +11,7 @@ #include "content/browser/service_worker/service_worker_controllee_request_handler.h" #include "content/browser/service_worker/service_worker_dispatcher_host.h" #include "content/browser/service_worker/service_worker_handle.h" +#include "content/browser/service_worker/service_worker_registration_handle.h" #include "content/browser/service_worker/service_worker_utils.h" #include "content/browser/service_worker/service_worker_version.h" #include "content/common/service_worker/service_worker_messages.h" @@ -41,6 +42,24 @@ ServiceWorkerProviderHost::~ServiceWorkerProviderHost() { waiting_version_->RemovePotentialControllee(this); if (installing_version_) installing_version_->RemovePotentialControllee(this); + if (associated_registration_) + associated_registration_->RemoveListener(this); +} + +void ServiceWorkerProviderHost::OnVersionAttributesChanged( + ServiceWorkerRegistration* registration, + ChangedVersionAttributesMask changed_mask, + const ServiceWorkerRegistrationInfo& info) { + DCHECK_EQ(associated_registration_, registration); + SetVersionAttributes(registration->installing_version(), + registration->waiting_version(), + registration->active_version()); +} + +void ServiceWorkerProviderHost::OnRegistrationFailed( + ServiceWorkerRegistration* registration) { + DCHECK_EQ(associated_registration_, registration); + UnassociateRegistration(); } void ServiceWorkerProviderHost::SetDocumentUrl(const GURL& url) { @@ -48,93 +67,79 @@ void ServiceWorkerProviderHost::SetDocumentUrl(const GURL& url) { document_url_ = url; } -void ServiceWorkerProviderHost::SetControllerVersion( - ServiceWorkerVersion* version) { - DCHECK(CanAssociateVersion(version)); - if (version == controlling_version_) - return; - scoped_refptr previous_version = controlling_version_; - controlling_version_ = version; - if (version) - version->AddControllee(this); - if (previous_version) - previous_version->RemoveControllee(this); - - if (!dispatcher_host_) - return; // Could be NULL in some tests. - - dispatcher_host_->Send(new ServiceWorkerMsg_SetControllerServiceWorker( - kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version))); -} +void ServiceWorkerProviderHost::SetVersionAttributes( + ServiceWorkerVersion* installing_version, + ServiceWorkerVersion* waiting_version, + ServiceWorkerVersion* active_version) { + ChangedVersionAttributesMask mask; -void ServiceWorkerProviderHost::SetActiveVersion( - ServiceWorkerVersion* version) { - DCHECK(CanAssociateVersion(version)); - if (version == active_version_) - return; - scoped_refptr previous_version = active_version_; - active_version_ = version; - if (version) - version->AddPotentialControllee(this); - if (previous_version) - previous_version->RemovePotentialControllee(this); + if (installing_version != installing_version_) { + SetVersionAttributesInternal(installing_version, &installing_version_); + mask.add(ChangedVersionAttributesMask::INSTALLING_VERSION); + } + if (waiting_version != waiting_version_) { + SetVersionAttributesInternal(waiting_version, &waiting_version_); + mask.add(ChangedVersionAttributesMask::WAITING_VERSION); + } + if (active_version != active_version_) { + SetVersionAttributesInternal(active_version, &active_version_); + mask.add(ChangedVersionAttributesMask::ACTIVE_VERSION); + } if (!dispatcher_host_) return; // Could be NULL in some tests. + if (!mask.changed()) + return; - dispatcher_host_->Send(new ServiceWorkerMsg_SetActiveServiceWorker( - kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version))); + ServiceWorkerVersionAttributes attributes; + if (mask.installing_changed()) + attributes.installing = CreateHandleAndPass(installing_version); + if (mask.waiting_changed()) + attributes.waiting = CreateHandleAndPass(waiting_version); + if (mask.active_changed()) + attributes.active = CreateHandleAndPass(active_version); + + dispatcher_host_->Send(new ServiceWorkerMsg_SetVersionAttributes( + kDocumentMainThreadId, + provider_id(), + kInvalidServiceWorkerRegistrationHandleId, + mask.changed(), + attributes)); } -void ServiceWorkerProviderHost::SetWaitingVersion( - ServiceWorkerVersion* version) { - DCHECK(CanAssociateVersion(version)); - if (version == waiting_version_) - return; - scoped_refptr previous_version = waiting_version_; - waiting_version_ = version; +void ServiceWorkerProviderHost::SetVersionAttributesInternal( + ServiceWorkerVersion* version, + scoped_refptr* data_member) { + scoped_refptr previous_version = *data_member; + *data_member = version; if (version) version->AddPotentialControllee(this); if (previous_version) previous_version->RemovePotentialControllee(this); - - if (!dispatcher_host_) - return; // Could be NULL in some tests. - - dispatcher_host_->Send(new ServiceWorkerMsg_SetWaitingServiceWorker( - kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version))); } -void ServiceWorkerProviderHost::SetInstallingVersion( +void ServiceWorkerProviderHost::SetControllerVersionAttribute( ServiceWorkerVersion* version) { - DCHECK(CanAssociateVersion(version)); - if (version == installing_version_) + if (version == controlling_version_) return; - scoped_refptr previous_version = installing_version_; - installing_version_ = version; + + scoped_refptr previous_version = controlling_version_; + controlling_version_ = version; if (version) - version->AddPotentialControllee(this); + version->AddControllee(this); if (previous_version) - previous_version->RemovePotentialControllee(this); + previous_version->RemoveControllee(this); if (!dispatcher_host_) return; // Could be NULL in some tests. - dispatcher_host_->Send(new ServiceWorkerMsg_SetInstallingServiceWorker( + dispatcher_host_->Send(new ServiceWorkerMsg_SetControllerServiceWorker( kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version))); } -void ServiceWorkerProviderHost::UnsetVersion(ServiceWorkerVersion* version) { - if (!version) - return; - if (installing_version_ == version) - SetInstallingVersion(NULL); - else if (waiting_version_ == version) - SetWaitingVersion(NULL); - else if (active_version_ == version) - SetActiveVersion(NULL); - else if (controlling_version_ == version) - SetControllerVersion(NULL); +void ServiceWorkerProviderHost::ClearVersionAttributes() { + SetVersionAttributes(NULL, NULL, NULL); + SetControllerVersionAttribute(NULL); } bool ServiceWorkerProviderHost::SetHostedVersionId(int64 version_id) { @@ -159,6 +164,25 @@ bool ServiceWorkerProviderHost::SetHostedVersionId(int64 version_id) { return true; } +void ServiceWorkerProviderHost::AssociateRegistration( + ServiceWorkerRegistration* registration) { + DCHECK(CanAssociateRegistration(registration)); + associated_registration_ = registration; + registration->AddListener(this); + SetVersionAttributes(registration->installing_version(), + registration->waiting_version(), + registration->active_version()); + SetControllerVersionAttribute(registration->active_version()); +} + +void ServiceWorkerProviderHost::UnassociateRegistration() { + if (!associated_registration_) + return; + associated_registration_->RemoveListener(this); + associated_registration_ = NULL; + ClearVersionAttributes(); +} + scoped_ptr ServiceWorkerProviderHost::CreateRequestHandler( ResourceType resource_type, @@ -177,28 +201,15 @@ ServiceWorkerProviderHost::CreateRequestHandler( return scoped_ptr(); } -bool ServiceWorkerProviderHost::CanAssociateVersion( - ServiceWorkerVersion* version) { +bool ServiceWorkerProviderHost::CanAssociateRegistration( + ServiceWorkerRegistration* registration) { if (!context_) return false; if (running_hosted_version_) return false; - if (!version) - return true; - - ServiceWorkerVersion* already_associated_version = NULL; - if (controlling_version_) - already_associated_version = controlling_version_; - if (active_version_) - already_associated_version = active_version_; - else if (waiting_version_) - already_associated_version = waiting_version_; - else if (installing_version_) - already_associated_version = installing_version_; - - return !already_associated_version || - already_associated_version->registration_id() == - version->registration_id(); + if (!registration || associated_registration_) + return false; + return true; } void ServiceWorkerProviderHost::PostMessage( diff --git a/content/browser/service_worker/service_worker_provider_host.h b/content/browser/service_worker/service_worker_provider_host.h index 3175ef10d160d..c417fc72439b3 100644 --- a/content/browser/service_worker/service_worker_provider_host.h +++ b/content/browser/service_worker/service_worker_provider_host.h @@ -10,6 +10,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "content/browser/service_worker/service_worker_registration.h" #include "content/common/content_export.h" #include "content/common/service_worker/service_worker_types.h" #include "content/public/common/resource_type.h" @@ -38,13 +39,14 @@ class ServiceWorkerVersion; // Note this class can also host a running service worker, in which // case it will observe resource loads made directly by the service worker. class CONTENT_EXPORT ServiceWorkerProviderHost - : public base::SupportsWeakPtr { + : public ServiceWorkerRegistration::Listener, + public base::SupportsWeakPtr { public: ServiceWorkerProviderHost(int process_id, int provider_id, base::WeakPtr context, ServiceWorkerDispatcherHost* dispatcher_host); - ~ServiceWorkerProviderHost(); + virtual ~ServiceWorkerProviderHost(); int process_id() const { return process_id_; } int provider_id() const { return provider_id_; } @@ -76,16 +78,11 @@ class CONTENT_EXPORT ServiceWorkerProviderHost void SetDocumentUrl(const GURL& url); const GURL& document_url() const { return document_url_; } - // Associates |version| to the corresponding field or if |version| is NULL - // clears the field. - void SetControllerVersion(ServiceWorkerVersion* version); - void SetActiveVersion(ServiceWorkerVersion* version); - void SetWaitingVersion(ServiceWorkerVersion* version); - void SetInstallingVersion(ServiceWorkerVersion* version); + // Associates to |registration| to listen for its version change events. + void AssociateRegistration(ServiceWorkerRegistration* registration); - // If |version| is the installing, waiting, or active version of this - // provider, the method will reset that field to NULL. - void UnsetVersion(ServiceWorkerVersion* version); + // Clears the associated registration and stop listening to it. + void UnassociateRegistration(); // Returns false if the version is not in the expected STARTING in our // process state. That would be indicative of a bad IPC message. @@ -97,8 +94,8 @@ class CONTENT_EXPORT ServiceWorkerProviderHost ResourceType resource_type, base::WeakPtr blob_storage_context); - // Returns true if |version| can be associated with this provider. - bool CanAssociateVersion(ServiceWorkerVersion* version); + // Returns true if |registration| can be associated with this provider. + bool CanAssociateRegistration(ServiceWorkerRegistration* registration); // Returns true if the context referred to by this host (i.e. |context_|) is // still alive. @@ -109,6 +106,37 @@ class CONTENT_EXPORT ServiceWorkerProviderHost const std::vector& sent_message_port_ids); private: + friend class ServiceWorkerProviderHostTest; + FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, + UpdateBefore24Hours); + FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, + UpdateAfter24Hours); + + // ServiceWorkerRegistration::Listener overrides. + virtual void OnVersionAttributesChanged( + ServiceWorkerRegistration* registration, + ChangedVersionAttributesMask changed_mask, + const ServiceWorkerRegistrationInfo& info) OVERRIDE; + virtual void OnRegistrationFailed( + ServiceWorkerRegistration* registration) OVERRIDE; + + // Sets the corresponding version field to the given version or if the given + // version is NULL, clears the field. + void SetVersionAttributes( + ServiceWorkerVersion* installing_version, + ServiceWorkerVersion* waiting_version, + ServiceWorkerVersion* active_version); + void SetVersionAttributesInternal( + ServiceWorkerVersion* version, + scoped_refptr* data_member); + + // Sets the controller version field to |version| or if |version| is NULL, + // clears the field. + void SetControllerVersionAttribute(ServiceWorkerVersion* version); + + // Clears all version fields. + void ClearVersionAttributes(); + // Creates a ServiceWorkerHandle to retain |version| and returns a // ServiceWorkerInfo with the handle ID to pass to the provider. The // provider is responsible for releasing the handle. @@ -118,6 +146,8 @@ class CONTENT_EXPORT ServiceWorkerProviderHost const int provider_id_; GURL document_url_; + scoped_refptr associated_registration_; + scoped_refptr controlling_version_; scoped_refptr active_version_; scoped_refptr waiting_version_; diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc index e7bb590b09e6e..94baf11c027a5 100644 --- a/content/browser/service_worker/service_worker_provider_host_unittest.cc +++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc @@ -47,15 +47,10 @@ class ServiceWorkerProviderHostTest : public testing::Test { scoped_ptr host2(new ServiceWorkerProviderHost( kRenderProcessId, 2 /* provider_id */, context_->AsWeakPtr(), NULL)); - scoped_ptr host3(new ServiceWorkerProviderHost( - kRenderProcessId, 3 /* provider_id */, - context_->AsWeakPtr(), NULL)); provider_host1_ = host1->AsWeakPtr(); provider_host2_ = host2->AsWeakPtr(); - provider_host3_ = host3->AsWeakPtr(); context_->AddProviderHost(make_scoped_ptr(host1.release())); context_->AddProviderHost(make_scoped_ptr(host2.release())); - context_->AddProviderHost(make_scoped_ptr(host3.release())); } virtual void TearDown() OVERRIDE { @@ -64,13 +59,23 @@ class ServiceWorkerProviderHostTest : public testing::Test { context_.reset(); } + void VerifyVersionAttributes( + base::WeakPtr provider_host, + ServiceWorkerVersion* installing, + ServiceWorkerVersion* waiting, + ServiceWorkerVersion* active) { + EXPECT_EQ(installing, provider_host->installing_version_); + EXPECT_EQ(waiting, provider_host->waiting_version_); + EXPECT_EQ(active, provider_host->active_version_); + EXPECT_FALSE(provider_host->controlling_version_); + } + content::TestBrowserThreadBundle thread_bundle_; scoped_ptr context_; scoped_refptr registration_; scoped_refptr version_; base::WeakPtr provider_host1_; base::WeakPtr provider_host2_; - base::WeakPtr provider_host3_; GURL scope_; GURL script_url_; @@ -79,207 +84,146 @@ class ServiceWorkerProviderHostTest : public testing::Test { }; TEST_F(ServiceWorkerProviderHostTest, SetActiveVersion_ProcessStatus) { + provider_host1_->AssociateRegistration(registration_); ASSERT_FALSE(version_->HasProcessToRun()); // Associating version_ to a provider_host's active version will internally // add the provider_host's process ref to the version. - provider_host1_->SetActiveVersion(version_); + registration_->SetActiveVersion(version_); ASSERT_TRUE(version_->HasProcessToRun()); // Re-associating the same version and provider_host should just work too. - provider_host1_->SetActiveVersion(version_); + registration_->SetActiveVersion(version_); ASSERT_TRUE(version_->HasProcessToRun()); // Resetting the provider_host's active version should remove process refs // from the version. - provider_host1_->SetActiveVersion(NULL); + provider_host1_->UnassociateRegistration(); ASSERT_FALSE(version_->HasProcessToRun()); } TEST_F(ServiceWorkerProviderHostTest, SetActiveVersion_MultipleHostsForSameProcess) { + provider_host1_->AssociateRegistration(registration_); + provider_host2_->AssociateRegistration(registration_); ASSERT_FALSE(version_->HasProcessToRun()); // Associating version_ to two providers as active version. - provider_host1_->SetActiveVersion(version_); - provider_host2_->SetActiveVersion(version_); + registration_->SetActiveVersion(version_); ASSERT_TRUE(version_->HasProcessToRun()); // Disassociating one provider_host shouldn't remove all process refs // from the version yet. - provider_host1_->SetActiveVersion(NULL); + provider_host1_->UnassociateRegistration(); ASSERT_TRUE(version_->HasProcessToRun()); // Disassociating the other provider_host will remove all process refs. - provider_host2_->SetActiveVersion(NULL); + provider_host2_->UnassociateRegistration(); ASSERT_FALSE(version_->HasProcessToRun()); } TEST_F(ServiceWorkerProviderHostTest, SetWaitingVersion_ProcessStatus) { + provider_host1_->AssociateRegistration(registration_); ASSERT_FALSE(version_->HasProcessToRun()); // Associating version_ to a provider_host's waiting version will internally // add the provider_host's process ref to the version. - provider_host1_->SetWaitingVersion(version_); + registration_->SetWaitingVersion(version_); ASSERT_TRUE(version_->HasProcessToRun()); // Re-associating the same version and provider_host should just work too. - provider_host1_->SetWaitingVersion(version_); + registration_->SetWaitingVersion(version_); ASSERT_TRUE(version_->HasProcessToRun()); // Resetting the provider_host's waiting version should remove process refs // from the version. - provider_host1_->SetWaitingVersion(NULL); + provider_host1_->UnassociateRegistration(); ASSERT_FALSE(version_->HasProcessToRun()); } TEST_F(ServiceWorkerProviderHostTest, SetWaitingVersion_MultipleHostsForSameProcess) { + provider_host1_->AssociateRegistration(registration_); + provider_host2_->AssociateRegistration(registration_); ASSERT_FALSE(version_->HasProcessToRun()); - // Associating version_ to two providers as active version. - provider_host1_->SetWaitingVersion(version_); - provider_host2_->SetWaitingVersion(version_); + // Associating version_ to two providers as waiting version. + registration_->SetWaitingVersion(version_); ASSERT_TRUE(version_->HasProcessToRun()); // Disassociating one provider_host shouldn't remove all process refs // from the version yet. - provider_host1_->SetWaitingVersion(NULL); + provider_host1_->UnassociateRegistration(); ASSERT_TRUE(version_->HasProcessToRun()); // Disassociating the other provider_host will remove all process refs. - provider_host2_->SetWaitingVersion(NULL); + provider_host2_->UnassociateRegistration(); ASSERT_FALSE(version_->HasProcessToRun()); } -class ServiceWorkerProviderHostWaitingVersionTest : public testing::Test { - protected: - ServiceWorkerProviderHostWaitingVersionTest() - : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP), - next_provider_id_(1L) {} - virtual ~ServiceWorkerProviderHostWaitingVersionTest() {} - - virtual void SetUp() OVERRIDE { - context_.reset( - new ServiceWorkerContextCore(base::FilePath(), - base::MessageLoopProxy::current(), - base::MessageLoopProxy::current(), - base::MessageLoopProxy::current(), - NULL, - NULL, - NULL)); - - // Prepare provider hosts (for the same process). - provider_host1_ = CreateProviderHost(GURL("http://www.example.com/foo")); - provider_host2_ = CreateProviderHost(GURL("http://www.example.com/bar")); - provider_host3_ = CreateProviderHost(GURL("http://www.example.ca/foo")); - } - - base::WeakPtr CreateProviderHost( - const GURL& document_url) { - scoped_ptr host(new ServiceWorkerProviderHost( - kRenderProcessId, next_provider_id_++, context_->AsWeakPtr(), NULL)); - host->SetDocumentUrl(document_url); - base::WeakPtr provider_host = host->AsWeakPtr(); - context_->AddProviderHost(host.Pass()); - return provider_host; - } - - virtual void TearDown() OVERRIDE { - context_.reset(); - } - - content::TestBrowserThreadBundle thread_bundle_; - scoped_ptr context_; - base::WeakPtr provider_host1_; - base::WeakPtr provider_host2_; - base::WeakPtr provider_host3_; - - private: - int64 next_provider_id_; - - DISALLOW_COPY_AND_ASSIGN(ServiceWorkerProviderHostWaitingVersionTest); -}; - -TEST_F(ServiceWorkerProviderHostWaitingVersionTest, - AssociateInstallingVersionToDocuments) { - const GURL scope1("http://www.example.com/"); - const GURL script_url1("http://www.example.com/service_worker1.js"); - scoped_refptr registration1( - new ServiceWorkerRegistration( - scope1, script_url1, 1L, context_->AsWeakPtr())); - scoped_refptr version1( - new ServiceWorkerVersion(registration1, 1L, context_->AsWeakPtr())); - - ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( - context_->AsWeakPtr(), version1); - EXPECT_EQ(version1.get(), provider_host1_->installing_version()); - EXPECT_EQ(version1.get(), provider_host2_->installing_version()); - EXPECT_EQ(NULL, provider_host3_->installing_version()); - - // Version2 is associated with the same registration as version1, so the - // waiting version of host1 and host2 should be replaced. - scoped_refptr version2( - new ServiceWorkerVersion(registration1, 2L, context_->AsWeakPtr())); - ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( - context_->AsWeakPtr(), version2); - EXPECT_EQ(version2.get(), provider_host1_->installing_version()); - EXPECT_EQ(version2.get(), provider_host2_->installing_version()); - EXPECT_EQ(NULL, provider_host3_->installing_version()); - - const GURL scope3(provider_host1_->document_url()); - const GURL script_url3("http://www.example.com/service_worker3.js"); - scoped_refptr registration3( - new ServiceWorkerRegistration( - scope3, script_url3, 3L, context_->AsWeakPtr())); - scoped_refptr version3( - new ServiceWorkerVersion(registration3, 3L, context_->AsWeakPtr())); - - // Although version3 can match longer than version2 for host1, it should be - // ignored because version3 is associated with a different registration from - // version2. - ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( - context_->AsWeakPtr(), version3); - EXPECT_EQ(version2.get(), provider_host1_->installing_version()); - EXPECT_EQ(version2.get(), provider_host2_->installing_version()); - EXPECT_EQ(NULL, provider_host3_->installing_version()); +TEST_F(ServiceWorkerProviderHostTest, + ObserveVersionAttributesChanged_Basic) { + provider_host1_->AssociateRegistration(registration_); + provider_host2_->AssociateRegistration(registration_); + VerifyVersionAttributes(provider_host1_, NULL, NULL, NULL); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); + + registration_->SetInstallingVersion(version_); + VerifyVersionAttributes(provider_host1_, version_, NULL, NULL); + VerifyVersionAttributes(provider_host2_, version_, NULL, NULL); + + registration_->SetWaitingVersion(version_); + VerifyVersionAttributes(provider_host1_, NULL, version_, NULL); + VerifyVersionAttributes(provider_host2_, NULL, version_, NULL); + + // Disassociating the registration should clear all version attributes. + provider_host2_->UnassociateRegistration(); + VerifyVersionAttributes(provider_host1_, NULL, version_, NULL); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); + + // Shouldn't notify the disassociated provider of the change. + registration_->SetActiveVersion(version_); + VerifyVersionAttributes(provider_host1_, NULL, NULL, version_); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); } -TEST_F(ServiceWorkerProviderHostWaitingVersionTest, - DisassociateVersionFromDocuments) { - const GURL scope1("http://www.example.com/"); - const GURL script_url1("http://www.example.com/service_worker.js"); - scoped_refptr registration1( - new ServiceWorkerRegistration( - scope1, script_url1, 1L, context_->AsWeakPtr())); - scoped_refptr version1( - new ServiceWorkerVersion(registration1, 1L, context_->AsWeakPtr())); - - const GURL scope2("http://www.example.ca/"); - const GURL script_url2("http://www.example.ca/service_worker.js"); - scoped_refptr registration2( - new ServiceWorkerRegistration( - scope2, script_url2, 2L, context_->AsWeakPtr())); - scoped_refptr version2( - new ServiceWorkerVersion(registration2, 2L, context_->AsWeakPtr())); - - ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( - context_->AsWeakPtr(), version1); - ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( - context_->AsWeakPtr(), version2); - - // Host1 and host2 are associated with version1 as a waiting version, whereas - // host3 is associated with version2. - EXPECT_EQ(version1.get(), provider_host1_->installing_version()); - EXPECT_EQ(version1.get(), provider_host2_->installing_version()); - EXPECT_EQ(version2.get(), provider_host3_->installing_version()); - - // Disassociate version1 from host1 and host2. - ServiceWorkerRegisterJob::DisassociateVersionFromDocuments( - context_->AsWeakPtr(), version1); - EXPECT_EQ(NULL, provider_host1_->installing_version()); - EXPECT_EQ(NULL, provider_host2_->installing_version()); - EXPECT_EQ(version2.get(), provider_host3_->installing_version()); +TEST_F(ServiceWorkerProviderHostTest, + ObserveVersionAttributesChanged_MultipleVersions) { + provider_host1_->AssociateRegistration(registration_); + provider_host2_->AssociateRegistration(registration_); + VerifyVersionAttributes(provider_host1_, NULL, NULL, NULL); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); + + scoped_refptr version1 = + new ServiceWorkerVersion(registration_, 10L, context_->AsWeakPtr()); + scoped_refptr version2 = + new ServiceWorkerVersion(registration_, 20L, context_->AsWeakPtr()); + + registration_->SetInstallingVersion(version1); + VerifyVersionAttributes(provider_host1_, version1, NULL, NULL); + VerifyVersionAttributes(provider_host2_, version1, NULL, NULL); + + registration_->SetWaitingVersion(version1); + VerifyVersionAttributes(provider_host1_, NULL, version1, NULL); + VerifyVersionAttributes(provider_host2_, NULL, version1, NULL); + + registration_->SetInstallingVersion(version2); + VerifyVersionAttributes(provider_host1_, version2, version1, NULL); + VerifyVersionAttributes(provider_host2_, version2, version1, NULL); + + // Disassociating the registration should clear all version attributes. + provider_host2_->UnassociateRegistration(); + VerifyVersionAttributes(provider_host1_, version2, version1, NULL); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); + + // Shouldn't notify the disassociated provider of the change. + registration_->SetActiveVersion(version1); + VerifyVersionAttributes(provider_host1_, version2, NULL, version1); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); + + registration_->SetActiveVersion(version2); + VerifyVersionAttributes(provider_host1_, NULL, NULL, version2); + VerifyVersionAttributes(provider_host2_, NULL, NULL, NULL); } } // namespace content diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc index 9d1a83d3101ee..a0453313bc0d3 100644 --- a/content/browser/service_worker/service_worker_register_job.cc +++ b/content/browser/service_worker/service_worker_register_job.cc @@ -83,7 +83,13 @@ void ServiceWorkerRegisterJob::Start() { &ServiceWorkerRegisterJob::ContinueWithUpdate, weak_factory_.GetWeakPtr()); } - context_->storage()->FindRegistrationForPattern(pattern_, next_step); + + scoped_refptr registration = + context_->storage()->GetUninstallingRegistration(pattern_); + if (registration) + RunSoon(base::Bind(next_step, SERVICE_WORKER_OK, registration)); + else + context_->storage()->FindRegistrationForPattern(pattern_, next_step); } void ServiceWorkerRegisterJob::Abort() { @@ -163,53 +169,48 @@ void ServiceWorkerRegisterJob::SetPhase(Phase phase) { phase_ = phase; } -// This function corresponds to the steps in Register following -// "Let serviceWorkerRegistration be _GetRegistration(scope)" -// |existing_registration| corresponds to serviceWorkerRegistration. +// This function corresponds to the steps in [[Register]] following +// "Let registration be the result of running the [[GetRegistration]] algorithm. // Throughout this file, comments in quotes are excerpts from the spec. void ServiceWorkerRegisterJob::ContinueWithRegistration( ServiceWorkerStatusCode status, const scoped_refptr& existing_registration) { DCHECK_EQ(REGISTRATION_JOB, job_type_); - // On unexpected error, abort this registration job. if (status != SERVICE_WORKER_ERROR_NOT_FOUND && status != SERVICE_WORKER_OK) { Complete(status); return; } - // "If serviceWorkerRegistration is not null and script is equal to - // serviceWorkerRegistration.scriptUrl..." resolve with the existing - // registration and abort. - if (existing_registration.get() && - existing_registration->script_url() == script_url_) { + if (!existing_registration) { + RegisterAndContinue(SERVICE_WORKER_OK); + return; + } + + // "Set registration.[[Uninstalling]] to false." + existing_registration->AbortPendingClear(); + + // "If scriptURL is equal to registration.[[ScriptURL]], then:" + if (existing_registration->script_url() == script_url_) { + // Spec says to resolve with registration.[[GetNewestWorker]]. We come close + // by resolving with the active version. set_registration(existing_registration); - // If there's no active version, go ahead to Update (this isn't in the spec - // but seems reasonable, and without SoftUpdate implemented we can never - // Update otherwise). + if (!existing_registration->active_version()) { UpdateAndContinue(); return; } + ResolvePromise( status, existing_registration, existing_registration->active_version()); Complete(SERVICE_WORKER_OK); return; } - // "If serviceWorkerRegistration is null..." create a new registration. - if (!existing_registration.get()) { - RegisterAndContinue(SERVICE_WORKER_OK); - return; - } - - // On script URL mismatch, "set serviceWorkerRegistration.scriptUrl to - // script." We accomplish this by deleting the existing registration and - // registering a new one. - // TODO(falken): Match the spec. We now throw away the active_version_ and - // waiting_version_ of the existing registration, which isn't in the spec. + // "Set registration.[[ScriptURL]] to scriptURL." We accomplish this by + // deleting the existing registration and registering a new one. // TODO(michaeln): Deactivate the live existing_registration object and - // eventually call storage->DeleteVersionResources() - // when it no longer has any controllees. + // eventually call storage->DeleteVersionResources() when it no longer has any + // controllees. context_->storage()->DeleteRegistration( existing_registration->id(), existing_registration->script_url().GetOrigin(), @@ -231,6 +232,10 @@ void ServiceWorkerRegisterJob::ContinueWithUpdate( return; } + // TODO(michaeln): If the last update check was less than 24 hours + // ago, depending on the freshness of the cached worker script we + // may be able to complete the update job right here. + UpdateAndContinue(); } @@ -247,6 +252,7 @@ void ServiceWorkerRegisterJob::RegisterAndContinue( set_registration(new ServiceWorkerRegistration( pattern_, script_url_, context_->storage()->NewRegistrationId(), context_)); + AssociateProviderHostsToRegistration(registration()); UpdateAndContinue(); } @@ -296,7 +302,6 @@ void ServiceWorkerRegisterJob::InstallAndContinue() { // "3. Set registration.installingWorker to worker." registration()->SetInstallingVersion(new_version()); - AssociateInstallingVersionToDocuments(context_, new_version()); // "4. Run the [[UpdateState]] algorithm passing registration.installingWorker // and "installing" as the arguments." @@ -322,6 +327,7 @@ void ServiceWorkerRegisterJob::OnInstallFinished( } SetPhase(STORE); + registration()->set_last_update_check(base::Time::Now()); context_->storage()->StoreRegistration( registration(), new_version(), @@ -346,9 +352,7 @@ void ServiceWorkerRegisterJob::OnStoreRegistrationComplete( // "10. Set registration.waitingWorker to registration.installingWorker." // "11. Set registration.installingWorker to null." - DisassociateVersionFromDocuments(context_, new_version()); registration()->SetWaitingVersion(new_version()); - AssociateWaitingVersionToDocuments(context_, new_version()); // "12. Run the [[UpdateState]] algorithm passing registration.waitingWorker // and "installed" as the arguments." @@ -374,11 +378,12 @@ void ServiceWorkerRegisterJob::CompleteInternal( if (status != SERVICE_WORKER_OK) { if (registration()) { if (new_version()) { - DisassociateVersionFromDocuments(context_, new_version()); registration()->UnsetVersion(new_version()); new_version()->Doom(); } - if (!registration()->active_version()) { + if (!registration()->waiting_version() && + !registration()->active_version()) { + registration()->NotifyRegistrationFailed(); context_->storage()->DeleteRegistration( registration()->id(), registration()->script_url().GetOrigin(), @@ -416,11 +421,13 @@ void ServiceWorkerRegisterJob::ResolvePromise( void ServiceWorkerRegisterJob::OnPausedAfterDownload() { // This happens prior to OnStartWorkerFinished time. - scoped_refptr current_version = - registration()->active_version(); - DCHECK(current_version); - int64 current_script_id = - current_version->script_cache_map()->Lookup(script_url_); + scoped_refptr most_recent_version = + registration()->waiting_version() ? + registration()->waiting_version() : + registration()->active_version(); + DCHECK(most_recent_version); + int64 most_recent_script_id = + most_recent_version->script_cache_map()->Lookup(script_url_); int64 new_script_id = new_version()->script_cache_map()->Lookup(script_url_); @@ -428,10 +435,10 @@ void ServiceWorkerRegisterJob::OnPausedAfterDownload() { // is being downloaded and to avoid writing it to disk until we know // its needed. context_->storage()->CompareScriptResources( - current_script_id, new_script_id, + most_recent_script_id, new_script_id, base::Bind(&ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete, weak_factory_.GetWeakPtr(), - current_version)); + most_recent_version)); } bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) { @@ -439,11 +446,19 @@ bool ServiceWorkerRegisterJob::OnMessageReceived(const IPC::Message& message) { } void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete( - ServiceWorkerVersion* current_version, + ServiceWorkerVersion* most_recent_version, ServiceWorkerStatusCode status, bool are_equal) { if (are_equal) { - ResolvePromise(SERVICE_WORKER_OK, registration(), current_version); + // Only bump the last check time when we've bypassed the browser cache. + base::TimeDelta time_since_last_check = + base::Time::Now() - registration()->last_update_check(); + if (time_since_last_check > base::TimeDelta::FromHours(24)) { + registration()->set_last_update_check(base::Time::Now()); + context_->storage()->UpdateLastUpdateCheckTime(registration()); + } + + ResolvePromise(SERVICE_WORKER_OK, registration(), most_recent_version); Complete(SERVICE_WORKER_ERROR_EXISTS); return; } @@ -453,77 +468,19 @@ void ServiceWorkerRegisterJob::OnCompareScriptResourcesComplete( new_version()->embedded_worker()->RemoveListener(this); } -// static -void ServiceWorkerRegisterJob::AssociateInstallingVersionToDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version) { - DCHECK(context); - DCHECK(version); - - for (scoped_ptr it = - context->GetProviderHostIterator(); - !it->IsAtEnd(); it->Advance()) { - ServiceWorkerProviderHost* host = it->GetProviderHost(); - if (ServiceWorkerUtils::ScopeMatches(version->scope(), - host->document_url())) { - if (!host->CanAssociateVersion(version)) - continue; - host->SetInstallingVersion(version); - } - } -} - -// static -void ServiceWorkerRegisterJob::AssociateWaitingVersionToDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version) { - DCHECK(context); - DCHECK(version); - - for (scoped_ptr it = - context->GetProviderHostIterator(); - !it->IsAtEnd(); it->Advance()) { - ServiceWorkerProviderHost* host = it->GetProviderHost(); - if (ServiceWorkerUtils::ScopeMatches(version->scope(), - host->document_url())) { - if (!host->CanAssociateVersion(version)) - continue; - host->SetWaitingVersion(version); - } - } -} - -// static -void ServiceWorkerRegisterJob::AssociateActiveVersionToDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version) { - DCHECK(context); - DCHECK(version); - +void ServiceWorkerRegisterJob::AssociateProviderHostsToRegistration( + ServiceWorkerRegistration* registration) { + DCHECK(registration); for (scoped_ptr it = - context->GetProviderHostIterator(); + context_->GetProviderHostIterator(); !it->IsAtEnd(); it->Advance()) { ServiceWorkerProviderHost* host = it->GetProviderHost(); - if (ServiceWorkerUtils::ScopeMatches(version->scope(), + if (ServiceWorkerUtils::ScopeMatches(registration->pattern(), host->document_url())) { - if (!host->CanAssociateVersion(version)) - continue; - host->SetActiveVersion(version); + if (host->CanAssociateRegistration(registration)) + host->AssociateRegistration(registration); } } } -// static -void ServiceWorkerRegisterJob::DisassociateVersionFromDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version) { - DCHECK(context); - for (scoped_ptr it = - context->GetProviderHostIterator(); - !it->IsAtEnd(); it->Advance()) { - ServiceWorkerProviderHost* host = it->GetProviderHost(); - host->UnsetVersion(version); - } -} - } // namespace content diff --git a/content/browser/service_worker/service_worker_register_job.h b/content/browser/service_worker/service_worker_register_job.h index 548fa5ae001f9..88704337d28cc 100644 --- a/content/browser/service_worker/service_worker_register_job.h +++ b/content/browser/service_worker/service_worker_register_job.h @@ -67,22 +67,6 @@ class ServiceWorkerRegisterJob virtual bool Equals(ServiceWorkerRegisterJobBase* job) OVERRIDE; virtual RegistrationJobType GetType() OVERRIDE; - // TODO(michaeln): Use the registration listerer's OnVersionAttributesChanged - // method to replace these methods, have the host listen for changes - // to their registration. - CONTENT_EXPORT static void AssociateInstallingVersionToDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version); - static void AssociateWaitingVersionToDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version); - static void AssociateActiveVersionToDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version); - CONTENT_EXPORT static void DisassociateVersionFromDocuments( - base::WeakPtr context, - ServiceWorkerVersion* version); - private: FRIEND_TEST_ALL_PREFIXES(ServiceWorkerProviderHostWaitingVersionTest, AssociateInstallingVersionToDocuments); @@ -144,10 +128,13 @@ class ServiceWorkerRegisterJob virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; void OnCompareScriptResourcesComplete( - ServiceWorkerVersion* current_version, + ServiceWorkerVersion* most_recent_version, ServiceWorkerStatusCode status, bool are_equal); + void AssociateProviderHostsToRegistration( + ServiceWorkerRegistration* registration); + // The ServiceWorkerContextCore object should always outlive this. base::WeakPtr context_; diff --git a/content/browser/service_worker/service_worker_registration.cc b/content/browser/service_worker/service_worker_registration.cc index 9cd539d42567c..cea1d5570ecab 100644 --- a/content/browser/service_worker/service_worker_registration.cc +++ b/content/browser/service_worker/service_worker_registration.cc @@ -31,6 +31,7 @@ ServiceWorkerRegistration::ServiceWorkerRegistration( script_url_(script_url), registration_id_(registration_id), is_deleted_(false), + is_uninstalling_(false), should_activate_when_ready_(false), context_(context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); @@ -40,9 +41,11 @@ ServiceWorkerRegistration::ServiceWorkerRegistration( ServiceWorkerRegistration::~ServiceWorkerRegistration() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!listeners_.might_have_observers()); if (context_) context_->RemoveLiveRegistration(registration_id_); - ResetShouldActivateWhenReady(); + if (active_version()) + active_version()->RemoveListener(this); } void ServiceWorkerRegistration::AddListener(Listener* listener) { @@ -53,6 +56,10 @@ void ServiceWorkerRegistration::RemoveListener(Listener* listener) { listeners_.RemoveObserver(listener); } +void ServiceWorkerRegistration::NotifyRegistrationFailed() { + FOR_EACH_OBSERVER(Listener, listeners_, OnRegistrationFailed(this)); +} + ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); return ServiceWorkerRegistrationInfo( @@ -66,14 +73,14 @@ ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() { void ServiceWorkerRegistration::SetActiveVersion( ServiceWorkerVersion* version) { - ResetShouldActivateWhenReady(); + should_activate_when_ready_ = false; SetVersionInternal(version, &active_version_, ChangedVersionAttributesMask::ACTIVE_VERSION); } void ServiceWorkerRegistration::SetWaitingVersion( ServiceWorkerVersion* version) { - ResetShouldActivateWhenReady(); + should_activate_when_ready_ = false; SetVersionInternal(version, &waiting_version_, ChangedVersionAttributesMask::WAITING_VERSION); } @@ -107,6 +114,8 @@ void ServiceWorkerRegistration::SetVersionInternal( if (version) UnsetVersionInternal(version, &mask); *data_member = version; + if (active_version_ && active_version_ == version) + active_version_->AddListener(this); mask.add(change_flag); ServiceWorkerRegistrationInfo info = GetInfo(); FOR_EACH_OBSERVER(Listener, listeners_, @@ -124,6 +133,7 @@ void ServiceWorkerRegistration::UnsetVersionInternal( waiting_version_ = NULL; mask->add(ChangedVersionAttributesMask::WAITING_VERSION); } else if (active_version_ == version) { + active_version_->RemoveListener(this); active_version_ = NULL; mask->add(ChangedVersionAttributesMask::ACTIVE_VERSION); } @@ -131,27 +141,61 @@ void ServiceWorkerRegistration::UnsetVersionInternal( void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() { DCHECK(waiting_version()); - if (should_activate_when_ready_) + should_activate_when_ready_ = true; + if (!active_version() || !active_version()->HasControllee()) + ActivateWaitingVersion(); +} + +void ServiceWorkerRegistration::ClearWhenReady() { + DCHECK(context_); + if (is_uninstalling_) return; - if (active_version() && active_version()->HasControllee()) { - active_version()->AddListener(this); - should_activate_when_ready_ = true; + is_uninstalling_ = true; + + context_->storage()->NotifyUninstallingRegistration(this); + context_->storage()->DeleteRegistration( + id(), + script_url().GetOrigin(), + base::Bind(&ServiceWorkerUtils::NoOpStatusCallback)); + + if (!active_version() || !active_version()->HasControllee()) + Clear(); +} + +void ServiceWorkerRegistration::AbortPendingClear() { + DCHECK(context_); + if (!is_uninstalling()) return; - } - ActivateWaitingVersion(); + is_uninstalling_ = false; + context_->storage()->NotifyDoneUninstallingRegistration(this); + + scoped_refptr most_recent_version = + waiting_version() ? waiting_version() : active_version(); + DCHECK(most_recent_version); + context_->storage()->NotifyInstallingRegistration(this); + context_->storage()->StoreRegistration( + this, + most_recent_version, + base::Bind(&ServiceWorkerRegistration::OnStoreFinished, + this, + most_recent_version)); } void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) { DCHECK_EQ(active_version(), version); - DCHECK(should_activate_when_ready_); - active_version_->RemoveListener(this); + if (is_uninstalling_) + Clear(); + else if (should_activate_when_ready_) + ActivateWaitingVersion(); + is_uninstalling_ = false; should_activate_when_ready_ = false; - ActivateWaitingVersion(); } void ServiceWorkerRegistration::ActivateWaitingVersion() { DCHECK(context_); DCHECK(waiting_version()); + DCHECK(should_activate_when_ready_); + should_activate_when_ready_ = false; scoped_refptr activating_version = waiting_version(); scoped_refptr exiting_version = active_version(); @@ -175,11 +219,7 @@ void ServiceWorkerRegistration::ActivateWaitingVersion() { // "5. Set serviceWorkerRegistration.activeWorker to activatingWorker." // "6. Set serviceWorkerRegistration.waitingWorker to null." - ServiceWorkerRegisterJob::DisassociateVersionFromDocuments( - context_, activating_version); SetActiveVersion(activating_version); - ServiceWorkerRegisterJob::AssociateActiveVersionToDocuments( - context_, activating_version); // "7. Run the [[UpdateState]] algorithm passing registration.activeWorker and // "activating" as arguments." @@ -202,8 +242,6 @@ void ServiceWorkerRegistration::OnActivateEventFinished( // unexpectedly terminated) we may want to retry sending the event again. if (status != SERVICE_WORKER_OK) { // "11. If activateFailed is true, then:..." - ServiceWorkerRegisterJob::DisassociateVersionFromDocuments( - context_, activating_version); UnsetVersion(activating_version); activating_version->Doom(); if (!waiting_version()) { @@ -228,17 +266,38 @@ void ServiceWorkerRegistration::OnActivateEventFinished( } } -void ServiceWorkerRegistration::ResetShouldActivateWhenReady() { - if (should_activate_when_ready_) { - active_version()->RemoveListener(this); - should_activate_when_ready_ = false; - } -} - void ServiceWorkerRegistration::OnDeleteFinished( ServiceWorkerStatusCode status) { // Intentionally empty completion callback, used to prevent // |this| from being deleted until the storage method completes. } +void ServiceWorkerRegistration::Clear() { + context_->storage()->NotifyDoneUninstallingRegistration(this); + + if (installing_version()) { + installing_version()->Doom(); + UnsetVersion(installing_version()); + } + + if (waiting_version()) { + waiting_version()->Doom(); + UnsetVersion(waiting_version()); + } + + if (active_version()) { + active_version()->Doom(); + UnsetVersion(active_version()); + } +} + +void ServiceWorkerRegistration::OnStoreFinished( + scoped_refptr version, + ServiceWorkerStatusCode status) { + if (!context_) + return; + context_->storage()->NotifyDoneInstallingRegistration( + this, version.get(), status); +} + } // namespace content diff --git a/content/browser/service_worker/service_worker_registration.h b/content/browser/service_worker/service_worker_registration.h index 41092889e74c4..51872d2144739 100644 --- a/content/browser/service_worker/service_worker_registration.h +++ b/content/browser/service_worker/service_worker_registration.h @@ -36,7 +36,9 @@ class CONTENT_EXPORT ServiceWorkerRegistration virtual void OnVersionAttributesChanged( ServiceWorkerRegistration* registration, ChangedVersionAttributesMask changed_mask, - const ServiceWorkerRegistrationInfo& info) = 0; + const ServiceWorkerRegistrationInfo& info) = 0; + virtual void OnRegistrationFailed( + ServiceWorkerRegistration* registration) = 0; }; ServiceWorkerRegistration(const GURL& pattern, @@ -48,6 +50,11 @@ class CONTENT_EXPORT ServiceWorkerRegistration const GURL& script_url() const { return script_url_; } const GURL& pattern() const { return pattern_; } + bool is_deleted() const { return is_deleted_; } + void set_is_deleted(bool deleted) { is_deleted_ = deleted; } + + bool is_uninstalling() const { return is_uninstalling_; } + ServiceWorkerVersion* active_version() const { return active_version_.get(); } @@ -62,6 +69,7 @@ class CONTENT_EXPORT ServiceWorkerRegistration void AddListener(Listener* listener); void RemoveListener(Listener* listener); + void NotifyRegistrationFailed(); ServiceWorkerRegistrationInfo GetInfo(); @@ -82,8 +90,19 @@ class CONTENT_EXPORT ServiceWorkerRegistration // is called, activation is initiated immediately. void ActivateWaitingVersionWhenReady(); - bool is_deleted() const { return is_deleted_; } - void set_is_deleted() { is_deleted_ = true; } + // Triggers the [[ClearRegistration]] algorithm when the currently + // active version has no controllees. Deletes this registration + // from storage immediately. + void ClearWhenReady(); + + // Restores this registration in storage and cancels the pending + // [[ClearRegistration]] algorithm. If the algorithm was already triggered, + // does nothing. + void AbortPendingClear(); + + // The time of the most recent update check. + base::Time last_update_check() const { return last_update_check_; } + void set_last_update_check(base::Time last) { last_update_check_ = last; } private: friend class base::RefCounted; @@ -106,14 +125,20 @@ class CONTENT_EXPORT ServiceWorkerRegistration void OnActivateEventFinished( ServiceWorkerVersion* activating_version, ServiceWorkerStatusCode status); - void ResetShouldActivateWhenReady(); void OnDeleteFinished(ServiceWorkerStatusCode status); + // This method corresponds to the [[ClearRegistration]] algorithm. + void Clear(); + void OnStoreFinished(scoped_refptr version, + ServiceWorkerStatusCode status); + const GURL pattern_; const GURL script_url_; const int64 registration_id_; bool is_deleted_; + bool is_uninstalling_; bool should_activate_when_ready_; + base::Time last_update_check_; scoped_refptr active_version_; scoped_refptr waiting_version_; scoped_refptr installing_version_; @@ -122,5 +147,7 @@ class CONTENT_EXPORT ServiceWorkerRegistration DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegistration); }; + } // namespace content + #endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_H_ diff --git a/content/browser/service_worker/service_worker_registration_handle.cc b/content/browser/service_worker/service_worker_registration_handle.cc new file mode 100644 index 0000000000000..3e333955b9c77 --- /dev/null +++ b/content/browser/service_worker/service_worker_registration_handle.cc @@ -0,0 +1,130 @@ +// 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 "content/browser/service_worker/service_worker_registration_handle.h" + +#include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_dispatcher_host.h" +#include "content/browser/service_worker/service_worker_handle.h" +#include "content/common/service_worker/service_worker_messages.h" + +namespace content { + +static const int kDocumentMainThreadId = 0; + +ServiceWorkerRegistrationHandle::ServiceWorkerRegistrationHandle( + base::WeakPtr context, + ServiceWorkerDispatcherHost* dispatcher_host, + int provider_id, + ServiceWorkerRegistration* registration) + : context_(context), + dispatcher_host_(dispatcher_host), + provider_id_(provider_id), + handle_id_(context ? context->GetNewRegistrationHandleId() + : kInvalidServiceWorkerRegistrationHandleId), + ref_count_(1), + registration_(registration) { + DCHECK(registration_); + SetVersionAttributes(registration->installing_version(), + registration->waiting_version(), + registration->active_version()); + registration_->AddListener(this); +} + +ServiceWorkerRegistrationHandle::~ServiceWorkerRegistrationHandle() { + DCHECK(registration_); + registration_->RemoveListener(this); +} + +void ServiceWorkerRegistrationHandle::IncrementRefCount() { + DCHECK_GT(ref_count_, 0); + ++ref_count_; +} + +void ServiceWorkerRegistrationHandle::DecrementRefCount() { + DCHECK_GT(ref_count_, 0); + --ref_count_; +} + +void ServiceWorkerRegistrationHandle::OnVersionAttributesChanged( + ServiceWorkerRegistration* registration, + ChangedVersionAttributesMask changed_mask, + const ServiceWorkerRegistrationInfo& info) { + DCHECK_EQ(registration->id(), registration_->id()); + SetVersionAttributes(registration->installing_version(), + registration->waiting_version(), + registration->active_version()); +} + +void ServiceWorkerRegistrationHandle::OnRegistrationFailed( + ServiceWorkerRegistration* registration) { + DCHECK_EQ(registration->id(), registration_->id()); + ClearVersionAttributes(); +} + +void ServiceWorkerRegistrationHandle::SetVersionAttributes( + ServiceWorkerVersion* installing_version, + ServiceWorkerVersion* waiting_version, + ServiceWorkerVersion* active_version) { + ChangedVersionAttributesMask mask; + + if (installing_version != installing_version_) { + installing_version_ = installing_version; + mask.add(ChangedVersionAttributesMask::INSTALLING_VERSION); + } + if (waiting_version != waiting_version_) { + waiting_version_ = waiting_version; + mask.add(ChangedVersionAttributesMask::WAITING_VERSION); + } + if (active_version != active_version_) { + active_version_ = active_version; + mask.add(ChangedVersionAttributesMask::ACTIVE_VERSION); + } + + if (!dispatcher_host_) + return; // Could be NULL in some tests. + if (!mask.changed()) + return; + + ServiceWorkerVersionAttributes attributes; + if (mask.installing_changed()) { + attributes.installing = + CreateServiceWorkerHandleAndPass(installing_version); + } + if (mask.waiting_changed()) { + attributes.waiting = + CreateServiceWorkerHandleAndPass(waiting_version); + } + if (mask.active_changed()) { + attributes.active = + CreateServiceWorkerHandleAndPass(active_version); + } + + dispatcher_host_->Send(new ServiceWorkerMsg_SetVersionAttributes( + kDocumentMainThreadId, provider_id_, handle_id_, + mask.changed(), attributes)); +} + +void ServiceWorkerRegistrationHandle::ClearVersionAttributes() { + SetVersionAttributes(NULL, NULL, NULL); +} + +ServiceWorkerObjectInfo +ServiceWorkerRegistrationHandle::CreateServiceWorkerHandleAndPass( + ServiceWorkerVersion* version) { + ServiceWorkerObjectInfo info; + if (context_ && version) { + scoped_ptr handle = + ServiceWorkerHandle::Create(context_, + dispatcher_host_, + kDocumentMainThreadId, + provider_id_, + version); + info = handle->GetObjectInfo(); + dispatcher_host_->RegisterServiceWorkerHandle(handle.Pass()); + } + return info; +} + +} // namespace content diff --git a/content/browser/service_worker/service_worker_registration_handle.h b/content/browser/service_worker/service_worker_registration_handle.h new file mode 100644 index 0000000000000..1831636c8c1bc --- /dev/null +++ b/content/browser/service_worker/service_worker_registration_handle.h @@ -0,0 +1,83 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_HANDLE_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_HANDLE_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/browser/service_worker/service_worker_registration.h" +#include "content/browser/service_worker/service_worker_version.h" +#include "content/common/service_worker/service_worker_types.h" + +namespace content { + +class ServiceWorkerContextCore; +class ServiceWorkerDispatcherHost; +class ServiceWorkerVersion; + +// Roughly Corresponds to one ServiceWorkerRegistration object in the renderer +// process (WebServiceWorkerRegistrationImpl). +// Has references to the corresponding ServiceWorkerRegistration and +// ServiceWorkerVersions (therefore they're guaranteed to be alive while this +// handle is around). +class ServiceWorkerRegistrationHandle + : public ServiceWorkerRegistration::Listener { + public: + CONTENT_EXPORT ServiceWorkerRegistrationHandle( + base::WeakPtr context, + ServiceWorkerDispatcherHost* dispatcher_host, + int provider_id, + ServiceWorkerRegistration* registration); + virtual ~ServiceWorkerRegistrationHandle(); + + bool HasNoRefCount() const { return ref_count_ <= 0; } + void IncrementRefCount(); + void DecrementRefCount(); + + int provider_id() const { return provider_id_; } + int handle_id() const { return handle_id_; } + + ServiceWorkerRegistration* registration() { return registration_.get(); } + + private: + // ServiceWorkerRegistration::Listener overrides. + virtual void OnVersionAttributesChanged( + ServiceWorkerRegistration* registration, + ChangedVersionAttributesMask changed_mask, + const ServiceWorkerRegistrationInfo& info) OVERRIDE; + virtual void OnRegistrationFailed( + ServiceWorkerRegistration* registration) OVERRIDE; + + // Sets the corresponding version field to the given version or if the given + // version is NULL, clears the field. + void SetVersionAttributes( + ServiceWorkerVersion* installing_version, + ServiceWorkerVersion* waiting_version, + ServiceWorkerVersion* active_version); + + // Clears all version fields. + void ClearVersionAttributes(); + + ServiceWorkerObjectInfo CreateServiceWorkerHandleAndPass( + ServiceWorkerVersion* version); + + base::WeakPtr context_; + ServiceWorkerDispatcherHost* dispatcher_host_; + const int provider_id_; + const int handle_id_; + int ref_count_; // Created with 1. + + scoped_refptr registration_; + scoped_refptr installing_version_; + scoped_refptr waiting_version_; + scoped_refptr active_version_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegistrationHandle); +}; + +} // namespace content + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_HANDLE_H_ diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc index 55d8a31842aaa..67746bb55aa97 100644 --- a/content/browser/service_worker/service_worker_registration_unittest.cc +++ b/content/browser/service_worker/service_worker_registration_unittest.cc @@ -10,6 +10,7 @@ #include "base/run_loop.h" #include "content/browser/browser_thread_impl.h" #include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_registration_handle.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -40,7 +41,10 @@ class ServiceWorkerRegistrationTest : public testing::Test { class RegistrationListener : public ServiceWorkerRegistration::Listener { public: RegistrationListener() {} - ~RegistrationListener() {} + ~RegistrationListener() { + if (observed_registration_) + observed_registration_->RemoveListener(this); + } virtual void OnVersionAttributesChanged( ServiceWorkerRegistration* registration, @@ -51,6 +55,11 @@ class ServiceWorkerRegistrationTest : public testing::Test { observed_info_ = info; } + virtual void OnRegistrationFailed( + ServiceWorkerRegistration* registration) OVERRIDE { + NOTREACHED(); + } + void Reset() { observed_registration_ = NULL; observed_changed_mask_ = ChangedVersionAttributesMask(); @@ -138,4 +147,24 @@ TEST_F(ServiceWorkerRegistrationTest, SetAndUnsetVersions) { EXPECT_TRUE(listener.observed_info_.controlling_version.is_null); } +TEST_F(ServiceWorkerRegistrationTest, FailedRegistrationNoCrash) { + const GURL kScope("http://www.example.not/"); + const GURL kScript("http://www.example.not/service_worker.js"); + int64 kRegistrationId = 1L; + int kProviderId = 1; + scoped_refptr registration = + new ServiceWorkerRegistration( + kScope, + kScript, + kRegistrationId, + context_ptr_); + scoped_ptr handle( + new ServiceWorkerRegistrationHandle(context_ptr_, + NULL, + kProviderId, + registration.get())); + registration->NotifyRegistrationFailed(); + // Don't crash when handle gets destructed. +} + } // namespace content diff --git a/content/browser/service_worker/service_worker_script_cache_map.h b/content/browser/service_worker/service_worker_script_cache_map.h index da790f609c66c..45afcc3ba232e 100644 --- a/content/browser/service_worker/service_worker_script_cache_map.h +++ b/content/browser/service_worker/service_worker_script_cache_map.h @@ -40,6 +40,8 @@ class CONTENT_EXPORT ServiceWorkerScriptCacheMap { void SetResources( const std::vector& resources); + size_t size() const { return resource_ids_.size(); } + private: typedef std::map ResourceIDMap; diff --git a/content/browser/service_worker/service_worker_storage.cc b/content/browser/service_worker/service_worker_storage.cc index f09127ee81457..38e8c621a0981 100644 --- a/content/browser/service_worker/service_worker_storage.cc +++ b/content/browser/service_worker/service_worker_storage.cc @@ -327,6 +327,22 @@ void ServiceWorkerStorage::FindRegistrationForPattern( weak_factory_.GetWeakPtr(), scope, callback))); } +ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration( + const GURL& scope) { + if (state_ != INITIALIZED || !context_) + return NULL; + for (RegistrationRefsById::const_iterator it = + uninstalling_registrations_.begin(); + it != uninstalling_registrations_.end(); + ++it) { + if (it->second->pattern() == scope) { + DCHECK(it->second->is_uninstalling()); + return it->second; + } + } + return NULL; +} + void ServiceWorkerStorage::FindRegistrationForId( int64 registration_id, const GURL& origin, @@ -417,8 +433,8 @@ void ServiceWorkerStorage::StoreRegistration( data.script = registration->script_url(); data.has_fetch_handler = true; data.version_id = version->version_id(); - data.last_update_check = base::Time::Now(); - data.is_active = false; // initially stored in the waiting state + data.last_update_check = registration->last_update_check(); + data.is_active = (version == registration->active_version()); ResourceList resources; version->script_cache_map()->GetResources(&resources); @@ -435,6 +451,8 @@ void ServiceWorkerStorage::StoreRegistration( base::Bind(&ServiceWorkerStorage::DidStoreRegistration, weak_factory_.GetWeakPtr(), callback))); + + registration->set_is_deleted(false); } void ServiceWorkerStorage::UpdateToActiveState( @@ -460,6 +478,24 @@ void ServiceWorkerStorage::UpdateToActiveState( callback)); } +void ServiceWorkerStorage::UpdateLastUpdateCheckTime( + ServiceWorkerRegistration* registration) { + DCHECK(registration); + + DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_; + if (IsDisabled() || !context_) + return; + + database_task_runner_->PostTask( + FROM_HERE, + base::Bind( + base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime), + base::Unretained(database_.get()), + registration->id(), + registration->script_url().GetOrigin(), + registration->last_update_check())); +} + void ServiceWorkerStorage::DeleteRegistration( int64 registration_id, const GURL& origin, @@ -492,7 +528,7 @@ void ServiceWorkerStorage::DeleteRegistration( ServiceWorkerRegistration* registration = context_->GetLiveRegistration(registration_id); if (registration) - registration->set_is_deleted(); + registration->set_is_deleted(true); } scoped_ptr @@ -581,6 +617,8 @@ int64 ServiceWorkerStorage::NewResourceId() { void ServiceWorkerStorage::NotifyInstallingRegistration( ServiceWorkerRegistration* registration) { + DCHECK(installing_registrations_.find(registration->id()) == + installing_registrations_.end()); installing_registrations_[registration->id()] = registration; } @@ -606,6 +644,18 @@ void ServiceWorkerStorage::NotifyDoneInstallingRegistration( } } +void ServiceWorkerStorage::NotifyUninstallingRegistration( + ServiceWorkerRegistration* registration) { + DCHECK(uninstalling_registrations_.find(registration->id()) == + uninstalling_registrations_.end()); + uninstalling_registrations_[registration->id()] = registration; +} + +void ServiceWorkerStorage::NotifyDoneUninstallingRegistration( + ServiceWorkerRegistration* registration) { + uninstalling_registrations_.erase(registration->id()); +} + void ServiceWorkerStorage::Disable() { state_ = DISABLED; if (disk_cache_) @@ -922,9 +972,10 @@ ServiceWorkerStorage::GetOrCreateRegistration( registration = new ServiceWorkerRegistration( data.scope, data.script, data.registration_id, context_); + registration->set_last_update_check(data.last_update_check); if (pending_deletions_.find(data.registration_id) != pending_deletions_.end()) { - registration->set_is_deleted(); + registration->set_is_deleted(true); } scoped_refptr version = context_->GetLiveVersion(data.version_id); diff --git a/content/browser/service_worker/service_worker_storage.h b/content/browser/service_worker/service_worker_storage.h index 30db1e960c1ff..0180cba4486a7 100644 --- a/content/browser/service_worker/service_worker_storage.h +++ b/content/browser/service_worker/service_worker_storage.h @@ -87,6 +87,8 @@ class CONTENT_EXPORT ServiceWorkerStorage const GURL& origin, const FindRegistrationCallback& callback); + ServiceWorkerRegistration* GetUninstallingRegistration(const GURL& scope); + // Returns info about all stored and initially installing registrations. void GetAllRegistrations(const GetAllRegistrationInfosCallback& callback); @@ -104,6 +106,10 @@ class CONTENT_EXPORT ServiceWorkerStorage ServiceWorkerRegistration* registration, const StatusCallback& callback); + // Updates the stored time to match the value of + // registration->last_update_check(). + void UpdateLastUpdateCheckTime(ServiceWorkerRegistration* registration); + // Deletes the registration data for |registration_id|. If the registration's // version is live, its script resources will remain available. // PurgeResources should be called when it's OK to delete them. @@ -136,13 +142,17 @@ class CONTENT_EXPORT ServiceWorkerStorage int64 NewVersionId(); int64 NewResourceId(); - // Intended for use only by ServiceWorkerRegisterJob. + // Intended for use only by ServiceWorkerRegisterJob and + // ServiceWorkerRegistration. void NotifyInstallingRegistration( ServiceWorkerRegistration* registration); void NotifyDoneInstallingRegistration( ServiceWorkerRegistration* registration, ServiceWorkerVersion* version, ServiceWorkerStatusCode status); + void NotifyUninstallingRegistration(ServiceWorkerRegistration* registration); + void NotifyDoneUninstallingRegistration( + ServiceWorkerRegistration* registration); void Disable(); bool IsDisabled() const; @@ -153,6 +163,7 @@ class CONTENT_EXPORT ServiceWorkerStorage private: friend class ServiceWorkerResourceStorageTest; friend class ServiceWorkerControlleeRequestHandlerTest; + friend class ServiceWorkerContextRequestHandlerTest; FRIEND_TEST_ALL_PREFIXES(ServiceWorkerResourceStorageTest, DeleteRegistration_NoLiveVersion); FRIEND_TEST_ALL_PREFIXES(ServiceWorkerResourceStorageTest, @@ -335,8 +346,9 @@ class CONTENT_EXPORT ServiceWorkerStorage const StatusCallback& callback, bool result); - // For finding registrations being installed. + // For finding registrations being installed or uninstalled. RegistrationRefsById installing_registrations_; + RegistrationRefsById uninstalling_registrations_; // Origins having registations. std::set registered_origins_; diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc index b9d1cdb8ad8b2..70bd78b209f52 100644 --- a/content/browser/service_worker/service_worker_storage_unittest.cc +++ b/content/browser/service_worker/service_worker_storage_unittest.cc @@ -273,6 +273,11 @@ class ServiceWorkerStorageTest : public testing::Test { return result; } + void UpdateLastUpdateCheckTime(ServiceWorkerRegistration* registration) { + storage()->UpdateLastUpdateCheckTime(registration); + base::RunLoop().RunUntilIdle(); + } + ServiceWorkerStatusCode FindRegistrationForDocument( const GURL& document_url, scoped_refptr* registration) { @@ -323,6 +328,8 @@ TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) { const GURL kDocumentUrl("http://www.test.not/scope/document.html"); const int64 kRegistrationId = 0; const int64 kVersionId = 0; + const base::Time kToday = base::Time::Now(); + const base::Time kYesterday = kToday - base::TimeDelta::FromDays(1); scoped_refptr found_registration; @@ -349,6 +356,7 @@ TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) { live_registration, kVersionId, context_ptr_); live_version->SetStatus(ServiceWorkerVersion::INSTALLED); live_registration->SetWaitingVersion(live_version); + live_registration->set_last_update_check(kYesterday); EXPECT_EQ(SERVICE_WORKER_OK, StoreRegistration(live_registration, live_version)); @@ -396,16 +404,20 @@ TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) { EXPECT_TRUE(found_registration->HasOneRef()); EXPECT_FALSE(found_registration->active_version()); ASSERT_TRUE(found_registration->waiting_version()); + EXPECT_EQ(kYesterday, found_registration->last_update_check()); EXPECT_EQ(ServiceWorkerVersion::INSTALLED, found_registration->waiting_version()->status()); - // Update to active. + // Update to active and update the last check time. scoped_refptr temp_version = found_registration->waiting_version(); temp_version->SetStatus(ServiceWorkerVersion::ACTIVATED); found_registration->SetActiveVersion(temp_version); temp_version = NULL; EXPECT_EQ(SERVICE_WORKER_OK, UpdateToActiveState(found_registration)); + found_registration->set_last_update_check(kToday); + UpdateLastUpdateCheckTime(found_registration); + found_registration = NULL; // Trying to update a unstored registration to active should fail. @@ -416,7 +428,8 @@ TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) { UpdateToActiveState(unstored_registration)); unstored_registration = NULL; - // The Find methods should return a registration with an active version. + // The Find methods should return a registration with an active version + // and the expected update time. EXPECT_EQ(SERVICE_WORKER_OK, FindRegistrationForDocument(kDocumentUrl, &found_registration)); ASSERT_TRUE(found_registration); @@ -426,6 +439,7 @@ TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) { ASSERT_TRUE(found_registration->active_version()); EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, found_registration->active_version()->status()); + EXPECT_EQ(kToday, found_registration->last_update_check()); // Delete from storage but with a instance still live. EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId)); diff --git a/content/browser/service_worker/service_worker_unregister_job.cc b/content/browser/service_worker/service_worker_unregister_job.cc index 6797b0b5b79a8..aabad3aaf2f49 100644 --- a/content/browser/service_worker/service_worker_unregister_job.cc +++ b/content/browser/service_worker/service_worker_unregister_job.cc @@ -4,10 +4,13 @@ #include "content/browser/service_worker/service_worker_unregister_job.h" +#include "base/memory/weak_ptr.h" #include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_job_coordinator.h" #include "content/browser/service_worker/service_worker_registration.h" #include "content/browser/service_worker/service_worker_storage.h" +#include "content/browser/service_worker/service_worker_utils.h" +#include "content/browser/service_worker/service_worker_version.h" namespace content { @@ -18,7 +21,9 @@ ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob( const GURL& pattern) : context_(context), pattern_(pattern), - weak_factory_(this) {} + is_promise_resolved_(false), + weak_factory_(this) { +} ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob() {} @@ -57,28 +62,19 @@ void ServiceWorkerUnregisterJob::OnRegistrationFound( return; } - if (status != SERVICE_WORKER_OK) { + if (status != SERVICE_WORKER_OK || registration->is_uninstalling()) { Complete(status); return; } - DCHECK(registration); - DeleteRegistration(registration); -} + // TODO: "7. If registration.updatePromise is not null..." -void ServiceWorkerUnregisterJob::DeleteRegistration( - const scoped_refptr& registration) { - // TODO: Also doom installing version. - if (registration->waiting_version()) - registration->waiting_version()->Doom(); - if (registration->active_version()) - registration->active_version()->Doom(); - - context_->storage()->DeleteRegistration( - registration->id(), - registration->script_url().GetOrigin(), - base::Bind(&ServiceWorkerUnregisterJob::Complete, - weak_factory_.GetWeakPtr())); + // "8. Resolve promise." + ResolvePromise(SERVICE_WORKER_OK); + + registration->ClearWhenReady(); + + Complete(SERVICE_WORKER_OK); } void ServiceWorkerUnregisterJob::Complete(ServiceWorkerStatusCode status) { @@ -88,6 +84,14 @@ void ServiceWorkerUnregisterJob::Complete(ServiceWorkerStatusCode status) { void ServiceWorkerUnregisterJob::CompleteInternal( ServiceWorkerStatusCode status) { + if (!is_promise_resolved_) + ResolvePromise(status); +} + +void ServiceWorkerUnregisterJob::ResolvePromise( + ServiceWorkerStatusCode status) { + DCHECK(!is_promise_resolved_); + is_promise_resolved_ = true; for (std::vector::iterator it = callbacks_.begin(); it != callbacks_.end(); ++it) { diff --git a/content/browser/service_worker/service_worker_unregister_job.h b/content/browser/service_worker/service_worker_unregister_job.h index 0a1697aec2880..5e627ae1a4e3a 100644 --- a/content/browser/service_worker/service_worker_unregister_job.h +++ b/content/browser/service_worker/service_worker_unregister_job.h @@ -48,14 +48,14 @@ class ServiceWorkerUnregisterJob : public ServiceWorkerRegisterJobBase { void OnRegistrationFound( ServiceWorkerStatusCode status, const scoped_refptr& registration); - void DeleteRegistration( - const scoped_refptr& registration); void Complete(ServiceWorkerStatusCode status); void CompleteInternal(ServiceWorkerStatusCode status); + void ResolvePromise(ServiceWorkerStatusCode status); base::WeakPtr context_; const GURL pattern_; std::vector callbacks_; + bool is_promise_resolved_; base::WeakPtrFactory weak_factory_; DISALLOW_COPY_AND_ASSIGN(ServiceWorkerUnregisterJob); 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 fd101a08cb571..94298caa7a909 100644 --- a/content/browser/service_worker/service_worker_url_request_job.cc +++ b/content/browser/service_worker/service_worker_url_request_job.cc @@ -117,9 +117,10 @@ bool ServiceWorkerURLRequestJob::ReadRawData( return status.is_success(); } -void ServiceWorkerURLRequestJob::OnReceivedRedirect(net::URLRequest* request, - const GURL& new_url, - bool* defer_redirect) { +void ServiceWorkerURLRequestJob::OnReceivedRedirect( + net::URLRequest* request, + const net::RedirectInfo& redirect_info, + bool* defer_redirect) { NOTREACHED(); } 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 4da11fe69172c..dbbba108df09c 100644 --- a/content/browser/service_worker/service_worker_url_request_job.h +++ b/content/browser/service_worker/service_worker_url_request_job.h @@ -64,7 +64,7 @@ class CONTENT_EXPORT ServiceWorkerURLRequestJob // net::URLRequest::Delegate overrides that read the blob from the // ServiceWorkerFetchResponse. virtual void OnReceivedRedirect(net::URLRequest* request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE; virtual void OnAuthRequired(net::URLRequest* request, net::AuthChallengeInfo* auth_info) OVERRIDE; 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 41b0de7a3fd52..df9d1263f5879 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 @@ -105,7 +105,8 @@ class ServiceWorkerURLRequestJobTest : public testing::Test { scoped_ptr provider_host( new ServiceWorkerProviderHost( kProcessID, kProviderID, helper_->context()->AsWeakPtr(), NULL)); - provider_host->SetActiveVersion(version_.get()); + provider_host->AssociateRegistration(registration_); + registration_->SetActiveVersion(version_.get()); ChromeBlobStorageContext* chrome_blob_storage_context = ChromeBlobStorageContext::GetFor(browser_context_.get()); diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index 1532e3f728e4d..161b89489a55a 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc @@ -110,6 +110,8 @@ ServiceWorkerVersion::ServiceWorkerVersion( context_->AddLiveVersion(this); embedded_worker_ = context_->embedded_worker_registry()->CreateWorker(); embedded_worker_->AddListener(this); + cache_listener_.reset(new ServiceWorkerCacheListener(this, context)); + embedded_worker_->AddListener(cache_listener_.get()); } ServiceWorkerVersion::~ServiceWorkerVersion() { diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h index 497f74320c67e..48cbda98f59a4 100644 --- a/content/browser/service_worker/service_worker_version.h +++ b/content/browser/service_worker/service_worker_version.h @@ -18,6 +18,7 @@ #include "base/observer_list.h" #include "base/timer/timer.h" #include "content/browser/service_worker/embedded_worker_instance.h" +#include "content/browser/service_worker/service_worker_cache_listener.h" #include "content/browser/service_worker/service_worker_script_cache_map.h" #include "content/common/content_export.h" #include "content/common/service_worker/service_worker_status_code.h" @@ -284,6 +285,7 @@ class CONTENT_EXPORT ServiceWorkerVersion GURL scope_; Status status_; scoped_ptr embedded_worker_; + scoped_ptr cache_listener_; std::vector start_callbacks_; std::vector stop_callbacks_; std::vector status_change_callbacks_; diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.cc b/content/browser/service_worker/service_worker_write_to_cache_job.cc index b3e6cadfcb424..77c2c2dca64f5 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job.cc +++ b/content/browser/service_worker/service_worker_write_to_cache_job.cc @@ -24,6 +24,7 @@ ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob( ResourceType resource_type, base::WeakPtr context, ServiceWorkerVersion* version, + int extra_load_flags, int64 response_id) : net::URLRequestJob(request, network_delegate), resource_type_(resource_type), @@ -35,7 +36,7 @@ ServiceWorkerWriteToCacheJob::ServiceWorkerWriteToCacheJob( did_notify_started_(false), did_notify_finished_(false), weak_factory_(this) { - InitNetRequest(); + InitNetRequest(extra_load_flags); } ServiceWorkerWriteToCacheJob::~ServiceWorkerWriteToCacheJob() { @@ -131,7 +132,8 @@ const net::HttpResponseInfo* ServiceWorkerWriteToCacheJob::http_info() const { return http_info_.get(); } -void ServiceWorkerWriteToCacheJob::InitNetRequest() { +void ServiceWorkerWriteToCacheJob::InitNetRequest( + int extra_load_flags) { DCHECK(request()); net_request_ = request()->context()->CreateRequest( request()->url(), @@ -141,6 +143,8 @@ void ServiceWorkerWriteToCacheJob::InitNetRequest() { net_request_->set_first_party_for_cookies( request()->first_party_for_cookies()); net_request_->SetReferrer(request()->referrer()); + if (extra_load_flags) + net_request_->SetLoadFlags(net_request_->load_flags() | extra_load_flags); if (resource_type_ == RESOURCE_TYPE_SERVICE_WORKER) { // This will get copied into net_request_ when URLRequest::StartJob calls @@ -242,7 +246,7 @@ void ServiceWorkerWriteToCacheJob::OnWriteDataComplete(int result) { void ServiceWorkerWriteToCacheJob::OnReceivedRedirect( net::URLRequest* request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer_redirect) { DCHECK_EQ(net_request_, request); // Script resources can't redirect. @@ -301,6 +305,18 @@ void ServiceWorkerWriteToCacheJob::OnResponseStarted( // response to our consumer, just don't cache it? return; } + // To prevent most user-uploaded content from being used as a serviceworker. + if (version_->script_url() == url_) { + std::string mime_type; + request->GetMimeType(&mime_type); + if (mime_type != "application/x-javascript" && + mime_type != "text/javascript" && + mime_type != "application/javascript") { + AsyncNotifyDoneHelper(net::URLRequestStatus( + net::URLRequestStatus::FAILED, net::ERR_FAILED)); + return; + } + } WriteHeadersToCache(); } diff --git a/content/browser/service_worker/service_worker_write_to_cache_job.h b/content/browser/service_worker/service_worker_write_to_cache_job.h index 6833227c9f5cf..7506c6c93149b 100644 --- a/content/browser/service_worker/service_worker_write_to_cache_job.h +++ b/content/browser/service_worker/service_worker_write_to_cache_job.h @@ -40,9 +40,15 @@ class CONTENT_EXPORT ServiceWorkerWriteToCacheJob ResourceType resource_type, base::WeakPtr context, ServiceWorkerVersion* version, + int extra_load_flags, int64 response_id); private: + FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, + UpdateBefore24Hours); + FRIEND_TEST_ALL_PREFIXES(ServiceWorkerContextRequestHandlerTest, + UpdateAfter24Hours); + virtual ~ServiceWorkerWriteToCacheJob(); // net::URLRequestJob overrides @@ -63,7 +69,7 @@ class CONTENT_EXPORT ServiceWorkerWriteToCacheJob // Methods to drive the net request forward and // write data to the disk cache. - void InitNetRequest(); + void InitNetRequest(int extra_load_flags); void StartNetRequest(); net::URLRequestStatus ReadNetData( net::IOBuffer* buf, @@ -77,7 +83,7 @@ class CONTENT_EXPORT ServiceWorkerWriteToCacheJob // net::URLRequest::Delegate overrides that observe the net request. virtual void OnReceivedRedirect( net::URLRequest* request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE; virtual void OnAuthRequired( net::URLRequest* request, diff --git a/content/browser/site_instance_impl.cc b/content/browser/site_instance_impl.cc index 102c06758f932..6d5690d869d03 100644 --- a/content/browser/site_instance_impl.cc +++ b/content/browser/site_instance_impl.cc @@ -337,7 +337,8 @@ void SiteInstanceImpl::RenderProcessHostDestroyed(RenderProcessHost* host) { void SiteInstanceImpl::LockToOrigin() { // We currently only restrict this process to a particular site if the // --enable-strict-site-isolation or --site-per-process flags are present. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kEnableStrictSiteIsolation) || command_line.HasSwitch(switches::kSitePerProcess)) { ChildProcessSecurityPolicyImpl* policy = diff --git a/content/browser/site_instance_impl_unittest.cc b/content/browser/site_instance_impl_unittest.cc index 897a09c71516a..ce02db9331c13 100644 --- a/content/browser/site_instance_impl_unittest.cc +++ b/content/browser/site_instance_impl_unittest.cc @@ -405,7 +405,7 @@ TEST_F(SiteInstanceTest, IsSameWebSite) { // Test to ensure that there is only one SiteInstance per site in a given // BrowsingInstance, when process-per-site is not in use. TEST_F(SiteInstanceTest, OneSiteInstancePerSite) { - ASSERT_FALSE(CommandLine::ForCurrentProcess()->HasSwitch( + ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kProcessPerSite)); int delete_counter = 0; scoped_ptr browser_context(new TestBrowserContext()); @@ -479,7 +479,7 @@ TEST_F(SiteInstanceTest, OneSiteInstancePerSite) { // Test to ensure that there is only one RenderProcessHost per site for an // entire BrowserContext, if process-per-site is in use. TEST_F(SiteInstanceTest, OneSiteInstancePerSiteInBrowserContext) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kProcessPerSite); int delete_counter = 0; scoped_ptr browser_context(new TestBrowserContext()); @@ -570,7 +570,8 @@ static SiteInstanceImpl* CreateSiteInstance(BrowserContext* browser_context, TEST_F(SiteInstanceTest, ProcessSharingByType) { // This test shouldn't run with --site-per-process mode, since it doesn't // allow render process reuse, which this test explicitly exercises. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kSitePerProcess)) + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSitePerProcess)) return; // On Android by default the number of renderer hosts is unlimited and process @@ -690,7 +691,7 @@ TEST_F(SiteInstanceTest, HasWrongProcessForURL) { // Test to ensure that HasWrongProcessForURL behaves properly even when // --site-per-process is used (http://crbug.com/160671). TEST_F(SiteInstanceTest, HasWrongProcessForURLInSitePerProcess) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kSitePerProcess); scoped_ptr browser_context(new TestBrowserContext()); @@ -764,7 +765,7 @@ TEST_F(SiteInstanceTest, ProcessPerSiteWithWrongBindings) { // Test that we do not register processes with empty sites for process-per-site // mode. TEST_F(SiteInstanceTest, NoProcessPerSiteForEmptySite) { - CommandLine::ForCurrentProcess()->AppendSwitch( + base::CommandLine::ForCurrentProcess()->AppendSwitch( switches::kProcessPerSite); scoped_ptr browser_context(new TestBrowserContext()); scoped_ptr host; diff --git a/content/browser/speech/speech_recognition_browsertest.cc b/content/browser/speech/speech_recognition_browsertest.cc index 0f84793ce00cd..481b2a01dce2c 100644 --- a/content/browser/speech/speech_recognition_browsertest.cc +++ b/content/browser/speech/speech_recognition_browsertest.cc @@ -189,7 +189,7 @@ class SpeechRecognitionBrowserTest : // Simply loads the test page and checks if it was able to create a Speech // Recognition object in JavaScript, to make sure the Web Speech API is enabled. // http://crbug.com/396414 -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_MACOSX) #define MAYBE_Precheck DISABLED_Precheck #else #define MAYBE_Precheck Precheck diff --git a/content/browser/ssl/ssl_host_state.cc b/content/browser/ssl/ssl_host_state.cc deleted file mode 100644 index 6820a7d30da6d..0000000000000 --- a/content/browser/ssl/ssl_host_state.cc +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/browser/ssl/ssl_host_state.h" - -#include "base/logging.h" -#include "base/lazy_instance.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/ssl_host_state_delegate.h" -#include "net/http/http_transaction_factory.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_context_getter.h" - -const char kKeyName[] = "content_ssl_host_state"; - -namespace content { - -SSLHostState* SSLHostState::GetFor(BrowserContext* context) { - SSLHostState* rv = static_cast(context->GetUserData(kKeyName)); - if (!rv) { - rv = new SSLHostState(); - rv->delegate_ = context->GetSSLHostStateDelegate(); - // |context| may be NULL, implementing the default storage strategy. - if (context) - context->SetUserData(kKeyName, rv); - } - return rv; -} - -SSLHostState::SSLHostState() { -} - -SSLHostState::~SSLHostState() { -} - -void SSLHostState::HostRanInsecureContent(const std::string& host, int pid) { - DCHECK(CalledOnValidThread()); - ran_insecure_content_hosts_.insert(BrokenHostEntry(host, pid)); -} - -bool SSLHostState::DidHostRunInsecureContent(const std::string& host, - int pid) const { - DCHECK(CalledOnValidThread()); - return !!ran_insecure_content_hosts_.count(BrokenHostEntry(host, pid)); -} - -void SSLHostState::DenyCertForHost(net::X509Certificate* cert, - const std::string& host, - net::CertStatus error) { - DCHECK(CalledOnValidThread()); - - if (!delegate_) - return; - - delegate_->DenyCert(host, cert, error); -} - -void SSLHostState::AllowCertForHost(net::X509Certificate* cert, - const std::string& host, - net::CertStatus error) { - DCHECK(CalledOnValidThread()); - - if (!delegate_) - return; - - delegate_->AllowCert(host, cert, error); -} - -void SSLHostState::RevokeAllowAndDenyPreferences(const std::string& host) { - DCHECK(CalledOnValidThread()); - - if (!delegate_) - return; - - // TODO(jww): This will revoke all of the decisions in the browser context. - // However, the networking stack actually keeps track of its own list of - // exceptions per-HttpNetworkTransaction in the SSLConfig structure (see the - // allowed_bad_certs Vector in net/ssl/ssl_config.h). This dual-tracking of - // exceptions introduces a problem where the browser context can revoke a - // certificate, but if a transaction reuses a cached version of the SSLConfig - // (probably from a pooled socket), it may bypass the intestitial layer. - // - // Over time, the cached versions should expire and it should converge on - // showing the interstitial. We probably need to - // introduce into the networking stack a way revoke SSLConfig's - // allowed_bad_certs lists per socket. - delegate_->RevokeAllowAndDenyPreferences(host); -} - -bool SSLHostState::HasAllowedOrDeniedCert(const std::string& host) { - DCHECK(CalledOnValidThread()); - - if (!delegate_) - return false; - - return delegate_->HasAllowedOrDeniedCert(host); -} - -void SSLHostState::Clear() { - if (!delegate_) - return; - - delegate_->Clear(); -} - -net::CertPolicy::Judgment SSLHostState::QueryPolicy(net::X509Certificate* cert, - const std::string& host, - net::CertStatus error) { - DCHECK(CalledOnValidThread()); - - if (!delegate_) - return net::CertPolicy::Judgment::UNKNOWN; - - return delegate_->QueryPolicy(host, cert, error); -} - -} // namespace content diff --git a/content/browser/ssl/ssl_host_state.h b/content/browser/ssl/ssl_host_state.h deleted file mode 100644 index 392049bb4e252..0000000000000 --- a/content/browser/ssl/ssl_host_state.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_BROWSER_SSL_SSL_HOST_STATE_H_ -#define CONTENT_BROWSER_SSL_SSL_HOST_STATE_H_ - -#include -#include -#include - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/supports_user_data.h" -#include "base/threading/non_thread_safe.h" -#include "content/common/content_export.h" -#include "net/cert/cert_status_flags.h" -#include "net/cert/x509_certificate.h" - -namespace content { -class BrowserContext; -class SSLHostStateDelegate; - -// SSLHostState -// -// The SSLHostState encapulates the host-specific state for SSL errors. For -// example, SSLHostState remembers whether the user has whitelisted a -// particular broken cert for use with particular host. We separate this state -// from the SSLManager because this state is shared across many navigation -// controllers. -class CONTENT_EXPORT SSLHostState - : NON_EXPORTED_BASE(base::SupportsUserData::Data), - NON_EXPORTED_BASE(public base::NonThreadSafe) { - public: - // Contexts may specify a NULL certificate decision storage strategy. In that - // case, the returned SSLHostState from GetFor() will implement a default - // strategy of ignoring all exception requests and returning - // net::QueryPolicy::Judgment::UNKOWN from QueryPolicy(). - static SSLHostState* GetFor(BrowserContext* browser_context); - - SSLHostState(); - virtual ~SSLHostState(); - - // Records that a host has run insecure content. - void HostRanInsecureContent(const std::string& host, int pid); - - // Returns whether the specified host ran insecure content. - bool DidHostRunInsecureContent(const std::string& host, int pid) const; - - // Records that |cert| is not permitted to be used for |url| in the future, - // for a specified |error| type. - void DenyCertForHost(net::X509Certificate* cert, - const std::string& host, - net::CertStatus error); - - // Records that |cert| is permitted to be used for |url| in the future, for - // a specified |error| type. - void AllowCertForHost(net::X509Certificate* cert, - const std::string& host, - net::CertStatus error); - - // Revoke all allow/deny preferences for |url|. - void RevokeAllowAndDenyPreferences(const std::string& host); - - bool HasAllowedOrDeniedCert(const std::string& host); - - // Clear all allow/deny preferences. - void Clear(); - - // Queries whether |cert| is allowed or denied for |url| and |error|. - net::CertPolicy::Judgment QueryPolicy(net::X509Certificate* cert, - const std::string& host, - net::CertStatus error); - - private: - // A BrokenHostEntry is a pair of (host, process_id) that indicates the host - // contains insecure content in that renderer process. - typedef std::pair BrokenHostEntry; - - // Hosts which have been contaminated with insecure content in the - // specified process. Note that insecure content can travel between - // same-origin frames in one processs but cannot jump between processes. - std::set ran_insecure_content_hosts_; - - // The certificate decision store. It may be NULL, depending on the browsing - // context. This is owned by the browsing context. - SSLHostStateDelegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(SSLHostState); -}; - -} // namespace content - -#endif // CONTENT_BROWSER_SSL_SSL_HOST_STATE_H_ diff --git a/content/browser/ssl/ssl_policy.cc b/content/browser/ssl/ssl_policy.cc index c06c7db852c97..18fdde4dfe2bf 100644 --- a/content/browser/ssl/ssl_policy.cc +++ b/content/browser/ssl/ssl_policy.cc @@ -32,11 +32,13 @@ SSLPolicy::SSLPolicy(SSLPolicyBackend* backend) } void SSLPolicy::OnCertError(SSLCertErrorHandler* handler) { + bool expired_previous_decision; // First we check if we know the policy for this error. net::CertPolicy::Judgment judgment = backend_->QueryPolicy(handler->ssl_info().cert.get(), handler->request_url().host(), - handler->cert_error()); + handler->cert_error(), + &expired_previous_decision); if (judgment == net::CertPolicy::ALLOWED) { handler->ContinueRequest(); @@ -47,6 +49,7 @@ void SSLPolicy::OnCertError(SSLCertErrorHandler* handler) { // For now we handle the DENIED as the UNKNOWN, which means a blocking // page is shown to the user every time he comes back to the page. + int options_mask = 0; switch (handler->cert_error()) { case net::ERR_CERT_COMMON_NAME_INVALID: case net::ERR_CERT_DATE_INVALID: @@ -54,7 +57,13 @@ void SSLPolicy::OnCertError(SSLCertErrorHandler* handler) { case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM: case net::ERR_CERT_WEAK_KEY: case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION: - OnCertErrorInternal(handler, !handler->fatal(), handler->fatal()); + if (!handler->fatal()) + options_mask |= OVERRIDABLE; + else + options_mask |= STRICT_ENFORCEMENT; + if (expired_previous_decision) + options_mask |= EXPIRED_PREVIOUS_DECISION; + OnCertErrorInternal(handler, options_mask); break; case net::ERR_CERT_NO_REVOCATION_MECHANISM: // Ignore this error. @@ -70,7 +79,11 @@ void SSLPolicy::OnCertError(SSLCertErrorHandler* handler) { case net::ERR_CERT_INVALID: case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: - OnCertErrorInternal(handler, false, handler->fatal()); + if (handler->fatal()) + options_mask |= STRICT_ENFORCEMENT; + if (expired_previous_decision) + options_mask |= EXPIRED_PREVIOUS_DECISION; + OnCertErrorInternal(handler, options_mask); break; default: NOTREACHED(); @@ -182,8 +195,11 @@ void SSLPolicy::OnAllowCertificate(scoped_refptr handler, // Certificate Error Routines void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler* handler, - bool overridable, - bool strict_enforcement) { + int options_mask) { + bool overridable = (options_mask & OVERRIDABLE) != 0; + bool strict_enforcement = (options_mask & STRICT_ENFORCEMENT) != 0; + bool expired_previous_decision = + (options_mask & EXPIRED_PREVIOUS_DECISION) != 0; CertificateRequestResultType result = CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE; GetContentClient()->browser()->AllowCertificateError( @@ -195,7 +211,9 @@ void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler* handler, handler->resource_type(), overridable, strict_enforcement, - base::Bind(&SSLPolicy::OnAllowCertificate, base::Unretained(this), + expired_previous_decision, + base::Bind(&SSLPolicy::OnAllowCertificate, + base::Unretained(this), make_scoped_refptr(handler)), &result); switch (result) { diff --git a/content/browser/ssl/ssl_policy.h b/content/browser/ssl/ssl_policy.h index 64b9c3a3b94e5..78dbb6d8960f5 100644 --- a/content/browser/ssl/ssl_policy.h +++ b/content/browser/ssl/ssl_policy.h @@ -44,20 +44,27 @@ class SSLPolicy { SSLPolicyBackend* backend() const { return backend_; } private: + enum OnCertErrorInternalOptionsMask { + OVERRIDABLE = 1 << 0, + STRICT_ENFORCEMENT = 1 << 1, + EXPIRED_PREVIOUS_DECISION = 1 << 2 + }; + // Callback that the user chose to accept or deny the certificate. void OnAllowCertificate(scoped_refptr handler, bool allow); // Helper method for derived classes handling certificate errors. // - // |overridable| indicates whether or not the user could (assuming perfect + // Options should be a bitmask combination of OnCertErrorInternalOptionsMask. + // OVERRIDABLE indicates whether or not the user could (assuming perfect // knowledge) successfully override the error and still get the security - // guarantees of TLS. |strict_enforcement| indicates whether or not the - // site the user is trying to connect to has requested strict enforcement - // of certificate validation (e.g. with HTTP Strict-Transport-Security). - void OnCertErrorInternal(SSLCertErrorHandler* handler, - bool overridable, - bool strict_enforcement); + // guarantees of TLS. STRICT_ENFORCEMENT indicates whether or not the site the + // user is trying to connect to has requested strict enforcement of + // certificate validation (e.g. with HTTP Strict-Transport-Security). + // EXPIRED_PREVIOUS_DECISION indicates whether a user decision had been + // previously made but the decision has expired. + void OnCertErrorInternal(SSLCertErrorHandler* handler, int options_mask); // If the security style of |entry| has not been initialized, then initialize // it with the default style for its URL. diff --git a/content/browser/ssl/ssl_policy_backend.cc b/content/browser/ssl/ssl_policy_backend.cc index 3eb4f467e709d..e81c35d162810 100644 --- a/content/browser/ssl/ssl_policy_backend.cc +++ b/content/browser/ssl/ssl_policy_backend.cc @@ -5,44 +5,56 @@ #include "content/browser/ssl/ssl_policy_backend.h" #include "content/browser/frame_host/navigation_controller_impl.h" -#include "content/browser/ssl/ssl_host_state.h" #include "content/public/browser/browser_context.h" +#include "content/public/browser/ssl_host_state_delegate.h" namespace content { SSLPolicyBackend::SSLPolicyBackend(NavigationControllerImpl* controller) - : ssl_host_state_(SSLHostState::GetFor(controller->GetBrowserContext())), + : ssl_host_state_delegate_( + controller->GetBrowserContext()->GetSSLHostStateDelegate()), controller_(controller) { DCHECK(controller_); } void SSLPolicyBackend::HostRanInsecureContent(const std::string& host, int id) { - ssl_host_state_->HostRanInsecureContent(host, id); + if (ssl_host_state_delegate_) + ssl_host_state_delegate_->HostRanInsecureContent(host, id); SSLManager::NotifySSLInternalStateChanged(controller_->GetBrowserContext()); } bool SSLPolicyBackend::DidHostRunInsecureContent(const std::string& host, int pid) const { - return ssl_host_state_->DidHostRunInsecureContent(host, pid); + if (!ssl_host_state_delegate_) + return false; + + return ssl_host_state_delegate_->DidHostRunInsecureContent(host, pid); } void SSLPolicyBackend::DenyCertForHost(net::X509Certificate* cert, const std::string& host, net::CertStatus error) { - ssl_host_state_->DenyCertForHost(cert, host, error); + if (ssl_host_state_delegate_) + ssl_host_state_delegate_->DenyCert(host, cert, error); } void SSLPolicyBackend::AllowCertForHost(net::X509Certificate* cert, const std::string& host, net::CertStatus error) { - ssl_host_state_->AllowCertForHost(cert, host, error); + if (ssl_host_state_delegate_) + ssl_host_state_delegate_->AllowCert(host, cert, error); } net::CertPolicy::Judgment SSLPolicyBackend::QueryPolicy( net::X509Certificate* cert, const std::string& host, - net::CertStatus error) { - return ssl_host_state_->QueryPolicy(cert, host, error); + net::CertStatus error, + bool* expired_previous_decision) { + if (!ssl_host_state_delegate_) + return net::CertPolicy::UNKNOWN; + + return ssl_host_state_delegate_->QueryPolicy( + host, cert, error, expired_previous_decision); } } // namespace content diff --git a/content/browser/ssl/ssl_policy_backend.h b/content/browser/ssl/ssl_policy_backend.h index 06ea23eccca94..5997b289aabda 100644 --- a/content/browser/ssl/ssl_policy_backend.h +++ b/content/browser/ssl/ssl_policy_backend.h @@ -15,7 +15,7 @@ namespace content { class NavigationControllerImpl; -class SSLHostState; +class SSLHostStateDelegate; class SSLPolicyBackend { public: @@ -39,14 +39,17 @@ class SSLPolicyBackend { const std::string& host, net::CertStatus error); - // Queries whether |cert| is allowed or denied for |host|. + // Queries whether |cert| is allowed or denied for |host|. Returns true in + // |expired_previous_decision| if a user decision had been made previously but + // that decision has expired, otherwise false. net::CertPolicy::Judgment QueryPolicy(net::X509Certificate* cert, const std::string& host, - net::CertStatus error); + net::CertStatus error, + bool* expired_previous_decision); private: - // SSL state specific for each host. - SSLHostState* ssl_host_state_; + // SSL state delegate specific for each host. + SSLHostStateDelegate* ssl_host_state_delegate_; NavigationControllerImpl* controller_; diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc index ea344cea890da..134c1617768d6 100644 --- a/content/browser/storage_partition_impl.cc +++ b/content/browser/storage_partition_impl.cc @@ -202,6 +202,8 @@ STATIC_CONST_MEMBER_DEFINITION const uint32 StoragePartition::REMOVE_DATA_MASK_INDEXEDDB; STATIC_CONST_MEMBER_DEFINITION const uint32 StoragePartition::REMOVE_DATA_MASK_LOCAL_STORAGE; +STATIC_CONST_MEMBER_DEFINITION const uint32 + StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS; STATIC_CONST_MEMBER_DEFINITION const uint32 StoragePartition::REMOVE_DATA_MASK_SHADER_CACHE; STATIC_CONST_MEMBER_DEFINITION const uint32 @@ -231,6 +233,7 @@ int StoragePartitionImpl::GenerateQuotaClientMask(uint32 remove_mask) { quota_client_mask |= quota::QuotaClient::kAppcache; if (remove_mask & StoragePartition::REMOVE_DATA_MASK_INDEXEDDB) quota_client_mask |= quota::QuotaClient::kIndexedDatabase; + // TODO(jsbell): StoragePartition::REMOVE_DATA_MASK_SERVICE_WORKERS) return quota_client_mask; } @@ -698,7 +701,8 @@ void StoragePartitionImpl::DataDeletionHelper::ClearDataOnUIThread( if (remove_mask & REMOVE_DATA_MASK_INDEXEDDB || remove_mask & REMOVE_DATA_MASK_WEBSQL || remove_mask & REMOVE_DATA_MASK_APPCACHE || - remove_mask & REMOVE_DATA_MASK_FILE_SYSTEMS) { + remove_mask & REMOVE_DATA_MASK_FILE_SYSTEMS || + remove_mask & REMOVE_DATA_MASK_SERVICE_WORKERS) { IncrementTaskCountOnUI(); BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, diff --git a/content/browser/storage_partition_impl_map.cc b/content/browser/storage_partition_impl_map.cc index 1c5175de10c28..3a67d724837c3 100644 --- a/content/browser/storage_partition_impl_map.cc +++ b/content/browser/storage_partition_impl_map.cc @@ -580,6 +580,15 @@ void StoragePartitionImplMap::PostCreateInitialization( make_scoped_refptr( browser_context_->GetSpecialStoragePolicy()))); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&ServiceWorkerContextWrapper::SetBlobParametersForCache, + partition->GetServiceWorkerContext(), + make_scoped_refptr(partition->GetURLRequestContext()), + make_scoped_refptr( + ChromeBlobStorageContext::GetFor(browser_context_)))); + // We do not call InitializeURLRequestContext() for media contexts because, // other than the HTTP cache, the media contexts share the same backing // objects as their associated "normal" request context. Thus, the previous diff --git a/content/browser/tracing/tracing_ui.cc b/content/browser/tracing/tracing_ui.cc index d977d21ba44cf..193774a6ea091 100644 --- a/content/browser/tracing/tracing_ui.cc +++ b/content/browser/tracing/tracing_ui.cc @@ -317,7 +317,8 @@ void TracingUI::OnMonitoringStateChanged(bool is_monitoring) { } void TracingUI::DoUpload(const base::ListValue* args) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); std::string upload_url = kUploadURL; if (command_line.HasSwitch(switches::kTraceUploadURL)) { upload_url = diff --git a/content/browser/transition_browsertest.cc b/content/browser/transition_browsertest.cc index 927fbba5e7921..ff3203505a995 100644 --- a/content/browser/transition_browsertest.cc +++ b/content/browser/transition_browsertest.cc @@ -3,12 +3,14 @@ // found in the LICENSE file. #include "base/bind.h" +#include "base/command_line.h" #include "content/browser/loader/cross_site_resource_handler.h" #include "content/browser/loader/resource_dispatcher_host_impl.h" #include "content/browser/loader/resource_request_info_impl.h" #include "content/browser/transition_request_manager.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/web_contents_observer.h" +#include "content/public/common/content_switches.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" @@ -24,6 +26,11 @@ class TransitionBrowserTest : public ContentBrowserTest { public: TransitionBrowserTest() {} + virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { + command_line->AppendSwitch( + switches::kEnableExperimentalWebPlatformFeatures); + } + private: DISALLOW_COPY_AND_ASSIGN(TransitionBrowserTest); }; @@ -60,8 +67,10 @@ class TransitionBrowserTestObserver ResourceRequestInfoImpl* info = ResourceRequestInfoImpl::ForRequest(request_); - TransitionRequestManager::GetInstance()->SetHasPendingTransitionRequest( - child_id, info->GetRenderFrameID(), is_transition_request_); + if (is_transition_request_) { + TransitionRequestManager::GetInstance()->AddPendingTransitionRequestData( + child_id, info->GetRenderFrameID(), "*", "", ""); + } } virtual void OnResponseStarted( diff --git a/content/browser/transition_request_manager.cc b/content/browser/transition_request_manager.cc index 133dc3c5bea7f..ae9a9f3fbd27a 100644 --- a/content/browser/transition_request_manager.cc +++ b/content/browser/transition_request_manager.cc @@ -4,9 +4,12 @@ #include "content/browser/transition_request_manager.h" +#include "base/command_line.h" #include "base/memory/singleton.h" +#include "base/metrics/field_trial.h" #include "base/strings/string_util.h" #include "content/public/browser/browser_thread.h" +#include "content/public/common/content_switches.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" @@ -72,6 +75,12 @@ bool EnumerateLinkHeaders( namespace content { +TransitionLayerData::TransitionLayerData() { +} + +TransitionLayerData::~TransitionLayerData() { +} + void TransitionRequestManager::ParseTransitionStylesheetsFromHeaders( const scoped_refptr& headers, std::vector& entering_stylesheets, @@ -93,28 +102,72 @@ void TransitionRequestManager::ParseTransitionStylesheetsFromHeaders( } } +TransitionRequestManager::TransitionRequestData::TransitionRequestData() { +} + +TransitionRequestManager::TransitionRequestData::~TransitionRequestData() { +} + +void TransitionRequestManager::TransitionRequestData::AddEntry( + const std::string& allowed_destination_host_pattern, + const std::string& css_selector, + const std::string& markup) { + allowed_entries_.push_back(AllowedEntry(allowed_destination_host_pattern, + css_selector, + markup)); +} + +bool TransitionRequestManager::TransitionRequestData::FindEntry( + const GURL& request_url, + TransitionLayerData* transition_data) { + DCHECK(!allowed_entries_.empty()); + CHECK(transition_data); + // TODO(oysteine): Add CSP check to validate the host pattern and the + // request_url. Must be done before this feature is moved out from the flag. + CHECK(CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableExperimentalWebPlatformFeatures) || + base::FieldTrialList::FindFullName("NavigationTransitions") == + "Enabled"); + + const AllowedEntry& allowed_entry = allowed_entries_[0]; + transition_data->markup = allowed_entry.markup; + transition_data->css_selector = allowed_entry.css_selector; + return true; +} + bool TransitionRequestManager::HasPendingTransitionRequest( - int process_id, - int render_frame_id) { + int render_process_id, + int render_frame_id, + const GURL& request_url, + TransitionLayerData* transition_data) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - - std::pair key(process_id, render_frame_id); - return (pending_transition_frames_.find(key) != - pending_transition_frames_.end()); + DCHECK(transition_data); + std::pair key(render_process_id, render_frame_id); + RenderFrameRequestDataMap::iterator iter = + pending_transition_frames_.find(key); + return iter != pending_transition_frames_.end() && + iter->second.FindEntry(request_url, transition_data); } -void TransitionRequestManager::SetHasPendingTransitionRequest( - int process_id, +void TransitionRequestManager::AddPendingTransitionRequestData( + int render_process_id, int render_frame_id, - bool has_pending) { + const std::string& allowed_destination_host_pattern, + const std::string& css_selector, + const std::string& markup) { DCHECK_CURRENTLY_ON(BrowserThread::IO); - std::pair key(process_id, render_frame_id); - if (has_pending) { - pending_transition_frames_.insert(key); - } else { - pending_transition_frames_.erase(key); - } + std::pair key(render_process_id, render_frame_id); + pending_transition_frames_[key].AddEntry(allowed_destination_host_pattern, + css_selector, + markup); +} + +void TransitionRequestManager::ClearPendingTransitionRequestData( + int render_process_id, int render_frame_id) { + DCHECK_CURRENTLY_ON(BrowserThread::IO); + std::pair key(render_process_id, render_frame_id); + pending_transition_frames_.erase(key); } TransitionRequestManager::TransitionRequestManager() { diff --git a/content/browser/transition_request_manager.h b/content/browser/transition_request_manager.h index 48be189ca50e0..0a01878eb1a5f 100644 --- a/content/browser/transition_request_manager.h +++ b/content/browser/transition_request_manager.h @@ -5,7 +5,8 @@ #ifndef CONTENT_BROWSER_TRANSITION_REQUEST_MANAGER_H_ #define CONTENT_BROWSER_TRANSITION_REQUEST_MANAGER_H_ -#include +#include +#include #include #include @@ -20,9 +21,21 @@ struct DefaultSingletonTraits; namespace net { class HttpResponseHeaders; } +class GURL; namespace content { +// This struct passes data about an imminent transition between threads. +struct TransitionLayerData { + TransitionLayerData(); + ~TransitionLayerData(); + + std::string markup; + std::string css_selector; + scoped_refptr response_headers; + GURL request_url; +}; + // TransitionRequestManager is used to handle bookkeeping for transition // requests and responses. // @@ -42,28 +55,67 @@ class TransitionRequestManager { const GURL& resolve_address); // Returns whether the RenderFrameHost specified by the given IDs currently - // has a pending transition request. If so, we will have to delay the + // has any pending transition request data. If so, we will have to delay the // response until the embedder resumes the request. - bool HasPendingTransitionRequest(int process_id, int render_frame_id); - - // Sets whether the RenderFrameHost specified by the given IDs currently has a - // pending transition request. - CONTENT_EXPORT void SetHasPendingTransitionRequest(int process_id, - int render_frame_id, - bool has_pending); + bool HasPendingTransitionRequest(int render_process_id, + int render_frame_id, + const GURL& request_url, + TransitionLayerData* transition_data); + + // Adds pending request data for a transition navigation for the + // RenderFrameHost specified by the given IDs. + CONTENT_EXPORT void AddPendingTransitionRequestData( + int render_process_id, + int render_frame_id, + const std::string& allowed_destination_host_pattern, + const std::string& css_selector, + const std::string& markup); + + void ClearPendingTransitionRequestData(int render_process_id, + int render_frame_id); private: + class TransitionRequestData { + public: + TransitionRequestData(); + ~TransitionRequestData(); + void AddEntry(const std::string& allowed_destination_host_pattern, + const std::string& selector, + const std::string& markup); + bool FindEntry(const GURL& request_url, + TransitionLayerData* transition_data); + + private: + struct AllowedEntry { + // These strings could have originated from a compromised renderer, + // and should not be trusted or assumed safe. They are only used within + // a sandboxed iframe with scripts disabled. + std::string allowed_destination_host_pattern; + std::string css_selector; + std::string markup; + + AllowedEntry(const std::string& allowed_destination_host_pattern, + const std::string& css_selector, + const std::string& markup) : + allowed_destination_host_pattern(allowed_destination_host_pattern), + css_selector(css_selector), + markup(markup) {} + }; + std::vector allowed_entries_; + }; + friend struct DefaultSingletonTraits; - typedef std::set > RenderFrameSet; + typedef std::map, TransitionRequestData> + RenderFrameRequestDataMap; TransitionRequestManager(); ~TransitionRequestManager(); - // Set of (render_process_host_id, render_frame_id) pairs of all - // RenderFrameHosts that have pending transition requests. Used to pass - // information to the CrossSiteResourceHandler without doing a round-trip - // between IO->UI->IO threads. - RenderFrameSet pending_transition_frames_; + // Map of (render_process_host_id, render_frame_id) pairs of all + // RenderFrameHosts that have pending cross-site requests and their data. + // Used to pass information to the CrossSiteResourceHandler without doing a + // round-trip between IO->UI->IO threads. + RenderFrameRequestDataMap pending_transition_frames_; DISALLOW_COPY_AND_ASSIGN(TransitionRequestManager); }; diff --git a/content/browser/utility_process_host_impl.cc b/content/browser/utility_process_host_impl.cc index ddc41a7413132..1a1b94273cb30 100644 --- a/content/browser/utility_process_host_impl.cc +++ b/content/browser/utility_process_host_impl.cc @@ -194,7 +194,8 @@ bool UtilityProcessHostImpl::StartProcess() { in_process_thread_.reset(g_utility_main_thread_factory(channel_id)); in_process_thread_->Start(); } else { - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); int child_flags = child_flags_; #if defined(OS_POSIX) @@ -216,7 +217,7 @@ bool UtilityProcessHostImpl::StartProcess() { return false; } - CommandLine* cmd_line = new CommandLine(exe_path); + base::CommandLine* cmd_line = new base::CommandLine(exe_path); cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kUtilityProcess); cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); diff --git a/content/browser/web_contents/aura/gesture_nav_simple.cc b/content/browser/web_contents/aura/gesture_nav_simple.cc index f904fe28cf16a..78f77a1063458 100644 --- a/content/browser/web_contents/aura/gesture_nav_simple.cc +++ b/content/browser/web_contents/aura/gesture_nav_simple.cc @@ -145,9 +145,9 @@ void GestureNavSimple::CompleteGestureAnimation() { ApplyEffectsAndDestroy(arrow_->transform(), 0.f); } -void GestureNavSimple::ApplyEffectsForDelta(float delta_x) { +bool GestureNavSimple::ApplyEffectsForDelta(float delta_x) { if (!arrow_) - return; + return false; CHECK_GT(completion_threshold_, 0.f); CHECK_GE(delta_x, 0.f); double complete = std::min(1.f, delta_x / completion_threshold_); @@ -157,14 +157,15 @@ void GestureNavSimple::ApplyEffectsForDelta(float delta_x) { 0.f); arrow_->SetTransform(transform); arrow_->SetOpacity(gfx::Tween::FloatValueBetween(complete, kMinOpacity, 1.f)); + return true; } gfx::Rect GestureNavSimple::GetVisibleBounds() const { return web_contents_->GetNativeView()->bounds(); } -void GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) { - ApplyEffectsForDelta(std::abs(delta_x) + 50.f); +bool GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) { + return ApplyEffectsForDelta(std::abs(delta_x) + 50.f); } void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) { diff --git a/content/browser/web_contents/aura/gesture_nav_simple.h b/content/browser/web_contents/aura/gesture_nav_simple.h index e8466e5e6e24f..eeecebf3cb9ff 100644 --- a/content/browser/web_contents/aura/gesture_nav_simple.h +++ b/content/browser/web_contents/aura/gesture_nav_simple.h @@ -32,11 +32,12 @@ class GestureNavSimple : public OverscrollControllerDelegate { void ApplyEffectsAndDestroy(const gfx::Transform& transform, float opacity); void AbortGestureAnimation(); void CompleteGestureAnimation(); - void ApplyEffectsForDelta(float delta_x); + bool ApplyEffectsForDelta(float delta_x); // OverscrollControllerDelegate: virtual gfx::Rect GetVisibleBounds() const OVERRIDE; - virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE; + // Returns true if the scroll update was consumed. + virtual bool OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE; virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE; virtual void OnOverscrollModeChange(OverscrollMode old_mode, OverscrollMode new_mode) OVERRIDE; diff --git a/content/browser/web_contents/aura/overscroll_navigation_overlay.cc b/content/browser/web_contents/aura/overscroll_navigation_overlay.cc index b478c60d070b0..530028b6721af 100644 --- a/content/browser/web_contents/aura/overscroll_navigation_overlay.cc +++ b/content/browser/web_contents/aura/overscroll_navigation_overlay.cc @@ -197,8 +197,7 @@ ui::Layer* OverscrollNavigationOverlay::CreateSlideLayer(int offset) { gfx::Image image; if (entry && entry->screenshot().get()) { std::vector image_reps; - image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), - ui::GetScaleFactorForNativeView(window_.get()))); + image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f)); image = gfx::Image(image_reps); } if (!layer_delegate_) diff --git a/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc b/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc index 5c93195231ff2..3f56f338064ea 100644 --- a/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc +++ b/content/browser/web_contents/aura/overscroll_navigation_overlay_unittest.cc @@ -10,6 +10,7 @@ #include "content/common/frame_messages.h" #include "content/common/view_messages.h" #include "content/public/test/mock_render_process_host.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" #include "ui/aura/test/test_windows.h" diff --git a/content/browser/web_contents/web_contents_android.cc b/content/browser/web_contents/web_contents_android.cc index 6033d2c0e8b15..a007f21a7e59a 100644 --- a/content/browser/web_contents/web_contents_android.cc +++ b/content/browser/web_contents/web_contents_android.cc @@ -6,20 +6,43 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" +#include "base/command_line.h" +#include "base/json/json_writer.h" #include "base/logging.h" #include "content/browser/android/interstitial_page_delegate_android.h" #include "content/browser/frame_host/interstitial_page_impl.h" +#include "content/browser/media/android/browser_media_player_manager.h" #include "content/browser/media/media_web_contents_observer.h" #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/common/input_messages.h" #include "content/common/view_messages.h" +#include "content/public/browser/browser_context.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" #include "jni/WebContentsImpl_jni.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF8; +using base::android::ConvertJavaStringToUTF16; +using base::android::ConvertUTF8ToJavaString; +using base::android::ScopedJavaGlobalRef; + +namespace { + +void JavaScriptResultCallback(const ScopedJavaGlobalRef& callback, + const base::Value* result) { + JNIEnv* env = base::android::AttachCurrentThread(); + std::string json; + base::JSONWriter::Write(result, &json); + ScopedJavaLocalRef j_json = ConvertUTF8ToJavaString(env, json); + content::Java_WebContentsImpl_onEvaluateJavaScriptResult( + env, j_json.obj(), callback.obj()); +} + +} // namespace namespace content { @@ -104,6 +127,55 @@ jint WebContentsAndroid::GetBackgroundColor(JNIEnv* env, jobject obj) { return rwhva->GetCachedBackgroundColor(); } +ScopedJavaLocalRef WebContentsAndroid::GetURL(JNIEnv* env, + jobject obj) const { + return ConvertUTF8ToJavaString(env, web_contents_->GetURL().spec()); +} + +jboolean WebContentsAndroid::IsIncognito(JNIEnv* env, jobject obj) { + return web_contents_->GetBrowserContext()->IsOffTheRecord(); +} + +void WebContentsAndroid::ResumeResponseDeferredAtStart(JNIEnv* env, + jobject obj) { + static_cast(web_contents_)->ResumeResponseDeferredAtStart(); +} + +void WebContentsAndroid::SetHasPendingNavigationTransitionForTesting( + JNIEnv* env, + jobject obj) { + CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kEnableExperimentalWebPlatformFeatures); + RenderFrameHost* frame = + static_cast(web_contents_)->GetMainFrame(); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(&TransitionRequestManager::AddPendingTransitionRequestData, + base::Unretained(TransitionRequestManager::GetInstance()), + frame->GetProcess()->GetID(), + frame->GetRoutingID(), + "*", + "", + "")); +} + +void WebContentsAndroid::SetupTransitionView(JNIEnv* env, + jobject jobj, + jstring markup) { + web_contents_->GetMainFrame()->Send(new FrameMsg_SetupTransitionView( + web_contents_->GetMainFrame()->GetRoutingID(), + ConvertJavaStringToUTF8(env, markup))); +} + +void WebContentsAndroid::BeginExitTransition(JNIEnv* env, + jobject jobj, + jstring css_selector) { + web_contents_->GetMainFrame()->Send(new FrameMsg_BeginExitTransition( + web_contents_->GetMainFrame()->GetRoutingID(), + ConvertJavaStringToUTF8(env, css_selector))); +} + void WebContentsAndroid::OnHide(JNIEnv* env, jobject obj) { web_contents_->WasHidden(); PauseVideo(); @@ -113,6 +185,21 @@ void WebContentsAndroid::OnShow(JNIEnv* env, jobject obj) { web_contents_->WasShown(); } +void WebContentsAndroid::ReleaseMediaPlayers(JNIEnv* env, jobject jobj) { +#if defined(ENABLE_BROWSER_CDMS) + RenderViewHostImpl* rvhi = static_cast( + web_contents_->GetRenderViewHost()); + if (!rvhi || !rvhi->GetMainFrame()) + return; + + BrowserMediaPlayerManager* manager = + rvhi->media_web_contents_observer()->GetMediaPlayerManager( + rvhi->GetMainFrame()); + if (manager) + manager->ReleaseAllMediaPlayers(); +#endif // defined(ENABLE_BROWSER_CDMS) +} + void WebContentsAndroid::PauseVideo() { RenderViewHostImpl* rvhi = static_cast( web_contents_->GetRenderViewHost()); @@ -201,4 +288,90 @@ void WebContentsAndroid::SelectWordAroundCaret(JNIEnv* env, jobject obj) { host->SelectWordAroundCaret(); } +bool WebContentsAndroid::WillHandleDeferAfterResponseStarted() { + JNIEnv* env = AttachCurrentThread(); + return Java_WebContentsImpl_willHandleDeferAfterResponseStarted(env, + obj_.obj()); +} + +void WebContentsAndroid::DidDeferAfterResponseStarted( + const TransitionLayerData& transition_data) { + JNIEnv* env = AttachCurrentThread(); + std::vector entering_stylesheets; + std::string transition_color; + if (transition_data.response_headers) { + TransitionRequestManager::ParseTransitionStylesheetsFromHeaders( + transition_data.response_headers, + entering_stylesheets, + transition_data.request_url); + + transition_data.response_headers->EnumerateHeader( + NULL, "X-Transition-Entering-Color", &transition_color); + } + + ScopedJavaLocalRef jstring_markup( + ConvertUTF8ToJavaString(env, transition_data.markup)); + + ScopedJavaLocalRef jstring_css_selector( + ConvertUTF8ToJavaString(env, transition_data.css_selector)); + + ScopedJavaLocalRef jstring_transition_color( + ConvertUTF8ToJavaString(env, transition_color)); + + Java_WebContentsImpl_didDeferAfterResponseStarted( + env, + obj_.obj(), + jstring_markup.obj(), + jstring_css_selector.obj(), + jstring_transition_color.obj()); + + std::vector::const_iterator iter = entering_stylesheets.begin(); + for (; iter != entering_stylesheets.end(); ++iter) { + ScopedJavaLocalRef jstring_url( + ConvertUTF8ToJavaString(env, iter->spec())); + Java_WebContentsImpl_addEnteringStylesheetToTransition( + env, obj_.obj(), jstring_url.obj()); + } +} + +void WebContentsAndroid::DidStartNavigationTransitionForFrame(int64 frame_id) { + JNIEnv* env = AttachCurrentThread(); + Java_WebContentsImpl_didStartNavigationTransitionForFrame( + env, obj_.obj(), frame_id); +} + +void WebContentsAndroid::EvaluateJavaScript(JNIEnv* env, + jobject obj, + jstring script, + jobject callback, + jboolean start_renderer) { + RenderViewHost* rvh = web_contents_->GetRenderViewHost(); + DCHECK(rvh); + + if (start_renderer && !rvh->IsRenderViewLive()) { + if (!static_cast(web_contents_)-> + CreateRenderViewForInitialEmptyDocument()) { + LOG(ERROR) << "Failed to create RenderView in EvaluateJavaScript"; + return; + } + } + + if (!callback) { + // No callback requested. + web_contents_->GetMainFrame()->ExecuteJavaScript( + ConvertJavaStringToUTF16(env, script)); + return; + } + + // Secure the Java callback in a scoped object and give ownership of it to the + // base::Callback. + ScopedJavaGlobalRef j_callback; + j_callback.Reset(env, callback); + content::RenderFrameHost::JavaScriptResultCallback js_callback = + base::Bind(&JavaScriptResultCallback, j_callback); + + web_contents_->GetMainFrame()->ExecuteJavaScript( + ConvertJavaStringToUTF16(env, script), js_callback); +} + } // namespace content diff --git a/content/browser/web_contents/web_contents_android.h b/content/browser/web_contents/web_contents_android.h index add9637f5290b..514481794eedc 100644 --- a/content/browser/web_contents/web_contents_android.h +++ b/content/browser/web_contents/web_contents_android.h @@ -13,11 +13,13 @@ #include "base/supports_user_data.h" #include "content/browser/frame_host/navigation_controller_android.h" #include "content/browser/renderer_host/render_widget_host_view_android.h" +#include "content/browser/transition_request_manager.h" #include "content/common/content_export.h" namespace content { class WebContents; +struct TransitionLayerData; // Android wrapper around WebContents that provides safer passage from java and // back to native and provides java with a means of communicating with its @@ -41,9 +43,30 @@ class CONTENT_EXPORT WebContentsAndroid jobject obj) const; void Stop(JNIEnv* env, jobject obj); jint GetBackgroundColor(JNIEnv* env, jobject obj); + base::android::ScopedJavaLocalRef GetURL(JNIEnv* env, jobject) const; + jboolean IsIncognito(JNIEnv* env, jobject obj); + + void ResumeResponseDeferredAtStart(JNIEnv* env, jobject obj); + void SetHasPendingNavigationTransitionForTesting(JNIEnv* env, jobject obj); + void SetupTransitionView(JNIEnv* env, jobject jobj, jstring markup); + void BeginExitTransition(JNIEnv* env, jobject jobj, jstring css_selector); + + // This method is invoked when the request is deferred immediately after + // receiving response headers. + void DidDeferAfterResponseStarted(const TransitionLayerData& transition_data); + + // This method is invoked when a navigation transition is detected, to + // determine if the embedder intends to handle it. + bool WillHandleDeferAfterResponseStarted(); + + // This method is invoked when a navigation transition has started. + void DidStartNavigationTransitionForFrame(int64 frame_id); + void OnHide(JNIEnv* env, jobject obj); void OnShow(JNIEnv* env, jobject obj); + void ReleaseMediaPlayers(JNIEnv* env, jobject jobj); void PauseVideo(); + void AddStyleSheetByURL( JNIEnv* env, jobject obj, jstring url); void ShowInterstitialPage( @@ -62,6 +85,11 @@ class CONTENT_EXPORT WebContentsAndroid void SelectWordAroundCaret(JNIEnv* env, jobject obj); void InsertCSS(JNIEnv* env, jobject jobj, jstring jcss); + void EvaluateJavaScript(JNIEnv* env, + jobject obj, + jstring script, + jobject callback, + jboolean start_renderer); private: RenderWidgetHostViewAndroid* GetRenderWidgetHostViewAndroid(); diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index b40774f564202..396b6ac04cbb4 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -532,17 +532,10 @@ bool WebContentsImpl::OnMessageReceived(RenderViewHost* render_view_host, // Message handlers should be aware of which // RenderViewHost/RenderFrameHost sent the message, which is temporarily // stored in render_(view|frame)_message_source_. - if (render_frame_host) { - if (RenderViewDevToolsAgentHost::DispatchIPCMessage( - render_frame_host->GetRenderViewHost(), message)) - return true; + if (render_frame_host) render_frame_message_source_ = render_frame_host; - } else { - if (RenderViewDevToolsAgentHost::DispatchIPCMessage( - render_view_host, message)) - return true; + else render_view_message_source_ = render_view_host; - } bool handled = true; IPC_BEGIN_MESSAGE_MAP(WebContentsImpl, message) @@ -648,6 +641,10 @@ const GURL& WebContentsImpl::GetLastCommittedURL() const { return entry ? entry->GetVirtualURL() : GURL::EmptyGURL(); } +ScreenOrientationDispatcherHost* WebContentsImpl::GetScreenOrientationDispatcherHost() { + return screen_orientation_dispatcher_host_.get(); +} + WebContentsDelegate* WebContentsImpl::GetDelegate() { return delegate_; } @@ -1016,7 +1013,8 @@ bool WebContentsImpl::IsBeingDestroyed() const { return is_being_destroyed_; } -void WebContentsImpl::NotifyNavigationStateChanged(unsigned changed_flags) { +void WebContentsImpl::NotifyNavigationStateChanged( + InvalidateTypes changed_flags) { if (delegate_) delegate_->NavigationStateChanged(this, changed_flags); } @@ -2435,8 +2433,8 @@ void WebContentsImpl::DidStartNavigationTransition( RenderFrameHostImpl* render_frame_host) { #if defined(OS_ANDROID) int render_frame_id = render_frame_host->GetRoutingID(); - ContentViewCoreImpl::FromWebContents(this)-> - DidStartNavigationTransitionForFrame(render_frame_id); + GetWebContentsAndroid()->DidStartNavigationTransitionForFrame( + render_frame_id); #endif } @@ -3012,7 +3010,6 @@ void WebContentsImpl::OnUpdateFaviconURL( void WebContentsImpl::OnMediaPlayingNotification(int64 player_cookie, bool has_video, bool has_audio) { -// Chrome OS does its own detection of audio and video. #if !defined(OS_CHROMEOS) scoped_ptr blocker; if (has_video) { @@ -3028,17 +3025,24 @@ void WebContentsImpl::OnMediaPlayingNotification(int64 player_cookie, } if (blocker) { - power_save_blockers_[render_frame_message_source_][player_cookie] = - blocker.release(); + uintptr_t key = reinterpret_cast(render_frame_message_source_); + if (!power_save_blockers_.contains(key)) { + power_save_blockers_.add(key, + make_scoped_ptr(new PowerSaveBlockerMapEntry)); + } + PowerSaveBlockerMapEntry* map_entry = + power_save_blockers_.get(key); + map_entry->set(player_cookie, blocker.Pass()); } #endif // !defined(OS_CHROMEOS) } void WebContentsImpl::OnMediaPausedNotification(int64 player_cookie) { - // Chrome OS does its own detection of audio and video. #if !defined(OS_CHROMEOS) - delete power_save_blockers_[render_frame_message_source_][player_cookie]; - power_save_blockers_[render_frame_message_source_].erase(player_cookie); + uintptr_t key = reinterpret_cast(render_frame_message_source_); + PowerSaveBlockerMapEntry* map_entry = power_save_blockers_.get(key); + if (map_entry) + map_entry->erase(player_cookie); #endif // !defined(OS_CHROMEOS) } @@ -3602,18 +3606,15 @@ void WebContentsImpl::SwappedOut(RenderFrameHost* rfh) { } void WebContentsImpl::DidDeferAfterResponseStarted( - const scoped_refptr& headers, - const GURL& url) { + const TransitionLayerData& transition_data) { #if defined(OS_ANDROID) - ContentViewCoreImpl::FromWebContents(this)->DidDeferAfterResponseStarted( - headers, url); + GetWebContentsAndroid()->DidDeferAfterResponseStarted(transition_data); #endif } bool WebContentsImpl::WillHandleDeferAfterResponseStarted() { #if defined(OS_ANDROID) - return ContentViewCoreImpl::FromWebContents(this)-> - WillHandleDeferAfterResponseStarted(); + return GetWebContentsAndroid()->WillHandleDeferAfterResponseStarted(); #else return false; #endif @@ -3825,7 +3826,7 @@ bool WebContentsImpl::AddMessageToConsole(int32 level, source_id); } -WebPreferences WebContentsImpl::GetWebkitPrefs() { +WebPreferences WebContentsImpl::ComputeWebkitPrefs() { // We want to base the page config off of the actual URL, rather than the // virtual URL. // TODO(nasko): Investigate how to remove the GetActiveEntry usage here, @@ -3833,7 +3834,7 @@ WebPreferences WebContentsImpl::GetWebkitPrefs() { GURL url = controller_.GetActiveEntry() ? controller_.GetActiveEntry()->GetURL() : GURL::EmptyGURL(); - return GetRenderManager()->current_host()->GetWebkitPrefs(url); + return GetRenderManager()->current_host()->ComputeWebkitPrefs(url); } int WebContentsImpl::CreateSwappedOutRenderView( @@ -3920,7 +3921,8 @@ void WebContentsImpl::LoadStateChanged( if (load_state_.state == net::LOAD_STATE_READING_RESPONSE) SetNotWaitingForResponse(); if (IsLoading()) { - NotifyNavigationStateChanged(INVALIDATE_TYPE_LOAD | INVALIDATE_TYPE_TAB); + NotifyNavigationStateChanged(static_cast( + INVALIDATE_TYPE_LOAD | INVALIDATE_TYPE_TAB)); } } @@ -4098,14 +4100,17 @@ bool WebContentsImpl::CreateRenderFrameForRenderManager( base::android::ScopedJavaLocalRef WebContentsImpl::GetJavaWebContents() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + return GetWebContentsAndroid()->GetJavaObject(); +} +WebContentsAndroid* WebContentsImpl::GetWebContentsAndroid() { WebContentsAndroid* web_contents_android = static_cast(GetUserData(kWebContentsAndroidKey)); if (!web_contents_android) { web_contents_android = new WebContentsAndroid(this); SetUserData(kWebContentsAndroidKey, web_contents_android); } - return web_contents_android->GetJavaObject(); + return web_contents_android; } bool WebContentsImpl::CreateRenderViewForInitialEmptyDocument() { @@ -4209,15 +4214,19 @@ BrowserPluginEmbedder* WebContentsImpl::GetBrowserPluginEmbedder() const { void WebContentsImpl::ClearPowerSaveBlockers( RenderFrameHost* render_frame_host) { - STLDeleteValues(&power_save_blockers_[render_frame_host]); - power_save_blockers_.erase(render_frame_host); +#if !defined(OS_CHROMEOS) + uintptr_t key = reinterpret_cast(render_frame_host); + scoped_ptr map_entry = + power_save_blockers_.take_and_erase(key); + if (map_entry) + map_entry->clear(); +#endif } void WebContentsImpl::ClearAllPowerSaveBlockers() { - for (PowerSaveBlockerMap::iterator i(power_save_blockers_.begin()); - i != power_save_blockers_.end(); ++i) - STLDeleteValues(&power_save_blockers_[i->first]); +#if !defined(OS_CHROMEOS) power_save_blockers_.clear(); +#endif } gfx::Size WebContentsImpl::GetSizeForNewRenderView() { diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index cd5b970236d4c..c8252af69971b 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -10,6 +10,7 @@ #include #include "base/compiler_specific.h" +#include "base/containers/scoped_ptr_hash_map.h" #include "base/gtest_prod_util.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" @@ -74,6 +75,10 @@ struct LoadNotificationDetails; struct ResourceRedirectDetails; struct ResourceRequestDetails; +#if defined(OS_ANDROID) +class WebContentsAndroid; +#endif + // Factory function for the implementations that content knows about. Takes // ownership of |delegate|. WebContentsView* CreateWebContentsView( @@ -189,6 +194,7 @@ class CONTENT_EXPORT WebContentsImpl void RemoveAccessibilityMode(AccessibilityMode mode); // WebContents ------------------------------------------------------ + virtual ScreenOrientationDispatcherHost* GetScreenOrientationDispatcherHost() OVERRIDE; virtual WebContentsDelegate* GetDelegate() OVERRIDE; virtual void SetDelegate(WebContentsDelegate* delegate) OVERRIDE; virtual NavigationControllerImpl& GetController() OVERRIDE; @@ -244,7 +250,8 @@ class CONTENT_EXPORT WebContentsImpl int error_code) OVERRIDE; virtual base::TerminationStatus GetCrashedStatus() const OVERRIDE; virtual bool IsBeingDestroyed() const OVERRIDE; - virtual void NotifyNavigationStateChanged(unsigned changed_flags) OVERRIDE; + virtual void NotifyNavigationStateChanged( + InvalidateTypes changed_flags) OVERRIDE; virtual base::TimeTicks GetLastActiveTime() const OVERRIDE; virtual void WasShown() OVERRIDE; virtual void WasHidden() OVERRIDE; @@ -326,6 +333,7 @@ class CONTENT_EXPORT WebContentsImpl #if defined(OS_ANDROID) virtual base::android::ScopedJavaLocalRef GetJavaWebContents() OVERRIDE; + virtual WebContentsAndroid* GetWebContentsAndroid(); #elif defined(OS_MACOSX) virtual void SetAllowOverlappingViews(bool overlapping) OVERRIDE; virtual bool GetAllowOverlappingViews() OVERRIDE; @@ -349,8 +357,7 @@ class CONTENT_EXPORT WebContentsImpl bool to_different_document) OVERRIDE; virtual void SwappedOut(RenderFrameHost* render_frame_host) OVERRIDE; virtual void DidDeferAfterResponseStarted( - const scoped_refptr& headers, - const GURL& url) OVERRIDE; + const TransitionLayerData& transition_data) OVERRIDE; virtual bool WillHandleDeferAfterResponseStarted() OVERRIDE; virtual void WorkerCrashed(RenderFrameHost* render_frame_host) OVERRIDE; virtual void ShowContextMenu(RenderFrameHost* render_frame_host, @@ -417,7 +424,7 @@ class CONTENT_EXPORT WebContentsImpl const base::string16& source_id) OVERRIDE; virtual RendererPreferences GetRendererPrefs( BrowserContext* browser_context) const OVERRIDE; - virtual WebPreferences GetWebkitPrefs() OVERRIDE; + virtual WebPreferences ComputeWebkitPrefs() OVERRIDE; virtual void OnUserGesture() OVERRIDE; virtual void OnIgnoredUIEvent() OVERRIDE; virtual void RendererUnresponsive(RenderViewHost* render_view_host, @@ -899,7 +906,7 @@ class CONTENT_EXPORT WebContentsImpl // Clear |render_frame_host|'s PowerSaveBlockers. void ClearPowerSaveBlockers(RenderFrameHost* render_frame_host); - // Clear all PowerSaveBlockers, leave power_save_blocker_ empty. + // Clear all PowerSaveBlockers, leave |power_save_blocker_| empty. void ClearAllPowerSaveBlockers(); // Helper function to invoke WebContentsDelegate::GetSizeForNewRenderView(). @@ -966,12 +973,18 @@ class CONTENT_EXPORT WebContentsImpl // Helper classes ------------------------------------------------------------ +#if !defined(OS_CHROMEOS) // Maps the RenderFrameHost to its media_player_cookie and PowerSaveBlocker // pairs. Key is the RenderFrameHost, value is the map which maps // player_cookie on to PowerSaveBlocker. - typedef std::map > + // + // ChromeOS does its own detection of audio and video. + typedef base::ScopedPtrHashMap + PowerSaveBlockerMapEntry; + typedef base::ScopedPtrHashMap PowerSaveBlockerMap; PowerSaveBlockerMap power_save_blockers_; +#endif // Manages the frame tree of the page and process swaps in each node. FrameTree frame_tree_; diff --git a/content/browser/web_contents/web_contents_impl_unittest.cc b/content/browser/web_contents/web_contents_impl_unittest.cc index fdee01a2bd851..0079a71cc2ed5 100644 --- a/content/browser/web_contents/web_contents_impl_unittest.cc +++ b/content/browser/web_contents/web_contents_impl_unittest.cc @@ -30,6 +30,7 @@ #include "content/public/test/test_utils.h" #include "content/test/test_content_browser_client.h" #include "content/test/test_content_client.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_render_view_host.h" #include "content/test/test_web_contents.h" #include "testing/gtest/include/gtest/gtest.h" @@ -345,9 +346,9 @@ TEST_F(WebContentsImplTest, UpdateTitle) { ¶ms, 0, GURL(url::kAboutBlankURL), PAGE_TRANSITION_TYPED); LoadCommittedDetails details; - cont.RendererDidNavigate(main_test_rfh(), params, &details); + cont.RendererDidNavigate(contents()->GetMainFrame(), params, &details); - contents()->UpdateTitle(main_test_rfh(), 0, + contents()->UpdateTitle(contents()->GetMainFrame(), 0, base::ASCIIToUTF16(" Lots O' Whitespace\n"), base::i18n::LEFT_TO_RIGHT); EXPECT_EQ(base::ASCIIToUTF16("Lots O' Whitespace"), contents()->GetTitle()); @@ -392,7 +393,7 @@ TEST_F(WebContentsImplTest, NTPViewSource) { FrameHostMsg_DidCommitProvisionalLoad_Params params; InitNavigateParams(¶ms, 0, kGURL, PAGE_TRANSITION_TYPED); LoadCommittedDetails details; - cont.RendererDidNavigate(main_test_rfh(), params, &details); + cont.RendererDidNavigate(contents()->GetMainFrame(), params, &details); // Also check title and url. EXPECT_EQ(base::ASCIIToUTF16(kUrl), contents()->GetTitle()); } @@ -422,16 +423,16 @@ TEST_F(WebContentsImplTest, UpdateMaxPageID) { // Test simple same-SiteInstance navigation. TEST_F(WebContentsImplTest, SimpleNavigation) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); // Navigate to URL const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(instance1, orig_rvh->GetSiteInstance()); + EXPECT_EQ(instance1, orig_rfh->GetSiteInstance()); // Controller's pending entry will have a NULL site instance until we assign // it in DidNavigate. EXPECT_TRUE( @@ -439,10 +440,10 @@ TEST_F(WebContentsImplTest, SimpleNavigation) { site_instance() == NULL); // DidNavigate from the page - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); - EXPECT_EQ(instance1, orig_rvh->GetSiteInstance()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); + EXPECT_EQ(instance1, orig_rfh->GetSiteInstance()); // Controller's entry should now have the SiteInstance, or else we won't be // able to find it later. EXPECT_EQ( @@ -465,66 +466,60 @@ TEST_F(WebContentsImplTest, NavigateToExcessivelyLongURL) { // Test that navigating across a site boundary creates a new RenderViewHost // with a new SiteInstance. Going back should do the same. TEST_F(WebContentsImplTest, CrossSiteBoundaries) { - TestRenderViewHost* orig_rvh = test_rvh(); - RenderFrameHostImpl* orig_rfh = - contents()->GetFrameTree()->root()->current_frame_host(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); int orig_rvh_delete_count = 0; - orig_rvh->set_delete_counter(&orig_rvh_delete_count); + orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count); SiteInstance* instance1 = contents()->GetSiteInstance(); // Navigate to URL. First URL should use first RenderViewHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); - // Keep the number of active views in orig_rvh's SiteInstance - // non-zero so that orig_rvh doesn't get deleted when it gets - // swapped out. - static_cast(orig_rvh->GetSiteInstance())-> + // Keep the number of active views in orig_rfh's SiteInstance non-zero so that + // orig_rfh doesn't get deleted when it gets swapped out. + static_cast(orig_rfh->GetSiteInstance())-> increment_active_view_count(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost()); EXPECT_EQ(url, contents()->GetLastCommittedURL()); EXPECT_EQ(url, contents()->GetVisibleURL()); // Navigate to new site const GURL url2("http://www.yahoo.com"); - controller().LoadURL( - url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); + controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(url, contents()->GetLastCommittedURL()); EXPECT_EQ(url2, contents()->GetVisibleURL()); - TestRenderViewHost* pending_rvh = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); int pending_rvh_delete_count = 0; - pending_rvh->set_delete_counter(&pending_rvh_delete_count); - RenderFrameHostImpl* pending_rfh = contents()->GetFrameTree()->root()-> - render_manager()->pending_frame_host(); + pending_rfh->GetRenderViewHost()->set_delete_counter( + &pending_rvh_delete_count); - // Navigations should be suspended in pending_rvh until BeforeUnloadACK. - EXPECT_TRUE(pending_rvh->are_navigations_suspended()); - orig_rvh->SendBeforeUnloadACK(true); - EXPECT_FALSE(pending_rvh->are_navigations_suspended()); + // Navigations should be suspended in pending_rfh until BeforeUnloadACK. + EXPECT_TRUE(pending_rfh->are_navigations_suspended()); + orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); + EXPECT_FALSE(pending_rfh->are_navigations_suspended()); // DidNavigate from the pending page contents()->TestDidNavigate( - pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); + pending_rfh, 1, url2, PAGE_TRANSITION_TYPED); SiteInstance* instance2 = contents()->GetSiteInstance(); - // Keep the number of active views in pending_rvh's SiteInstance - // non-zero so that orig_rvh doesn't get deleted when it gets + // Keep the number of active views in pending_rfh's SiteInstance + // non-zero so that orig_rfh doesn't get deleted when it gets // swapped out. - static_cast(pending_rvh->GetSiteInstance())-> + static_cast(pending_rfh->GetSiteInstance())-> increment_active_view_count(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); EXPECT_EQ(url2, contents()->GetLastCommittedURL()); EXPECT_EQ(url2, contents()->GetVisibleURL()); EXPECT_NE(instance1, instance2); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); // We keep the original RFH around, swapped out. EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList( orig_rfh)); @@ -532,29 +527,27 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) { // Going back should switch SiteInstances again. The first SiteInstance is // stored in the NavigationEntry, so it should be the same as at the start. - // We should use the same RVH as before, swapping it back in. + // We should use the same RFH as before, swapping it back in. controller().GoBack(); - TestRenderViewHost* goback_rvh = - static_cast(contents()->GetPendingRenderViewHost()); - EXPECT_EQ(orig_rvh, goback_rvh); + TestRenderFrameHost* goback_rfh = contents()->GetPendingMainFrame(); + EXPECT_EQ(orig_rfh, goback_rfh); EXPECT_TRUE(contents()->cross_navigation_pending()); - // Navigations should be suspended in goback_rvh until BeforeUnloadACK. - EXPECT_TRUE(goback_rvh->are_navigations_suspended()); - pending_rvh->SendBeforeUnloadACK(true); - EXPECT_FALSE(goback_rvh->are_navigations_suspended()); + // Navigations should be suspended in goback_rfh until BeforeUnloadACK. + EXPECT_TRUE(goback_rfh->are_navigations_suspended()); + pending_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); + EXPECT_FALSE(goback_rfh->are_navigations_suspended()); // DidNavigate from the back action - contents()->TestDidNavigate( - goback_rvh, 1, url2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(goback_rfh, 1, url2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(goback_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(goback_rfh, contents()->GetMainFrame()); EXPECT_EQ(instance1, contents()->GetSiteInstance()); // The pending RFH should now be swapped out, not deleted. EXPECT_TRUE(contents()->GetRenderManagerForTesting()-> IsOnSwappedOutList(pending_rfh)); EXPECT_EQ(pending_rvh_delete_count, 0); - pending_rvh->OnSwappedOut(false); + pending_rfh->OnSwappedOut(false); // Close contents and ensure RVHs are deleted. DeleteContents(); @@ -563,43 +556,44 @@ TEST_F(WebContentsImplTest, CrossSiteBoundaries) { } // Test that navigating across a site boundary after a crash creates a new -// RVH without requiring a cross-site transition (i.e., PENDING state). +// RFH without requiring a cross-site transition (i.e., PENDING state). TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); + int orig_rvh_delete_count = 0; - orig_rvh->set_delete_counter(&orig_rvh_delete_count); + orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count); SiteInstance* instance1 = contents()->GetSiteInstance(); // Navigate to URL. First URL should use first RenderViewHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh->GetRenderViewHost(), contents()->GetRenderViewHost()); // Crash the renderer. - orig_rvh->set_render_view_created(false); + orig_rfh->GetRenderViewHost()->set_render_view_created(false); // Navigate to new site. We should not go into PENDING. const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - RenderViewHost* new_rvh = rvh(); + TestRenderFrameHost* new_rfh = contents()->GetMainFrame(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); - EXPECT_NE(orig_rvh, new_rvh); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); + EXPECT_NE(orig_rfh, new_rfh); EXPECT_EQ(orig_rvh_delete_count, 1); // DidNavigate from the new page - contents()->TestDidNavigate(new_rvh, 1, url2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(new_rfh, 1, url2, PAGE_TRANSITION_TYPED); SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(new_rvh, rvh()); + EXPECT_EQ(new_rfh, main_rfh()); EXPECT_NE(instance1, instance2); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); // Close contents and ensure RVHs are deleted. DeleteContents(); @@ -610,56 +604,52 @@ TEST_F(WebContentsImplTest, CrossSiteBoundariesAfterCrash) { // both contentses to a new site will place both contentses in a single // SiteInstance. TEST_F(WebContentsImplTest, NavigateTwoTabsCrossSite) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); // Navigate to URL. First URL should use first RenderViewHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); // Open a new contents with the same SiteInstance, navigated to the same site. scoped_ptr contents2( TestWebContents::Create(browser_context(), instance1)); contents2->GetController().LoadURL(url, Referrer(), - PAGE_TRANSITION_TYPED, - std::string()); + PAGE_TRANSITION_TYPED, + std::string()); // Need this page id to be 2 since the site instance is the same (which is the // scope of page IDs) and we want to consider this a new page. contents2->TestDidNavigate( - contents2->GetRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED); + contents2->GetMainFrame(), 2, url, PAGE_TRANSITION_TYPED); // Navigate first contents to a new site. const GURL url2a("http://www.yahoo.com"); controller().LoadURL( url2a, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - orig_rvh->SendBeforeUnloadACK(true); - TestRenderViewHost* pending_rvh_a = - static_cast(contents()->GetPendingRenderViewHost()); + orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); + TestRenderFrameHost* pending_rfh_a = contents()->GetPendingMainFrame(); contents()->TestDidNavigate( - pending_rvh_a, 1, url2a, PAGE_TRANSITION_TYPED); + pending_rfh_a, 1, url2a, PAGE_TRANSITION_TYPED); SiteInstance* instance2a = contents()->GetSiteInstance(); EXPECT_NE(instance1, instance2a); // Navigate second contents to the same site as the first tab. const GURL url2b("http://mail.yahoo.com"); contents2->GetController().LoadURL(url2b, Referrer(), - PAGE_TRANSITION_TYPED, - std::string()); - TestRenderViewHost* rvh2 = - static_cast(contents2->GetRenderViewHost()); - rvh2->SendBeforeUnloadACK(true); - TestRenderViewHost* pending_rvh_b = - static_cast(contents2->GetPendingRenderViewHost()); - EXPECT_TRUE(pending_rvh_b != NULL); + PAGE_TRANSITION_TYPED, + std::string()); + TestRenderFrameHost* rfh2 = contents2->GetMainFrame(); + rfh2->GetRenderViewHost()->SendBeforeUnloadACK(true); + TestRenderFrameHost* pending_rfh_b = contents2->GetPendingMainFrame(); + EXPECT_TRUE(pending_rfh_b != NULL); EXPECT_TRUE(contents2->cross_navigation_pending()); // NOTE(creis): We used to be in danger of showing a crash page here if the // second contents hadn't navigated somewhere first (bug 1145430). That case // is now covered by the CrossSiteBoundariesAfterCrash test. - contents2->TestDidNavigate( - pending_rvh_b, 2, url2b, PAGE_TRANSITION_TYPED); + contents2->TestDidNavigate(pending_rfh_b, 2, url2b, PAGE_TRANSITION_TYPED); SiteInstance* instance2b = contents2->GetSiteInstance(); EXPECT_NE(instance1, instance2b); @@ -675,11 +665,9 @@ TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) { WebContentsImplTestBrowserClient browser_client; SetBrowserClientForTesting(&browser_client); - TestRenderViewHost* orig_rvh = test_rvh(); - RenderFrameHostImpl* orig_rfh = - contents()->GetFrameTree()->root()->current_frame_host(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); int orig_rvh_delete_count = 0; - orig_rvh->set_delete_counter(&orig_rvh_delete_count); + orig_rfh->GetRenderViewHost()->set_delete_counter(&orig_rvh_delete_count); SiteInstanceImpl* orig_instance = static_cast(contents()->GetSiteInstance()); @@ -688,10 +676,10 @@ TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) { const GURL native_url("non-site-url://stuffandthings"); controller().LoadURL( native_url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, native_url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, native_url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); EXPECT_EQ(native_url, contents()->GetVisibleURL()); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); @@ -706,13 +694,13 @@ TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) { EXPECT_FALSE(contents()->cross_navigation_pending()); EXPECT_EQ(native_url, contents()->GetLastCommittedURL()); EXPECT_EQ(url, contents()->GetVisibleURL()); - EXPECT_FALSE(contents()->GetPendingRenderViewHost()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + EXPECT_FALSE(contents()->GetPendingMainFrame()); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); - // Keep the number of active views in orig_rvh's SiteInstance - // non-zero so that orig_rvh doesn't get deleted when it gets + // Keep the number of active views in orig_rfh's SiteInstance + // non-zero so that orig_rfh doesn't get deleted when it gets // swapped out. - static_cast(orig_rvh->GetSiteInstance())-> + static_cast(orig_rfh->GetSiteInstance())-> increment_active_view_count(); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); @@ -727,32 +715,32 @@ TEST_F(WebContentsImplTest, NavigateFromSitelessUrl) { EXPECT_TRUE(contents()->cross_navigation_pending()); EXPECT_EQ(url, contents()->GetLastCommittedURL()); EXPECT_EQ(url2, contents()->GetVisibleURL()); - TestRenderViewHost* pending_rvh = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); int pending_rvh_delete_count = 0; - pending_rvh->set_delete_counter(&pending_rvh_delete_count); + pending_rfh->GetRenderViewHost()->set_delete_counter( + &pending_rvh_delete_count); // Navigations should be suspended in pending_rvh until BeforeUnloadACK. - EXPECT_TRUE(pending_rvh->are_navigations_suspended()); - orig_rvh->SendBeforeUnloadACK(true); - EXPECT_FALSE(pending_rvh->are_navigations_suspended()); + EXPECT_TRUE(pending_rfh->are_navigations_suspended()); + orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); + EXPECT_FALSE(pending_rfh->are_navigations_suspended()); // DidNavigate from the pending page. contents()->TestDidNavigate( - pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); + pending_rfh, 1, url2, PAGE_TRANSITION_TYPED); SiteInstance* new_instance = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); EXPECT_EQ(url2, contents()->GetLastCommittedURL()); EXPECT_EQ(url2, contents()->GetVisibleURL()); EXPECT_NE(new_instance, orig_instance); - EXPECT_FALSE(contents()->GetPendingRenderViewHost()); + EXPECT_FALSE(contents()->GetPendingMainFrame()); // We keep the original RFH around, swapped out. EXPECT_TRUE(contents()->GetRenderManagerForTesting()->IsOnSwappedOutList( orig_rfh)); EXPECT_EQ(orig_rvh_delete_count, 0); - orig_rvh->OnSwappedOut(false); + orig_rfh->OnSwappedOut(false); // Close contents and ensure RVHs are deleted. DeleteContents(); @@ -768,7 +756,7 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) { SetBrowserClientForTesting(&browser_client); SiteInstanceImpl* orig_instance = static_cast(contents()->GetSiteInstance()); - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); // Restore a navigation entry for URL that should not assign site to the // SiteInstance. @@ -787,7 +775,7 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) { ASSERT_EQ(0u, entries.size()); ASSERT_EQ(1, controller().GetEntryCount()); controller().GoToIndex(0); - contents()->TestDidNavigate(orig_rvh, 0, native_url, PAGE_TRANSITION_RELOAD); + contents()->TestDidNavigate(orig_rfh, 0, native_url, PAGE_TRANSITION_RELOAD); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); EXPECT_EQ(GURL(), contents()->GetSiteInstance()->GetSiteURL()); EXPECT_FALSE(orig_instance->HasSite()); @@ -797,7 +785,7 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredSitelessUrl) { const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 2, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 2, url, PAGE_TRANSITION_TYPED); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); // Cleanup. @@ -811,7 +799,7 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) { SetBrowserClientForTesting(&browser_client); SiteInstanceImpl* orig_instance = static_cast(contents()->GetSiteInstance()); - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); // Restore a navigation entry for a regular URL ensuring that the embedder // ShouldAssignSiteForUrl override is disabled (i.e. returns true). @@ -830,7 +818,7 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) { ASSERT_EQ(0u, entries.size()); ASSERT_EQ(1, controller().GetEntryCount()); controller().GoToIndex(0); - contents()->TestDidNavigate(orig_rvh, 0, regular_url, PAGE_TRANSITION_RELOAD); + contents()->TestDidNavigate(orig_rfh, 0, regular_url, PAGE_TRANSITION_RELOAD); EXPECT_EQ(orig_instance, contents()->GetSiteInstance()); EXPECT_TRUE(orig_instance->HasSite()); @@ -839,7 +827,7 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) { controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); contents()->TestDidNavigate( - contents()->GetPendingRenderViewHost(), 2, url, PAGE_TRANSITION_TYPED); + contents()->GetPendingMainFrame(), 2, url, PAGE_TRANSITION_TYPED); EXPECT_NE(orig_instance, contents()->GetSiteInstance()); // Cleanup. @@ -849,36 +837,36 @@ TEST_F(WebContentsImplTest, NavigateFromRestoredRegularUrl) { // Test that we can find an opener RVH even if it's pending. // http://crbug.com/176252. TEST_F(WebContentsImplTest, FindOpenerRVHWhenPending) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); // Navigate to a URL. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); // Start to navigate first tab to a new site, so that it has a pending RVH. const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - orig_rvh->SendBeforeUnloadACK(true); - TestRenderViewHost* pending_rvh = - static_cast(contents()->GetPendingRenderViewHost()); + orig_rfh->GetRenderViewHost()->SendBeforeUnloadACK(true); + TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); // While it is still pending, simulate opening a new tab with the first tab // as its opener. This will call WebContentsImpl::CreateOpenerRenderViews // on the opener to ensure that an RVH exists. - int opener_routing_id = contents()->CreateOpenerRenderViews( - pending_rvh->GetSiteInstance()); + int opener_routing_id = + contents()->CreateOpenerRenderViews(pending_rfh->GetSiteInstance()); // We should find the pending RVH and not create a new one. - EXPECT_EQ(pending_rvh->GetRoutingID(), opener_routing_id); + EXPECT_EQ(pending_rfh->GetRenderViewHost()->GetRoutingID(), + opener_routing_id); } // Tests that WebContentsImpl uses the current URL, not the SiteInstance's site, // to determine whether a navigation is cross-site. TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { - RenderViewHost* orig_rvh = rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); // Navigate to URL. @@ -886,21 +874,20 @@ TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); contents()->TestDidNavigate( - orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + orig_rfh, 1, url, PAGE_TRANSITION_TYPED); // Open a related contents to a second site. scoped_ptr contents2( TestWebContents::Create(browser_context(), instance1)); const GURL url2("http://www.yahoo.com"); contents2->GetController().LoadURL(url2, Referrer(), - PAGE_TRANSITION_TYPED, - std::string()); + PAGE_TRANSITION_TYPED, + std::string()); // The first RVH in contents2 isn't live yet, so we shortcut the cross site // pending. - TestRenderViewHost* rvh2 = static_cast( - contents2->GetRenderViewHost()); + TestRenderFrameHost* rfh2 = contents2->GetMainFrame(); EXPECT_FALSE(contents2->cross_navigation_pending()); - contents2->TestDidNavigate(rvh2, 2, url2, PAGE_TRANSITION_TYPED); + contents2->TestDidNavigate(rfh2, 2, url2, PAGE_TRANSITION_TYPED); SiteInstance* instance2 = contents2->GetSiteInstance(); EXPECT_NE(instance1, instance2); EXPECT_FALSE(contents2->cross_navigation_pending()); @@ -908,7 +895,7 @@ TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { // Simulate a link click in first contents to second site. Doesn't switch // SiteInstances, because we don't intercept WebKit navigations. contents()->TestDidNavigate( - orig_rvh, 2, url2, PAGE_TRANSITION_TYPED); + orig_rfh, 2, url2, PAGE_TRANSITION_TYPED); SiteInstance* instance3 = contents()->GetSiteInstance(); EXPECT_EQ(instance1, instance3); EXPECT_FALSE(contents()->cross_navigation_pending()); @@ -920,7 +907,7 @@ TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_FALSE(contents()->cross_navigation_pending()); contents()->TestDidNavigate( - orig_rvh, 3, url3, PAGE_TRANSITION_TYPED); + orig_rfh, 3, url3, PAGE_TRANSITION_TYPED); SiteInstance* instance4 = contents()->GetSiteInstance(); EXPECT_EQ(instance1, instance4); } @@ -928,88 +915,89 @@ TEST_F(WebContentsImplTest, CrossSiteComparesAgainstCurrentPage) { // Test that the onbeforeunload and onunload handlers run when navigating // across site boundaries. TEST_F(WebContentsImplTest, CrossSiteUnloadHandlers) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); // Navigate to URL. First URL should use first RenderViewHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Navigate to new site, but simulate an onbeforeunload denial. const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); base::TimeTicks now = base::TimeTicks::Now(); - orig_rvh->GetMainFrame()->OnMessageReceived( + orig_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, false, now, now)); - EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_FALSE( + orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Navigate again, but simulate an onbeforeunload approval. controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); now = base::TimeTicks::Now(); - orig_rvh->GetMainFrame()->OnMessageReceived( + orig_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); - EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_FALSE( + orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* pending_rvh = static_cast( - contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); // We won't hear DidNavigate until the onunload handler has finished running. // DidNavigate from the pending page. contents()->TestDidNavigate( - pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); + pending_rfh, 1, url2, PAGE_TRANSITION_TYPED); SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(pending_rvh, rvh()); + EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); EXPECT_NE(instance1, instance2); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); } // Test that during a slow cross-site navigation, the original renderer can // navigate to a different URL and have it displayed, canceling the slow // navigation. TEST_F(WebContentsImplTest, CrossSiteNavigationPreempted) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); - // Navigate to URL. First URL should use first RenderViewHost. + // Navigate to URL. First URL should use first RenderFrameHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Navigate to new site, simulating an onbeforeunload approval. const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); base::TimeTicks now = base::TimeTicks::Now(); - orig_rvh->GetMainFrame()->OnMessageReceived( - FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); + orig_rfh->OnMessageReceived(FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); // Suppose the original renderer navigates before the new one is ready. - orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo")); + orig_rfh->SendNavigate(2, GURL("http://www.google.com/foo")); // Verify that the pending navigation is cancelled. - EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_FALSE( + orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, rvh()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); EXPECT_EQ(instance1, instance2); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); } TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { @@ -1017,46 +1005,47 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { const GURL url1("chrome://blah"); controller().LoadURL( url1, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - TestRenderViewHost* ntp_rvh = test_rvh(); - contents()->TestDidNavigate(ntp_rvh, 1, url1, PAGE_TRANSITION_TYPED); + TestRenderFrameHost* ntp_rfh = contents()->GetMainFrame(); + contents()->TestDidNavigate(ntp_rfh, 1, url1, PAGE_TRANSITION_TYPED); NavigationEntry* entry1 = controller().GetLastCommittedEntry(); SiteInstance* instance1 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(ntp_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(ntp_rfh, contents()->GetMainFrame()); EXPECT_EQ(url1, entry1->GetURL()); EXPECT_EQ(instance1, NavigationEntryImpl::FromNavigationEntry(entry1)->site_instance()); - EXPECT_TRUE(ntp_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); + EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->GetEnabledBindings() & + BINDINGS_POLICY_WEB_UI); // Navigate to new site. const GURL url2("http://www.google.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* google_rvh = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* google_rfh = contents()->GetPendingMainFrame(); // Simulate beforeunload approval. - EXPECT_TRUE(ntp_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(ntp_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); base::TimeTicks now = base::TimeTicks::Now(); - ntp_rvh->GetMainFrame()->OnMessageReceived( + ntp_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); // DidNavigate from the pending page. contents()->TestDidNavigate( - google_rvh, 1, url2, PAGE_TRANSITION_TYPED); + google_rfh, 1, url2, PAGE_TRANSITION_TYPED); NavigationEntry* entry2 = controller().GetLastCommittedEntry(); SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(google_rfh, contents()->GetMainFrame()); EXPECT_NE(instance1, instance2); - EXPECT_FALSE(contents()->GetPendingRenderViewHost()); + EXPECT_FALSE(contents()->GetPendingMainFrame()); EXPECT_EQ(url2, entry2->GetURL()); EXPECT_EQ(instance2, NavigationEntryImpl::FromNavigationEntry(entry2)->site_instance()); - EXPECT_FALSE(google_rvh->GetEnabledBindings() & BINDINGS_POLICY_WEB_UI); + EXPECT_FALSE(google_rfh->GetRenderViewHost()->GetEnabledBindings() & + BINDINGS_POLICY_WEB_UI); // Navigate to third page on same site. const GURL url3("http://news.google.com"); @@ -1064,14 +1053,14 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { url3, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_FALSE(contents()->cross_navigation_pending()); contents()->TestDidNavigate( - google_rvh, 2, url3, PAGE_TRANSITION_TYPED); + google_rfh, 2, url3, PAGE_TRANSITION_TYPED); NavigationEntry* entry3 = controller().GetLastCommittedEntry(); SiteInstance* instance3 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(google_rfh, contents()->GetMainFrame()); EXPECT_EQ(instance2, instance3); - EXPECT_FALSE(contents()->GetPendingRenderViewHost()); + EXPECT_FALSE(contents()->GetPendingMainFrame()); EXPECT_EQ(url3, entry3->GetURL()); EXPECT_EQ(instance3, NavigationEntryImpl::FromNavigationEntry(entry3)->site_instance()); @@ -1084,22 +1073,23 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { // Before that commits, go back again. controller().GoBack(); EXPECT_TRUE(contents()->cross_navigation_pending()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost()); + EXPECT_TRUE(contents()->GetPendingMainFrame()); EXPECT_EQ(entry1, controller().GetPendingEntry()); // Simulate beforeunload approval. - EXPECT_TRUE(google_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE( + google_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); now = base::TimeTicks::Now(); - google_rvh->GetMainFrame()->OnMessageReceived( + google_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); - // DidNavigate from the first back. This aborts the second back's pending RVH. - contents()->TestDidNavigate(google_rvh, 1, url2, PAGE_TRANSITION_TYPED); + // DidNavigate from the first back. This aborts the second back's pending RFH. + contents()->TestDidNavigate(google_rfh, 1, url2, PAGE_TRANSITION_TYPED); // We should commit this page and forget about the second back. EXPECT_FALSE(contents()->cross_navigation_pending()); EXPECT_FALSE(controller().GetPendingEntry()); - EXPECT_EQ(google_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(google_rfh, contents()->GetMainFrame()); EXPECT_EQ(url2, controller().GetLastCommittedEntry()->GetURL()); // We should not have corrupted the NTP entry. @@ -1115,15 +1105,15 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationBackPreempted) { // Test that during a slow cross-site navigation, a sub-frame navigation in the // original renderer will not cancel the slow navigation (bug 42029). TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); - // Navigate to URL. First URL should use first RenderViewHost. + // Navigate to URL. First URL should use the original RenderFrameHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Start navigating to new site. const GURL url2("http://www.yahoo.com"); @@ -1132,18 +1122,18 @@ TEST_F(WebContentsImplTest, CrossSiteNavigationNotPreemptedByFrame) { // Simulate a sub-frame navigation arriving and ensure the RVH is still // waiting for a before unload response. - TestRenderFrameHost* child_rfh = static_cast( - orig_rvh->main_render_frame_host()->AppendChild("subframe")); + TestRenderFrameHost* child_rfh = orig_rfh->AppendChild("subframe"); child_rfh->SendNavigateWithTransition( 1, GURL("http://google.com/frame"), PAGE_TRANSITION_AUTO_SUBFRAME); - EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); // Now simulate the onbeforeunload approval and verify the navigation is // not canceled. base::TimeTicks now = base::TimeTicks::Now(); - orig_rvh->GetMainFrame()->OnMessageReceived( + orig_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); - EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_FALSE( + orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); EXPECT_TRUE(contents()->cross_navigation_pending()); } @@ -1156,30 +1146,30 @@ TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) { const GURL url("chrome://blah"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); EXPECT_FALSE(contents()->cross_navigation_pending()); // Navigate to new site, with the beforeunload request in flight. const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - TestRenderViewHost* pending_rvh = - static_cast(contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); EXPECT_TRUE(contents()->cross_navigation_pending()); - EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); // Suppose the first navigation tries to commit now, with a // ViewMsg_Stop in flight. This should not cancel the pending navigation, // but it should act as if the beforeunload ack arrived. - orig_rvh->SendNavigate(1, GURL("chrome://blah")); + orig_rfh->SendNavigate(1, GURL("chrome://blah")); EXPECT_TRUE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); - EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); + EXPECT_FALSE( + orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); // The pending navigation should be able to commit successfully. - contents()->TestDidNavigate(pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(pending_rfh, 1, url2, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(pending_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); } // Test that the original renderer cannot preempt a cross-site navigation once @@ -1187,30 +1177,29 @@ TEST_F(WebContentsImplTest, CrossSiteNotPreemptedDuringBeforeUnload) { // is almost ready to be displayed, and the original renderer is only given a // short chance to run an unload handler. Prevents regression of bug 23942. TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); // Navigate to URL. First URL should use first RenderViewHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Navigate to new site, simulating an onbeforeunload approval. const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); base::TimeTicks now = base::TimeTicks::Now(); - orig_rvh->GetMainFrame()->OnMessageReceived( + orig_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); - TestRenderViewHost* pending_rvh = static_cast( - contents()->GetPendingRenderViewHost()); + TestRenderFrameHost* pending_rfh = contents()->GetPendingMainFrame(); // Simulate the pending renderer's response, which leads to an unload request - // being sent to orig_rvh. + // being sent to orig_rfh. std::vector url_chain; url_chain.push_back(GURL()); contents()->GetRenderManagerForTesting()->OnCrossSiteResponse( @@ -1225,67 +1214,69 @@ TEST_F(WebContentsImplTest, CrossSiteCantPreemptAfterUnload) { FrameHostMsg_DidCommitProvisionalLoad_Params params1a; InitNavigateParams(¶ms1a, 2, GURL("http://www.google.com/foo"), PAGE_TRANSITION_TYPED); - orig_rvh->SendNavigate(2, GURL("http://www.google.com/foo")); + orig_rfh->SendNavigate(2, GURL("http://www.google.com/foo")); // Verify that the pending navigation is still in progress. EXPECT_TRUE(contents()->cross_navigation_pending()); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() != NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() != NULL); // DidNavigate from the pending page should commit it. contents()->TestDidNavigate( - pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); + pending_rfh, 1, url2, PAGE_TRANSITION_TYPED); SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(pending_rvh, rvh()); + EXPECT_EQ(pending_rfh, contents()->GetMainFrame()); EXPECT_NE(instance1, instance2); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); } // Test that a cross-site navigation that doesn't commit after the unload // handler doesn't leave the contents in a stuck state. http://crbug.com/88562 TEST_F(WebContentsImplTest, CrossSiteNavigationCanceled) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); SiteInstance* instance1 = contents()->GetSiteInstance(); - // Navigate to URL. First URL should use first RenderViewHost. + // Navigate to URL. First URL should use original RenderFrameHost. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Navigate to new site, simulating an onbeforeunload approval. const GURL url2("http://www.yahoo.com"); controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - EXPECT_TRUE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_TRUE(orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); base::TimeTicks now = base::TimeTicks::Now(); - orig_rvh->GetMainFrame()->OnMessageReceived( + orig_rfh->OnMessageReceived( FrameHostMsg_BeforeUnload_ACK(0, true, now, now)); EXPECT_TRUE(contents()->cross_navigation_pending()); // Simulate swap out message when the response arrives. - orig_rvh->OnSwappedOut(false); + orig_rfh->OnSwappedOut(false); // Suppose the navigation doesn't get a chance to commit, and the user - // navigates in the current RVH's SiteInstance. + // navigates in the current RFH's SiteInstance. controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); // Verify that the pending navigation is cancelled and the renderer is no // longer swapped out. - EXPECT_FALSE(orig_rvh->is_waiting_for_beforeunload_ack()); + EXPECT_FALSE( + orig_rfh->GetRenderViewHost()->is_waiting_for_beforeunload_ack()); SiteInstance* instance2 = contents()->GetSiteInstance(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, rvh()); - EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, orig_rvh->rvh_state()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); + EXPECT_EQ(RenderViewHostImpl::STATE_DEFAULT, + orig_rfh->GetRenderViewHost()->rvh_state()); EXPECT_EQ(instance1, instance2); - EXPECT_TRUE(contents()->GetPendingRenderViewHost() == NULL); + EXPECT_TRUE(contents()->GetPendingMainFrame() == NULL); } // Test that NavigationEntries have the correct page state after going // forward and back. Prevents regression for bug 1116137. TEST_F(WebContentsImplTest, NavigationEntryContentState) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); // Navigate to URL. There should be no committed entry yet. const GURL url("http://www.google.com"); @@ -1294,7 +1285,7 @@ TEST_F(WebContentsImplTest, NavigationEntryContentState) { EXPECT_TRUE(entry == NULL); // Committed entry should have page state after DidNavigate. - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); entry = controller().GetLastCommittedEntry(); EXPECT_TRUE(entry->GetPageState().IsValid()); @@ -1305,13 +1296,13 @@ TEST_F(WebContentsImplTest, NavigationEntryContentState) { EXPECT_TRUE(entry->GetPageState().IsValid()); // Committed entry should have page state after DidNavigate. - contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 2, url2, PAGE_TRANSITION_TYPED); entry = controller().GetLastCommittedEntry(); EXPECT_TRUE(entry->GetPageState().IsValid()); // Now go back. Committed entry should still have page state. controller().GoBack(); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); entry = controller().GetLastCommittedEntry(); EXPECT_TRUE(entry->GetPageState().IsValid()); } @@ -1320,13 +1311,13 @@ TEST_F(WebContentsImplTest, NavigationEntryContentState) { // state after opening a new window to about:blank. Prevents regression for // bugs b/1116137 and http://crbug.com/111975. TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) { - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); // When opening a new window, it is navigated to about:blank internally. // Currently, this results in two DidNavigate events. const GURL url(url::kAboutBlankURL); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); // Should have a page state here. NavigationEntry* entry = controller().GetLastCommittedEntry(); @@ -1343,8 +1334,8 @@ TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) { controller().LoadURL(new_url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); - contents()->TestDidNavigate(orig_rvh, 1, new_url, PAGE_TRANSITION_TYPED); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); + contents()->TestDidNavigate(orig_rfh, 1, new_url, PAGE_TRANSITION_TYPED); NavigationEntryImpl* entry_impl2 = NavigationEntryImpl::FromNavigationEntry( controller().GetLastCommittedEntry()); EXPECT_EQ(site_instance_id, entry_impl2->site_instance()->GetId()); @@ -1356,14 +1347,15 @@ TEST_F(WebContentsImplTest, NavigationEntryContentStateNewWindow) { TEST_F(WebContentsImplTest, NavigationExitsFullscreen) { FakeFullscreenDelegate fake_delegate; contents()->SetDelegate(&fake_delegate); - TestRenderViewHost* orig_rvh = test_rvh(); + TestRenderFrameHost* orig_rfh = contents()->GetMainFrame(); + TestRenderViewHost* orig_rvh = orig_rfh->GetRenderViewHost(); // Navigate to a site. const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Toggle fullscreen mode on (as if initiated via IPC from renderer). EXPECT_FALSE(orig_rvh->IsFullscreen()); @@ -1379,9 +1371,9 @@ TEST_F(WebContentsImplTest, NavigationExitsFullscreen) { const GURL url2("http://www.yahoo.com"); controller().LoadURL( url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - RenderViewHost* const pending_rvh = contents()->GetPendingRenderViewHost(); + TestRenderFrameHost* const pending_rfh = contents()->GetPendingMainFrame(); contents()->TestDidNavigate( - pending_rvh, 1, url2, PAGE_TRANSITION_TYPED); + pending_rfh, 1, url2, PAGE_TRANSITION_TYPED); // Confirm fullscreen has exited. EXPECT_FALSE(orig_rvh->IsFullscreen()); @@ -1396,20 +1388,21 @@ TEST_F(WebContentsImplTest, NavigationExitsFullscreen) { TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) { FakeFullscreenDelegate fake_delegate; contents()->SetDelegate(&fake_delegate); - TestRenderViewHost* const orig_rvh = test_rvh(); + TestRenderFrameHost* const orig_rfh = contents()->GetMainFrame(); + TestRenderViewHost* const orig_rvh = orig_rfh->GetRenderViewHost(); // Navigate to a site. const GURL url("http://www.google.com"); controller().LoadURL(url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(orig_rvh, 1, url, PAGE_TRANSITION_TYPED); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + contents()->TestDidNavigate(orig_rfh, 1, url, PAGE_TRANSITION_TYPED); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Now, navigate to another page on the same site. const GURL url2("http://www.google.com/search?q=kittens"); controller().LoadURL(url2, Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_FALSE(contents()->cross_navigation_pending()); - contents()->TestDidNavigate(orig_rvh, 2, url2, PAGE_TRANSITION_TYPED); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + contents()->TestDidNavigate(orig_rfh, 2, url2, PAGE_TRANSITION_TYPED); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); // Sanity-check: Confirm we're not starting out in fullscreen mode. EXPECT_FALSE(orig_rvh->IsFullscreen()); @@ -1430,9 +1423,9 @@ TEST_F(WebContentsImplTest, HistoryNavigationExitsFullscreen) { else controller().GoForward(); EXPECT_FALSE(contents()->cross_navigation_pending()); - EXPECT_EQ(orig_rvh, contents()->GetRenderViewHost()); + EXPECT_EQ(orig_rfh, contents()->GetMainFrame()); contents()->TestDidNavigate( - orig_rvh, i + 1, url, PAGE_TRANSITION_FORWARD_BACK); + orig_rfh, i + 1, url, PAGE_TRANSITION_FORWARD_BACK); // Confirm fullscreen has exited. EXPECT_FALSE(orig_rvh->IsFullscreen()); @@ -1449,7 +1442,7 @@ TEST_F(WebContentsImplTest, TerminateHidesValidationMessage) { EXPECT_FALSE(fake_delegate.hide_validation_message_was_called()); // Crash the renderer. - test_rvh()->OnMessageReceived( + contents()->GetMainFrame()->GetRenderViewHost()->OnMessageReceived( ViewHostMsg_RenderProcessGone( 0, base::TERMINATION_STATUS_PROCESS_CRASHED, -1)); @@ -1469,8 +1462,8 @@ TEST_F(WebContentsImplTest, CrashExitsFullscreen) { const GURL url("http://www.google.com"); controller().LoadURL( url, Referrer(), PAGE_TRANSITION_TYPED, std::string()); - contents()->TestDidNavigate(test_rvh(), 1, url, PAGE_TRANSITION_TYPED); - EXPECT_EQ(test_rvh(), contents()->GetRenderViewHost()); + contents()->TestDidNavigate( + contents()->GetMainFrame(), 1, url, PAGE_TRANSITION_TYPED); // Toggle fullscreen mode on (as if initiated via IPC from renderer). EXPECT_FALSE(test_rvh()->IsFullscreen()); @@ -1506,12 +1499,12 @@ TEST_F(WebContentsImplTest, ShowInterstitialFromBrowserWithNewNavigationDontProceed) { // Navigate to a page. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Initiate a browser navigation that will trigger the interstitial controller().LoadURL(GURL("http://www.evil.com"), Referrer(), - PAGE_TRANSITION_TYPED, std::string()); + PAGE_TRANSITION_TYPED, std::string()); // Show an interstitial. TestInterstitialPage::InterstitialState state = @@ -1556,7 +1549,7 @@ TEST_F(WebContentsImplTest, ShowInterstitiaFromRendererlWithNewNavigationDontProceed) { // Navigate to a page. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial (no pending entry, the interstitial would have been @@ -1602,7 +1595,7 @@ TEST_F(WebContentsImplTest, TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationDontProceed) { // Navigate to a page. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial. @@ -1649,7 +1642,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialFromBrowserNewNavigationProceed) { // Navigate to a page. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Initiate a browser navigation that will trigger the interstitial @@ -1690,7 +1683,7 @@ TEST_F(WebContentsImplTest, // Simulate the navigation to the page, that's when the interstitial gets // hidden. GURL url3("http://www.thepage.com"); - test_rvh()->SendNavigate(2, url3); + contents()->GetMainFrame()->SendNavigate(2, url3); EXPECT_FALSE(contents()->ShowingInterstitialPage()); EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); @@ -1711,7 +1704,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialFromRendererNewNavigationProceed) { // Navigate to a page. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial. @@ -1748,7 +1741,7 @@ TEST_F(WebContentsImplTest, // Simulate the navigation to the page, that's when the interstitial gets // hidden. GURL url3("http://www.thepage.com"); - test_rvh()->SendNavigate(2, url3); + contents()->GetMainFrame()->SendNavigate(2, url3); EXPECT_FALSE(contents()->ShowingInterstitialPage()); EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); @@ -1768,7 +1761,7 @@ TEST_F(WebContentsImplTest, TEST_F(WebContentsImplTest, ShowInterstitialNoNewNavigationProceed) { // Navigate to a page so we have a navigation entry in the controller. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial. @@ -1826,7 +1819,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) { // While interstitial showing, navigate to a new URL. const GURL url2("http://www.yahoo.com"); - test_rvh()->SendNavigate(1, url2); + contents()->GetMainFrame()->SendNavigate(1, url2); EXPECT_EQ(TestInterstitialPage::CANCELED, state); @@ -1838,7 +1831,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialThenNavigate) { TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) { // Navigate to a page so we have a navigation entry in the controller. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show interstitial. @@ -1855,7 +1848,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) { // While the interstitial is showing, go back. controller().GoBack(); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); // Make sure we are back to the original page and that the interstitial is // gone. @@ -1873,7 +1866,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialThenGoBack) { TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) { // Navigate to a page so we have a navigation entry in the controller. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show interstitial. @@ -1895,7 +1888,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) { // While the interstitial is showing, go back. controller().GoBack(); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); // Make sure we are back to the original page and that the interstitial is // gone. @@ -1913,7 +1906,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenGoBack) { TEST_F(WebContentsImplTest, ShowInterstitialCrashRendererThenNavigate) { // Navigate to a page so we have a navigation entry in the controller. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show interstitial. @@ -1992,7 +1985,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialThenCloseAndShutdown) { TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) { // Navigate to a page so we have a navigation entry in the controller. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial. @@ -2027,7 +2020,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialProceedMultipleCommands) { TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) { // Navigate to a page so we have a navigation entry in the controller. GURL start_url("http://www.google.com"); - test_rvh()->SendNavigate(1, start_url); + contents()->GetMainFrame()->SendNavigate(1, start_url); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial. @@ -2063,7 +2056,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) { // Let's make sure interstitial2 is working as intended. interstitial2->Proceed(); GURL landing_url("http://www.thepage.com"); - test_rvh()->SendNavigate(2, landing_url); + contents()->GetMainFrame()->SendNavigate(2, landing_url); EXPECT_FALSE(contents()->ShowingInterstitialPage()); EXPECT_TRUE(contents()->GetInterstitialPage() == NULL); @@ -2080,7 +2073,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialOnInterstitial) { TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) { // Navigate to a page so we have a navigation entry in the controller. GURL start_url("http://www.google.com"); - test_rvh()->SendNavigate(1, start_url); + contents()->GetMainFrame()->SendNavigate(1, start_url); EXPECT_EQ(1, controller().GetEntryCount()); // Show an interstitial. @@ -2120,7 +2113,7 @@ TEST_F(WebContentsImplTest, ShowInterstitialProceedShowInterstitial) { // Let's make sure interstitial2 is working as intended. interstitial2->Proceed(); GURL landing_url("http://www.thepage.com"); - test_rvh()->SendNavigate(2, landing_url); + contents()->GetMainFrame()->SendNavigate(2, landing_url); RunAllPendingInMessageLoop(); EXPECT_TRUE(deleted2); @@ -2294,7 +2287,8 @@ TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) { contents()->GetController().LoadURL( kGURL, Referrer(), PAGE_TRANSITION_TYPED, std::string()); // DidNavigate from the page - contents()->TestDidNavigate(rvh(), 1, kGURL, PAGE_TRANSITION_TYPED); + contents()->TestDidNavigate( + contents()->GetMainFrame(), 1, kGURL, PAGE_TRANSITION_TYPED); // Simulate showing an interstitial while the page is showing. TestInterstitialPage::InterstitialState state = @@ -2321,7 +2315,7 @@ TEST_F(WebContentsImplTest, NoJSMessageOnInterstitials) { TEST_F(WebContentsImplTest, CopyStateFromAndPruneSourceInterstitial) { // Navigate to a page. GURL url1("http://www.google.com"); - test_rvh()->SendNavigate(1, url1); + contents()->GetMainFrame()->SendNavigate(1, url1); EXPECT_EQ(1, controller().GetEntryCount()); // Initiate a browser navigation that will trigger the interstitial @@ -2732,7 +2726,7 @@ TEST_F(WebContentsImplTest, ActiveContentsCountChangeBrowsingInstance) { GURL(kTestWebUIUrl), Referrer(), PAGE_TRANSITION_TYPED, std::string()); EXPECT_TRUE(contents->cross_navigation_pending()); scoped_refptr instance_webui( - contents->GetPendingRenderViewHost()->GetSiteInstance()); + contents->GetPendingMainFrame()->GetSiteInstance()); EXPECT_FALSE(instance->IsRelatedSiteInstance(instance_webui.get())); // At this point, contents still counts for the old BrowsingInstance. diff --git a/content/browser/web_contents/web_contents_view_aura.cc b/content/browser/web_contents/web_contents_view_aura.cc index d3c5d7ae8119d..b58b77012b7d9 100644 --- a/content/browser/web_contents/web_contents_view_aura.cc +++ b/content/browser/web_contents/web_contents_view_aura.cc @@ -83,7 +83,7 @@ WebContentsView* CreateWebContentsView( namespace { bool IsScrollEndEffectEnabled() { - return CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + return base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kScrollEndEffect) == "1"; } @@ -134,8 +134,7 @@ class OverscrollWindowDelegate : public ImageWindowDelegate { gfx::Image image; if (entry && entry->screenshot().get()) { std::vector image_reps; - image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), - ui::GetScaleFactorForNativeView(web_contents_window()))); + image_reps.push_back(gfx::ImagePNGRep(entry->screenshot(), 1.0f)); image = gfx::Image(image_reps); } SetImage(image); @@ -790,7 +789,7 @@ void WebContentsViewAura::EndDrag(blink::WebDragOperationsMask ops) { void WebContentsViewAura::InstallOverscrollControllerDelegate( RenderWidgetHostViewAura* view) { - const std::string value = CommandLine::ForCurrentProcess()-> + const std::string value = base::CommandLine::ForCurrentProcess()-> GetSwitchValueASCII(switches::kOverscrollHistoryNavigation); if (value == "0") { navigation_overlay_.reset(); @@ -1281,9 +1280,9 @@ gfx::Rect WebContentsViewAura::GetVisibleBounds() const { return rwhv->GetViewBounds(); } -void WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) { +bool WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) { if (current_overscroll_gesture_ == OVERSCROLL_NONE) - return; + return false; aura::Window* target = GetWindowToAnimateForOverscroll(); gfx::Vector2d translate = GetTranslationForOverscroll(delta_x, delta_y); @@ -1298,6 +1297,7 @@ void WebContentsViewAura::OnOverscrollUpdate(float delta_x, float delta_y) { } OverscrollUpdateForWebContentsDelegate(translate.y()); + return !translate.IsZero(); } void WebContentsViewAura::OnOverscrollComplete(OverscrollMode mode) { diff --git a/content/browser/web_contents/web_contents_view_aura.h b/content/browser/web_contents/web_contents_view_aura.h index a615c3486b2bb..2d7df30a122ec 100644 --- a/content/browser/web_contents/web_contents_view_aura.h +++ b/content/browser/web_contents/web_contents_view_aura.h @@ -139,7 +139,7 @@ class WebContentsViewAura // Overridden from OverscrollControllerDelegate: virtual gfx::Rect GetVisibleBounds() const OVERRIDE; - virtual void OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE; + virtual bool OnOverscrollUpdate(float delta_x, float delta_y) OVERRIDE; virtual void OnOverscrollComplete(OverscrollMode overscroll_mode) OVERRIDE; virtual void OnOverscrollModeChange(OverscrollMode old_mode, OverscrollMode new_mode) OVERRIDE; diff --git a/content/browser/web_contents/web_contents_view_aura_browsertest.cc b/content/browser/web_contents/web_contents_view_aura_browsertest.cc index bdd74817ddb5f..3fb779190e3e9 100644 --- a/content/browser/web_contents/web_contents_view_aura_browsertest.cc +++ b/content/browser/web_contents/web_contents_view_aura_browsertest.cc @@ -18,7 +18,10 @@ #include "content/browser/renderer_host/render_widget_host_view_aura.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_view.h" +#include "content/common/input/synthetic_web_input_event_builders.h" +#include "content/common/input_messages.h" #include "content/common/view_messages.h" +#include "content/public/browser/browser_message_filter.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/common/content_switches.h" @@ -32,9 +35,26 @@ #include "ui/aura/window_tree_host.h" #include "ui/compositor/scoped_animation_duration_scale_mode.h" #include "ui/events/event_processor.h" +#include "ui/events/event_switches.h" #include "ui/events/event_utils.h" #include "ui/events/test/event_generator.h" +namespace { + +// TODO(tdresser): Find a way to avoid sleeping like this. See crbug.com/405282 +// for details. +void GiveItSomeTime() { + base::RunLoop run_loop; + base::MessageLoop::current()->PostDelayedTask( + FROM_HERE, + run_loop.QuitClosure(), + base::TimeDelta::FromMillisecondsD(10)); + run_loop.Run(); +} + +} //namespace + + namespace content { // This class keeps track of the RenderViewHost whose screenshot was captured. @@ -127,6 +147,55 @@ class NavigationWatcher : public WebContentsObserver { DISALLOW_COPY_AND_ASSIGN(NavigationWatcher); }; +class InputEventMessageFilterWaitsForAcks : public BrowserMessageFilter { + public: + InputEventMessageFilterWaitsForAcks() + : BrowserMessageFilter(InputMsgStart), + type_(blink::WebInputEvent::Undefined), + state_(INPUT_EVENT_ACK_STATE_UNKNOWN) {} + + void WaitForAck(blink::WebInputEvent::Type type) { + base::RunLoop run_loop; + base::AutoReset reset_quit(&quit_, run_loop.QuitClosure()); + base::AutoReset reset_type(&type_, type); + run_loop.Run(); + } + + InputEventAckState last_ack_state() const { return state_; } + + protected: + virtual ~InputEventMessageFilterWaitsForAcks() {} + + private: + void ReceivedEventAck(blink::WebInputEvent::Type type, + InputEventAckState state) { + if (type_ == type) { + state_ = state; + quit_.Run(); + } + } + + // BrowserMessageFilter: + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE { + if (message.type() == InputHostMsg_HandleInputEvent_ACK::ID) { + InputHostMsg_HandleInputEvent_ACK::Param params; + InputHostMsg_HandleInputEvent_ACK::Read(&message, ¶ms); + blink::WebInputEvent::Type type = params.a.type; + InputEventAckState ack = params.a.state; + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + base::Bind(&InputEventMessageFilterWaitsForAcks::ReceivedEventAck, + this, type, ack)); + } + return false; + } + + base::Closure quit_; + blink::WebInputEvent::Type type_; + InputEventAckState state_; + + DISALLOW_COPY_AND_ASSIGN(InputEventMessageFilterWaitsForAcks); +}; + class WebContentsViewAuraTest : public ContentBrowserTest { public: WebContentsViewAuraTest() @@ -156,6 +225,11 @@ class WebContentsViewAuraTest : public ContentBrowserTest { controller->SetScreenshotManager(screenshot_manager_); } + virtual void SetUpCommandLine(CommandLine* cmd) OVERRIDE { + cmd->AppendSwitchASCII(switches::kTouchEvents, + switches::kTouchEventsEnabled); + } + void TestOverscrollNavigation(bool touch_handler) { ASSERT_NO_FATAL_FAILURE( StartTestWithPage("files/overscroll_navigation.html")); @@ -256,14 +330,62 @@ class WebContentsViewAuraTest : public ContentBrowserTest { return index; } + int ExecuteScriptAndExtractInt(const std::string& script) { + int value = 0; + EXPECT_TRUE(content::ExecuteScriptAndExtractInt( + shell()->web_contents(), + "domAutomationController.send(" + script + ")", + &value)); + return value; + } + + RenderViewHost* GetRenderViewHost() const { + RenderViewHost* const rvh = shell()->web_contents()->GetRenderViewHost(); + CHECK(rvh); + return rvh; + } + + RenderWidgetHostImpl* GetRenderWidgetHost() const { + RenderWidgetHostImpl* const rwh = + RenderWidgetHostImpl::From(shell() + ->web_contents() + ->GetRenderWidgetHostView() + ->GetRenderWidgetHost()); + CHECK(rwh); + return rwh; + } + + RenderWidgetHostViewBase* GetRenderWidgetHostView() const { + return static_cast( + GetRenderViewHost()->GetView()); + } + + InputEventMessageFilterWaitsForAcks* filter() { + return filter_.get(); + } + + void WaitAFrame() { + uint32 frame = GetRenderWidgetHostView()->RendererFrameNumber(); + while (!GetRenderWidgetHost()->ScheduleComposite()) + GiveItSomeTime(); + while (GetRenderWidgetHostView()->RendererFrameNumber() == frame) + GiveItSomeTime(); + } + protected: ScreenshotTracker* screenshot_manager() { return screenshot_manager_; } void set_min_screenshot_interval(int interval_ms) { screenshot_manager_->SetScreenshotInterval(interval_ms); } + void AddInputEventMessageFilter() { + filter_ = new InputEventMessageFilterWaitsForAcks(); + GetRenderWidgetHost()->GetProcess()->AddFilter(filter_); + } + private: ScreenshotTracker* screenshot_manager_; + scoped_refptr filter_; DISALLOW_COPY_AND_ASSIGN(WebContentsViewAuraTest); }; @@ -747,4 +869,100 @@ IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, WebContentsViewReparent) { EXPECT_TRUE(rwhva->has_snapped_to_boundary()); } +// Flaky on Windows, likely for the same reason as other flaky overscroll tests. +// http://crbug.com/305722 +#if defined(OS_WIN) +#define MAYBE_OverscrollNavigationTouchThrottling \ + DISABLED_OverscrollNavigationTouchThrottling +#else +#define MAYBE_OverscrollNavigationTouchThrottling \ + OverscrollNavigationTouchThrottling +#endif + +// Tests that touch moves are not throttled when performing a scroll gesture on +// a non-scrollable area, except during gesture-nav. +IN_PROC_BROWSER_TEST_F(WebContentsViewAuraTest, + MAYBE_OverscrollNavigationTouchThrottling) { + ASSERT_NO_FATAL_FAILURE( + StartTestWithPage("files/overscroll_navigation.html")); + + AddInputEventMessageFilter(); + + WebContentsImpl* web_contents = + static_cast(shell()->web_contents()); + aura::Window* content = web_contents->GetContentNativeView(); + gfx::Rect bounds = content->GetBoundsInRootWindow(); + const int dx = 20; + + ExecuteSyncJSFunction(web_contents->GetMainFrame(), + "install_touchmove_handler()"); + + WaitAFrame(); + + for (int navigated = 0; navigated <= 1; ++navigated) { + if (navigated) { + ExecuteSyncJSFunction(web_contents->GetMainFrame(), "navigate_next()"); + ExecuteSyncJSFunction(web_contents->GetMainFrame(), + "reset_touchmove_count()"); + } + // Send touch press. + SyntheticWebTouchEvent touch; + touch.PressPoint(bounds.x() + 2, bounds.y() + 10); + GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, + ui::LatencyInfo()); + filter()->WaitForAck(blink::WebInputEvent::TouchStart); + // Assert on the ack, because we'll end up waiting for acks that will never + // come if this is not true. + ASSERT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state()); + + // Send first touch move, and then a scroll begin. + touch.MovePoint(0, bounds.x() + 20 + 1 * dx, bounds.y() + 100); + GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, + ui::LatencyInfo()); + filter()->WaitForAck(blink::WebInputEvent::TouchMove); + ASSERT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED, filter()->last_ack_state()); + + blink::WebGestureEvent scroll_begin = + SyntheticWebGestureEventBuilder::BuildScrollBegin(1, 1); + GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo( + scroll_begin, ui::LatencyInfo()); + // Scroll begin ignores ack disposition, so don't wait for the ack. + // GiveItSomeTime(); + WaitAFrame(); + + // First touchmove already sent, start at 2. + for (int i = 2; i <= 10; ++i) { + // Send a touch move, followed by a scroll update + touch.MovePoint(0, bounds.x() + 20 + i * dx, bounds.y() + 100); + GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo( + touch, ui::LatencyInfo()); + WaitAFrame(); + + blink::WebGestureEvent scroll_update = + SyntheticWebGestureEventBuilder::BuildScrollUpdate(dx, 5, 0); + + GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo( + scroll_update, ui::LatencyInfo()); + + WaitAFrame(); + } + + touch.ReleasePoint(0); + GetRenderWidgetHost()->ForwardTouchEventWithLatencyInfo(touch, + ui::LatencyInfo()); + WaitAFrame(); + + blink::WebGestureEvent scroll_end; + scroll_end.type = blink::WebInputEvent::GestureScrollEnd; + GetRenderWidgetHost()->ForwardGestureEventWithLatencyInfo( + scroll_end, ui::LatencyInfo()); + WaitAFrame(); + + if (!navigated) + EXPECT_EQ(10, ExecuteScriptAndExtractInt("touchmoveCount")); + else + EXPECT_GT(10, ExecuteScriptAndExtractInt("touchmoveCount")); + } +} + } // namespace content diff --git a/content/browser/worker.sb b/content/browser/worker.sb deleted file mode 100644 index 2e408883e8728..0000000000000 --- a/content/browser/worker.sb +++ /dev/null @@ -1,12 +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. -;; -; This is the Sandbox configuration file used for safeguarding the worker -; process which is used to run web workers in a sandboxed environment. -; -; This is the most restrictive sandbox profile and only enables just enough -; to allow basic use of Cocoa. - -; *** The contents of content/common/common.sb are implicitly included here. *** diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc index 81d075688d725..2c3467e0ecb28 100644 --- a/content/browser/zygote_host/zygote_host_impl_linux.cc +++ b/content/browser/zygote_host/zygote_host_impl_linux.cc @@ -100,7 +100,7 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { base::FilePath chrome_path; CHECK(PathService::Get(base::FILE_EXE, &chrome_path)); - CommandLine cmd_line(chrome_path); + base::CommandLine cmd_line(chrome_path); cmd_line.AppendSwitchASCII(switches::kProcessType, switches::kZygoteProcess); @@ -111,7 +111,8 @@ void ZygoteHostImpl::Init(const std::string& sandbox_cmd) { fds_to_map.push_back(std::make_pair(fds[1], kZygoteSocketPairFd)); base::LaunchOptions options; - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); if (browser_command_line.HasSwitch(switches::kZygoteCmdPrefix)) { cmd_line.PrependWrapper( browser_command_line.GetSwitchValueNative(switches::kZygoteCmdPrefix)); diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn index e77fcd84d3727..fc0237897167b 100644 --- a/content/child/BUILD.gn +++ b/content/child/BUILD.gn @@ -87,13 +87,14 @@ source_set("child") { sources = [] } else { deps += [ + "//content/app/strings", "//crypto:platform", "//third_party/WebKit/public:blink", + "//third_party/WebKit/public:resources", "//third_party/npapi", - "//webkit:resources", - "//webkit:strings", "//webkit/child", "//webkit/common", + "//webkit/glue/resources", ] } diff --git a/content/child/DEPS b/content/child/DEPS index 3adcf04965d1a..be1e09fb53936 100644 --- a/content/child/DEPS +++ b/content/child/DEPS @@ -1,5 +1,6 @@ include_rules = [ "+components/tracing", + "+content/app/strings/grit", # For generated headers "+content/public/child", "+media/base/android", ] diff --git a/content/child/blink_glue.cc b/content/child/blink_glue.cc deleted file mode 100644 index 9a3af757bafba..0000000000000 --- a/content/child/blink_glue.cc +++ /dev/null @@ -1,29 +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 "content/child/blink_glue.h" - -#include "base/logging.h" -#include "third_party/WebKit/public/platform/WebFileInfo.h" - -namespace content { - -void FileInfoToWebFileInfo(const base::File::Info& file_info, - blink::WebFileInfo* web_file_info) { - DCHECK(web_file_info); - // WebKit now expects NaN as uninitialized/null Date. - if (file_info.last_modified.is_null()) - web_file_info->modificationTime = std::numeric_limits::quiet_NaN(); - else - web_file_info->modificationTime = file_info.last_modified.ToDoubleT(); - web_file_info->length = file_info.size; - if (file_info.is_directory) - web_file_info->type = blink::WebFileInfo::TypeDirectory; - else - web_file_info->type = blink::WebFileInfo::TypeFile; -} - -COMPILE_ASSERT(std::numeric_limits::has_quiet_NaN, has_quiet_NaN); - -} // namespace content diff --git a/content/child/blink_glue.h b/content/child/blink_glue.h deleted file mode 100644 index 0181472d83136..0000000000000 --- a/content/child/blink_glue.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 CONTENT_CHILD_BLINK_GLUE_H_ -#define CONTENT_CHILD_BLINK_GLUE_H_ - -#include "base/files/file.h" -#include "content/common/content_export.h" - -namespace blink { -struct WebFileInfo; -} - -namespace content { - -// File info conversion -CONTENT_EXPORT void FileInfoToWebFileInfo(const base::File::Info& file_info, - blink::WebFileInfo* web_file_info); - -} // namespace content - -#endif // CONTENT_CHILD_BLINK_GLUE_H_ diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc index 648ae99feecc5..1050276ef02c2 100644 --- a/content/child/blink_platform_impl.cc +++ b/content/child/blink_platform_impl.cc @@ -27,6 +27,7 @@ #include "base/sys_info.h" #include "base/time/time.h" #include "blink/public/resources/grit/blink_resources.h" +#include "content/app/strings/grit/content_strings.h" #include "content/child/child_thread.h" #include "content/child/content_child_helpers.h" #include "content/child/fling_curve_configuration.h" @@ -38,14 +39,16 @@ #include "content/child/worker_task_runner.h" #include "content/public/common/content_client.h" #include "grit/webkit_resources.h" -#include "grit/webkit_strings.h" #include "net/base/data_url.h" #include "net/base/mime_util.h" #include "net/base/net_errors.h" +#include "net/base/net_util.h" #include "third_party/WebKit/public/platform/WebConvertableToTraceFormat.h" #include "third_party/WebKit/public/platform/WebData.h" #include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/platform/WebURL.h" #include "third_party/WebKit/public/platform/WebWaitableEvent.h" +#include "third_party/WebKit/public/web/WebSecurityOrigin.h" #include "ui/base/layout.h" #if defined(OS_ANDROID) @@ -151,6 +154,13 @@ class ConvertableToTraceFormatWrapper blink::WebConvertableToTraceFormat convertable_; }; +bool isHostnameReservedIPAddress(const std::string& host) { + net::IPAddressNumber address; + if (!net::ParseURLHostnameToNumber(host, &address)) + return false; + return net::IsIPAddressReserved(address); +} + } // namespace static int ToMessageID(WebLocalizedString::Name name) { @@ -439,6 +449,15 @@ WebURLError BlinkPlatformImpl::cancelledError( return WebURLLoaderImpl::CreateError(unreachableURL, false, net::ERR_ABORTED); } +bool BlinkPlatformImpl::isReservedIPAddress( + const blink::WebSecurityOrigin& securityOrigin) const { + return isHostnameReservedIPAddress(securityOrigin.host().utf8()); +} + +bool BlinkPlatformImpl::isReservedIPAddress(const blink::WebURL& url) const { + return isHostnameReservedIPAddress(GURL(url).host()); +} + blink::WebThread* BlinkPlatformImpl::createThread(const char* name) { return new WebThreadImpl(name); } diff --git a/content/child/blink_platform_impl.h b/content/child/blink_platform_impl.h index e9d3e186cc9b2..797aae1c62041 100644 --- a/content/child/blink_platform_impl.h +++ b/content/child/blink_platform_impl.h @@ -81,6 +81,9 @@ class CONTENT_EXPORT BlinkPlatformImpl const blink::WebURL& url, blink::WebString& mimetype, blink::WebString& charset); virtual blink::WebURLError cancelledError(const blink::WebURL& url) const; + virtual bool isReservedIPAddress( + const blink::WebSecurityOrigin&) const OVERRIDE; + virtual bool isReservedIPAddress(const blink::WebURL&) const OVERRIDE; virtual blink::WebThread* createThread(const char* name); virtual blink::WebThread* currentThread(); virtual blink::WebWaitableEvent* createWaitableEvent(); diff --git a/content/child/blink_platform_impl_unittest.cc b/content/child/blink_platform_impl_unittest.cc new file mode 100644 index 0000000000000..c49dc179b790f --- /dev/null +++ b/content/child/blink_platform_impl_unittest.cc @@ -0,0 +1,197 @@ +// 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 "content/child/blink_platform_impl.h" + +#include "base/run_loop.h" +#include "base/time/time.h" +#include "net/base/net_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebString.h" +#include "third_party/WebKit/public/web/WebSecurityOrigin.h" + +namespace content { + +// Derives BlinkPlatformImpl for testing shared timers. +class TestBlinkPlatformImpl : public BlinkPlatformImpl { + public: + TestBlinkPlatformImpl() : mock_monotonically_increasing_time_(0) {} + + // Returns mock time when enabled. + virtual double monotonicallyIncreasingTime() OVERRIDE { + if (mock_monotonically_increasing_time_ > 0.0) + return mock_monotonically_increasing_time_; + return BlinkPlatformImpl::monotonicallyIncreasingTime(); + } + + virtual void OnStartSharedTimer(base::TimeDelta delay) OVERRIDE { + shared_timer_delay_ = delay; + } + + base::TimeDelta shared_timer_delay() { return shared_timer_delay_; } + + void set_mock_monotonically_increasing_time(double mock_time) { + mock_monotonically_increasing_time_ = mock_time; + } + + private: + base::TimeDelta shared_timer_delay_; + double mock_monotonically_increasing_time_; + + DISALLOW_COPY_AND_ASSIGN(TestBlinkPlatformImpl); +}; + +TEST(BlinkPlatformTest, SuspendResumeSharedTimer) { + base::MessageLoop message_loop; + + TestBlinkPlatformImpl platform_impl; + + // Set a timer to fire as soon as possible. + platform_impl.setSharedTimerFireInterval(0); + + // Suspend timers immediately so the above timer wouldn't be fired. + platform_impl.SuspendSharedTimer(); + + // The above timer would have posted a task which can be processed out of the + // message loop. + base::RunLoop().RunUntilIdle(); + + // Set a mock time after 1 second to simulate timers suspended for 1 second. + double new_time = base::Time::Now().ToDoubleT() + 1; + platform_impl.set_mock_monotonically_increasing_time(new_time); + + // Resume timers so that the timer set above will be set again to fire + // immediately. + platform_impl.ResumeSharedTimer(); + + EXPECT_TRUE(base::TimeDelta() == platform_impl.shared_timer_delay()); +} + +TEST(BlinkPlatformTest, IsReservedIPAddress_WebURL) { + TestBlinkPlatformImpl platform_impl; + + // Unreserved IPv4 addresses (in various forms). + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://8.8.8.8/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://99.64.0.0/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://212.15.0.0/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://212.15/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://212.15.0/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://3557752832/"))); + + // Reserved IPv4 addresses (in various forms). + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://192.168.0.0/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://192.168.0.6/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://10.0.0.5/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://10.0.0/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://10.0/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://3232235526/"))); + + // Unreserved IPv6 addresses. + EXPECT_FALSE(platform_impl.isReservedIPAddress( + GURL("http://[FFC0:ba98:7654:3210:FEDC:BA98:7654:3210]/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + GURL("http://[2000:ba98:7654:2301:EFCD:BA98:7654:3210]/"))); + + // Reserved IPv6 addresses. + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://[::1]/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://[::192.9.5.5]/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress(GURL("http://[FEED::BEEF]/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + GURL("http://[FEC0:ba98:7654:3210:FEDC:BA98:7654:3210]/"))); + + // Not IP addresses at all. + EXPECT_FALSE(platform_impl.isReservedIPAddress(GURL("http://example.com/"))); + EXPECT_FALSE( + platform_impl.isReservedIPAddress(GURL("http://127.0.0.1.example.com/"))); + + // Moar IPv4 + uint8 address[4] = {0, 0, 0, 1}; + for (int i = 0; i < 256; i++) { + address[0] = i; + std::string addressString = + net::IPAddressToString(address, sizeof(address)); + if (i == 0 || i == 10 || i == 127 || i > 223) { + EXPECT_TRUE( + platform_impl.isReservedIPAddress(GURL("http://" + addressString))); + } else { + EXPECT_FALSE( + platform_impl.isReservedIPAddress(GURL("http://" + addressString))); + } + } +} + +TEST(BlinkPlatformTest, IsReservedIPAddress_WebSecurityOrigin) { + TestBlinkPlatformImpl platform_impl; + + // Unreserved IPv4 addresses (in various forms). + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://8.8.8.8/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://99.64.0.0/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://212.15.0.0/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://212.15/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://212.15.0/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://3557752832/"))); + + // Reserved IPv4 addresses (in various forms). + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://192.168.0.0/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://192.168.0.6/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://10.0.0.5/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://10.0.0/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://10.0/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://3232235526/"))); + + // Unreserved IPv6 addresses. + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString( + "http://[FFC0:ba98:7654:3210:FEDC:BA98:7654:3210]/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString( + "http://[2000:ba98:7654:2301:EFCD:BA98:7654:3210]/"))); + + // Reserved IPv6 addresses. + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://[::1]/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://[::192.9.5.5]/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://[FEED::BEEF]/"))); + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString( + "http://[FEC0:ba98:7654:3210:FEDC:BA98:7654:3210]/"))); + + // Not IP addresses at all. + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString("http://example.com/"))); + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString( + "http://127.0.0.1.example.com/"))); + + // Moar IPv4 + uint8 address[4] = {0, 0, 0, 1}; + for (int i = 0; i < 256; i++) { + address[0] = i; + blink::WebString addressString = blink::WebString::fromUTF8( + "http://" + net::IPAddressToString(address, sizeof(address)) + "/"); + if (i == 0 || i == 10 || i == 127 || i > 223) { + EXPECT_TRUE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString(addressString))); + } else { + EXPECT_FALSE(platform_impl.isReservedIPAddress( + blink::WebSecurityOrigin::createFromString(addressString))); + } + } +} + +} // namespace content diff --git a/content/child/blink_platform_unittest.cc b/content/child/blink_platform_unittest.cc deleted file mode 100644 index 88a3c0dc518ac..0000000000000 --- a/content/child/blink_platform_unittest.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/run_loop.h" -#include "base/time/time.h" -#include "content/child/blink_platform_impl.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace content { - -// Derives BlinkPlatformImpl for testing shared timers. -class TestBlinkPlatformImpl : public BlinkPlatformImpl { - public: - TestBlinkPlatformImpl() : mock_monotonically_increasing_time_(0) {} - - // Returns mock time when enabled. - virtual double monotonicallyIncreasingTime() OVERRIDE { - if (mock_monotonically_increasing_time_ > 0.0) - return mock_monotonically_increasing_time_; - return BlinkPlatformImpl::monotonicallyIncreasingTime(); - } - - virtual void OnStartSharedTimer(base::TimeDelta delay) OVERRIDE { - shared_timer_delay_ = delay; - } - - base::TimeDelta shared_timer_delay() { return shared_timer_delay_; } - - void set_mock_monotonically_increasing_time(double mock_time) { - mock_monotonically_increasing_time_ = mock_time; - } - - private: - base::TimeDelta shared_timer_delay_; - double mock_monotonically_increasing_time_; - - DISALLOW_COPY_AND_ASSIGN(TestBlinkPlatformImpl); -}; - -TEST(BlinkPlatformTest, SuspendResumeSharedTimer) { - base::MessageLoop message_loop; - - TestBlinkPlatformImpl platform_impl; - - // Set a timer to fire as soon as possible. - platform_impl.setSharedTimerFireInterval(0); - - // Suspend timers immediately so the above timer wouldn't be fired. - platform_impl.SuspendSharedTimer(); - - // The above timer would have posted a task which can be processed out of the - // message loop. - base::RunLoop().RunUntilIdle(); - - // Set a mock time after 1 second to simulate timers suspended for 1 second. - double new_time = base::Time::Now().ToDoubleT() + 1; - platform_impl.set_mock_monotonically_increasing_time(new_time); - - // Resume timers so that the timer set above will be set again to fire - // immediately. - platform_impl.ResumeSharedTimer(); - - EXPECT_TRUE(base::TimeDelta() == platform_impl.shared_timer_delay()); -} - -} // namespace content diff --git a/content/child/child_process.h b/content/child/child_process.h index af2d91343e194..1aedcb688a1c6 100644 --- a/content/child/child_process.h +++ b/content/child/child_process.h @@ -54,8 +54,7 @@ class CONTENT_EXPORT ChildProcess { // process goes away prematurely, the background thread can at least notice // the child processes's main thread exiting to determine that it should give // up waiting. - // For example, see the renderer code used to implement - // webkit_glue::GetCookies. + // For example, see the renderer code used to implement GetCookies(). base::WaitableEvent* GetShutDownEvent(); // These are used for ref-counting the child process. The process shuts diff --git a/content/child/child_thread.cc b/content/child/child_thread.cc index 7c6432af2452c..a9a2fb6a6831e 100644 --- a/content/child/child_thread.cc +++ b/content/child/child_thread.cc @@ -193,12 +193,12 @@ void QuitMainThreadMessageLoop() { } // namespace ChildThread::Options::Options() - : channel_name(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + : channel_name(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProcessChannelID)), use_mojo_channel(false) {} ChildThread::Options::Options(bool mojo) - : channel_name(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + : channel_name(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProcessChannelID)), use_mojo_channel(mojo) {} @@ -297,7 +297,8 @@ void ChildThread::Init(const Options& options) { channel_->AddFilter(quota_message_filter_->GetFilter()); channel_->AddFilter(service_worker_message_filter_->GetFilter()); - if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess)) { + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kSingleProcess)) { // In single process mode, browser-side tracing will cover the whole // process including renderers. channel_->AddFilter(new tracing::ChildTraceMessageFilter( @@ -317,13 +318,13 @@ void ChildThread::Init(const Options& options) { #if defined(OS_POSIX) // Check that --process-type is specified so we don't do this in unit tests // and single-process mode. - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessType)) + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kProcessType)) channel_->AddFilter(new SuicideOnChannelErrorFilter()); #endif int connection_timeout = kConnectionTimeoutS; std::string connection_override = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kIPCConnectionTimeout); if (!connection_override.empty()) { int temp; @@ -526,7 +527,7 @@ void ChildThread::OnDumpHandles() { #if defined(OS_WIN) scoped_refptr handle_enum( new HandleEnumerator( - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kAuditAllHandles))); handle_enum->EnumerateHandles(); Send(new ChildProcessHostMsg_DumpHandlesDone); diff --git a/content/child/file_info_util.cc b/content/child/file_info_util.cc new file mode 100644 index 0000000000000..c605adfd6cfb3 --- /dev/null +++ b/content/child/file_info_util.cc @@ -0,0 +1,29 @@ +// 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 "content/child/file_info_util.h" + +#include "base/logging.h" +#include "third_party/WebKit/public/platform/WebFileInfo.h" + +namespace content { + +void FileInfoToWebFileInfo(const base::File::Info& file_info, + blink::WebFileInfo* web_file_info) { + DCHECK(web_file_info); + // WebKit now expects NaN as uninitialized/null Date. + if (file_info.last_modified.is_null()) + web_file_info->modificationTime = std::numeric_limits::quiet_NaN(); + else + web_file_info->modificationTime = file_info.last_modified.ToDoubleT(); + web_file_info->length = file_info.size; + if (file_info.is_directory) + web_file_info->type = blink::WebFileInfo::TypeDirectory; + else + web_file_info->type = blink::WebFileInfo::TypeFile; +} + +COMPILE_ASSERT(std::numeric_limits::has_quiet_NaN, has_quiet_NaN); + +} // namespace content diff --git a/content/child/file_info_util.h b/content/child/file_info_util.h new file mode 100644 index 0000000000000..82a11728b7221 --- /dev/null +++ b/content/child/file_info_util.h @@ -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. + +#ifndef CONTENT_CHILD_FILE_INFO_UTIL_H_ +#define CONTENT_CHILD_FILE_INFO_UTIL_H_ + +#include "base/files/file.h" + +namespace blink { +struct WebFileInfo; +} + +namespace content { + +// File info conversion +void FileInfoToWebFileInfo(const base::File::Info& file_info, + blink::WebFileInfo* web_file_info); + +} // namespace content + +#endif // CONTENT_CHILD_FILE_INFO_UTIL_H_ diff --git a/content/child/fileapi/webfilesystem_impl.cc b/content/child/fileapi/webfilesystem_impl.cc index dae9788bf5928..e629fdd4a2936 100644 --- a/content/child/fileapi/webfilesystem_impl.cc +++ b/content/child/fileapi/webfilesystem_impl.cc @@ -11,8 +11,8 @@ #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/threading/thread_local.h" -#include "content/child/blink_glue.h" #include "content/child/child_thread.h" +#include "content/child/file_info_util.h" #include "content/child/fileapi/file_system_dispatcher.h" #include "content/child/fileapi/webfilewriter_impl.h" #include "content/child/worker_task_runner.h" diff --git a/content/child/fling_animator_impl_android.h b/content/child/fling_animator_impl_android.h index ea33ba291962e..b9de497a78892 100644 --- a/content/child/fling_animator_impl_android.h +++ b/content/child/fling_animator_impl_android.h @@ -42,6 +42,6 @@ class FlingAnimatorImpl : public blink::WebGestureCurve { DISALLOW_COPY_AND_ASSIGN(FlingAnimatorImpl); }; -} // namespace webkit_glue +} // namespace content #endif // CONTENT_CHILD_FLING_ANIMATOR_IMPL_ANDROID_H_ diff --git a/content/child/npapi/npobject_stub.cc b/content/child/npapi/npobject_stub.cc index 298e3cde7a087..fb910d9b41259 100644 --- a/content/child/npapi/npobject_stub.cc +++ b/content/child/npapi/npobject_stub.cc @@ -257,7 +257,7 @@ void NPObjectStub::OnSetProperty(const NPIdentifier_Param& name, if (npobject_->_class->setProperty) { #if defined(OS_WIN) static base::FilePath plugin_path = - CommandLine::ForCurrentProcess()->GetSwitchValuePath( + base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kPluginPath); static std::wstring filename = base::StringToLowerASCII( plugin_path.BaseName().value()); diff --git a/content/child/npapi/plugin_url_fetcher.cc b/content/child/npapi/plugin_url_fetcher.cc index 48c685e353f33..d339033a37ceb 100644 --- a/content/child/npapi/plugin_url_fetcher.cc +++ b/content/child/npapi/plugin_url_fetcher.cc @@ -23,7 +23,7 @@ #include "net/base/load_flags.h" #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" -#include "net/url_request/url_request.h" +#include "net/url_request/redirect_info.h" #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" #include "third_party/WebKit/public/platform/WebURLResponse.h" #include "webkit/child/resource_loader_bridge.h" @@ -95,7 +95,6 @@ PluginURLFetcher::PluginURLFetcher(PluginStreamUrl* plugin_stream, : plugin_stream_(plugin_stream), url_(url), first_party_for_cookies_(first_party_for_cookies), - method_(method), referrer_(referrer), notify_redirects_(notify_redirects), is_plugin_src_load_(is_plugin_src_load), @@ -191,8 +190,7 @@ void PluginURLFetcher::OnUploadProgress(uint64 position, uint64 size) { } bool PluginURLFetcher::OnReceivedRedirect( - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) { if (!plugin_stream_) return false; @@ -206,42 +204,28 @@ bool PluginURLFetcher::OnReceivedRedirect( // initiated by plug-ins. if (is_plugin_src_load_ && !plugin_stream_->instance()->webplugin()->CheckIfRunInsecureContent( - new_url)) { + redirect_info.new_url)) { plugin_stream_->DidFail(resource_id_); // That will delete |this|. return false; } - // It's unfortunate that this logic of when a redirect's method changes is - // in url_request.cc, but weburlloader_impl.cc and this file have to duplicate - // it instead of passing that information. - int response_code; - if (info.headers) { - response_code = info.headers->response_code(); - } else { - // A redirect may have NULL headers if it came from URLRequestRedirectJob. - // - // TODO(davidben): Get the actual response code from the browser. Either - // fake enough of headers to have a response code or pass it down as part of - // https://crbug.com/384609. - response_code = 307; - } - method_ = net::URLRequest::ComputeMethodForRedirect(method_, response_code); GURL old_url = url_; - url_ = new_url; - first_party_for_cookies_ = new_first_party_for_cookies; + url_ = redirect_info.new_url; + first_party_for_cookies_ = redirect_info.new_first_party_for_cookies; // If the plugin does not participate in url redirect notifications then just // block cross origin 307 POST redirects. if (!notify_redirects_) { - if (response_code == 307 && method_ == "POST" && - old_url.GetOrigin() != new_url.GetOrigin()) { + if (redirect_info.status_code == 307 && + redirect_info.new_method == "POST" && + old_url.GetOrigin() != url_.GetOrigin()) { plugin_stream_->DidFail(resource_id_); // That will delete |this|. return false; } } else { // Pause the request while we ask the plugin what to do about the redirect. bridge_->SetDefersLoading(true); - plugin_stream_->WillSendRequest(url_, response_code); + plugin_stream_->WillSendRequest(url_, redirect_info.status_code); } return true; diff --git a/content/child/npapi/plugin_url_fetcher.h b/content/child/npapi/plugin_url_fetcher.h index b1d77160de50c..8dc53f20ca6c7 100644 --- a/content/child/npapi/plugin_url_fetcher.h +++ b/content/child/npapi/plugin_url_fetcher.h @@ -56,8 +56,7 @@ class PluginURLFetcher : public RequestPeer { private: // RequestPeer implementation: virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnReceivedRedirect(const GURL& new_url, - const GURL& new_first_party_for_cookies, + virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) OVERRIDE; virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE; virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE; @@ -76,7 +75,6 @@ class PluginURLFetcher : public RequestPeer { PluginStreamUrl* plugin_stream_; GURL url_; GURL first_party_for_cookies_; - std::string method_; GURL referrer_; bool notify_redirects_; bool is_plugin_src_load_; diff --git a/content/child/request_info.cc b/content/child/request_info.cc index 860b944b59dbf..cd3ca217b74e7 100644 --- a/content/child/request_info.cc +++ b/content/child/request_info.cc @@ -17,6 +17,7 @@ RequestInfo::RequestInfo() routing_id(0), download_to_file(false), has_user_gesture(false), + enable_load_timing(false), extra_data(NULL) { } diff --git a/content/child/request_info.h b/content/child/request_info.h index f1bfa9791523a..4f43e36ab9003 100644 --- a/content/child/request_info.h +++ b/content/child/request_info.h @@ -75,6 +75,10 @@ struct CONTENT_EXPORT RequestInfo { // True if the request was user initiated. bool has_user_gesture; + // TODO(mmenke): Investigate if enable_load_timing is safe to remove. + // True if load timing data should be collected for the request. + bool enable_load_timing; + // Extra data associated with this request. We do not own this pointer. blink::WebURLRequest::ExtraData* extra_data; diff --git a/content/child/resource_dispatcher.cc b/content/child/resource_dispatcher.cc index 59dafe1bfacbd..c8fe61af5c2a0 100644 --- a/content/child/resource_dispatcher.cc +++ b/content/child/resource_dispatcher.cc @@ -128,6 +128,7 @@ IPCResourceLoaderBridge::IPCResourceLoaderBridge( request_.appcache_host_id = request_info.appcache_host_id; request_.download_to_file = request_info.download_to_file; request_.has_user_gesture = request_info.has_user_gesture; + request_.enable_load_timing = request_info.enable_load_timing; const RequestExtraData kEmptyData; const RequestExtraData* extra_data; @@ -498,8 +499,7 @@ void ResourceDispatcher::OnDownloadedData(int request_id, void ResourceDispatcher::OnReceivedRedirect( int request_id, - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const ResourceResponseHead& response_head) { TRACE_EVENT0("loader", "ResourceDispatcher::OnReceivedRedirect"); PendingRequestInfo* request_info = GetPendingRequestInfo(request_id); @@ -509,8 +509,8 @@ void ResourceDispatcher::OnReceivedRedirect( ResourceResponseInfo renderer_response_info; ToResourceResponseInfo(*request_info, response_head, &renderer_response_info); - if (request_info->peer->OnReceivedRedirect( - new_url, new_first_party_for_cookies, renderer_response_info)) { + if (request_info->peer->OnReceivedRedirect(redirect_info, + renderer_response_info)) { // Double-check if the request is still around. The call above could // potentially remove it. request_info = GetPendingRequestInfo(request_id); @@ -518,7 +518,7 @@ void ResourceDispatcher::OnReceivedRedirect( return; // We update the response_url here so that we can send it to // SiteIsolationPolicy later when OnReceivedResponse is called. - request_info->response_url = new_url; + request_info->response_url = redirect_info.new_url; request_info->pending_redirect_message.reset( new ResourceHostMsg_FollowRedirect(request_id)); if (!request_info->is_deferred) { diff --git a/content/child/resource_dispatcher.h b/content/child/resource_dispatcher.h index 5fa68e95dc184..ad1c79d27cfaa 100644 --- a/content/child/resource_dispatcher.h +++ b/content/child/resource_dispatcher.h @@ -28,6 +28,10 @@ namespace blink { class WebThreadedDataReceiver; } +namespace net { +struct RedirectInfo; +} + namespace webkit_glue { class ResourceLoaderBridge; } @@ -156,8 +160,7 @@ class CONTENT_EXPORT ResourceDispatcher : public IPC::Listener { void OnReceivedResponse(int request_id, const ResourceResponseHead&); void OnReceivedCachedMetadata(int request_id, const std::vector& data); void OnReceivedRedirect(int request_id, - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const ResourceResponseHead& response_head); void OnSetDataBuffer(int request_id, base::SharedMemoryHandle shm_handle, diff --git a/content/child/resource_dispatcher_unittest.cc b/content/child/resource_dispatcher_unittest.cc index 4e95428c7da51..d878e376b1632 100644 --- a/content/child/resource_dispatcher_unittest.cc +++ b/content/child/resource_dispatcher_unittest.cc @@ -59,8 +59,7 @@ class TestRequestPeer : public RequestPeer { virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE { } - virtual bool OnReceivedRedirect(const GURL& new_url, - const GURL& new_first_party_for_cookies, + virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) OVERRIDE { ++seen_redirects_; if (defer_on_redirect_) @@ -252,9 +251,13 @@ class ResourceDispatcherTest : public testing::Test, public IPC::Sender { std::replace(raw_headers.begin(), raw_headers.end(), '\n', '\0'); head.headers = new net::HttpResponseHeaders(raw_headers); head.error_code = net::OK; + net::RedirectInfo redirect_info; + redirect_info.status_code = 302; + redirect_info.new_method = "GET"; + redirect_info.new_url = GURL(kTestPageUrl); + redirect_info.new_first_party_for_cookies = GURL(kTestPageUrl); EXPECT_EQ(true, dispatcher_.OnMessageReceived( - ResourceMsg_ReceivedRedirect(request_id, GURL(kTestPageUrl), - GURL(kTestPageUrl), head))); + ResourceMsg_ReceivedRedirect(request_id, redirect_info, head))); } void NotifyReceivedResponse(int request_id) { @@ -722,8 +725,7 @@ class TimeConversionTest : public ResourceDispatcherTest, virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE { } - virtual bool OnReceivedRedirect(const GURL& new_url, - const GURL& new_first_party_for_cookies, + virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) OVERRIDE { return true; } diff --git a/content/child/runtime_features.cc b/content/child/runtime_features.cc index 29820b90ff5b6..6a779e0d606aa 100644 --- a/content/child/runtime_features.cc +++ b/content/child/runtime_features.cc @@ -15,6 +15,8 @@ #include "base/android/build_info.h" #include "base/metrics/field_trial.h" #include "media/base/android/media_codec_bridge.h" +#elif defined(OS_WIN) +#include "base/win/windows_version.h" #endif using blink::WebRuntimeFeatures; @@ -44,8 +46,8 @@ static void SetRuntimeFeatureDefaultsForPlatform() { base::android::BuildInfo::GetInstance()->sdk_int() >= 16); // Android does not have support for PagePopup WebRuntimeFeatures::enablePagePopup(false); - // Android does not yet support the Web Notification API. crbug.com/115320 - WebRuntimeFeatures::enableNotifications(false); + // Crosswalk supports the Web Notification API on Android. + WebRuntimeFeatures::enableNotifications(true); // Android does not yet support SharedWorker. crbug.com/154571 WebRuntimeFeatures::enableSharedWorker(false); // Android does not yet support NavigatorContentUtils. @@ -67,10 +69,23 @@ static void SetRuntimeFeatureDefaultsForPlatform() { // Only Android, ChromeOS, and IOS support NetInfo right now. WebRuntimeFeatures::enableNetworkInformation(false); #endif + +#if defined(OS_WIN) + // Screen Orientation API is currently broken on Windows 8 Metro mode and + // until we can find how to disable it only for Blink instances running in a + // renderer process in Metro, we need to disable the API altogether for Win8. + // See http://crbug.com/400846 + if (base::win::OSInfo::GetInstance()->version() >= base::win::VERSION_WIN8) + WebRuntimeFeatures::enableScreenOrientation(false); +#endif // OS_WIN + +#if defined(OS_TIZEN) + WebRuntimeFeatures::enableOrientationEvent(true); +#endif // defined(OS_TIZEN) } void SetRuntimeFeaturesDefaultsAndUpdateFromArgs( - const CommandLine& command_line) { + const base::CommandLine& command_line) { if (command_line.HasSwitch(switches::kEnableExperimentalWebPlatformFeatures)) WebRuntimeFeatures::enableExperimentalFeatures(true); diff --git a/content/child/service_worker/service_worker_dispatcher.cc b/content/child/service_worker/service_worker_dispatcher.cc index 3b9bf4997e57b..255c4bf518e96 100644 --- a/content/child/service_worker/service_worker_dispatcher.cc +++ b/content/child/service_worker/service_worker_dispatcher.cc @@ -10,6 +10,7 @@ #include "content/child/child_thread.h" #include "content/child/service_worker/service_worker_handle_reference.h" #include "content/child/service_worker/service_worker_provider_context.h" +#include "content/child/service_worker/service_worker_registration_handle_reference.h" #include "content/child/service_worker/web_service_worker_impl.h" #include "content/child/service_worker/web_service_worker_registration_impl.h" #include "content/child/thread_safe_sender.h" @@ -59,12 +60,8 @@ void ServiceWorkerDispatcher::OnMessageReceived(const IPC::Message& msg) { OnRegistrationError) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerStateChanged, OnServiceWorkerStateChanged) - IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetInstallingServiceWorker, - OnSetInstallingServiceWorker) - IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetWaitingServiceWorker, - OnSetWaitingServiceWorker) - IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetActiveServiceWorker, - OnSetActiveServiceWorker) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetVersionAttributes, + OnSetVersionAttributes) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetControllerServiceWorker, OnSetControllerServiceWorker) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_MessageToDocument, @@ -206,9 +203,43 @@ WebServiceWorkerImpl* ServiceWorkerDispatcher::GetServiceWorker( return new WebServiceWorkerImpl(handle_ref.Pass(), thread_safe_sender_); } +WebServiceWorkerRegistrationImpl* +ServiceWorkerDispatcher::GetServiceWorkerRegistration( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + bool adopt_handle) { + if (registration_handle_id == kInvalidServiceWorkerRegistrationHandleId) + return NULL; + + RegistrationObjectMap::iterator existing_registration = + registrations_.find(registration_handle_id); + + if (existing_registration != registrations_.end()) { + if (adopt_handle) { + // We are instructed to adopt a handle but we already have one, so + // adopt and destroy a handle ref. + ServiceWorkerRegistrationHandleReference::Adopt( + registration_handle_id, info, thread_safe_sender_); + } + return existing_registration->second; + } + + scoped_ptr handle_ref = + adopt_handle + ? ServiceWorkerRegistrationHandleReference::Adopt( + registration_handle_id, info, thread_safe_sender_) + : ServiceWorkerRegistrationHandleReference::Create( + registration_handle_id, info, thread_safe_sender_); + + // WebServiceWorkerRegistrationImpl constructor calls + // AddServiceWorkerRegistration. + return new WebServiceWorkerRegistrationImpl(handle_ref.Pass()); +} + void ServiceWorkerDispatcher::OnRegistered( int thread_id, int request_id, + int registration_handle_id, const ServiceWorkerObjectInfo& info) { WebServiceWorkerRegistrationCallbacks* callbacks = pending_callbacks_.Lookup(request_id); @@ -218,8 +249,14 @@ void ServiceWorkerDispatcher::OnRegistered( #ifdef DISABLE_SERVICE_WORKER_REGISTRATION callbacks->onSuccess(GetServiceWorker(info, true)); + // We should adopt and destroy an unused handle ref. + ServiceWorkerRegistrationHandleReference::Adopt( + registration_handle_id, info, thread_safe_sender_); #else - callbacks->onSuccess(new WebServiceWorkerRegistrationImpl(info)); + callbacks->onSuccess(GetServiceWorkerRegistration( + registration_handle_id, info, true)); + // We should adopt and destroy an unused handle ref. + ServiceWorkerHandleReference::Adopt(info, thread_safe_sender_); #endif pending_callbacks_.Remove(request_id); } @@ -267,9 +304,33 @@ void ServiceWorkerDispatcher::OnServiceWorkerStateChanged( provider->second->OnServiceWorkerStateChanged(handle_id, state); } -void ServiceWorkerDispatcher::OnSetInstallingServiceWorker( +void ServiceWorkerDispatcher::OnSetVersionAttributes( int thread_id, int provider_id, + int registration_handle_id, + int changed_mask, + const ServiceWorkerVersionAttributes& attributes) { + ChangedVersionAttributesMask mask(changed_mask); + if (mask.installing_changed()) { + SetInstallingServiceWorker(provider_id, + registration_handle_id, + attributes.installing); + } + if (mask.waiting_changed()) { + SetWaitingServiceWorker(provider_id, + registration_handle_id, + attributes.waiting); + } + if (mask.active_changed()) { + SetActiveServiceWorker(provider_id, + registration_handle_id, + attributes.active); + } +} + +void ServiceWorkerDispatcher::SetInstallingServiceWorker( + int provider_id, + int registration_handle_id, const ServiceWorkerObjectInfo& info) { ProviderContextMap::iterator provider = provider_contexts_.find(provider_id); if (provider != provider_contexts_.end()) { @@ -287,16 +348,26 @@ void ServiceWorkerDispatcher::OnSetInstallingServiceWorker( worker_to_provider_[info.handle_id] = provider->second; } +#ifdef DISABLE_SERVICE_WORKER_REGISTRATION ScriptClientMap::iterator found = script_clients_.find(provider_id); if (found != script_clients_.end()) { // Populate the .installing field with the new worker object. found->second->setInstalling(GetServiceWorker(info, false)); } +#else + RegistrationObjectMap::iterator found = + registrations_.find(registration_handle_id); + if (found != registrations_.end()) { + found->second->setInstalling(GetServiceWorker(info, false)); + if (info.handle_id != kInvalidServiceWorkerHandleId) + found->second->OnUpdateFound(); + } +#endif } -void ServiceWorkerDispatcher::OnSetWaitingServiceWorker( - int thread_id, +void ServiceWorkerDispatcher::SetWaitingServiceWorker( int provider_id, + int registration_handle_id, const ServiceWorkerObjectInfo& info) { ProviderContextMap::iterator provider = provider_contexts_.find(provider_id); if (provider != provider_contexts_.end()) { @@ -314,16 +385,23 @@ void ServiceWorkerDispatcher::OnSetWaitingServiceWorker( worker_to_provider_[info.handle_id] = provider->second; } +#ifdef DISABLE_SERVICE_WORKER_REGISTRATION ScriptClientMap::iterator found = script_clients_.find(provider_id); if (found != script_clients_.end()) { // Populate the .waiting field with the new worker object. found->second->setWaiting(GetServiceWorker(info, false)); } +#else + RegistrationObjectMap::iterator found = + registrations_.find(registration_handle_id); + if (found != registrations_.end()) + found->second->setWaiting(GetServiceWorker(info, false)); +#endif } -void ServiceWorkerDispatcher::OnSetActiveServiceWorker( - int thread_id, +void ServiceWorkerDispatcher::SetActiveServiceWorker( int provider_id, + int registration_handle_id, const ServiceWorkerObjectInfo& info) { ProviderContextMap::iterator provider = provider_contexts_.find(provider_id); if (provider != provider_contexts_.end()) { @@ -341,11 +419,18 @@ void ServiceWorkerDispatcher::OnSetActiveServiceWorker( worker_to_provider_[info.handle_id] = provider->second; } +#ifdef DISABLE_SERVICE_WORKER_REGISTRATION ScriptClientMap::iterator found = script_clients_.find(provider_id); if (found != script_clients_.end()) { // Populate the .active field with the new worker object. found->second->setActive(GetServiceWorker(info, false)); } +#else + RegistrationObjectMap::iterator found = + registrations_.find(registration_handle_id); + if (found != registrations_.end()) + found->second->setActive(GetServiceWorker(info, false)); +#endif } void ServiceWorkerDispatcher::OnSetControllerServiceWorker( @@ -406,4 +491,17 @@ void ServiceWorkerDispatcher::RemoveServiceWorker(int handle_id) { service_workers_.erase(handle_id); } +void ServiceWorkerDispatcher::AddServiceWorkerRegistration( + int registration_handle_id, + WebServiceWorkerRegistrationImpl* registration) { + DCHECK(!ContainsKey(registrations_, registration_handle_id)); + registrations_[registration_handle_id] = registration; +} + +void ServiceWorkerDispatcher::RemoveServiceWorkerRegistration( + int registration_handle_id) { + DCHECK(ContainsKey(registrations_, registration_handle_id)); + registrations_.erase(registration_handle_id); +} + } // namespace content diff --git a/content/child/service_worker/service_worker_dispatcher.h b/content/child/service_worker/service_worker_dispatcher.h index 6b591be404ea4..877b8e14b1c3b 100644 --- a/content/child/service_worker/service_worker_dispatcher.h +++ b/content/child/service_worker/service_worker_dispatcher.h @@ -29,10 +29,12 @@ class Message; namespace content { class ServiceWorkerMessageFilter; -struct ServiceWorkerObjectInfo; class ServiceWorkerProviderContext; class ThreadSafeSender; class WebServiceWorkerImpl; +class WebServiceWorkerRegistrationImpl; +struct ServiceWorkerObjectInfo; +struct ServiceWorkerVersionAttributes; // This class manages communication with the browser process about // registration of the service worker, exposed to renderer and worker @@ -86,8 +88,19 @@ class ServiceWorkerDispatcher : public WorkerTaskRunner::Observer { // WebServiceWorkerImpl, in which case ownership is transferred to // the caller who must bounce it to a method that will associate it // with a WebCore::ServiceWorker. - WebServiceWorkerImpl* GetServiceWorker(const ServiceWorkerObjectInfo&, - bool adopt_handle); + WebServiceWorkerImpl* GetServiceWorker( + const ServiceWorkerObjectInfo& info, + bool adopt_handle); + + // If an existing WebServiceWorkerRegistrationImpl exists for the + // registration, it is returned; otherwise a WebServiceWorkerRegistrationImpl + // is created and its ownership is transferred to the caller. If + // |adopt_handle| is true, a ServiceWorkerRegistrationHandleReference will be + // adopted for the specified registration. + WebServiceWorkerRegistrationImpl* GetServiceWorkerRegistration( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + bool adopt_handle); // |thread_safe_sender| needs to be passed in because if the call leads to // construction it will be needed. @@ -105,14 +118,18 @@ class ServiceWorkerDispatcher : public WorkerTaskRunner::Observer { typedef std::map ProviderContextMap; typedef std::map WorkerObjectMap; typedef std::map WorkerToProviderMap; + typedef std::map + RegistrationObjectMap; friend class WebServiceWorkerImpl; + friend class WebServiceWorkerRegistrationImpl; // WorkerTaskRunner::Observer implementation. virtual void OnWorkerRunLoopStopped() OVERRIDE; void OnRegistered(int thread_id, int request_id, + int registration_handle_id, const ServiceWorkerObjectInfo& info); void OnUnregistered(int thread_id, int request_id); @@ -123,15 +140,11 @@ class ServiceWorkerDispatcher : public WorkerTaskRunner::Observer { void OnServiceWorkerStateChanged(int thread_id, int handle_id, blink::WebServiceWorkerState state); - void OnSetInstallingServiceWorker(int thread_id, - int provider_id, - const ServiceWorkerObjectInfo& info); - void OnSetWaitingServiceWorker(int thread_id, - int provider_id, - const ServiceWorkerObjectInfo& info); - void OnSetActiveServiceWorker(int thread_id, - int provider_id, - const ServiceWorkerObjectInfo& info); + void OnSetVersionAttributes(int thread_id, + int provider_id, + int registration_handle_id, + int changed_mask, + const ServiceWorkerVersionAttributes& attributes); void OnSetControllerServiceWorker(int thread_id, int provider_id, const ServiceWorkerObjectInfo& info); @@ -141,14 +154,35 @@ class ServiceWorkerDispatcher : public WorkerTaskRunner::Observer { const std::vector& sent_message_port_ids, const std::vector& new_routing_ids); + void SetInstallingServiceWorker( + int provider_id, + int registration_handle_id, + const ServiceWorkerObjectInfo& info); + void SetWaitingServiceWorker( + int provider_id, + int registration_handle_id, + const ServiceWorkerObjectInfo& info); + void SetActiveServiceWorker( + int provider_id, + int registration_handle_id, + const ServiceWorkerObjectInfo& info); + // Keeps map from handle_id to ServiceWorker object. void AddServiceWorker(int handle_id, WebServiceWorkerImpl* worker); void RemoveServiceWorker(int handle_id); + // Keeps map from registration_handle_id to ServiceWorkerRegistration object. + void AddServiceWorkerRegistration( + int registration_handle_id, + WebServiceWorkerRegistrationImpl* registration); + void RemoveServiceWorkerRegistration( + int registration_handle_id); + CallbackMap pending_callbacks_; ScriptClientMap script_clients_; ProviderContextMap provider_contexts_; WorkerObjectMap service_workers_; + RegistrationObjectMap registrations_; // A map for ServiceWorkers that are associated to a particular document // (e.g. as .current). diff --git a/content/child/service_worker/service_worker_message_filter.cc b/content/child/service_worker/service_worker_message_filter.cc index 2b95d8545bd60..0033207df91df 100644 --- a/content/child/service_worker/service_worker_message_filter.cc +++ b/content/child/service_worker/service_worker_message_filter.cc @@ -9,6 +9,7 @@ #include "content/child/thread_safe_sender.h" #include "content/child/worker_thread_task_runner.h" #include "content/common/service_worker/service_worker_messages.h" +#include "content/common/service_worker/service_worker_types.h" #include "ipc/ipc_message_macros.h" namespace content { @@ -18,7 +19,7 @@ namespace { // Sends a ServiceWorkerObjectDestroyed message to the browser so it can delete // the ServiceWorker handle. void SendServiceWorkerObjectDestroyed( - scoped_refptr sender, + ThreadSafeSender* sender, int handle_id) { if (handle_id == kInvalidServiceWorkerHandleId) return; @@ -26,6 +27,15 @@ void SendServiceWorkerObjectDestroyed( new ServiceWorkerHostMsg_DecrementServiceWorkerRefCount(handle_id)); } +void SendRegistrationObjectDestroyed( + ThreadSafeSender* sender, + int handle_id) { + if (handle_id == kInvalidServiceWorkerRegistrationHandleId) + return; + sender->Send( + new ServiceWorkerHostMsg_DecrementRegistrationRefCount(handle_id)); +} + } // namespace ServiceWorkerMessageFilter::ServiceWorkerMessageFilter(ThreadSafeSender* sender) @@ -61,21 +71,38 @@ void ServiceWorkerMessageFilter::OnStaleMessageReceived( IPC_BEGIN_MESSAGE_MAP(ServiceWorkerMessageFilter, msg) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_ServiceWorkerRegistered, OnStaleRegistered) - IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetWaitingServiceWorker, - OnStaleSetServiceWorker) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetVersionAttributes, + OnStaleSetVersionAttributes) IPC_MESSAGE_HANDLER(ServiceWorkerMsg_SetControllerServiceWorker, - OnStaleSetServiceWorker) + OnStaleSetControllerServiceWorker) IPC_END_MESSAGE_MAP() } void ServiceWorkerMessageFilter::OnStaleRegistered( int thread_id, int request_id, + int registration_handle_id, const ServiceWorkerObjectInfo& info) { SendServiceWorkerObjectDestroyed(thread_safe_sender_, info.handle_id); + SendRegistrationObjectDestroyed(thread_safe_sender_, registration_handle_id); +} + +void ServiceWorkerMessageFilter::OnStaleSetVersionAttributes( + int thread_id, + int provider_id, + int registration_handle_id, + int changed_mask, + const ServiceWorkerVersionAttributes& attributes) { + SendServiceWorkerObjectDestroyed(thread_safe_sender_, + attributes.installing.handle_id); + SendServiceWorkerObjectDestroyed(thread_safe_sender_, + attributes.waiting.handle_id); + SendServiceWorkerObjectDestroyed(thread_safe_sender_, + attributes.active.handle_id); + SendRegistrationObjectDestroyed(thread_safe_sender_, registration_handle_id); } -void ServiceWorkerMessageFilter::OnStaleSetServiceWorker( +void ServiceWorkerMessageFilter::OnStaleSetControllerServiceWorker( int thread_id, int provider_id, const ServiceWorkerObjectInfo& info) { diff --git a/content/child/service_worker/service_worker_message_filter.h b/content/child/service_worker/service_worker_message_filter.h index d9b4084854997..d1e59faae1e2a 100644 --- a/content/child/service_worker/service_worker_message_filter.h +++ b/content/child/service_worker/service_worker_message_filter.h @@ -14,8 +14,9 @@ class MessageLoopProxy; namespace content { -struct ServiceWorkerObjectInfo; class ThreadSafeSender; +struct ServiceWorkerObjectInfo; +struct ServiceWorkerVersionAttributes; class CONTENT_EXPORT ServiceWorkerMessageFilter : public NON_EXPORTED_BASE(ChildMessageFilter) { @@ -33,12 +34,21 @@ class CONTENT_EXPORT ServiceWorkerMessageFilter virtual void OnStaleMessageReceived(const IPC::Message& msg) OVERRIDE; // Message handlers for stale messages. - void OnStaleRegistered(int thread_id, - int request_id, - const ServiceWorkerObjectInfo& info); - void OnStaleSetServiceWorker(int thread_id, - int provider_id, - const ServiceWorkerObjectInfo& info); + void OnStaleRegistered( + int thread_id, + int request_id, + int registration_handle_id, + const ServiceWorkerObjectInfo& info); + void OnStaleSetVersionAttributes( + int thread_id, + int provider_id, + int registration_handle_id, + int changed_mask, + const ServiceWorkerVersionAttributes& attributes); + void OnStaleSetControllerServiceWorker( + int thread_id, + int provider_id, + const ServiceWorkerObjectInfo& info); scoped_refptr main_thread_loop_proxy_; scoped_refptr thread_safe_sender_; diff --git a/content/child/service_worker/service_worker_registration_handle_reference.cc b/content/child/service_worker/service_worker_registration_handle_reference.cc new file mode 100644 index 0000000000000..3b72a35c99838 --- /dev/null +++ b/content/child/service_worker/service_worker_registration_handle_reference.cc @@ -0,0 +1,54 @@ +// 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 "content/child/service_worker/service_worker_registration_handle_reference.h" + +#include "content/child/thread_safe_sender.h" +#include "content/common/service_worker/service_worker_messages.h" +#include "content/common/service_worker/service_worker_types.h" + +namespace content { + +scoped_ptr +ServiceWorkerRegistrationHandleReference::Create( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + ThreadSafeSender* sender) { + return make_scoped_ptr(new ServiceWorkerRegistrationHandleReference( + registration_handle_id, info, sender, true)); +} + +scoped_ptr +ServiceWorkerRegistrationHandleReference::Adopt( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + ThreadSafeSender* sender) { + return make_scoped_ptr(new ServiceWorkerRegistrationHandleReference( + registration_handle_id, info, sender, false)); +} + +ServiceWorkerRegistrationHandleReference:: +ServiceWorkerRegistrationHandleReference( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + ThreadSafeSender* sender, + bool increment_ref_in_ctor) + : handle_id_(registration_handle_id), + scope_(info.scope), + sender_(sender) { + DCHECK_NE(kInvalidServiceWorkerRegistrationHandleId, handle_id_); + DCHECK(sender_); + if (increment_ref_in_ctor) + return; + sender_->Send( + new ServiceWorkerHostMsg_IncrementRegistrationRefCount(handle_id_)); +} + +ServiceWorkerRegistrationHandleReference:: +~ServiceWorkerRegistrationHandleReference() { + sender_->Send( + new ServiceWorkerHostMsg_DecrementRegistrationRefCount(handle_id_)); +} + +} // namespace content diff --git a/content/child/service_worker/service_worker_registration_handle_reference.h b/content/child/service_worker/service_worker_registration_handle_reference.h new file mode 100644 index 0000000000000..fdfb1095cc881 --- /dev/null +++ b/content/child/service_worker/service_worker_registration_handle_reference.h @@ -0,0 +1,53 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_HANDLE_REFERENCE_H_ +#define CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_HANDLE_REFERENCE_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "url/gurl.h" + +namespace content { + +class ThreadSafeSender; +struct ServiceWorkerObjectInfo; + +class ServiceWorkerRegistrationHandleReference { + public: + // Creates a new ServiceWorkerRegistrationHandleReference and increments + // ref-count. + static scoped_ptr Create( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + ThreadSafeSender* sender); + + // Creates a new ServiceWorkerRegistrationHandleReference by adopting a + // ref-count. + static scoped_ptr Adopt( + int registration_handle_id, + const ServiceWorkerObjectInfo& info, + ThreadSafeSender* sender); + + ~ServiceWorkerRegistrationHandleReference(); + + int handle_id() const { return handle_id_; } + GURL scope() const { return scope_; } + + private: + ServiceWorkerRegistrationHandleReference(int registration_handle_id, + const ServiceWorkerObjectInfo& info, + ThreadSafeSender* sender, + bool increment_ref_in_ctor); + + const int handle_id_; + const GURL scope_; + scoped_refptr sender_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRegistrationHandleReference); +}; + +} // namespace content + +#endif // CONTENT_CHILD_SERVICE_WORKER_SERVICE_WORKER_REGISTRATION_HANDLE_REFERENCE_H_ diff --git a/content/child/service_worker/web_service_worker_registration_impl.cc b/content/child/service_worker/web_service_worker_registration_impl.cc index 66ddb707e8004..8bb7ed1141727 100644 --- a/content/child/service_worker/web_service_worker_registration_impl.cc +++ b/content/child/service_worker/web_service_worker_registration_impl.cc @@ -4,20 +4,68 @@ #include "content/child/service_worker/web_service_worker_registration_impl.h" +#include "content/child/service_worker/service_worker_dispatcher.h" +#include "content/child/service_worker/service_worker_registration_handle_reference.h" #include "content/common/service_worker/service_worker_types.h" +#include "third_party/WebKit/public/platform/WebServiceWorkerRegistrationProxy.h" namespace content { WebServiceWorkerRegistrationImpl::WebServiceWorkerRegistrationImpl( - const ServiceWorkerObjectInfo& info) - : scope_(info.scope) { + scoped_ptr handle_ref) + : handle_ref_(handle_ref.Pass()), + proxy_(NULL) { + DCHECK(handle_ref_); + DCHECK_NE(kInvalidServiceWorkerRegistrationHandleId, + handle_ref_->handle_id()); + ServiceWorkerDispatcher* dispatcher = + ServiceWorkerDispatcher::GetThreadSpecificInstance(); + DCHECK(dispatcher); + dispatcher->AddServiceWorkerRegistration(handle_ref_->handle_id(), this); } WebServiceWorkerRegistrationImpl::~WebServiceWorkerRegistrationImpl() { + ServiceWorkerDispatcher* dispatcher = + ServiceWorkerDispatcher::GetThreadSpecificInstance(); + if (dispatcher) + dispatcher->RemoveServiceWorkerRegistration(handle_ref_->handle_id()); +} + +void WebServiceWorkerRegistrationImpl::OnUpdateFound() { + DCHECK(proxy_); + proxy_->dispatchUpdateFoundEvent(); +} + +void WebServiceWorkerRegistrationImpl::setProxy( + blink::WebServiceWorkerRegistrationProxy* proxy) { + proxy_ = proxy; +} + +blink::WebServiceWorkerRegistrationProxy* +WebServiceWorkerRegistrationImpl::proxy() { + return proxy_; +} + +void WebServiceWorkerRegistrationImpl::setInstalling( + blink::WebServiceWorker* service_worker) { + DCHECK(proxy_); + proxy_->setInstalling(service_worker); +} + +void WebServiceWorkerRegistrationImpl::setWaiting( + blink::WebServiceWorker* service_worker) { + DCHECK(proxy_); + proxy_->setWaiting(service_worker); +} + +void WebServiceWorkerRegistrationImpl::setActive( + blink::WebServiceWorker* service_worker) { + DCHECK(proxy_); + proxy_->setActive(service_worker); } blink::WebURL WebServiceWorkerRegistrationImpl::scope() const { - return scope_; + return handle_ref_->scope(); } } // namespace content diff --git a/content/child/service_worker/web_service_worker_registration_impl.h b/content/child/service_worker/web_service_worker_registration_impl.h index 1fd65ddbf63ff..a11e3581fc4a3 100644 --- a/content/child/service_worker/web_service_worker_registration_impl.h +++ b/content/child/service_worker/web_service_worker_registration_impl.h @@ -8,21 +8,37 @@ #include "base/compiler_specific.h" #include "third_party/WebKit/public/platform/WebServiceWorkerRegistration.h" +namespace blink { +class WebServiceWorker; +class WebServiceWorkerRegistrationProxy; +} + namespace content { +class ServiceWorkerRegistrationHandleReference; +class ThreadSafeSender; struct ServiceWorkerObjectInfo; class WebServiceWorkerRegistrationImpl : NON_EXPORTED_BASE(public blink::WebServiceWorkerRegistration) { public: explicit WebServiceWorkerRegistrationImpl( - const ServiceWorkerObjectInfo& info); + scoped_ptr handle_ref); virtual ~WebServiceWorkerRegistrationImpl(); + void OnUpdateFound(); + + virtual void setProxy(blink::WebServiceWorkerRegistrationProxy* proxy); + virtual blink::WebServiceWorkerRegistrationProxy* proxy(); + virtual void setInstalling(blink::WebServiceWorker* service_worker); + virtual void setWaiting(blink::WebServiceWorker* service_worker); + virtual void setActive(blink::WebServiceWorker* service_worker); + virtual blink::WebURL scope() const; private: - const GURL scope_; + scoped_ptr handle_ref_; + blink::WebServiceWorkerRegistrationProxy* proxy_; DISALLOW_COPY_AND_ASSIGN(WebServiceWorkerRegistrationImpl); }; diff --git a/content/child/threaded_data_provider.h b/content/child/threaded_data_provider.h index 223153f414e82..55ae060eaa93c 100644 --- a/content/child/threaded_data_provider.h +++ b/content/child/threaded_data_provider.h @@ -22,12 +22,7 @@ namespace IPC { class SyncChannel; } -namespace webkit_glue { -class WebThreadImpl; -} - namespace content { -class ResourceDispatcher; class WebThreadImpl; class ThreadedDataProvider { diff --git a/content/child/web_url_loader_impl.cc b/content/child/web_url_loader_impl.cc index 32b446715375b..ba560b65593df 100644 --- a/content/child/web_url_loader_impl.cc +++ b/content/child/web_url_loader_impl.cc @@ -29,7 +29,7 @@ #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" -#include "net/url_request/url_request.h" +#include "net/url_request/redirect_info.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" #include "third_party/WebKit/public/platform/WebHTTPLoadInfo.h" #include "third_party/WebKit/public/platform/WebURL.h" @@ -223,8 +223,7 @@ class WebURLLoaderImpl::Context : public base::RefCounted, // RequestPeer methods: virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; - virtual bool OnReceivedRedirect(const GURL& new_url, - const GURL& new_first_party_for_cookies, + virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) OVERRIDE; virtual void OnReceivedResponse(const ResourceResponseInfo& info) OVERRIDE; virtual void OnDownloadedData(int len, int encoded_data_length) OVERRIDE; @@ -332,7 +331,7 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, request.httpHeaderField(WebString::fromUTF8("Referer")).latin1()); const std::string& method = request.httpMethod().latin1(); - int load_flags = net::LOAD_NORMAL | net::LOAD_ENABLE_LOAD_TIMING; + int load_flags = net::LOAD_NORMAL; switch (request.cachePolicy()) { case WebURLRequest::ReloadIgnoringCacheData: // Required by LayoutTests/http/tests/misc/refresh-headers.php @@ -384,6 +383,7 @@ void WebURLLoaderImpl::Context::Start(const WebURLRequest& request, request_info.referrer = referrer_url; request_info.headers = flattener.GetBuffer(); request_info.load_flags = load_flags; + request_info.enable_load_timing = true; // requestor_pid only needs to be non-zero if the request originates outside // the render process, so we can use requestorProcessID even for requests // from in-process plugins. @@ -469,8 +469,7 @@ void WebURLLoaderImpl::Context::OnUploadProgress(uint64 position, uint64 size) { } bool WebURLLoaderImpl::Context::OnReceivedRedirect( - const GURL& new_url, - const GURL& new_first_party_for_cookies, + const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) { if (!client_) return false; @@ -481,23 +480,17 @@ bool WebURLLoaderImpl::Context::OnReceivedRedirect( // TODO(darin): We lack sufficient information to construct the actual // request that resulted from the redirect. - WebURLRequest new_request(new_url); - new_request.setFirstPartyForCookies(new_first_party_for_cookies); + WebURLRequest new_request(redirect_info.new_url); + new_request.setFirstPartyForCookies( + redirect_info.new_first_party_for_cookies); new_request.setDownloadToFile(request_.downloadToFile()); - WebString referrer_string = WebString::fromUTF8("Referer"); - WebString referrer = WebSecurityPolicy::generateReferrerHeader( - referrer_policy_, - new_url, - request_.httpHeaderField(referrer_string)); - if (!referrer.isEmpty()) - new_request.setHTTPReferrer(referrer, referrer_policy_); - - std::string method = request_.httpMethod().utf8(); - std::string new_method = net::URLRequest::ComputeMethodForRedirect( - method, response.httpStatusCode()); - new_request.setHTTPMethod(WebString::fromUTF8(new_method)); - if (new_method == method) + new_request.setHTTPReferrer(WebString::fromUTF8(redirect_info.new_referrer), + referrer_policy_); + + std::string old_method = request_.httpMethod().utf8(); + new_request.setHTTPMethod(WebString::fromUTF8(redirect_info.new_method)); + if (redirect_info.new_method == old_method) new_request.setHTTPBody(request_.httpBody()); // Protect from deletion during call to willSendRequest. @@ -507,11 +500,11 @@ bool WebURLLoaderImpl::Context::OnReceivedRedirect( request_ = new_request; // Only follow the redirect if WebKit left the URL unmodified. - if (new_url == GURL(new_request.url())) { + if (redirect_info.new_url == GURL(new_request.url())) { // First-party cookie logic moved from DocumentLoader in Blink to - // CrossSiteResourceHandler in the browser. Assert that Blink didn't try to - // change it to something else. - DCHECK_EQ(new_first_party_for_cookies.spec(), + // net::URLRequest in the browser. Assert that Blink didn't try to change it + // to something else. + DCHECK_EQ(redirect_info.new_first_party_for_cookies.spec(), request_.firstPartyForCookies().string().utf8()); return true; } diff --git a/content/child/web_url_loader_impl_unittest.cc b/content/child/web_url_loader_impl_unittest.cc index 81b6779bcb6d7..0adfe08f12e97 100644 --- a/content/child/web_url_loader_impl_unittest.cc +++ b/content/child/web_url_loader_impl_unittest.cc @@ -15,6 +15,7 @@ #include "net/base/net_errors.h" #include "net/http/http_response_headers.h" #include "net/http/http_util.h" +#include "net/url_request/redirect_info.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/WebKit/public/platform/WebString.h" #include "third_party/WebKit/public/platform/WebURLError.h" @@ -281,7 +282,12 @@ class WebURLLoaderImplTest : public testing::Test { void DoReceiveRedirect() { EXPECT_FALSE(client()->did_receive_redirect()); - peer()->OnReceivedRedirect(GURL(kTestURL), GURL(kTestURL), + net::RedirectInfo redirect_info; + redirect_info.status_code = 302; + redirect_info.new_method = "GET"; + redirect_info.new_url = GURL(kTestURL); + redirect_info.new_first_party_for_cookies = GURL(kTestURL); + peer()->OnReceivedRedirect(redirect_info, content::ResourceResponseInfo()); EXPECT_TRUE(client()->did_receive_redirect()); } diff --git a/content/child/webfileutilities_impl.cc b/content/child/webfileutilities_impl.cc index e2f3e3311046e..89d328b574389 100644 --- a/content/child/webfileutilities_impl.cc +++ b/content/child/webfileutilities_impl.cc @@ -7,7 +7,7 @@ #include "base/file_util.h" #include "base/files/file_path.h" #include "base/logging.h" -#include "content/child/blink_glue.h" +#include "content/child/file_info_util.h" #include "net/base/file_stream.h" #include "net/base/filename_util.h" #include "third_party/WebKit/public/platform/WebFileInfo.h" diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn index 02efe32b17671..152a9f752f4f0 100644 --- a/content/common/BUILD.gn +++ b/content/common/BUILD.gn @@ -49,8 +49,6 @@ source_set("common") { "//cc", "//ipc", "//ipc/mojo", - "//mojo/environment:chromium", - "//mojo/system", # TODO: the dependency on gl_in_process_context should be decoupled from # content and moved to android_webview. See crbug.com/365797. "//gpu/command_buffer/client:gl_in_process_context", @@ -61,7 +59,10 @@ source_set("common") { "//gpu/skia_bindings", "//media", "//media:shared_memory_support", + "//mojo/environment:chromium", "//mojo/public/interfaces/application", + "//mojo/system", + "//sandbox", "//third_party/WebKit/public:blink", "//ui/gl", "//webkit/browser:storage", @@ -95,8 +96,9 @@ source_set("common") { ] deps += [ - "//webkit:resources", "//content:resources", + "//third_party/WebKit/public:resources", + "//webkit/glue/resources", ] libs += [ "QuartzCore.framework" ] } diff --git a/content/common/DEPS b/content/common/DEPS index 418c8e926fbec..651cb80d2a64c 100644 --- a/content/common/DEPS +++ b/content/common/DEPS @@ -29,6 +29,7 @@ include_rules = [ "+third_party/WebKit/public/platform/WebScreenInfo.h", "+third_party/WebKit/public/platform/WebServiceWorkerError.h", "+third_party/WebKit/public/platform/WebServiceWorkerEventResult.h", + "+third_party/WebKit/public/platform/WebServiceWorkerCacheError.h", "+third_party/WebKit/public/platform/WebServiceWorkerState.h", "+third_party/WebKit/public/platform/WebStorageArea.h", "+third_party/WebKit/public/platform/WebString.h", diff --git a/content/common/android/gin_java_bridge_errors.cc b/content/common/android/gin_java_bridge_errors.cc index 57326b38725c3..b2458cd0354f5 100644 --- a/content/common/android/gin_java_bridge_errors.cc +++ b/content/common/android/gin_java_bridge_errors.cc @@ -25,6 +25,8 @@ const char* GinJavaBridgeErrorToString(GinJavaBridgeError error) { case kGinJavaBridgeNonAssignableTypes: return "The type of the object passed to the method is incompatible " "with the type of method's argument"; + case kGinJavaBridgeRenderFrameDeleted: + return "RenderFrame has been deleted"; } NOTREACHED(); return "Unknown error"; diff --git a/content/common/android/gin_java_bridge_errors.h b/content/common/android/gin_java_bridge_errors.h index 37d14cba82486..140201ee7c664 100644 --- a/content/common/android/gin_java_bridge_errors.h +++ b/content/common/android/gin_java_bridge_errors.h @@ -17,6 +17,7 @@ enum GinJavaBridgeError { kGinJavaBridgeAccessToObjectGetClassIsBlocked, kGinJavaBridgeJavaExceptionRaised, kGinJavaBridgeNonAssignableTypes, + kGinJavaBridgeRenderFrameDeleted, }; CONTENT_EXPORT const char* GinJavaBridgeErrorToString(GinJavaBridgeError error); diff --git a/content/common/cc_messages.cc b/content/common/cc_messages.cc index 1e2d7419b3bb2..77ba68b7b95f8 100644 --- a/content/common/cc_messages.cc +++ b/content/common/cc_messages.cc @@ -6,6 +6,7 @@ #include "cc/output/compositor_frame.h" #include "cc/output/filter_operations.h" +#include "cc/quads/largest_draw_quad.h" #include "content/public/common/common_param_traits.h" #include "third_party/skia/include/core/SkData.h" #include "third_party/skia/include/core/SkFlattenableSerialization.h" @@ -388,7 +389,7 @@ static size_t ReserveSizeForRenderPassWrite(const cc::RenderPass& p) { to_reserve += p.quad_list.size() * sizeof(size_t); // The largest quad type, verified by a unit test. - to_reserve += p.quad_list.size() * sizeof(cc::RenderPassDrawQuad); + to_reserve += p.quad_list.size() * sizeof(cc::kLargestDrawQuad); return to_reserve; } diff --git a/content/common/cc_messages.h b/content/common/cc_messages.h index 57aef15d5da75..5a84551e8b02e 100644 --- a/content/common/cc_messages.h +++ b/content/common/cc_messages.h @@ -176,11 +176,10 @@ IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(cc::RenderPassDrawQuad) IPC_STRUCT_TRAITS_PARENT(cc::DrawQuad) IPC_STRUCT_TRAITS_MEMBER(render_pass_id) - IPC_STRUCT_TRAITS_MEMBER(is_replica) IPC_STRUCT_TRAITS_MEMBER(mask_resource_id) - IPC_STRUCT_TRAITS_MEMBER(contents_changed_since_last_frame) IPC_STRUCT_TRAITS_MEMBER(mask_uv_rect) IPC_STRUCT_TRAITS_MEMBER(filters) + IPC_STRUCT_TRAITS_MEMBER(filters_scale) IPC_STRUCT_TRAITS_MEMBER(background_filters) IPC_STRUCT_TRAITS_END() diff --git a/content/common/cc_messages_unittest.cc b/content/common/cc_messages_unittest.cc index 73b1a0836344b..054e079acc9c4 100644 --- a/content/common/cc_messages_unittest.cc +++ b/content/common/cc_messages_unittest.cc @@ -143,10 +143,7 @@ class CCMessagesTest : public testing::Test { void Compare(const RenderPassDrawQuad* a, const RenderPassDrawQuad* b) { EXPECT_EQ(a->render_pass_id, b->render_pass_id); - EXPECT_EQ(a->is_replica, b->is_replica); EXPECT_EQ(a->mask_resource_id, b->mask_resource_id); - EXPECT_EQ(a->contents_changed_since_last_frame, - b->contents_changed_since_last_frame); EXPECT_EQ(a->mask_uv_rect.ToString(), b->mask_uv_rect.ToString()); EXPECT_EQ(a->filters.size(), b->filters.size()); for (size_t i = 0; i < a->filters.size(); ++i) { @@ -158,6 +155,7 @@ class CCMessagesTest : public testing::Test { b->filters.at(i).image_filter()->countInputs()); } } + EXPECT_EQ(a->filters_scale, b->filters_scale); EXPECT_EQ(a->background_filters, b->background_filters); } @@ -242,6 +240,7 @@ TEST_F(CCMessagesTest, AllQuads) { gfx::SizeF arbitrary_sizef1(15.2f, 104.6f); gfx::PointF arbitrary_pointf1(31.4f, 15.9f); gfx::PointF arbitrary_pointf2(26.5f, -35.8f); + gfx::Vector2dF arbitrary_vector2df1(16.2f, -85.1f); float arbitrary_float1 = 0.7f; float arbitrary_float2 = 0.3f; float arbitrary_float3 = 0.9f; @@ -365,11 +364,10 @@ TEST_F(CCMessagesTest, AllQuads) { arbitrary_rect1_inside_rect1, arbitrary_bool1, arbitrary_id, - arbitrary_bool2, arbitrary_resourceid2, - arbitrary_rect1, arbitrary_rectf1, arbitrary_filters1, + arbitrary_vector2df1, arbitrary_filters2); pass_cmp->CopyFromAndAppendRenderPassDrawQuad( renderpass_in, @@ -689,58 +687,6 @@ TEST_F(CCMessagesTest, Resources) { Compare(arbitrary_resource2, frame_out.resource_list[1]); } -TEST_F(CCMessagesTest, LargestQuadType) { - size_t largest = 0; - - bool done = false; - for (int i = 0; !done; ++i) { - switch (static_cast(i)) { - case cc::DrawQuad::CHECKERBOARD: - largest = std::max(largest, sizeof(cc::CheckerboardDrawQuad)); - break; - case cc::DrawQuad::DEBUG_BORDER: - largest = std::max(largest, sizeof(cc::DebugBorderDrawQuad)); - break; - case cc::DrawQuad::IO_SURFACE_CONTENT: - largest = std::max(largest, sizeof(cc::IOSurfaceDrawQuad)); - break; - case cc::DrawQuad::PICTURE_CONTENT: - largest = std::max(largest, sizeof(cc::PictureDrawQuad)); - break; - case cc::DrawQuad::TEXTURE_CONTENT: - largest = std::max(largest, sizeof(cc::TextureDrawQuad)); - break; - case cc::DrawQuad::RENDER_PASS: - largest = std::max(largest, sizeof(cc::RenderPassDrawQuad)); - break; - case cc::DrawQuad::SOLID_COLOR: - largest = std::max(largest, sizeof(cc::SolidColorDrawQuad)); - break; - case cc::DrawQuad::SURFACE_CONTENT: - largest = std::max(largest, sizeof(cc::SurfaceDrawQuad)); - break; - case cc::DrawQuad::TILED_CONTENT: - largest = std::max(largest, sizeof(cc::TileDrawQuad)); - break; - case cc::DrawQuad::STREAM_VIDEO_CONTENT: - largest = std::max(largest, sizeof(cc::StreamVideoDrawQuad)); - break; - case cc::DrawQuad::YUV_VIDEO_CONTENT: - largest = std::max(largest, sizeof(cc::YUVVideoDrawQuad)); - break; - case cc::DrawQuad::INVALID: - break; - default: - done = true; - } - } - - // Verify the largest DrawQuad type is RenderPassDrawQuad. If this ever - // changes, then the ReserveSizeForRenderPassWrite() method needs to be - // updated as well to use the new largest quad. - EXPECT_EQ(sizeof(RenderPassDrawQuad), largest); -} - TEST_F(CCMessagesTest, SoftwareFrameData) { cc::SoftwareFrameData frame_in; frame_in.id = 3; diff --git a/content/common/child_process_host_impl.cc b/content/common/child_process_host_impl.cc index 6fa01994a1418..c4d209e08773d 100644 --- a/content/common/child_process_host_impl.cc +++ b/content/common/child_process_host_impl.cc @@ -93,7 +93,7 @@ ChildProcessHost* ChildProcessHost::Create(ChildProcessHostDelegate* delegate) { base::FilePath ChildProcessHost::GetChildPath(int flags) { base::FilePath child_path; - child_path = CommandLine::ForCurrentProcess()->GetSwitchValuePath( + child_path = base::CommandLine::ForCurrentProcess()->GetSwitchValuePath( switches::kBrowserSubprocessPath); #if defined(OS_LINUX) diff --git a/content/common/content_message_generator.h b/content/common/content_message_generator.h index e0ce7f669a950..00cd3975adae5 100644 --- a/content/common/content_message_generator.h +++ b/content/common/content_message_generator.h @@ -71,3 +71,7 @@ #include "content/common/gin_java_bridge_messages.h" #include "content/common/media/media_player_messages_android.h" #endif // defined(OS_ANDROID) + +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) +#include "xwalk/tizen/common/media/media_player_messages.h" +#endif diff --git a/content/common/content_switches_internal.cc b/content/common/content_switches_internal.cc index 2f0638fbfba7a..76d877224a4ef 100644 --- a/content/common/content_switches_internal.cc +++ b/content/common/content_switches_internal.cc @@ -14,7 +14,8 @@ namespace content { bool IsPinchToZoomEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); // --disable-pinch should always disable pinch if (command_line.HasSwitch(switches::kDisablePinch)) diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h index 72da9eb673e64..9c1c18430df8a 100644 --- a/content/common/frame_messages.h +++ b/content/common/frame_messages.h @@ -287,8 +287,7 @@ IPC_STRUCT_BEGIN(FrameHostMsg_BeginNavigation_Params) // Additional HTTP request headers. IPC_STRUCT_MEMBER(std::string, headers) - // net::URLRequest load flags (net::LOAD_NORMAL | net::LOAD_ENABLE_LOAD_TIMING - // by default). + // net::URLRequest load flags (net::LOAD_NORMAL) by default). IPC_STRUCT_MEMBER(int, load_flags) // Optional resource request body (may be null). @@ -398,6 +397,16 @@ IPC_MESSAGE_ROUTED2(FrameMsg_SetEditableSelectionOffsets, int /* start */, int /* end */) +// Requests a navigation to the supplied markup, in an iframe with sandbox +// attributes. +IPC_MESSAGE_ROUTED1(FrameMsg_SetupTransitionView, + std::string /* markup */) + +// Tells the renderer to hide the elements specified by the supplied CSS +// selector, and activates any exiting-transition stylesheets. +IPC_MESSAGE_ROUTED1(FrameMsg_BeginExitTransition, + std::string /* css_selector */) + // Tells the renderer to reload the frame, optionally ignoring the cache while // doing so. IPC_MESSAGE_ROUTED1(FrameMsg_Reload, @@ -691,9 +700,12 @@ IPC_MESSAGE_ROUTED3(FrameHostMsg_TextSurroundingSelectionResponse, size_t /* endOffset */) // Notifies the browser that the renderer has a pending navigation transition. -IPC_MESSAGE_CONTROL2(FrameHostMsg_SetHasPendingTransitionRequest, +// The string parameters are all UTF8. +IPC_MESSAGE_CONTROL4(FrameHostMsg_AddNavigationTransitionData, int /* render_frame_id */, - bool /* is_transition */) + std::string /* allowed_destination_host_pattern */, + std::string /* selector */, + std::string /* markup */) // Tells the browser to perform a navigation. IPC_MESSAGE_ROUTED1(FrameHostMsg_BeginNavigation, diff --git a/content/common/gpu/client/gl_helper_benchmark.cc b/content/common/gpu/client/gl_helper_benchmark.cc index fc41fedd5ba79..225db42653487 100644 --- a/content/common/gpu/client/gl_helper_benchmark.cc +++ b/content/common/gpu/client/gl_helper_benchmark.cc @@ -296,7 +296,7 @@ TEST_F(GLHelperTest, DISABLED_ScaleTestImage) { // These tests needs to run against a proper GL environment, so we // need to set it up before we can run the tests. int main(int argc, char** argv) { - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); base::TestSuite* suite = new content::ContentTestSuite(argc, argv); #if defined(OS_MACOSX) base::mac::ScopedNSAutoreleasePool pool; diff --git a/content/common/gpu/client/gl_helper_unittest.cc b/content/common/gpu/client/gl_helper_unittest.cc index f2a148e03ab82..e22c3f7966bc6 100644 --- a/content/common/gpu/client/gl_helper_unittest.cc +++ b/content/common/gpu/client/gl_helper_unittest.cc @@ -1698,7 +1698,7 @@ TEST_F(GLHelperTest, CheckOptimizations) { // These tests needs to run against a proper GL environment, so we // need to set it up before we can run the tests. int main(int argc, char** argv) { - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); base::TestSuite* suite = new content::ContentTestSuite(argc, argv); #if defined(OS_MACOSX) base::mac::ScopedNSAutoreleasePool pool; diff --git a/content/common/gpu/client/gpu_video_decode_accelerator_host.cc b/content/common/gpu/client/gpu_video_decode_accelerator_host.cc index 8b714b9e19d34..a7844678001fe 100644 --- a/content/common/gpu/client/gpu_video_decode_accelerator_host.cc +++ b/content/common/gpu/client/gpu_video_decode_accelerator_host.cc @@ -231,11 +231,13 @@ void GpuVideoDecodeAcceleratorHost::OnDismissPictureBuffer( } void GpuVideoDecodeAcceleratorHost::OnPictureReady( - int32 picture_buffer_id, int32 bitstream_buffer_id) { + int32 picture_buffer_id, + int32 bitstream_buffer_id, + const gfx::Rect& visible_rect) { DCHECK(CalledOnValidThread()); if (!client_) return; - media::Picture picture(picture_buffer_id, bitstream_buffer_id); + media::Picture picture(picture_buffer_id, bitstream_buffer_id, visible_rect); client_->PictureReady(picture); } diff --git a/content/common/gpu/client/gpu_video_decode_accelerator_host.h b/content/common/gpu/client/gpu_video_decode_accelerator_host.h index 571bd0c94bfde..e333f6b50ba22 100644 --- a/content/common/gpu/client/gpu_video_decode_accelerator_host.h +++ b/content/common/gpu/client/gpu_video_decode_accelerator_host.h @@ -64,7 +64,9 @@ class GpuVideoDecodeAcceleratorHost const gfx::Size& dimensions, uint32 texture_target); void OnDismissPictureBuffer(int32 picture_buffer_id); - void OnPictureReady(int32 picture_buffer_id, int32 bitstream_buffer_id); + void OnPictureReady(int32 picture_buffer_id, + int32 bitstream_buffer_id, + const gfx::Rect& visible_rect); void OnFlushDone(); void OnResetDone(); void OnNotifyError(uint32 error); diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc index 3bf0eebd8cb67..21ba2a6c198f7 100644 --- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc +++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.cc @@ -185,24 +185,13 @@ bool WebGraphicsContext3DCommandBufferImpl::InitializeCommandBuffer( share_group_command_buffer = share_context->command_buffer_.get(); } + ::gpu::gles2::ContextCreationAttribHelper attribs_for_gles2; + ConvertAttributes(attributes_, &attribs_for_gles2); + attribs_for_gles2.lose_context_when_out_of_memory = + lose_context_when_out_of_memory_; + DCHECK(attribs_for_gles2.buffer_preserved); std::vector attribs; - attribs.push_back(ALPHA_SIZE); - attribs.push_back(attributes_.alpha ? 8 : 0); - attribs.push_back(DEPTH_SIZE); - attribs.push_back(attributes_.depth ? 24 : 0); - attribs.push_back(STENCIL_SIZE); - attribs.push_back(attributes_.stencil ? 8 : 0); - attribs.push_back(SAMPLES); - attribs.push_back(attributes_.antialias ? 4 : 0); - attribs.push_back(SAMPLE_BUFFERS); - attribs.push_back(attributes_.antialias ? 1 : 0); - attribs.push_back(FAIL_IF_MAJOR_PERF_CAVEAT); - attribs.push_back(attributes_.failIfMajorPerformanceCaveat ? 1 : 0); - attribs.push_back(LOSE_CONTEXT_WHEN_OUT_OF_MEMORY); - attribs.push_back(lose_context_when_out_of_memory_ ? 1 : 0); - attribs.push_back(BIND_GENERATES_RESOURCES); - attribs.push_back(0); - attribs.push_back(NONE); + attribs_for_gles2.Serialize(&attribs); // Create a proxy to a command buffer in the GPU process. if (onscreen) { diff --git a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h index 67a4fa2718bc6..9aebaf0ce1fc8 100644 --- a/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h +++ b/content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h @@ -161,30 +161,6 @@ class WebGraphicsContext3DCommandBufferImpl BAD_ATTRIBUTE = 0x3004, CONTEXT_LOST = 0x300E }; - // WebGraphicsContext3DCommandBufferImpl configuration attributes. Those in - // the 16-bit range are the same as used by EGL. Those outside the 16-bit - // range are unique to Chromium. Attributes are matched using a closest fit - // algorithm. - // Changes to this enum should also be copied to - // gpu/command_buffer/common/gles2_cmd_utils.cc and to - // gpu/command_buffer/client/gl_in_process_context.cc - enum Attribute { - ALPHA_SIZE = 0x3021, - BLUE_SIZE = 0x3022, - GREEN_SIZE = 0x3023, - RED_SIZE = 0x3024, - DEPTH_SIZE = 0x3025, - STENCIL_SIZE = 0x3026, - SAMPLES = 0x3031, - SAMPLE_BUFFERS = 0x3032, - HEIGHT = 0x3056, - WIDTH = 0x3057, - NONE = 0x3038, // Attrib list = terminator - SHARE_RESOURCES = 0x10000, - BIND_GENERATES_RESOURCES = 0x10001, - FAIL_IF_MAJOR_PERF_CAVEAT = 0x10002, - LOSE_CONTEXT_WHEN_OUT_OF_MEMORY = 0x10003, - }; // Initialize the underlying GL context. May be called multiple times; second // and subsequent calls are ignored. Must be called from the thread that is diff --git a/content/common/gpu/gpu_channel.cc b/content/common/gpu/gpu_channel.cc index 330e3cfa5d099..0759e5d00f961 100644 --- a/content/common/gpu/gpu_channel.cc +++ b/content/common/gpu/gpu_channel.cc @@ -417,7 +417,8 @@ GpuChannel::GpuChannel(GpuChannelManager* gpu_channel_manager, DCHECK(client_id); channel_id_ = IPC::Channel::GenerateVerifiedChannelID("gpu"); - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); log_messages_ = command_line->HasSwitch(switches::kLogPluginMessages); } diff --git a/content/common/gpu/gpu_command_buffer_stub.cc b/content/common/gpu/gpu_command_buffer_stub.cc index 989e00bc5cfc0..22b90a3d98c33 100644 --- a/content/common/gpu/gpu_command_buffer_stub.cc +++ b/content/common/gpu/gpu_command_buffer_stub.cc @@ -186,14 +186,14 @@ GpuCommandBufferStub::GpuCommandBufferStub( if (share_group) { context_group_ = share_group->context_group_; DCHECK(context_group_->bind_generates_resource() == - attrib_parser.bind_generates_resource_); + attrib_parser.bind_generates_resource); } else { context_group_ = new gpu::gles2::ContextGroup( mailbox_manager, new GpuCommandBufferMemoryTracker(channel), channel_->gpu_channel_manager()->shader_translator_cache(), NULL, - attrib_parser.bind_generates_resource_); + attrib_parser.bind_generates_resource); } use_virtualized_gl_context_ |= diff --git a/content/common/gpu/gpu_memory_manager.cc b/content/common/gpu/gpu_memory_manager.cc index 9a233758f07c2..045210aa8951b 100644 --- a/content/common/gpu/gpu_memory_manager.cc +++ b/content/common/gpu/gpu_memory_manager.cc @@ -62,11 +62,11 @@ GpuMemoryManager::~GpuMemoryManager() { void GpuMemoryManager::UpdateAvailableGpuMemory() { // If the value was overridden on the command line, use the specified value. static bool client_hard_limit_bytes_overridden = - CommandLine::ForCurrentProcess()->HasSwitch( + base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kForceGpuMemAvailableMb); if (client_hard_limit_bytes_overridden) { base::StringToUint64( - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kForceGpuMemAvailableMb), &client_hard_limit_bytes_); client_hard_limit_bytes_ *= 1024 * 1024; diff --git a/content/common/gpu/gpu_messages.h b/content/common/gpu/gpu_messages.h index 164052e644446..a91bd1f419cfb 100644 --- a/content/common/gpu/gpu_messages.h +++ b/content/common/gpu/gpu_messages.h @@ -696,9 +696,10 @@ IPC_MESSAGE_ROUTED1(AcceleratedVideoDecoderHostMsg_DismissPictureBuffer, int32) /* Picture buffer ID */ // Decoder reports that a picture is ready. -IPC_MESSAGE_ROUTED2(AcceleratedVideoDecoderHostMsg_PictureReady, - int32, /* Picture buffer ID */ - int32) /* Bitstream buffer ID */ +IPC_MESSAGE_ROUTED3(AcceleratedVideoDecoderHostMsg_PictureReady, + int32, /* Picture buffer ID */ + int32, /* Bitstream buffer ID */ + gfx::Rect) /* Visible rectangle */ // Confirm decoder has been flushed. IPC_MESSAGE_ROUTED0(AcceleratedVideoDecoderHostMsg_FlushDone) diff --git a/content/common/gpu/image_transport_surface_android.cc b/content/common/gpu/image_transport_surface_android.cc index 5fc9e51605f45..5e0d6ff11de25 100644 --- a/content/common/gpu/image_transport_surface_android.cc +++ b/content/common/gpu/image_transport_surface_android.cc @@ -92,7 +92,8 @@ bool ImageTransportSurfaceAndroid::Initialize() { GpuChannel* parent_channel = GetHelper()->manager()->LookupChannel(parent_client_id_); if (parent_channel) { - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess)) GetHelper()->SetPreemptByFlag(parent_channel->GetPreemptionFlag()); } diff --git a/content/common/gpu/image_transport_surface_calayer_mac.h b/content/common/gpu/image_transport_surface_calayer_mac.h index e8e2af0d77b38..85ee4181a3a33 100644 --- a/content/common/gpu/image_transport_surface_calayer_mac.h +++ b/content/common/gpu/image_transport_surface_calayer_mac.h @@ -11,12 +11,7 @@ #include "ui/gl/gl_bindings.h" #include "ui/gl/scoped_cgl.h" -@interface ImageTransportLayer : CAOpenGLLayer { - base::ScopedTypeRef shareContext_; - GLuint texture_; - gfx::Size pixelSize_; -} -@end +@class ImageTransportLayer; namespace content { @@ -24,7 +19,7 @@ namespace content { class CALayerStorageProvider : public ImageTransportSurfaceFBO::StorageProvider { public: - CALayerStorageProvider(); + CALayerStorageProvider(ImageTransportSurfaceFBO* transport_surface); virtual ~CALayerStorageProvider(); // ImageTransportSurfaceFBO::StorageProvider implementation: @@ -35,8 +30,31 @@ class CALayerStorageProvider virtual void FreeColorBufferStorage() OVERRIDE; virtual uint64 GetSurfaceHandle() const OVERRIDE; virtual void WillSwapBuffers() OVERRIDE; + virtual void CanFreeSwappedBuffer() OVERRIDE; + + // Interface to ImageTransportLayer: + CGLContextObj LayerShareGroupContext(); + bool LayerCanDraw(); + void LayerDoDraw(); private: + ImageTransportSurfaceFBO* transport_surface_; + + // Set when a new swap occurs, and un-set when |layer_| draws that frame. + bool has_pending_draw_; + + // A counter that is incremented whenever LayerCanDraw returns false. If this + // reaches a threshold, then |layer_| is switched to synchronous drawing to + // save CPU work. + uint32 can_draw_returned_false_count_; + + // The texture with the pixels to draw, and the share group it is allocated + // in. + base::ScopedTypeRef share_group_context_; + GLuint fbo_texture_; + gfx::Size fbo_pixel_size_; + + // The CALayer that the current frame is being drawn into. base::scoped_nsobject context_; base::scoped_nsobject layer_; diff --git a/content/common/gpu/image_transport_surface_calayer_mac.mm b/content/common/gpu/image_transport_surface_calayer_mac.mm index 4ad8f92b4031c..ed8d3535cb9b2 100644 --- a/content/common/gpu/image_transport_surface_calayer_mac.mm +++ b/content/common/gpu/image_transport_surface_calayer_mac.mm @@ -9,36 +9,39 @@ #include "ui/base/cocoa/animation_utils.h" #include "ui/gfx/geometry/size_conversions.h" -@interface ImageTransportLayer (Private) { +@interface ImageTransportLayer : CAOpenGLLayer { + content::CALayerStorageProvider* storageProvider_; } +- (id)initWithStorageProvider:(content::CALayerStorageProvider*)storageProvider; +- (void)resetStorageProvider; @end @implementation ImageTransportLayer -- (id)initWithContext:(CGLContextObj)context - withTexture:(GLuint)texture - withPixelSize:(gfx::Size)pixelSize - withScaleFactor:(float)scaleFactor { - if (self = [super init]) { - shareContext_.reset(CGLRetainContext(context)); - texture_ = texture; - pixelSize_ = pixelSize; - - gfx::Size dipSize(gfx::ToFlooredSize(gfx::ScaleSize( - pixelSize_, 1.0f / scaleFactor))); - [self setContentsScale:scaleFactor]; - [self setFrame:CGRectMake(0, 0, dipSize.width(), dipSize.height())]; - } +- (id)initWithStorageProvider: + (content::CALayerStorageProvider*)storageProvider { + if (self = [super init]) + storageProvider_ = storageProvider; return self; } +- (void)resetStorageProvider { + storageProvider_ = NULL; +} + - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { - return CGLRetainPixelFormat(CGLGetPixelFormat(shareContext_)); + if (!storageProvider_) + return NULL; + return CGLRetainPixelFormat(CGLGetPixelFormat( + storageProvider_->LayerShareGroupContext())); } - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { + if (!storageProvider_) + return NULL; CGLContextObj context = NULL; - CGLError error = CGLCreateContext(pixelFormat, shareContext_, &context); + CGLError error = CGLCreateContext( + pixelFormat, storageProvider_->LayerShareGroupContext(), &context); if (error != kCGLNoError) DLOG(ERROR) << "CGLCreateContext failed with CGL error: " << error; return context; @@ -48,49 +51,21 @@ - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp*)timeStamp { - return YES; + if (!storageProvider_) + return NO; + return storageProvider_->LayerCanDraw(); } - (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp*)timeStamp { - glClearColor(1, 0, 1, 1); - glClear(GL_COLOR_BUFFER_BIT); - - GLint viewport[4] = {0, 0, 0, 0}; - glGetIntegerv(GL_VIEWPORT, viewport); - gfx::Size viewportSize(viewport[2], viewport[3]); - - // Set the coordinate system to be one-to-one with pixels. - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(0, viewportSize.width(), 0, viewportSize.height(), -1, 1); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - // Draw a fullscreen quad. - glColor4f(1, 1, 1, 1); - glEnable(GL_TEXTURE_RECTANGLE_ARB); - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_); - glBegin(GL_QUADS); - { - glTexCoord2f(0, 0); - glVertex2f(0, 0); - - glTexCoord2f(0, pixelSize_.height()); - glVertex2f(0, pixelSize_.height()); - - glTexCoord2f(pixelSize_.width(), pixelSize_.height()); - glVertex2f(pixelSize_.width(), pixelSize_.height()); - - glTexCoord2f(pixelSize_.width(), 0); - glVertex2f(pixelSize_.width(), 0); + if (storageProvider_) { + storageProvider_->LayerDoDraw(); + } else { + glClearColor(1, 1, 1, 1); + glClear(GL_COLOR_BUFFER_BIT); } - glEnd(); - glBindTexture(0, texture_); - glDisable(GL_TEXTURE_RECTANGLE_ARB); - [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval @@ -101,7 +76,14 @@ - (void)drawInCGLContext:(CGLContextObj)glContext namespace content { -CALayerStorageProvider::CALayerStorageProvider() { +CALayerStorageProvider::CALayerStorageProvider( + ImageTransportSurfaceFBO* transport_surface) + : transport_surface_(transport_surface), + has_pending_draw_(false), + can_draw_returned_false_count_(0), + fbo_texture_(0) { + // Allocate a CAContext to use to transport the CALayer to the browser + // process. base::scoped_nsobject dict([[NSDictionary alloc] init]); CGSConnectionID connection_id = CGSMainConnectionID(); context_.reset([CAContext contextWithCGSConnection:connection_id @@ -144,18 +126,33 @@ - (void)drawInCGLContext:(CGLContextObj)glContext // Disable the fade-in animation as the layer is changed. ScopedCAActionDisabler disabler; - // Resize the CAOpenGLLayer to match the size needed, and change it to be the - // hosted layer. - layer_.reset([[ImageTransportLayer alloc] initWithContext:context - withTexture:texture - withPixelSize:pixel_size - withScaleFactor:scale_factor]); + // Allocate a CALayer to draw texture into. + share_group_context_.reset(CGLRetainContext(context)); + fbo_texture_ = texture; + fbo_pixel_size_ = pixel_size; + layer_.reset([[ImageTransportLayer alloc] initWithStorageProvider:this]); + gfx::Size dip_size(gfx::ToFlooredSize(gfx::ScaleSize( + fbo_pixel_size_, 1.0f / scale_factor))); + [layer_ setContentsScale:scale_factor]; + [layer_ setFrame:CGRectMake(0, 0, dip_size.width(), dip_size.height())]; return true; } void CALayerStorageProvider::FreeColorBufferStorage() { - [context_ setLayer:nil]; + // We shouldn't be asked to free a texture when we still have yet to draw it. + DCHECK(!has_pending_draw_); + has_pending_draw_ = false; + can_draw_returned_false_count_ = 0; + + // Note that |context_| still holds a reference to |layer_|, and will until + // a new frame is swapped in. + [layer_ displayIfNeeded]; + [layer_ resetStorageProvider]; layer_.reset(); + + share_group_context_.reset(); + fbo_texture_ = 0; + fbo_pixel_size_ = gfx::Size(); } uint64 CALayerStorageProvider::GetSurfaceHandle() const { @@ -163,15 +160,80 @@ - (void)drawInCGLContext:(CGLContextObj)glContext } void CALayerStorageProvider::WillSwapBuffers() { + DCHECK(!has_pending_draw_); + has_pending_draw_ = true; + // Don't add the layer to the CAContext until a SwapBuffers is going to be // called, because the texture does not have any content until the // SwapBuffers call is about to be made. if ([context_ layer] != layer_.get()) [context_ setLayer:layer_]; - // TODO(ccameron): Use the isAsynchronous property to ensure smooth - // animation. - [layer_ setNeedsDisplay]; + if (![layer_ isAsynchronous]) + [layer_ setAsynchronous:YES]; +} + +void CALayerStorageProvider::CanFreeSwappedBuffer() { +} + +CGLContextObj CALayerStorageProvider::LayerShareGroupContext() { + return share_group_context_; +} + +bool CALayerStorageProvider::LayerCanDraw() { + if (has_pending_draw_) { + can_draw_returned_false_count_ = 0; + return true; + } else { + if (can_draw_returned_false_count_ == 30) { + if ([layer_ isAsynchronous]) + [layer_ setAsynchronous:NO]; + } else { + can_draw_returned_false_count_ += 1; + } + return false; + } +} + +void CALayerStorageProvider::LayerDoDraw() { + DCHECK(has_pending_draw_); + has_pending_draw_ = false; + + GLint viewport[4] = {0, 0, 0, 0}; + glGetIntegerv(GL_VIEWPORT, viewport); + gfx::Size viewport_size(viewport[2], viewport[3]); + + // Set the coordinate system to be one-to-one with pixels. + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, viewport_size.width(), 0, viewport_size.height(), -1, 1); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Draw a fullscreen quad. + glColor4f(1, 1, 1, 1); + glEnable(GL_TEXTURE_RECTANGLE_ARB); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, fbo_texture_); + glBegin(GL_QUADS); + { + glTexCoord2f(0, 0); + glVertex2f(0, 0); + + glTexCoord2f(0, fbo_pixel_size_.height()); + glVertex2f(0, fbo_pixel_size_.height()); + + glTexCoord2f(fbo_pixel_size_.width(), fbo_pixel_size_.height()); + glVertex2f(fbo_pixel_size_.width(), fbo_pixel_size_.height()); + + glTexCoord2f(fbo_pixel_size_.width(), 0); + glVertex2f(fbo_pixel_size_.width(), 0); + } + glEnd(); + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + glDisable(GL_TEXTURE_RECTANGLE_ARB); + + // Allow forward progress in the context now that the swap is complete. + transport_surface_->UnblockContextAfterPendingSwap(); } } // namespace content diff --git a/content/common/gpu/image_transport_surface_fbo_mac.cc b/content/common/gpu/image_transport_surface_fbo_mac.cc deleted file mode 100644 index 3e86a484fe9d0..0000000000000 --- a/content/common/gpu/image_transport_surface_fbo_mac.cc +++ /dev/null @@ -1,349 +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 "content/common/gpu/image_transport_surface_fbo_mac.h" - -#include "content/common/gpu/gpu_messages.h" -#include "content/common/gpu/image_transport_surface_iosurface_mac.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_implementation.h" -#include "ui/gl/gl_surface_osmesa.h" - -namespace content { - -ImageTransportSurfaceFBO::ImageTransportSurfaceFBO( - StorageProvider* storage_provider, - GpuChannelManager* manager, - GpuCommandBufferStub* stub, - gfx::PluginWindowHandle handle) - : storage_provider_(storage_provider), - backbuffer_suggested_allocation_(true), - frontbuffer_suggested_allocation_(true), - fbo_id_(0), - texture_id_(0), - depth_stencil_renderbuffer_id_(0), - has_complete_framebuffer_(false), - context_(NULL), - scale_factor_(1.f), - made_current_(false), - is_swap_buffers_pending_(false), - did_unschedule_(false) { - helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); -} - -ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() { -} - -bool ImageTransportSurfaceFBO::Initialize() { - // Only support IOSurfaces if the GL implementation is the native desktop GL. - // IO surfaces will not work with, for example, OSMesa software renderer - // GL contexts. - if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && - gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) - return false; - - if (!helper_->Initialize()) - return false; - - helper_->stub()->AddDestructionObserver(this); - return true; -} - -void ImageTransportSurfaceFBO::Destroy() { - DestroyFramebuffer(); - - helper_->Destroy(); -} - -bool ImageTransportSurfaceFBO::DeferDraws() { - // The command buffer hit a draw/clear command that could clobber the - // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort - // processing of the command by returning true and unschedule until the Swap - // Ack arrives. - if(did_unschedule_) - return true; // Still unscheduled, so just return true. - if (is_swap_buffers_pending_) { - did_unschedule_ = true; - helper_->SetScheduled(false); - return true; - } - return false; -} - -bool ImageTransportSurfaceFBO::IsOffscreen() { - return false; -} - -bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) { - context_ = context; - - if (made_current_) - return true; - - OnResize(gfx::Size(1, 1), 1.f); - - made_current_ = true; - return true; -} - -unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() { - return fbo_id_; -} - -bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) { - if (backbuffer_suggested_allocation_ == allocation) - return true; - backbuffer_suggested_allocation_ = allocation; - AdjustBufferAllocation(); - return true; -} - -void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) { - if (frontbuffer_suggested_allocation_ == allocation) - return; - frontbuffer_suggested_allocation_ = allocation; - AdjustBufferAllocation(); -} - -void ImageTransportSurfaceFBO::AdjustBufferAllocation() { - // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is - // free'd when both the browser and gpu processes have Unref'd the IOSurface. - if (!backbuffer_suggested_allocation_ && - !frontbuffer_suggested_allocation_ && - has_complete_framebuffer_) { - DestroyFramebuffer(); - helper_->Suspend(); - } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) { - CreateFramebuffer(); - } -} - -bool ImageTransportSurfaceFBO::SwapBuffers() { - DCHECK(backbuffer_suggested_allocation_); - if (!frontbuffer_suggested_allocation_) - return true; - glFlush(); - - GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; - params.surface_handle = storage_provider_->GetSurfaceHandle(); - params.size = GetSize(); - params.scale_factor = scale_factor_; - params.latency_info.swap(latency_info_); - helper_->SendAcceleratedSurfaceBuffersSwapped(params); - - DCHECK(!is_swap_buffers_pending_); - is_swap_buffers_pending_ = true; - - storage_provider_->WillSwapBuffers(); - return true; -} - -bool ImageTransportSurfaceFBO::PostSubBuffer( - int x, int y, int width, int height) { - DCHECK(backbuffer_suggested_allocation_); - if (!frontbuffer_suggested_allocation_) - return true; - glFlush(); - - GpuHostMsg_AcceleratedSurfacePostSubBuffer_Params params; - params.surface_handle = storage_provider_->GetSurfaceHandle(); - params.x = x; - params.y = y; - params.width = width; - params.height = height; - params.surface_size = GetSize(); - params.surface_scale_factor = scale_factor_; - params.latency_info.swap(latency_info_); - helper_->SendAcceleratedSurfacePostSubBuffer(params); - - DCHECK(!is_swap_buffers_pending_); - is_swap_buffers_pending_ = true; - - storage_provider_->WillSwapBuffers(); - return true; -} - -bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() { - return true; -} - -gfx::Size ImageTransportSurfaceFBO::GetSize() { - return size_; -} - -void* ImageTransportSurfaceFBO::GetHandle() { - return NULL; -} - -void* ImageTransportSurfaceFBO::GetDisplay() { - return NULL; -} - -void ImageTransportSurfaceFBO::OnBufferPresented( - const AcceleratedSurfaceMsg_BufferPresented_Params& params) { - DCHECK(is_swap_buffers_pending_); - - context_->share_group()->SetRendererID(params.renderer_id); - is_swap_buffers_pending_ = false; - if (did_unschedule_) { - did_unschedule_ = false; - helper_->SetScheduled(true); - } -} - -void ImageTransportSurfaceFBO::OnResize(gfx::Size size, - float scale_factor) { - // This trace event is used in gpu_feature_browsertest.cc - the test will need - // to be updated if this event is changed or moved. - TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize", - "old_width", size_.width(), "new_width", size.width()); - // Caching |context_| from OnMakeCurrent. It should still be current. - DCHECK(context_->IsCurrent(this)); - - size_ = size; - scale_factor_ = scale_factor; - - CreateFramebuffer(); -} - -void ImageTransportSurfaceFBO::SetLatencyInfo( - const std::vector& latency_info) { - for (size_t i = 0; i < latency_info.size(); i++) - latency_info_.push_back(latency_info[i]); -} - -void ImageTransportSurfaceFBO::WakeUpGpu() { - NOTIMPLEMENTED(); -} - -void ImageTransportSurfaceFBO::OnWillDestroyStub() { - helper_->stub()->RemoveDestructionObserver(this); - Destroy(); -} - -void ImageTransportSurfaceFBO::DestroyFramebuffer() { - // If we have resources to destroy, then make sure that we have a current - // context which we can use to delete the resources. - if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { - DCHECK(gfx::GLContext::GetCurrent() == context_); - DCHECK(context_->IsCurrent(this)); - DCHECK(CGLGetCurrentContext()); - } - - if (fbo_id_) { - glDeleteFramebuffersEXT(1, &fbo_id_); - fbo_id_ = 0; - } - - if (texture_id_) { - glDeleteTextures(1, &texture_id_); - texture_id_ = 0; - } - - if (depth_stencil_renderbuffer_id_) { - glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); - depth_stencil_renderbuffer_id_ = 0; - } - - storage_provider_->FreeColorBufferStorage(); - - has_complete_framebuffer_ = false; -} - -void ImageTransportSurfaceFBO::CreateFramebuffer() { - gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_); - - // Only recreate surface when the rounded up size has changed. - if (has_complete_framebuffer_ && new_rounded_size == rounded_size_) - return; - - // This trace event is used in gpu_feature_browsertest.cc - the test will need - // to be updated if this event is changed or moved. - TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer", - "width", new_rounded_size.width(), - "height", new_rounded_size.height()); - - rounded_size_ = new_rounded_size; - - // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on - // Mac OS X and is required for IOSurface interoperability. - GLint previous_texture_id = 0; - glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); - - // Free the old IO Surface first to reduce memory fragmentation. - DestroyFramebuffer(); - - glGenFramebuffersEXT(1, &fbo_id_); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); - - glGenTextures(1, &texture_id_); - - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, - GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, - GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, - GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_RECTANGLE_ARB, - texture_id_, - 0); - - // Search through the provided attributes; if the caller has - // requested a stencil buffer, try to get one. - - int32 stencil_bits = - helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); - if (stencil_bits > 0) { - // Create and bind the stencil buffer - bool has_packed_depth_stencil = - GLSurface::ExtensionsContain( - reinterpret_cast(glGetString(GL_EXTENSIONS)), - "GL_EXT_packed_depth_stencil"); - - if (has_packed_depth_stencil) { - glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); - glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, - depth_stencil_renderbuffer_id_); - glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, - rounded_size_.width(), rounded_size_.height()); - glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, - GL_STENCIL_ATTACHMENT_EXT, - GL_RENDERBUFFER_EXT, - depth_stencil_renderbuffer_id_); - } - - // If we asked for stencil but the extension isn't present, - // it's OK to silently fail; subsequent code will/must check - // for the presence of a stencil buffer before attempting to - // do stencil-based operations. - } - - bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage( - static_cast(context_->GetHandle()), texture_id_, - rounded_size_, scale_factor_); - if (!allocated_color_buffer) { - DLOG(ERROR) << "Failed to allocate color buffer storage."; - DestroyFramebuffer(); - return; - } - - GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { - DLOG(ERROR) << "Framebuffer was incomplete: " << status; - DestroyFramebuffer(); - return; - } - - has_complete_framebuffer_ = true; - - glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id); - // The FBO remains bound for this GL context. -} - -} // namespace content diff --git a/content/common/gpu/image_transport_surface_fbo_mac.h b/content/common/gpu/image_transport_surface_fbo_mac.h index 3587e21b0b3a8..42f6683498c48 100644 --- a/content/common/gpu/image_transport_surface_fbo_mac.h +++ b/content/common/gpu/image_transport_surface_fbo_mac.h @@ -45,12 +45,19 @@ class ImageTransportSurfaceFBO // display. virtual uint64 GetSurfaceHandle() const = 0; - // Called when a frame is about to be sent to the browser process. + // Called when a new frame has been rendered into the texture, and the + // browser is about to be sent the surface to display. virtual void WillSwapBuffers() = 0; + + // Called once for every WillSwapBuffers call when the buffer that was sent + // to the browser may be released by the GPU process (this may be because + // the browser is holding a reference, in which case this will come + // quickly, or it may be because the browser is done with the surface, in + // which case it will come much later). + virtual void CanFreeSwappedBuffer() = 0; }; - ImageTransportSurfaceFBO(StorageProvider* storage_provider, - GpuChannelManager* manager, + ImageTransportSurfaceFBO(GpuChannelManager* manager, GpuCommandBufferStub* stub, gfx::PluginWindowHandle handle); @@ -70,6 +77,9 @@ class ImageTransportSurfaceFBO virtual bool SetBackbufferAllocation(bool allocated) OVERRIDE; virtual void SetFrontbufferAllocation(bool allocated) OVERRIDE; + // Called when the context may continue to make forward progress after a swap. + void UnblockContextAfterPendingSwap(); + protected: // ImageTransportSurface implementation virtual void OnBufferPresented( diff --git a/content/common/gpu/image_transport_surface_fbo_mac.mm b/content/common/gpu/image_transport_surface_fbo_mac.mm new file mode 100644 index 0000000000000..c62205c4700f1 --- /dev/null +++ b/content/common/gpu/image_transport_surface_fbo_mac.mm @@ -0,0 +1,338 @@ +// 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 "content/common/gpu/image_transport_surface_fbo_mac.h" + +#include "content/common/gpu/gpu_messages.h" +#include "content/common/gpu/image_transport_surface_calayer_mac.h" +#include "content/common/gpu/image_transport_surface_iosurface_mac.h" +#include "ui/base/cocoa/remote_layer_api.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gl/gl_context.h" +#include "ui/gl/gl_implementation.h" +#include "ui/gl/gl_surface_osmesa.h" + +namespace content { + +ImageTransportSurfaceFBO::ImageTransportSurfaceFBO( + GpuChannelManager* manager, + GpuCommandBufferStub* stub, + gfx::PluginWindowHandle handle) + : backbuffer_suggested_allocation_(true), + frontbuffer_suggested_allocation_(true), + fbo_id_(0), + texture_id_(0), + depth_stencil_renderbuffer_id_(0), + has_complete_framebuffer_(false), + context_(NULL), + scale_factor_(1.f), + made_current_(false), + is_swap_buffers_pending_(false), + did_unschedule_(false) { + if (ui::RemoteLayerAPISupported()) + storage_provider_.reset(new CALayerStorageProvider(this)); + else + storage_provider_.reset(new IOSurfaceStorageProvider(this)); + helper_.reset(new ImageTransportHelper(this, manager, stub, handle)); +} + +ImageTransportSurfaceFBO::~ImageTransportSurfaceFBO() { +} + +bool ImageTransportSurfaceFBO::Initialize() { + // Only support IOSurfaces if the GL implementation is the native desktop GL. + // IO surfaces will not work with, for example, OSMesa software renderer + // GL contexts. + if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL && + gfx::GetGLImplementation() != gfx::kGLImplementationAppleGL) + return false; + + if (!helper_->Initialize()) + return false; + + helper_->stub()->AddDestructionObserver(this); + return true; +} + +void ImageTransportSurfaceFBO::Destroy() { + DestroyFramebuffer(); + + helper_->Destroy(); +} + +bool ImageTransportSurfaceFBO::DeferDraws() { + // The command buffer hit a draw/clear command that could clobber the + // IOSurface in use by an earlier SwapBuffers. If a Swap is pending, abort + // processing of the command by returning true and unschedule until the Swap + // Ack arrives. + if(did_unschedule_) + return true; // Still unscheduled, so just return true. + if (is_swap_buffers_pending_) { + did_unschedule_ = true; + helper_->SetScheduled(false); + return true; + } + return false; +} + +bool ImageTransportSurfaceFBO::IsOffscreen() { + return false; +} + +bool ImageTransportSurfaceFBO::OnMakeCurrent(gfx::GLContext* context) { + context_ = context; + + if (made_current_) + return true; + + OnResize(gfx::Size(1, 1), 1.f); + + made_current_ = true; + return true; +} + +unsigned int ImageTransportSurfaceFBO::GetBackingFrameBufferObject() { + return fbo_id_; +} + +bool ImageTransportSurfaceFBO::SetBackbufferAllocation(bool allocation) { + if (backbuffer_suggested_allocation_ == allocation) + return true; + backbuffer_suggested_allocation_ = allocation; + AdjustBufferAllocation(); + return true; +} + +void ImageTransportSurfaceFBO::SetFrontbufferAllocation(bool allocation) { + if (frontbuffer_suggested_allocation_ == allocation) + return; + frontbuffer_suggested_allocation_ = allocation; + AdjustBufferAllocation(); +} + +void ImageTransportSurfaceFBO::AdjustBufferAllocation() { + // On mac, the frontbuffer and backbuffer are the same buffer. The buffer is + // free'd when both the browser and gpu processes have Unref'd the IOSurface. + if (!backbuffer_suggested_allocation_ && + !frontbuffer_suggested_allocation_ && + has_complete_framebuffer_) { + DestroyFramebuffer(); + helper_->Suspend(); + } else if (backbuffer_suggested_allocation_ && !has_complete_framebuffer_) { + CreateFramebuffer(); + } +} + +bool ImageTransportSurfaceFBO::SwapBuffers() { + DCHECK(backbuffer_suggested_allocation_); + if (!frontbuffer_suggested_allocation_) + return true; + glFlush(); + + GpuHostMsg_AcceleratedSurfaceBuffersSwapped_Params params; + params.surface_handle = storage_provider_->GetSurfaceHandle(); + params.size = GetSize(); + params.scale_factor = scale_factor_; + params.latency_info.swap(latency_info_); + helper_->SendAcceleratedSurfaceBuffersSwapped(params); + + DCHECK(!is_swap_buffers_pending_); + is_swap_buffers_pending_ = true; + + storage_provider_->WillSwapBuffers(); + return true; +} + +bool ImageTransportSurfaceFBO::PostSubBuffer( + int x, int y, int width, int height) { + // Mac does not support sub-buffer swaps. + NOTREACHED(); + return false; +} + +bool ImageTransportSurfaceFBO::SupportsPostSubBuffer() { + return true; +} + +gfx::Size ImageTransportSurfaceFBO::GetSize() { + return size_; +} + +void* ImageTransportSurfaceFBO::GetHandle() { + return NULL; +} + +void* ImageTransportSurfaceFBO::GetDisplay() { + return NULL; +} + +void ImageTransportSurfaceFBO::OnBufferPresented( + const AcceleratedSurfaceMsg_BufferPresented_Params& params) { + context_->share_group()->SetRendererID(params.renderer_id); + storage_provider_->CanFreeSwappedBuffer(); +} + +void ImageTransportSurfaceFBO::UnblockContextAfterPendingSwap() { + DCHECK(is_swap_buffers_pending_); + is_swap_buffers_pending_ = false; + if (did_unschedule_) { + did_unschedule_ = false; + helper_->SetScheduled(true); + } +} + +void ImageTransportSurfaceFBO::OnResize(gfx::Size size, + float scale_factor) { + // This trace event is used in gpu_feature_browsertest.cc - the test will need + // to be updated if this event is changed or moved. + TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::OnResize", + "old_width", size_.width(), "new_width", size.width()); + // Caching |context_| from OnMakeCurrent. It should still be current. + DCHECK(context_->IsCurrent(this)); + + size_ = size; + scale_factor_ = scale_factor; + + CreateFramebuffer(); +} + +void ImageTransportSurfaceFBO::SetLatencyInfo( + const std::vector& latency_info) { + for (size_t i = 0; i < latency_info.size(); i++) + latency_info_.push_back(latency_info[i]); +} + +void ImageTransportSurfaceFBO::WakeUpGpu() { + NOTIMPLEMENTED(); +} + +void ImageTransportSurfaceFBO::OnWillDestroyStub() { + helper_->stub()->RemoveDestructionObserver(this); + Destroy(); +} + +void ImageTransportSurfaceFBO::DestroyFramebuffer() { + // If we have resources to destroy, then make sure that we have a current + // context which we can use to delete the resources. + if (context_ || fbo_id_ || texture_id_ || depth_stencil_renderbuffer_id_) { + DCHECK(gfx::GLContext::GetCurrent() == context_); + DCHECK(context_->IsCurrent(this)); + DCHECK(CGLGetCurrentContext()); + } + + if (fbo_id_) { + glDeleteFramebuffersEXT(1, &fbo_id_); + fbo_id_ = 0; + } + + if (texture_id_) { + glDeleteTextures(1, &texture_id_); + texture_id_ = 0; + } + + if (depth_stencil_renderbuffer_id_) { + glDeleteRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); + depth_stencil_renderbuffer_id_ = 0; + } + + storage_provider_->FreeColorBufferStorage(); + + has_complete_framebuffer_ = false; +} + +void ImageTransportSurfaceFBO::CreateFramebuffer() { + gfx::Size new_rounded_size = storage_provider_->GetRoundedSize(size_); + + // Only recreate surface when the rounded up size has changed. + if (has_complete_framebuffer_ && new_rounded_size == rounded_size_) + return; + + // This trace event is used in gpu_feature_browsertest.cc - the test will need + // to be updated if this event is changed or moved. + TRACE_EVENT2("gpu", "ImageTransportSurfaceFBO::CreateFramebuffer", + "width", new_rounded_size.width(), + "height", new_rounded_size.height()); + + rounded_size_ = new_rounded_size; + + // GL_TEXTURE_RECTANGLE_ARB is the best supported render target on + // Mac OS X and is required for IOSurface interoperability. + GLint previous_texture_id = 0; + glGetIntegerv(GL_TEXTURE_BINDING_RECTANGLE_ARB, &previous_texture_id); + + // Free the old IO Surface first to reduce memory fragmentation. + DestroyFramebuffer(); + + glGenFramebuffersEXT(1, &fbo_id_); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo_id_); + + glGenTextures(1, &texture_id_); + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture_id_); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, + GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_RECTANGLE_ARB, + texture_id_, + 0); + + // Search through the provided attributes; if the caller has + // requested a stencil buffer, try to get one. + + int32 stencil_bits = + helper_->stub()->GetRequestedAttribute(EGL_STENCIL_SIZE); + if (stencil_bits > 0) { + // Create and bind the stencil buffer + bool has_packed_depth_stencil = + GLSurface::ExtensionsContain( + reinterpret_cast(glGetString(GL_EXTENSIONS)), + "GL_EXT_packed_depth_stencil"); + + if (has_packed_depth_stencil) { + glGenRenderbuffersEXT(1, &depth_stencil_renderbuffer_id_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, + depth_stencil_renderbuffer_id_); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH24_STENCIL8_EXT, + rounded_size_.width(), rounded_size_.height()); + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + depth_stencil_renderbuffer_id_); + } + + // If we asked for stencil but the extension isn't present, + // it's OK to silently fail; subsequent code will/must check + // for the presence of a stencil buffer before attempting to + // do stencil-based operations. + } + + bool allocated_color_buffer = storage_provider_->AllocateColorBufferStorage( + static_cast(context_->GetHandle()), texture_id_, + rounded_size_, scale_factor_); + if (!allocated_color_buffer) { + DLOG(ERROR) << "Failed to allocate color buffer storage."; + DestroyFramebuffer(); + return; + } + + GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (status != GL_FRAMEBUFFER_COMPLETE_EXT) { + DLOG(ERROR) << "Framebuffer was incomplete: " << status; + DestroyFramebuffer(); + return; + } + + has_complete_framebuffer_ = true; + + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, previous_texture_id); + // The FBO remains bound for this GL context. +} + +} // namespace content diff --git a/content/common/gpu/image_transport_surface_iosurface_mac.cc b/content/common/gpu/image_transport_surface_iosurface_mac.cc index 49aa4cda74dbc..cbae4a2612535 100644 --- a/content/common/gpu/image_transport_surface_iosurface_mac.cc +++ b/content/common/gpu/image_transport_surface_iosurface_mac.cc @@ -40,7 +40,9 @@ void AddIntegerValue(CFMutableDictionaryRef dictionary, } // namespace -IOSurfaceStorageProvider::IOSurfaceStorageProvider() {} +IOSurfaceStorageProvider::IOSurfaceStorageProvider( + ImageTransportSurfaceFBO* transport_surface) + : transport_surface_(transport_surface) {} IOSurfaceStorageProvider::~IOSurfaceStorageProvider() { DCHECK(!io_surface_); @@ -108,6 +110,18 @@ uint64 IOSurfaceStorageProvider::GetSurfaceHandle() const { } void IOSurfaceStorageProvider::WillSwapBuffers() { + // The browser compositor will throttle itself, so we are free to unblock the + // context immediately. Make sure that the browser is doing its throttling + // appropriately by ensuring that the previous swap was acknowledged before + // we get another swap. + DCHECK(pending_swapped_surfaces_.empty()); + pending_swapped_surfaces_.push_back(io_surface_); + transport_surface_->UnblockContextAfterPendingSwap(); +} + +void IOSurfaceStorageProvider::CanFreeSwappedBuffer() { + DCHECK(!pending_swapped_surfaces_.empty()); + pending_swapped_surfaces_.pop_front(); } } // namespace content diff --git a/content/common/gpu/image_transport_surface_iosurface_mac.h b/content/common/gpu/image_transport_surface_iosurface_mac.h index 5470ce3a4d371..df6954dea4ae7 100644 --- a/content/common/gpu/image_transport_surface_iosurface_mac.h +++ b/content/common/gpu/image_transport_surface_iosurface_mac.h @@ -5,6 +5,8 @@ #ifndef CONTENT_COMMON_GPU_IMAGE_TRANSPORT_SURFACE_IOSURFACE_MAC_H_ #define CONTENT_COMMON_GPU_IMAGE_TRANSPORT_SURFACE_IOSURFACE_MAC_H_ +#include + #include "content/common/gpu/image_transport_surface_fbo_mac.h" #include "ui/gl/gl_bindings.h" @@ -17,7 +19,7 @@ namespace content { class IOSurfaceStorageProvider : public ImageTransportSurfaceFBO::StorageProvider { public: - IOSurfaceStorageProvider(); + IOSurfaceStorageProvider(ImageTransportSurfaceFBO* transport_surface); virtual ~IOSurfaceStorageProvider(); // ImageTransportSurfaceFBO::StorageProvider implementation: @@ -28,10 +30,18 @@ class IOSurfaceStorageProvider virtual void FreeColorBufferStorage() OVERRIDE; virtual uint64 GetSurfaceHandle() const OVERRIDE; virtual void WillSwapBuffers() OVERRIDE; + virtual void CanFreeSwappedBuffer() OVERRIDE; private: + ImageTransportSurfaceFBO* transport_surface_; + base::ScopedCFTypeRef io_surface_; + // The list of IOSurfaces that have been sent to the browser process but have + // not been opened in the browser process yet. This list should never have + // more than one entry. + std::list> pending_swapped_surfaces_; + // The id of |io_surface_| or 0 if that's NULL. IOSurfaceID io_surface_id_; diff --git a/content/common/gpu/image_transport_surface_mac.mm b/content/common/gpu/image_transport_surface_mac.mm index 3cfda14b93ed1..c193f6f70d907 100644 --- a/content/common/gpu/image_transport_surface_mac.mm +++ b/content/common/gpu/image_transport_surface_mac.mm @@ -5,8 +5,6 @@ #include "content/common/gpu/image_transport_surface_fbo_mac.h" #include "content/common/gpu/gpu_messages.h" -#include "content/common/gpu/image_transport_surface_iosurface_mac.h" -#include "content/common/gpu/image_transport_surface_calayer_mac.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_implementation.h" @@ -49,10 +47,8 @@ switch (gfx::GetGLImplementation()) { case gfx::kGLImplementationDesktopGL: case gfx::kGLImplementationAppleGL: - // TODO(ccameron): If the remote layer API is supported on this system, - // use a CALayerStorageProvider instead of an IOSurfaceStorageProvider. return scoped_refptr(new ImageTransportSurfaceFBO( - new IOSurfaceStorageProvider, manager, stub, surface_handle.handle)); + manager, stub, surface_handle.handle)); default: // Content shell in DRT mode spins up a gpu process which needs an // image transport surface, but that surface isn't used to read pixel diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc index f72e1b257d77c..0eee64152f511 100644 --- a/content/common/gpu/media/android_video_decode_accelerator.cc +++ b/content/common/gpu/media/android_video_decode_accelerator.cc @@ -89,7 +89,7 @@ bool AndroidVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile, client_ = client; - if (profile == media::VP8PROFILE_MAIN) { + if (profile == media::VP8PROFILE_ANY) { codec_ = media::kCodecVP8; } else { // TODO(dwkang): enable H264 once b/8125974 is fixed. @@ -357,16 +357,30 @@ void AndroidVideoDecodeAccelerator::SendCurrentSurfaceToClient( // attached. // 2. SurfaceTexture requires us to apply a transform matrix when we show // the texture. - copier_->DoCopyTexture(gl_decoder_.get(), GL_TEXTURE_EXTERNAL_OES, - GL_TEXTURE_2D, surface_texture_id_, - picture_buffer_texture_id, 0, size_.width(), - size_.height(), false, false, false); + // TODO(hkuang): get the StreamTexture transform matrix in GPU process + // instead of using default matrix crbug.com/226218. + const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + copier_->DoCopyTextureWithTransform(gl_decoder_.get(), + GL_TEXTURE_EXTERNAL_OES, + surface_texture_id_, + picture_buffer_texture_id, + 0, + size_.width(), + size_.height(), + false, + false, + false, + default_matrix); base::MessageLoop::current()->PostTask( FROM_HERE, - base::Bind(&AndroidVideoDecodeAccelerator::NotifyPictureReady, - weak_this_factory_.GetWeakPtr(), - media::Picture(picture_buffer_id, bitstream_id))); + base::Bind( + &AndroidVideoDecodeAccelerator::NotifyPictureReady, + weak_this_factory_.GetWeakPtr(), + media::Picture(picture_buffer_id, bitstream_id, gfx::Rect(size_)))); } void AndroidVideoDecodeAccelerator::Decode( diff --git a/content/common/gpu/media/android_video_encode_accelerator.cc b/content/common/gpu/media/android_video_encode_accelerator.cc index 83593fa5901a5..f124b148a190b 100644 --- a/content/common/gpu/media/android_video_encode_accelerator.cc +++ b/content/common/gpu/media/android_video_encode_accelerator.cc @@ -86,7 +86,7 @@ AndroidVideoEncodeAccelerator::GetSupportedProfiles() { std::vector profiles; #if defined(ENABLE_WEBRTC) - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (cmd_line->HasSwitch(switches::kDisableWebRtcHWEncoding)) return profiles; #endif @@ -100,7 +100,7 @@ AndroidVideoEncodeAccelerator::GetSupportedProfiles() { continue; } SupportedProfile profile; - profile.profile = media::VP8PROFILE_MAIN; + profile.profile = media::VP8PROFILE_ANY; // Wouldn't it be nice if MediaCodec exposed the maximum capabilities of the // encoder? Sure would be. Too bad it doesn't. So we hard-code some // reasonable defaults. @@ -129,7 +129,7 @@ bool AndroidVideoEncodeAccelerator::Initialize( if (!(media::MediaCodecBridge::SupportsSetParameters() && format == VideoFrame::I420 && - output_profile == media::VP8PROFILE_MAIN)) { + output_profile == media::VP8PROFILE_ANY)) { DLOG(ERROR) << "Unexpected combo: " << format << ", " << output_profile; return false; } diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.cc b/content/common/gpu/media/dxva_video_decode_accelerator.cc index 2e667d0bd1a35..3518f2052ed21 100644 --- a/content/common/gpu/media/dxva_video_decode_accelerator.cc +++ b/content/common/gpu/media/dxva_video_decode_accelerator.cc @@ -557,12 +557,25 @@ void DXVAVideoDecodeAccelerator::ReusePictureBuffer( RETURN_AND_NOTIFY_ON_FAILURE((state_ != kUninitialized), "Invalid state: " << state_, ILLEGAL_STATE,); - if (output_picture_buffers_.empty()) + if (output_picture_buffers_.empty() && stale_output_picture_buffers_.empty()) return; OutputBuffers::iterator it = output_picture_buffers_.find(picture_buffer_id); - RETURN_AND_NOTIFY_ON_FAILURE(it != output_picture_buffers_.end(), - "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,); + // If we didn't find the picture id in the |output_picture_buffers_| map we + // try the |stale_output_picture_buffers_| map, as this may have been an + // output picture buffer from before a resolution change, that at resolution + // change time had yet to be displayed. The client is calling us back to tell + // us that we can now recycle this picture buffer, so if we were waiting to + // dispose of it we now can. + if (it == output_picture_buffers_.end()) { + it = stale_output_picture_buffers_.find(picture_buffer_id); + RETURN_AND_NOTIFY_ON_FAILURE(it != stale_output_picture_buffers_.end(), + "Invalid picture id: " << picture_buffer_id, INVALID_ARGUMENT,); + base::MessageLoop::current()->PostTask(FROM_HERE, + base::Bind(&DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer, + weak_this_factory_.GetWeakPtr(), picture_buffer_id)); + return; + } it->second->ReusePictureBuffer(); ProcessPendingSamples(); @@ -943,7 +956,8 @@ void DXVAVideoDecodeAccelerator::ProcessPendingSamples() { PLATFORM_FAILURE, ); media::Picture output_picture(index->second->id(), - sample_info.input_buffer_id); + sample_info.input_buffer_id, + gfx::Rect(index->second->size())); base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&DXVAVideoDecodeAccelerator::NotifyPictureReady, @@ -981,6 +995,7 @@ void DXVAVideoDecodeAccelerator::Invalidate() { return; weak_this_factory_.InvalidateWeakPtrs(); output_picture_buffers_.clear(); + stale_output_picture_buffers_.clear(); pending_output_samples_.clear(); pending_input_buffers_.clear(); decoder_.Release(); @@ -1157,8 +1172,7 @@ void DXVAVideoDecodeAccelerator::HandleResolutionChanged(int width, base::MessageLoop::current()->PostTask( FROM_HERE, base::Bind(&DXVAVideoDecodeAccelerator::DismissStaleBuffers, - weak_this_factory_.GetWeakPtr(), - output_picture_buffers_)); + weak_this_factory_.GetWeakPtr())); base::MessageLoop::current()->PostTask( FROM_HERE, @@ -1166,20 +1180,35 @@ void DXVAVideoDecodeAccelerator::HandleResolutionChanged(int width, weak_this_factory_.GetWeakPtr(), width, height)); - - output_picture_buffers_.clear(); } -void DXVAVideoDecodeAccelerator::DismissStaleBuffers( - const OutputBuffers& picture_buffers) { - OutputBuffers::const_iterator index; +void DXVAVideoDecodeAccelerator::DismissStaleBuffers() { + OutputBuffers::iterator index; - for (index = picture_buffers.begin(); - index != picture_buffers.end(); + for (index = output_picture_buffers_.begin(); + index != output_picture_buffers_.end(); ++index) { - DVLOG(1) << "Dismissing picture id: " << index->second->id(); - client_->DismissPictureBuffer(index->second->id()); + if (index->second->available()) { + DVLOG(1) << "Dismissing picture id: " << index->second->id(); + client_->DismissPictureBuffer(index->second->id()); + } else { + // Move to |stale_output_picture_buffers_| for deferred deletion. + stale_output_picture_buffers_.insert( + std::make_pair(index->first, index->second)); + } } + + output_picture_buffers_.clear(); +} + +void DXVAVideoDecodeAccelerator::DeferredDismissStaleBuffer( + int32 picture_buffer_id) { + OutputBuffers::iterator it = stale_output_picture_buffers_.find( + picture_buffer_id); + DCHECK(it != stale_output_picture_buffers_.end()); + DVLOG(1) << "Dismissing picture id: " << it->second->id(); + client_->DismissPictureBuffer(it->second->id()); + stale_output_picture_buffers_.erase(it); } } // namespace content diff --git a/content/common/gpu/media/dxva_video_decode_accelerator.h b/content/common/gpu/media/dxva_video_decode_accelerator.h index 0b92551e5bef3..65b148b2c91eb 100644 --- a/content/common/gpu/media/dxva_video_decode_accelerator.h +++ b/content/common/gpu/media/dxva_video_decode_accelerator.h @@ -151,7 +151,10 @@ class CONTENT_EXPORT DXVAVideoDecodeAccelerator typedef std::map > OutputBuffers; // Tells the client to dismiss the stale picture buffers passed in. - void DismissStaleBuffers(const OutputBuffers& picture_buffers); + void DismissStaleBuffers(); + + // Called after the client indicates we can recycle a stale picture buffer. + void DeferredDismissStaleBuffer(int32 picture_buffer_id); // To expose client callbacks from VideoDecodeAccelerator. media::VideoDecodeAccelerator::Client* client_; @@ -196,6 +199,12 @@ class CONTENT_EXPORT DXVAVideoDecodeAccelerator // The key is the picture buffer id. OutputBuffers output_picture_buffers_; + // After a resolution change there may be a few output buffers which have yet + // to be displayed so they cannot be dismissed immediately. We move them from + // |output_picture_buffers_| to this map so they may be dismissed once they + // become available. + OutputBuffers stale_output_picture_buffers_; + // Set to true if we requested picture slots from the client. bool pictures_requested_; diff --git a/content/common/gpu/media/gpu_video_decode_accelerator.cc b/content/common/gpu/media/gpu_video_decode_accelerator.cc index 6ce9330f8c2c7..c1cd5538e122f 100644 --- a/content/common/gpu/media/gpu_video_decode_accelerator.cc +++ b/content/common/gpu/media/gpu_video_decode_accelerator.cc @@ -212,7 +212,8 @@ void GpuVideoDecodeAccelerator::PictureReady( if (!Send(new AcceleratedVideoDecoderHostMsg_PictureReady( host_route_id_, picture.picture_buffer_id(), - picture.bitstream_buffer_id()))) { + picture.bitstream_buffer_id(), + picture.visible_rect()))) { DLOG(ERROR) << "Send(AcceleratedVideoDecoderHostMsg_PictureReady) failed"; } } diff --git a/content/common/gpu/media/gpu_video_encode_accelerator.cc b/content/common/gpu/media/gpu_video_encode_accelerator.cc index 52ecab8e7242c..099c456ed812d 100644 --- a/content/common/gpu/media/gpu_video_encode_accelerator.cc +++ b/content/common/gpu/media/gpu_video_encode_accelerator.cc @@ -191,8 +191,8 @@ void GpuVideoEncodeAccelerator::CreateEncoder() { encoder_.reset(new V4L2VideoEncodeAccelerator(device.Pass())); #elif defined(ARCH_CPU_X86_FAMILY) - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (cmd_line->HasSwitch(switches::kEnableVaapiAcceleratedVideoEncode)) + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + if (!cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode)) encoder_.reset(new VaapiVideoEncodeAccelerator(gfx::GetXDisplay())); #endif #elif defined(OS_ANDROID) && defined(ENABLE_WEBRTC) diff --git a/content/common/gpu/media/rendering_helper.cc b/content/common/gpu/media/rendering_helper.cc index 3abc30ea197f3..ce2f94b16d405 100644 --- a/content/common/gpu/media/rendering_helper.cc +++ b/content/common/gpu/media/rendering_helper.cc @@ -9,6 +9,7 @@ #include #include "base/bind.h" +#include "base/callback_helpers.h" #include "base/command_line.h" #include "base/mac/scoped_nsautorelease_pool.h" #include "base/message_loop/message_loop.h" @@ -60,9 +61,28 @@ RenderingHelperParams::RenderingHelperParams() {} RenderingHelperParams::~RenderingHelperParams() {} +VideoFrameTexture::VideoFrameTexture(uint32 texture_target, + uint32 texture_id, + const base::Closure& no_longer_needed_cb) + : texture_target_(texture_target), + texture_id_(texture_id), + no_longer_needed_cb_(no_longer_needed_cb) { + DCHECK(!no_longer_needed_cb_.is_null()); +} + +VideoFrameTexture::~VideoFrameTexture() { + base::ResetAndReturn(&no_longer_needed_cb_).Run(); +} + +RenderingHelper::RenderedVideo::RenderedVideo() : last_frame_rendered(false) { +} + +RenderingHelper::RenderedVideo::~RenderedVideo() { +} + // static bool RenderingHelper::InitializeOneOff() { - CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); #if GL_VARIANT_GLX cmd_line->AppendSwitchASCII(switches::kUseGL, gfx::kGLImplementationDesktopName); @@ -78,15 +98,15 @@ RenderingHelper::RenderingHelper() { } RenderingHelper::~RenderingHelper() { - CHECK_EQ(clients_.size(), 0U) << "Must call UnInitialize before dtor."; + CHECK_EQ(videos_.size(), 0U) << "Must call UnInitialize before dtor."; Clear(); } void RenderingHelper::Initialize(const RenderingHelperParams& params, base::WaitableEvent* done) { - // Use cients_.size() != 0 as a proxy for the class having already been + // Use videos_.size() != 0 as a proxy for the class having already been // Initialize()'d, and UnInitialize() before continuing. - if (clients_.size()) { + if (videos_.size()) { base::WaitableEvent done(false, false); UnInitialize(&done); done.Wait(); @@ -153,12 +173,12 @@ void RenderingHelper::Initialize(const RenderingHelperParams& params, NULL, gl_surface_, gfx::PreferIntegratedGpu); gl_context_->MakeCurrent(gl_surface_); - clients_ = params.clients; - CHECK_GT(clients_.size(), 0U); - LayoutRenderingAreas(); + CHECK_GT(params.window_sizes.size(), 0U); + videos_.resize(params.window_sizes.size()); + LayoutRenderingAreas(params.window_sizes); if (render_as_thumbnails_) { - CHECK_EQ(clients_.size(), 1U); + CHECK_EQ(videos_.size(), 1U); GLint max_texture_size; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); @@ -370,6 +390,31 @@ void RenderingHelper::RenderThumbnail(uint32 texture_target, ++frame_count_; } +void RenderingHelper::QueueVideoFrame( + size_t window_id, + scoped_refptr video_frame) { + CHECK_EQ(base::MessageLoop::current(), message_loop_); + RenderedVideo* video = &videos_[window_id]; + + // Pop the last frame if it has been rendered. + if (video->last_frame_rendered) { + // When last_frame_rendered is true, we should have only one pending frame. + // Since we are going to have a new frame, we can release the pending one. + DCHECK(video->pending_frames.size() == 1); + video->pending_frames.pop(); + video->last_frame_rendered = false; + } + + video->pending_frames.push(video_frame); +} + +void RenderingHelper::DropPendingFrames(size_t window_id) { + CHECK_EQ(base::MessageLoop::current(), message_loop_); + RenderedVideo* video = &videos_[window_id]; + video->pending_frames = std::queue >(); + video->last_frame_rendered = false; +} + void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { // The ExternalOES sampler is bound to GL_TEXTURE1 and the Texture2D sampler // is bound to GL_TEXTURE0. @@ -385,6 +430,7 @@ void RenderingHelper::RenderTexture(uint32 texture_target, uint32 texture_id) { } void RenderingHelper::DeleteTexture(uint32 texture_id) { + CHECK_EQ(base::MessageLoop::current(), message_loop_); glDeleteTextures(1, &texture_id); CHECK_EQ(static_cast(glGetError()), GL_NO_ERROR); } @@ -398,7 +444,7 @@ void* RenderingHelper::GetGLDisplay() { } void RenderingHelper::Clear() { - clients_.clear(); + videos_.clear(); message_loop_ = NULL; gl_context_ = NULL; gl_surface_ = NULL; @@ -461,16 +507,30 @@ void RenderingHelper::RenderContent() { CHECK_EQ(base::MessageLoop::current(), message_loop_); glUniform1i(glGetUniformLocation(program_, "tex_flip"), 1); + // Frames that will be returned to the client (via the no_longer_needed_cb) + // after this vector falls out of scope at the end of this method. We need + // to keep references to them until after SwapBuffers() call below. + std::vector > frames_to_be_returned; + if (render_as_thumbnails_) { // In render_as_thumbnails_ mode, we render the FBO content on the // screen instead of the decoded textures. - GLSetViewPort(render_areas_[0]); + GLSetViewPort(videos_[0].render_area); RenderTexture(GL_TEXTURE_2D, thumbnails_texture_id_); } else { - for (size_t i = 0; i < clients_.size(); ++i) { - if (clients_[i]) { - GLSetViewPort(render_areas_[i]); - clients_[i]->RenderContent(this); + for (size_t i = 0; i < videos_.size(); ++i) { + RenderedVideo* video = &videos_[i]; + if (video->pending_frames.empty()) + continue; + scoped_refptr frame = video->pending_frames.front(); + GLSetViewPort(video->render_area); + RenderTexture(frame->texture_target(), frame->texture_id()); + + if (video->pending_frames.size() > 1) { + frames_to_be_returned.push_back(video->pending_frames.front()); + video->pending_frames.pop(); + } else { + video->last_frame_rendered = true; } } } @@ -492,11 +552,12 @@ static void ScaleAndCalculateOffsets(std::vector* lengths, } } -void RenderingHelper::LayoutRenderingAreas() { +void RenderingHelper::LayoutRenderingAreas( + const std::vector& window_sizes) { // Find the number of colums and rows. - // The smallest n * n or n * (n + 1) > number of clients. - size_t cols = sqrt(clients_.size() - 1) + 1; - size_t rows = (clients_.size() + cols - 1) / cols; + // The smallest n * n or n * (n + 1) > number of windows. + size_t cols = sqrt(videos_.size() - 1) + 1; + size_t rows = (videos_.size() + cols - 1) / cols; // Find the widths and heights of the grid. std::vector widths(cols); @@ -504,31 +565,30 @@ void RenderingHelper::LayoutRenderingAreas() { std::vector offset_x(cols); std::vector offset_y(rows); - for (size_t i = 0; i < clients_.size(); ++i) { - const gfx::Size& window_size = clients_[i]->GetWindowSize(); - widths[i % cols] = std::max(widths[i % cols], window_size.width()); - heights[i / cols] = std::max(heights[i / cols], window_size.height()); + for (size_t i = 0; i < window_sizes.size(); ++i) { + const gfx::Size& size = window_sizes[i]; + widths[i % cols] = std::max(widths[i % cols], size.width()); + heights[i / cols] = std::max(heights[i / cols], size.height()); } ScaleAndCalculateOffsets(&widths, &offset_x, screen_size_.width()); ScaleAndCalculateOffsets(&heights, &offset_y, screen_size_.height()); // Put each render_area_ in the center of each cell. - render_areas_.clear(); - for (size_t i = 0; i < clients_.size(); ++i) { - const gfx::Size& window_size = clients_[i]->GetWindowSize(); + for (size_t i = 0; i < window_sizes.size(); ++i) { + const gfx::Size& size = window_sizes[i]; float scale = - std::min(static_cast(widths[i % cols]) / window_size.width(), - static_cast(heights[i / cols]) / window_size.height()); + std::min(static_cast(widths[i % cols]) / size.width(), + static_cast(heights[i / cols]) / size.height()); // Don't scale up the texture. scale = std::min(1.0f, scale); - size_t w = scale * window_size.width(); - size_t h = scale * window_size.height(); + size_t w = scale * size.width(); + size_t h = scale * size.height(); size_t x = offset_x[i % cols] + (widths[i % cols] - w) / 2; size_t y = offset_y[i / cols] + (heights[i / cols] - h) / 2; - render_areas_.push_back(gfx::Rect(x, y, w, h)); + videos_[i].render_area = gfx::Rect(x, y, w, h); } } } // namespace content diff --git a/content/common/gpu/media/rendering_helper.h b/content/common/gpu/media/rendering_helper.h index a2dfb1bbf4926..6b0013f5b93b1 100644 --- a/content/common/gpu/media/rendering_helper.h +++ b/content/common/gpu/media/rendering_helper.h @@ -6,6 +6,7 @@ #define CONTENT_COMMON_GPU_MEDIA_RENDERING_HELPER_H_ #include +#include #include #include "base/basictypes.h" @@ -24,25 +25,53 @@ class WaitableEvent; namespace content { -struct RenderingHelperParams; +class VideoFrameTexture : public base::RefCounted { + public: + uint32 texture_id() const { return texture_id_; } + uint32 texture_target() const { return texture_target_; } + + VideoFrameTexture(uint32 texture_target, + uint32 texture_id, + const base::Closure& no_longer_needed_cb); + + private: + friend class base::RefCounted; + + uint32 texture_target_; + uint32 texture_id_; + base::Closure no_longer_needed_cb_; + + ~VideoFrameTexture(); +}; + +struct RenderingHelperParams { + RenderingHelperParams(); + ~RenderingHelperParams(); + + // The rendering FPS. + int rendering_fps; + + // The desired size of each window. We play each stream in its own window + // on the screen. + std::vector window_sizes; + + // The members below are only used for the thumbnail mode where all frames + // are rendered in sequence onto one FBO for comparison/verification purposes. + + // Whether the frames are rendered as scaled thumbnails within a + // larger FBO that is in turn rendered to the window. + bool render_as_thumbnails; + // The size of the FBO containing all visible thumbnails. + gfx::Size thumbnails_page_size; + // The size of each thumbnail within the FBO. + gfx::Size thumbnail_size; +}; // Creates and draws textures used by the video decoder. // This class is not thread safe and thus all the methods of this class // (except for ctor/dtor) ensure they're being run on a single thread. class RenderingHelper { public: - // Interface for the content provider of the RenderingHelper. - class Client { - public: - // Callback to tell client to render the content. - virtual void RenderContent(RenderingHelper* helper) = 0; - - // Callback to get the desired window size of the client. - virtual const gfx::Size& GetWindowSize() = 0; - - protected: - virtual ~Client() {} - }; RenderingHelper(); ~RenderingHelper(); @@ -67,9 +96,12 @@ class RenderingHelper { // |texture_target|. void RenderThumbnail(uint32 texture_target, uint32 texture_id); - // Render |texture_id| to the current view port of the screen using target - // |texture_target|. - void RenderTexture(uint32 texture_target, uint32 texture_id); + // Queues the |video_frame| for rendering. + void QueueVideoFrame(size_t window_id, + scoped_refptr video_frame); + + // Drops all the pending video frames of the specified window. + void DropPendingFrames(size_t window_id); // Delete |texture_id|. void DeleteTexture(uint32 texture_id); @@ -87,11 +119,33 @@ class RenderingHelper { base::WaitableEvent* done); private: + struct RenderedVideo { + // The rect on the screen where the video will be rendered. + gfx::Rect render_area; + + // True if the last (and the only one) frame in pending_frames has + // been rendered. We keep the last remaining frame in pending_frames even + // after it has been rendered, so that we have something to display if the + // client is falling behind on providing us with new frames during + // timer-driven playback. + bool last_frame_rendered; + + // The video frames pending for rendering. + std::queue > pending_frames; + + RenderedVideo(); + ~RenderedVideo(); + }; + void Clear(); void RenderContent(); - void LayoutRenderingAreas(); + void LayoutRenderingAreas(const std::vector& window_sizes); + + // Render |texture_id| to the current view port of the screen using target + // |texture_target|. + void RenderTexture(uint32 texture_target, uint32 texture_id); // Timer to trigger the RenderContent() repeatly. scoped_ptr > render_timer_; @@ -104,10 +158,7 @@ class RenderingHelper { gfx::Size screen_size_; - // The rendering area of each window on the screen. - std::vector render_areas_; - - std::vector > clients_; + std::vector videos_; bool render_as_thumbnails_; int frame_count_; @@ -121,24 +172,6 @@ class RenderingHelper { DISALLOW_COPY_AND_ASSIGN(RenderingHelper); }; -struct RenderingHelperParams { - RenderingHelperParams(); - ~RenderingHelperParams(); - - // The rendering FPS. - int rendering_fps; - - // The clients who provide the content for rendering. - std::vector > clients; - - // Whether the frames are rendered as scaled thumbnails within a - // larger FBO that is in turn rendered to the window. - bool render_as_thumbnails; - // The size of the FBO containing all visible thumbnails. - gfx::Size thumbnails_page_size; - // The size of each thumbnail within the FBO. - gfx::Size thumbnail_size; -}; } // namespace content #endif // CONTENT_COMMON_GPU_MEDIA_RENDERING_HELPER_H_ diff --git a/content/common/gpu/media/v4l2_video_decode_accelerator.cc b/content/common/gpu/media/v4l2_video_decode_accelerator.cc index 1b47516c0e7f0..cea1c7b3bb238 100644 --- a/content/common/gpu/media/v4l2_video_decode_accelerator.cc +++ b/content/common/gpu/media/v4l2_video_decode_accelerator.cc @@ -224,8 +224,8 @@ bool V4L2VideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile, case media::H264PROFILE_HIGH: DVLOG(2) << "Initialize(): profile H264PROFILE_HIGH"; break; - case media::VP8PROFILE_MAIN: - DVLOG(2) << "Initialize(): profile VP8PROFILE_MAIN"; + case media::VP8PROFILE_ANY: + DVLOG(2) << "Initialize(): profile VP8PROFILE_ANY"; break; default: DLOG(ERROR) << "Initialize(): unsupported profile=" << profile; @@ -1092,7 +1092,9 @@ void V4L2VideoDecodeAccelerator::Dequeue() { DVLOG(3) << "Dequeue(): returning input_id=" << dqbuf.timestamp.tv_sec << " as picture_id=" << output_record.picture_id; const media::Picture& picture = - media::Picture(output_record.picture_id, dqbuf.timestamp.tv_sec); + media::Picture(output_record.picture_id, + dqbuf.timestamp.tv_sec, + gfx::Rect(frame_buffer_size_)); pending_picture_ready_.push( PictureRecord(output_record.cleared, picture)); SendPictureReady(); diff --git a/content/common/gpu/media/v4l2_video_encode_accelerator.cc b/content/common/gpu/media/v4l2_video_encode_accelerator.cc index a5cb4fb00f87f..25320f4603809 100644 --- a/content/common/gpu/media/v4l2_video_encode_accelerator.cc +++ b/content/common/gpu/media/v4l2_video_encode_accelerator.cc @@ -286,9 +286,9 @@ V4L2VideoEncodeAccelerator::GetSupportedProfiles() { std::vector profiles; SupportedProfile profile; - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (cmd_line->HasSwitch(switches::kEnableWebRtcHWVp8Encoding)) { - profile.profile = media::VP8PROFILE_MAIN; + profile.profile = media::VP8PROFILE_ANY; profile.max_resolution.SetSize(1920, 1088); profile.max_framerate.numerator = 30; profile.max_framerate.denominator = 1; diff --git a/content/common/gpu/media/vaapi_h264_decoder_unittest.cc b/content/common/gpu/media/vaapi_h264_decoder_unittest.cc index b14c50b153e8e..9c7822b85d5ab 100644 --- a/content/common/gpu/media/vaapi_h264_decoder_unittest.cc +++ b/content/common/gpu/media/vaapi_h264_decoder_unittest.cc @@ -350,7 +350,7 @@ TEST(VaapiH264DecoderTest, TestDecode) { int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args. - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); // Needed to enable DVLOG through --vmodule. logging::LoggingSettings settings; @@ -358,11 +358,11 @@ int main(int argc, char** argv) { CHECK(logging::InitLogging(settings)); // Process command line. - CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); CHECK(cmd_line); - CommandLine::SwitchMap switches = cmd_line->GetSwitches(); - for (CommandLine::SwitchMap::const_iterator it = switches.begin(); + base::CommandLine::SwitchMap switches = cmd_line->GetSwitches(); + for (base::CommandLine::SwitchMap::const_iterator it = switches.begin(); it != switches.end(); ++it) { if (it->first == "input_file") { diff --git a/content/common/gpu/media/vaapi_video_decode_accelerator.cc b/content/common/gpu/media/vaapi_video_decode_accelerator.cc index afcfc8a3b0632..0b3001272ac8e 100644 --- a/content/common/gpu/media/vaapi_video_decode_accelerator.cc +++ b/content/common/gpu/media/vaapi_video_decode_accelerator.cc @@ -371,8 +371,11 @@ void VaapiVideoDecodeAccelerator::OutputPicture( TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_); DVLOG(4) << "Notifying output picture id " << output_id << " for input "<< input_id << " is ready"; + // TODO(posciak): Use visible size from decoder here instead + // (crbug.com/402760). if (client_) - client_->PictureReady(media::Picture(output_id, input_id)); + client_->PictureReady( + media::Picture(output_id, input_id, gfx::Rect(tfp_picture->size()))); } void VaapiVideoDecodeAccelerator::TryOutputSurface() { diff --git a/content/common/gpu/media/vaapi_video_encode_accelerator.cc b/content/common/gpu/media/vaapi_video_encode_accelerator.cc index fa45ca91b2e62..022bec9a6c0a9 100644 --- a/content/common/gpu/media/vaapi_video_encode_accelerator.cc +++ b/content/common/gpu/media/vaapi_video_encode_accelerator.cc @@ -109,8 +109,8 @@ std::vector VaapiVideoEncodeAccelerator::GetSupportedProfiles() { std::vector profiles; - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); - if (!cmd_line->HasSwitch(switches::kEnableVaapiAcceleratedVideoEncode)) + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); + if (cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode)) return profiles; SupportedProfile profile; diff --git a/content/common/gpu/media/video_decode_accelerator_unittest.cc b/content/common/gpu/media/video_decode_accelerator_unittest.cc index c2b77a340777e..65cc9397a6e90 100644 --- a/content/common/gpu/media/video_decode_accelerator_unittest.cc +++ b/content/common/gpu/media/video_decode_accelerator_unittest.cc @@ -193,9 +193,10 @@ enum ClientState { // the TESTs below. class GLRenderingVDAClient : public VideoDecodeAccelerator::Client, - public RenderingHelper::Client, public base::SupportsWeakPtr { public: + // |window_id| the window_id of the client, which is used to identify the + // rendering area in the |rendering_helper|. // Doesn't take ownership of |rendering_helper| or |note|, which must outlive // |*this|. // |num_play_throughs| indicates how many times to play through the video. @@ -212,7 +213,8 @@ class GLRenderingVDAClient // will start delaying the call to ReusePictureBuffer() for kReuseDelay. // |decode_calls_per_second| is the number of VDA::Decode calls per second. // If |decode_calls_per_second| > 0, |num_in_flight_decodes| must be 1. - GLRenderingVDAClient(RenderingHelper* rendering_helper, + GLRenderingVDAClient(size_t window_id, + RenderingHelper* rendering_helper, ClientStateNotification* note, const std::string& encoded_data, int num_in_flight_decodes, @@ -242,14 +244,8 @@ class GLRenderingVDAClient virtual void NotifyResetDone() OVERRIDE; virtual void NotifyError(VideoDecodeAccelerator::Error error) OVERRIDE; - // RenderingHelper::Client implementation. - virtual void RenderContent(RenderingHelper*) OVERRIDE; - virtual const gfx::Size& GetWindowSize() OVERRIDE; - void OutputFrameDeliveryTimes(base::File* output); - void NotifyFrameDropped(int32 picture_buffer_id); - // Simple getters for inspecting the state of the Client. int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } int num_skipped_fragments() { return num_skipped_fragments_; } @@ -285,6 +281,7 @@ class GLRenderingVDAClient // Request decode of the next fragment in the encoded data. void DecodeNextFragment(); + size_t window_id_; RenderingHelper* rendering_helper_; gfx::Size frame_size_; std::string encoded_data_; @@ -319,13 +316,12 @@ class GLRenderingVDAClient // The number of VDA::Decode calls per second. This is to simulate webrtc. int decode_calls_per_second_; bool render_as_thumbnails_; - bool pending_picture_updated_; - std::deque pending_picture_buffer_ids_; DISALLOW_IMPLICIT_CONSTRUCTORS(GLRenderingVDAClient); }; GLRenderingVDAClient::GLRenderingVDAClient( + size_t window_id, RenderingHelper* rendering_helper, ClientStateNotification* note, const std::string& encoded_data, @@ -340,7 +336,8 @@ GLRenderingVDAClient::GLRenderingVDAClient( int delay_reuse_after_frame_num, int decode_calls_per_second, bool render_as_thumbnails) - : rendering_helper_(rendering_helper), + : window_id_(window_id), + rendering_helper_(rendering_helper), frame_size_(frame_width, frame_height), encoded_data_(encoded_data), num_in_flight_decodes_(num_in_flight_decodes), @@ -360,8 +357,7 @@ GLRenderingVDAClient::GLRenderingVDAClient( suppress_rendering_(suppress_rendering), delay_reuse_after_frame_num_(delay_reuse_after_frame_num), decode_calls_per_second_(decode_calls_per_second), - render_as_thumbnails_(render_as_thumbnails), - pending_picture_updated_(true) { + render_as_thumbnails_(render_as_thumbnails) { CHECK_GT(num_in_flight_decodes, 0); CHECK_GT(num_play_throughs, 0); // |num_in_flight_decodes_| is unsupported if |decode_calls_per_second_| > 0. @@ -459,42 +455,6 @@ void GLRenderingVDAClient::DismissPictureBuffer(int32 picture_buffer_id) { picture_buffers_by_id_.erase(it); } -void GLRenderingVDAClient::RenderContent(RenderingHelper*) { - CHECK(!render_as_thumbnails_); - - // No decoded texture for rendering yet, just skip. - if (pending_picture_buffer_ids_.size() == 0) - return; - - int32 buffer_id = pending_picture_buffer_ids_.front(); - media::PictureBuffer* picture_buffer = picture_buffers_by_id_[buffer_id]; - - CHECK(picture_buffer); - if (!pending_picture_updated_) { - // Frame dropped, just redraw the last texture. - rendering_helper_->RenderTexture(texture_target_, - picture_buffer->texture_id()); - return; - } - - base::TimeTicks now = base::TimeTicks::Now(); - frame_delivery_times_.push_back(now); - - rendering_helper_->RenderTexture(texture_target_, - picture_buffer->texture_id()); - - if (pending_picture_buffer_ids_.size() == 1) { - pending_picture_updated_ = false; - } else { - pending_picture_buffer_ids_.pop_front(); - ReturnPicture(buffer_id); - } -} - -const gfx::Size& GLRenderingVDAClient::GetWindowSize() { - return render_as_thumbnails_ ? kThumbnailsPageSize : frame_size_; -} - void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { // We shouldn't be getting pictures delivered after Reset has completed. CHECK_LT(state_, CS_RESET); @@ -503,6 +463,9 @@ void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { return; base::TimeTicks now = base::TimeTicks::Now(); + + frame_delivery_times_.push_back(now); + // Save the decode time of this picture. std::map::iterator it = decode_start_time_.find(picture.bitstream_buffer_id()); @@ -524,25 +487,22 @@ void GLRenderingVDAClient::PictureReady(const media::Picture& picture) { encoded_data_next_pos_to_decode_ = 0; } + media::PictureBuffer* picture_buffer = + picture_buffers_by_id_[picture.picture_buffer_id()]; + CHECK(picture_buffer); + + scoped_refptr video_frame = + new VideoFrameTexture(texture_target_, + picture_buffer->texture_id(), + base::Bind(&GLRenderingVDAClient::ReturnPicture, + AsWeakPtr(), + picture.picture_buffer_id())); + if (render_as_thumbnails_) { - frame_delivery_times_.push_back(now); - media::PictureBuffer* picture_buffer = - picture_buffers_by_id_[picture.picture_buffer_id()]; - CHECK(picture_buffer); - rendering_helper_->RenderThumbnail(texture_target_, - picture_buffer->texture_id()); - ReturnPicture(picture.picture_buffer_id()); + rendering_helper_->RenderThumbnail(video_frame->texture_target(), + video_frame->texture_id()); } else if (!suppress_rendering_) { - // Keep the picture for rendering. - pending_picture_buffer_ids_.push_back(picture.picture_buffer_id()); - if (pending_picture_buffer_ids_.size() > 1 && !pending_picture_updated_) { - ReturnPicture(pending_picture_buffer_ids_.front()); - pending_picture_buffer_ids_.pop_front(); - pending_picture_updated_ = true; - } - } else { - frame_delivery_times_.push_back(now); - ReturnPicture(picture.picture_buffer_id()); + rendering_helper_->QueueVideoFrame(window_id_, video_frame); } } @@ -590,12 +550,7 @@ void GLRenderingVDAClient::NotifyResetDone() { if (decoder_deleted()) return; - // Clear pending_pictures and reuse them. - while (!pending_picture_buffer_ids_.empty()) { - decoder_->ReusePictureBuffer(pending_picture_buffer_ids_.front()); - pending_picture_buffer_ids_.pop_front(); - } - pending_picture_updated_ = true; + rendering_helper_->DropPendingFrames(window_id_); if (reset_after_frame_num_ == MID_STREAM_RESET) { reset_after_frame_num_ = END_OF_STREAM_RESET; @@ -637,10 +592,6 @@ void GLRenderingVDAClient::OutputFrameDeliveryTimes(base::File* output) { } } -void GLRenderingVDAClient::NotifyFrameDropped(int32 picture_buffer_id) { - decoder_->ReusePictureBuffer(picture_buffer_id); -} - static bool LookingAtNAL(const std::string& encoded, size_t pos) { return encoded[pos] == 0 && encoded[pos + 1] == 0 && encoded[pos + 2] == 0 && encoded[pos + 3] == 1; @@ -1127,7 +1078,8 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { } GLRenderingVDAClient* client = - new GLRenderingVDAClient(&rendering_helper_, + new GLRenderingVDAClient(index, + &rendering_helper_, note, video_file->data_str, num_in_flight_decodes, @@ -1143,7 +1095,10 @@ TEST_P(VideoDecodeAcceleratorParamTest, TestSimpleDecode) { render_as_thumbnails); clients[index] = client; - helper_params.clients.push_back(client->AsWeakPtr()); + helper_params.window_sizes.push_back( + render_as_thumbnails + ? kThumbnailsPageSize + : gfx::Size(video_file->width, video_file->height)); } InitializeRenderingHelper(helper_params); @@ -1376,7 +1331,8 @@ TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) { ClientStateNotification* note = new ClientStateNotification(); GLRenderingVDAClient* client = - new GLRenderingVDAClient(&rendering_helper_, + new GLRenderingVDAClient(0, + &rendering_helper_, note, test_video_files_[0]->data_str, 1, @@ -1390,7 +1346,8 @@ TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) { std::numeric_limits::max(), kWebRtcDecodeCallsPerSecond, false /* render_as_thumbnail */); - helper_params.clients.push_back(client->AsWeakPtr()); + helper_params.window_sizes.push_back( + gfx::Size(test_video_files_[0]->width, test_video_files_[0]->height)); InitializeRenderingHelper(helper_params); CreateAndStartDecoder(client, note); WaitUntilDecodeFinish(note); @@ -1420,17 +1377,17 @@ TEST_F(VideoDecodeAcceleratorTest, TestDecodeTimeMedian) { int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args. - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); // Needed to enable DVLOG through --vmodule. logging::LoggingSettings settings; settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; CHECK(logging::InitLogging(settings)); - CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); DCHECK(cmd_line); - CommandLine::SwitchMap switches = cmd_line->GetSwitches(); + base::CommandLine::SwitchMap switches = cmd_line->GetSwitches(); for (CommandLine::SwitchMap::const_iterator it = switches.begin(); it != switches.end(); ++it) { if (it->first == "test_video_data") { diff --git a/content/common/gpu/media/video_encode_accelerator_unittest.cc b/content/common/gpu/media/video_encode_accelerator_unittest.cc index b07211d0867f5..9d1e3b69dc7c9 100644 --- a/content/common/gpu/media/video_encode_accelerator_unittest.cc +++ b/content/common/gpu/media/video_encode_accelerator_unittest.cc @@ -1029,7 +1029,7 @@ INSTANTIATE_TEST_CASE_P( int main(int argc, char** argv) { testing::InitGoogleTest(&argc, argv); // Removes gtest-specific args. - CommandLine::Init(argc, argv); + base::CommandLine::Init(argc, argv); base::ShadowingAtExitManager at_exit_manager; scoped_ptr test_stream_data( @@ -1043,11 +1043,11 @@ int main(int argc, char** argv) { settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; CHECK(logging::InitLogging(settings)); - CommandLine* cmd_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); DCHECK(cmd_line); - CommandLine::SwitchMap switches = cmd_line->GetSwitches(); - for (CommandLine::SwitchMap::const_iterator it = switches.begin(); + base::CommandLine::SwitchMap switches = cmd_line->GetSwitches(); + for (base::CommandLine::SwitchMap::const_iterator it = switches.begin(); it != switches.end(); ++it) { if (it->first == "test_stream_data") { diff --git a/content/common/gpu/media/vt_video_decode_accelerator.cc b/content/common/gpu/media/vt_video_decode_accelerator.cc index 5b916501b1b3f..8e4c40819c9d6 100644 --- a/content/common/gpu/media/vt_video_decode_accelerator.cc +++ b/content/common/gpu/media/vt_video_decode_accelerator.cc @@ -376,7 +376,8 @@ void VTVideoDecodeAccelerator::SendPictures() { 0)); // plane picture_bindings_[picture_id] = frame.image_buffer; - client_->PictureReady(media::Picture(picture_id, frame.bitstream_id)); + client_->PictureReady(media::Picture( + picture_id, frame.bitstream_id, gfx::Rect(texture_size_))); client_->NotifyEndOfBitstreamBuffer(frame.bitstream_id); } diff --git a/content/common/gpu/texture_image_transport_surface.cc b/content/common/gpu/texture_image_transport_surface.cc index 0ab94e3ed75bc..f51f54515229f 100644 --- a/content/common/gpu/texture_image_transport_surface.cc +++ b/content/common/gpu/texture_image_transport_surface.cc @@ -73,7 +73,8 @@ bool TextureImageTransportSurface::Initialize() { GpuChannel* parent_channel = manager->LookupChannel(handle_.parent_client_id); if (parent_channel) { - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kUIPrioritizeInGpuProcess)) helper_->SetPreemptByFlag(parent_channel->GetPreemptionFlag()); } diff --git a/content/common/handle_enumerator_win.cc b/content/common/handle_enumerator_win.cc index 0b8cbe5fd4208..981655065d978 100644 --- a/content/common/handle_enumerator_win.cc +++ b/content/common/handle_enumerator_win.cc @@ -52,7 +52,7 @@ const size_t kMaxHandleNameLength = 1024; void HandleEnumerator::EnumerateHandles() { sandbox::HandleTable handles; std::string process_type = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kProcessType); base::string16 output = ASCIIToUTF16(process_type); output.append(ASCIIToUTF16(" process - Handles at shutdown:\n")); diff --git a/content/common/input/did_overscroll_params.cc b/content/common/input/did_overscroll_params.cc new file mode 100644 index 0000000000000..2b69d8600e7ce --- /dev/null +++ b/content/common/input/did_overscroll_params.cc @@ -0,0 +1,15 @@ +// 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 "content/common/input/did_overscroll_params.h" + +namespace content { + +DidOverscrollParams::DidOverscrollParams() { +} + +DidOverscrollParams::~DidOverscrollParams() { +} + +} // namespace content diff --git a/content/common/input/did_overscroll_params.h b/content/common/input/did_overscroll_params.h index 4f6a7f1f22694..7db3ac430098c 100644 --- a/content/common/input/did_overscroll_params.h +++ b/content/common/input/did_overscroll_params.h @@ -5,14 +5,19 @@ #ifndef CONTENT_COMMON_INPUT_DID_OVERSCROLL_PARAMS_H_ #define CONTENT_COMMON_INPUT_DID_OVERSCROLL_PARAMS_H_ +#include "content/common/content_export.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/vector2d_f.h" namespace content { -struct DidOverscrollParams { +struct CONTENT_EXPORT DidOverscrollParams { + DidOverscrollParams(); + ~DidOverscrollParams(); gfx::Vector2dF accumulated_overscroll; gfx::Vector2dF latest_overscroll_delta; gfx::Vector2dF current_fling_velocity; + gfx::PointF causal_event_viewport_point; }; } // namespace content diff --git a/content/common/input_messages.h b/content/common/input_messages.h index d00d45d93f2c7..1827212699c9a 100644 --- a/content/common/input_messages.h +++ b/content/common/input_messages.h @@ -59,6 +59,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::DidOverscrollParams) IPC_STRUCT_TRAITS_MEMBER(accumulated_overscroll) IPC_STRUCT_TRAITS_MEMBER(latest_overscroll_delta) IPC_STRUCT_TRAITS_MEMBER(current_fling_velocity) + IPC_STRUCT_TRAITS_MEMBER(causal_event_viewport_point) IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(content::EditCommand) diff --git a/content/common/media/media_player_messages_android.h b/content/common/media/media_player_messages_android.h index 4548f0b849b05..2908a2f84ae54 100644 --- a/content/common/media/media_player_messages_android.h +++ b/content/common/media/media_player_messages_android.h @@ -71,6 +71,7 @@ IPC_STRUCT_BEGIN(MediaPlayerHostMsg_Initialize_Params) IPC_STRUCT_MEMBER(GURL, url) IPC_STRUCT_MEMBER(GURL, first_party_for_cookies) IPC_STRUCT_MEMBER(GURL, frame_url) + IPC_STRUCT_MEMBER(bool, allow_credentials) IPC_STRUCT_END() // Chrome for Android seek message sequence is: diff --git a/content/common/media/media_stream_messages.h b/content/common/media/media_stream_messages.h index e88aeab66ddf3..9434a6d6a44a2 100644 --- a/content/common/media/media_stream_messages.h +++ b/content/common/media/media_stream_messages.h @@ -128,12 +128,11 @@ IPC_MESSAGE_CONTROL2(MediaStreamHostMsg_GetSources, // Request to enumerate devices. // Used by Pepper and WebRTC. -IPC_MESSAGE_CONTROL5(MediaStreamHostMsg_EnumerateDevices, +IPC_MESSAGE_CONTROL4(MediaStreamHostMsg_EnumerateDevices, int /* render frame id */, int /* request id */, content::MediaStreamType /* type */, - GURL /* security origin */, - bool /* hide_labels_if_no_access */) + GURL /* security origin */) // Request to stop enumerating devices. IPC_MESSAGE_CONTROL2(MediaStreamHostMsg_CancelEnumerateDevices, diff --git a/content/common/pepper_plugin_list.cc b/content/common/pepper_plugin_list.cc index fac77f6f2e4bd..3a3e0e076d25a 100644 --- a/content/common/pepper_plugin_list.cc +++ b/content/common/pepper_plugin_list.cc @@ -36,11 +36,12 @@ void ComputePluginsFromCommandLine(std::vector* plugins) { max_plugins_to_register_from_command_line_exceeds_limit); bool out_of_process = true; - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kPpapiInProcess)) + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kPpapiInProcess)) out_of_process = false; const std::string value = - CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( switches::kRegisterPepperPlugins); if (value.empty()) return; diff --git a/content/common/plugin_list.cc b/content/common/plugin_list.cc index e89aaf9e84964..a0f9507ea9d42 100644 --- a/content/common/plugin_list.cc +++ b/content/common/plugin_list.cc @@ -36,7 +36,7 @@ PluginList* PluginList::Singleton() { // static bool PluginList::DebugPluginLoading() { - return CommandLine::ForCurrentProcess()->HasSwitch( + return base::CommandLine::ForCurrentProcess()->HasSwitch( switches::kDebugPluginLoading); } diff --git a/content/common/resource_messages.h b/content/common/resource_messages.h index 345d2fd608768..dc6147a96d2ee 100644 --- a/content/common/resource_messages.h +++ b/content/common/resource_messages.h @@ -16,6 +16,7 @@ #include "ipc/ipc_message_macros.h" #include "net/base/request_priority.h" #include "net/http/http_response_info.h" +#include "net/url_request/redirect_info.h" #ifndef CONTENT_COMMON_RESOURCE_MESSAGES_H_ #define CONTENT_COMMON_RESOURCE_MESSAGES_H_ @@ -121,6 +122,14 @@ IPC_STRUCT_TRAITS_BEGIN(content::ResourceResponseInfo) IPC_STRUCT_TRAITS_MEMBER(original_url_via_service_worker) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(net::RedirectInfo) + IPC_STRUCT_TRAITS_MEMBER(status_code) + IPC_STRUCT_TRAITS_MEMBER(new_method) + IPC_STRUCT_TRAITS_MEMBER(new_url) + IPC_STRUCT_TRAITS_MEMBER(new_first_party_for_cookies) + IPC_STRUCT_TRAITS_MEMBER(new_referrer) +IPC_STRUCT_TRAITS_END() + // Parameters for a resource request. IPC_STRUCT_BEGIN(ResourceHostMsg_Request) // The request method: GET, POST, etc. @@ -185,6 +194,9 @@ IPC_STRUCT_BEGIN(ResourceHostMsg_Request) // True if the request was user initiated. IPC_STRUCT_MEMBER(bool, has_user_gesture) + // True if load timing data should be collected for request. + IPC_STRUCT_MEMBER(bool, enable_load_timing) + // The routing id of the RenderFrame. IPC_STRUCT_MEMBER(int, render_frame_id) @@ -257,10 +269,9 @@ IPC_MESSAGE_CONTROL3(ResourceMsg_UploadProgress, // Sent when the request has been redirected. The receiver is expected to // respond with either a FollowRedirect message (if the redirect is to be // followed) or a CancelRequest message (if it should not be followed). -IPC_MESSAGE_CONTROL4(ResourceMsg_ReceivedRedirect, +IPC_MESSAGE_CONTROL3(ResourceMsg_ReceivedRedirect, int /* request_id */, - GURL /* new_url */, - GURL /* new_first_party_for_cookies */, + net::RedirectInfo /* redirect_info */, content::ResourceResponseHead) // Sent to set the shared memory buffer to be used to transmit response data to diff --git a/content/common/sandbox_init_mac.cc b/content/common/sandbox_init_mac.cc index d6f2112dac625..609517932c2fe 100644 --- a/content/common/sandbox_init_mac.cc +++ b/content/common/sandbox_init_mac.cc @@ -31,7 +31,8 @@ bool GetSandboxTypeFromCommandLine(int* sandbox_type, *sandbox_type = -1; *allowed_dir = base::FilePath(); // Empty by default. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kNoSandbox)) return false; @@ -47,9 +48,6 @@ bool GetSandboxTypeFromCommandLine(int* sandbox_type, *sandbox_type = SANDBOX_TYPE_UTILITY; *allowed_dir = command_line.GetSwitchValuePath(switches::kUtilityProcessAllowedDir); - } else if (process_type == switches::kWorkerProcess) { - // Worker process sandbox. - *sandbox_type = SANDBOX_TYPE_WORKER; } else if (process_type == switches::kGpuProcess) { if (command_line.HasSwitch(switches::kDisableGpuSandbox)) return false; diff --git a/content/common/sandbox_init_win.cc b/content/common/sandbox_init_win.cc index 7d9d920d9db1d..16e3e4afbd4a6 100644 --- a/content/common/sandbox_init_win.cc +++ b/content/common/sandbox_init_win.cc @@ -14,7 +14,8 @@ namespace content { bool InitializeSandbox(sandbox::SandboxInterfaceInfo* sandbox_info) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); sandbox::BrokerServices* broker_services = sandbox_info->broker_services; if (broker_services) { if (!InitBrokerServices(broker_services)) diff --git a/content/common/sandbox_linux/bpf_gpu_policy_linux.cc b/content/common/sandbox_linux/bpf_gpu_policy_linux.cc index c30c166a8f0ad..c603ad5a26404 100644 --- a/content/common/sandbox_linux/bpf_gpu_policy_linux.cc +++ b/content/common/sandbox_linux/bpf_gpu_policy_linux.cc @@ -73,11 +73,12 @@ inline bool IsArchitectureArm() { } bool IsAcceleratedVideoEnabled() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); bool accelerated_encode_enabled = false; #if defined(OS_CHROMEOS) accelerated_encode_enabled = - command_line.HasSwitch(switches::kEnableVaapiAcceleratedVideoEncode); + !command_line.HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode); #endif return !command_line.HasSwitch(switches::kDisableAcceleratedVideoDecode) || accelerated_encode_enabled; @@ -143,12 +144,13 @@ ResultExpr GpuBrokerProcessPolicy::EvaluateSyscall(int sysno) const { } void UpdateProcessTypeToGpuBroker() { - CommandLine::StringVector exec = CommandLine::ForCurrentProcess()->GetArgs(); - CommandLine::Reset(); - CommandLine::Init(0, NULL); - CommandLine::ForCurrentProcess()->InitFromArgv(exec); - CommandLine::ForCurrentProcess()->AppendSwitchASCII(switches::kProcessType, - "gpu-broker"); + base::CommandLine::StringVector exec = + base::CommandLine::ForCurrentProcess()->GetArgs(); + base::CommandLine::Reset(); + base::CommandLine::Init(0, NULL); + base::CommandLine::ForCurrentProcess()->InitFromArgv(exec); + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kProcessType, "gpu-broker"); // Update the process title. The argv was already cached by the call to // SetProcessTitleFromCommandLine in content_main_runner.cc, so we can pass @@ -222,11 +224,21 @@ bool GpuProcessPolicy::PreSandboxHook() { if (IsAcceleratedVideoEnabled()) { const char* I965DrvVideoPath = NULL; +#if defined(OS_TIZEN) + if (IsArchitectureX86_64()) { + // TODO(halton): Add 64-bit VA driver when 64-bit Tizen support + // is ready. + return false; + } else if (IsArchitectureI386()) { + I965DrvVideoPath = "/usr/lib/dri/i965_dri.so"; + } +#else if (IsArchitectureX86_64()) { I965DrvVideoPath = "/usr/lib64/va/drivers/i965_drv_video.so"; } else if (IsArchitectureI386()) { I965DrvVideoPath = "/usr/lib/va/drivers/i965_drv_video.so"; } +#endif dlopen(I965DrvVideoPath, RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE); dlopen("libva.so.1", RTLD_NOW|RTLD_GLOBAL|RTLD_NODELETE); diff --git a/content/common/sandbox_linux/sandbox_linux.cc b/content/common/sandbox_linux/sandbox_linux.cc index 88afef9295e75..7c7c85615b33b 100644 --- a/content/common/sandbox_linux/sandbox_linux.cc +++ b/content/common/sandbox_linux/sandbox_linux.cc @@ -52,7 +52,8 @@ struct FDCloser { }; void LogSandboxStarted(const std::string& sandbox_name) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); const std::string process_type = command_line.GetSwitchValueASCII(switches::kProcessType); const std::string activated_sandbox = @@ -260,7 +261,7 @@ bool LinuxSandbox::StartSeccompBPF(const std::string& process_type) { } bool LinuxSandbox::InitializeSandboxImpl() { - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const std::string process_type = command_line->GetSwitchValueASCII(switches::kProcessType); @@ -335,7 +336,7 @@ bool LinuxSandbox::seccomp_bpf_supported() const { bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) { (void) process_type; #if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) - CommandLine* command_line = CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kNoSandbox)) { return false; } @@ -354,7 +355,6 @@ bool LinuxSandbox::LimitAddressSpace(const std::string& process_type) { // For now, increase limit to 16GB for renderer and worker and gpu processes // to accomodate. if (process_type == switches::kRendererProcess || - process_type == switches::kWorkerProcess || process_type == switches::kGpuProcess) { address_space_limit = 1L << 34; } @@ -393,7 +393,6 @@ void LinuxSandbox::CheckForBrokenPromises(const std::string& process_type) { // Make sure that any promise made with GetStatus() wasn't broken. bool promised_seccomp_bpf_would_start = false; if (process_type == switches::kRendererProcess || - process_type == switches::kWorkerProcess || process_type == switches::kPpapiPluginProcess) { promised_seccomp_bpf_would_start = (sandbox_status_flags_ != kSandboxLinuxInvalid) && diff --git a/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc b/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc index ae856ee714013..86d2c0fc60c78 100644 --- a/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc +++ b/content/common/sandbox_linux/sandbox_seccomp_bpf_linux.cc @@ -114,7 +114,6 @@ ResultExpr AllowAllPolicy::EvaluateSyscall(int sysno) const { // If a BPF policy is engaged for |process_type|, run a few sanity checks. void RunSandboxSanityChecks(const std::string& process_type) { if (process_type == switches::kRendererProcess || - process_type == switches::kWorkerProcess || process_type == switches::kGpuProcess || process_type == switches::kPpapiPluginProcess) { int syscall_ret; @@ -158,7 +157,8 @@ void StartSandboxWithPolicy(sandbox::SandboxBPFPolicy* policy) { // in its dependencies. Make sure to not link things that are not needed. #if !defined(IN_NACL_HELPER) scoped_ptr GetGpuProcessSandbox() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); bool allow_sysv_shm = false; if (command_line.HasSwitch(switches::kGpuSandboxAllowSysVShm)) { DCHECK(IsArchitectureArm()); @@ -174,14 +174,13 @@ scoped_ptr GetGpuProcessSandbox() { } // Initialize the seccomp-bpf sandbox. -bool StartBPFSandbox(const CommandLine& command_line, +bool StartBPFSandbox(const base::CommandLine& command_line, const std::string& process_type) { scoped_ptr policy; if (process_type == switches::kGpuProcess) { policy.reset(GetGpuProcessSandbox().release()); - } else if (process_type == switches::kRendererProcess || - process_type == switches::kWorkerProcess) { + } else if (process_type == switches::kRendererProcess) { policy.reset(new RendererProcessPolicy); } else if (process_type == switches::kPpapiPluginProcess) { policy.reset(new PpapiProcessPolicy); @@ -199,7 +198,7 @@ bool StartBPFSandbox(const CommandLine& command_line, return true; } #else // defined(IN_NACL_HELPER) -bool StartBPFSandbox(const CommandLine& command_line, +bool StartBPFSandbox(const base::CommandLine& command_line, const std::string& process_type) { NOTREACHED(); // Avoid -Wunused-function with no-op code. @@ -216,7 +215,8 @@ bool StartBPFSandbox(const CommandLine& command_line, // Is seccomp BPF globally enabled? bool SandboxSeccompBPF::IsSeccompBPFDesired() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (!command_line.HasSwitch(switches::kNoSandbox) && !command_line.HasSwitch(switches::kDisableSeccompFilterSandbox)) { return true; @@ -228,7 +228,8 @@ bool SandboxSeccompBPF::IsSeccompBPFDesired() { bool SandboxSeccompBPF::ShouldEnableSeccompBPF( const std::string& process_type) { #if defined(USE_SECCOMP_BPF) - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (process_type == switches::kGpuProcess) return !command_line.HasSwitch(switches::kDisableGpuSandbox); @@ -256,7 +257,8 @@ bool SandboxSeccompBPF::SupportsSandbox() { bool SandboxSeccompBPF::StartSandbox(const std::string& process_type) { #if defined(USE_SECCOMP_BPF) - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (IsSeccompBPFDesired() && // Global switches policy. ShouldEnableSeccompBPF(process_type) && // Process-specific policy. diff --git a/content/common/sandbox_mac.mm b/content/common/sandbox_mac.mm index f4ea8d5758315..c7c126594aec3 100644 --- a/content/common/sandbox_mac.mm +++ b/content/common/sandbox_mac.mm @@ -53,7 +53,6 @@ // profile for all process types known to content. SandboxTypeToResourceIDMapping kDefaultSandboxTypeToResourceIDMapping[] = { { SANDBOX_TYPE_RENDERER, IDR_RENDERER_SANDBOX_PROFILE }, - { SANDBOX_TYPE_WORKER, IDR_WORKER_SANDBOX_PROFILE }, { SANDBOX_TYPE_UTILITY, IDR_UTILITY_SANDBOX_PROFILE }, { SANDBOX_TYPE_GPU, IDR_GPU_SANDBOX_PROFILE }, { SANDBOX_TYPE_PPAPI, IDR_PPAPI_SANDBOX_PROFILE }, @@ -526,7 +525,8 @@ NOINLINE void FatalStringQuoteException(const std::string& str) { // Enable verbose logging if enabled on the command line. (See common.sb // for details). - const CommandLine* command_line = CommandLine::ForCurrentProcess(); + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); bool enable_logging = command_line->HasSwitch(switches::kEnableSandboxLogging);; if (enable_logging) { diff --git a/content/common/sandbox_win.cc b/content/common/sandbox_win.cc index 69b379593d1b6..1f13fb8f7afd0 100644 --- a/content/common/sandbox_win.cc +++ b/content/common/sandbox_win.cc @@ -243,7 +243,7 @@ base::string16 PrependWindowsSessionPath(const base::char16* object) { } // Checks if the sandbox should be let to run without a job object assigned. -bool ShouldSetJobLevel(const CommandLine& cmd_line) { +bool ShouldSetJobLevel(const base::CommandLine& cmd_line) { if (!cmd_line.HasSwitch(switches::kAllowNoSandboxJob)) return true; @@ -349,13 +349,18 @@ bool AddPolicyForSandboxedProcess(sandbox::TargetPolicy* policy) { // Win8+ adds a device DeviceApi that we don't need. if (base::win::GetVersion() > base::win::VERSION_WIN7) - policy->AddKernelObjectToClose(L"File", L"\\Device\\DeviceApi"); + result = policy->AddKernelObjectToClose(L"File", L"\\Device\\DeviceApi"); + if (result != sandbox::SBOX_ALL_OK) + return false; // Close the proxy settings on XP. if (base::win::GetVersion() <= base::win::VERSION_SERVER_2003) - policy->AddKernelObjectToClose(L"Key", - L"HKCU\\Software\\Microsoft\\Windows\\" \ - L"CurrentVersion\\Internet Settings"); + result = policy->AddKernelObjectToClose(L"Key", + L"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\" \ + L"CurrentVersion\\Internet Settings"); + if (result != sandbox::SBOX_ALL_OK) + return false; + sandbox::TokenLevel initial_token = sandbox::USER_UNPROTECTED; if (base::win::GetVersion() > base::win::VERSION_XP) { @@ -379,8 +384,9 @@ bool AddPolicyForSandboxedProcess(sandbox::TargetPolicy* policy) { // Updates the command line arguments with debug-related flags. If debug flags // have been used with this process, they will be filtered and added to // command_line as needed. -void ProcessDebugFlags(CommandLine* command_line) { - const CommandLine& current_cmd_line = *CommandLine::ForCurrentProcess(); +void ProcessDebugFlags(base::CommandLine* command_line) { + const base::CommandLine& current_cmd_line = + *base::CommandLine::ForCurrentProcess(); std::string type = command_line->GetSwitchValueASCII(switches::kProcessType); if (current_cmd_line.HasSwitch(switches::kWaitForDebuggerChildren)) { // Look to pass-on the kWaitForDebugger flag. @@ -500,7 +506,7 @@ BOOL WINAPI DuplicateHandlePatch(HANDLE source_process_handle, } // namespace -void SetJobLevel(const CommandLine& cmd_line, +void SetJobLevel(const base::CommandLine& cmd_line, sandbox::JobLevel job_level, uint32 ui_exceptions, sandbox::TargetPolicy* policy) { @@ -586,7 +592,8 @@ bool ShouldUseDirectWrite() { } // If forced off, don't use it. - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); if (command_line.HasSwitch(switches::kDisableDirectWrite)) return false; @@ -604,8 +611,9 @@ bool ShouldUseDirectWrite() { base::ProcessHandle StartSandboxedProcess( SandboxedProcessLauncherDelegate* delegate, - CommandLine* cmd_line) { - const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); + base::CommandLine* cmd_line) { + const base::CommandLine& browser_command_line = + *base::CommandLine::ForCurrentProcess(); std::string type_str = cmd_line->GetSwitchValueASCII(switches::kProcessType); TRACE_EVENT_BEGIN_ETW("StartProcessWithAccess", 0, type_str); diff --git a/content/common/screen_orientation_messages.h b/content/common/screen_orientation_messages.h index c2114d615adeb..2defcc71fa550 100644 --- a/content/common/screen_orientation_messages.h +++ b/content/common/screen_orientation_messages.h @@ -60,3 +60,17 @@ IPC_MESSAGE_ROUTED2(ScreenOrientationHostMsg_LockRequest, // The renderer process requests the browser process to unlock the screen // orientation. IPC_MESSAGE_ROUTED0(ScreenOrientationHostMsg_Unlock) + +// The renderer process is now using the Screen Orientation API and informs the +// browser process that it should start accurately listening to the screen +// orientation if it wasn't already. +// This is only expected to be acted upon when the underlying platform requires +// heavy work in order to accurately know the screen orientation. +IPC_MESSAGE_CONTROL0(ScreenOrientationHostMsg_StartListening) + +// The renderer process is no longer using the Screen Orientation API and +// informs the browser process that it can stop accurately listening to the +// screen orientation if no other process cares about it. +// This is only expected to be acted upon when the underlying platform requires +// heavy work in order to accurately know the screen orientation. +IPC_MESSAGE_CONTROL0(ScreenOrientationHostMsg_StopListening) diff --git a/content/common/service_worker/service_worker_messages.h b/content/common/service_worker/service_worker_messages.h index 7a917de9d022e..715dac89b022c 100644 --- a/content/common/service_worker/service_worker_messages.h +++ b/content/common/service_worker/service_worker_messages.h @@ -4,11 +4,15 @@ // Message definition file, included multiple times, hence no include guard. +#include +#include + #include "base/strings/string16.h" #include "content/common/service_worker/service_worker_status_code.h" #include "content/common/service_worker/service_worker_types.h" #include "ipc/ipc_message_macros.h" #include "ipc/ipc_param_traits.h" +#include "third_party/WebKit/public/platform/WebServiceWorkerCacheError.h" #include "third_party/WebKit/public/platform/WebServiceWorkerError.h" #include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h" #include "url/gurl.h" @@ -53,6 +57,16 @@ IPC_STRUCT_TRAITS_BEGIN(content::ServiceWorkerObjectInfo) IPC_STRUCT_TRAITS_MEMBER(state) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(content::ServiceWorkerVersionAttributes) + IPC_STRUCT_TRAITS_MEMBER(installing) + IPC_STRUCT_TRAITS_MEMBER(waiting) + IPC_STRUCT_TRAITS_MEMBER(active) +IPC_STRUCT_TRAITS_END() + +IPC_ENUM_TRAITS_MAX_VALUE( + blink::WebServiceWorkerCacheError, + blink::WebServiceWorkerCacheErrorLast) + //--------------------------------------------------------------------------- // Messages sent from the child process to the browser. @@ -88,8 +102,16 @@ IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_ProviderDestroyed, // counting in the browser side. The ServiceWorker object is created // with ref-count==1 initially. IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount, - int /* handle_id */) + int /* registration_handle_id */) IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount, + int /* registration_handle_id */) + +// Increments and decrements the ServiceWorkerRegistration object's reference +// counting in the browser side. The registration object is created with +// ref-count==1 initially. +IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_IncrementRegistrationRefCount, + int /* handle_id */) +IPC_MESSAGE_CONTROL1(ServiceWorkerHostMsg_DecrementRegistrationRefCount, int /* handle_id */) // Informs the browser that |provider_id| is associated @@ -127,6 +149,26 @@ IPC_MESSAGE_ROUTED3(ServiceWorkerHostMsg_PostMessageToDocument, base::string16 /* message */, std::vector /* sent_message_port_ids */) +// CacheStorage operations in the browser. +IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_CacheStorageGet, + int /* request_id */, + base::string16 /* fetch_store_name */) + +IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_CacheStorageHas, + int /* request_id */, + base::string16 /* fetch_store_name */) + +IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_CacheStorageCreate, + int /* request_id */, + base::string16 /* fetch_store_name */) + +IPC_MESSAGE_ROUTED2(ServiceWorkerHostMsg_CacheStorageDelete, + int /* request_id */, + base::string16 /* fetch_store_name */) + +IPC_MESSAGE_ROUTED1(ServiceWorkerHostMsg_CacheStorageKeys, + int /* request_id */) + //--------------------------------------------------------------------------- // Messages sent from the browser to the child process. // @@ -136,9 +178,10 @@ IPC_MESSAGE_ROUTED3(ServiceWorkerHostMsg_PostMessageToDocument, // on the correct thread. // Response to ServiceWorkerMsg_RegisterServiceWorker. -IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_ServiceWorkerRegistered, +IPC_MESSAGE_CONTROL4(ServiceWorkerMsg_ServiceWorkerRegistered, int /* thread_id */, int /* request_id */, + int /* registration_handle_id */, content::ServiceWorkerObjectInfo) // Response to ServiceWorkerMsg_UnregisterServiceWorker. @@ -160,26 +203,13 @@ IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_ServiceWorkerStateChanged, int /* handle_id */, blink::WebServiceWorkerState) -// Tells the child process to set the installing ServiceWorker for the given -// provider. -IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_SetInstallingServiceWorker, +// Tells the child process to set service workers for the given provider. +IPC_MESSAGE_CONTROL5(ServiceWorkerMsg_SetVersionAttributes, int /* thread_id */, int /* provider_id */, - content::ServiceWorkerObjectInfo) - -// Tells the child process to set the waiting ServiceWorker for the given -// provider. -IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_SetWaitingServiceWorker, - int /* thread_id */, - int /* provider_id */, - content::ServiceWorkerObjectInfo) - -// Tells the child process to set the active ServiceWorker for the given -// provider. -IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_SetActiveServiceWorker, - int /* thread_id */, - int /* provider_id */, - content::ServiceWorkerObjectInfo) + int /* registration_handle_id */, + int /* changed_mask */, + content::ServiceWorkerVersionAttributes) // Tells the child process to set the controller ServiceWorker for the given // provider. @@ -219,3 +249,35 @@ IPC_MESSAGE_CONTROL3(ServiceWorkerMsg_MessageToWorker, IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_DidGetClientDocuments, int /* request_id */, std::vector /* client_ids */) + +// Sent via EmbeddedWorker at successful completion of CacheStorage operations. +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageGetSuccess, + int /* request_id */, + int /* fetch_store_id */) +IPC_MESSAGE_CONTROL1(ServiceWorkerMsg_CacheStorageHasSuccess, + int /* request_id */) +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageCreateSuccess, + int /* request_id */, + int /* fetch_store_id */) +IPC_MESSAGE_CONTROL1(ServiceWorkerMsg_CacheStorageDeleteSuccess, + int /* request_id */) +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageKeysSuccess, + int /* request_id */, + std::vector /* keys */) + +// Sent via EmbeddedWorker at erroneous completion of CacheStorage operations. +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageGetError, + int /* request_id */, + blink::WebServiceWorkerCacheError /* reason */) +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageHasError, + int /* request_id */, + blink::WebServiceWorkerCacheError /* reason */) +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageCreateError, + int /* request_id */, + blink::WebServiceWorkerCacheError /* reason */) +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageDeleteError, + int /* request_id */, + blink::WebServiceWorkerCacheError /* reason */) +IPC_MESSAGE_CONTROL2(ServiceWorkerMsg_CacheStorageKeysError, + int /* request_id */, + blink::WebServiceWorkerCacheError /* reason */) diff --git a/content/common/service_worker/service_worker_types.h b/content/common/service_worker/service_worker_types.h index de582e432d757..c29dcabe47d79 100644 --- a/content/common/service_worker/service_worker_types.h +++ b/content/common/service_worker/service_worker_types.h @@ -25,6 +25,7 @@ static const int kInvalidServiceWorkerRequestId = -1; // Constants for invalid identifiers. static const int kInvalidServiceWorkerHandleId = -1; +static const int kInvalidServiceWorkerRegistrationHandleId = -1; static const int kInvalidServiceWorkerProviderId = -1; static const int64 kInvalidServiceWorkerRegistrationId = -1; static const int64 kInvalidServiceWorkerVersionId = -1; @@ -85,6 +86,12 @@ struct CONTENT_EXPORT ServiceWorkerObjectInfo { blink::WebServiceWorkerState state; }; +struct ServiceWorkerVersionAttributes { + ServiceWorkerObjectInfo installing; + ServiceWorkerObjectInfo waiting; + ServiceWorkerObjectInfo active; +}; + class ChangedVersionAttributesMask { public: enum { diff --git a/content/common/set_process_title.cc b/content/common/set_process_title.cc index 99eb243bba163..9d62ff5546998 100644 --- a/content/common/set_process_title.cc +++ b/content/common/set_process_title.cc @@ -89,4 +89,10 @@ void SetProcessTitleFromCommandLine(const char** /* main_argv */) { #endif +#if defined(OS_TIZEN_MOBILE) +void StoreArgvPointerAddress(const char** main_argv) { + setproctitle_init(main_argv); +} +#endif + } // namespace content diff --git a/content/common/set_process_title.h b/content/common/set_process_title.h index 91ea61a89a1a5..d53c6ef09d831 100644 --- a/content/common/set_process_title.h +++ b/content/common/set_process_title.h @@ -22,6 +22,10 @@ namespace content { // will try to fix it so the "effective" command line shows up instead. void SetProcessTitleFromCommandLine(const char** main_argv); +#if defined(OS_TIZEN_MOBILE) +void StoreArgvPointerAddress(const char** main_argv); +#endif + } // namespace content #endif // CONTENT_COMMON_SET_PROCESS_TITLE_H_ diff --git a/content/common/view_messages.h b/content/common/view_messages.h index 44812aaf157c4..4497d508b5c3a 100644 --- a/content/common/view_messages.h +++ b/content/common/view_messages.h @@ -367,6 +367,9 @@ IPC_STRUCT_BEGIN(ViewHostMsg_TextInputState_Params) // The mode of input field IPC_STRUCT_MEMBER(ui::TextInputMode, mode) + // The flags of the input field (autocorrect, autocomplete, etc.) + IPC_STRUCT_MEMBER(int, flags) + // The value of the input field IPC_STRUCT_MEMBER(std::string, value) @@ -611,8 +614,9 @@ IPC_MESSAGE_ROUTED0(ViewMsg_WasHidden) // render view is expected to respond with a full repaint if needs_repainting // is true. If needs_repainting is false, then this message does not trigger a // message in response. -IPC_MESSAGE_ROUTED1(ViewMsg_WasShown, - bool /* needs_repainting */) +IPC_MESSAGE_ROUTED2(ViewMsg_WasShown, + bool /* needs_repainting */, + ui::LatencyInfo /* latency_info */) // Sent to inform the view that it was swapped out. This allows the process to // exit if no other views are using it. @@ -881,6 +885,9 @@ IPC_MESSAGE_ROUTED3(ViewMsg_WindowSnapshotCompleted, gfx::Size /* size */, std::vector /* png */) +// Fetches complete rendered content of a web page as plain text. +IPC_MESSAGE_ROUTED0(ViewMsg_GetRenderedText) + #if defined(OS_MACOSX) IPC_ENUM_TRAITS_MAX_VALUE(blink::ScrollerStyle, blink::ScrollerStyleOverlay) @@ -1658,6 +1665,9 @@ IPC_MESSAGE_ROUTED2(ViewHostMsg_PluginFocusChanged, // Instructs the browser to start plugin IME. IPC_MESSAGE_ROUTED0(ViewHostMsg_StartPluginIme) +// Receives content of a web page as plain text. +IPC_MESSAGE_ROUTED1(ViewMsg_GetRenderedTextCompleted, std::string); + #elif defined(OS_WIN) // Request that the given font characters be loaded by the browser so it's // cached by the OS. Please see RenderMessageFilter::OnPreCacheFontCharacters diff --git a/content/content_app.gypi b/content/content_app.gypi index 24ddad5a88ff1..0684e41d46114 100644 --- a/content/content_app.gypi +++ b/content/content_app.gypi @@ -69,7 +69,7 @@ '../mojo/mojo_base.gyp:mojo_application_bindings', '../mojo/mojo_base.gyp:mojo_environment_chromium', '../mojo/mojo_base.gyp:mojo_system_impl', - '../mojo/mojo.gyp:mojo_service_manager', + '../mojo/mojo.gyp:mojo_application_manager', ], }], ], diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 38717c9b28460..b30478b098244 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -23,7 +23,7 @@ '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/resources/ui_resources.gyp:ui_resources', '../ui/snapshot/snapshot.gyp:snapshot', - 'browser/service_worker/service_worker_proto.gyp:database_proto', + 'browser/service_worker/service_worker_proto.gyp:proto', 'browser/speech/proto/speech_proto.gyp:speech_proto', ], 'export_dependent_settings': [ @@ -192,6 +192,8 @@ 'public/browser/resource_throttle.h', 'public/browser/save_page_type.h', 'public/browser/service_worker_context.h', + 'public/browser/service_worker_usage_info.cc', + 'public/browser/service_worker_usage_info.h', 'public/browser/session_storage_namespace.h', 'public/browser/session_storage_usage_info.h', 'public/browser/signed_certificate_timestamp_store.h', @@ -293,6 +295,8 @@ 'browser/android/devtools_auth.cc', 'browser/android/edge_effect.cc', 'browser/android/edge_effect.h', + 'browser/android/edge_effect_l.cc', + 'browser/android/edge_effect_l.h', 'browser/android/gesture_event_type_list.h', 'browser/android/in_process/synchronous_compositor_factory_impl.cc', 'browser/android/in_process/synchronous_compositor_factory_impl.h', @@ -372,7 +376,11 @@ 'browser/battery_status/battery_status_manager_android.h', 'browser/battery_status/battery_status_manager_chromeos.cc', 'browser/battery_status/battery_status_manager_default.cc', + 'browser/battery_status/battery_status_manager_linux.cc', + 'browser/battery_status/battery_status_manager_linux.h', 'browser/battery_status/battery_status_manager_mac.cc', + 'browser/battery_status/battery_status_manager_win.cc', + 'browser/battery_status/battery_status_manager_win.h', 'browser/battery_status/battery_status_manager.h', 'browser/battery_status/battery_status_message_filter.cc', 'browser/battery_status/battery_status_message_filter.h', @@ -445,6 +453,8 @@ 'browser/devtools/forwarding_agent_host.h', 'browser/devtools/ipc_devtools_agent_host.cc', 'browser/devtools/ipc_devtools_agent_host.h', + 'browser/devtools/embedded_worker_devtools_agent_host.cc', + 'browser/devtools/embedded_worker_devtools_agent_host.h', 'browser/devtools/embedded_worker_devtools_manager.cc', 'browser/devtools/embedded_worker_devtools_manager.h', 'browser/devtools/render_view_devtools_agent_host.cc', @@ -1111,6 +1121,9 @@ 'browser/safe_util_win.h', 'browser/screen_orientation/screen_orientation_dispatcher_host.cc', 'browser/screen_orientation/screen_orientation_dispatcher_host.h', + 'browser/screen_orientation/screen_orientation_message_filter_android.h', + 'browser/screen_orientation/screen_orientation_message_filter_android.cc', + 'browser/screen_orientation/screen_orientation_provider.cc', 'browser/screen_orientation/screen_orientation_provider.h', 'browser/screen_orientation/screen_orientation_provider_android.h', 'browser/screen_orientation/screen_orientation_provider_android.cc', @@ -1118,6 +1131,14 @@ 'browser/service_worker/embedded_worker_instance.h', 'browser/service_worker/embedded_worker_registry.cc', 'browser/service_worker/embedded_worker_registry.h', + 'browser/service_worker/service_worker_cache.cc', + 'browser/service_worker/service_worker_cache.h', + 'browser/service_worker/service_worker_cache_listener.cc', + 'browser/service_worker/service_worker_cache_listener.h', + 'browser/service_worker/service_worker_cache_storage.cc', + 'browser/service_worker/service_worker_cache_storage.h', + 'browser/service_worker/service_worker_cache_storage_manager.cc', + 'browser/service_worker/service_worker_cache_storage_manager.h', 'browser/service_worker/service_worker_context_core.cc', 'browser/service_worker/service_worker_context_core.h', 'browser/service_worker/service_worker_context_observer.h', @@ -1135,10 +1156,6 @@ 'browser/service_worker/service_worker_dispatcher_host.h', 'browser/service_worker/service_worker_fetch_dispatcher.cc', 'browser/service_worker/service_worker_fetch_dispatcher.h', - 'browser/service_worker/service_worker_fetch_stores.cc', - 'browser/service_worker/service_worker_fetch_stores.h', - 'browser/service_worker/service_worker_fetch_stores_manager.cc', - 'browser/service_worker/service_worker_fetch_stores_manager.h', 'browser/service_worker/service_worker_handle.cc', 'browser/service_worker/service_worker_handle.h', 'browser/service_worker/service_worker_info.cc', @@ -1160,6 +1177,8 @@ 'browser/service_worker/service_worker_register_job.h', 'browser/service_worker/service_worker_registration.cc', 'browser/service_worker/service_worker_registration.h', + 'browser/service_worker/service_worker_registration_handle.cc', + 'browser/service_worker/service_worker_registration_handle.h', 'browser/service_worker/service_worker_registration_status.cc', 'browser/service_worker/service_worker_registration_status.h', 'browser/service_worker/service_worker_request_handler.cc', @@ -1227,8 +1246,6 @@ 'browser/ssl/ssl_client_auth_handler.h', 'browser/ssl/ssl_error_handler.cc', 'browser/ssl/ssl_error_handler.h', - 'browser/ssl/ssl_host_state.cc', - 'browser/ssl/ssl_host_state.h', 'browser/ssl/ssl_manager.cc', 'browser/ssl/ssl_manager.h', 'browser/ssl/ssl_policy_backend.cc', @@ -1576,6 +1593,7 @@ ], }, { # OS!="ios" 'dependencies': [ + 'app/strings/content_strings.gyp:content_strings', 'browser/devtools/devtools_resources.gyp:devtools_resources', 'content_common_mojo_bindings', '../cc/cc.gyp:cc', @@ -1590,8 +1608,7 @@ '../webkit/common/webkit_common.gyp:webkit_common', '../webkit/storage_browser.gyp:webkit_storage_browser', '../webkit/storage_common.gyp:webkit_storage_common', - '../webkit/webkit_resources.gyp:webkit_resources', - '../webkit/webkit_resources.gyp:webkit_strings', + '../webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], }], ['enable_printing!=0', { @@ -1783,11 +1800,32 @@ ], 'sources!': [ 'browser/battery_status/battery_status_manager_default.cc', + 'browser/battery_status/battery_status_manager_linux.cc', 'browser/geolocation/wifi_data_provider_linux.cc', 'browser/power_save_blocker_ozone.cc', 'browser/power_save_blocker_x11.cc', ], }], + ['tizen==1 and enable_murphy==1', { + 'sources': [ + '<(DEPTH)/xwalk/tizen/browser/media/browser_mediaplayer_manager.cc', + '<(DEPTH)/xwalk/tizen/browser/media/browser_mediaplayer_manager.h', + '<(DEPTH)/xwalk/tizen/browser/media/media_webcontents_observer.cc', + '<(DEPTH)/xwalk/tizen/browser/media/media_webcontents_observer.h', + '<(DEPTH)/xwalk/tizen/browser/media/murphy_mainloop.cc', + '<(DEPTH)/xwalk/tizen/browser/media/murphy_mainloop.h', + '<(DEPTH)/xwalk/tizen/browser/media/murphy_resource.cc', + '<(DEPTH)/xwalk/tizen/browser/media/murphy_resource.h', + '<(DEPTH)/xwalk/tizen/browser/media/murphy_resource_manager.cc', + '<(DEPTH)/xwalk/tizen/browser/media/murphy_resource_manager.h', + ], + 'dependencies': [ + '../build/linux/system.gyp:resource_manager', + ], + 'export_dependent_settings': [ + '../build/linux/system.gyp:resource_manager', + ], + }], ['os_bsd==1', { 'sources/': [ ['exclude', '^browser/gamepad/gamepad_platform_data_fetcher_linux\\.cc$'], @@ -1834,11 +1872,13 @@ }], ['OS == "win"', { 'sources!': [ + 'browser/battery_status/battery_status_manager_default.cc', 'browser/geolocation/empty_wifi_data_provider.cc', ], }], ['OS == "linux" and use_dbus==1', { 'sources!': [ + 'browser/battery_status/battery_status_manager_default.cc', 'browser/geolocation/empty_wifi_data_provider.cc', ], 'dependencies': [ @@ -1847,6 +1887,7 @@ ], }, { # OS != "linux" or use_dbus==0 'sources!': [ + 'browser/battery_status/battery_status_manager_linux.cc', 'browser/geolocation/wifi_data_provider_linux.cc', ], }], diff --git a/content/content_child.gypi b/content/content_child.gypi index 4e5784f6af8d3..5056144c553ad 100644 --- a/content/content_child.gypi +++ b/content/content_child.gypi @@ -36,8 +36,6 @@ 'child/appcache/web_application_cache_host_impl.cc', 'child/appcache/web_application_cache_host_impl.h', 'child/assert_matching_enums.cc', - 'child/blink_glue.cc', - 'child/blink_glue.h', 'child/blink_platform_impl.cc', 'child/blink_platform_impl.h', 'child/browser_font_resource_trusted.cc', @@ -60,6 +58,8 @@ 'child/database_util.h', 'child/db_message_filter.cc', 'child/db_message_filter.h', + 'child/file_info_util.cc', + 'child/file_info_util.h', 'child/fileapi/file_system_dispatcher.cc', 'child/fileapi/file_system_dispatcher.h', 'child/fileapi/webfilesystem_impl.cc', @@ -165,6 +165,8 @@ 'child/service_worker/service_worker_network_provider.h', 'child/service_worker/service_worker_provider_context.cc', 'child/service_worker/service_worker_provider_context.h', + 'child/service_worker/service_worker_registration_handle_reference.cc', + 'child/service_worker/service_worker_registration_handle_reference.h', 'child/service_worker/web_service_worker_impl.cc', 'child/service_worker/web_service_worker_impl.h', 'child/service_worker/web_service_worker_provider_impl.cc', @@ -319,12 +321,13 @@ ], }, { # OS!="ios" 'dependencies': [ + 'app/strings/content_strings.gyp:content_strings', '../third_party/WebKit/public/blink.gyp:blink', '../third_party/WebKit/public/blink_resources.gyp:blink_resources', '../third_party/npapi/npapi.gyp:npapi', '../webkit/child/webkit_child.gyp:webkit_child', '../webkit/common/webkit_common.gyp:webkit_common', - '../webkit/webkit_resources.gyp:webkit_resources', + '../webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], }], ['use_aura==1', { diff --git a/content/content_common.gypi b/content/content_common.gypi index 1d2b4094b7f86..67d8912f464f4 100644 --- a/content/content_common.gypi +++ b/content/content_common.gypi @@ -301,7 +301,7 @@ 'common/gpu/image_transport_surface_android.cc', 'common/gpu/image_transport_surface_calayer_mac.mm', 'common/gpu/image_transport_surface_calayer_mac.h', - 'common/gpu/image_transport_surface_fbo_mac.cc', + 'common/gpu/image_transport_surface_fbo_mac.mm', 'common/gpu/image_transport_surface_fbo_mac.h', 'common/gpu/image_transport_surface_linux.cc', 'common/gpu/image_transport_surface_mac.mm', @@ -335,6 +335,7 @@ 'common/indexed_db/indexed_db_messages.h', 'common/indexed_db/indexed_db_param_traits.cc', 'common/indexed_db/indexed_db_param_traits.h', + 'common/input/did_overscroll_params.cc', 'common/input/did_overscroll_params.h', 'common/input/gesture_event_stream_validator.cc', 'common/input/gesture_event_stream_validator.h', @@ -594,7 +595,7 @@ }], ['OS=="mac"', { 'dependencies': [ - '../webkit/webkit_resources.gyp:webkit_resources', + '../webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], 'sources': [ 'common/gpu/client/gpu_memory_buffer_impl_io_surface.cc', @@ -842,6 +843,11 @@ '<(DEPTH)/third_party/khronos', ], }], + ['tizen==1 and enable_murphy==1', { + 'sources': [ + '<(DEPTH)/xwalk/tizen/common/media/media_player_messages.h', + ], + }], ['OS=="win" and directxsdk_exists=="True"', { 'actions': [ { diff --git a/content/content_renderer.gypi b/content/content_renderer.gypi index 7139f85017c5e..1d549db84a2ff 100644 --- a/content/content_renderer.gypi +++ b/content/content_renderer.gypi @@ -435,6 +435,8 @@ 'renderer/service_worker/embedded_worker_devtools_agent.h', 'renderer/service_worker/embedded_worker_dispatcher.cc', 'renderer/service_worker/embedded_worker_dispatcher.h', + 'renderer/service_worker/service_worker_cache_storage_dispatcher.cc', + 'renderer/service_worker/service_worker_cache_storage_dispatcher.h', 'renderer/service_worker/service_worker_script_context.cc', 'renderer/service_worker/service_worker_script_context.h', 'renderer/shared_memory_seqlock_reader.cc', @@ -553,6 +555,8 @@ 'renderer/pepper/pepper_plugin_registry.h', 'renderer/pepper/pepper_proxy_channel_delegate_impl.cc', 'renderer/pepper/pepper_proxy_channel_delegate_impl.h', + 'renderer/pepper/pepper_try_catch.cc', + 'renderer/pepper/pepper_try_catch.h', 'renderer/pepper/pepper_url_loader_host.cc', 'renderer/pepper/pepper_url_loader_host.h', 'renderer/pepper/pepper_video_capture_host.cc', @@ -607,6 +611,8 @@ 'renderer/pepper/usb_key_code_conversion_linux.cc', 'renderer/pepper/usb_key_code_conversion_mac.cc', 'renderer/pepper/usb_key_code_conversion_win.cc', + 'renderer/pepper/v8object_var.cc', + 'renderer/pepper/v8object_var.h', 'renderer/pepper/v8_var_converter.cc', 'renderer/pepper/v8_var_converter.h', 'renderer/pepper/video_decoder_shim.cc', @@ -885,6 +891,14 @@ 'renderer/media/crypto/renderer_cdm_manager.h', ], }], + ['tizen==1 and enable_murphy==1', { + 'sources': [ + '<(DEPTH)/xwalk/tizen/renderer/media/mediaplayer_impl.cc', + '<(DEPTH)/xwalk/tizen/renderer/media/mediaplayer_impl.h', + '<(DEPTH)/xwalk/tizen/renderer/media/renderer_mediaplayer_manager.cc', + '<(DEPTH)/xwalk/tizen/renderer/media/renderer_mediaplayer_manager.h', + ], + }], ], 'target_conditions': [ ['OS=="android"', { diff --git a/content/content_resources.grd b/content/content_resources.grd index 595aed6221328..6b0b7b67f124f 100644 --- a/content/content_resources.grd +++ b/content/content_resources.grd @@ -32,7 +32,6 @@ - diff --git a/content/content_shell.gypi b/content/content_shell.gypi index b7f25a79eb13d..e0969dde299a5 100644 --- a/content/content_shell.gypi +++ b/content/content_shell.gypi @@ -27,6 +27,7 @@ 'chromium_code': 1, }, 'dependencies': [ + 'app/strings/content_strings.gyp:content_strings', 'content.gyp:content_app_both', 'content.gyp:content_browser', 'content.gyp:content_common', @@ -62,7 +63,7 @@ '../url/url.gyp:url_lib', '../v8/tools/gyp/v8.gyp:v8', '../webkit/storage_browser.gyp:webkit_storage_browser', - '../webkit/webkit_resources.gyp:webkit_resources', + '../webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], 'include_dirs': [ '..', @@ -175,8 +176,6 @@ 'shell/renderer/shell_render_process_observer.h', 'shell/renderer/shell_render_view_observer.cc', 'shell/renderer/shell_render_view_observer.h', - 'shell/renderer/test_runner/TestInterfaces.cpp', - 'shell/renderer/test_runner/TestInterfaces.h', 'shell/renderer/test_runner/TestPlugin.cpp', 'shell/renderer/test_runner/TestPlugin.h', 'shell/renderer/test_runner/WebTask.cpp', @@ -228,6 +227,8 @@ 'shell/renderer/test_runner/spell_check_client.h', 'shell/renderer/test_runner/test_common.cc', 'shell/renderer/test_runner/test_common.h', + 'shell/renderer/test_runner/test_interfaces.cc', + 'shell/renderer/test_runner/test_interfaces.h', 'shell/renderer/test_runner/test_runner.cc', 'shell/renderer/test_runner/test_runner.h', 'shell/renderer/test_runner/text_input_controller.cc', @@ -256,10 +257,10 @@ }], ['OS=="win"', { 'resource_include_dirs': [ - '<(SHARED_INTERMEDIATE_DIR)/webkit', + '<(SHARED_INTERMEDIATE_DIR)/content/app/strings', ], 'dependencies': [ - '<(DEPTH)/webkit/webkit_resources.gyp:webkit_strings', + '<(DEPTH)/content/app/strings/content_strings.gyp:content_strings', ], 'configurations': { 'Debug_Base': { @@ -430,6 +431,7 @@ 'target_name': 'content_shell_pak', 'type': 'none', 'dependencies': [ + 'app/strings/content_strings.gyp:content_strings', 'browser/tracing/tracing_resources.gyp:tracing_resources', 'content_resources.gyp:content_resources', 'content_shell_resources', @@ -437,8 +439,7 @@ '<(DEPTH)/third_party/WebKit/public/blink_resources.gyp:blink_resources', '<(DEPTH)/ui/resources/ui_resources.gyp:ui_resources', '<(DEPTH)/ui/strings/ui_strings.gyp:ui_strings', - '<(DEPTH)/webkit/webkit_resources.gyp:webkit_resources', - '<(DEPTH)/webkit/webkit_resources.gyp:webkit_strings', + '<(DEPTH)/webkit/glue/resources/webkit_resources.gyp:webkit_resources', ], 'conditions': [ ['OS!="android"', { @@ -453,6 +454,7 @@ 'variables': { 'pak_inputs': [ '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.pak', + '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/content/browser/tracing/tracing_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/content/content_resources.pak', '<(SHARED_INTERMEDIATE_DIR)/content/shell_resources.pak', @@ -462,7 +464,6 @@ '<(SHARED_INTERMEDIATE_DIR)/ui/strings/app_locale_settings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/ui/strings/ui_strings_en-US.pak', '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_resources_100_percent.pak', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.pak', ], 'conditions': [ ['OS!="android"', { diff --git a/content/content_tests.gypi b/content/content_tests.gypi index bf2ea215c6e9b..94bbcee802a15 100644 --- a/content/content_tests.gypi +++ b/content/content_tests.gypi @@ -106,6 +106,8 @@ 'browser/renderer_host/test/no_transport_image_transport_factory_android.h', 'test/appcache_test_helper.cc', 'test/appcache_test_helper.h', + 'test/blink_test_environment.cc', + 'test/blink_test_environment.h', 'test/content_browser_test_utils_internal.cc', 'test/content_browser_test_utils_internal.h', 'test/content_test_suite.cc', @@ -161,8 +163,6 @@ 'test/web_gesture_curve_mock.h', 'test/web_layer_tree_view_impl_for_testing.cc', 'test/web_layer_tree_view_impl_for_testing.h', - 'test/webkit_support.cc', - 'test/webkit_support.h', 'test/weburl_loader_mock.cc', 'test/weburl_loader_mock.h', 'test/weburl_loader_mock_factory.cc', @@ -352,7 +352,7 @@ 'target_name': 'content_unittests', 'type': '<(gtest_target_type)', 'dependencies': [ - 'browser/service_worker/service_worker_proto.gyp:database_proto', + 'browser/service_worker/service_worker_proto.gyp:proto', 'browser/speech/proto/speech_proto.gyp:speech_proto', 'content.gyp:content_browser', 'content.gyp:content_common', @@ -404,6 +404,8 @@ 'browser/appcache/mock_appcache_storage.cc', 'browser/appcache/mock_appcache_storage.h', 'browser/appcache/mock_appcache_storage_unittest.cc', + 'browser/battery_status/battery_status_manager_linux_unittest.cc', + 'browser/battery_status/battery_status_manager_win_unittest.cc', 'browser/battery_status/battery_status_service_unittest.cc', 'browser/browser_thread_unittest.cc', 'browser/browser_url_handler_impl_unittest.cc', @@ -483,7 +485,7 @@ 'browser/gamepad/gamepad_service_unittest.cc', 'browser/gamepad/gamepad_test_helpers.cc', 'browser/gamepad/gamepad_test_helpers.h', - 'browser/geolocation/geolocation_provider_unittest.cc', + 'browser/geolocation/geolocation_provider_impl_unittest.cc', 'browser/geolocation/location_arbitrator_impl_unittest.cc', 'browser/geolocation/network_location_provider_unittest.cc', 'browser/geolocation/wifi_data_provider_chromeos_unittest.cc', @@ -591,11 +593,12 @@ 'browser/service_worker/embedded_worker_instance_unittest.cc', 'browser/service_worker/embedded_worker_test_helper.cc', 'browser/service_worker/embedded_worker_test_helper.h', + 'browser/service_worker/service_worker_cache_storage_manager_unittest.cc', 'browser/service_worker/service_worker_context_unittest.cc', 'browser/service_worker/service_worker_controllee_request_handler_unittest.cc', + 'browser/service_worker/service_worker_context_request_handler_unittest.cc', 'browser/service_worker/service_worker_database_unittest.cc', 'browser/service_worker/service_worker_dispatcher_host_unittest.cc', - 'browser/service_worker/service_worker_dispatcher_host_unittest.cc', 'browser/service_worker/service_worker_handle_unittest.cc', 'browser/service_worker/service_worker_job_unittest.cc', 'browser/service_worker/service_worker_provider_host_unittest.cc', @@ -629,7 +632,7 @@ 'browser/web_contents/web_drag_source_mac_unittest.mm', 'browser/webui/web_ui_data_source_unittest.cc', 'browser/webui/web_ui_message_handler_unittest.cc', - 'child/blink_platform_unittest.cc', + 'child/blink_platform_impl_unittest.cc', 'child/fileapi/webfilewriter_base_unittest.cc', 'child/indexed_db/indexed_db_dispatcher_unittest.cc', 'child/indexed_db/webidbcursor_impl_unittest.cc', @@ -877,6 +880,7 @@ '../chromeos/chromeos.gyp:chromeos', ], 'sources/': [ + ['exclude', '^browser/battery_status/battery_status_manager_linux_unittest.cc'], ['exclude', '^browser/geolocation/wifi_data_provider_linux_unittest.cc'], ], }], @@ -902,6 +906,7 @@ 'browser/android/java/java_type_unittest.cc', 'browser/android/java/jni_helper_unittest.cc', 'browser/android/system_ui_resource_manager_impl_unittest.cc', + 'browser/renderer_host/input/motion_event_android_unittest.cc', 'renderer/java/gin_java_bridge_value_converter_unittest.cc', ], 'sources!': [ @@ -928,6 +933,7 @@ }], ['use_dbus==0', { 'sources!': [ + 'browser/battery_status/battery_status_manager_linux_unittest.cc', 'browser/geolocation/wifi_data_provider_linux_unittest.cc', ], }], @@ -1081,7 +1087,7 @@ '../mojo/mojo_base.gyp:mojo_environment_chromium', '../mojo/mojo_base.gyp:mojo_js_bindings', '../mojo/mojo_base.gyp:mojo_system_impl', - '../mojo/mojo.gyp:mojo_service_manager', + '../mojo/mojo.gyp:mojo_application_manager', '../net/net.gyp:net_test_support', '../ppapi/ppapi_internal.gyp:ppapi_host', '../ppapi/ppapi_internal.gyp:ppapi_ipc', @@ -1225,15 +1231,15 @@ # but that causes errors in other targets when # resulting .res files get referenced multiple times. '<(SHARED_INTERMEDIATE_DIR)/blink/public/resources/blink_resources.rc', + '<(SHARED_INTERMEDIATE_DIR)/content/app/strings/content_strings_en-US.rc', '<(SHARED_INTERMEDIATE_DIR)/net/net_resources.rc', - '<(SHARED_INTERMEDIATE_DIR)/webkit/webkit_strings_en-US.rc', ], 'dependencies': [ + '<(DEPTH)/content/app/strings/content_strings.gyp:content_strings', '<(DEPTH)/net/net.gyp:net_resources', '<(DEPTH)/third_party/WebKit/public/blink_resources.gyp:blink_resources', '<(DEPTH)/third_party/iaccessible2/iaccessible2.gyp:iaccessible2', '<(DEPTH)/third_party/isimpledom/isimpledom.gyp:isimpledom', - '<(DEPTH)/webkit/webkit_resources.gyp:webkit_strings', ], 'configurations': { 'Debug_Base': { diff --git a/content/gpu/BUILD.gn b/content/gpu/BUILD.gn index 02f0de67a28f1..e8e39fefae790 100644 --- a/content/gpu/BUILD.gn +++ b/content/gpu/BUILD.gn @@ -2,6 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/ui.gni") import("//content/content.gni") # We don't support x64 prior to Win7 and D3DCompiler_43.dll is not needed on @@ -51,6 +52,10 @@ source_set("gpu") { if (is_chromeos && cpu_arch != "arm") { configs += [ "//third_party/libva/libva_config" ] } + + if (use_x11) { + deps += [ "//ui/events/platform/x11" ] + } } if (need_d3dcompiler) { diff --git a/content/public/android/java/res/mipmap-hdpi/app_icon.png b/content/public/android/java/res/mipmap-hdpi/app_icon.png index 29c220d862712..da8814853b220 100644 Binary files a/content/public/android/java/res/mipmap-hdpi/app_icon.png and b/content/public/android/java/res/mipmap-hdpi/app_icon.png differ diff --git a/content/public/android/java/res/mipmap-mdpi/app_icon.png b/content/public/android/java/res/mipmap-mdpi/app_icon.png index c508d89f05dc2..24611dec799d4 100644 Binary files a/content/public/android/java/res/mipmap-mdpi/app_icon.png and b/content/public/android/java/res/mipmap-mdpi/app_icon.png differ diff --git a/content/public/android/java/res/mipmap-xhdpi/app_icon.png b/content/public/android/java/res/mipmap-xhdpi/app_icon.png index 093d07ee47e12..98c9189977127 100644 Binary files a/content/public/android/java/res/mipmap-xhdpi/app_icon.png and b/content/public/android/java/res/mipmap-xhdpi/app_icon.png differ diff --git a/content/public/android/java/res/mipmap-xxhdpi/app_icon.png b/content/public/android/java/res/mipmap-xxhdpi/app_icon.png index 3273d2d40aec5..1fbc0e7c353ec 100644 Binary files a/content/public/android/java/res/mipmap-xxhdpi/app_icon.png and b/content/public/android/java/res/mipmap-xxhdpi/app_icon.png differ diff --git a/content/public/android/java/res/mipmap-xxxhdpi/app_icon.png b/content/public/android/java/res/mipmap-xxxhdpi/app_icon.png new file mode 100644 index 0000000000000..5b09d42ba50a6 Binary files /dev/null and b/content/public/android/java/res/mipmap-xxxhdpi/app_icon.png differ diff --git a/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java b/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java index 5b2e911680be3..e18924dd0b106 100644 --- a/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java +++ b/content/public/android/java/src/org/chromium/content/app/ChildProcessService.java @@ -17,6 +17,7 @@ import android.view.Surface; import org.chromium.base.CalledByNative; +import org.chromium.base.CommandLine; import org.chromium.base.JNINamespace; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.base.library_loader.Linker; @@ -147,6 +148,12 @@ public void run() { } } boolean isLoaded = false; + synchronized (mMainThread) { + while (mCommandLineParams == null) { + mMainThread.wait(); + } + } + CommandLine.init(mCommandLineParams); try { LibraryLoader.loadNow(getApplicationContext(), false); isLoaded = true; @@ -170,12 +177,7 @@ public void run() { if (!isLoaded) { System.exit(-1); } - synchronized (mMainThread) { - while (mCommandLineParams == null) { - mMainThread.wait(); - } - } - LibraryLoader.initialize(mCommandLineParams); + LibraryLoader.initialize(); synchronized (mMainThread) { mLibraryInitialized = true; mMainThread.notifyAll(); diff --git a/content/public/android/java/src/org/chromium/content/browser/BatteryStatusManager.java b/content/public/android/java/src/org/chromium/content/browser/BatteryStatusManager.java index 5153143dcacb8..8e586ebe94c1f 100644 --- a/content/public/android/java/src/org/chromium/content/browser/BatteryStatusManager.java +++ b/content/public/android/java/src/org/chromium/content/browser/BatteryStatusManager.java @@ -91,13 +91,18 @@ void onReceive(Intent intent) { boolean present = ignoreBatteryPresentState() ? true : intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false); + int pluggedStatus = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); - if (!present) { - // No battery, return default values. + if (!present || pluggedStatus == -1) { + // No battery or no plugged status: return default values. gotBatteryStatus(true, 0, Double.POSITIVE_INFINITY, 1); return; } + boolean charging = pluggedStatus != 0; + int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); + boolean batteryFull = status == BatteryManager.BATTERY_STATUS_FULL; + int current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); int max = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1); double level = (double)current / (double)max; @@ -106,12 +111,11 @@ void onReceive(Intent intent) { level = 1.0; } - int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1); - boolean charging = !(status == BatteryManager.BATTERY_STATUS_DISCHARGING); - - // TODO(timvolodine) : add proper projection for chargingTime, dischargingTime. - double chargingTime = (status == BatteryManager.BATTERY_STATUS_FULL) ? - 0 : Double.POSITIVE_INFINITY; + // Currently Android does not provide charging/discharging time, as a work-around + // we could compute it manually based on level delta. + // TODO(timvolodine): add proper projection for chargingTime, dischargingTime + // (see crbug.com/401553). + double chargingTime = (charging & batteryFull) ? 0 : Double.POSITIVE_INFINITY; double dischargingTime = Double.POSITIVE_INFINITY; gotBatteryStatus(charging, chargingTime, dischargingTime, level); diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java index 614d4b006200f..1b653d0e3be16 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java +++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessLauncher.java @@ -101,6 +101,8 @@ public ChildProcessConnection allocate( assert mChildProcessConnections[slot] == null; mChildProcessConnections[slot] = new ChildProcessConnectionImpl(context, slot, mInSandbox, deathCallback, mChildClass, chromiumLinkerParams); + Log.d(TAG, "Allocator allocated a connection, sandbox: " + mInSandbox + + ", slot: " + slot); return mChildProcessConnections[slot]; } } @@ -118,6 +120,8 @@ public void free(ChildProcessConnection connection) { mChildProcessConnections[slot] = null; assert !mFreeConnectionIndices.contains(slot); mFreeConnectionIndices.add(slot); + Log.d(TAG, "Allocator freed a connection, sandbox: " + mInSandbox + + ", slot: " + slot); } } } diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java b/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java index 6113a087d19ff..4848f07329c49 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentVideoView.java @@ -377,11 +377,6 @@ private static ContentVideoView createContentVideoView( Context context, long nativeContentVideoView, ContentVideoViewClient client, boolean legacy) { ThreadUtils.assertOnUiThread(); - // The context needs be Activity to create the ContentVideoView correctly. - if (!isActivityContext(context)) { - Log.e(TAG, "Wrong type of context, can't create fullscreen video"); - return null; - } ContentVideoView videoView = null; if (legacy) { videoView = new ContentVideoViewLegacy(context, nativeContentVideoView, client); 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 81825090adeb0..c3e4bf091c520 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 @@ -123,11 +123,20 @@ public boolean doesPerformWebSearch() { /** * Notification that the selection has changed. + * TODO(donnd): Remove this and instead expose a ContextualSearchClient, crbug.com/403001. * @param selection The newly established selection. */ public void onSelectionChanged(String selection) { } + /** + * Notification that a selection or insertion-related event has occurred. + * TODO(donnd): Remove this and instead expose a ContextualSearchClient, crbug.com/403001. + * @param eventType The selection event type, see {@link SelectionEventType}. + */ + public void onSelectionEvent(int eventType) { + } + /** * Called when a new content intent is requested to be started. */ 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 5f0f0fefcc5bf..8488038fae557 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 @@ -11,7 +11,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.ContentObserver; @@ -77,6 +76,7 @@ import org.chromium.content.browser.input.SelectionEventType; import org.chromium.content.common.ContentSwitches; import org.chromium.content_public.browser.GestureStateListener; +import org.chromium.content_public.browser.JavaScriptCallback; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.ViewAndroid; @@ -111,15 +111,6 @@ public class ContentViewCore private static final int IS_LONG_PRESS = 1; private static final int IS_LONG_TAP = 2; - // These values are obtained from Samsung. - // TODO(changwan): refactor SPen related code into a separate class. See - // http://crbug.com/398169. - private static final int SPEN_ACTION_DOWN = 211; - private static final int SPEN_ACTION_UP = 212; - private static final int SPEN_ACTION_MOVE = 213; - private static final int SPEN_ACTION_CANCEL = 214; - private static Boolean sIsSPenSupported; - // If the embedder adds a JavaScript interface object that contains an indirect reference to // the ContentViewCore, then storing a strong ref to the interface object on the native // side would prevent garbage collection of the ContentViewCore (as that strong ref would @@ -227,40 +218,6 @@ public interface SmartClipDataListener { public void onSmartClipDataExtracted(String text, String html, Rect clipRect); } - /** - * An interface that allows the embedder to be notified of navigation transition - * related events and respond to them. - */ - public interface NavigationTransitionDelegate { - /** - * Called when the navigation is deferred immediately after the response started. - * - * @param enteringColor The background color of the entering document, as a String - * representing a legal CSS color value. This is inserted into - * the transition layer's markup after the entering stylesheets - * have been applied. - */ - public void didDeferAfterResponseStarted(String enteringColor); - - /** - * Called when a navigation transition has been detected, and we need to check - * if it's supported. - */ - public boolean willHandleDeferAfterResponseStarted(); - - /** - * Called when the navigation is deferred immediately after the response - * started. - */ - public void addEnteringStylesheetToTransition(String stylesheet); - - /** - * Notifies that a navigation transition is started for a given frame. - * @param frameId A positive, non-zero integer identifying the navigating frame. - */ - public void didStartNavigationTransitionForFrame(long frameId); - } - private final Context mContext; private ViewGroup mContainerView; private InternalAccessDelegate mContainerViewInternals; @@ -292,6 +249,7 @@ public interface NavigationTransitionDelegate { // Lazily created paste popup menu, triggered either via long press in an // editable region or from tapping the insertion handle. private PastePopupMenu mPastePopupMenu; + private boolean mWasPastePopupShowingOnInsertionDragStart; private PopupTouchHandleDrawableDelegate mTouchHandleDelegate; @@ -339,6 +297,12 @@ public interface NavigationTransitionDelegate { // Accessibility touch exploration state. private boolean mTouchExplorationEnabled; + // Whether accessibility focus should be set to the page when it finishes loading. + // This only applies if an accessibility service like TalkBack is running. + // This is desirable behavior for a browser window, but not for an embedded + // WebView. + private boolean mShouldSetAccessibilityFocusOnPageLoad; + // Allows us to dynamically respond when the accessibility script injection flag changes. private ContentObserver mAccessibilityScriptInjectionObserver; @@ -363,8 +327,6 @@ public interface NavigationTransitionDelegate { private SmartClipDataListener mSmartClipDataListener = null; - private NavigationTransitionDelegate mNavigationTransitionDelegate = null; - // This holds the state of editable text (e.g. contents of , contenteditable) of // a focused element. // Every time the user, IME, javascript (Blink), autofill etc. modifies the content, the new @@ -485,6 +447,11 @@ public View acquireAnchorView() { @SuppressWarnings("deprecation") // AbsoluteLayout public void setAnchorViewPosition( View view, float x, float y, float width, float height) { + if (view.getParent() == null) { + // Ignore. setAnchorViewPosition has been called after the anchor view has + // already been released. + return; + } assert view.getParent() == mContainerViewAtCreation; float scale = (float) DeviceDisplayInfo.create(mContext).getDIPScale(); @@ -906,7 +873,8 @@ public void loadUrl(LoadUrlParams params) { * Stops loading the current web contents. */ public void stopLoading() { - if (mWebContents != null) mWebContents.stop(); + assert mWebContents != null; + mWebContents.stop(); } /** @@ -915,8 +883,8 @@ public void stopLoading() { * @return The URL of the current page. */ public String getUrl() { - if (mNativeContentViewCore != 0) return nativeGetURL(mNativeContentViewCore); - return null; + assert mWebContents != null; + return mWebContents.getUrl(); } /** @@ -925,7 +893,8 @@ public String getUrl() { * @return The title of the current page. */ public String getTitle() { - return mWebContents == null ? null : mWebContents.getTitle(); + assert mWebContents != null; + return mWebContents.getTitle(); } /** @@ -1136,55 +1105,6 @@ public boolean isFocusedNodeEditable() { // End FrameLayout overrides. - /** - * TODO(changwan): refactor SPen related code into a separate class. See - * http://crbug.com/398169. - * @return Whether SPen is supported on the device. - */ - public static boolean isSPenSupported(Context context) { - if (sIsSPenSupported == null) - sIsSPenSupported = detectSPenSupport(context); - return sIsSPenSupported.booleanValue(); - } - - private static boolean detectSPenSupport(Context context) { - if (!"SAMSUNG".equalsIgnoreCase(Build.MANUFACTURER)) - return false; - - final FeatureInfo[] infos = context.getPackageManager().getSystemAvailableFeatures(); - for (FeatureInfo info : infos) { - if ("com.sec.feature.spen_usp".equalsIgnoreCase(info.name)) { - return true; - } - } - return false; - } - - /** - * Convert SPen event action into normal event action. - * TODO(changwan): refactor SPen related code into a separate class. See - * http://crbug.com/398169. - * - * @param eventActionMasked Input event action. It is assumed that it is masked as the values - cannot be ORed. - * @return Event action after the conversion - */ - public static int convertSPenEventAction(int eventActionMasked) { - // S-Pen support: convert to normal stylus event handling - switch (eventActionMasked) { - case SPEN_ACTION_DOWN: - return MotionEvent.ACTION_DOWN; - case SPEN_ACTION_UP: - return MotionEvent.ACTION_UP; - case SPEN_ACTION_MOVE: - return MotionEvent.ACTION_MOVE; - case SPEN_ACTION_CANCEL: - return MotionEvent.ACTION_CANCEL; - default: - return eventActionMasked; - } - } - /** * @see View#onTouchEvent(MotionEvent) */ @@ -1202,8 +1122,8 @@ private boolean onTouchEventImpl(MotionEvent event, boolean isTouchHandleEvent) cancelRequestToScrollFocusedEditableNodeIntoView(); } - if (isSPenSupported(mContext)) - eventAction = convertSPenEventAction(eventAction); + if (SPenSupport.isSPenSupported(mContext)) + eventAction = SPenSupport.convertSPenEventAction(eventAction); if (!isValidTouchEventActionForNative(eventAction)) return false; if (mNativeContentViewCore == 0) return false; @@ -1420,20 +1340,32 @@ void updateGestureStateListener(int gestureType) { } } + /** + * Inserts the provided markup sandboxed into the frame. + */ + public void setupTransitionView(String markup) { + assert mWebContents != null; + mWebContents.setupTransitionView(markup); + } + + /** + * Hides transition elements specified by the selector, and activates any + * exiting-transition stylesheets. + */ + public void beginExitTransition(String cssSelector) { + assert mWebContents != null; + mWebContents.beginExitTransition(cssSelector); + } + /** * Requests the renderer insert a link to the specified stylesheet in the * main frame's document. */ - void addStyleSheetByURL(String url) { + public void addStyleSheetByURL(String url) { assert mWebContents != null; mWebContents.addStyleSheetByURL(url); } - /** Callback interface for evaluateJavaScript(). */ - public interface JavaScriptCallback { - void handleJavaScriptResult(String jsonResult); - } - /** * Injects the passed Javascript code in the current page and evaluates it. * If a result is required, pass in a callback. @@ -1446,8 +1378,8 @@ public interface JavaScriptCallback { * If no result is required, pass null. */ public void evaluateJavaScript(String script, JavaScriptCallback callback) { - if (mNativeContentViewCore == 0) return; - nativeEvaluateJavaScript(mNativeContentViewCore, script, callback, false); + assert mWebContents != null; + mWebContents.evaluateJavaScript(script, callback, false); } /** @@ -1457,8 +1389,8 @@ public void evaluateJavaScript(String script, JavaScriptCallback callback) { * @param script The Javascript to execute. */ public void evaluateJavaScriptEvenIfNotYetNavigated(String script) { - if (mNativeContentViewCore == 0) return; - nativeEvaluateJavaScript(mNativeContentViewCore, script, null, true); + assert mWebContents != null; + mWebContents.evaluateJavaScript(script, null, true); } /** @@ -1616,7 +1548,7 @@ public void onConfigurationChanged(Configuration newConfig) { if (newConfig.keyboard != Configuration.KEYBOARD_NOKEYS) { if (mNativeContentViewCore != 0) { mImeAdapter.attach(nativeGetNativeImeAdapter(mNativeContentViewCore), - ImeAdapter.getTextInputTypeNone()); + ImeAdapter.getTextInputTypeNone(), 0 /* no flags */); } mInputMethodManagerWrapper.restartInput(mContainerView); } @@ -2152,9 +2084,10 @@ public boolean isWebSearchAvailable() { mActionMode = null; // On ICS, startActionMode throws an NPE when getParent() is null. if (mContainerView.getParent() != null) { + assert mWebContents != null; mActionMode = mContainerView.startActionMode( getContentViewClient().getSelectActionModeCallback(getContext(), actionHandler, - nativeIsIncognito(mNativeContentViewCore))); + mWebContents.isIncognito())); } mUnselectAllOnActionModeDismiss = true; if (mActionMode == null) { @@ -2165,6 +2098,13 @@ public boolean isWebSearchAvailable() { } } + /** + * Clears the current text selection. + */ + public void clearSelection() { + mImeAdapter.unselect(); + } + private void hidePastePopup() { if (mPastePopupMenu == null) return; mPastePopupMenu.hide(); @@ -2187,18 +2127,28 @@ private void onSelectionEvent(int eventType, float posXDip, float posYDip) { hideSelectActionBar(); break; + case SelectionEventType.SELECTION_DRAG_STARTED: + break; + + case SelectionEventType.SELECTION_DRAG_STOPPED: + break; + case SelectionEventType.INSERTION_SHOWN: mHasInsertion = true; break; case SelectionEventType.INSERTION_MOVED: - // TODO(jdduke): Handle case where movement triggered by focus. - hidePastePopup(); + if (mPastePopupMenu == null) break; + if (!isScrollInProgress() && mPastePopupMenu.isShowing()) { + showPastePopup((int) posXDip, (int) posYDip); + } else { + hidePastePopup(); + } break; case SelectionEventType.INSERTION_TAPPED: - if (getPastePopup().isShowing()) - mPastePopupMenu.hide(); + if (mWasPastePopupShowingOnInsertionDragStart) + hidePastePopup(); else showPastePopup((int) posXDip, (int) posYDip); break; @@ -2208,9 +2158,16 @@ private void onSelectionEvent(int eventType, float posXDip, float posYDip) { hidePastePopup(); break; + case SelectionEventType.INSERTION_DRAG_STARTED: + mWasPastePopupShowingOnInsertionDragStart = + mPastePopupMenu != null && mPastePopupMenu.isShowing(); + hidePastePopup(); + break; + default: assert false : "Invalid selection event type."; } + getContentViewClient().onSelectionEvent(eventType); } public boolean getUseDesktopUserAgent() { @@ -2346,7 +2303,7 @@ private void updateFrameInfo( @CalledByNative private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType, - String text, int selectionStart, int selectionEnd, + int textInputFlags, String text, int selectionStart, int selectionEnd, int compositionStart, int compositionEnd, boolean showImeIfNeeded, boolean isNonImeChange) { TraceEvent.begin(); @@ -2354,7 +2311,7 @@ private void updateImeAdapter(long nativeImeAdapterAndroid, int textInputType, if (!mFocusedNodeEditable) hidePastePopup(); mImeAdapter.updateKeyboardVisibility( - nativeImeAdapterAndroid, textInputType, showImeIfNeeded); + nativeImeAdapterAndroid, textInputType, textInputFlags, showImeIfNeeded); if (mInputConnection != null) { mInputConnection.updateState(text, selectionStart, selectionEnd, compositionStart, @@ -2460,19 +2417,21 @@ private void onSelectionChanged(String text) { @SuppressWarnings("unused") @CalledByNative - private static void onEvaluateJavaScriptResult( - String jsonResult, JavaScriptCallback callback) { - callback.handleJavaScriptResult(jsonResult); + private void showPastePopupWithFeedback(int xDip, int yDip) { + // TODO(jdduke): Remove this when there is a better signal that long press caused + // showing of the paste popup. See http://crbug.com/150151. + if (showPastePopup(xDip, yDip)) { + mContainerView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); + } } - @SuppressWarnings("unused") - @CalledByNative - private void showPastePopup(int xDip, int yDip) { - if (!mHasInsertion) return; + private boolean showPastePopup(int xDip, int yDip) { + if (!mHasInsertion || !canPaste()) return false; final float contentOffsetYPix = mRenderCoordinates.getContentOffsetYPix(); getPastePopup().showAt( (int) mRenderCoordinates.fromDipToPix(xDip), (int) (mRenderCoordinates.fromDipToPix(yDip) + contentOffsetYPix)); + return true; } private PastePopupMenu getPastePopup() { @@ -2483,16 +2442,17 @@ public void paste() { mImeAdapter.paste(); hideTextHandles(); } - public boolean canPaste() { - if (!mFocusedNodeEditable) return false; - return ((ClipboardManager) mContext.getSystemService( - Context.CLIPBOARD_SERVICE)).hasPrimaryClip(); - } }); } return mPastePopupMenu; } + private boolean canPaste() { + if (!mFocusedNodeEditable) return false; + return ((ClipboardManager) mContext.getSystemService( + Context.CLIPBOARD_SERVICE)).hasPrimaryClip(); + } + @SuppressWarnings("unused") @CalledByNative private void onRenderProcessChange() { @@ -2943,6 +2903,23 @@ public void stopCurrentAccessibilityNotifications() { mAccessibilityInjector.onPageLostFocus(); } + /** + * Return whether or not we should set accessibility focus on page load. + */ + public boolean shouldSetAccessibilityFocusOnPageLoad() { + return mShouldSetAccessibilityFocusOnPageLoad; + } + + /** + * Return whether or not we should set accessibility focus on page load. + * This only applies if an accessibility service like TalkBack is running. + * This is desirable behavior for a browser window, but not for an embedded + * WebView. + */ + public void setShouldSetAccessibilityFocusOnPageLoad(boolean on) { + mShouldSetAccessibilityFocusOnPageLoad = on; + } + /** * Inform WebKit that Fullscreen mode has been exited by the user. */ @@ -3060,43 +3037,6 @@ public void setBackgroundOpaque(boolean opaque) { } } - @CalledByNative - private void didDeferAfterResponseStarted(String enteringColor) { - if (mNavigationTransitionDelegate != null ) { - mNavigationTransitionDelegate.didDeferAfterResponseStarted(enteringColor); - } - } - - @CalledByNative - public void didStartNavigationTransitionForFrame(long frameId) { - if (mNavigationTransitionDelegate != null ) { - mNavigationTransitionDelegate.didStartNavigationTransitionForFrame(frameId); - } - } - - @CalledByNative - private boolean willHandleDeferAfterResponseStarted() { - if (mNavigationTransitionDelegate == null) return false; - return mNavigationTransitionDelegate.willHandleDeferAfterResponseStarted(); - } - - @VisibleForTesting - void setHasPendingNavigationTransitionForTesting() { - if (mNativeContentViewCore == 0) return; - nativeSetHasPendingNavigationTransitionForTesting(mNativeContentViewCore); - } - - public void setNavigationTransitionDelegate(NavigationTransitionDelegate delegate) { - mNavigationTransitionDelegate = delegate; - } - - @CalledByNative - private void addEnteringStylesheetToTransition(String stylesheet) { - if (mNavigationTransitionDelegate != null ) { - mNavigationTransitionDelegate.addEnteringStylesheetToTransition(stylesheet); - } - } - /** * Offer a long press gesture to the embedding View, primarily for WebView compatibility. * @@ -3152,8 +3092,8 @@ public void onScreenOrientationChanged(int orientation) { } public void resumeResponseDeferredAtStart() { - if (mNativeContentViewCore == 0) return; - nativeResumeResponseDeferredAtStart(mNativeContentViewCore); + assert mWebContents != null; + mWebContents.resumeResponseDeferredAtStart(); } /** @@ -3188,10 +3128,6 @@ private native void nativeLoadUrl( boolean canLoadLocalResources, boolean isRendererInitiated); - private native String nativeGetURL(long nativeContentViewCoreImpl); - - private native boolean nativeIsIncognito(long nativeContentViewCoreImpl); - private native void nativeSetFocus(long nativeContentViewCoreImpl, boolean focused); private native void nativeSendOrientationChangeEvent( @@ -3254,8 +3190,10 @@ private native void nativeSelectBetweenCoordinates( private native void nativeHideTextHandles(long nativeContentViewCoreImpl); private native void nativeResetGestureDetection(long nativeContentViewCoreImpl); + private native void nativeSetDoubleTapSupportEnabled( long nativeContentViewCoreImpl, boolean enabled); + private native void nativeSetMultiTouchZoomSupportEnabled( long nativeContentViewCoreImpl, boolean enabled); @@ -3263,9 +3201,6 @@ private native void nativeSetMultiTouchZoomSupportEnabled( private native void nativeClearHistory(long nativeContentViewCoreImpl); - private native void nativeEvaluateJavaScript(long nativeContentViewCoreImpl, - String script, JavaScriptCallback callback, boolean startRenderer); - private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, String frameId, String message, String sourceOrigin, String targetOrigin); @@ -3275,6 +3210,7 @@ private native void nativePostMessageToFrame(long nativeContentViewCoreImpl, Str private native void nativeSetUseDesktopUserAgent(long nativeContentViewCoreImpl, boolean enabled, boolean reloadOnChange); + private native boolean nativeGetUseDesktopUserAgent(long nativeContentViewCoreImpl); private native void nativeClearSslPreferences(long nativeContentViewCoreImpl); @@ -3289,8 +3225,10 @@ private native void nativeRemoveJavascriptInterface(long nativeContentViewCoreIm String name); private native int nativeGetNavigationHistory(long nativeContentViewCoreImpl, Object context); + private native void nativeGetDirectedNavigationHistory(long nativeContentViewCoreImpl, Object context, boolean isForward, int maxEntries); + private native String nativeGetOriginalUrlForActiveNavigationEntry( long nativeContentViewCoreImpl); @@ -3301,10 +3239,6 @@ private native void nativeSetAccessibilityEnabled( private native void nativeExtractSmartClipData(long nativeContentViewCoreImpl, int x, int y, int w, int h); - private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque); - private native void nativeResumeResponseDeferredAtStart( - long nativeContentViewCoreImpl); - private native void nativeSetHasPendingNavigationTransitionForTesting( - long nativeContentViewCoreImpl); + private native void nativeSetBackgroundOpaque(long nativeContentViewCoreImpl, boolean opaque); } diff --git a/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java b/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java index 758c8ca45ebcd..66950c282323b 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContentViewRenderView.java @@ -7,18 +7,18 @@ import android.content.Context; import android.graphics.Color; import android.graphics.PixelFormat; +import android.graphics.SurfaceTexture; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.TextureView; +import android.view.TextureView.SurfaceTextureListener; import android.widget.FrameLayout; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; import org.chromium.ui.base.WindowAndroid; -import java.util.ArrayList; -import java.util.List; - /*** * This view is used by a ContentView to render its content. * Call {@link #setCurrentContentViewCore(ContentViewCore)} with the contentViewCore that should be @@ -30,44 +30,103 @@ public class ContentViewRenderView extends FrameLayout { // The native side of this object. private long mNativeContentViewRenderView; private SurfaceHolder.Callback mSurfaceCallback; - private List mDelayedSurfaceRunnableList; private final SurfaceView mSurfaceView; protected ContentViewCore mContentViewCore; + // Enum for the type of compositing surface: + // SURFACE_VIEW - Use SurfaceView as compositing surface which + // has a bit performance advantage + // TEXTURE_VIEW - Use TextureView as compositing surface which + // supports animation on the View + public enum CompositingSurfaceType { SURFACE_VIEW, TEXTURE_VIEW }; + + // The stuff for TextureView usage. It is not a good practice to mix 2 different + // implementations into one single class. However, for the sake of reducing the + // effort of rebasing maintanence in future, here we avoid heavily changes in + // this class. + private TextureView mTextureView; + private Surface mSurface; + private CompositingSurfaceType mCompositingSurfaceType; + private ContentReadbackHandler mContentReadbackHandler; + // The listener which will be triggered when below two conditions become valid. + // 1. The view has been initialized and ready to draw content to the screen. + // 2. The compositor finished compositing and the OpenGL buffers has been swapped. + // Which means the view has been updated with visually non-empty content. + // This listener will be triggered only once after registered. + private FirstRenderedFrameListener mFirstRenderedFrameListener; + private boolean mFirstFrameReceived; + + public interface FirstRenderedFrameListener{ + public void onFirstFrameReceived(); + } - /** - * Constructing the SurfaceView early sends surface created notifications - * before the native library is loaded. This runnable sends the same signals after - * the library is loaded. - */ - private class DelayedSurfaceRunnable implements Runnable { - private final SurfaceHolder mHolder; - private final int mFormat; - private final int mWidth; - private final int mHeight; - - /** - * see https://developer.android.com/reference/android/view/SurfaceHolder.Callback.html# - * surfaceChanged(android.view.SurfaceHolder, int, int, int) - */ - public DelayedSurfaceRunnable(SurfaceHolder holder, int format, int width, int height) { - mHolder = holder; - mFormat = format; - mWidth = width; - mHeight = height; - } + // Initialize the TextureView for rendering ContentView and configure the callback + // listeners. + private void initTextureView(Context context) { + mTextureView = new TextureView(context); + mTextureView.setBackgroundColor(Color.WHITE); - @Override - public void run() { - assert mNativeContentViewRenderView != 0; - nativeSurfaceChanged(mNativeContentViewRenderView, mFormat, mWidth, mHeight, - mHolder.getSurface()); - if (mContentViewCore != null) { - mContentViewCore.onPhysicalBackingSizeChanged(mWidth, mHeight); + mTextureView.setSurfaceTextureListener(new SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, + int width, int height) { + assert mNativeContentViewRenderView != 0; + + mSurface = new Surface(surfaceTexture); + nativeSurfaceCreated(mNativeContentViewRenderView); + // Force to trigger the compositor to start working. + onSurfaceTextureSizeChanged(surfaceTexture, width, height); + onReadyToRender(); } - } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, + int width, int height) { + assert mNativeContentViewRenderView != 0 && mSurface != null; + assert surfaceTexture == mTextureView.getSurfaceTexture(); + assert mSurface != null; + + // Here we hard-code the pixel format since the native part requires + // the format parameter to decide if the compositing surface should be + // replaced with a new one when the format is changed. + // + // If TextureView is used, the surface won't be possible to changed, + // so that the format is also not changed. There is no special reason + // to use RGBA_8888 value since the native part won't use its real + // value to do something for drawing. + // + // TODO(hmin): Figure out how to get pixel format from SurfaceTexture. + int format = PixelFormat.RGBA_8888; + nativeSurfaceChanged(mNativeContentViewRenderView, + format, width, height, mSurface); + if (mContentViewCore != null) { + mContentViewCore.onPhysicalBackingSizeChanged( + width, height); + } + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + assert mNativeContentViewRenderView != 0; + nativeSurfaceDestroyed(mNativeContentViewRenderView); + + // Release the underlying surface to make it invalid. + mSurface.release(); + mSurface = null; + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + // Do nothing since the SurfaceTexture won't be updated via updateTexImage(). + } + }); + } + + public ContentViewRenderView(Context context) { + this(context, CompositingSurfaceType.SURFACE_VIEW); } /** @@ -76,42 +135,36 @@ public void run() { * hierarchy before the first draw to avoid a black flash that is seen every time a * {@link SurfaceView} is added. * @param context The context used to create this. + * @param surfaceType TextureView is used as compositing target surface, + * otherwise SurfaceView is used. */ - public ContentViewRenderView(Context context) { + public ContentViewRenderView(Context context, CompositingSurfaceType surfaceType) { super(context); - mSurfaceView = createSurfaceView(getContext()); - mSurfaceView.setZOrderMediaOverlay(true); + mCompositingSurfaceType = surfaceType; + if (surfaceType == CompositingSurfaceType.TEXTURE_VIEW) { + initTextureView(context); - setSurfaceViewBackgroundColor(Color.WHITE); - - // Add a placeholder callback which will keep track of the last surfaceChanged call if we - // get any until the native libraries have been loaded. - mSurfaceCallback = new SurfaceHolder.Callback() { - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - mDelayedSurfaceRunnableList = null; - } + addView(mTextureView, + new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT)); - @Override - public void surfaceCreated(SurfaceHolder holder) { - mDelayedSurfaceRunnableList = - new ArrayList(); - } + // Avoid compiler warning. + mSurfaceView = null; + mSurfaceCallback = null; + return; + } - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - mDelayedSurfaceRunnableList.add( - new DelayedSurfaceRunnable(holder, format, width, height)); - return; - } - }; - mSurfaceView.getHolder().addCallback(mSurfaceCallback); + mSurfaceView = createSurfaceView(getContext()); + mSurfaceView.setZOrderMediaOverlay(true); + setSurfaceViewBackgroundColor(Color.WHITE); addView(mSurfaceView, new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + mSurfaceView.setVisibility(GONE); } /** @@ -123,7 +176,20 @@ public void onNativeLibraryLoaded(WindowAndroid rootWindow) { assert rootWindow != null; mNativeContentViewRenderView = nativeInit(rootWindow.getNativePointer()); assert mNativeContentViewRenderView != 0; - mSurfaceView.getHolder().removeCallback(mSurfaceCallback); + + mContentReadbackHandler = new ContentReadbackHandler() { + @Override + protected boolean readyForReadback() { + return mNativeContentViewRenderView != 0 && mContentViewCore != null; + } + }; + mContentReadbackHandler.initNativeContentReadbackHandler(); + + if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) + return; + + assert !mSurfaceView.getHolder().getSurface().isValid() : + "Surface created before native library loaded."; mSurfaceCallback = new SurfaceHolder.Callback() { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { @@ -151,21 +217,7 @@ public void surfaceDestroyed(SurfaceHolder holder) { } }; mSurfaceView.getHolder().addCallback(mSurfaceCallback); - - mContentReadbackHandler = new ContentReadbackHandler() { - @Override - protected boolean readyForReadback() { - return mNativeContentViewRenderView != 0 && mContentViewCore != null; - } - }; - mContentReadbackHandler.initNativeContentReadbackHandler(); - if (mDelayedSurfaceRunnableList != null) { - nativeSurfaceCreated(mNativeContentViewRenderView); - for (int i = 0; i < mDelayedSurfaceRunnableList.size(); i++) { - mDelayedSurfaceRunnableList.get(i).run(); - } - mDelayedSurfaceRunnableList = null; - } + mSurfaceView.setVisibility(VISIBLE); } /** @@ -194,7 +246,15 @@ public void setSurfaceViewBackgroundColor(int color) { public void destroy() { mContentReadbackHandler.destroy(); mContentReadbackHandler = null; - mSurfaceView.getHolder().removeCallback(mSurfaceCallback); + if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) { + mTextureView.setSurfaceTextureListener(null); + if (mSurface != null) { + mSurface.release(); + mSurface = null; + } + } else { + mSurfaceView.getHolder().removeCallback(mSurfaceCallback); + } nativeDestroy(mNativeContentViewRenderView); mNativeContentViewRenderView = 0; } @@ -212,6 +272,15 @@ public void setCurrentContentViewCore(ContentViewCore contentViewCore) { } } + /** + * Trigger a redraw of the compositor. This is only needed if the UI changes something that + * does not trigger a redraw itself by updating the layer tree. + */ + public void setNeedsComposite() { + if (mNativeContentViewRenderView == 0) return; + nativeSetNeedsComposite(mNativeContentViewRenderView); + } + /** * This method should be subclassed to provide actions to be performed once the view is ready to * render. @@ -229,11 +298,18 @@ protected SurfaceView createSurfaceView(Context context) { return new SurfaceView(context); } + public void registerFirstRenderedFrameListener(FirstRenderedFrameListener listener) { + mFirstRenderedFrameListener = listener; + if (mFirstFrameReceived && mFirstRenderedFrameListener != null) { + mFirstRenderedFrameListener.onFirstFrameReceived(); + } + } + /** * @return whether the surface view is initialized and ready to render. */ public boolean isInitialized() { - return mSurfaceView.getHolder().getSurface() != null; + return mSurfaceView.getHolder().getSurface() != null || mSurface != null; } /** @@ -241,6 +317,10 @@ public boolean isInitialized() { * @param enabled Whether overlay mode is enabled. */ public void setOverlayVideoMode(boolean enabled) { + if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) { + nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled); + return; + } int format = enabled ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; mSurfaceView.getHolder().setFormat(format); nativeSetOverlayVideoMode(mNativeContentViewRenderView, enabled); @@ -260,6 +340,17 @@ protected void onCompositorLayout() { @CalledByNative private void onSwapBuffersCompleted() { + if (!mFirstFrameReceived && mContentViewCore != null && + mContentViewCore.isReady()) { + mFirstFrameReceived = true; + if (mFirstRenderedFrameListener != null) { + mFirstRenderedFrameListener.onFirstFrameReceived(); + } + } + + // Ignore if TextureView is used. + if (mCompositingSurfaceType == CompositingSurfaceType.TEXTURE_VIEW) return; + if (mSurfaceView.getBackground() != null) { post(new Runnable() { @Override public void run() { @@ -269,7 +360,15 @@ private void onSwapBuffersCompleted() { } } + /** + * @return Native pointer for the UI resource provider taken from the compositor. + */ + public long getUIResourceProvider() { + return nativeGetUIResourceProvider(mNativeContentViewRenderView); + } + private native long nativeInit(long rootWindowNativePointer); + private native long nativeGetUIResourceProvider(long nativeContentViewRenderView); private native void nativeDestroy(long nativeContentViewRenderView); private native void nativeSetCurrentContentViewCore(long nativeContentViewRenderView, long nativeContentViewCore); @@ -281,4 +380,5 @@ private native void nativeSurfaceChanged(long nativeContentViewRenderView, int format, int width, int height, Surface surface); private native void nativeSetOverlayVideoMode(long nativeContentViewRenderView, boolean enabled); + private native void nativeSetNeedsComposite(long nativeContentViewRenderView); } diff --git a/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java b/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java index d6955fa86a6fb..66f89274cff40 100644 --- a/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java +++ b/content/public/android/java/src/org/chromium/content/browser/MediaResourceGetter.java @@ -34,10 +34,12 @@ class MediaResourceGetter { private static final String TAG = "MediaResourceGetter"; - private final MediaMetadata EMPTY_METADATA = new MediaMetadata(0,0,0,false); + private static final MediaMetadata EMPTY_METADATA = new MediaMetadata(0,0,0,false); private final MediaMetadataRetriever mRetriever = new MediaMetadataRetriever(); + private static String PACKAGE_NAME = null; + @VisibleForTesting static class MediaMetadata { private final int mDurationInMilliseconds; @@ -114,6 +116,7 @@ private static MediaMetadata extractMediaMetadata(final Context context, final String url, final String cookies, final String userAgent) { + PACKAGE_NAME = context.getPackageName(); return new MediaResourceGetter().extract( context, url, cookies, userAgent); } @@ -217,7 +220,7 @@ boolean configure(Context context, String url, String cookies, String userAgent) return false; } String scheme = uri.getScheme(); - if (scheme == null || scheme.equals("file")) { + if (scheme == null || scheme.equals("file") || scheme.equals("app")) { File file = uriToFile(uri.getPath()); if (!file.exists()) { Log.e(TAG, "File does not exist."); @@ -365,6 +368,8 @@ private List getRawAcceptableDirectories() { List result = new ArrayList(); result.add("/mnt/sdcard/"); result.add("/sdcard/"); + if (PACKAGE_NAME != null) + result.add("/data/data/" + PACKAGE_NAME + "/cache/"); return result; } diff --git a/content/public/android/java/src/org/chromium/content/browser/ResourceExtractor.java b/content/public/android/java/src/org/chromium/content/browser/ResourceExtractor.java index 81a188c2ddaf1..c8f0e1a6954db 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ResourceExtractor.java +++ b/content/public/android/java/src/org/chromium/content/browser/ResourceExtractor.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.util.HashSet; +import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.regex.Pattern; @@ -39,12 +40,18 @@ public class ResourceExtractor { private static final String ICU_DATA_FILENAME = "icudtl.dat"; private static String[] sMandatoryPaks = null; + private static ResourceIntercepter sIntercepter = null; // By default, we attempt to extract a pak file for the users // current device locale. Use setExtractImplicitLocale() to // change this behavior. private static boolean sExtractImplicitLocalePak = true; + public interface ResourceIntercepter { + Set getInterceptableResourceList(); + InputStream interceptLoadingForResource(String resource); + } + private class ExtractTask extends AsyncTask { private static final int BUFFER_SIZE = 16 * 1024; @@ -53,17 +60,19 @@ public ExtractTask() { @Override protected Void doInBackground(Void... unused) { - if (!mOutputDir.exists() && !mOutputDir.mkdirs()) { + final File outputDir = getOutputDir(); + if (!outputDir.exists() && !outputDir.mkdirs()) { Log.e(LOGTAG, "Unable to create pak resources directory!"); return null; } - String timestampFile = checkPakTimestamp(); + String timestampFile = checkPakTimestamp(outputDir); if (timestampFile != null) { deleteFiles(); } - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( + mContext.getApplicationContext()); HashSet filenames = (HashSet) prefs.getStringSet( PAK_FILENAMES, new HashSet()); String currentLocale = LocalizationUtils.getDefaultLocale(); @@ -73,7 +82,7 @@ protected Void doInBackground(Void... unused) { && filenames.size() >= sMandatoryPaks.length) { boolean filesPresent = true; for (String file : filenames) { - if (!new File(mOutputDir, file).exists()) { + if (!new File(outputDir, file).exists()) { filesPresent = false; break; } @@ -107,12 +116,24 @@ protected Void doInBackground(Void... unused) { // created above. byte[] buffer = null; String[] files = manager.list(""); + if (sIntercepter != null) { + Set filesIncludingInterceptableFiles = + sIntercepter.getInterceptableResourceList(); + if (filesIncludingInterceptableFiles != null && + !filesIncludingInterceptableFiles.isEmpty()) { + for (String file : files) { + filesIncludingInterceptableFiles.add(file); + } + files = new String[filesIncludingInterceptableFiles.size()]; + filesIncludingInterceptableFiles.toArray(files); + } + } for (String file : files) { if (!paksToInstall.matcher(file).matches()) { continue; } boolean isICUData = file.equals(ICU_DATA_FILENAME); - File output = new File(isICUData ? mAppDataDir : mOutputDir, file); + File output = new File(isICUData ? getAppDataDir() : outputDir, file); if (output.exists()) { continue; } @@ -120,7 +141,10 @@ protected Void doInBackground(Void... unused) { InputStream is = null; OutputStream os = null; try { - is = manager.open(file); + if (sIntercepter != null) { + is = sIntercepter.interceptLoadingForResource(file); + } + if (is == null) is = manager.open(file); os = new FileOutputStream(output); Log.i(LOGTAG, "Extracting resource " + file); if (buffer == null) { @@ -170,7 +194,7 @@ protected Void doInBackground(Void... unused) { if (timestampFile != null) { try { - new File(mOutputDir, timestampFile).createNewFile(); + new File(outputDir, timestampFile).createNewFile(); } catch (IOException e) { // Worst case we don't write a timestamp, so we'll re-extract the resource // paks next start up. @@ -190,7 +214,7 @@ protected Void doInBackground(Void... unused) { // Note that we do this to avoid adding a BroadcastReceiver on // android.content.Intent#ACTION_PACKAGE_CHANGED as that causes process churn // on (re)installation of *all* APK files. - private String checkPakTimestamp() { + private String checkPakTimestamp(File outputDir) { final String TIMESTAMP_PREFIX = "pak_timestamp-"; PackageManager pm = mContext.getPackageManager(); PackageInfo pi = null; @@ -207,7 +231,7 @@ private String checkPakTimestamp() { String expectedTimestamp = TIMESTAMP_PREFIX + pi.versionCode + "-" + pi.lastUpdateTime; - String[] timestamps = mOutputDir.list(new FilenameFilter() { + String[] timestamps = outputDir.list(new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.startsWith(TIMESTAMP_PREFIX); @@ -231,8 +255,6 @@ public boolean accept(File dir, String name) { private final Context mContext; private ExtractTask mExtractTask; - private final File mAppDataDir; - private final File mOutputDir; private static ResourceExtractor sInstance; @@ -256,6 +278,19 @@ public static void setMandatoryPaksToExtract(String... mandatoryPaks) { } + /** + * Allow embedders to intercept the resource loading process. Embedders may + * want to load paks from res/raw instead of assets, since assets are not + * supported in Android library project. + * @param intercepter The instance of intercepter which provides the files list + * to intercept and the inputstream for the files it wants to intercept with. + */ + public static void setResourceIntercepter(ResourceIntercepter intercepter) { + assert (sInstance == null || sInstance.mExtractTask == null) + : "Must be called before startExtractingResources is called"; + sIntercepter = intercepter; + } + /** * By default the ResourceExtractor will attempt to extract a pak resource for the users * currently specified locale. This behavior can be changed with this function and is @@ -272,8 +307,6 @@ public static void setExtractImplicitLocaleForTesting(boolean extract) { private ResourceExtractor(Context context) { mContext = context.getApplicationContext(); - mAppDataDir = getAppDataDir(); - mOutputDir = getOutputDir(); } public void waitForCompletion() { @@ -285,6 +318,10 @@ public void waitForCompletion() { try { mExtractTask.get(); + // ResourceExtractor is not needed any more. + // Release static objects to avoid leak of Context. + sIntercepter = null; + sInstance = null; } catch (CancellationException e) { // Don't leave the files in an inconsistent state. deleteFiles(); diff --git a/content/public/android/java/src/org/chromium/content/browser/SPenSupport.java b/content/public/android/java/src/org/chromium/content/browser/SPenSupport.java new file mode 100644 index 0000000000000..62fb90c6fcea9 --- /dev/null +++ b/content/public/android/java/src/org/chromium/content/browser/SPenSupport.java @@ -0,0 +1,70 @@ +// 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; + +import android.content.Context; +import android.content.pm.FeatureInfo; +import android.os.Build; +import android.view.MotionEvent; + +/** + * Support S-Pen event detection and conversion. + */ +public final class SPenSupport { + + // These values are obtained from Samsung. + private static final int SPEN_ACTION_DOWN = 211; + private static final int SPEN_ACTION_UP = 212; + private static final int SPEN_ACTION_MOVE = 213; + private static final int SPEN_ACTION_CANCEL = 214; + private static Boolean sIsSPenSupported; + + /** + * @return Whether SPen is supported on the device. + */ + public static boolean isSPenSupported(Context context) { + if (sIsSPenSupported == null) { + sIsSPenSupported = detectSPenSupport(context); + } + return sIsSPenSupported.booleanValue(); + } + + private static boolean detectSPenSupport(Context context) { + if (!"SAMSUNG".equalsIgnoreCase(Build.MANUFACTURER)) { + return false; + } + + final FeatureInfo[] infos = context.getPackageManager().getSystemAvailableFeatures(); + for (FeatureInfo info : infos) { + if ("com.sec.feature.spen_usp".equalsIgnoreCase(info.name)) { + return true; + } + } + return false; + } + + /** + * Convert SPen event action into normal event action. + * + * @param eventActionMasked Input event action. It is assumed that it is masked as the values + cannot be ORed. + * @return Event action after the conversion. + */ + public static int convertSPenEventAction(int eventActionMasked) { + // S-Pen support: convert to normal stylus event handling + switch (eventActionMasked) { + case SPEN_ACTION_DOWN: + return MotionEvent.ACTION_DOWN; + case SPEN_ACTION_UP: + return MotionEvent.ACTION_UP; + case SPEN_ACTION_MOVE: + return MotionEvent.ACTION_MOVE; + case SPEN_ACTION_CANCEL: + return MotionEvent.ACTION_CANCEL; + default: + return eventActionMasked; + } + } +} diff --git a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationListener.java b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationListener.java index 4cd67865a4dea..381f98b0992bf 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationListener.java +++ b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationListener.java @@ -60,6 +60,19 @@ private interface ScreenOrientationListenerBackend { * when the last observer is removed. */ void stopListening(); + + /** + * Toggle the accurate mode if it wasn't already doing so. The backend + * will keep track of the number of times this has been called. + */ + void startAccurateListening(); + + /** + * Request to stop the accurate mode. It will effectively be stopped + * only if this method is called as many times as + * startAccurateListening(). + */ + void stopAccurateListening(); } /** @@ -69,10 +82,16 @@ private interface ScreenOrientationListenerBackend { * * This method is known to not correctly detect 180 degrees changes but it * is the only method that will work before API Level 17 (excluding polling). + * When toggleAccurateMode() is called, it will start polling in order to + * find out if the display has changed. */ private class ScreenOrientationConfigurationListener implements ScreenOrientationListenerBackend, ComponentCallbacks { + private static final long POLLING_DELAY = 500; + + private int mAccurateCount = 0; + // ScreenOrientationListenerBackend implementation: @Override @@ -85,6 +104,36 @@ public void stopListening() { mAppContext.unregisterComponentCallbacks(this); } + @Override + public void startAccurateListening() { + ++mAccurateCount; + + if (mAccurateCount > 1) + return; + + // Start polling if we went from 0 to 1. The polling will + // automatically stop when mAccurateCount reaches 0. + final ScreenOrientationConfigurationListener self = this; + ThreadUtils.postOnUiThreadDelayed(new Runnable() { + @Override + public void run() { + self.onConfigurationChanged(null); + + if (self.mAccurateCount < 1) + return; + + ThreadUtils.postOnUiThreadDelayed(this, + ScreenOrientationConfigurationListener.POLLING_DELAY); + } + }, POLLING_DELAY); + } + + @Override + public void stopAccurateListening() { + --mAccurateCount; + assert mAccurateCount >= 0; + } + // ComponentCallbacks implementation: @Override @@ -123,6 +172,16 @@ public void stopListening() { displayManager.unregisterDisplayListener(this); } + @Override + public void startAccurateListening() { + // Always accurate. Do nothing. + } + + @Override + public void stopAccurateListening() { + // Always accurate. Do nothing. + } + // DisplayListener implementation: @Override @@ -234,6 +293,22 @@ public void removeObserver(ScreenOrientationObserver observer) { } } + /** + * Toggle the accurate mode if it wasn't already doing so. The backend will + * keep track of the number of times this has been called. + */ + public void startAccurateListening() { + mBackend.startAccurateListening(); + } + + /** + * Request to stop the accurate mode. It will effectively be stopped only if + * this method is called as many times as startAccurateListening(). + */ + public void stopAccurateListening() { + mBackend.stopAccurateListening(); + } + /** * This should be called by classes extending ScreenOrientationListener when * it is possible that there is a screen orientation change. If there is an @@ -243,12 +318,12 @@ private void notifyObservers() { int previousOrientation = mOrientation; updateOrientation(); - DeviceDisplayInfo.create(mAppContext).updateNativeSharedDisplayInfo(); - if (mOrientation == previousOrientation) { return; } + DeviceDisplayInfo.create(mAppContext).updateNativeSharedDisplayInfo(); + for (ScreenOrientationObserver observer : mObservers) { observer.onScreenOrientationChanged(mOrientation); } diff --git a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java index 53c8c0f845270..c90adc17d68ac 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java +++ b/content/public/android/java/src/org/chromium/content/browser/ScreenOrientationProvider.java @@ -12,6 +12,7 @@ import org.chromium.base.ApplicationStatus; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import org.chromium.base.ThreadUtils; import org.chromium.content.common.ScreenOrientationValues; /** @@ -80,6 +81,26 @@ static void unlockOrientation() { } } + @CalledByNative + static void startAccurateListening() { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + ScreenOrientationListener.getInstance().startAccurateListening(); + } + }); + } + + @CalledByNative + static void stopAccurateListening() { + ThreadUtils.runOnUiThread(new Runnable() { + @Override + public void run() { + ScreenOrientationListener.getInstance().stopAccurateListening(); + } + }); + } + private ScreenOrientationProvider() { } } diff --git a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java index 6f4723369ce1b..8ba9800c62bd8 100644 --- a/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java +++ b/content/public/android/java/src/org/chromium/content/browser/accessibility/BrowserAccessibilityManager.java @@ -370,8 +370,10 @@ private boolean isFrameInfoInitialized() { private void handlePageLoaded(int id) { if (mUserHasTouchExplored) return; - mAccessibilityFocusId = id; - sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + if (mContentViewCore.shouldSetAccessibilityFocusOnPageLoad()) { + mAccessibilityFocusId = id; + sendAccessibilityEvent(id, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); + } } @CalledByNative diff --git a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java index be5f4958858af..d185262386a60 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/AdapterInputConnection.java @@ -63,43 +63,53 @@ public class AdapterInputConnection extends BaseInputConnection { outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; - if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeText) { + int inputType = imeAdapter.getTextInputType(); + int inputFlags = imeAdapter.getTextInputFlags(); + if ((inputFlags & imeAdapter.sTextInputFlagAutocompleteOff) != 0) { + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } + + if (inputType == ImeAdapter.sTextInputTypeText) { // Normal text field - outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeTextArea || - imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeContentEditable) { + if ((inputFlags & imeAdapter.sTextInputFlagAutocorrectOff) == 0) { + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; + } + } else if (inputType == ImeAdapter.sTextInputTypeTextArea || + inputType == ImeAdapter.sTextInputTypeContentEditable) { // TextArea or contenteditable. outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE - | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES - | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; + | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; + if ((inputFlags & imeAdapter.sTextInputFlagAutocorrectOff) == 0) { + outAttrs.inputType |= EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT; + } outAttrs.imeOptions |= EditorInfo.IME_ACTION_NONE; mSingleLine = false; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypePassword) { + } else if (inputType == ImeAdapter.sTextInputTypePassword) { // Password outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD; outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeSearch) { + } else if (inputType == ImeAdapter.sTextInputTypeSearch) { // Search outAttrs.imeOptions |= EditorInfo.IME_ACTION_SEARCH; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeUrl) { + } else if (inputType == ImeAdapter.sTextInputTypeUrl) { // Url outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI; outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeEmail) { + } else if (inputType == ImeAdapter.sTextInputTypeEmail) { // Email outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; outAttrs.imeOptions |= EditorInfo.IME_ACTION_GO; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeTel) { + } else if (inputType == ImeAdapter.sTextInputTypeTel) { // Telephone // Number and telephone do not have both a Tab key and an // action in default OSK, so set the action to NEXT outAttrs.inputType = InputType.TYPE_CLASS_PHONE; outAttrs.imeOptions |= EditorInfo.IME_ACTION_NEXT; - } else if (imeAdapter.getTextInputType() == ImeAdapter.sTextInputTypeNumber) { + } else if (inputType == ImeAdapter.sTextInputTypeNumber) { // Number outAttrs.inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL @@ -463,7 +473,7 @@ public boolean setComposingRegion(int start, int end) { CharSequence regionText = null; if (b > a) { - regionText = mEditable.subSequence(start, end); + regionText = mEditable.subSequence(a, b); } return mImeAdapter.setComposingRegion(regionText, a, b); } diff --git a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java index b577acb384d67..369786891041e 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/ImeAdapter.java @@ -82,7 +82,7 @@ private class DelayedDismissInput implements Runnable { @Override public void run() { - attach(mNativeImeAdapter, sTextInputTypeNone); + attach(mNativeImeAdapter, sTextInputTypeNone, sTextInputFlagNone); dismissInput(true); } } @@ -111,6 +111,13 @@ public void run() { static int sTextInputTypeTel; static int sTextInputTypeNumber; static int sTextInputTypeContentEditable; + static int sTextInputFlagNone = 0; + static int sTextInputFlagAutocompleteOn; + static int sTextInputFlagAutocompleteOff; + static int sTextInputFlagAutocorrectOn; + static int sTextInputFlagAutocorrectOff; + static int sTextInputFlagSpellcheckOn; + static int sTextInputFlagSpellcheckOff; static int sModifierShift; static int sModifierAlt; static int sModifierCtrl; @@ -126,6 +133,7 @@ public void run() { private final Handler mHandler; private DelayedDismissInput mDismissInput = null; private int mTextInputType; + private int mTextInputFlags; private String mLastComposeText; @VisibleForTesting @@ -184,13 +192,21 @@ void setInputConnection(AdapterInputConnection inputConnection) { } /** - * Should be only used by AdapterInputConnection. + * Should be used only by AdapterInputConnection. * @return The input type of currently focused element. */ int getTextInputType() { return mTextInputType; } + /** + * Should be used only by AdapterInputConnection. + * @return The input flags of the currently focused element. + */ + int getTextInputFlags() { + return mTextInputFlags; + } + /** * @return Constant representing that a focused node is not an input field. */ @@ -225,7 +241,7 @@ private static int getModifiers(int metaState) { * @param showIfNeeded Whether the keyboard should be shown if it is currently hidden. */ public void updateKeyboardVisibility(long nativeImeAdapter, int textInputType, - boolean showIfNeeded) { + int textInputFlags, boolean showIfNeeded) { mHandler.removeCallbacks(mDismissInput); // If current input type is none and showIfNeeded is false, IME should not be shown @@ -243,7 +259,7 @@ public void updateKeyboardVisibility(long nativeImeAdapter, int textInputType, return; } - attach(nativeImeAdapter, textInputType); + attach(nativeImeAdapter, textInputType, textInputFlags); mInputMethodManagerWrapper.restartInput(mViewEmbedder.getAttachedView()); if (showIfNeeded) { @@ -254,12 +270,13 @@ public void updateKeyboardVisibility(long nativeImeAdapter, int textInputType, } } - public void attach(long nativeImeAdapter, int textInputType) { + public void attach(long nativeImeAdapter, int textInputType, int textInputFlags) { if (mNativeImeAdapterAndroid != 0) { nativeResetImeAdapter(mNativeImeAdapterAndroid); } mNativeImeAdapterAndroid = nativeImeAdapter; mTextInputType = textInputType; + mTextInputFlags = textInputFlags; mLastComposeText = null; if (nativeImeAdapter != 0) { nativeAttachImeAdapter(mNativeImeAdapterAndroid); @@ -275,7 +292,7 @@ public void attach(long nativeImeAdapter, int textInputType) { * @param nativeImeAdapter The pointer to the native ImeAdapter object. */ public void attach(long nativeImeAdapter) { - attach(nativeImeAdapter, sTextInputTypeNone); + attach(nativeImeAdapter, sTextInputTypeNone, sTextInputFlagNone); } private void showKeyboard() { @@ -631,6 +648,19 @@ private static void initializeTextInputTypes(int textInputTypeNone, int textInpu sTextInputTypeContentEditable = textInputTypeContentEditable; } + @CalledByNative + private static void initializeTextInputFlags( + int textInputFlagAutocompleteOn, int textInputFlagAutocompleteOff, + int textInputFlagAutocorrectOn, int textInputFlagAutocorrectOff, + int textInputFlagSpellcheckOn, int textInputFlagSpellcheckOff) { + sTextInputFlagAutocompleteOn = textInputFlagAutocompleteOn; + sTextInputFlagAutocompleteOff = textInputFlagAutocompleteOff; + sTextInputFlagAutocorrectOn = textInputFlagAutocorrectOn; + sTextInputFlagAutocorrectOff = textInputFlagAutocorrectOff; + sTextInputFlagSpellcheckOn = textInputFlagSpellcheckOn; + sTextInputFlagSpellcheckOff = textInputFlagSpellcheckOff; + } + @CalledByNative private void focusedNodeChanged(boolean isEditable) { if (mInputConnection != null && isEditable) mInputConnection.restartInput(); diff --git a/content/public/android/java/src/org/chromium/content/browser/input/PastePopupMenu.java b/content/public/android/java/src/org/chromium/content/browser/input/PastePopupMenu.java index b2127709b32de..c3442685d4b1d 100644 --- a/content/public/android/java/src/org/chromium/content/browser/input/PastePopupMenu.java +++ b/content/public/android/java/src/org/chromium/content/browser/input/PastePopupMenu.java @@ -28,8 +28,8 @@ public class PastePopupMenu implements OnClickListener { private int mPositionX; private int mPositionY; private int mStatusBarHeight; - private final View[] mPasteViews; - private final int[] mPasteViewLayouts; + private View mPasteView; + private final int mPasteViewLayout; private final int mLineOffsetY; private final int mWidthOffsetX; @@ -41,11 +41,6 @@ public interface PastePopupMenuDelegate { * Called to initiate a paste after the popup has been tapped. */ void paste(); - - /** - * @return true iff content can be pasted to a focused editable region. - */ - boolean canPaste(); } public PastePopupMenu(View parent, PastePopupMenuDelegate delegate) { @@ -61,20 +56,12 @@ public PastePopupMenu(View parent, PastePopupMenuDelegate delegate) { mContainer.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT); mContainer.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT); - final int[] POPUP_LAYOUT_ATTRS = { - android.R.attr.textEditPasteWindowLayout, - android.R.attr.textEditNoPasteWindowLayout, - android.R.attr.textEditSidePasteWindowLayout, - android.R.attr.textEditSideNoPasteWindowLayout, - }; - - mPasteViews = new View[POPUP_LAYOUT_ATTRS.length]; - mPasteViewLayouts = new int[POPUP_LAYOUT_ATTRS.length]; + final int[] POPUP_LAYOUT_ATTRS = { android.R.attr.textEditPasteWindowLayout, }; + mPasteView = null; TypedArray attrs = mContext.getTheme().obtainStyledAttributes(POPUP_LAYOUT_ATTRS); - for (int i = 0; i < attrs.length(); ++i) { - mPasteViewLayouts[i] = attrs.getResourceId(attrs.getIndex(i), 0); - } + mPasteViewLayout = attrs.getResourceId(attrs.getIndex(0), 0); + attrs.recycle(); // Convert line offset dips to pixels. @@ -95,8 +82,7 @@ public PastePopupMenu(View parent, PastePopupMenuDelegate delegate) { * Shows the paste popup at an appropriate location relative to the specified position. */ public void showAt(int x, int y) { - if (!canPaste()) return; - updateContent(true); + updateContent(); positionAt(x, y); } @@ -116,9 +102,7 @@ public boolean isShowing() { @Override public void onClick(View v) { - if (canPaste()) { - paste(); - } + paste(); hide(); } @@ -146,7 +130,6 @@ private void positionAt(int x, int y) { final int screenWidth = mContext.getResources().getDisplayMetrics().widthPixels; if (coords[1] < minOffsetY) { - updateContent(false); // Update dimensions from new view contentView = mContainer.getContentView(); width = contentView.getMeasuredWidth(); @@ -171,44 +154,36 @@ private void positionAt(int x, int y) { coords[0] = Math.min(screenWidth - width, coords[0]); } - mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]); - } - - private int viewIndex(boolean onTop) { - return (onTop ? 0 : 1 << 1) + (canPaste() ? 0 : 1 << 0); + if (!isShowing()) { + mContainer.showAtLocation(mParent, Gravity.NO_GRAVITY, coords[0], coords[1]); + } else { + mContainer.update(coords[0], coords[1], -1, -1); + } } - private void updateContent(boolean onTop) { - final int viewIndex = viewIndex(onTop); - View view = mPasteViews[viewIndex]; - - if (view == null) { - final int layout = mPasteViewLayouts[viewIndex]; + private void updateContent() { + if (mPasteView == null) { + final int layout = mPasteViewLayout; LayoutInflater inflater = (LayoutInflater) mContext. getSystemService(Context.LAYOUT_INFLATER_SERVICE); if (inflater != null) { - view = inflater.inflate(layout, null); + mPasteView = inflater.inflate(layout, null); } - if (view == null) { + if (mPasteView == null) { throw new IllegalArgumentException("Unable to inflate TextEdit paste window"); } final int size = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); - view.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT)); - view.measure(size, size); + mPasteView.setLayoutParams( + new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + mPasteView.measure(size, size); - view.setOnClickListener(this); - - mPasteViews[viewIndex] = view; + mPasteView.setOnClickListener(this); } - mContainer.setContentView(view); - } - - private boolean canPaste() { - return mDelegate.canPaste(); + mContainer.setContentView(mPasteView); } private void paste() { diff --git a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java index 5caa21422090d..bfafff0101682 100644 --- a/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java +++ b/content/public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java @@ -6,7 +6,9 @@ import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import org.chromium.content_public.browser.JavaScriptCallback; import org.chromium.content_public.browser.NavigationController; +import org.chromium.content_public.browser.NavigationTransitionDelegate; import org.chromium.content_public.browser.WebContents; /** @@ -21,6 +23,8 @@ private long mNativeWebContentsAndroid; private NavigationController mNavigationController; + private NavigationTransitionDelegate mNavigationTransitionDelegate = null; + private WebContentsImpl( long nativeWebContentsAndroid, NavigationController navigationController) { mNativeWebContentsAndroid = nativeWebContentsAndroid; @@ -80,6 +84,11 @@ public void onShow() { nativeOnShow(mNativeWebContentsAndroid); } + @Override + public void releaseMediaPlayers() { + nativeReleaseMediaPlayers(mNativeWebContentsAndroid); + } + @Override public int getBackgroundColor() { return nativeGetBackgroundColor(mNativeWebContentsAndroid); @@ -136,12 +145,96 @@ public void selectWordAroundCaret() { nativeSelectWordAroundCaret(mNativeWebContentsAndroid); } + @Override + public String getUrl() { + return nativeGetURL(mNativeWebContentsAndroid); + } + + @Override + public boolean isIncognito() { + return nativeIsIncognito(mNativeWebContentsAndroid); + } + + @Override + public void resumeResponseDeferredAtStart() { + nativeResumeResponseDeferredAtStart(mNativeWebContentsAndroid); + } + + @Override + public void setHasPendingNavigationTransitionForTesting() { + nativeSetHasPendingNavigationTransitionForTesting(mNativeWebContentsAndroid); + } + + @Override + public void setNavigationTransitionDelegate(NavigationTransitionDelegate delegate) { + mNavigationTransitionDelegate = delegate; + } + + /** + * Inserts the provided markup sandboxed into the frame. + */ + @Override + public void setupTransitionView(String markup) { + nativeSetupTransitionView(mNativeWebContentsAndroid, markup); + } + + /** + * Hides transition elements specified by the selector, and activates any + * exiting-transition stylesheets. + */ + @Override + public void beginExitTransition(String cssSelector) { + nativeBeginExitTransition(mNativeWebContentsAndroid, cssSelector); + } + + @CalledByNative + private void didDeferAfterResponseStarted(String markup, String cssSelector, + String enteringColor) { + if (mNavigationTransitionDelegate != null ) { + mNavigationTransitionDelegate.didDeferAfterResponseStarted(markup, + cssSelector, enteringColor); + } + } + + @CalledByNative + private boolean willHandleDeferAfterResponseStarted() { + if (mNavigationTransitionDelegate == null) return false; + return mNavigationTransitionDelegate.willHandleDeferAfterResponseStarted(); + } + + @CalledByNative + private void addEnteringStylesheetToTransition(String stylesheet) { + if (mNavigationTransitionDelegate != null ) { + mNavigationTransitionDelegate.addEnteringStylesheetToTransition(stylesheet); + } + } + + @CalledByNative + private void didStartNavigationTransitionForFrame(long frameId) { + if (mNavigationTransitionDelegate != null ) { + mNavigationTransitionDelegate.didStartNavigationTransitionForFrame(frameId); + } + } + + @Override + public void evaluateJavaScript(String script, JavaScriptCallback callback, + boolean startRenderer) { + nativeEvaluateJavaScript(mNativeWebContentsAndroid, script, callback, true); + } + + @CalledByNative + private static void onEvaluateJavaScriptResult( + String jsonResult, JavaScriptCallback callback) { + callback.handleJavaScriptResult(jsonResult); + } + private native String nativeGetTitle(long nativeWebContentsAndroid); private native String nativeGetVisibleURL(long nativeWebContentsAndroid); private native void nativeStop(long nativeWebContentsAndroid); private native void nativeInsertCSS(long nativeWebContentsAndroid, String css); private native void nativeOnHide(long nativeWebContentsAndroid); private native void nativeOnShow(long nativeWebContentsAndroid); + private native void nativeReleaseMediaPlayers(long nativeWebContentsAndroid); private native int nativeGetBackgroundColor(long nativeWebContentsAndroid); private native void nativeAddStyleSheetByURL(long nativeWebContentsAndroid, String url); @@ -155,4 +248,15 @@ private native void nativeUpdateTopControlsState(long nativeWebContentsAndroid, private native void nativeShowImeIfNeeded(long nativeWebContentsAndroid); private native void nativeScrollFocusedEditableNodeIntoView(long nativeWebContentsAndroid); private native void nativeSelectWordAroundCaret(long nativeWebContentsAndroid); + private native String nativeGetURL(long nativeWebContentsAndroid); + private native boolean nativeIsIncognito(long nativeWebContentsAndroid); + private native void nativeResumeResponseDeferredAtStart(long nativeWebContentsAndroid); + private native void nativeSetHasPendingNavigationTransitionForTesting( + long nativeWebContentsAndroid); + private native void nativeSetupTransitionView(long nativeWebContentsAndroid, + String markup); + private native void nativeBeginExitTransition(long nativeWebContentsAndroid, + String cssSelector); + private native void nativeEvaluateJavaScript(long nativeWebContentsAndroid, + String script, JavaScriptCallback callback, boolean startRenderer); } diff --git a/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java b/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java new file mode 100644 index 0000000000000..43bc0391cce6a --- /dev/null +++ b/content/public/android/java/src/org/chromium/content_public/browser/JavaScriptCallback.java @@ -0,0 +1,14 @@ +// 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_public.browser; + +/** Callback interface for WebContents evaluateJavaScript(). */ +public interface JavaScriptCallback { + /** + * Called from native in response to evaluateJavaScript(). + * @param jsonResult json result curresponds to JS execution + */ + void handleJavaScriptResult(String jsonResult); +} diff --git a/content/public/android/java/src/org/chromium/content_public/browser/NavigationTransitionDelegate.java b/content/public/android/java/src/org/chromium/content_public/browser/NavigationTransitionDelegate.java new file mode 100644 index 0000000000000..d719b4325c470 --- /dev/null +++ b/content/public/android/java/src/org/chromium/content_public/browser/NavigationTransitionDelegate.java @@ -0,0 +1,42 @@ +// 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_public.browser; + +/** + * An interface that allows the embedder to be notified of navigation transition + * related events and respond to them. + */ +public interface NavigationTransitionDelegate { + /** + * Called when the navigation is deferred immediately after the response started. + * @param markup The markup coming as part of response. + * @param cssSelector The CSS selectors, which is to be applied to transition layer's + * makrup. + * @param enteringColor The background color of the entering document, as a String + * representing a legal CSS color value. This is inserted into + * the transition layer's markup after the entering stylesheets + * have been applied. + */ + public void didDeferAfterResponseStarted( + String markup, String cssSelector, String enteringColor); + + /** + * Called when a navigation transition has been detected, and we need to check + * if it's supported. + */ + public boolean willHandleDeferAfterResponseStarted(); + + /** + * Called when the navigation is deferred immediately after the response + * started. + */ + public void addEnteringStylesheetToTransition(String stylesheet); + + /** + * Notifies that a navigation transition is started for a given frame. + * @param frameId A positive, non-zero integer identifying the navigating frame. + */ + public void didStartNavigationTransitionForFrame(long frameId); +} diff --git a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java index 7003298561674..9a7174d8a42fb 100644 --- a/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java +++ b/content/public/android/java/src/org/chromium/content_public/browser/WebContents.java @@ -42,6 +42,11 @@ public interface WebContents { */ public void onShow(); + /** + * Stops all media players for this WebContents. + */ + public void releaseMediaPlayers(); + /** * Get the Background color from underlying RenderWidgetHost for this WebContent. */ @@ -106,4 +111,58 @@ public void updateTopControlsState(boolean enableHiding, boolean enableShowing, */ public void selectWordAroundCaret(); + /** + * Get the URL of the current page. + * + * @return The URL of the current page. + */ + public String getUrl(); + + /** + * Get the InCognito state of WebContents. + * + * @return whether this WebContents is in InCognito mode or not + */ + public boolean isIncognito(); + + /** + * Resumes the response which is deferred during start. + */ + public void resumeResponseDeferredAtStart(); + + /** + * Set pending Navigation for transition testing on this WebContents. + */ + public void setHasPendingNavigationTransitionForTesting(); + + /** + * Set delegate for notifying navigation transition. + */ + public void setNavigationTransitionDelegate(NavigationTransitionDelegate delegate); + + /** + * Inserts the provided markup sandboxed into the frame. + */ + public void setupTransitionView(String markup); + + /** + * Hides transition elements specified by the selector, and activates any + * exiting-transition stylesheets. + */ + public void beginExitTransition(String cssSelector); + + /** + * Injects the passed Javascript code in the current page and evaluates it. + * If a result is required, pass in a callback. + * + * @param script The Javascript to execute. + * @param callback The callback to be fired off when a result is ready. The script's + * result will be json encoded and passed as the parameter, and the call + * will be made on the main thread. + * If no result is required, pass null. + * @param startRenderer Tells whether to start Renderer or not for initial empty document + */ + public void evaluateJavaScript(String script, JavaScriptCallback callback, + boolean startRenderer); + } diff --git a/content/public/android/javatests/src/org/chromium/content/browser/BatteryStatusManagerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/BatteryStatusManagerTest.java index d1b6cc83c0295..e5f95d1599c61 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/BatteryStatusManagerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/BatteryStatusManagerTest.java @@ -26,12 +26,13 @@ public void setUp() throws Exception { } @SmallTest - public void testOnReceiveBatteryDischarging() { + public void testOnReceiveBatteryNotPluggedIn() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, true); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, 0); intent.putExtra(BatteryManager.EXTRA_LEVEL, 10); intent.putExtra(BatteryManager.EXTRA_SCALE, 100); - intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_DISCHARGING); + intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_NOT_CHARGING); mBatteryStatusManager.onReceive(intent); @@ -41,9 +42,10 @@ public void testOnReceiveBatteryDischarging() { } @SmallTest - public void testOnReceiveBatteryCharging() { + public void testOnReceiveBatteryPluggedInACCharging() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, true); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_AC); intent.putExtra(BatteryManager.EXTRA_LEVEL, 50); intent.putExtra(BatteryManager.EXTRA_SCALE, 100); intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING); @@ -56,9 +58,26 @@ public void testOnReceiveBatteryCharging() { } @SmallTest - public void testOnReceiveBatteryFull() { + public void testOnReceiveBatteryPluggedInACNotCharging() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, true); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_AC); + intent.putExtra(BatteryManager.EXTRA_LEVEL, 50); + intent.putExtra(BatteryManager.EXTRA_SCALE, 100); + intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_NOT_CHARGING); + + mBatteryStatusManager.onReceive(intent); + + mBatteryStatusManager.verifyCalls("gotBatteryStatus"); + mBatteryStatusManager.verifyValues(true, Double.POSITIVE_INFINITY, + Double.POSITIVE_INFINITY, 0.5); + } + + @SmallTest + public void testOnReceiveBatteryPluggedInUSBFull() { + Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); + intent.putExtra(BatteryManager.EXTRA_PRESENT, true); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); intent.putExtra(BatteryManager.EXTRA_LEVEL, 100); intent.putExtra(BatteryManager.EXTRA_SCALE, 100); intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_FULL); @@ -73,6 +92,18 @@ public void testOnReceiveBatteryFull() { public void testOnReceiveNoBattery() { Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PRESENT, false); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); + + mBatteryStatusManager.onReceive(intent); + + mBatteryStatusManager.verifyCalls("gotBatteryStatus"); + mBatteryStatusManager.verifyValues(true, 0, Double.POSITIVE_INFINITY, 1); + } + + @SmallTest + public void testOnReceiveNoPluggedStatus() { + Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); + intent.putExtra(BatteryManager.EXTRA_PRESENT, true); mBatteryStatusManager.onReceive(intent); diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java index f0b554691c42c..296b808b28276 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ClipboardTest.java @@ -4,6 +4,7 @@ package org.chromium.content.browser; +import android.annotation.TargetApi; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -47,6 +48,7 @@ protected void setUp() throws Exception { * the HTML representation of the fragment to be available. */ @LargeTest + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) @Feature({"Clipboard","TextInput"}) @RerunWithUpdatedContainerView public void testCopyDocumentFragment() throws Throwable { @@ -116,4 +118,4 @@ private Boolean hasPrimaryClip(ClipboardManager clipboardManager) { return false; } -} \ No newline at end of file +} diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreFocusTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreFocusTest.java index d9361de221a04..5ae6b380de966 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreFocusTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ContentViewCoreFocusTest.java @@ -21,20 +21,20 @@ */ public class ContentViewCoreFocusTest extends ContentShellTestBase { private static class TestInputMethodManagerWrapper extends InputMethodManagerWrapper { - private boolean hidden = false; + private boolean mHidden = false; public TestInputMethodManagerWrapper(Context context) { super(context); } @Override public void showSoftInput(View view, int flags, ResultReceiver resultReceiver) { - hidden = false; + mHidden = false; } @Override public boolean hideSoftInputFromWindow(IBinder windowToken, int flags, ResultReceiver resultReceiver) { - hidden = true; + mHidden = true; return true; } @@ -44,7 +44,7 @@ public boolean isActive(View view) { } public boolean isHidden() { - return hidden; + return mHidden; } } diff --git a/content/public/android/javatests/src/org/chromium/content/browser/DeviceSensorsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/DeviceSensorsTest.java index 2b143ccfaaf57..6cd3e25515288 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/DeviceSensorsTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/DeviceSensorsTest.java @@ -44,8 +44,8 @@ public void testRegisterSensorsDeviceMotion() { assertFalse(mDeviceSensors.mDeviceOrientationIsActive); assertEquals(DeviceSensors.DEVICE_MOTION_SENSORS.size(), - mMockSensorManager.numRegistered); - assertEquals(0, mMockSensorManager.numUnRegistered); + mMockSensorManager.mNumRegistered); + assertEquals(0, mMockSensorManager.mNumUnRegistered); assertEquals(DeviceSensors.DEVICE_MOTION_SENSORS.size(), mDeviceSensors.getNumberActiveDeviceMotionSensors()); } @@ -63,8 +63,8 @@ public void testRegisterSensorsDeviceOrientation() { assertTrue(mDeviceSensors.mDeviceOrientationIsActive); assertEquals(DeviceSensors.DEVICE_ORIENTATION_SENSORS.size(), - mMockSensorManager.numRegistered); - assertEquals(0, mMockSensorManager.numUnRegistered); + mMockSensorManager.mNumRegistered); + assertEquals(0, mMockSensorManager.mNumUnRegistered); } @SmallTest @@ -90,8 +90,8 @@ public void testRegisterSensorsDeviceMotionAndOrientation() { assertEquals(union.size(), mDeviceSensors.mActiveSensors.size()); assertTrue(mDeviceSensors.mDeviceMotionIsActive); assertTrue(mDeviceSensors.mDeviceOrientationIsActive); - assertEquals(union.size(), mMockSensorManager.numRegistered); - assertEquals(0, mMockSensorManager.numUnRegistered); + assertEquals(union.size(), mMockSensorManager.mNumRegistered); + assertEquals(0, mMockSensorManager.mNumUnRegistered); assertEquals(DeviceSensors.DEVICE_MOTION_SENSORS.size(), mDeviceSensors.getNumberActiveDeviceMotionSensors()); } @@ -106,7 +106,7 @@ public void testUnregisterSensorsDeviceMotion() { assertFalse(mDeviceSensors.mDeviceMotionIsActive); assertFalse(mDeviceSensors.mDeviceOrientationIsActive); assertEquals(DeviceSensors.DEVICE_MOTION_SENSORS.size(), - mMockSensorManager.numUnRegistered); + mMockSensorManager.mNumUnRegistered); assertEquals(0, mDeviceSensors.getNumberActiveDeviceMotionSensors()); } @@ -120,7 +120,7 @@ public void testUnregisterSensorsDeviceOrientation() { assertFalse(mDeviceSensors.mDeviceMotionIsActive); assertFalse(mDeviceSensors.mDeviceOrientationIsActive); assertEquals(DeviceSensors.DEVICE_ORIENTATION_SENSORS.size(), - mMockSensorManager.numUnRegistered); + mMockSensorManager.mNumUnRegistered); } @SmallTest @@ -136,14 +136,14 @@ public void testUnRegisterSensorsDeviceMotionAndOrientation() { Set diff = new HashSet(DeviceSensors.DEVICE_MOTION_SENSORS); diff.removeAll(DeviceSensors.DEVICE_ORIENTATION_SENSORS); - assertEquals(diff.size(), mMockSensorManager.numUnRegistered); + assertEquals(diff.size(), mMockSensorManager.mNumUnRegistered); mDeviceSensors.stop(DeviceSensors.DEVICE_ORIENTATION); assertTrue("should contain no sensors", mDeviceSensors.mActiveSensors.isEmpty()); assertEquals(diff.size() + DeviceSensors.DEVICE_ORIENTATION_SENSORS.size(), - mMockSensorManager.numUnRegistered); + mMockSensorManager.mNumUnRegistered); assertEquals(0, mDeviceSensors.getNumberActiveDeviceMotionSensors()); } @@ -316,9 +316,9 @@ private void verifyOrientationAngles(float[] gravity, float[] magnetic, private static class DeviceSensorsForTests extends DeviceSensors { - private double value1 = 0; - private double value2 = 0; - private double value3 = 0; + private double mValue1 = 0; + private double mValue2 = 0; + private double mValue3 = 0; private String mCalls = ""; private DeviceSensorsForTests(Context context) { @@ -330,15 +330,15 @@ static DeviceSensorsForTests getInstance(Context context) { } private void verifyValues(double v1, double v2, double v3) { - assertEquals(v1, value1); - assertEquals(v2, value2); - assertEquals(v3, value3); + assertEquals(v1, mValue1); + assertEquals(v2, mValue2); + assertEquals(v3, mValue3); } private void verifyValuesEpsilon(double v1, double v2, double v3) { - assertEquals(v1, value1, 0.1); - assertEquals(v2, value2, 0.1); - assertEquals(v3, value3, 0.1); + assertEquals(v1, mValue1, 0.1); + assertEquals(v2, mValue2, 0.1); + assertEquals(v3, mValue3, 0.1); } private void verifyCalls(String names) { @@ -347,33 +347,33 @@ private void verifyCalls(String names) { @Override protected void gotOrientation(double alpha, double beta, double gamma) { - value1 = alpha; - value2 = beta; - value3 = gamma; + mValue1 = alpha; + mValue2 = beta; + mValue3 = gamma; mCalls = mCalls.concat("gotOrientation"); } @Override protected void gotAcceleration(double x, double y, double z) { - value1 = x; - value2 = y; - value3 = z; + mValue1 = x; + mValue2 = y; + mValue3 = z; mCalls = mCalls.concat("gotAcceleration"); } @Override protected void gotAccelerationIncludingGravity(double x, double y, double z) { - value1 = x; - value2 = y; - value3 = z; + mValue1 = x; + mValue2 = y; + mValue3 = z; mCalls = mCalls.concat("gotAccelerationIncludingGravity"); } @Override protected void gotRotationRate(double alpha, double beta, double gamma) { - value1 = alpha; - value2 = beta; - value3 = gamma; + mValue1 = alpha; + mValue2 = beta; + mValue3 = gamma; mCalls = mCalls.concat("gotRotationRate"); } } @@ -381,8 +381,8 @@ protected void gotRotationRate(double alpha, double beta, double gamma) { private static class MockSensorManager implements DeviceSensors.SensorManagerProxy { - private int numRegistered = 0; - private int numUnRegistered = 0; + private int mNumRegistered = 0; + private int mNumUnRegistered = 0; private MockSensorManager() { } @@ -390,13 +390,13 @@ private MockSensorManager() { @Override public boolean registerListener(SensorEventListener listener, int sensorType, int rate, Handler handler) { - numRegistered++; + mNumRegistered++; return true; } @Override public void unregisterListener(SensorEventListener listener, int sensorType) { - numUnRegistered++; + mNumUnRegistered++; } } } diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java index ae58a1d0d2293..fa42e6cdcc805 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeBasicsTest.java @@ -428,11 +428,11 @@ class InnerObject { class TestObject { public InnerObject getInnerObject() { InnerObject inner = new InnerObject(); - weakRefForInner = new WeakReference(inner); + mWeakRefForInner = new WeakReference(inner); return inner; } // A weak reference is used to check InnerObject instance reachability. - WeakReference weakRefForInner; + WeakReference mWeakRefForInner; } TestObject object = new TestObject(); injectObjectAndReload(object, "testObject"); @@ -442,12 +442,12 @@ public InnerObject getInnerObject() { "(function() { " + "globalInner = testObject.getInnerObject(); return typeof globalInner; " + "})()")); - assertTrue(object.weakRefForInner.get() != null); + assertTrue(object.mWeakRefForInner.get() != null); // Check that returned Java object is being held by the Java bridge, thus it's not // collected. Note that despite that what JavaDoc says about invoking "gc()", both Dalvik // and ART actually run the collector. Runtime.getRuntime().gc(); - assertTrue(object.weakRefForInner.get() != null); + assertTrue(object.mWeakRefForInner.get() != null); // Now dereference the inner object in JS and run GC to collect the interface object. assertEquals("true", executeJavaScriptAndGetStringResult( "(function() { " + @@ -456,7 +456,7 @@ public InnerObject getInnerObject() { // Force GC on the Java side again. The bridge had to release the inner object, so it must // be collected this time. Runtime.getRuntime().gc(); - assertEquals(null, object.weakRefForInner.get()); + assertEquals(null, object.mWeakRefForInner.get()); } @SmallTest diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java index f0bfe74dbb167..cbb6b6a515579 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeChildFrameTest.java @@ -7,6 +7,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.chromium.base.test.util.Feature; +import org.chromium.content_public.browser.JavaScriptCallback; /** * Part of the test suite for the WebView's Java Bridge. @@ -103,7 +104,7 @@ private String executeJavaScriptAndGetResult(final ContentViewCore contentViewCo final String script) throws Throwable { final String[] result = new String[1]; class ResultCallback extends JavaBridgeTestBase.Controller - implements ContentViewCore.JavaScriptCallback { + implements JavaScriptCallback { @Override public void handleJavaScriptResult(String jsonResult) { result[0] = jsonResult; diff --git a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java index 3afecfe9acab3..5c4a9823978ba 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/JavaBridgeCoercionTest.java @@ -22,9 +22,9 @@ */ public class JavaBridgeCoercionTest extends JavaBridgeTestBase { private class TestObject extends Controller { - private Object objectInstance; - private CustomType customTypeInstance; - private CustomType2 customType2Instance; + private Object mObjectInstance; + private CustomType mCustomTypeInstance; + private CustomType2 mCustomType2Instance; private boolean mBooleanValue; private byte mByteValue; @@ -39,19 +39,19 @@ private class TestObject extends Controller { private CustomType mCustomTypeValue; public TestObject() { - objectInstance = new Object(); - customTypeInstance = new CustomType(); - customType2Instance = new CustomType2(); + mObjectInstance = new Object(); + mCustomTypeInstance = new CustomType(); + mCustomType2Instance = new CustomType2(); } public Object getObjectInstance() { - return objectInstance; + return mObjectInstance; } public CustomType getCustomTypeInstance() { - return customTypeInstance; + return mCustomTypeInstance; } public CustomType2 getCustomType2Instance() { - return customType2Instance; + return mCustomType2Instance; } public synchronized void setBooleanValue(boolean x) { diff --git a/content/public/android/javatests/src/org/chromium/content/browser/MediaResourceGetterTest.java b/content/public/android/javatests/src/org/chromium/content/browser/MediaResourceGetterTest.java index a6adc96909b12..4d17f42a880b0 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/MediaResourceGetterTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/MediaResourceGetterTest.java @@ -195,12 +195,12 @@ void bind(int key, String value) { * Helper class to control the result of permission checks. */ private static class InternalMockContext extends MockContext { - boolean allowPermission = false; + boolean mAllowPermission = false; @Override public int checkCallingOrSelfPermission(String permission) { assertEquals(android.Manifest.permission.ACCESS_NETWORK_STATE, permission); - return allowPermission ? PackageManager.PERMISSION_GRANTED : + return mAllowPermission ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; } } @@ -256,14 +256,14 @@ public void testMediaMetadataGetters() { @SmallTest public void testConfigure_Net_NoPermissions() { - mMockContext.allowPermission = false; + mMockContext.mAllowPermission = false; assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT)); } @SmallTest public void testConfigure_Net_NoActiveNetwork() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = null; assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT)); @@ -271,7 +271,7 @@ public void testConfigure_Net_NoActiveNetwork() { @SmallTest public void testConfigure_Net_Disallowed_Mobile() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_MOBILE; assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT)); @@ -279,7 +279,7 @@ public void testConfigure_Net_Disallowed_Mobile() { @SmallTest public void testConfigure_Net_Disallowed_Wimax() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIMAX; assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT)); @@ -287,7 +287,7 @@ public void testConfigure_Net_Disallowed_Wimax() { @SmallTest public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, null)); @@ -298,7 +298,7 @@ public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() { @SmallTest public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIFI; assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, null)); @@ -309,7 +309,7 @@ public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() { @SmallTest public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, "", null)); @@ -320,7 +320,7 @@ public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() { @SmallTest public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, TEST_COOKIES, TEST_USER_AGENT)); @@ -331,7 +331,7 @@ public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() { @SmallTest public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, "", TEST_USER_AGENT)); @@ -342,7 +342,7 @@ public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() { @SmallTest public void testConfigure_Net_Allowed_Ethernet_Exception() { - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mThrowExceptionInConfigure = true; mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, @@ -358,7 +358,7 @@ public void testConfigure_Net_Allowed_LocalHost_WithNoNetwork() { "https://127.0.0.1/", "http://[::1]:8888/", }; - mMockContext.allowPermission = true; + mMockContext.mAllowPermission = true; mFakeMRG.mNetworkType = null; for (String localHostUrl : localHostUrls) { assertTrue(mFakeMRG.configure(mMockContext, localHostUrl, @@ -439,6 +439,46 @@ public void testConfigure_File_Allowed_Exception() { assertNull(mFakeMRG.mPath); } + @SmallTest + public void testConfigure_Blob_Disallow_Null_Cache() { + final String path = "/data/data/" + null + "/cache/"; + final String url = path; + mFakeMRG.mFileExists = true; + mFakeMRG.mThrowExceptionInConfigure = true; + assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); + assertNull(mFakeMRG.mPath); + } + + @SmallTest + public void testConfigure_Blob_Disallowed_Incomplete_Path() { + final String path = "/data/data/"; + final String url = path; + mFakeMRG.mFileExists = true; + mFakeMRG.mThrowExceptionInConfigure = true; + assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); + assertNull(mFakeMRG.mPath); + } + + @SmallTest + public void testConfigure_Blob_Disallowed_Unknown_Path() { + final String path = "/unknown/path/"; + final String url = path; + mFakeMRG.mFileExists = true; + mFakeMRG.mThrowExceptionInConfigure = true; + assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); + assertNull(mFakeMRG.mPath); + } + + @SmallTest + public void testConfigure_Blob_Disallowed_Other_Application() { + final String path = "/data/data/org.some.other.app/cache/"; + final String url = path; + mFakeMRG.mFileExists = true; + mFakeMRG.mThrowExceptionInConfigure = true; + assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); + assertNull(mFakeMRG.mPath); + } + @SmallTest public void testExtract_NoMetadata() { mFakeMRG.mFileExists = true; diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java index 153e0af321d81..907df4c03effd 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationListenerTest.java @@ -5,7 +5,6 @@ package org.chromium.content.browser; import android.content.pm.ActivityInfo; -import android.os.Build; import android.test.suitebuilder.annotation.MediumTest; import org.chromium.base.ThreadUtils; @@ -82,6 +81,7 @@ public void setUp() throws Exception { @Override public void run() { ScreenOrientationListener.getInstance().addObserver(mObserver, activity); + ScreenOrientationListener.getInstance().startAccurateListening(); } }); @@ -95,6 +95,7 @@ public void tearDown() throws Exception { @Override public void run() { getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); + ScreenOrientationListener.getInstance().startAccurateListening(); } }); @@ -122,13 +123,6 @@ public void testVariousOrientationChanges() throws Exception { @MediumTest @Feature({"ScreenOrientation"}) public void testFlipPortrait() throws Exception { - // This can't work for pre JB-MR1 SDKs for the moment. We will whether - // disable the feature entirely on that platform or add an "accurate" - // mode. - // See http://crbug.com/400158 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) - return; - lockOrientationAndWait(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); assertEquals(0, mObserver.mOrientation); @@ -140,13 +134,6 @@ public void testFlipPortrait() throws Exception { @MediumTest @Feature({"ScreenOrientation"}) public void testFlipLandscape() throws Exception { - // This can't work for pre JB-MR1 SDKs for the moment. We will whether - // disable the feature entirely on that platform or add an "accurate" - // mode. - // See http://crbug.com/400158 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) - return; - lockOrientationAndWait(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); assertEquals(90, mObserver.mOrientation); diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationProviderTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationProviderTest.java index 92354f1087c15..4e711f624c1a0 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationProviderTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/ScreenOrientationProviderTest.java @@ -4,7 +4,6 @@ package org.chromium.content.browser; -import android.os.Build; import android.test.suitebuilder.annotation.MediumTest; import org.chromium.base.ThreadUtils; @@ -87,6 +86,7 @@ public void setUp() throws Exception { @Override public void run() { ScreenOrientationListener.getInstance().addObserver(mObserver, activity); + ScreenOrientationProvider.startAccurateListening(); } }); @@ -103,6 +103,7 @@ public void tearDown() throws Exception { @Override public void run() { ScreenOrientationProvider.unlockOrientation(); + ScreenOrientationProvider.startAccurateListening(); } }); @@ -129,14 +130,6 @@ public void testBasicValues() throws Exception { @MediumTest @Feature({"ScreenOrientation"}) public void testPortrait() throws Exception { - // Do not run that test for versions of Android before JB-MR1 because - // the ScreenOrientationListener for those versions isn't accurate - // enough. We will later simply not run that code in that version or add - // an "accurate mode" that will fix this by polling. - // See http://crbug.com/400158 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) - return; - lockOrientationAndWait(ScreenOrientationValues.PORTRAIT_PRIMARY); assertTrue(checkOrientationForLock(ScreenOrientationValues.PORTRAIT_PRIMARY)); @@ -157,14 +150,6 @@ public void testPortrait() throws Exception { @MediumTest @Feature({"ScreenOrientation"}) public void testLandscape() throws Exception { - // Do not run that test for versions of Android before JB-MR1 because - // the ScreenOrientationListener for those versions isn't accurate - // enough. We will later simply not run that code in that version or add - // an "accurate mode" that will fix this by polling. - // See http://crbug.com/400158 - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) - return; - lockOrientationAndWait(ScreenOrientationValues.LANDSCAPE_PRIMARY); assertTrue(checkOrientationForLock(ScreenOrientationValues.LANDSCAPE_PRIMARY)); diff --git a/content/public/android/javatests/src/org/chromium/content/browser/TransitionTest.java b/content/public/android/javatests/src/org/chromium/content/browser/TransitionTest.java index 6e84b75e67a5d..a9035b7c0271d 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/TransitionTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/TransitionTest.java @@ -9,8 +9,8 @@ import android.util.Pair; import org.chromium.base.test.util.UrlUtils; -import org.chromium.content.browser.ContentViewCore.NavigationTransitionDelegate; import org.chromium.content.browser.test.util.TestCallbackHelperContainer; +import org.chromium.content_public.browser.NavigationTransitionDelegate; import org.chromium.content_shell_apk.ContentShellActivity; import org.chromium.content_shell_apk.ContentShellTestBase; import org.chromium.net.test.util.TestWebServer; @@ -46,7 +46,8 @@ static class TestNavigationTransitionDelegate implements NavigationTransitionDel } @Override - public void didDeferAfterResponseStarted(String enteringColor) { + public void didDeferAfterResponseStarted(String markup, String cssSelector, + String enteringColor) { mDidCallDefer = true; mContentViewCore.resumeResponseDeferredAtStart(); mTransitionEnteringColor = enteringColor; @@ -109,11 +110,11 @@ public void testDidDeferAfterResponseStartedCalled() throws Throwable { TestCallbackHelperContainer testCallbackHelperContainer = new TestCallbackHelperContainer(contentViewCore); - contentViewCore.setHasPendingNavigationTransitionForTesting(); + contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting(); TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate( contentViewCore, true); - contentViewCore.setNavigationTransitionDelegate(delegate); + contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate); loadUrl(contentViewCore, testCallbackHelperContainer, new LoadUrlParams(URL_1)); @@ -132,11 +133,11 @@ public void testDidDeferAfterResponseStartedNotCalled() throws Throwable { TestCallbackHelperContainer testCallbackHelperContainer = new TestCallbackHelperContainer(contentViewCore); - contentViewCore.setHasPendingNavigationTransitionForTesting(); + contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting(); TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate( contentViewCore, false); - contentViewCore.setNavigationTransitionDelegate(delegate); + contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate); loadUrl(contentViewCore, testCallbackHelperContainer, new LoadUrlParams(URL_1)); @@ -157,7 +158,7 @@ public void testWillHandleDeferAfterResponseStartedNotCalled() throws Throwable TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate( contentViewCore, false); - contentViewCore.setNavigationTransitionDelegate(delegate); + contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate); loadUrl(contentViewCore, testCallbackHelperContainer, new LoadUrlParams(URL_1)); @@ -182,10 +183,10 @@ public void testAddStylesheetToTransitionCalled() throws Throwable { ContentViewCore contentViewCore = activity.getActiveContentViewCore(); TestCallbackHelperContainer testCallbackHelperContainer = new TestCallbackHelperContainer(contentViewCore); - contentViewCore.setHasPendingNavigationTransitionForTesting(); + contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting(); TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate(contentViewCore, true); - contentViewCore.setNavigationTransitionDelegate(delegate); + contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate); int currentCallCount = testCallbackHelperContainer .getOnPageFinishedHelper().getCallCount(); @@ -234,10 +235,10 @@ public void testAddStylesheetToTransitionNotCalled() throws Throwable { ContentViewCore contentViewCore = activity.getActiveContentViewCore(); TestCallbackHelperContainer testCallbackHelperContainer = new TestCallbackHelperContainer(contentViewCore); - contentViewCore.setHasPendingNavigationTransitionForTesting(); + contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting(); TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate(contentViewCore, true); - contentViewCore.setNavigationTransitionDelegate(delegate); + contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate); int currentCallCount = testCallbackHelperContainer .getOnPageFinishedHelper().getCallCount(); @@ -276,10 +277,10 @@ public void testParseTransitionEnteringColor() throws Throwable { ContentViewCore contentViewCore = activity.getActiveContentViewCore(); TestCallbackHelperContainer testCallbackHelperContainer = new TestCallbackHelperContainer(contentViewCore); - contentViewCore.setHasPendingNavigationTransitionForTesting(); + contentViewCore.getWebContents().setHasPendingNavigationTransitionForTesting(); TestNavigationTransitionDelegate delegate = new TestNavigationTransitionDelegate(contentViewCore, true); - contentViewCore.setNavigationTransitionDelegate(delegate); + contentViewCore.getWebContents().setNavigationTransitionDelegate(delegate); String transitionEnteringColor = "#00FF00"; diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java index bc88692586913..8ec79e19723a3 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/ImeTest.java @@ -379,13 +379,52 @@ public void testKeyCodesWhileComposingText() throws Throwable { assertEquals("", mConnection.getTextBeforeCursor(9, 0)); } + @SmallTest + @Feature({"TextInput", "Main"}) + public void testKeyCodesWhileSwipingText() throws Throwable { + DOMUtils.focusNode(mContentViewCore, "textarea"); + assertWaitForKeyboardStatus(true); + + // The calls below are a reflection of what the stock Google Keyboard (Android 4.4) sends + // when the word is swiped on the soft keyboard. Exercise care when altering to make sure + // that the test reflects reality. If this test breaks, it's possible that code has + // changed and different calls need to be made instead. + mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); + waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1); + + // "three" + expectUpdateStateCall(mConnection); + setComposingText(mConnection, "three", 1); + assertEquals(KeyEvent.KEYCODE_UNKNOWN, mImeAdapter.mLastSyntheticKeyCode); + assertUpdateStateCall(mConnection, 1000); + assertEquals("three", mConnection.getTextBeforeCursor(99, 0)); + + // "word" + commitText(mConnection, "three", 1); + commitText(mConnection, " ", 1); + expectUpdateStateCall(mConnection); + setComposingText(mConnection, "word", 1); + assertEquals(KeyEvent.KEYCODE_UNKNOWN, mImeAdapter.mLastSyntheticKeyCode); + assertUpdateStateCall(mConnection, 1000); + assertEquals("three word", mConnection.getTextBeforeCursor(99, 0)); + + // "test" + commitText(mConnection, "word", 1); + commitText(mConnection, " ", 1); + expectUpdateStateCall(mConnection); + setComposingText(mConnection, "test", 1); + assertEquals(KeyEvent.KEYCODE_UNKNOWN, mImeAdapter.mLastSyntheticKeyCode); + assertUpdateStateCall(mConnection, 1000); + assertEquals("three word test", mConnection.getTextBeforeCursor(99, 0)); + } + @SmallTest @Feature({"TextInput", "Main"}) public void testKeyCodesWhileTypingText() throws Throwable { DOMUtils.focusNode(mContentViewCore, "textarea"); assertWaitForKeyboardStatus(true); - // The calls below are a reflection of what the Hacker's Keyboard sends when the noted + // The calls below are a reflection of what the Hacker's Keyboard sends when the noted // key is touched on screen. Exercise care when altering to make sure that the test // reflects reality. mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); @@ -461,6 +500,21 @@ public void testKeyCodesWhileTypingText() throws Throwable { assertEquals("", mConnection.getTextBeforeCursor(9, 0)); } + @SmallTest + @Feature({"TextInput", "Main"}) + public void testSetComposingRegionOutOfBounds() throws Throwable { + DOMUtils.focusNode(mContentViewCore, "textarea"); + assertWaitForKeyboardStatus(true); + + mConnection = (TestAdapterInputConnection) getAdapterInputConnection(); + waitAndVerifyEditableCallback(mConnection.mImeUpdateQueue, 0, "", 0, 0, -1, -1); + setComposingText(mConnection, "hello", 1); + + setComposingRegion(mConnection, 0, 0); + setComposingRegion(mConnection, 0, 9); + setComposingRegion(mConnection, 9, 0); + } + @SmallTest @Feature({"TextInput", "Main"}) public void testEnterKeyEventWhileComposingText() throws Throwable { diff --git a/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java b/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java index 003a92f40a7de..aafef35569438 100644 --- a/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java +++ b/content/public/android/javatests/src/org/chromium/content/browser/input/InputDialogContainerTest.java @@ -11,24 +11,24 @@ import org.chromium.base.test.util.Feature; public class InputDialogContainerTest extends AndroidTestCase { - private static int TEXT_INPUT_TYPE_DATE = 0; - private static int TEXT_INPUT_TYPE_DATETIME = 1; - private static int TEXT_INPUT_TYPE_DATETIMELOCAL = 2; - private static int TEXT_INPUT_TYPE_MONTH = 3; - private static int TEXT_INPUT_TYPE_TIME = 4; - private static int TEXT_INPUT_TYPE_WEEK = 5; + private static final int TEXT_INPUT_TYPE_DATE = 0; + private static final int TEXT_INPUT_TYPE_DATETIME = 1; + private static final int TEXT_INPUT_TYPE_DATETIMELOCAL = 2; + private static final int TEXT_INPUT_TYPE_MONTH = 3; + private static final int TEXT_INPUT_TYPE_TIME = 4; + private static final int TEXT_INPUT_TYPE_WEEK = 5; // Defined in third_party/WebKit/Source/platform/DateComponents.h - private static double DATE_DIALOG_DEFAULT_MIN = -62135596800000.0; - private static double DATE_DIALOG_DEFAULT_MAX = 8640000000000000.0; - private static double DATETIMELOCAL_DIALOG_DEFAULT_MIN = -62135596800000.0; - private static double DATETIMELOCAL_DIALOG_DEFAULT_MAX = 8640000000000000.0; - private static double MONTH_DIALOG_DEFAULT_MIN = -23628.0; - private static double MONTH_DIALOG_DEFAULT_MAX = 3285488.0; - private static double TIME_DIALOG_DEFAULT_MIN = 0.0; - private static double TIME_DIALOG_DEFAULT_MAX = 86399999.0; - private static double WEEK_DIALOG_DEFAULT_MIN = -62135596800000.0; - private static double WEEK_DIALOG_DEFAULT_MAX = 8639999568000000.0; + private static final double DATE_DIALOG_DEFAULT_MIN = -62135596800000.0; + private static final double DATE_DIALOG_DEFAULT_MAX = 8640000000000000.0; + private static final double DATETIMELOCAL_DIALOG_DEFAULT_MIN = -62135596800000.0; + private static final double DATETIMELOCAL_DIALOG_DEFAULT_MAX = 8640000000000000.0; + private static final double MONTH_DIALOG_DEFAULT_MIN = -23628.0; + private static final double MONTH_DIALOG_DEFAULT_MAX = 3285488.0; + private static final double TIME_DIALOG_DEFAULT_MIN = 0.0; + private static final double TIME_DIALOG_DEFAULT_MAX = 86399999.0; + private static final double WEEK_DIALOG_DEFAULT_MIN = -62135596800000.0; + private static final double WEEK_DIALOG_DEFAULT_MAX = 8639999568000000.0; InputActionDelegateForTests mInputActionDelegate; InputDialogContainerForTests mInputDialogContainer; @@ -332,7 +332,7 @@ public void replaceDateTime(double dialogValue) { @Override public void cancelDateTimeDialog() { } - }; + } private static class InputDialogContainerForTests extends InputDialogContainer { private int mExpectedDialogType; @@ -391,6 +391,7 @@ void showPickerDialog(final int dialogType, assertEquals(mExpectedStep, step); } + @Override public void setFieldDateTimeValue(int dialogType, int year, int month, int monthDay, int hourOfDay, int minute, int second, int millis, diff --git a/content/public/app/android_library_loader_hooks.h b/content/public/app/android_library_loader_hooks.h index b815f0807055b..f6779c6a1499c 100644 --- a/content/public/app/android_library_loader_hooks.h +++ b/content/public/app/android_library_loader_hooks.h @@ -21,8 +21,7 @@ CONTENT_EXPORT bool EnsureJniRegistered(JNIEnv* env); // This is designed to be used as a hook function to be passed to // base::android::SetLibraryLoadedHook CONTENT_EXPORT bool LibraryLoaded(JNIEnv* env, - jclass clazz, - jobjectArray init_command_line); + jclass clazz); } // namespace content diff --git a/content/public/browser/BUILD.gn b/content/public/browser/BUILD.gn index 05cb6f190961d..378eed5dfe59f 100644 --- a/content/public/browser/BUILD.gn +++ b/content/public/browser/BUILD.gn @@ -5,7 +5,18 @@ import("//content/browser/browser.gni") import("//build/config/ui.gni") -source_set("browser") { +group("browser") { + if (is_component_build) { + deps = [ "//content" ] + } else { + deps = [ ":sources" ] + } + forward_dependent_configs_from = deps +} + +source_set("sources") { + visibility = [ "//content", ":browser" ] + if (is_ios) { # iOS doesn't get the normal file list and only takes these whitelisted # files. diff --git a/content/public/browser/android/devtools_auth.h b/content/public/browser/android/devtools_auth.h index 700892c034a00..857a1a141371b 100644 --- a/content/public/browser/android/devtools_auth.h +++ b/content/public/browser/android/devtools_auth.h @@ -6,14 +6,14 @@ #define CONTENT_PUBLIC_BROWSER_DEVTOOLS_AUTH_ANDROID_H_ #include "content/common/content_export.h" - -#include +#include "net/socket/unix_domain_server_socket_posix.h" namespace content { -// Returns true if the given user/group pair is authorized to connect to the -// devtools server, false if not. -CONTENT_EXPORT bool CanUserConnectToDevTools(uid_t uid, gid_t gid); +// Returns true if the given peer identified by the credentials is authorized +// to connect to the devtools server, false if not. +CONTENT_EXPORT bool CanUserConnectToDevTools( + const net::UnixDomainServerSocket::Credentials& credentials); } // namespace content diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 23ea93765f8fa..8de36c1415b8a 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -392,6 +392,7 @@ class CONTENT_EXPORT ContentBrowserClient { ResourceType resource_type, bool overridable, bool strict_enforcement, + bool expired_previous_decision, const base::Callback& callback, CertificateRequestResultType* result) {} diff --git a/content/public/browser/devtools_agent_host.h b/content/public/browser/devtools_agent_host.h index dd18a53886b17..be02843731f42 100644 --- a/content/public/browser/devtools_agent_host.h +++ b/content/public/browser/devtools_agent_host.h @@ -15,7 +15,6 @@ namespace content { class DevToolsExternalAgentProxyDelegate; -class RenderViewHost; class WebContents; // Describes interface for managing devtools agents from browser process. @@ -30,13 +29,9 @@ class CONTENT_EXPORT DevToolsAgentHost static scoped_refptr GetOrCreateFor( WebContents* web_contents); - // Returns DevToolsAgentHost that can be used for inspecting |rvh|. - // New DevToolsAgentHost will be created if it does not exist. - static scoped_refptr GetOrCreateFor(RenderViewHost* rvh); - - // Returns true iff an instance of DevToolsAgentHost for the |rvh| + // Returns true iff an instance of DevToolsAgentHost for the |web_contents| // does exist. - static bool HasFor(RenderViewHost* rvh); + static bool HasFor(WebContents* web_contents); // Returns DevToolsAgentHost that can be used for inspecting shared worker // with given worker process host id and routing id. @@ -51,8 +46,8 @@ class CONTENT_EXPORT DevToolsAgentHost static bool IsDebuggerAttached(WebContents* web_contents); - // Returns a list of all existing RenderViewHost's that can be debugged. - static std::vector GetValidRenderViewHosts(); + // Returns a list of all existing WebContents that can be debugged. + static std::vector GetInspectableWebContents(); // Returns true if there is a client attached. virtual bool IsAttached() = 0; @@ -63,15 +58,15 @@ class CONTENT_EXPORT DevToolsAgentHost // Returns the unique id of the agent. virtual std::string GetId() = 0; - // Returns render view host instance for this host if any. - virtual RenderViewHost* GetRenderViewHost() = 0; + // Returns web contents instance for this host if any. + virtual WebContents* GetWebContents() = 0; // Temporarily detaches render view host from this host. Must be followed by - // a call to ConnectRenderViewHost (may leak the host instance otherwise). - virtual void DisconnectRenderViewHost() = 0; + // a call to ConnectWebContents (may leak the host instance otherwise). + virtual void DisconnectWebContents() = 0; // Attaches render view host to this host. - virtual void ConnectRenderViewHost(RenderViewHost* rvh) = 0; + virtual void ConnectWebContents(WebContents* web_contents) = 0; // Returns true if DevToolsAgentHost is for worker. virtual bool IsWorker() const = 0; diff --git a/content/public/browser/devtools_client_host.h b/content/public/browser/devtools_client_host.h index 67b7003ab501c..bee3a5eece3b5 100644 --- a/content/public/browser/devtools_client_host.h +++ b/content/public/browser/devtools_client_host.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. diff --git a/content/public/browser/devtools_manager.h b/content/public/browser/devtools_manager.h index 32082ea8bc55a..9bd9475679bd2 100644 --- a/content/public/browser/devtools_manager.h +++ b/content/public/browser/devtools_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2012 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. diff --git a/content/public/browser/invalidate_type.h b/content/public/browser/invalidate_type.h index e352e013969c6..9eec214e435ef 100644 --- a/content/public/browser/invalidate_type.h +++ b/content/public/browser/invalidate_type.h @@ -16,6 +16,8 @@ enum InvalidateTypes { INVALIDATE_TYPE_LOAD = 1 << 2, // The loading state has changed. INVALIDATE_TYPE_PAGE_ACTIONS = 1 << 3, // Page action icons have changed. INVALIDATE_TYPE_TITLE = 1 << 4, // The title changed. + + INVALIDATE_TYPE_ALL = (1 << 5) - 1, }; } diff --git a/content/public/browser/render_view_host.h b/content/public/browser/render_view_host.h index 840be8cb06673..2c4c25f59fe7d 100644 --- a/content/public/browser/render_view_host.h +++ b/content/public/browser/render_view_host.h @@ -194,9 +194,14 @@ class CONTENT_EXPORT RenderViewHost : virtual public RenderWidgetHost { // RenderViewHostDelegate. virtual void SyncRendererPrefs() = 0; - // Returns the current WebKit preferences. + // Returns the current WebKit preferences. Note: WebPreferences is cached, so + // this lookup will be fast virtual WebPreferences GetWebkitPreferences() = 0; + // If any state that affects the webkit preferences changed, this method must + // be called. This triggers recomputing preferences. + virtual void OnWebkitPreferencesChanged() = 0; + // Passes a list of Webkit preferences to the renderer. virtual void UpdateWebkitPreferences(const WebPreferences& prefs) = 0; diff --git a/content/public/browser/service_worker_context.h b/content/public/browser/service_worker_context.h index 2d0805e9b99ec..e035fbec34ed0 100644 --- a/content/public/browser/service_worker_context.h +++ b/content/public/browser/service_worker_context.h @@ -7,12 +7,13 @@ #include "base/basictypes.h" #include "base/callback_forward.h" +#include "content/public/browser/service_worker_usage_info.h" #include "url/gurl.h" namespace content { // Represents the per-StoragePartition ServiceWorker data. Must be used from -// the UI thread. +// the IO thread. class ServiceWorkerContext { public: // https://rawgithub.com/slightlyoff/ServiceWorker/master/spec/service_worker/index.html#url-scope: @@ -21,6 +22,9 @@ class ServiceWorkerContext { typedef base::Callback ResultCallback; + typedef base::Callback& + usage_info)> GetUsageInfoCallback; + // Equivalent to calling navigator.serviceWorker.register(script_url, {scope: // pattern}) from a renderer, except that |pattern| is an absolute URL instead // of relative to some current origin. |callback| is passed true when the JS @@ -54,6 +58,10 @@ class ServiceWorkerContext { // from starting up. virtual void Terminate() = 0; + // Methods used in response to browsing data and quota manager requests. + virtual void GetAllOriginsInfo(const GetUsageInfoCallback& callback) = 0; + virtual void DeleteForOrigin(const GURL& origin_url) = 0; + protected: ServiceWorkerContext() {} virtual ~ServiceWorkerContext() {} diff --git a/content/public/browser/service_worker_usage_info.cc b/content/public/browser/service_worker_usage_info.cc new file mode 100644 index 0000000000000..c35d54546d47b --- /dev/null +++ b/content/public/browser/service_worker_usage_info.cc @@ -0,0 +1,24 @@ +// 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 "content/public/browser/service_worker_usage_info.h" + +namespace content { + +ServiceWorkerUsageInfo::ServiceWorkerUsageInfo(const GURL& origin, + const std::vector& scopes) + : origin(origin), scopes(scopes) { +} + +ServiceWorkerUsageInfo::ServiceWorkerUsageInfo(const GURL& origin) + : origin(origin) { +} + +ServiceWorkerUsageInfo::ServiceWorkerUsageInfo() { +} + +ServiceWorkerUsageInfo::~ServiceWorkerUsageInfo() { +} + +} // namespace content diff --git a/content/public/browser/service_worker_usage_info.h b/content/public/browser/service_worker_usage_info.h new file mode 100644 index 0000000000000..bfd6fc579bbd7 --- /dev/null +++ b/content/public/browser/service_worker_usage_info.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_PUBLIC_BROWSER_SERVICE_WORKER_USAGE_INFO_H_ +#define CONTENT_PUBLIC_BROWSER_SERVICE_WORKER_USAGE_INFO_H_ + +#include + +#include "content/common/content_export.h" +#include "url/gurl.h" + +namespace content { + +// Used to report per-origin storage info for registered Service Workers. +class CONTENT_EXPORT ServiceWorkerUsageInfo { + public: + ServiceWorkerUsageInfo(const GURL& origin, const std::vector& scopes); + ServiceWorkerUsageInfo(const GURL& origin); + ServiceWorkerUsageInfo(); + ~ServiceWorkerUsageInfo(); + + // The origin this object is describing. + GURL origin; + + // The set of all Service Worker registrations within this origin; + // a registration is a 'scope' - a URL with optional wildcard. + std::vector scopes; + + // TODO(jsbell): Add size on disk (in bytes). + // TODO(jsbell): Add last modified time. +}; + +} // namespace content + +#endif // CONTENT_PUBLIC_BROWSER_SERVICE_WORKER_USAGE_INFO_H_ diff --git a/content/public/browser/ssl_host_state_delegate.h b/content/public/browser/ssl_host_state_delegate.h index 859a4e78be5fc..bcacd7fdef1aa 100644 --- a/content/public/browser/ssl_host_state_delegate.h +++ b/content/public/browser/ssl_host_state_delegate.h @@ -7,13 +7,21 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" +#include "base/threading/non_thread_safe.h" #include "content/common/content_export.h" #include "net/cert/x509_certificate.h" namespace content { +// The SSLHostStateDelegate encapulates the host-specific state for SSL errors. +// For example, SSLHostStateDelegate remembers whether the user has whitelisted +// a particular broken cert for use with particular host. We separate this +// state from the SSLManager because this state is shared across many navigation +// controllers. +// // SSLHostStateDelegate may be implemented by the embedder to provide a storage -// strategy for certificate decisions. +// strategy for certificate decisions or it may be left unimplemented to use a +// default strategy of not remembering decisions at all. class SSLHostStateDelegate { public: // Records that |cert| is not permitted to be used for |host| in the future, @@ -31,17 +39,21 @@ class SSLHostStateDelegate { // Clear all allow/deny preferences. virtual void Clear() = 0; - // Queries whether |cert| is allowed or denied for |host| and |error|. - virtual net::CertPolicy::Judgment QueryPolicy(const std::string& host, - net::X509Certificate* cert, - net::CertStatus error) = 0; + // Queries whether |cert| is allowed or denied for |host| and |error|. Returns + // true in |expired_previous_decision| if a previous user decision expired + // immediately prior to this query, otherwise false. + virtual net::CertPolicy::Judgment QueryPolicy( + const std::string& host, + net::X509Certificate* cert, + net::CertStatus error, + bool* expired_previous_decision) = 0; - // Revoke all allow/deny preferences for |host|. - virtual void RevokeAllowAndDenyPreferences(const std::string& host) = 0; + // Records that a host has run insecure content. + virtual void HostRanInsecureContent(const std::string& host, int pid) = 0; - // Returns true if any decisions has been recorded for |host|, otherwise - // false. - virtual bool HasAllowedOrDeniedCert(const std::string& host) = 0; + // Returns whether the specified host ran insecure content. + virtual bool DidHostRunInsecureContent(const std::string& host, + int pid) const = 0; protected: virtual ~SSLHostStateDelegate() {} diff --git a/content/public/browser/storage_partition.h b/content/public/browser/storage_partition.h index fdce3b551be7f..8e6fc9c5bae9d 100644 --- a/content/public/browser/storage_partition.h +++ b/content/public/browser/storage_partition.h @@ -71,6 +71,7 @@ class CONTENT_EXPORT StoragePartition { static const uint32 REMOVE_DATA_MASK_SHADER_CACHE = 1 << 5; static const uint32 REMOVE_DATA_MASK_WEBSQL = 1 << 6; static const uint32 REMOVE_DATA_MASK_WEBRTC_IDENTITY = 1 << 7; + static const uint32 REMOVE_DATA_MASK_SERVICE_WORKERS = 1 << 8; static const uint32 REMOVE_DATA_MASK_ALL = 0xFFFFFFFF; // Corresponds to quota::kStorageTypeTemporary. diff --git a/content/public/browser/web_contents.h b/content/public/browser/web_contents.h index b16330626f7ce..af3f23e8d39c9 100644 --- a/content/public/browser/web_contents.h +++ b/content/public/browser/web_contents.h @@ -14,6 +14,7 @@ #include "base/strings/string16.h" #include "base/supports_user_data.h" #include "content/common/content_export.h" +#include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_controller.h" #include "content/public/browser/page_navigator.h" #include "content/public/browser/save_page_type.h" @@ -52,6 +53,7 @@ class RenderFrameHost; class RenderProcessHost; class RenderViewHost; class RenderWidgetHostView; +class ScreenOrientationDispatcherHost; class SiteInstance; class WebContentsDelegate; struct CustomContextMenuContext; @@ -146,6 +148,8 @@ class WebContents : public PageNavigator, // Intrinsic tab state ------------------------------------------------------- + virtual ScreenOrientationDispatcherHost* GetScreenOrientationDispatcherHost() = 0; + // Gets/Sets the delegate. virtual WebContentsDelegate* GetDelegate() = 0; virtual void SetDelegate(WebContentsDelegate* delegate) = 0; @@ -315,8 +319,8 @@ class WebContents : public PageNavigator, virtual bool IsBeingDestroyed() const = 0; // Convenience method for notifying the delegate of a navigation state - // change. See InvalidateType enum. - virtual void NotifyNavigationStateChanged(unsigned changed_flags) = 0; + // change. + virtual void NotifyNavigationStateChanged(InvalidateTypes changed_flags) = 0; // Get the last time that the WebContents was made active (either when it was // created or shown with WasShown()). diff --git a/content/public/browser/web_contents_delegate.h b/content/public/browser/web_contents_delegate.h index 96133e23b1cfb..b9aa02d8aba23 100644 --- a/content/public/browser/web_contents_delegate.h +++ b/content/public/browser/web_contents_delegate.h @@ -12,6 +12,7 @@ #include "base/callback.h" #include "base/strings/string16.h" #include "content/common/content_export.h" +#include "content/public/browser/invalidate_type.h" #include "content/public/browser/navigation_type.h" #include "content/public/common/media_stream_request.h" #include "content/public/common/page_transition_types.h" @@ -84,10 +85,9 @@ class CONTENT_EXPORT WebContentsDelegate { // Called to inform the delegate that the WebContents's navigation state // changed. The |changed_flags| indicates the parts of the navigation state - // that have been updated, and is any combination of the - // |WebContents::InvalidateTypes| bits. + // that have been updated. virtual void NavigationStateChanged(const WebContents* source, - unsigned changed_flags) {} + InvalidateTypes changed_flags) {} // Called to inform the delegate that the WebContent's visible SSL state (as // defined by SSLStatus) changed. diff --git a/content/public/child/request_peer.h b/content/public/child/request_peer.h index 93143c5b4a8fc..ebce5f88989b8 100644 --- a/content/public/child/request_peer.h +++ b/content/public/child/request_peer.h @@ -16,6 +16,10 @@ namespace base { class TimeTicks; } +namespace net { +struct RedirectInfo; +} + namespace content { struct ResourceResponseInfo; @@ -34,12 +38,10 @@ class CONTENT_EXPORT RequestPeer { virtual void OnUploadProgress(uint64 position, uint64 size) = 0; // Called when a redirect occurs. The implementation may return false to - // suppress the redirect. The given ResponseInfo provides complete - // information about the redirect, and new_url is the URL that will be loaded - // if this method returns true. new_first_party_for_cookies is the new - // first-party URL for cookies should that have changed. - virtual bool OnReceivedRedirect(const GURL& new_url, - const GURL& new_first_party_for_cookies, + // suppress the redirect. The ResourceResponseInfo provides information about + // the redirect response and the RedirectInfo includes information about the + // request to be made if the method returns true. + virtual bool OnReceivedRedirect(const net::RedirectInfo& redirect_info, const ResourceResponseInfo& info) = 0; // Called when response headers are available (after all redirects have diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h index 86731820305a8..c7f6645e6ccfd 100644 --- a/content/public/common/common_param_traits_macros.h +++ b/content/public/common/common_param_traits_macros.h @@ -42,6 +42,8 @@ IPC_ENUM_TRAITS_MAX_VALUE(content::EditingBehavior, IPC_ENUM_TRAITS_MAX_VALUE(WindowOpenDisposition, WINDOW_OPEN_DISPOSITION_LAST) IPC_ENUM_TRAITS_MAX_VALUE(net::RequestPriority, net::MAXIMUM_PRIORITY) +IPC_ENUM_TRAITS_MAX_VALUE(content::V8CacheOptions, + content::V8_CACHE_OPTIONS_LAST) IPC_STRUCT_TRAITS_BEGIN(blink::WebPoint) IPC_STRUCT_TRAITS_MEMBER(x) @@ -107,7 +109,6 @@ IPC_STRUCT_TRAITS_BEGIN(content::WebPreferences) IPC_STRUCT_TRAITS_MEMBER(plugins_enabled) IPC_STRUCT_TRAITS_MEMBER(dom_paste_enabled) IPC_STRUCT_TRAITS_MEMBER(inspector_settings) - IPC_STRUCT_TRAITS_MEMBER(site_specific_quirks_enabled) IPC_STRUCT_TRAITS_MEMBER(shrinks_standalone_images_to_fit) IPC_STRUCT_TRAITS_MEMBER(uses_universal_detector) IPC_STRUCT_TRAITS_MEMBER(text_areas_are_resizable) @@ -176,12 +177,14 @@ IPC_STRUCT_TRAITS_BEGIN(content::WebPreferences) IPC_STRUCT_TRAITS_MEMBER(cookie_enabled) IPC_STRUCT_TRAITS_MEMBER(navigate_on_drag_drop) IPC_STRUCT_TRAITS_MEMBER(spatial_navigation_enabled) + IPC_STRUCT_TRAITS_MEMBER(v8_cache_options) IPC_STRUCT_TRAITS_MEMBER(pepper_accelerated_video_decode_enabled) #if defined(OS_ANDROID) IPC_STRUCT_TRAITS_MEMBER(text_autosizing_enabled) IPC_STRUCT_TRAITS_MEMBER(font_scale_factor) IPC_STRUCT_TRAITS_MEMBER(device_scale_adjustment) IPC_STRUCT_TRAITS_MEMBER(force_enable_zoom) + IPC_STRUCT_TRAITS_MEMBER(fullscreen_supported) IPC_STRUCT_TRAITS_MEMBER(disallow_fullscreen_for_non_media_elements) IPC_STRUCT_TRAITS_MEMBER(double_tap_to_zoom_enabled) IPC_STRUCT_TRAITS_MEMBER(user_gesture_required_for_media_playback) @@ -190,6 +193,7 @@ IPC_STRUCT_TRAITS_BEGIN(content::WebPreferences) IPC_STRUCT_TRAITS_MEMBER(use_legacy_background_size_shorthand_behavior) IPC_STRUCT_TRAITS_MEMBER(wide_viewport_quirk) IPC_STRUCT_TRAITS_MEMBER(use_wide_viewport) + IPC_STRUCT_TRAITS_MEMBER(force_zero_layout_height) IPC_STRUCT_TRAITS_MEMBER(viewport_meta_layout_size_quirk) IPC_STRUCT_TRAITS_MEMBER(viewport_meta_merge_content_quirk) IPC_STRUCT_TRAITS_MEMBER(viewport_meta_non_user_scalable_quirk) diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc index 1bc2a24ca3067..d4bfd4ee4b437 100644 --- a/content/public/common/content_switches.cc +++ b/content/public/common/content_switches.cc @@ -246,9 +246,6 @@ const char kDisableSetuidSandbox[] = "disable-setuid-sandbox"; // Disable shared workers. const char kDisableSharedWorkers[] = "disable-shared-workers"; -// Disables site-specific tailoring to compatibility issues in WebKit. -const char kDisableSiteSpecificQuirks[] = "disable-site-specific-quirks"; - // Disable smooth scrolling for testing. const char kDisableSmoothScrolling[] = "disable-smooth-scrolling"; @@ -418,8 +415,8 @@ const char kEnablePinch[] = "enable-pinch"; // also applys to workers. const char kEnablePreciseMemoryInfo[] = "enable-precise-memory-info"; -// Enable caching of pre-parsed JS script data. See http://crbug.com/32407. -const char kEnablePreparsedJsCaching[] = "enable-preparsed-js-caching"; +// Set options to cache V8 data. (off, preparse data, or code) +const char kV8CacheOptions[] = "v8-cache-options"; // Enables the CSS multicol implementation that uses the regions implementation. const char kEnableRegionBasedColumns[] = @@ -851,9 +848,6 @@ const char kValidateInputEventStream[] = "validate-input-event-stream"; // kWaitForDebugger flag passed on or not. const char kWaitForDebuggerChildren[] = "wait-for-debugger-children"; -// Causes the process to run as a worker subprocess. -const char kWorkerProcess[] = "worker"; - // The prefix used when starting the zygote process. (i.e. 'gdb --args') const char kZygoteCmdPrefix[] = "zygote-cmd-prefix"; @@ -877,6 +871,9 @@ const char kDisableWebRtcHWEncoding[] = "disable-webrtc-hw-encoding"; // Enables VP8 HW encode acceleration for WebRTC. const char kEnableWebRtcHWVp8Encoding[] = "enable-webrtc-hw-vp8-encoding"; + +// Enables H264 HW encode acceleration for WebRTC. +const char kEnableWebRtcHWH264Encoding[] = "enable-webrtc-hw-h264-encoding"; #endif #if defined(OS_ANDROID) @@ -915,8 +912,8 @@ const char kDisableWebAudio[] = "disable-webaudio"; const char kDisablePanelFitting[] = "disable-panel-fitting"; // Enables VA-API accelerated video encode. -const char kEnableVaapiAcceleratedVideoEncode[] = - "enable-vaapi-accelerated-video-encode"; +const char kDisableVaapiAcceleratedVideoEncode[] = + "disable-vaapi-accelerated-video-encode"; #endif #if defined(OS_LINUX) && !defined(OS_CHROMEOS) diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h index 47b5fdc8d65df..c90fa96446244 100644 --- a/content/public/common/content_switches.h +++ b/content/public/common/content_switches.h @@ -79,7 +79,6 @@ CONTENT_EXPORT extern const char kDisableSeccompFilterSandbox[]; extern const char kDisableSessionStorage[]; CONTENT_EXPORT extern const char kDisableSetuidSandbox[]; CONTENT_EXPORT extern const char kDisableSharedWorkers[]; -extern const char kDisableSiteSpecificQuirks[]; CONTENT_EXPORT extern const char kDisableSmoothScrolling[]; CONTENT_EXPORT extern const char kDisableSoftwareRasterizer[]; CONTENT_EXPORT extern const char kDisableThreadedCompositing[]; @@ -122,7 +121,6 @@ CONTENT_EXPORT extern const char kDisableOverlayFullscreenVideoSubtitle[]; CONTENT_EXPORT extern const char kEnableOverscrollNotifications[]; CONTENT_EXPORT extern const char kEnablePinch[]; CONTENT_EXPORT extern const char kEnablePreciseMemoryInfo[]; -extern const char kEnablePreparsedJsCaching[]; CONTENT_EXPORT extern const char kEnableRegionBasedColumns[]; CONTENT_EXPORT extern const char kEnableRendererMojoChannel[]; CONTENT_EXPORT extern const char kEnableSandboxLogging[]; @@ -233,9 +231,9 @@ CONTENT_EXPORT extern const char kUtilityProcess[]; extern const char kUtilityProcessAllowedDir[]; CONTENT_EXPORT extern const char kUtilityProcessEnableMDns[]; CONTENT_EXPORT extern const char kUtilityProcessRunningElevated[]; +extern const char kV8CacheOptions[]; CONTENT_EXPORT extern const char kValidateInputEventStream[]; CONTENT_EXPORT extern const char kWaitForDebuggerChildren[]; -CONTENT_EXPORT extern const char kWorkerProcess[]; CONTENT_EXPORT extern const char kZygoteCmdPrefix[]; CONTENT_EXPORT extern const char kZygoteProcess[]; @@ -245,6 +243,7 @@ CONTENT_EXPORT extern const char kDisableWebRtcHWDecoding[]; CONTENT_EXPORT extern const char kDisableWebRtcEncryption[]; CONTENT_EXPORT extern const char kDisableWebRtcHWEncoding[]; CONTENT_EXPORT extern const char kEnableWebRtcHWVp8Encoding[]; +CONTENT_EXPORT extern const char kEnableWebRtcHWH264Encoding[]; #endif #if defined(OS_ANDROID) @@ -263,7 +262,7 @@ CONTENT_EXPORT extern const char kDisableWebAudio[]; #if defined(OS_CHROMEOS) CONTENT_EXPORT extern const char kDisablePanelFitting[]; -CONTENT_EXPORT extern const char kEnableVaapiAcceleratedVideoEncode[]; +CONTENT_EXPORT extern const char kDisableVaapiAcceleratedVideoEncode[]; #endif #if defined(OS_LINUX) && !defined(OS_CHROMEOS) diff --git a/content/public/common/media_stream_request.h b/content/public/common/media_stream_request.h index 6d882ead1ed5d..fc72ee9242373 100644 --- a/content/public/common/media_stream_request.h +++ b/content/public/common/media_stream_request.h @@ -65,17 +65,20 @@ enum VideoFacingMode { NUM_MEDIA_VIDEO_FACING_MODE }; +// Elements in this enum should not be deleted or rearranged; the only +// permitted operation is to add new elements before NUM_MEDIA_REQUEST_RESULTS. enum MediaStreamRequestResult { MEDIA_DEVICE_OK = 0, - MEDIA_DEVICE_PERMISSION_DENIED, - MEDIA_DEVICE_PERMISSION_DISMISSED, - MEDIA_DEVICE_INVALID_STATE, - MEDIA_DEVICE_NO_HARDWARE, - MEDIA_DEVICE_INVALID_SECURITY_ORIGIN, - MEDIA_DEVICE_TAB_CAPTURE_FAILURE, - MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE, - MEDIA_DEVICE_CAPTURE_FAILURE, - MEDIA_DEVICE_TRACK_START_FAILURE, + MEDIA_DEVICE_PERMISSION_DENIED = 1, + MEDIA_DEVICE_PERMISSION_DISMISSED = 2, + MEDIA_DEVICE_INVALID_STATE = 3, + MEDIA_DEVICE_NO_HARDWARE = 4, + MEDIA_DEVICE_INVALID_SECURITY_ORIGIN = 5, + MEDIA_DEVICE_TAB_CAPTURE_FAILURE = 6, + MEDIA_DEVICE_SCREEN_CAPTURE_FAILURE = 7, + MEDIA_DEVICE_CAPTURE_FAILURE = 8, + MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED = 9, + MEDIA_DEVICE_TRACK_START_FAILURE = 10, NUM_MEDIA_REQUEST_RESULTS }; diff --git a/content/public/common/sandbox_type_mac.h b/content/public/common/sandbox_type_mac.h index 4c7faaa756c84..6f8a6f0e11f78 100644 --- a/content/public/common/sandbox_type_mac.h +++ b/content/public/common/sandbox_type_mac.h @@ -18,12 +18,6 @@ enum SandboxType { SANDBOX_TYPE_RENDERER = SANDBOX_TYPE_FIRST_TYPE, - // The worker process uses the most restrictive sandbox which has almost - // *everything* locked down. Only a couple of /System/Library/ paths and - // some other very basic operations (e.g., reading metadata to allow - // following symlinks) are permitted. - SANDBOX_TYPE_WORKER, - // Utility process is as restrictive as the worker process except full // access is allowed to one configurable directory. SANDBOX_TYPE_UTILITY, diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc index 6a1858ec8373f..374c71f79a3c7 100644 --- a/content/public/common/web_preferences.cc +++ b/content/public/common/web_preferences.cc @@ -31,6 +31,15 @@ COMPILE_ASSERT_MATCHING_ENUMS(EDITING_BEHAVIOR_UNIX, COMPILE_ASSERT_MATCHING_ENUMS(EDITING_BEHAVIOR_ANDROID, WebSettings::EditingBehaviorAndroid); +COMPILE_ASSERT_MATCHING_ENUMS(V8_CACHE_OPTIONS_OFF, + WebSettings::V8CacheOptionsOff); +COMPILE_ASSERT_MATCHING_ENUMS(V8_CACHE_OPTIONS_PARSE, + WebSettings::V8CacheOptionsParse); +COMPILE_ASSERT_MATCHING_ENUMS(V8_CACHE_OPTIONS_CODE, + WebSettings::V8CacheOptionsCode); +COMPILE_ASSERT_MATCHING_ENUMS(V8_CACHE_OPTIONS_LAST, + WebSettings::V8CacheOptionsCode); + WebPreferences::WebPreferences() : default_font_size(16), default_fixed_font_size(13), @@ -44,7 +53,6 @@ WebPreferences::WebPreferences() images_enabled(true), plugins_enabled(true), dom_paste_enabled(false), // enables execCommand("paste") - site_specific_quirks_enabled(false), shrinks_standalone_images_to_fit(true), uses_universal_detector(false), // Disabled: page cycler regression text_areas_are_resizable(true), @@ -128,6 +136,7 @@ WebPreferences::WebPreferences() pinch_overlay_scrollbar_thickness(0), use_solid_color_scrollbars(false), navigate_on_drag_drop(true), + v8_cache_options(V8_CACHE_OPTIONS_OFF), cookie_enabled(true), pepper_accelerated_video_decode_enabled(false) #if defined(OS_ANDROID) @@ -137,12 +146,14 @@ WebPreferences::WebPreferences() device_scale_adjustment(1.0f), force_enable_zoom(false), disallow_fullscreen_for_non_media_elements(false), + fullscreen_supported(true), double_tap_to_zoom_enabled(true), user_gesture_required_for_media_playback(true), support_deprecated_target_density_dpi(false), use_legacy_background_size_shorthand_behavior(false), wide_viewport_quirk(false), use_wide_viewport(true), + force_zero_layout_height(false), viewport_meta_layout_size_quirk(false), viewport_meta_merge_content_quirk(false), viewport_meta_non_user_scalable_quirk(false), diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h index 4bc2de17313b6..d03758addbe04 100644 --- a/content/public/common/web_preferences.h +++ b/content/public/common/web_preferences.h @@ -35,6 +35,13 @@ enum EditingBehavior { EDITING_BEHAVIOR_LAST = EDITING_BEHAVIOR_ANDROID }; +enum V8CacheOptions { + V8_CACHE_OPTIONS_OFF, + V8_CACHE_OPTIONS_PARSE, + V8_CACHE_OPTIONS_CODE, + V8_CACHE_OPTIONS_LAST = V8_CACHE_OPTIONS_CODE +}; + // The ISO 15924 script code for undetermined script aka Common. It's the // default used on WebKit's side to get/set a font setting when no script is // specified. @@ -66,7 +73,6 @@ struct CONTENT_EXPORT WebPreferences { bool plugins_enabled; bool dom_paste_enabled; WebInspectorPreferences inspector_settings; - bool site_specific_quirks_enabled; bool shrinks_standalone_images_to_fit; bool uses_universal_detector; bool text_areas_are_resizable; @@ -140,6 +146,7 @@ struct CONTENT_EXPORT WebPreferences { int pinch_overlay_scrollbar_thickness; bool use_solid_color_scrollbars; bool navigate_on_drag_drop; + V8CacheOptions v8_cache_options; // This flags corresponds to a Page's Settings' setCookieEnabled state. It // only controls whether or not the "document.cookie" field is properly @@ -158,6 +165,7 @@ struct CONTENT_EXPORT WebPreferences { float device_scale_adjustment; bool force_enable_zoom; bool disallow_fullscreen_for_non_media_elements; + bool fullscreen_supported; bool double_tap_to_zoom_enabled; bool user_gesture_required_for_media_playback; GURL default_video_poster_url; @@ -165,6 +173,7 @@ struct CONTENT_EXPORT WebPreferences { bool use_legacy_background_size_shorthand_behavior; bool wide_viewport_quirk; bool use_wide_viewport; + bool force_zero_layout_height; bool viewport_meta_layout_size_quirk; bool viewport_meta_merge_content_quirk; bool viewport_meta_non_user_scalable_quirk; diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/NestedSystemMessageHandler.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/NestedSystemMessageHandler.java index ae9f5a98d8019..c6f4649063e78 100644 --- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/NestedSystemMessageHandler.java +++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/NestedSystemMessageHandler.java @@ -28,7 +28,7 @@ class NestedSystemMessageHandler { // See org.chromium.base.SystemMessageHandler for more message ids. // The id here should not conflict with the ones in SystemMessageHandler. private static final int QUIT_MESSAGE = 10; - private static final Handler mHandler = new Handler(); + private static final Handler sHandler = new Handler(); private NestedSystemMessageHandler() { } @@ -45,7 +45,7 @@ private boolean runNestedLoopTillIdle() { queue.addIdleHandler(new MessageQueue.IdleHandler() { @Override public boolean queueIdle() { - mHandler.sendMessage(mHandler.obtainMessage(QUIT_MESSAGE)); + sHandler.sendMessage(sHandler.obtainMessage(QUIT_MESSAGE)); return false; } }); diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/MockLocationProvider.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/MockLocationProvider.java index 5b0db392c7ec3..1efb313909794 100644 --- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/MockLocationProvider.java +++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/MockLocationProvider.java @@ -20,7 +20,7 @@ public class MockLocationProvider implements LocationProviderFactory.LocationPro private boolean mIsRunning; private Handler mHandler; private HandlerThread mHandlerThread; - private static final Object mLock = new Object(); + private final Object mLock = new Object(); private static final int UPDATE_LOCATION_MSG = 100; diff --git a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java index 1fb05e7a14faf..b1bc80ba9602f 100644 --- a/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java +++ b/content/public/test/android/javatests/src/org/chromium/content/browser/test/util/TestCallbackHelperContainer.java @@ -7,6 +7,7 @@ import org.chromium.base.ThreadUtils; import org.chromium.content.browser.ContentViewCore; +import org.chromium.content_public.browser.JavaScriptCallback; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -109,8 +110,8 @@ public static class OnEvaluateJavaScriptResultHelper extends CallbackHelper { * @param code A JavaScript code to be evaluated. */ public void evaluateJavaScript(ContentViewCore contentViewCore, String code) { - ContentViewCore.JavaScriptCallback callback = - new ContentViewCore.JavaScriptCallback() { + JavaScriptCallback callback = + new JavaScriptCallback() { @Override public void handleJavaScriptResult(String jsonResult) { notifyCalled(jsonResult); diff --git a/content/public/test/browser_test_base.cc b/content/public/test/browser_test_base.cc index 34932427aa6b8..e31d9372968ef 100644 --- a/content/public/test/browser_test_base.cc +++ b/content/public/test/browser_test_base.cc @@ -42,6 +42,7 @@ #if defined(USE_AURA) #include "content/browser/compositor/image_transport_factory.h" +#include "ui/aura/test/event_generator_delegate_aura.h" #if defined(USE_X11) #include "ui/aura/window_tree_host_x11.h" #endif @@ -132,9 +133,12 @@ BrowserTestBase::BrowserTestBase() base::mac::SetOverrideAmIBundled(true); #endif -#if defined(USE_AURA) && defined(USE_X11) +#if defined(USE_AURA) +#if defined(USE_X11) aura::test::SetUseOverrideRedirectWindowByDefault(true); #endif + aura::test::InitializeAuraEventGeneratorDelegate(); +#endif #if defined(OS_POSIX) handle_sigterm_ = true; diff --git a/content/public/test/browser_test_utils.cc b/content/public/test/browser_test_utils.cc index 182f135650149..079312c193298 100644 --- a/content/public/test/browser_test_utils.cc +++ b/content/public/test/browser_test_utils.cc @@ -294,21 +294,13 @@ void SimulateMouseEvent(WebContents* web_contents, } void SimulateTapAt(WebContents* web_contents, const gfx::Point& point) { - const double kTapDurationSeconds = - 0.5 * (ui::GestureConfiguration:: - min_touch_down_duration_in_seconds_for_click() + - ui::GestureConfiguration:: - max_touch_down_duration_in_seconds_for_click()); - SyntheticWebTouchEvent touch; - // Set the timestamp to the base::TimeDelta representing the current time. - touch.SetTimestamp(base::TimeTicks::Now() - base::TimeTicks()); - touch.PressPoint(point.x(), point.y()); + blink::WebGestureEvent tap; + tap.type = blink::WebGestureEvent::GestureTap; + tap.x = point.x(); + tap.y = point.y(); RenderWidgetHostImpl* widget_host = RenderWidgetHostImpl::From(web_contents->GetRenderViewHost()); - widget_host->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); - touch.timeStampSeconds += kTapDurationSeconds; - touch.ReleasePoint(0); - widget_host->ForwardTouchEventWithLatencyInfo(touch, ui::LatencyInfo()); + widget_host->ForwardGestureEvent(tap); } void SimulateKeyPress(WebContents* web_contents, diff --git a/content/public/test/render_view_test.cc b/content/public/test/render_view_test.cc index f4e44ca6d064f..725bd0dcda971 100644 --- a/content/public/test/render_view_test.cc +++ b/content/public/test/render_view_test.cc @@ -176,8 +176,9 @@ void RenderViewTest::SetUp() { // ResourceBundle isn't initialized (since we have to use a diferent test // suite implementation than for content_unittests). For browser_tests, this // is already initialized. - if (!ResourceBundle::HasSharedInstance()) - ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL); + if (!ui::ResourceBundle::HasSharedInstance()) + ui::ResourceBundle::InitSharedInstanceWithLocale( + "en-US", NULL, ui::ResourceBundle::LOAD_COMMON_RESOURCES); mock_process_.reset(new MockRenderProcess); diff --git a/content/public/test/test_launcher.cc b/content/public/test/test_launcher.cc index 1914fd17fd690..222248785abf4 100644 --- a/content/public/test/test_launcher.cc +++ b/content/public/test/test_launcher.cc @@ -345,7 +345,8 @@ void WrapperTestLauncherDelegate::DoRunTest(base::TestLauncher* test_launcher, new_cmd_line, browser_wrapper ? browser_wrapper : std::string(), TestTimeouts::action_max_timeout(), - true, + base::TestLauncher::USE_JOB_OBJECTS | + base::TestLauncher::ALLOW_BREAKAWAY_FROM_JOB, base::Bind(&WrapperTestLauncherDelegate::GTestCallback, base::Unretained(this), test_launcher, diff --git a/content/public/test/test_renderer_host.cc b/content/public/test/test_renderer_host.cc index 70d7e6694a22c..e96a13cb81390 100644 --- a/content/public/test/test_renderer_host.cc +++ b/content/public/test/test_renderer_host.cc @@ -13,6 +13,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/test/mock_render_process_host.h" #include "content/public/test/test_browser_context.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_render_frame_host_factory.h" #include "content/test/test_render_view_host.h" #include "content/test/test_render_view_host_factory.h" @@ -99,12 +100,13 @@ WebContents* RenderViewHostTestHarness::web_contents() { } RenderViewHost* RenderViewHostTestHarness::rvh() { - return web_contents()->GetRenderViewHost(); + RenderViewHost* result = web_contents()->GetRenderViewHost(); + CHECK_EQ(result, web_contents()->GetMainFrame()->GetRenderViewHost()); + return result; } RenderViewHost* RenderViewHostTestHarness::pending_rvh() { - return static_cast(web_contents())-> - GetRenderManagerForTesting()->pending_render_view_host(); + return pending_main_rfh() ? pending_main_rfh()->GetRenderViewHost() : NULL; } RenderViewHost* RenderViewHostTestHarness::active_rvh() { @@ -112,9 +114,11 @@ RenderViewHost* RenderViewHostTestHarness::active_rvh() { } RenderFrameHost* RenderViewHostTestHarness::main_rfh() { - WebContentsImpl* web_contents = static_cast( - this->web_contents()); - return web_contents->GetFrameTree()->GetMainFrame(); + return web_contents()->GetMainFrame(); +} + +RenderFrameHost* RenderViewHostTestHarness::pending_main_rfh() { + return WebContentsTester::For(web_contents())->GetPendingMainFrame(); } BrowserContext* RenderViewHostTestHarness::browser_context() { diff --git a/content/public/test/test_renderer_host.h b/content/public/test/test_renderer_host.h index a514d3085e12e..b1b4aec2e7419 100644 --- a/content/public/test/test_renderer_host.h +++ b/content/public/test/test_renderer_host.h @@ -155,11 +155,31 @@ class RenderViewHostTestHarness : public testing::Test { virtual ~RenderViewHostTestHarness(); NavigationController& controller(); + + // The contents under test. WebContents* web_contents(); + + // RVH/RFH getters are shorthand for oft-used bits of web_contents(). + + // rvh() is equivalent to either of: + // web_contents()->GetMainFrame()->GetRenderViewHost() + // web_contents()->GetRenderViewHost() RenderViewHost* rvh(); + + // pending_rvh() is equivalent to: + // WebContentsTester::For(web_contents())->GetPendingRenderViewHost() RenderViewHost* pending_rvh(); + + // active_rvh() is equivalent to pending_rvh() ? pending_rvh() : rvh() RenderViewHost* active_rvh(); + + // main_rfh() is equivalent to web_contents()->GetMainFrame() RenderFrameHost* main_rfh(); + + // pending_main_rfh() is equivalent to: + // WebContentsTester::For(web_contents())->GetPendingMainFrame() + RenderFrameHost* pending_main_rfh(); + BrowserContext* browser_context(); MockRenderProcessHost* process(); @@ -212,9 +232,6 @@ class RenderViewHostTestHarness : public testing::Test { private: scoped_ptr browser_context_; - // It is important not to use this directly in the implementation as - // web_contents() and SetContents() are virtual and may be - // overridden by subclasses. scoped_ptr contents_; #if defined(OS_WIN) scoped_ptr ole_initializer_; diff --git a/content/public/test/web_contents_tester.h b/content/public/test/web_contents_tester.h index 73ccb7e40270a..25a80f289f680 100644 --- a/content/public/test/web_contents_tester.h +++ b/content/public/test/web_contents_tester.h @@ -12,6 +12,7 @@ class GURL; namespace content { class BrowserContext; +class RenderFrameHost; class RenderViewHost; class SiteInstance; class WebContents; @@ -59,7 +60,9 @@ class WebContentsTester { // sending a navigate notification for the NavigationController pending entry. virtual void CommitPendingNavigation() = 0; - virtual RenderViewHost* GetPendingRenderViewHost() const = 0; + // Gets the pending RenderFrameHost, if any, for the main frame. For the + // current RenderFrameHost of the main frame, use WebContents::GetMainFrame(). + virtual RenderFrameHost* GetPendingMainFrame() const = 0; // Creates a pending navigation to the given URL with the default parameters // and then commits the load with a page ID one larger than any seen. This @@ -74,20 +77,20 @@ class WebContentsTester { // Does nothing if no cross-navigation is pending. virtual void ProceedWithCrossSiteNavigation() = 0; - virtual void TestDidNavigate(RenderViewHost* render_view_host, + virtual void TestDidNavigate(RenderFrameHost* render_frame_host, int page_id, const GURL& url, PageTransition transition) = 0; virtual void TestDidNavigateWithReferrer( - RenderViewHost* render_view_host, + RenderFrameHost* render_frame_host, int page_id, const GURL& url, const Referrer& referrer, PageTransition transition) = 0; - // Promote GetWebkitPrefs to public. - virtual WebPreferences TestGetWebkitPrefs() = 0; + // Promote ComputeWebkitPrefs to public. + virtual WebPreferences TestComputeWebkitPrefs() = 0; }; } // namespace content diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn index 24e0e5420d391..f2fef67d72774 100644 --- a/content/renderer/BUILD.gn +++ b/content/renderer/BUILD.gn @@ -111,7 +111,6 @@ source_set("renderer") { "media/media_stream_audio_renderer.cc", "media/media_stream_audio_renderer.h", "media/media_stream_center.h", - "media/media_stream_client.h", "media/media_stream_constraints_util.cc", "media/media_stream_constraints_util.h", "media/media_stream_dispatcher.h", @@ -149,6 +148,7 @@ source_set("renderer") { "//ppapi:ppapi_host", "//ppapi:ppapi_proxy", "//ppapi:ppapi_shared", + "//third_party/libyuv", ] } else { # These files are in the WebRTC list, but also require plugins. @@ -165,4 +165,8 @@ source_set("renderer") { content_renderer_gypi_values.private_renderer_plugin_webrtc_sources, ".", "//content") } + + if (printing_mode == 1) { + deps += [ "//printing" ] + } } diff --git a/content/renderer/android/synchronous_compositor_factory.h b/content/renderer/android/synchronous_compositor_factory.h index 84122cd8f53b7..c21d82acf09e2 100644 --- a/content/renderer/android/synchronous_compositor_factory.h +++ b/content/renderer/android/synchronous_compositor_factory.h @@ -52,7 +52,9 @@ class SynchronousCompositorFactory { virtual InputHandlerManagerClient* GetInputHandlerManagerClient() = 0; virtual scoped_refptr - GetSharedOffscreenContextProviderForMainThread() = 0; + CreateOffscreenContextProvider( + const blink::WebGraphicsContext3D::Attributes& attributes, + const std::string& debug_name) = 0; virtual scoped_refptr CreateStreamTextureFactory( int frame_id) = 0; virtual blink::WebGraphicsContext3D* CreateOffscreenGraphicsContext3D( diff --git a/content/renderer/compositor_bindings/web_animation_impl.cc b/content/renderer/compositor_bindings/web_animation_impl.cc index 67eedd1401be4..9e9092b6cb27c 100644 --- a/content/renderer/compositor_bindings/web_animation_impl.cc +++ b/content/renderer/compositor_bindings/web_animation_impl.cc @@ -79,6 +79,15 @@ WebCompositorAnimationImpl::targetProperty() const { animation_->target_property()); } +#if WEB_ANIMATION_SUPPORTS_FRACTIONAL_ITERATIONS +double WebCompositorAnimationImpl::iterations() const { + return animation_->iterations(); +} + +void WebCompositorAnimationImpl::setIterations(double n) { + animation_->set_iterations(n); +} +#else int WebCompositorAnimationImpl::iterations() const { return animation_->iterations(); } @@ -86,6 +95,7 @@ int WebCompositorAnimationImpl::iterations() const { void WebCompositorAnimationImpl::setIterations(int n) { animation_->set_iterations(n); } +#endif double WebCompositorAnimationImpl::startTime() const { return (animation_->start_time() - base::TimeTicks()).InSecondsF(); diff --git a/content/renderer/compositor_bindings/web_animation_impl.h b/content/renderer/compositor_bindings/web_animation_impl.h index 6586ceaad168b..460b6f531c23e 100644 --- a/content/renderer/compositor_bindings/web_animation_impl.h +++ b/content/renderer/compositor_bindings/web_animation_impl.h @@ -28,8 +28,13 @@ class WebCompositorAnimationImpl : public blink::WebCompositorAnimation { // blink::WebCompositorAnimation implementation virtual int id(); virtual TargetProperty targetProperty() const; +#if WEB_ANIMATION_SUPPORTS_FRACTIONAL_ITERATIONS + virtual double iterations() const; + virtual void setIterations(double iterations); +#else virtual int iterations() const; virtual void setIterations(int iterations); +#endif virtual double startTime() const; virtual void setStartTime(double monotonic_time); virtual double timeOffset() const; diff --git a/content/renderer/context_menu_params_builder.cc b/content/renderer/context_menu_params_builder.cc index 0d53dac225eef..63dcd4d1e7b6b 100644 --- a/content/renderer/context_menu_params_builder.cc +++ b/content/renderer/context_menu_params_builder.cc @@ -58,7 +58,7 @@ ContextMenuParams ContextMenuParamsBuilder::Build( if (!params.link_url.is_empty()) { blink::WebNode selectedNode = DomUtils::ExtractParentAnchorNode(data.node); blink::WebElement selectedElement = selectedNode.to(); - if (selectedNode.isLink() && !selectedElement.isNull()) { + if (!selectedElement.isNull() && selectedNode.isLink()) { params.link_text = selectedElement.innerText(); } else { LOG(ERROR) << "Creating a ContextMenuParams for a node that has a link" diff --git a/content/renderer/external_popup_menu_browsertest.cc b/content/renderer/external_popup_menu_browsertest.cc index a0a6a90c8f373..c29306c44edae 100644 --- a/content/renderer/external_popup_menu_browsertest.cc +++ b/content/renderer/external_popup_menu_browsertest.cc @@ -145,4 +145,44 @@ TEST_F(ExternalPopupMenuRemoveTest, RemoveOnChange) { EXPECT_FALSE(SimulateElementClick(kSelectID)); } +class ExternalPopupMenuDisplayNoneTest : public ExternalPopupMenuTest { + public: + ExternalPopupMenuDisplayNoneTest() {} + + virtual void SetUp() { + RenderViewTest::SetUp(); + // We need to set this explictly as RenderMain is not run. + blink::WebView::setUseExternalPopupMenus(true); + + std::string html = ""; + // Load the test page. + LoadHTML(html.c_str()); + + // Set a minimum size and give focus so simulated events work. + view()->webwidget()->resize(blink::WebSize(500, 500)); + view()->webwidget()->setFocus(true); + } + +}; + +TEST_F(ExternalPopupMenuDisplayNoneTest, SelectItem) { + // Click the text field once to show the popup. + EXPECT_TRUE(SimulateElementClick(kSelectID)); + + // Select index 1 item. This should select item with index 2, + // skipping the item with 'display: none' + view()->OnSelectPopupMenuItem(1); + + EXPECT_EQ(2,GetSelectedIndex()); +} + } // namespace content diff --git a/content/renderer/gpu/queue_message_swap_promise.cc b/content/renderer/gpu/queue_message_swap_promise.cc index f18427795a6c6..1cb5316e166c6 100644 --- a/content/renderer/gpu/queue_message_swap_promise.cc +++ b/content/renderer/gpu/queue_message_swap_promise.cc @@ -62,4 +62,8 @@ void QueueMessageSwapPromise::PromiseCompleted() { #endif } +int64 QueueMessageSwapPromise::TraceId() const { + return 0; +} + } // namespace content diff --git a/content/renderer/gpu/queue_message_swap_promise.h b/content/renderer/gpu/queue_message_swap_promise.h index 20a519bfe4d8a..d3eabf1d8097b 100644 --- a/content/renderer/gpu/queue_message_swap_promise.h +++ b/content/renderer/gpu/queue_message_swap_promise.h @@ -29,6 +29,8 @@ class QueueMessageSwapPromise : public cc::SwapPromise { virtual void DidNotSwap(DidNotSwapReason reason) OVERRIDE; + virtual int64 TraceId() const OVERRIDE; + private: void PromiseCompleted(); diff --git a/content/renderer/gpu/render_widget_compositor.cc b/content/renderer/gpu/render_widget_compositor.cc index 64e0982bc250d..09cb529275b27 100644 --- a/content/renderer/gpu/render_widget_compositor.cc +++ b/content/renderer/gpu/render_widget_compositor.cc @@ -388,7 +388,12 @@ scoped_ptr RenderWidgetCompositor::Create( #elif !defined(OS_MACOSX) if (ui::IsOverlayScrollbarEnabled()) { +#if defined(OS_TIZEN) + // Tizen fades out the scrollbar after contents interaction ends. + settings.scrollbar_animator = cc::LayerTreeSettings::LinearFade; +#else settings.scrollbar_animator = cc::LayerTreeSettings::Thinning; +#endif settings.solid_color_scrollbar_color = SkColorSetARGB(128, 128, 128, 128); } else if (cmd->HasSwitch(cc::switches::kEnablePinchVirtualViewport)) { // use_pinch_zoom_scrollbars is only true on desktop when non-overlay @@ -425,11 +430,11 @@ void RenderWidgetCompositor::SetSuppressScheduleComposite(bool suppress) { return; if (suppress) - TRACE_EVENT_ASYNC_BEGIN0("gpu", - "RenderWidgetCompositor::SetSuppressScheduleComposite", this); + TRACE_EVENT_ASYNC_BEGIN0( + "gpu", "RenderWidgetCompositor::SetSuppressScheduleComposite", this); else - TRACE_EVENT_ASYNC_END0("gpu", - "RenderWidgetCompositor::SetSuppressScheduleComposite", this); + TRACE_EVENT_ASYNC_END0( + "gpu", "RenderWidgetCompositor::SetSuppressScheduleComposite", this); suppress_schedule_composite_ = suppress; } diff --git a/content/renderer/input/input_event_filter.cc b/content/renderer/input/input_event_filter.cc index fa31658a74bca..5017cb4b5f074 100644 --- a/content/renderer/input/input_event_filter.cc +++ b/content/renderer/input/input_event_filter.cc @@ -164,12 +164,14 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) { bool is_keyboard_shortcut = params.c; DCHECK(event); + const bool send_ack = !WebInputEventTraits::IgnoresAckDisposition(*event); + // Intercept |DidOverscroll| notifications, bundling any triggered overscroll // response with the input event ack. scoped_ptr overscroll_params; base::AutoReset*> - auto_reset_current_overscroll_params(¤t_overscroll_params_, - &overscroll_params); + auto_reset_current_overscroll_params( + ¤t_overscroll_params_, send_ack ? &overscroll_params : NULL); InputEventAckState ack_state = handler_.Run(routing_id, event, &latency_info); @@ -188,7 +190,7 @@ void InputEventFilter::ForwardToHandler(const IPC::Message& message) { return; } - if (WebInputEventTraits::IgnoresAckDisposition(*event)) + if (!send_ack) return; InputHostMsg_HandleInputEvent_ACK_Params ack; diff --git a/content/renderer/input/input_handler_proxy.cc b/content/renderer/input/input_handler_proxy.cc index f37ad750f2f3d..6970d22003e9b 100644 --- a/content/renderer/input/input_handler_proxy.cc +++ b/content/renderer/input/input_handler_proxy.cc @@ -178,11 +178,10 @@ InputHandlerProxy::HandleInputEventWithLatencyInfo( SendScrollLatencyUma(event, *latency_info); - TRACE_EVENT_FLOW_STEP0( - "input", - "LatencyInfo.Flow", - TRACE_ID_DONT_MANGLE(latency_info->trace_id), - "HanldeInputEventImpl"); + TRACE_EVENT_FLOW_STEP0("input", + "LatencyInfo.Flow", + TRACE_ID_DONT_MANGLE(latency_info->trace_id), + "HandleInputEventImpl"); scoped_ptr latency_info_swap_promise_monitor = input_handler_->CreateLatencyInfoSwapPromiseMonitor(latency_info); @@ -337,7 +336,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( *static_cast(&event); return HandleGestureFling(gesture_event); } else if (event.type == WebInputEvent::GestureFlingCancel) { - if (CancelCurrentFling(true)) + if (CancelCurrentFling()) return DID_HANDLE; else if (!fling_may_be_active_on_main_thread_) return DROP_EVENT; @@ -358,7 +357,7 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleInputEvent( // Only call |CancelCurrentFling()| if a fling was active, as it will // otherwise disrupt an in-progress touch scroll. if (fling_curve_) - CancelCurrentFling(true); + CancelCurrentFling(); } else if (event.type == WebInputEvent::MouseMove) { const WebMouseEvent& mouse_event = *static_cast(&event); @@ -486,7 +485,7 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting( // Gestures from a different source should immediately interrupt the fling. if (gesture_event.sourceDevice != fling_parameters_.sourceDevice) { - FlingBoostCancelAndResumeScrollingIfNecessary(); + CancelCurrentFling(); return false; } @@ -501,13 +500,13 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting( fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchpad ? cc::InputHandler::NonBubblingGesture : cc::InputHandler::Gesture)) { - CancelCurrentFling(true); + CancelCurrentFling(); return false; } // TODO(jdduke): Use |gesture_event.data.scrollBegin.delta{X,Y}Hint| to // determine if the ScrollBegin should immediately cancel the fling. - FlingBoostExtend(gesture_event); + ExtendBoostedFlingTimeout(gesture_event); return true; case WebInputEvent::GestureScrollUpdate: { @@ -516,16 +515,19 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting( if (ShouldSuppressScrollForFlingBoosting(current_fling_velocity_, gesture_event, time_since_last_boost_event)) { - FlingBoostExtend(gesture_event); + ExtendBoostedFlingTimeout(gesture_event); return true; } - FlingBoostCancelAndResumeScrollingIfNecessary(); + CancelCurrentFling(); return false; } case WebInputEvent::GestureScrollEnd: - CancelCurrentFling(true); + // Clear the last fling boost event *prior* to fling cancellation, + // preventing insertion of a synthetic GestureScrollBegin. + last_fling_boost_event_ = WebGestureEvent(); + CancelCurrentFling(); return true; case WebInputEvent::GestureFlingStart: { @@ -577,36 +579,21 @@ bool InputHandlerProxy::FilterInputEventForFlingBoosting( default: // All other types of gestures (taps, presses, etc...) will complete the // deferred fling cancellation. - FlingBoostCancelAndResumeScrollingIfNecessary(); + CancelCurrentFling(); return false; } } -void InputHandlerProxy::FlingBoostExtend(const blink::WebGestureEvent& event) { - TRACE_EVENT_INSTANT0( - "input", "InputHandlerProxy::FlingBoostExtend", TRACE_EVENT_SCOPE_THREAD); +void InputHandlerProxy::ExtendBoostedFlingTimeout( + const blink::WebGestureEvent& event) { + TRACE_EVENT_INSTANT0("input", + "InputHandlerProxy::ExtendBoostedFlingTimeout", + TRACE_EVENT_SCOPE_THREAD); deferred_fling_cancel_time_seconds_ = event.timeStampSeconds + kFlingBoostTimeoutDelaySeconds; last_fling_boost_event_ = event; } -void InputHandlerProxy::FlingBoostCancelAndResumeScrollingIfNecessary() { - TRACE_EVENT_INSTANT0( - "input", "InputHandlerProxy::FlingBoostCancel", TRACE_EVENT_SCOPE_THREAD); - DCHECK(deferred_fling_cancel_time_seconds_); - - // Note: |last_fling_boost_event_| is cleared by |CancelCurrentFling()|. - WebGestureEvent last_fling_boost_event = last_fling_boost_event_; - - CancelCurrentFling(true); - - if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin || - last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) { - // Synthesize a GestureScrollBegin, as the original was suppressed. - HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event)); - } -} - void InputHandlerProxy::Animate(base::TimeTicks time) { if (!fling_curve_) return; @@ -615,7 +602,7 @@ void InputHandlerProxy::Animate(base::TimeTicks time) { if (deferred_fling_cancel_time_seconds_ && monotonic_time_sec > deferred_fling_cancel_time_seconds_) { - FlingBoostCancelAndResumeScrollingIfNecessary(); + CancelCurrentFling(); return; } @@ -646,7 +633,7 @@ void InputHandlerProxy::Animate(base::TimeTicks time) { TRACE_EVENT_INSTANT0("input", "InputHandlerProxy::animate::flingOver", TRACE_EVENT_SCOPE_THREAD); - CancelCurrentFling(true); + CancelCurrentFling(); } } @@ -656,6 +643,7 @@ void InputHandlerProxy::MainThreadHasStoppedFlinging() { } void InputHandlerProxy::DidOverscroll( + const gfx::PointF& causal_event_viewport_point, const gfx::Vector2dF& accumulated_overscroll, const gfx::Vector2dF& latest_overscroll_delta) { DCHECK(client_); @@ -672,6 +660,7 @@ void InputHandlerProxy::DidOverscroll( params.latest_overscroll_delta = latest_overscroll_delta; params.current_fling_velocity = ToClientScrollIncrement(current_fling_velocity_); + params.causal_event_viewport_point = causal_event_viewport_point; if (fling_curve_) { static const int kFlingOverscrollThreshold = 1; @@ -686,8 +675,15 @@ void InputHandlerProxy::DidOverscroll( client_->DidOverscroll(params); } -bool InputHandlerProxy::CancelCurrentFling( - bool send_fling_stopped_notification) { +bool InputHandlerProxy::CancelCurrentFling() { + if (CancelCurrentFlingWithoutNotifyingClient()) { + client_->DidStopFlinging(); + return true; + } + return false; +} + +bool InputHandlerProxy::CancelCurrentFlingWithoutNotifyingClient() { bool had_fling_animation = fling_curve_; if (had_fling_animation && fling_parameters_.sourceDevice == blink::WebGestureDeviceTouchscreen) { @@ -708,10 +704,19 @@ bool InputHandlerProxy::CancelCurrentFling( gesture_scroll_on_impl_thread_ = false; current_fling_velocity_ = gfx::Vector2dF(); fling_parameters_ = blink::WebActiveWheelFlingParameters(); - deferred_fling_cancel_time_seconds_ = 0; - last_fling_boost_event_ = WebGestureEvent(); - if (send_fling_stopped_notification && had_fling_animation) - client_->DidStopFlinging(); + + if (deferred_fling_cancel_time_seconds_) { + deferred_fling_cancel_time_seconds_ = 0; + + WebGestureEvent last_fling_boost_event = last_fling_boost_event_; + last_fling_boost_event_ = WebGestureEvent(); + if (last_fling_boost_event.type == WebInputEvent::GestureScrollBegin || + last_fling_boost_event.type == WebInputEvent::GestureScrollUpdate) { + // Synthesize a GestureScrollBegin, as the original was suppressed. + HandleInputEvent(ObtainGestureScrollBegin(last_fling_boost_event)); + } + } + return had_fling_animation; } @@ -747,7 +752,7 @@ bool InputHandlerProxy::TouchpadFlingScroll( // the subarea but then is flung "under" the pointer. client_->TransferActiveWheelFlingAnimation(fling_parameters_); fling_may_be_active_on_main_thread_ = true; - CancelCurrentFling(false); + CancelCurrentFlingWithoutNotifyingClient(); break; } diff --git a/content/renderer/input/input_handler_proxy.h b/content/renderer/input/input_handler_proxy.h index c1b666b62845f..c5f7897f879f2 100644 --- a/content/renderer/input/input_handler_proxy.h +++ b/content/renderer/input/input_handler_proxy.h @@ -45,9 +45,10 @@ class CONTENT_EXPORT InputHandlerProxy virtual void WillShutdown() OVERRIDE; virtual void Animate(base::TimeTicks time) OVERRIDE; virtual void MainThreadHasStoppedFlinging() OVERRIDE; - virtual void DidOverscroll(const gfx::Vector2dF& accumulated_overscroll, - const gfx::Vector2dF& latest_overscroll_delta) - OVERRIDE; + virtual void DidOverscroll( + const gfx::PointF& causal_event_viewport_point, + const gfx::Vector2dF& accumulated_overscroll, + const gfx::Vector2dF& latest_overscroll_delta) OVERRIDE; // blink::WebGestureCurveTarget implementation. virtual bool scrollBy(const blink::WebFloatSize& offset, @@ -66,16 +67,19 @@ class CONTENT_EXPORT InputHandlerProxy // Schedule a time in the future after which a boost-enabled fling will // terminate without further momentum from the user (see |Animate()|). - void FlingBoostExtend(const blink::WebGestureEvent& event); - - // Cancel the current fling and insert a GestureScrollBegin if necessary. - void FlingBoostCancelAndResumeScrollingIfNecessary(); + void ExtendBoostedFlingTimeout(const blink::WebGestureEvent& event); // Returns true if we scrolled by the increment. bool TouchpadFlingScroll(const blink::WebFloatSize& increment); + // Returns true if we actually had an active fling to cancel, also notifying + // the client that the fling has ended. Note that if a boosted fling is active + // and suppressing an active scroll sequence, a synthetic GestureScrollBegin + // will be injected to resume scrolling. + bool CancelCurrentFling(); + // Returns true if we actually had an active fling to cancel. - bool CancelCurrentFling(bool send_fling_stopped_notification); + bool CancelCurrentFlingWithoutNotifyingClient(); scoped_ptr fling_curve_; // Parameters for the active fling animation, stored in case we need to @@ -91,7 +95,7 @@ class CONTENT_EXPORT InputHandlerProxy // The last event that extended the lifetime of the boosted fling. If the // event was a scroll gesture, a GestureScrollBegin will be inserted if the - // fling terminates (via |FlingBoostCancelAndResumeScrollingIfNecessary()|). + // fling terminates (via |CancelCurrentFling()|). blink::WebGestureEvent last_fling_boost_event_; #ifndef NDEBUG diff --git a/content/renderer/input/input_handler_proxy_unittest.cc b/content/renderer/input/input_handler_proxy_unittest.cc index d932424e9a6a5..a2da06c0209d5 100644 --- a/content/renderer/input/input_handler_proxy_unittest.cc +++ b/content/renderer/input/input_handler_proxy_unittest.cc @@ -1208,17 +1208,20 @@ TEST_F(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) { // Simulate hitting the bottom content edge. gfx::Vector2dF accumulated_overscroll(0, 100); gfx::Vector2dF latest_overscroll_delta(0, 10); - EXPECT_CALL(mock_client_, - DidOverscroll(testing::AllOf( - testing::Field(&DidOverscrollParams::accumulated_overscroll, - testing::Eq(accumulated_overscroll)), - testing::Field(&DidOverscrollParams::latest_overscroll_delta, - testing::Eq(latest_overscroll_delta)), - testing::Field( - &DidOverscrollParams::current_fling_velocity, - testing::Property(&gfx::Vector2dF::y, testing::Lt(0)))))); - input_handler_->DidOverscroll(accumulated_overscroll, - latest_overscroll_delta); + gfx::PointF scroll_point(10, 0); + EXPECT_CALL( + mock_client_, + DidOverscroll(testing::AllOf( + testing::Field(&DidOverscrollParams::accumulated_overscroll, + testing::Eq(accumulated_overscroll)), + testing::Field(&DidOverscrollParams::latest_overscroll_delta, + testing::Eq(latest_overscroll_delta)), + testing::Field(&DidOverscrollParams::current_fling_velocity, + testing::Property(&gfx::Vector2dF::y, testing::Lt(0))), + testing::Field(&DidOverscrollParams::causal_event_viewport_point, + testing::Eq(scroll_point))))); + input_handler_->DidOverscroll( + scroll_point, accumulated_overscroll, latest_overscroll_delta); // The next call to animate will no longer scroll vertically. EXPECT_CALL(mock_input_handler_, SetNeedsAnimate()); @@ -1354,17 +1357,20 @@ TEST_F(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { // Simulate hitting the bottom content edge. gfx::Vector2dF accumulated_overscroll(0, 100); gfx::Vector2dF latest_overscroll_delta(0, 100); - EXPECT_CALL(mock_client_, - DidOverscroll(testing::AllOf( - testing::Field(&DidOverscrollParams::accumulated_overscroll, - testing::Eq(accumulated_overscroll)), - testing::Field(&DidOverscrollParams::latest_overscroll_delta, - testing::Eq(latest_overscroll_delta)), - testing::Field( - &DidOverscrollParams::current_fling_velocity, - testing::Property(&gfx::Vector2dF::y, testing::Lt(0)))))); - input_handler_->DidOverscroll(accumulated_overscroll, - latest_overscroll_delta); + gfx::PointF scroll_point(10, -50); + EXPECT_CALL( + mock_client_, + DidOverscroll(testing::AllOf( + testing::Field(&DidOverscrollParams::accumulated_overscroll, + testing::Eq(accumulated_overscroll)), + testing::Field(&DidOverscrollParams::latest_overscroll_delta, + testing::Eq(latest_overscroll_delta)), + testing::Field(&DidOverscrollParams::current_fling_velocity, + testing::Property(&gfx::Vector2dF::y, testing::Lt(0))), + testing::Field(&DidOverscrollParams::causal_event_viewport_point, + testing::Eq(scroll_point))))); + input_handler_->DidOverscroll( + scroll_point, accumulated_overscroll, latest_overscroll_delta); // The next call to animate will no longer scroll vertically. EXPECT_CALL(mock_input_handler_, SetNeedsAnimate()); @@ -1379,17 +1385,20 @@ TEST_F(InputHandlerProxyTest, GestureFlingCancelledAfterBothAxesStopScrolling) { // Simulate hitting the right content edge. accumulated_overscroll = gfx::Vector2dF(100, 100); latest_overscroll_delta = gfx::Vector2dF(100, 0); - EXPECT_CALL(mock_client_, - DidOverscroll(testing::AllOf( - testing::Field(&DidOverscrollParams::accumulated_overscroll, - testing::Eq(accumulated_overscroll)), - testing::Field(&DidOverscrollParams::latest_overscroll_delta, - testing::Eq(latest_overscroll_delta)), - testing::Field( - &DidOverscrollParams::current_fling_velocity, - testing::Property(&gfx::Vector2dF::x, testing::Lt(0)))))); - input_handler_->DidOverscroll(accumulated_overscroll, - latest_overscroll_delta); + scroll_point = gfx::PointF(50, 0); + EXPECT_CALL( + mock_client_, + DidOverscroll(testing::AllOf( + testing::Field(&DidOverscrollParams::accumulated_overscroll, + testing::Eq(accumulated_overscroll)), + testing::Field(&DidOverscrollParams::latest_overscroll_delta, + testing::Eq(latest_overscroll_delta)), + testing::Field(&DidOverscrollParams::current_fling_velocity, + testing::Property(&gfx::Vector2dF::x, testing::Lt(0))), + testing::Field(&DidOverscrollParams::causal_event_viewport_point, + testing::Eq(scroll_point))))); + input_handler_->DidOverscroll( + scroll_point, accumulated_overscroll, latest_overscroll_delta); // The next call to animate will no longer scroll horizontally or vertically, // and the fling should be cancelled. EXPECT_CALL(mock_input_handler_, SetNeedsAnimate()).Times(0); @@ -1856,5 +1865,75 @@ TEST_F(InputHandlerProxyTest, NoFlingBoostIfFlingTooSlow) { VERIFY_AND_RESET_MOCKS(); } +TEST_F(InputHandlerProxyTest, FlingBoostTerminatedDuringScrollSequence) { + base::TimeDelta dt = base::TimeDelta::FromMilliseconds(10); + base::TimeTicks time = base::TimeTicks() + dt; + base::TimeTicks last_animate_time = time; + WebFloatPoint fling_delta = WebFloatPoint(1000, 0); + WebPoint fling_point = WebPoint(7, 13); + StartFling( + time, blink::WebGestureDeviceTouchscreen, fling_delta, fling_point); + + // Now cancel the fling. The fling cancellation should be deferred to allow + // fling boosting events to arrive. + time += dt; + CancelFling(time); + + // The GestureScrollBegin should be swallowed by the fling. + time += dt; + gesture_.timeStampSeconds = InSecondsF(time); + gesture_.type = WebInputEvent::GestureScrollBegin; + EXPECT_CALL(mock_input_handler_, + IsCurrentlyScrollingLayerAt(testing::_, testing::_)) + .WillOnce(testing::Return(true)); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); + + // Now animate the fling to completion (in this case, the fling should + // terminate because the input handler reports a failed scroll). As the fling + // was cancelled during an active scroll sequence, a synthetic + // GestureScrollBegin should be processed, resuming the scroll. + time += dt; + float expected_delta = + (time - last_animate_time).InSecondsF() * -fling_delta.x; + EXPECT_CALL(mock_input_handler_, + ScrollBy(testing::_, + testing::Property(&gfx::Vector2dF::x, + testing::Eq(expected_delta)))) + .WillOnce(testing::Return(false)); + EXPECT_CALL(mock_input_handler_, ScrollEnd()); + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(cc::InputHandler::ScrollStarted)); + input_handler_->Animate(time); + + VERIFY_AND_RESET_MOCKS(); + + // Subsequent GestureScrollUpdates after the cancelled, boosted fling should + // cause scrolling as usual. + time += dt; + expected_delta = 7.3f; + gesture_.timeStampSeconds = InSecondsF(time); + gesture_.type = WebInputEvent::GestureScrollUpdate; + gesture_.data.scrollUpdate.deltaX = -expected_delta; + EXPECT_CALL(mock_input_handler_, + ScrollBy(testing::_, + testing::Property(&gfx::Vector2dF::x, + testing::Eq(expected_delta)))) + .WillOnce(testing::Return(true)); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); + + // GestureScrollEnd should terminate the resumed scroll properly. + time += dt; + gesture_.timeStampSeconds = InSecondsF(time); + gesture_.type = WebInputEvent::GestureScrollEnd; + EXPECT_CALL(mock_input_handler_, ScrollEnd()); + EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_)); + + VERIFY_AND_RESET_MOCKS(); +} + } // namespace } // namespace content diff --git a/content/renderer/media/android/media_info_loader.cc b/content/renderer/media/android/media_info_loader.cc index a89356f2cf788..3c22389e46e25 100644 --- a/content/renderer/media/android/media_info_loader.cc +++ b/content/renderer/media/android/media_info_loader.cc @@ -29,6 +29,7 @@ MediaInfoLoader::MediaInfoLoader( const ReadyCB& ready_cb) : loader_failed_(false), url_(url), + allow_stored_credentials_(false), cors_mode_(cors_mode), single_origin_(true), ready_cb_(ready_cb) {} @@ -41,6 +42,7 @@ void MediaInfoLoader::Start(blink::WebFrame* frame) { CHECK(frame); start_time_ = base::TimeTicks::Now(); + first_party_url_ = frame->document().firstPartyForCookies(); // Prepare the request. WebURLRequest request(url_); @@ -57,14 +59,17 @@ void MediaInfoLoader::Start(blink::WebFrame* frame) { options.allowCredentials = true; options.crossOriginRequestPolicy = WebURLLoaderOptions::CrossOriginRequestPolicyAllow; + allow_stored_credentials_ = true; } else { options.exposeAllResponseHeaders = true; // The author header set is empty, no preflight should go ahead. options.preflightPolicy = WebURLLoaderOptions::PreventPreflight; options.crossOriginRequestPolicy = WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl; - if (cors_mode_ == blink::WebMediaPlayer::CORSModeUseCredentials) + if (cors_mode_ == blink::WebMediaPlayer::CORSModeUseCredentials) { options.allowCredentials = true; + allow_stored_credentials_ = true; + } } loader.reset(frame->createAssociatedURLLoader(options)); } @@ -93,6 +98,8 @@ void MediaInfoLoader::willSendRequest( single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin(); url_ = newRequest.url(); + first_party_url_ = newRequest.firstPartyForCookies(); + allow_stored_credentials_ = newRequest.allowStoredCredentials(); } void MediaInfoLoader::didSendData( @@ -188,7 +195,8 @@ void MediaInfoLoader::DidBecomeReady(Status status) { base::TimeTicks::Now() - start_time_); active_loader_.reset(); if (!ready_cb_.is_null()) - base::ResetAndReturn(&ready_cb_).Run(status); + base::ResetAndReturn(&ready_cb_).Run(status, url_, first_party_url_, + allow_stored_credentials_); } } // namespace content diff --git a/content/renderer/media/android/media_info_loader.h b/content/renderer/media/android/media_info_loader.h index d87b67c30edcb..caefc9d16729c 100644 --- a/content/renderer/media/android/media_info_loader.h +++ b/content/renderer/media/android/media_info_loader.h @@ -14,6 +14,7 @@ #include "content/renderer/media/active_loader.h" #include "third_party/WebKit/public/platform/WebMediaPlayer.h" #include "third_party/WebKit/public/platform/WebURLLoaderClient.h" +#include "third_party/WebKit/public/web/WebDocument.h" #include "url/gurl.h" namespace blink { @@ -44,11 +45,16 @@ class CONTENT_EXPORT MediaInfoLoader : private blink::WebURLLoaderClient { kOk, }; + // Callback when MediaInfoLoader finishes loading the url. Args: whether URL + // is successfully loaded, the final URL destination following all the + // redirect, the first party URL for the final destination, and whether + // credentials needs to be sent to the final destination. + typedef base::Callback ReadyCB; + // Start loading information about the given media URL. // |url| - URL for the media resource to be loaded. // |cors_mode| - HTML media element's crossorigin attribute. // |ready_cb| - Called when media info has finished or failed loading. - typedef base::Callback ReadyCB; MediaInfoLoader( const GURL& url, blink::WebMediaPlayer::CORSMode cors_mode, @@ -66,10 +72,6 @@ class CONTENT_EXPORT MediaInfoLoader : private blink::WebURLLoaderClient { // Only valid to call after the loader becomes ready. bool DidPassCORSAccessCheck() const; - void set_single_origin(bool single_origin) { - single_origin_ = single_origin; - } - private: friend class MediaInfoLoaderTest; @@ -115,6 +117,8 @@ class CONTENT_EXPORT MediaInfoLoader : private blink::WebURLLoaderClient { bool loader_failed_; GURL url_; + GURL first_party_url_; + bool allow_stored_credentials_; blink::WebMediaPlayer::CORSMode cors_mode_; bool single_origin_; diff --git a/content/renderer/media/android/media_info_loader_unittest.cc b/content/renderer/media/android/media_info_loader_unittest.cc index ffde7927e1d33..41a7b894b77eb 100644 --- a/content/renderer/media/android/media_info_loader_unittest.cc +++ b/content/renderer/media/android/media_info_loader_unittest.cc @@ -86,7 +86,7 @@ class MediaInfoLoaderTest : public testing::Test { void SendResponse( int http_status, MediaInfoLoader::Status expected_status) { - EXPECT_CALL(*this, ReadyCallback(expected_status)); + EXPECT_CALL(*this, ReadyCallback(expected_status, _, _, _)); EXPECT_CALL(*url_loader_, cancel()); WebURLResponse response(gurl_); @@ -98,11 +98,13 @@ class MediaInfoLoaderTest : public testing::Test { } void FailLoad() { - EXPECT_CALL(*this, ReadyCallback(MediaInfoLoader::kFailed)); + EXPECT_CALL(*this, ReadyCallback( + MediaInfoLoader::kFailed, _, _, _)); loader_->didFail(url_loader_, WebURLError()); } - MOCK_METHOD1(ReadyCallback, void(MediaInfoLoader::Status)); + MOCK_METHOD4(ReadyCallback, + void(MediaInfoLoader::Status, const GURL&, const GURL&, bool)); protected: GURL gurl_; diff --git a/content/renderer/media/android/renderer_media_player_manager.cc b/content/renderer/media/android/renderer_media_player_manager.cc index f3e0bc01cfc6c..88af4740d0baa 100644 --- a/content/renderer/media/android/renderer_media_player_manager.cc +++ b/content/renderer/media/android/renderer_media_player_manager.cc @@ -67,8 +67,8 @@ void RendererMediaPlayerManager::Initialize( const GURL& url, const GURL& first_party_for_cookies, int demuxer_client_id, - const GURL& frame_url) { - + const GURL& frame_url, + bool allow_credentials) { MediaPlayerHostMsg_Initialize_Params media_player_params; media_player_params.type = type; media_player_params.player_id = player_id; @@ -76,6 +76,7 @@ void RendererMediaPlayerManager::Initialize( media_player_params.url = url; media_player_params.first_party_for_cookies = first_party_for_cookies; media_player_params.frame_url = frame_url; + media_player_params.allow_credentials = allow_credentials; Send(new MediaPlayerHostMsg_Initialize(routing_id(), media_player_params)); } diff --git a/content/renderer/media/android/renderer_media_player_manager.h b/content/renderer/media/android/renderer_media_player_manager.h index 4bcb2888378aa..e8698c9506ad8 100644 --- a/content/renderer/media/android/renderer_media_player_manager.h +++ b/content/renderer/media/android/renderer_media_player_manager.h @@ -45,7 +45,8 @@ class RendererMediaPlayerManager : public RenderFrameObserver { const GURL& url, const GURL& first_party_for_cookies, int demuxer_client_id, - const GURL& frame_url); + const GURL& frame_url, + bool allow_credentials); // Starts the player. void Start(int player_id); diff --git a/content/renderer/media/android/webmediaplayer_android.cc b/content/renderer/media/android/webmediaplayer_android.cc index 43886fc33d71a..61816427b11c8 100644 --- a/content/renderer/media/android/webmediaplayer_android.cc +++ b/content/renderer/media/android/webmediaplayer_android.cc @@ -4,8 +4,10 @@ #include "content/renderer/media/android/webmediaplayer_android.h" +#include #include +#include "base/android/build_info.h" #include "base/bind.h" #include "base/callback_helpers.h" #include "base/command_line.h" @@ -40,6 +42,7 @@ #include "media/base/video_frame.h" #include "net/base/mime_util.h" #include "third_party/WebKit/public/platform/Platform.h" +#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h" #include "third_party/WebKit/public/platform/WebGraphicsContext3DProvider.h" #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h" #include "third_party/WebKit/public/platform/WebString.h" @@ -55,6 +58,7 @@ #include "ui/gfx/image/image.h" static const uint32 kGLTextureExternalOES = 0x8D65; +static const int kSDKVersionToSupportSecurityOriginCheck = 20; using blink::WebMediaPlayer; using blink::WebSize; @@ -96,6 +100,10 @@ class SyncPointClientImpl : public media::VideoFrame::SyncPointClient { blink::WebGraphicsContext3D* web_graphics_context_; }; +// Used for calls to decryptor_ready_cb_ where the result can be ignored. +void DoNothing(bool) { +} + } // namespace namespace content { @@ -138,6 +146,7 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid( is_remote_(false), media_log_(media_log), web_cdm_(NULL), + allow_stored_credentials_(false), weak_factory_(this) { DCHECK(player_manager_); DCHECK(cdm_manager_); @@ -160,6 +169,7 @@ WebMediaPlayerAndroid::WebMediaPlayerAndroid( } WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { + DCHECK(main_thread_checker_.CalledOnValidThread()); SetVideoFrameProviderClient(NULL); client_->setWebLayer(NULL); @@ -188,6 +198,7 @@ WebMediaPlayerAndroid::~WebMediaPlayerAndroid() { void WebMediaPlayerAndroid::load(LoadType load_type, const blink::WebURL& url, CORSMode cors_mode) { + DCHECK(main_thread_checker_.CalledOnValidThread()); ReportMediaSchemeUma(GURL(url)); switch (load_type) { @@ -231,7 +242,8 @@ void WebMediaPlayerAndroid::load(LoadType load_type, weak_factory_.GetWeakPtr()), base::Bind(&WebMediaPlayerAndroid::OnDurationChanged, weak_factory_.GetWeakPtr())); - InitializePlayer(demuxer_client_id); + InitializePlayer(url_, frame_->document().firstPartyForCookies(), + true, demuxer_client_id); } } else { info_loader_.reset( @@ -240,11 +252,6 @@ void WebMediaPlayerAndroid::load(LoadType load_type, cors_mode, base::Bind(&WebMediaPlayerAndroid::DidLoadMediaInfo, weak_factory_.GetWeakPtr()))); - // TODO(qinmin): The url might be redirected when android media player - // requests the stream. As a result, we cannot guarantee there is only - // a single origin. Remove the following line when b/12573548 is fixed. - // Check http://crbug.com/334204. - info_loader_->set_single_origin(false); info_loader_->Start(frame_); } @@ -252,7 +259,12 @@ void WebMediaPlayerAndroid::load(LoadType load_type, UpdateReadyState(WebMediaPlayer::ReadyStateHaveNothing); } -void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) { +void WebMediaPlayerAndroid::DidLoadMediaInfo( + MediaInfoLoader::Status status, + const GURL& redirected_url, + const GURL& first_party_for_cookies, + bool allow_stored_credentials) { + DCHECK(main_thread_checker_.CalledOnValidThread()); DCHECK(!media_source_delegate_); if (status == MediaInfoLoader::kFailed) { info_loader_.reset(); @@ -260,12 +272,14 @@ void WebMediaPlayerAndroid::DidLoadMediaInfo(MediaInfoLoader::Status status) { return; } - InitializePlayer(0); + InitializePlayer( + redirected_url, first_party_for_cookies, allow_stored_credentials, 0); UpdateNetworkState(WebMediaPlayer::NetworkStateIdle); } void WebMediaPlayerAndroid::play() { + DCHECK(main_thread_checker_.CalledOnValidThread()); #if defined(VIDEO_HOLE) if (hasVideo() && needs_external_surface_ && !player_manager_->IsInFullscreen(frame_)) { @@ -289,6 +303,7 @@ void WebMediaPlayerAndroid::play() { } void WebMediaPlayerAndroid::pause() { + DCHECK(main_thread_checker_.CalledOnValidThread()); Pause(true); } @@ -345,10 +360,12 @@ void WebMediaPlayerAndroid::setRate(double rate) { } void WebMediaPlayerAndroid::setVolume(double volume) { + DCHECK(main_thread_checker_.CalledOnValidThread()); player_manager_->SetVolume(player_id_, volume); } bool WebMediaPlayerAndroid::hasVideo() const { + DCHECK(main_thread_checker_.CalledOnValidThread()); // If we have obtained video size information before, use it. if (has_size_info_) return !natural_size_.isEmpty(); @@ -370,6 +387,7 @@ bool WebMediaPlayerAndroid::hasVideo() const { } bool WebMediaPlayerAndroid::hasAudio() const { + DCHECK(main_thread_checker_.CalledOnValidThread()); if (!url_.has_path()) return false; std::string mime; @@ -393,6 +411,7 @@ bool WebMediaPlayerAndroid::seeking() const { } double WebMediaPlayerAndroid::duration() const { + DCHECK(main_thread_checker_.CalledOnValidThread()); // HTML5 spec requires duration to be NaN if readyState is HAVE_NOTHING if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) return std::numeric_limits::quiet_NaN(); @@ -404,6 +423,7 @@ double WebMediaPlayerAndroid::duration() const { } double WebMediaPlayerAndroid::timelineOffset() const { + DCHECK(main_thread_checker_.CalledOnValidThread()); base::Time timeline_offset; if (media_source_delegate_) timeline_offset = media_source_delegate_->GetTimelineOffset(); @@ -415,6 +435,7 @@ double WebMediaPlayerAndroid::timelineOffset() const { } double WebMediaPlayerAndroid::currentTime() const { + DCHECK(main_thread_checker_.CalledOnValidThread()); // If the player is processing a seek, return the seek time. // Blink may still query us if updatePlaybackState() occurs while seeking. if (seeking()) { @@ -463,6 +484,7 @@ bool WebMediaPlayerAndroid::EnsureTextureBackedSkBitmap(GrContext* gr, const WebSize& size, GrSurfaceOrigin origin, GrPixelConfig config) { + DCHECK(main_thread_checker_.CalledOnValidThread()); if (!bitmap.getTexture() || bitmap.width() != size.width || bitmap.height() != size.height) { if (!gr) @@ -493,6 +515,14 @@ bool WebMediaPlayerAndroid::EnsureTextureBackedSkBitmap(GrContext* gr, void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas, const blink::WebRect& rect, unsigned char alpha) { + paint(canvas, rect, alpha, SkXfermode::kSrcOver_Mode); +} + +void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, + unsigned char alpha, + SkXfermode::Mode mode) { + DCHECK(main_thread_checker_.CalledOnValidThread()); scoped_ptr provider = scoped_ptr(blink::Platform::current( )->createSharedOffscreenGraphicsContext3DProvider()); @@ -528,6 +558,7 @@ void WebMediaPlayerAndroid::paint(blink::WebCanvas* canvas, dest.set(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); SkPaint paint; paint.setAlpha(alpha); + paint.setXfermodeMode(mode); // It is not necessary to pass the dest into the drawBitmap call since all // the context have been set up before calling paintCurrentFrameInContext. canvas->drawBitmapRect(bitmap_, 0, dest, &paint); @@ -541,6 +572,7 @@ bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( unsigned int type, bool premultiply_alpha, bool flip_y) { + DCHECK(main_thread_checker_.CalledOnValidThread()); // Don't allow clients to copy an encrypted video frame. if (needs_external_surface_) return false; @@ -559,16 +591,6 @@ bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( mailbox_holder->texture_target == GL_TEXTURE_EXTERNAL_OES) || (is_remote_ && mailbox_holder->texture_target == GL_TEXTURE_2D)); - // For hidden video element (with style "display:none"), ensure the texture - // size is set. - if (!is_remote_ && - (cached_stream_texture_size_.width != natural_size_.width || - cached_stream_texture_size_.height != natural_size_.height)) { - stream_texture_factory_->SetStreamTextureSize( - stream_id_, gfx::Size(natural_size_.width, natural_size_.height)); - cached_stream_texture_size_ = natural_size_; - } - web_graphics_context->waitSyncPoint(mailbox_holder->sync_point); // Ensure the target of texture is set before copyTextureCHROMIUM, otherwise @@ -602,15 +624,28 @@ bool WebMediaPlayerAndroid::copyVideoTextureToPlatformTexture( } bool WebMediaPlayerAndroid::hasSingleSecurityOrigin() const { - if (info_loader_) - return info_loader_->HasSingleOrigin(); - // The info loader may have failed. - if (player_type_ == MEDIA_PLAYER_TYPE_URL) + DCHECK(main_thread_checker_.CalledOnValidThread()); + if (player_type_ != MEDIA_PLAYER_TYPE_URL) + return true; + + if (!info_loader_ || !info_loader_->HasSingleOrigin()) return false; - return true; + + // TODO(qinmin): The url might be redirected when android media player + // requests the stream. As a result, we cannot guarantee there is only + // a single origin. Only if the HTTP request was made without credentials, + // we will honor the return value from HasSingleSecurityOriginInternal() + // in pre-L android versions. + // Check http://crbug.com/334204. + if (!allow_stored_credentials_) + return true; + + return base::android::BuildInfo::GetInstance()->sdk_int() >= + kSDKVersionToSupportSecurityOriginCheck; } bool WebMediaPlayerAndroid::didPassCORSAccessCheck() const { + DCHECK(main_thread_checker_.CalledOnValidThread()); if (info_loader_) return info_loader_->DidPassCORSAccessCheck(); return false; @@ -650,9 +685,10 @@ unsigned WebMediaPlayerAndroid::videoDecodedByteCount() const { void WebMediaPlayerAndroid::OnMediaMetadataChanged( const base::TimeDelta& duration, int width, int height, bool success) { + DCHECK(main_thread_checker_.CalledOnValidThread()); bool need_to_signal_duration_changed = false; - if (url_.SchemeIs("file")) + if (url_.SchemeIs("file") || url_.SchemeIs("app")) UpdateNetworkState(WebMediaPlayer::NetworkStateLoaded); // Update duration, if necessary, prior to ready state updates that may @@ -753,6 +789,7 @@ void WebMediaPlayerAndroid::OnMediaError(int error_type) { } void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { + DCHECK(main_thread_checker_.CalledOnValidThread()); has_size_info_ = true; if (natural_size_.width == width && natural_size_.height == height) return; @@ -772,6 +809,9 @@ void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { SetNeedsEstablishPeer(true); } #endif // defined(VIDEO_HOLE) + natural_size_.width = width; + natural_size_.height = height; + // When play() gets called, |natural_size_| may still be empty and // EstablishSurfaceTexturePeer() will not get called. As a result, the video // may play without a surface texture. When we finally get the valid video @@ -780,10 +820,16 @@ void WebMediaPlayerAndroid::OnVideoSizeChanged(int width, int height) { if (!paused() && needs_establish_peer_) EstablishSurfaceTexturePeer(); - natural_size_.width = width; - natural_size_.height = height; ReallocateVideoFrame(); + // For hidden video element (with style "display:none"), ensure the texture + // size is set. + if (!is_remote_ && cached_stream_texture_size_ != natural_size_) { + stream_texture_factory_->SetStreamTextureSize( + stream_id_, gfx::Size(natural_size_.width, natural_size_.height)); + cached_stream_texture_size_ = natural_size_; + } + // Lazily allocate compositing layer. if (!video_weblayer_) { video_weblayer_.reset(new WebLayerImpl( @@ -938,11 +984,14 @@ void WebMediaPlayerAndroid::OnDestruct() { } void WebMediaPlayerAndroid::InitializePlayer( + const GURL& url, + const GURL& first_party_for_cookies, + bool allow_stored_credentials, int demuxer_client_id) { - GURL first_party_url = frame_->document().firstPartyForCookies(); + allow_stored_credentials_ = allow_stored_credentials; player_manager_->Initialize( - player_type_, player_id_, url_, first_party_url, demuxer_client_id, - frame_->document().url()); + player_type_, player_id_, url, first_party_for_cookies, demuxer_client_id, + frame_->document().url(), allow_stored_credentials); if (player_manager_->ShouldEnterFullscreen(frame_)) player_manager_->EnterFullscreen(player_id_, frame_); } @@ -954,7 +1003,6 @@ void WebMediaPlayerAndroid::Pause(bool is_media_related_action) { void WebMediaPlayerAndroid::DrawRemotePlaybackText( const std::string& remote_playback_message) { - DCHECK(main_thread_checker_.CalledOnValidThread()); if (!video_weblayer_) return; @@ -1076,6 +1124,7 @@ void WebMediaPlayerAndroid::DrawRemotePlaybackText( } void WebMediaPlayerAndroid::ReallocateVideoFrame() { + DCHECK(main_thread_checker_.CalledOnValidThread()); if (needs_external_surface_) { // VideoFrame::CreateHoleFrame is only defined under VIDEO_HOLE. #if defined(VIDEO_HOLE) @@ -1120,12 +1169,22 @@ void WebMediaPlayerAndroid::SetVideoFrameProviderClient( video_frame_provider_client_ = client; // Set the callback target when a frame is produced. - if (stream_texture_proxy_) + if (stream_texture_proxy_) { stream_texture_proxy_->SetClient(client); + // If client exists, the compositor thread calls it. At that time, + // stream_id_, needs_external_surface_, is_remote_ can be accessed because + // the main thread is blocked. + if (client && !stream_texture_proxy_initialized_ && stream_id_ && + !needs_external_surface_ && !is_remote_) { + stream_texture_proxy_->BindToCurrentThread(stream_id_); + stream_texture_proxy_initialized_ = true; + } + } } void WebMediaPlayerAndroid::SetCurrentFrameInternal( scoped_refptr& video_frame) { + DCHECK(main_thread_checker_.CalledOnValidThread()); base::AutoLock auto_lock(current_frame_lock_); current_frame_ = video_frame; } @@ -1137,16 +1196,6 @@ scoped_refptr WebMediaPlayerAndroid::GetCurrentFrame() { video_frame = current_frame_; } - if (!stream_texture_proxy_initialized_ && stream_texture_proxy_ && - stream_id_ && !needs_external_surface_ && !is_remote_) { - gfx::Size natural_size = video_frame->natural_size(); - // TODO(sievers): These variables are accessed on the wrong thread here. - stream_texture_proxy_->BindToCurrentThread(stream_id_); - stream_texture_factory_->SetStreamTextureSize(stream_id_, natural_size); - stream_texture_proxy_initialized_ = true; - cached_stream_texture_size_ = natural_size; - } - return video_frame; } @@ -1155,6 +1204,7 @@ void WebMediaPlayerAndroid::PutCurrentFrame( } void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() { + DCHECK(main_thread_checker_.CalledOnValidThread()); // Already created. if (stream_texture_proxy_) return; @@ -1174,15 +1224,25 @@ void WebMediaPlayerAndroid::TryCreateStreamTextureProxyIfNeeded() { } void WebMediaPlayerAndroid::EstablishSurfaceTexturePeer() { + DCHECK(main_thread_checker_.CalledOnValidThread()); if (!stream_texture_proxy_) return; if (stream_texture_factory_.get() && stream_id_) stream_texture_factory_->EstablishPeer(stream_id_, player_id_); + + // Set the deferred size because the size was changed in remote mode. + if (!is_remote_ && cached_stream_texture_size_ != natural_size_) { + stream_texture_factory_->SetStreamTextureSize( + stream_id_, gfx::Size(natural_size_.width, natural_size_.height)); + cached_stream_texture_size_ = natural_size_; + } + needs_establish_peer_ = false; } void WebMediaPlayerAndroid::DoCreateStreamTexture() { + DCHECK(main_thread_checker_.CalledOnValidThread()); DCHECK(!stream_id_); DCHECK(!texture_id_); stream_id_ = stream_texture_factory_->CreateStreamTexture( @@ -1369,7 +1429,7 @@ WebMediaPlayerAndroid::GenerateKeyRequestInternal( if (!decryptor_ready_cb_.is_null()) { base::ResetAndReturn(&decryptor_ready_cb_) - .Run(proxy_decryptor_->GetDecryptor()); + .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing)); } // Only browser CDMs have CDM ID. Render side CDMs (e.g. ClearKey CDM) do @@ -1488,13 +1548,78 @@ void WebMediaPlayerAndroid::setContentDecryptionModule( if (!web_cdm_) return; - if (!decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor()); + if (!decryptor_ready_cb_.is_null()) { + base::ResetAndReturn(&decryptor_ready_cb_) + .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing)); + } + + if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId) + player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId()); +} + +void WebMediaPlayerAndroid::setContentDecryptionModule( + blink::WebContentDecryptionModule* cdm, + blink::WebContentDecryptionModuleResult result) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + + // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 + if (!cdm) { + result.completeWithError( + blink::WebContentDecryptionModuleExceptionNotSupportedError, + 0, + "Null MediaKeys object is not supported."); + return; + } + + web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); + DCHECK(web_cdm_); + + if (!decryptor_ready_cb_.is_null()) { + base::ResetAndReturn(&decryptor_ready_cb_).Run( + web_cdm_->GetDecryptor(), + media::BindToCurrentLoop( + base::Bind(&WebMediaPlayerAndroid::ContentDecryptionModuleAttached, + weak_factory_.GetWeakPtr(), + result))); + } else { + // No pipeline/decoder connected, so resolve the promise. When something + // is connected, setting the CDM will happen in SetDecryptorReadyCB(). + ContentDecryptionModuleAttached(result, true); + } + + if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId) + player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId()); +} + +void WebMediaPlayerAndroid::setContentDecryptionModuleSync( + blink::WebContentDecryptionModule* cdm) { + DCHECK(main_thread_checker_.CalledOnValidThread()); + + // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 + if (!cdm) + return; + + DCHECK(decryptor_ready_cb_.is_null()); + web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); if (web_cdm_->GetCdmId() != RendererCdmManager::kInvalidCdmId) player_manager_->SetCdm(player_id_, web_cdm_->GetCdmId()); } +void WebMediaPlayerAndroid::ContentDecryptionModuleAttached( + blink::WebContentDecryptionModuleResult result, + bool success) { + if (success) { + result.complete(); + return; + } + + result.completeWithError( + blink::WebContentDecryptionModuleExceptionNotSupportedError, + 0, + "Unable to set MediaKeys object"); +} + void WebMediaPlayerAndroid::OnKeyAdded(const std::string& session_id) { EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); @@ -1569,8 +1694,10 @@ void WebMediaPlayerAndroid::SetDecryptorReadyCB( // Cancels the previous decryptor request. if (decryptor_ready_cb.is_null()) { - if (!decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL); + if (!decryptor_ready_cb_.is_null()) { + base::ResetAndReturn(&decryptor_ready_cb_) + .Run(NULL, base::Bind(DoNothing)); + } return; } @@ -1585,12 +1712,13 @@ void WebMediaPlayerAndroid::SetDecryptorReadyCB( DCHECK(!proxy_decryptor_ || !web_cdm_); if (proxy_decryptor_) { - decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor()); + decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(), + base::Bind(DoNothing)); return; } if (web_cdm_) { - decryptor_ready_cb.Run(web_cdm_->GetDecryptor()); + decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing)); return; } diff --git a/content/renderer/media/android/webmediaplayer_android.h b/content/renderer/media/android/webmediaplayer_android.h index 1b90148d93adf..dcb63c7f04a15 100644 --- a/content/renderer/media/android/webmediaplayer_android.h +++ b/content/renderer/media/android/webmediaplayer_android.h @@ -40,6 +40,7 @@ class MessageLoopProxy; namespace blink { class WebContentDecryptionModule; +class WebContentDecryptionModuleResult; class WebFrame; class WebURL; } @@ -110,6 +111,11 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer, // issue that Skia could not handle Android's GL_TEXTURE_EXTERNAL_OES texture // internally. It should be removed and replaced by the normal paint path. // https://code.google.com/p/skia/issues/detail?id=1189 + virtual void paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, + unsigned char alpha, + SkXfermode::Mode mode); + // TODO(dshwang): remove it because above method replaces. crbug.com/401027 virtual void paint(blink::WebCanvas* canvas, const blink::WebRect& rect, unsigned char alpha); @@ -219,7 +225,14 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer, virtual MediaKeyException cancelKeyRequest( const blink::WebString& key_system, const blink::WebString& session_id); + // TODO(jrummell): Remove this method once Blink updated to use the other + // two methods. + virtual void setContentDecryptionModule( + blink::WebContentDecryptionModule* cdm); virtual void setContentDecryptionModule( + blink::WebContentDecryptionModule* cdm, + blink::WebContentDecryptionModuleResult result); + virtual void setContentDecryptionModuleSync( blink::WebContentDecryptionModule* cdm); void OnKeyAdded(const std::string& session_id); @@ -256,12 +269,18 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer, void SetNeedsEstablishPeer(bool needs_establish_peer); private: - void InitializePlayer(int demuxer_client_id); + void InitializePlayer(const GURL& url, + const GURL& first_party_for_cookies, + bool allowed_stored_credentials, + int demuxer_client_id); void Pause(bool is_media_related_action); void DrawRemotePlaybackText(const std::string& remote_playback_message); void ReallocateVideoFrame(); void SetCurrentFrameInternal(scoped_refptr& frame); - void DidLoadMediaInfo(MediaInfoLoader::Status status); + void DidLoadMediaInfo(MediaInfoLoader::Status status, + const GURL& redirected_url, + const GURL& first_party_for_cookies, + bool allow_stored_credentials); bool IsKeySystemSupported(const std::string& key_system); // Actually do the work for generateKeyRequest/addKey so they can easily @@ -284,6 +303,12 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer, // NULL immediately and reset. void SetDecryptorReadyCB(const media::DecryptorReadyCB& decryptor_ready_cb); + // Called when the ContentDecryptionModule has been attached to the + // pipeline/decoders. + void ContentDecryptionModuleAttached( + blink::WebContentDecryptionModuleResult result, + bool success); + bool EnsureTextureBackedSkBitmap(GrContext* gr, SkBitmap& bitmap, const blink::WebSize& size, GrSurfaceOrigin origin, @@ -450,6 +475,9 @@ class WebMediaPlayerAndroid : public blink::WebMediaPlayer, SkBitmap bitmap_; + // Whether stored credentials are allowed to be passed to the server. + bool allow_stored_credentials_; + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory weak_factory_; diff --git a/content/renderer/media/audio_input_message_filter.cc b/content/renderer/media/audio_input_message_filter.cc index 412196cb3c1fb..02b0b57ba8a60 100644 --- a/content/renderer/media/audio_input_message_filter.cc +++ b/content/renderer/media/audio_input_message_filter.cc @@ -12,12 +12,21 @@ #include "ipc/ipc_logging.h" #include "ipc/ipc_sender.h" -namespace content { - namespace { + const int kStreamIDNotSet = -1; + +void LogMessage(int stream_id, const std::string& msg) { + std::ostringstream oss; + oss << "[stream_id=" << stream_id << "] AIMF::" << msg; + content::WebRtcLogMessage(oss.str()); + DVLOG(1) << oss.str(); } +} + +namespace content { + class AudioInputMessageFilter::AudioInputIPCImpl : public NON_EXPORTED_BASE(media::AudioInputIPC) { public: @@ -125,10 +134,7 @@ void AudioInputMessageFilter::OnStreamCreated( uint32 length, uint32 total_segments) { DCHECK(io_message_loop_->BelongsToCurrentThread()); - - WebRtcLogMessage(base::StringPrintf( - "AIMF::OnStreamCreated. stream_id=%d", - stream_id)); + LogMessage(stream_id, "OnStreamCreated"); #if !defined(OS_WIN) base::SyncSocket::Handle socket_handle = socket_descriptor.fd; @@ -193,6 +199,9 @@ void AudioInputMessageFilter::AudioInputIPCImpl::CreateStream( DCHECK(delegate); stream_id_ = filter_->delegates_.Add(delegate); + // TODO(henrika): remove all LogMessage calls when we have sorted out the + // existing "no input audio" issues. + LogMessage(stream_id_, "CreateStream"); AudioInputHostMsg_CreateStream_Config config; config.params = params; @@ -204,6 +213,7 @@ void AudioInputMessageFilter::AudioInputIPCImpl::CreateStream( void AudioInputMessageFilter::AudioInputIPCImpl::RecordStream() { DCHECK_NE(stream_id_, kStreamIDNotSet); + LogMessage(stream_id_, "RecordStream"); filter_->Send(new AudioInputHostMsg_RecordStream(stream_id_)); } @@ -215,6 +225,7 @@ void AudioInputMessageFilter::AudioInputIPCImpl::SetVolume(double volume) { void AudioInputMessageFilter::AudioInputIPCImpl::CloseStream() { DCHECK(filter_->io_message_loop_->BelongsToCurrentThread()); DCHECK_NE(stream_id_, kStreamIDNotSet); + LogMessage(stream_id_, "CloseStream"); filter_->Send(new AudioInputHostMsg_CloseStream(stream_id_)); filter_->delegates_.Remove(stream_id_); stream_id_ = kStreamIDNotSet; diff --git a/content/renderer/media/cdm_session_adapter.cc b/content/renderer/media/cdm_session_adapter.cc index 9a84d544c9820..de3d162019f87 100644 --- a/content/renderer/media/cdm_session_adapter.cc +++ b/content/renderer/media/cdm_session_adapter.cc @@ -9,6 +9,7 @@ #include "base/memory/weak_ptr.h" #include "base/stl_util.h" #include "content/renderer/media/crypto/content_decryption_module_factory.h" +#include "content/renderer/media/crypto/key_systems.h" #include "content/renderer/media/webcontentdecryptionmodulesession_impl.h" #include "media/base/cdm_promise.h" #include "media/base/media_keys.h" @@ -16,6 +17,9 @@ namespace content { +const char kMediaEME[] = "Media.EME."; +const char kDot[] = "."; + CdmSessionAdapter::CdmSessionAdapter() : #if defined(ENABLE_BROWSER_CDMS) cdm_id_(0), @@ -32,6 +36,7 @@ bool CdmSessionAdapter::Initialize( #endif // defined(ENABLE_PEPPER_CDMS) const std::string& key_system, const GURL& security_origin) { + key_system_uma_prefix_ = kMediaEME + KeySystemNameForUMA(key_system) + kDot; base::WeakPtr weak_this = weak_ptr_factory_.GetWeakPtr(); media_keys_ = ContentDecryptionModuleFactory::Create( key_system, @@ -103,6 +108,10 @@ media::Decryptor* CdmSessionAdapter::GetDecryptor() { return media_keys_->GetDecryptor(); } +const std::string& CdmSessionAdapter::GetKeySystemUMAPrefix() const { + return key_system_uma_prefix_; +} + #if defined(ENABLE_BROWSER_CDMS) int CdmSessionAdapter::GetCdmId() const { return cdm_id_; diff --git a/content/renderer/media/cdm_session_adapter.h b/content/renderer/media/cdm_session_adapter.h index 35e060beba1cb..7e32669962268 100644 --- a/content/renderer/media/cdm_session_adapter.h +++ b/content/renderer/media/cdm_session_adapter.h @@ -88,6 +88,9 @@ class CdmSessionAdapter : public base::RefCounted { // after WebContentDecryptionModule is freed. http://crbug.com/330324 media::Decryptor* GetDecryptor(); + // Returns a prefix to use for UMAs. + const std::string& GetKeySystemUMAPrefix() const; + #if defined(ENABLE_BROWSER_CDMS) // Returns the CDM ID associated with the |media_keys_|. May be kInvalidCdmId // if no CDM ID is associated. @@ -125,6 +128,8 @@ class CdmSessionAdapter : public base::RefCounted { int cdm_id_; #endif + std::string key_system_uma_prefix_; + // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory weak_ptr_factory_; diff --git a/content/renderer/media/crypto/ppapi_decryptor.cc b/content/renderer/media/crypto/ppapi_decryptor.cc index dd07aaacc6950..8f39c06bf84bd 100644 --- a/content/renderer/media/crypto/ppapi_decryptor.cc +++ b/content/renderer/media/crypto/ppapi_decryptor.cc @@ -55,6 +55,37 @@ class SessionUpdatedPromise : public media::SimpleCdmPromise { base::Closure additional_resolve_cb_; }; +// This class is needed so that resolving a SessionLoaded() promise triggers +// playback of the stream. It intercepts the resolve() call to invoke an +// additional callback. This is only needed until KeysChange event gets passed +// through Pepper. +class SessionLoadedPromise : public media::NewSessionCdmPromise { + public: + SessionLoadedPromise(scoped_ptr caller_promise, + base::Closure additional_resolve_cb) + : caller_promise_(caller_promise.Pass()), + additional_resolve_cb_(additional_resolve_cb) {} + + virtual void resolve(const std::string& web_session_id) OVERRIDE { + DCHECK(is_pending_); + is_pending_ = false; + additional_resolve_cb_.Run(); + caller_promise_->resolve(web_session_id); + } + + virtual void reject(media::MediaKeys::Exception exception_code, + uint32 system_code, + const std::string& error_message) OVERRIDE { + DCHECK(is_pending_); + is_pending_ = false; + caller_promise_->reject(exception_code, system_code, error_message); + } + + protected: + scoped_ptr caller_promise_; + base::Closure additional_resolve_cb_; +}; + scoped_ptr PpapiDecryptor::Create( const std::string& key_system, const GURL& security_origin, @@ -147,7 +178,14 @@ void PpapiDecryptor::LoadSession( return; } - CdmDelegate()->LoadSession(web_session_id, promise.Pass()); + scoped_ptr session_loaded_promise( + new SessionLoadedPromise(promise.Pass(), + base::Bind(&PpapiDecryptor::ResumePlayback, + weak_ptr_factory_.GetWeakPtr()))); + + CdmDelegate()->LoadSession( + web_session_id, + session_loaded_promise.PassAs()); } void PpapiDecryptor::UpdateSession( diff --git a/content/renderer/media/crypto/proxy_decryptor.cc b/content/renderer/media/crypto/proxy_decryptor.cc index 28cd421e85ff0..05026803c9f3e 100644 --- a/content/renderer/media/crypto/proxy_decryptor.cc +++ b/content/renderer/media/crypto/proxy_decryptor.cc @@ -102,21 +102,25 @@ bool ProxyDecryptor::GenerateKeyRequest(const std::string& content_type, const char kPrefixedApiPersistentSessionHeader[] = "PERSISTENT|"; const char kPrefixedApiLoadSessionHeader[] = "LOAD_SESSION|"; - bool loadSession = - HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader); - bool persistent = HasHeader( - init_data, init_data_length, kPrefixedApiPersistentSessionHeader); + SessionCreationType session_creation_type = TemporarySession; + if (HasHeader(init_data, init_data_length, kPrefixedApiLoadSessionHeader)) { + session_creation_type = LoadSession; + } else if (HasHeader(init_data, + init_data_length, + kPrefixedApiPersistentSessionHeader)) { + session_creation_type = PersistentSession; + } scoped_ptr promise( new media::NewSessionCdmPromise( base::Bind(&ProxyDecryptor::SetSessionId, weak_ptr_factory_.GetWeakPtr(), - persistent || loadSession), + session_creation_type), base::Bind(&ProxyDecryptor::OnSessionError, weak_ptr_factory_.GetWeakPtr(), std::string()))); // No session id until created. - if (loadSession) { + if (session_creation_type == LoadSession) { media_keys_->LoadSession( std::string(reinterpret_cast( init_data + strlen(kPrefixedApiLoadSessionHeader)), @@ -126,10 +130,23 @@ bool ProxyDecryptor::GenerateKeyRequest(const std::string& content_type, } media::MediaKeys::SessionType session_type = - persistent ? media::MediaKeys::PERSISTENT_SESSION - : media::MediaKeys::TEMPORARY_SESSION; - media_keys_->CreateSession( - content_type, init_data, init_data_length, session_type, promise.Pass()); + session_creation_type == PersistentSession + ? media::MediaKeys::PERSISTENT_SESSION + : media::MediaKeys::TEMPORARY_SESSION; + + // Convert MIME types used in the prefixed implementation. + std::string init_data_type; + if (content_type == "audio/mp4" || content_type == "video/mp4") { + init_data_type = "cenc"; + } else if (content_type == "audio/webm" || content_type == "video/webm") { + init_data_type = "webm"; + } else { + NOTREACHED(); + init_data_type = content_type; + } + + media_keys_->CreateSession(init_data_type, init_data, init_data_length, + session_type, promise.Pass()); return true; } @@ -286,9 +303,16 @@ void ProxyDecryptor::OnSessionError(const std::string& web_session_id, key_error_cb_.Run(web_session_id, error_code, system_code); } -void ProxyDecryptor::SetSessionId(bool persistent, +void ProxyDecryptor::SetSessionId(SessionCreationType session_type, const std::string& web_session_id) { - active_sessions_.insert(std::make_pair(web_session_id, persistent)); + // Loaded sessions are considered persistent. + bool is_persistent = + session_type == PersistentSession || session_type == LoadSession; + active_sessions_.insert(std::make_pair(web_session_id, is_persistent)); + + // For LoadSession(), generate the SessionReady event. + if (session_type == LoadSession) + OnSessionReady(web_session_id); } } // namespace content diff --git a/content/renderer/media/crypto/proxy_decryptor.h b/content/renderer/media/crypto/proxy_decryptor.h index e4fd7eace5e7f..b1e736d718054 100644 --- a/content/renderer/media/crypto/proxy_decryptor.h +++ b/content/renderer/media/crypto/proxy_decryptor.h @@ -96,8 +96,15 @@ class ProxyDecryptor { uint32 system_code, const std::string& error_message); + enum SessionCreationType { + TemporarySession, + PersistentSession, + LoadSession + }; + // Called when a session is actually created or loaded. - void SetSessionId(bool persistent, const std::string& web_session_id); + void SetSessionId(SessionCreationType session_type, + const std::string& web_session_id); #if defined(ENABLE_PEPPER_CDMS) // Callback to create the Pepper plugin. diff --git a/content/renderer/media/crypto/proxy_media_keys.cc b/content/renderer/media/crypto/proxy_media_keys.cc index 68a68b3bc370f..17e918d80ec23 100644 --- a/content/renderer/media/crypto/proxy_media_keys.cc +++ b/content/renderer/media/crypto/proxy_media_keys.cc @@ -57,9 +57,9 @@ void ProxyMediaKeys::CreateSession( // TODO(xhwang): Move these checks up to blink and DCHECK here. // See http://crbug.com/342510 CdmHostMsg_CreateSession_ContentType create_session_content_type; - if (init_data_type == "audio/mp4" || init_data_type == "video/mp4") { + if (init_data_type == "cenc") { create_session_content_type = CREATE_SESSION_TYPE_MP4; - } else if (init_data_type == "audio/webm" || init_data_type == "video/webm") { + } else if (init_data_type == "webm") { create_session_content_type = CREATE_SESSION_TYPE_WEBM; } else { DLOG(ERROR) << "Unsupported EME CreateSession content type of " diff --git a/content/renderer/media/media_stream_audio_processor.cc b/content/renderer/media/media_stream_audio_processor.cc index 2e7a40db18c27..76ce92c76d3b9 100644 --- a/content/renderer/media/media_stream_audio_processor.cc +++ b/content/renderer/media/media_stream_audio_processor.cc @@ -31,10 +31,32 @@ const int kAudioProcessingSampleRate = 16000; const int kAudioProcessingSampleRate = 32000; #endif const int kAudioProcessingNumberOfChannels = 1; -const AudioProcessing::ChannelLayout kAudioProcessingChannelLayout = - AudioProcessing::kMono; -const int kMaxNumberOfBuffersInFifo = 2; +AudioProcessing::ChannelLayout MapLayout(media::ChannelLayout media_layout) { + switch (media_layout) { + case media::CHANNEL_LAYOUT_MONO: + return AudioProcessing::kMono; + case media::CHANNEL_LAYOUT_STEREO: + return AudioProcessing::kStereo; + case media::CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC: + return AudioProcessing::kStereoAndKeyboard; + default: + NOTREACHED() << "Layout not supported: " << media_layout; + return AudioProcessing::kMono; + } +} + +AudioProcessing::ChannelLayout ChannelsToLayout(int num_channels) { + switch (num_channels) { + case 1: + return AudioProcessing::kMono; + case 2: + return AudioProcessing::kStereo; + default: + NOTREACHED() << "Channels not supported: " << num_channels; + return AudioProcessing::kMono; + } +} // Used by UMA histograms and entries shouldn't be re-ordered or removed. enum AudioTrackProcessingStates { @@ -51,122 +73,105 @@ void RecordProcessingState(AudioTrackProcessingStates state) { } // namespace -class MediaStreamAudioProcessor::MediaStreamAudioConverter - : public media::AudioConverter::InputCallback { +// Wraps AudioBus to provide access to the array of channel pointers, since this +// is the type webrtc::AudioProcessing deals in. The array is refreshed on every +// channel_ptrs() call, and will be valid until the underlying AudioBus pointers +// are changed, e.g. through calls to SetChannelData() or SwapChannels(). +// +// All methods are called on one of the capture or render audio threads +// exclusively. +class MediaStreamAudioBus { public: - MediaStreamAudioConverter(const media::AudioParameters& source_params, - const media::AudioParameters& sink_params) - : source_params_(source_params), - sink_params_(sink_params), - audio_converter_(source_params, sink_params_, false) { - // An instance of MediaStreamAudioConverter may be created in the main - // render thread and used in the audio thread, for example, the - // |MediaStreamAudioProcessor::capture_converter_|. + MediaStreamAudioBus(int channels, int frames) + : bus_(media::AudioBus::Create(channels, frames)), + channel_ptrs_(new float*[channels]) { + // May be created in the main render thread and used in the audio threads. thread_checker_.DetachFromThread(); - audio_converter_.AddInput(this); - - // Create and initialize audio fifo and audio bus wrapper. - // The size of the FIFO should be at least twice of the source buffer size - // or twice of the sink buffer size. Also, FIFO needs to have enough space - // to store pre-processed data before passing the data to - // webrtc::AudioProcessing, which requires 10ms as packet size. - int max_frame_size = std::max(source_params_.frames_per_buffer(), - sink_params_.frames_per_buffer()); - int buffer_size = std::max( - kMaxNumberOfBuffersInFifo * max_frame_size, - kMaxNumberOfBuffersInFifo * source_params_.sample_rate() / 100); - fifo_.reset(new media::AudioFifo(source_params_.channels(), buffer_size)); - - // TODO(xians): Use CreateWrapper to save one memcpy. - audio_wrapper_ = media::AudioBus::Create(sink_params_.channels(), - sink_params_.frames_per_buffer()); } - virtual ~MediaStreamAudioConverter() { - audio_converter_.RemoveInput(this); + media::AudioBus* bus() { + DCHECK(thread_checker_.CalledOnValidThread()); + return bus_.get(); } - void Push(const media::AudioBus* audio_source) { - // Called on the audio thread, which is the capture audio thread for - // |MediaStreamAudioProcessor::capture_converter_|, and render audio thread - // for |MediaStreamAudioProcessor::render_converter_|. - // And it must be the same thread as calling Convert(). + float* const* channel_ptrs() { DCHECK(thread_checker_.CalledOnValidThread()); - fifo_->Push(audio_source); + for (int i = 0; i < bus_->channels(); ++i) { + channel_ptrs_[i] = bus_->channel(i); + } + return channel_ptrs_.get(); } - bool Convert(webrtc::AudioFrame* out, bool audio_mirroring) { - // Called on the audio thread, which is the capture audio thread for - // |MediaStreamAudioProcessor::capture_converter_|, and render audio thread - // for |MediaStreamAudioProcessor::render_converter_|. - DCHECK(thread_checker_.CalledOnValidThread()); - // Return false if there is not enough data in the FIFO, this happens when - // fifo_->frames() / source_params_.sample_rate() is less than - // sink_params.frames_per_buffer() / sink_params.sample_rate(). - if (fifo_->frames() * sink_params_.sample_rate() < - sink_params_.frames_per_buffer() * source_params_.sample_rate()) { - return false; + private: + base::ThreadChecker thread_checker_; + scoped_ptr bus_; + scoped_ptr channel_ptrs_; +}; + +// Wraps AudioFifo to provide a cleaner interface to MediaStreamAudioProcessor. +// It avoids the FIFO when the source and destination frames match. All methods +// are called on one of the capture or render audio threads exclusively. +class MediaStreamAudioFifo { + public: + MediaStreamAudioFifo(int channels, int source_frames, + int destination_frames) + : source_frames_(source_frames), + destination_(new MediaStreamAudioBus(channels, destination_frames)), + data_available_(false) { + if (source_frames != destination_frames) { + // Since we require every Push to be followed by as many Consumes as + // possible, twice the larger of the two is a (probably) loose upper bound + // on the FIFO size. + const int fifo_frames = 2 * std::max(source_frames, destination_frames); + fifo_.reset(new media::AudioFifo(channels, fifo_frames)); } - // Convert data to the output format, this will trigger ProvideInput(). - audio_converter_.Convert(audio_wrapper_.get()); - DCHECK_EQ(audio_wrapper_->frames(), sink_params_.frames_per_buffer()); + // May be created in the main render thread and used in the audio threads. + thread_checker_.DetachFromThread(); + } - // Swap channels before interleaving the data if |audio_mirroring| is - // set to true. - if (audio_mirroring && - sink_params_.channel_layout() == media::CHANNEL_LAYOUT_STEREO) { - // Swap the first and second channels. - audio_wrapper_->SwapChannels(0, 1); + void Push(const media::AudioBus* source) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_EQ(source->channels(), destination_->bus()->channels()); + DCHECK_EQ(source->frames(), source_frames_); + + if (fifo_) { + fifo_->Push(source); + } else { + source->CopyTo(destination_->bus()); + data_available_ = true; } + } - // TODO(xians): Figure out a better way to handle the interleaved and - // deinterleaved format switching. - audio_wrapper_->ToInterleaved(audio_wrapper_->frames(), - sink_params_.bits_per_sample() / 8, - out->data_); + // Returns true if there are destination_frames() of data available to be + // consumed, and otherwise false. + bool Consume(MediaStreamAudioBus** destination) { + DCHECK(thread_checker_.CalledOnValidThread()); - out->samples_per_channel_ = sink_params_.frames_per_buffer(); - out->sample_rate_hz_ = sink_params_.sample_rate(); - out->speech_type_ = webrtc::AudioFrame::kNormalSpeech; - out->vad_activity_ = webrtc::AudioFrame::kVadUnknown; - out->num_channels_ = sink_params_.channels(); + if (fifo_) { + if (fifo_->frames() < destination_->bus()->frames()) + return false; - return true; - } + fifo_->Consume(destination_->bus(), 0, destination_->bus()->frames()); + } else { + if (!data_available_) + return false; - const media::AudioParameters& source_parameters() const { - return source_params_; - } - const media::AudioParameters& sink_parameters() const { - return sink_params_; - } + // The data was already copied to |destination_| in this case. + data_available_ = false; + } - private: - // AudioConverter::InputCallback implementation. - virtual double ProvideInput(media::AudioBus* audio_bus, - base::TimeDelta buffer_delay) OVERRIDE { - // Called on realtime audio thread. - // TODO(xians): Figure out why the first Convert() triggers ProvideInput - // two times. - if (fifo_->frames() < audio_bus->frames()) - return 0; - - fifo_->Consume(audio_bus, 0, audio_bus->frames()); - - // Return 1.0 to indicate no volume scaling on the data. - return 1.0; + *destination = destination_.get(); + return true; } + private: base::ThreadChecker thread_checker_; - const media::AudioParameters source_params_; - const media::AudioParameters sink_params_; - - // TODO(xians): consider using SincResampler to save some memcpy. - // Handles mixing and resampling between input and output parameters. - media::AudioConverter audio_converter_; - scoped_ptr audio_wrapper_; + const int source_frames_; // For a DCHECK. + scoped_ptr destination_; scoped_ptr fifo_; + // Only used when the FIFO is disabled; + bool data_available_; }; bool MediaStreamAudioProcessor::IsAudioTrackProcessingEnabled() { @@ -202,12 +207,12 @@ MediaStreamAudioProcessor::~MediaStreamAudioProcessor() { } void MediaStreamAudioProcessor::OnCaptureFormatChanged( - const media::AudioParameters& source_params) { + const media::AudioParameters& input_format) { DCHECK(main_thread_checker_.CalledOnValidThread()); // There is no need to hold a lock here since the caller guarantees that // there is no more PushCaptureData() and ProcessAndConsumeData() callbacks // on the capture thread. - InitializeCaptureConverter(source_params); + InitializeCaptureFifo(input_format); // Reset the |capture_thread_checker_| since the capture data will come from // a new capture thread. @@ -217,12 +222,8 @@ void MediaStreamAudioProcessor::OnCaptureFormatChanged( void MediaStreamAudioProcessor::PushCaptureData( const media::AudioBus* audio_source) { DCHECK(capture_thread_checker_.CalledOnValidThread()); - DCHECK_EQ(audio_source->channels(), - capture_converter_->source_parameters().channels()); - DCHECK_EQ(audio_source->frames(), - capture_converter_->source_parameters().frames_per_buffer()); - capture_converter_->Push(audio_source); + capture_fifo_->Push(audio_source); } bool MediaStreamAudioProcessor::ProcessAndConsumeData( @@ -231,12 +232,31 @@ bool MediaStreamAudioProcessor::ProcessAndConsumeData( DCHECK(capture_thread_checker_.CalledOnValidThread()); TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessAndConsumeData"); - if (!capture_converter_->Convert(&capture_frame_, audio_mirroring_)) + MediaStreamAudioBus* process_bus; + if (!capture_fifo_->Consume(&process_bus)) return false; - *new_volume = ProcessData(&capture_frame_, capture_delay, volume, - key_pressed); - *out = capture_frame_.data_; + // Use the process bus directly if audio processing is disabled. + MediaStreamAudioBus* output_bus = process_bus; + *new_volume = 0; + if (audio_processing_) { + output_bus = output_bus_.get(); + *new_volume = ProcessData(process_bus->channel_ptrs(), + process_bus->bus()->frames(), capture_delay, + volume, key_pressed, output_bus->channel_ptrs()); + } + + // Swap channels before interleaving the data. + if (audio_mirroring_ && + output_format_.channel_layout() == media::CHANNEL_LAYOUT_STEREO) { + // Swap the first and second channels. + output_bus->bus()->SwapChannels(0, 1); + } + + output_bus->bus()->ToInterleaved(output_bus->bus()->frames(), + sizeof(int16), + output_data_.get()); + *out = output_data_.get(); return true; } @@ -265,11 +285,11 @@ void MediaStreamAudioProcessor::Stop() { } const media::AudioParameters& MediaStreamAudioProcessor::InputFormat() const { - return capture_converter_->source_parameters(); + return input_format_; } const media::AudioParameters& MediaStreamAudioProcessor::OutputFormat() const { - return capture_converter_->sink_parameters(); + return output_format_; } void MediaStreamAudioProcessor::OnAecDumpFile( @@ -308,12 +328,18 @@ void MediaStreamAudioProcessor::OnPlayoutData(media::AudioBus* audio_bus, std::numeric_limits::max()); base::subtle::Release_Store(&render_delay_ms_, audio_delay_milliseconds); - InitializeRenderConverterIfNeeded(sample_rate, audio_bus->channels(), - audio_bus->frames()); - - render_converter_->Push(audio_bus); - while (render_converter_->Convert(&render_frame_, false)) - audio_processing_->AnalyzeReverseStream(&render_frame_); + InitializeRenderFifoIfNeeded(sample_rate, audio_bus->channels(), + audio_bus->frames()); + + render_fifo_->Push(audio_bus); + MediaStreamAudioBus* analysis_bus; + while (render_fifo_->Consume(&analysis_bus)) { + audio_processing_->AnalyzeReverseStream( + analysis_bus->channel_ptrs(), + analysis_bus->bus()->frames(), + sample_rate, + ChannelsToLayout(audio_bus->channels())); + } } void MediaStreamAudioProcessor::OnPlayoutDataSourceChanged() { @@ -321,7 +347,7 @@ void MediaStreamAudioProcessor::OnPlayoutDataSourceChanged() { // There is no need to hold a lock here since the caller guarantees that // there is no more OnPlayoutData() callback on the render thread. render_thread_checker_.DetachFromThread(); - render_converter_.reset(); + render_fifo_.reset(); } void MediaStreamAudioProcessor::GetStats(AudioProcessorStats* stats) { @@ -382,22 +408,20 @@ void MediaStreamAudioProcessor::InitializeAudioProcessingModule( return; } + // Experimental options provided at creation. + webrtc::Config config; + if (goog_experimental_aec) + config.Set(new webrtc::DelayCorrection(true)); + if (goog_experimental_ns) + config.Set(new webrtc::ExperimentalNs(true)); + // Create and configure the webrtc::AudioProcessing. - audio_processing_.reset(webrtc::AudioProcessing::Create()); - CHECK_EQ(0, audio_processing_->Initialize(kAudioProcessingSampleRate, - kAudioProcessingSampleRate, - kAudioProcessingSampleRate, - kAudioProcessingChannelLayout, - kAudioProcessingChannelLayout, - kAudioProcessingChannelLayout)); + audio_processing_.reset(webrtc::AudioProcessing::Create(config)); // Enable the audio processing components. if (echo_cancellation) { EnableEchoCancellation(audio_processing_.get()); - if (goog_experimental_aec) - EnableExperimentalEchoCancellation(audio_processing_.get()); - if (playout_data_source_) playout_data_source_->AddPlayoutSink(this); } @@ -405,9 +429,6 @@ void MediaStreamAudioProcessor::InitializeAudioProcessingModule( if (goog_ns) EnableNoiseSuppression(audio_processing_.get()); - if (goog_experimental_ns) - EnableExperimentalNoiseSuppression(audio_processing_.get()); - if (goog_high_pass_filter) EnableHighPassFilter(audio_processing_.get()); @@ -424,82 +445,95 @@ void MediaStreamAudioProcessor::InitializeAudioProcessingModule( RecordProcessingState(AUDIO_PROCESSING_ENABLED); } -void MediaStreamAudioProcessor::InitializeCaptureConverter( - const media::AudioParameters& source_params) { +void MediaStreamAudioProcessor::InitializeCaptureFifo( + const media::AudioParameters& input_format) { DCHECK(main_thread_checker_.CalledOnValidThread()); - DCHECK(source_params.IsValid()); - - // Create and initialize audio converter for the source data. - // When the webrtc AudioProcessing is enabled, the sink format of the - // converter will be the same as the post-processed data format, which is - // 32k mono for desktops and 16k mono for Android. When the AudioProcessing - // is disabled, the sink format will be the same as the source format. - const int sink_sample_rate = audio_processing_ ? - kAudioProcessingSampleRate : source_params.sample_rate(); - const media::ChannelLayout sink_channel_layout = audio_processing_ ? + DCHECK(input_format.IsValid()); + input_format_ = input_format; + + // TODO(ajm): For now, we assume fixed parameters for the output when audio + // processing is enabled, to match the previous behavior. We should either + // use the input parameters (in which case, audio processing will convert + // at output) or ideally, have a backchannel from the sink to know what + // format it would prefer. + const int output_sample_rate = audio_processing_ ? + kAudioProcessingSampleRate : input_format.sample_rate(); + const media::ChannelLayout output_channel_layout = audio_processing_ ? media::GuessChannelLayout(kAudioProcessingNumberOfChannels) : - source_params.channel_layout(); - - // WebRtc AudioProcessing requires 10ms as its packet size. We use this - // native size when processing is enabled. While processing is disabled, and - // the source is running with a buffer size smaller than 10ms buffer, we use - // same buffer size as the incoming format to avoid extra FIFO for WebAudio. - int sink_buffer_size = sink_sample_rate / 100; - if (!audio_processing_ && - source_params.frames_per_buffer() < sink_buffer_size) { - sink_buffer_size = source_params.frames_per_buffer(); + input_format.channel_layout(); + + // webrtc::AudioProcessing requires a 10 ms chunk size. We use this native + // size when processing is enabled. When disabled we use the same size as + // the source if less than 10 ms. + // + // TODO(ajm): This conditional buffer size appears to be assuming knowledge of + // the sink based on the source parameters. PeerConnection sinks seem to want + // 10 ms chunks regardless, while WebAudio sinks want less, and we're assuming + // we can identify WebAudio sinks by the input chunk size. Less fragile would + // be to have the sink actually tell us how much it wants (as in the above + // TODO). + int processing_frames = input_format.sample_rate() / 100; + int output_frames = output_sample_rate / 100; + if (!audio_processing_ && input_format.frames_per_buffer() < output_frames) { + processing_frames = input_format.frames_per_buffer(); + output_frames = processing_frames; } - media::AudioParameters sink_params( - media::AudioParameters::AUDIO_PCM_LOW_LATENCY, sink_channel_layout, - sink_sample_rate, 16, sink_buffer_size); - capture_converter_.reset( - new MediaStreamAudioConverter(source_params, sink_params)); + output_format_ = media::AudioParameters( + media::AudioParameters::AUDIO_PCM_LOW_LATENCY, + output_channel_layout, + output_sample_rate, + 16, + output_frames); + + capture_fifo_.reset( + new MediaStreamAudioFifo(input_format.channels(), + input_format.frames_per_buffer(), + processing_frames)); + + if (audio_processing_) { + output_bus_.reset(new MediaStreamAudioBus(output_format_.channels(), + output_frames)); + } + output_data_.reset(new int16[output_format_.GetBytesPerBuffer() / + sizeof(int16)]); } -void MediaStreamAudioProcessor::InitializeRenderConverterIfNeeded( +void MediaStreamAudioProcessor::InitializeRenderFifoIfNeeded( int sample_rate, int number_of_channels, int frames_per_buffer) { DCHECK(render_thread_checker_.CalledOnValidThread()); - // TODO(xians): Figure out if we need to handle the buffer size change. - if (render_converter_.get() && - render_converter_->source_parameters().sample_rate() == sample_rate && - render_converter_->source_parameters().channels() == number_of_channels) { - // Do nothing if the |render_converter_| has been setup properly. + if (render_fifo_.get() && + render_format_.sample_rate() == sample_rate && + render_format_.channels() == number_of_channels && + render_format_.frames_per_buffer() == frames_per_buffer) { + // Do nothing if the |render_fifo_| has been setup properly. return; } - // Create and initialize audio converter for the render data. - // webrtc::AudioProcessing accepts the same format as what it uses to process - // capture data, which is 32k mono for desktops and 16k mono for Android. - media::AudioParameters source_params( + render_format_ = media::AudioParameters( media::AudioParameters::AUDIO_PCM_LOW_LATENCY, - media::GuessChannelLayout(number_of_channels), sample_rate, 16, + media::GuessChannelLayout(number_of_channels), + sample_rate, + 16, frames_per_buffer); - media::AudioParameters sink_params( - media::AudioParameters::AUDIO_PCM_LOW_LATENCY, - media::CHANNEL_LAYOUT_MONO, kAudioProcessingSampleRate, 16, - kAudioProcessingSampleRate / 100); - render_converter_.reset( - new MediaStreamAudioConverter(source_params, sink_params)); - render_data_bus_ = media::AudioBus::Create(number_of_channels, - frames_per_buffer); + + const int analysis_frames = sample_rate / 100; // 10 ms chunks. + render_fifo_.reset( + new MediaStreamAudioFifo(number_of_channels, + frames_per_buffer, + analysis_frames)); } -int MediaStreamAudioProcessor::ProcessData(webrtc::AudioFrame* audio_frame, +int MediaStreamAudioProcessor::ProcessData(const float* const* process_ptrs, + int process_frames, base::TimeDelta capture_delay, int volume, - bool key_pressed) { + bool key_pressed, + float* const* output_ptrs) { + DCHECK(audio_processing_); DCHECK(capture_thread_checker_.CalledOnValidThread()); - if (!audio_processing_) - return 0; TRACE_EVENT0("audio", "MediaStreamAudioProcessor::ProcessData"); - DCHECK_EQ(audio_processing_->input_sample_rate_hz(), - capture_converter_->sink_parameters().sample_rate()); - DCHECK_EQ(audio_processing_->num_input_channels(), - capture_converter_->sink_parameters().channels()); - DCHECK_EQ(audio_processing_->num_output_channels(), - capture_converter_->sink_parameters().channels()); base::subtle::Atomic32 render_delay_ms = base::subtle::Acquire_Load(&render_delay_ms_); @@ -512,28 +546,34 @@ int MediaStreamAudioProcessor::ProcessData(webrtc::AudioFrame* audio_frame, << "ms; render delay: " << render_delay_ms << "ms"; } - audio_processing_->set_stream_delay_ms(total_delay_ms); + webrtc::AudioProcessing* ap = audio_processing_.get(); + ap->set_stream_delay_ms(total_delay_ms); DCHECK_LE(volume, WebRtcAudioDeviceImpl::kMaxVolumeLevel); - webrtc::GainControl* agc = audio_processing_->gain_control(); + webrtc::GainControl* agc = ap->gain_control(); int err = agc->set_stream_analog_level(volume); DCHECK_EQ(err, 0) << "set_stream_analog_level() error: " << err; - audio_processing_->set_stream_key_pressed(key_pressed); + ap->set_stream_key_pressed(key_pressed); - err = audio_processing_->ProcessStream(audio_frame); + err = ap->ProcessStream(process_ptrs, + process_frames, + input_format_.sample_rate(), + MapLayout(input_format_.channel_layout()), + output_format_.sample_rate(), + MapLayout(output_format_.channel_layout()), + output_ptrs); DCHECK_EQ(err, 0) << "ProcessStream() error: " << err; - if (typing_detector_ && - audio_frame->vad_activity_ != webrtc::AudioFrame::kVadUnknown) { - bool vad_active = - (audio_frame->vad_activity_ == webrtc::AudioFrame::kVadActive); - bool typing_detected = typing_detector_->Process(key_pressed, vad_active); - base::subtle::Release_Store(&typing_detected_, typing_detected); + if (typing_detector_) { + webrtc::VoiceDetection* vad = ap->voice_detection(); + DCHECK(vad->is_enabled()); + bool detected = typing_detector_->Process(key_pressed, + vad->stream_has_voice()); + base::subtle::Release_Store(&typing_detected_, detected); } - // Return 0 if the volume has not been changed, otherwise return the new - // volume. + // Return 0 if the volume hasn't been changed, and otherwise the new volume. return (agc->stream_analog_level() == volume) ? 0 : agc->stream_analog_level(); } diff --git a/content/renderer/media/media_stream_audio_processor.h b/content/renderer/media/media_stream_audio_processor.h index 8211fccf88210..3c44f808aedc0 100644 --- a/content/renderer/media/media_stream_audio_processor.h +++ b/content/renderer/media/media_stream_audio_processor.h @@ -35,6 +35,8 @@ class TypingDetection; namespace content { +class MediaStreamAudioBus; +class MediaStreamAudioFifo; class RTCMediaConstraints; using webrtc::AudioProcessorInterface; @@ -59,30 +61,32 @@ class CONTENT_EXPORT MediaStreamAudioProcessor : int effects, WebRtcPlayoutDataSource* playout_data_source); - // Called when format of the capture data has changed. - // Called on the main render thread. The caller is responsible for stopping + // Called when the format of the capture data has changed. + // Called on the main render thread. The caller is responsible for stopping // the capture thread before calling this method. // After this method, the capture thread will be changed to a new capture // thread. void OnCaptureFormatChanged(const media::AudioParameters& source_params); - // Pushes capture data in |audio_source| to the internal FIFO. + // Pushes capture data in |audio_source| to the internal FIFO. Each call to + // this method should be followed by calls to ProcessAndConsumeData() while + // it returns false, to pull out all available data. // Called on the capture audio thread. void PushCaptureData(const media::AudioBus* audio_source); // Processes a block of 10 ms data from the internal FIFO and outputs it via // |out|. |out| is the address of the pointer that will be pointed to // the post-processed data if the method is returning a true. The lifetime - // of the data represeted by |out| is guaranteed to outlive the method call. - // That also says *|out| won't change until this method is called again. + // of the data represeted by |out| is guaranteed until this method is called + // again. // |new_volume| receives the new microphone volume from the AGC. - // The new microphoen volume range is [0, 255], and the value will be 0 if + // The new microphone volume range is [0, 255], and the value will be 0 if // the microphone volume should not be adjusted. // Returns true if the internal FIFO has at least 10 ms data for processing, // otherwise false. - // |capture_delay|, |volume| and |key_pressed| will be passed to - // webrtc::AudioProcessing to help processing the data. // Called on the capture audio thread. + // + // TODO(ajm): Don't we want this to output float? bool ProcessAndConsumeData(base::TimeDelta capture_delay, int volume, bool key_pressed, @@ -93,10 +97,9 @@ class CONTENT_EXPORT MediaStreamAudioProcessor : // this method. void Stop(); - // The audio format of the input to the processor. + // The audio formats of the capture input to and output from the processor. + // Must only be called on the main render or audio capture threads. const media::AudioParameters& InputFormat() const; - - // The audio format of the output from the processor. const media::AudioParameters& OutputFormat() const; // Accessor to check if the audio processing is enabled or not. @@ -118,8 +121,6 @@ class CONTENT_EXPORT MediaStreamAudioProcessor : FRIEND_TEST_ALL_PREFIXES(MediaStreamAudioProcessorTest, GetAecDumpMessageFilter); - class MediaStreamAudioConverter; - // WebRtcPlayoutDataSource::Sink implementation. virtual void OnPlayoutData(media::AudioBus* audio_bus, int sample_rate, @@ -135,64 +136,63 @@ class CONTENT_EXPORT MediaStreamAudioProcessor : const blink::WebMediaConstraints& constraints, int effects); // Helper to initialize the capture converter. - void InitializeCaptureConverter(const media::AudioParameters& source_params); + void InitializeCaptureFifo(const media::AudioParameters& input_format); // Helper to initialize the render converter. - void InitializeRenderConverterIfNeeded(int sample_rate, - int number_of_channels, - int frames_per_buffer); + void InitializeRenderFifoIfNeeded(int sample_rate, + int number_of_channels, + int frames_per_buffer); // Called by ProcessAndConsumeData(). // Returns the new microphone volume in the range of |0, 255]. // When the volume does not need to be updated, it returns 0. - int ProcessData(webrtc::AudioFrame* audio_frame, + int ProcessData(const float* const* process_ptrs, + int process_frames, base::TimeDelta capture_delay, int volume, - bool key_pressed); + bool key_pressed, + float* const* output_ptrs); // Cached value for the render delay latency. This member is accessed by // both the capture audio thread and the render audio thread. base::subtle::Atomic32 render_delay_ms_; - // webrtc::AudioProcessing module which does AEC, AGC, NS, HighPass filter, - // ..etc. + // Module to handle processing and format conversion. scoped_ptr audio_processing_; - // Converter used for the down-mixing and resampling of the capture data. - scoped_ptr capture_converter_; - - // AudioFrame used to hold the output of |capture_converter_|. - webrtc::AudioFrame capture_frame_; - - // Converter used for the down-mixing and resampling of the render data when - // the AEC is enabled. - scoped_ptr render_converter_; + // FIFO to provide 10 ms capture chunks. + scoped_ptr capture_fifo_; + // Receives processing output. + scoped_ptr output_bus_; + // Receives interleaved int16 data for output. + scoped_ptr output_data_; - // AudioFrame used to hold the output of |render_converter_|. - webrtc::AudioFrame render_frame_; + // FIFO to provide 10 ms render chunks when the AEC is enabled. + scoped_ptr render_fifo_; - // Data bus to help converting interleaved data to an AudioBus. - scoped_ptr render_data_bus_; + // These are mutated on the main render thread in OnCaptureFormatChanged(). + // The caller guarantees this does not run concurrently with accesses on the + // capture audio thread. + media::AudioParameters input_format_; + media::AudioParameters output_format_; + // Only used on the render audio thread. + media::AudioParameters render_format_; // Raw pointer to the WebRtcPlayoutDataSource, which is valid for the // lifetime of RenderThread. WebRtcPlayoutDataSource* playout_data_source_; - // Used to DCHECK that the destructor is called on the main render thread. + // Used to DCHECK that some methods are called on the main render thread. base::ThreadChecker main_thread_checker_; - // Used to DCHECK that some methods are called on the capture audio thread. base::ThreadChecker capture_thread_checker_; - - // Used to DCHECK that PushRenderData() is called on the render audio thread. + // Used to DCHECK that some methods are called on the render audio thread. base::ThreadChecker render_thread_checker_; - // Flag to enable the stereo channels mirroring. + // Flag to enable stereo channel mirroring. bool audio_mirroring_; - // Used by the typing detection. scoped_ptr typing_detector_; - // This flag is used to show the result of typing detection. // It can be accessed by the capture audio thread and by the libjingle thread // which calls GetStats(). diff --git a/content/renderer/media/media_stream_audio_processor_options.cc b/content/renderer/media/media_stream_audio_processor_options.cc index d3299929056c0..9d2f5ee58c0c2 100644 --- a/content/renderer/media/media_stream_audio_processor_options.cc +++ b/content/renderer/media/media_stream_audio_processor_options.cc @@ -233,12 +233,6 @@ void EnableNoiseSuppression(AudioProcessing* audio_processing) { CHECK_EQ(err, 0); } -void EnableExperimentalNoiseSuppression(AudioProcessing* audio_processing) { - webrtc::Config config; - config.Set(new webrtc::ExperimentalNs(true)); - audio_processing->SetExtraOptions(config); -} - void EnableHighPassFilter(AudioProcessing* audio_processing) { CHECK_EQ(audio_processing->high_pass_filter()->Enable(true), 0); } @@ -254,12 +248,6 @@ void EnableTypingDetection(AudioProcessing* audio_processing, typing_detector->SetParameters(0, 0, 0, 0, 0, 100); } -void EnableExperimentalEchoCancellation(AudioProcessing* audio_processing) { - webrtc::Config config; - config.Set(new webrtc::DelayCorrection(true)); - audio_processing->SetExtraOptions(config); -} - void StartEchoCancellationDump(AudioProcessing* audio_processing, base::File aec_dump_file) { DCHECK(aec_dump_file.IsValid()); diff --git a/content/renderer/media/media_stream_audio_processor_options.h b/content/renderer/media/media_stream_audio_processor_options.h index 468355547f5d9..6f6526f18c8a4 100644 --- a/content/renderer/media/media_stream_audio_processor_options.h +++ b/content/renderer/media/media_stream_audio_processor_options.h @@ -94,9 +94,6 @@ void EnableEchoCancellation(AudioProcessing* audio_processing); // Enables the noise suppression in |audio_processing|. void EnableNoiseSuppression(AudioProcessing* audio_processing); -// Enables the experimental noise suppression in |audio_processing|. -void EnableExperimentalNoiseSuppression(AudioProcessing* audio_processing); - // Enables the high pass filter in |audio_processing|. void EnableHighPassFilter(AudioProcessing* audio_processing); @@ -104,9 +101,6 @@ void EnableHighPassFilter(AudioProcessing* audio_processing); void EnableTypingDetection(AudioProcessing* audio_processing, webrtc::TypingDetection* typing_detector); -// Enables the experimental echo cancellation in |audio_processing|. -void EnableExperimentalEchoCancellation(AudioProcessing* audio_processing); - // Starts the echo cancellation dump in |audio_processing|. void StartEchoCancellationDump(AudioProcessing* audio_processing, base::File aec_dump_file); diff --git a/content/renderer/media/media_stream_audio_source.cc b/content/renderer/media/media_stream_audio_source.cc index d98855220d860..86888f8b902a4 100644 --- a/content/renderer/media/media_stream_audio_source.cc +++ b/content/renderer/media/media_stream_audio_source.cc @@ -57,14 +57,14 @@ void MediaStreamAudioSource::AddTrack( // last track is removed from the source. But in this case, the source is // is not even started. So we need to fail both adding the track and // trigger |stop_callback|. - callback.Run(this, false); + callback.Run(this, MEDIA_DEVICE_TRACK_START_FAILURE, ""); StopSource(); return; } } factory_->CreateLocalAudioTrack(track); - callback.Run(this, true); + callback.Run(this, MEDIA_DEVICE_OK, ""); } } // namespace content diff --git a/content/renderer/media/media_stream_dispatcher.cc b/content/renderer/media/media_stream_dispatcher.cc index 9903bdf6c3dde..a3e5191abff57 100644 --- a/content/renderer/media/media_stream_dispatcher.cc +++ b/content/renderer/media/media_stream_dispatcher.cc @@ -140,8 +140,7 @@ void MediaStreamDispatcher::EnumerateDevices( int request_id, const base::WeakPtr& event_handler, MediaStreamType type, - const GURL& security_origin, - bool hide_labels_if_no_access) { + const GURL& security_origin) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK(type == MEDIA_DEVICE_AUDIO_CAPTURE || type == MEDIA_DEVICE_VIDEO_CAPTURE || @@ -158,8 +157,7 @@ void MediaStreamDispatcher::EnumerateDevices( Send(new MediaStreamHostMsg_EnumerateDevices(routing_id(), next_ipc_id_++, type, - security_origin, - hide_labels_if_no_access)); + security_origin)); } void MediaStreamDispatcher::StopEnumerateDevices( diff --git a/content/renderer/media/media_stream_dispatcher.h b/content/renderer/media/media_stream_dispatcher.h index c4e513600b9c4..799c2dd0dd297 100644 --- a/content/renderer/media/media_stream_dispatcher.h +++ b/content/renderer/media/media_stream_dispatcher.h @@ -55,15 +55,11 @@ class CONTENT_EXPORT MediaStreamDispatcher virtual void StopStreamDevice(const StreamDeviceInfo& device_info); // Request to enumerate devices. - // If |hide_labels_if_no_access| is true, labels will be empty in the - // response if permission has not been granted for the device type. This - // should normally be true. virtual void EnumerateDevices( int request_id, const base::WeakPtr& event_handler, MediaStreamType type, - const GURL& security_origin, - bool hide_labels_if_no_access); + const GURL& security_origin); // Request to stop enumerating devices. void StopEnumerateDevices( diff --git a/content/renderer/media/media_stream_dispatcher_unittest.cc b/content/renderer/media/media_stream_dispatcher_unittest.cc index 6e80f985d0010..85fe8d8713f12 100644 --- a/content/renderer/media/media_stream_dispatcher_unittest.cc +++ b/content/renderer/media/media_stream_dispatcher_unittest.cc @@ -227,15 +227,13 @@ TEST_F(MediaStreamDispatcherTest, BasicVideoDevice) { dispatcher->EnumerateDevices( kRequestId1, handler1.get()->AsWeakPtr(), kVideoType, - security_origin, - false); + security_origin); int ipc_request_id2 = dispatcher->next_ipc_id_; EXPECT_NE(ipc_request_id1, ipc_request_id2); dispatcher->EnumerateDevices( kRequestId2, handler2.get()->AsWeakPtr(), kVideoType, - security_origin, - false); + security_origin); EXPECT_EQ(dispatcher->requests_.size(), size_t(2)); StreamDeviceInfoArray video_device_array(1); diff --git a/content/renderer/media/media_stream_impl.cc b/content/renderer/media/media_stream_impl.cc index 3cbdc8f05e4a5..820f16e00e7ae 100644 --- a/content/renderer/media/media_stream_impl.cc +++ b/content/renderer/media/media_stream_impl.cc @@ -234,22 +234,19 @@ void MediaStreamImpl::requestMediaDevices( audio_input_request_id, weak_factory_.GetWeakPtr(), MEDIA_DEVICE_AUDIO_CAPTURE, - security_origin, - true); + security_origin); media_stream_dispatcher_->EnumerateDevices( video_input_request_id, weak_factory_.GetWeakPtr(), MEDIA_DEVICE_VIDEO_CAPTURE, - security_origin, - true); + security_origin); media_stream_dispatcher_->EnumerateDevices( audio_output_request_id, weak_factory_.GetWeakPtr(), MEDIA_DEVICE_AUDIO_OUTPUT, - security_origin, - true); + security_origin); } void MediaStreamImpl::cancelMediaDevicesRequest( @@ -342,7 +339,7 @@ void MediaStreamImpl::OnStreamGeneratedForCancelledRequest( // The requested stream failed to be generated. void MediaStreamImpl::OnStreamGenerationFailed( int request_id, - content::MediaStreamRequestResult result) { + MediaStreamRequestResult result) { DCHECK(CalledOnValidThread()); DVLOG(1) << "MediaStreamImpl::OnStreamGenerationFailed(" << request_id << ")"; @@ -500,14 +497,17 @@ void MediaStreamImpl::CreateAudioTracks( void MediaStreamImpl::OnCreateNativeTracksCompleted( UserMediaRequestInfo* request, - content::MediaStreamRequestResult result) { + MediaStreamRequestResult result, + const blink::WebString& result_name) { DVLOG(1) << "MediaStreamImpl::OnCreateNativeTracksComplete(" << "{request_id = " << request->request_id << "} " << "{result = " << result << "})"; if (result == content::MEDIA_DEVICE_OK) GetUserMediaRequestSucceeded(request->web_stream, &request->request); else - GetUserMediaRequestFailed(&request->request, result); + GetUserMediaRequestTrackStartedFailed(&request->request, + result, + result_name); DeleteUserMediaRequestInfo(request); } @@ -613,7 +613,7 @@ void MediaStreamImpl::GetUserMediaRequestSucceeded( void MediaStreamImpl::GetUserMediaRequestFailed( blink::WebUserMediaRequest* request_info, - content::MediaStreamRequestResult result) { + MediaStreamRequestResult result) { LogUserMediaRequestResult(result); switch (result) { case MEDIA_DEVICE_OK: @@ -643,10 +643,26 @@ void MediaStreamImpl::GetUserMediaRequestFailed( case MEDIA_DEVICE_CAPTURE_FAILURE: request_info->requestFailedUASpecific("DeviceCaptureError"); break; + default: + NOTREACHED(); + request_info->requestFailed(); + break; + } +} + +void MediaStreamImpl::GetUserMediaRequestTrackStartedFailed( + blink::WebUserMediaRequest* request_info, + MediaStreamRequestResult result, + const blink::WebString& result_name) { + switch (result) { + case MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED: + request_info->requestFailedConstraint(result_name); + break; case MEDIA_DEVICE_TRACK_START_FAILURE: request_info->requestFailedUASpecific("TrackStartError"); break; default: + NOTREACHED(); request_info->requestFailed(); break; } @@ -832,7 +848,8 @@ MediaStreamImpl::UserMediaRequestInfo::UserMediaRequestInfo( enable_automatic_output_device_selection( enable_automatic_output_device_selection), request(request), - request_failed_(false) { + request_result_(MEDIA_DEVICE_OK), + request_result_name_("") { } MediaStreamImpl::UserMediaRequestInfo::~UserMediaRequestInfo() { @@ -880,8 +897,10 @@ void MediaStreamImpl::UserMediaRequestInfo::CallbackOnTracksStarted( } void MediaStreamImpl::UserMediaRequestInfo::OnTrackStarted( - MediaStreamSource* source, bool success) { - DVLOG(1) << "OnTrackStarted result " << success; + MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name) { + DVLOG(1) << "OnTrackStarted result " << result; std::vector::iterator it = std::find(sources_waiting_for_callback_.begin(), sources_waiting_for_callback_.end(), @@ -890,16 +909,17 @@ void MediaStreamImpl::UserMediaRequestInfo::OnTrackStarted( sources_waiting_for_callback_.erase(it); // All tracks must be started successfully. Otherwise the request is a // failure. - if (!success) - request_failed_ = true; + if (result != MEDIA_DEVICE_OK) { + request_result_ = result; + request_result_name_ = result_name; + } + CheckAllTracksStarted(); } void MediaStreamImpl::UserMediaRequestInfo::CheckAllTracksStarted() { if (!ready_callback_.is_null() && sources_waiting_for_callback_.empty()) { - ready_callback_.Run( - this, - request_failed_ ? MEDIA_DEVICE_TRACK_START_FAILURE : MEDIA_DEVICE_OK); + ready_callback_.Run(this, request_result_, request_result_name_); } } diff --git a/content/renderer/media/media_stream_impl.h b/content/renderer/media/media_stream_impl.h index 87fedd70e74a0..d96ef27416720 100644 --- a/content/renderer/media/media_stream_impl.h +++ b/content/renderer/media/media_stream_impl.h @@ -73,7 +73,7 @@ class CONTENT_EXPORT MediaStreamImpl const StreamDeviceInfoArray& video_array) OVERRIDE; virtual void OnStreamGenerationFailed( int request_id, - content::MediaStreamRequestResult result) OVERRIDE; + MediaStreamRequestResult result) OVERRIDE; virtual void OnDeviceStopped(const std::string& label, const StreamDeviceInfo& device_info) OVERRIDE; virtual void OnDevicesEnumerated( @@ -100,7 +100,11 @@ class CONTENT_EXPORT MediaStreamImpl blink::WebUserMediaRequest* request_info); virtual void GetUserMediaRequestFailed( blink::WebUserMediaRequest* request_info, - content::MediaStreamRequestResult result); + MediaStreamRequestResult result); + virtual void GetUserMediaRequestTrackStartedFailed( + blink::WebUserMediaRequest* request_info, + MediaStreamRequestResult result, + const blink::WebString& result_name); virtual void EnumerateDevicesSucceded( blink::WebMediaDevicesRequest* request, blink::WebVector& devices); @@ -117,7 +121,8 @@ class CONTENT_EXPORT MediaStreamImpl : public base::SupportsWeakPtr { public: typedef base::Callback + MediaStreamRequestResult result, + const blink::WebString& result_name)> ResourcesReady; UserMediaRequestInfo(int request_id, @@ -149,11 +154,15 @@ class CONTENT_EXPORT MediaStreamImpl bool HasPendingSources() const; private: - void OnTrackStarted(MediaStreamSource* source, bool success); + void OnTrackStarted( + MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name); void CheckAllTracksStarted(); ResourcesReady ready_callback_; - bool request_failed_; + MediaStreamRequestResult request_result_; + blink::WebString request_result_name_; // Sources used in this request. std::vector sources_; std::vector sources_waiting_for_callback_; @@ -189,7 +198,8 @@ class CONTENT_EXPORT MediaStreamImpl // underlying media sources and tracks have been created and started. void OnCreateNativeTracksCompleted( UserMediaRequestInfo* request, - content::MediaStreamRequestResult result); + MediaStreamRequestResult result, + const blink::WebString& result_name); void OnStreamGeneratedForCancelledRequest( const StreamDeviceInfoArray& audio_array, diff --git a/content/renderer/media/media_stream_impl_unittest.cc b/content/renderer/media/media_stream_impl_unittest.cc index 997340a0b6278..c94a48762028b 100644 --- a/content/renderer/media/media_stream_impl_unittest.cc +++ b/content/renderer/media/media_stream_impl_unittest.cc @@ -50,6 +50,7 @@ class MediaStreamImplUnderTest : public MediaStreamImpl { NULL, dependency_factory, media_stream_dispatcher.Pass()), state_(REQUEST_NOT_STARTED), result_(NUM_MEDIA_REQUEST_RESULTS), + result_name_(""), factory_(dependency_factory), video_source_(NULL) { } @@ -81,6 +82,16 @@ class MediaStreamImplUnderTest : public MediaStreamImpl { result_ = result; } + virtual void GetUserMediaRequestTrackStartedFailed( + blink::WebUserMediaRequest* request_info, + MediaStreamRequestResult result, + const blink::WebString& result_name) OVERRIDE { + last_generated_stream_.reset(); + state_ = REQUEST_FAILED; + result_ = result; + result_name_ = result_name; + } + virtual void EnumerateDevicesSucceded( blink::WebMediaDevicesRequest* request, blink::WebVector& devices) OVERRIDE { @@ -115,11 +126,13 @@ class MediaStreamImplUnderTest : public MediaStreamImpl { RequestState request_state() const { return state_; } content::MediaStreamRequestResult error_reason() const { return result_; } + blink::WebString error_name() const { return result_name_; } private: blink::WebMediaStream last_generated_stream_; RequestState state_; content::MediaStreamRequestResult result_; + blink::WebString result_name_; blink::WebVector last_devices_; PeerConnectionDependencyFactory* factory_; MockMediaStreamVideoCapturerSource* video_source_; diff --git a/content/renderer/media/media_stream_source.h b/content/renderer/media/media_stream_source.h index 18821c38cf5a4..05aae2ad56aad 100644 --- a/content/renderer/media/media_stream_source.h +++ b/content/renderer/media/media_stream_source.h @@ -25,7 +25,9 @@ class CONTENT_EXPORT MediaStreamSource SourceStoppedCallback; typedef base::Callback ConstraintsCallback; + MediaStreamRequestResult result, + const blink::WebString& result_name)> + ConstraintsCallback; // Source constraints key for // http://dev.w3.org/2011/webrtc/editor/getusermedia.html. diff --git a/content/renderer/media/media_stream_video_capture_source_unittest.cc b/content/renderer/media/media_stream_video_capture_source_unittest.cc index a01db9e2e43b7..8492039a7bd6a 100644 --- a/content/renderer/media/media_stream_video_capture_source_unittest.cc +++ b/content/renderer/media/media_stream_video_capture_source_unittest.cc @@ -77,7 +77,9 @@ class MediaStreamVideoCapturerSourceTest : public testing::Test { } protected: - void OnConstraintsApplied(MediaStreamSource* source, bool success) { + void OnConstraintsApplied(MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name) { } base::MessageLoopForUI message_loop_; @@ -184,7 +186,7 @@ TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTime) { testing::SaveArg<2>(&running_cb))); EXPECT_CALL(mock_delegate(), StopCapture()); blink::WebMediaStreamTrack track = StartSource(); - running_cb.Run(true); + running_cb.Run(MEDIA_DEVICE_OK); base::RunLoop run_loop; base::TimeTicks reference_capture_time = diff --git a/content/renderer/media/media_stream_video_capturer_source.cc b/content/renderer/media/media_stream_video_capturer_source.cc index d322eb4051c61..1d82b12280d34 100644 --- a/content/renderer/media/media_stream_video_capturer_source.cc +++ b/content/renderer/media/media_stream_video_capturer_source.cc @@ -139,11 +139,12 @@ void VideoCapturerDelegate::OnStateUpdateOnRenderThread( DCHECK(thread_checker_.CalledOnValidThread()); DVLOG(3) << "OnStateUpdateOnRenderThread state = " << state; if (state == VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) { - running_callback_.Run(true); + running_callback_.Run(MEDIA_DEVICE_OK); return; } if (state > VIDEO_CAPTURE_STATE_STARTED && !running_callback_.is_null()) { - base::ResetAndReturn(&running_callback_).Run(false); + base::ResetAndReturn(&running_callback_).Run( + MEDIA_DEVICE_TRACK_START_FAILURE); } } diff --git a/content/renderer/media/media_stream_video_capturer_source.h b/content/renderer/media/media_stream_video_capturer_source.h index fff2dcc7938aa..a0dd791529c39 100644 --- a/content/renderer/media/media_stream_video_capturer_source.h +++ b/content/renderer/media/media_stream_video_capturer_source.h @@ -22,10 +22,9 @@ namespace content { class CONTENT_EXPORT VideoCapturerDelegate : public base::RefCountedThreadSafe { public: - typedef base::Callback RunningCallback; + typedef base::Callback RunningCallback; - explicit VideoCapturerDelegate( - const StreamDeviceInfo& device_info); + explicit VideoCapturerDelegate(const StreamDeviceInfo& device_info); // Collects the formats that can currently be used. // |max_requested_height|, |max_requested_width|, and diff --git a/content/renderer/media/media_stream_video_source.cc b/content/renderer/media/media_stream_video_source.cc index d6f8b8a405eff..55b855c1153d2 100644 --- a/content/renderer/media/media_stream_video_source.cc +++ b/content/renderer/media/media_stream_video_source.cc @@ -43,6 +43,7 @@ const char* kSupportedConstraints[] = { const int MediaStreamVideoSource::kDefaultWidth = 640; const int MediaStreamVideoSource::kDefaultHeight = 480; const int MediaStreamVideoSource::kDefaultFrameRate = 30; +const int MediaStreamVideoSource::kUnknownFrameRate = 0; namespace { @@ -220,7 +221,8 @@ void FilterFormatsByConstraint( // Returns the media::VideoCaptureFormats that matches |constraints|. media::VideoCaptureFormats FilterFormats( const blink::WebMediaConstraints& constraints, - const media::VideoCaptureFormats& supported_formats) { + const media::VideoCaptureFormats& supported_formats, + blink::WebString* unsatisfied_constraint) { if (constraints.isNull()) { return supported_formats; } @@ -270,8 +272,13 @@ media::VideoCaptureFormats FilterFormats( constraints.getMandatoryConstraints(mandatory); constraints.getOptionalConstraints(optional); media::VideoCaptureFormats candidates = supported_formats; - for (size_t i = 0; i < mandatory.size(); ++i) + for (size_t i = 0; i < mandatory.size(); ++i) { FilterFormatsByConstraint(mandatory[i], true, &candidates); + if (candidates.empty()) { + *unsatisfied_constraint = mandatory[i].m_name; + return candidates; + } + } if (candidates.empty()) return candidates; @@ -507,8 +514,9 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints( *best_format = media::VideoCaptureFormat(); return true; } + blink::WebString unsatisfied_constraint; media::VideoCaptureFormats filtered_formats = - FilterFormats(requested_constraints, formats); + FilterFormats(requested_constraints, formats, &unsatisfied_constraint); if (filtered_formats.size() > 0) { // A request with constraints that can be fulfilled. GetBestCaptureFormat(filtered_formats, @@ -520,10 +528,10 @@ bool MediaStreamVideoSource::FindBestFormatWithConstraints( return false; } -void MediaStreamVideoSource::OnStartDone(bool success) { +void MediaStreamVideoSource::OnStartDone(MediaStreamRequestResult result) { DCHECK(CalledOnValidThread()); - DVLOG(3) << "OnStartDone({success =" << success << "})"; - if (success) { + DVLOG(3) << "OnStartDone({result =" << result << "})"; + if (result == MEDIA_DEVICE_OK) { DCHECK_EQ(STARTING, state_); state_ = STARTED; SetReadyState(blink::WebMediaStreamSource::ReadyStateLive); @@ -545,17 +553,18 @@ void MediaStreamVideoSource::FinalizeAddTrack() { callbacks.swap(requested_constraints_); for (std::vector::iterator it = callbacks.begin(); it != callbacks.end(); ++it) { - // The track has been added successfully if the source has started and - // there are either no mandatory constraints and the source doesn't expose - // its format capabilities, or the constraints and the format match. - // For example, a remote source doesn't expose its format capabilities. - bool success = - state_ == STARTED && - ((!current_format_.IsValid() && !HasMandatoryConstraints( - it->constraints)) || - !FilterFormats(it->constraints, formats).empty()); - - if (success) { + MediaStreamRequestResult result = MEDIA_DEVICE_OK; + blink::WebString unsatisfied_constraint; + + if (HasMandatoryConstraints(it->constraints) && + FilterFormats(it->constraints, formats, + &unsatisfied_constraint).empty()) + result = MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED; + + if (state_ != STARTED && result == MEDIA_DEVICE_OK) + result = MEDIA_DEVICE_TRACK_START_FAILURE; + + if (result == MEDIA_DEVICE_OK) { int max_width; int max_height; GetDesiredMaxWidthAndHeight(it->constraints, &max_width, &max_height); @@ -579,10 +588,11 @@ void MediaStreamVideoSource::FinalizeAddTrack() { on_mute_callback); } - DVLOG(3) << "FinalizeAddTrack() success " << success; + DVLOG(3) << "FinalizeAddTrack() result " << result; - if (!it->callback.is_null()) - it->callback.Run(this, success); + if (!it->callback.is_null()) { + it->callback.Run(this, result, unsatisfied_constraint); + } } } diff --git a/content/renderer/media/media_stream_video_source.h b/content/renderer/media/media_stream_video_source.h index 19f000eaa551c..3aed40a84cd83 100644 --- a/content/renderer/media/media_stream_video_source.h +++ b/content/renderer/media/media_stream_video_source.h @@ -81,6 +81,7 @@ class CONTENT_EXPORT MediaStreamVideoSource static const int kDefaultWidth; static const int kDefaultHeight; static const int kDefaultFrameRate; + static const int kUnknownFrameRate; protected: virtual void DoStopSource() OVERRIDE; @@ -112,7 +113,7 @@ class CONTENT_EXPORT MediaStreamVideoSource virtual void StartSourceImpl( const media::VideoCaptureParams& params, const VideoCaptureDeliverFrameCB& frame_callback) = 0; - void OnStartDone(bool success); + void OnStartDone(MediaStreamRequestResult result); // An implementation must immediately stop capture video frames and must not // call OnSupportedFormats after this method has been called. After this diff --git a/content/renderer/media/media_stream_video_source_unittest.cc b/content/renderer/media/media_stream_video_source_unittest.cc index efbfbbdd1cae0..28816f1641edf 100644 --- a/content/renderer/media/media_stream_video_source_unittest.cc +++ b/content/renderer/media/media_stream_video_source_unittest.cc @@ -36,6 +36,8 @@ class MediaStreamVideoSourceTest : child_process_(new ChildProcess()), number_of_successful_constraints_applied_(0), number_of_failed_constraints_applied_(0), + result_(MEDIA_DEVICE_OK), + result_name_(""), mock_source_(new MockMediaStreamVideoSource(true)) { media::VideoCaptureFormats formats; formats.push_back(media::VideoCaptureFormat( @@ -96,6 +98,9 @@ class MediaStreamVideoSourceTest return number_of_failed_constraints_applied_; } + content::MediaStreamRequestResult error_type() const { return result_; } + blink::WebString error_name() const { return result_name_; } + MockMediaStreamVideoSource* mock_source() { return mock_source_; } // Test that the source crops/scales to the requested width and @@ -199,13 +204,18 @@ class MediaStreamVideoSourceTest } private: - void OnConstraintsApplied(MediaStreamSource* source, bool success) { + void OnConstraintsApplied(MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name) { ASSERT_EQ(source, webkit_source_.extraData()); - if (success) + if (result == MEDIA_DEVICE_OK) { ++number_of_successful_constraints_applied_; - else + } else { + result_ = result; + result_name_ = result_name; ++number_of_failed_constraints_applied_; + } if (!track_to_release_.isNull()) { mock_source_ = NULL; @@ -218,6 +228,8 @@ class MediaStreamVideoSourceTest blink::WebMediaStreamTrack track_to_release_; int number_of_successful_constraints_applied_; int number_of_failed_constraints_applied_; + content::MediaStreamRequestResult result_; + blink::WebString result_name_; blink::WebMediaStreamSource webkit_source_; // |mock_source_| is owned by |webkit_source_|. MockMediaStreamVideoSource* mock_source_; @@ -440,6 +452,8 @@ TEST_F(MediaStreamVideoSourceTest, InvalidMandatoryConstraint) { blink::WebMediaStreamTrack track = CreateTrack( "123", factory.CreateWebMediaConstraints()); mock_source()->CompleteGetSupportedFormats(); + EXPECT_EQ(MEDIA_DEVICE_CONSTRAINT_NOT_SATISFIED, error_type()); + EXPECT_EQ("weird key", error_name()); EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks()); } diff --git a/content/renderer/media/mock_media_stream_dispatcher.cc b/content/renderer/media/mock_media_stream_dispatcher.cc index 32ac1de61d358..7854a8d6574bb 100644 --- a/content/renderer/media/mock_media_stream_dispatcher.cc +++ b/content/renderer/media/mock_media_stream_dispatcher.cc @@ -59,8 +59,7 @@ void MockMediaStreamDispatcher::EnumerateDevices( int request_id, const base::WeakPtr& event_handler, MediaStreamType type, - const GURL& security_origin, - bool hide_labels_if_no_access) { + const GURL& security_origin) { if (type == MEDIA_DEVICE_AUDIO_CAPTURE) { audio_input_request_id_ = request_id; audio_input_array_.clear(); diff --git a/content/renderer/media/mock_media_stream_dispatcher.h b/content/renderer/media/mock_media_stream_dispatcher.h index eed1f5720519e..1548853a452ac 100644 --- a/content/renderer/media/mock_media_stream_dispatcher.h +++ b/content/renderer/media/mock_media_stream_dispatcher.h @@ -31,8 +31,7 @@ class MockMediaStreamDispatcher : public MediaStreamDispatcher { int request_id, const base::WeakPtr& event_handler, MediaStreamType type, - const GURL& security_origin, - bool hide_labels_if_no_access) OVERRIDE; + const GURL& security_origin) OVERRIDE; virtual void StopStreamDevice(const StreamDeviceInfo& device_info) OVERRIDE; virtual bool IsStream(const std::string& label) OVERRIDE; virtual int video_session_id(const std::string& label, int index) OVERRIDE; diff --git a/content/renderer/media/mock_media_stream_video_source.cc b/content/renderer/media/mock_media_stream_video_source.cc index 2e9b6a123775c..790d1355d0a12 100644 --- a/content/renderer/media/mock_media_stream_video_source.cc +++ b/content/renderer/media/mock_media_stream_video_source.cc @@ -30,13 +30,13 @@ MockMediaStreamVideoSource::~MockMediaStreamVideoSource() {} void MockMediaStreamVideoSource::StartMockedSource() { DCHECK(attempted_to_start_); attempted_to_start_ = false; - OnStartDone(true); + OnStartDone(MEDIA_DEVICE_OK); } void MockMediaStreamVideoSource::FailToStartMockedSource() { DCHECK(attempted_to_start_); attempted_to_start_ = false; - OnStartDone(false); + OnStartDone(MEDIA_DEVICE_TRACK_START_FAILURE); } void MockMediaStreamVideoSource::CompleteGetSupportedFormats() { diff --git a/content/renderer/media/peer_connection_tracker.cc b/content/renderer/media/peer_connection_tracker.cc index bead220fa2f2b..5aac78c3fd50b 100644 --- a/content/renderer/media/peer_connection_tracker.cc +++ b/content/renderer/media/peer_connection_tracker.cc @@ -214,7 +214,7 @@ static base::DictionaryValue* GetDictValueStats( dict->Set("values", values); for (size_t i = 0; i < report.values.size(); ++i) { - values->AppendString(report.values[i].name); + values->AppendString(report.values[i].display_name()); values->AppendString(report.values[i].value); } return dict; diff --git a/content/renderer/media/rtc_peer_connection_handler.cc b/content/renderer/media/rtc_peer_connection_handler.cc index 0f92bb6e9f0f7..ddb07ac54ae9f 100644 --- a/content/renderer/media/rtc_peer_connection_handler.cc +++ b/content/renderer/media/rtc_peer_connection_handler.cc @@ -281,12 +281,11 @@ class StatsResponse : public webrtc::StatsObserver { for (webrtc::StatsReport::Values::const_iterator value_it = report.values.begin(); value_it != report.values.end(); ++value_it) { - AddStatistic(idx, value_it->name, value_it->value); + AddStatistic(idx, value_it->display_name(), value_it->value); } } - void AddStatistic(int idx, const std::string& name, - const std::string& value) { + void AddStatistic(int idx, const char* name, const std::string& value) { response_->addStatistic(idx, blink::WebString::fromUTF8(name), blink::WebString::fromUTF8(value)); diff --git a/content/renderer/media/rtc_video_decoder.cc b/content/renderer/media/rtc_video_decoder.cc index 4e4b3199683e7..60ae893d9d4c5 100644 --- a/content/renderer/media/rtc_video_decoder.cc +++ b/content/renderer/media/rtc_video_decoder.cc @@ -61,13 +61,9 @@ RTCVideoDecoder::SHMBuffer::~SHMBuffer() { shm->Close(); } RTCVideoDecoder::BufferData::BufferData(int32 bitstream_buffer_id, uint32_t timestamp, - int width, - int height, size_t size) : bitstream_buffer_id(bitstream_buffer_id), timestamp(timestamp), - width(width), - height(height), size(size) {} RTCVideoDecoder::BufferData::BufferData() {} @@ -75,8 +71,10 @@ RTCVideoDecoder::BufferData::BufferData() {} RTCVideoDecoder::BufferData::~BufferData() {} RTCVideoDecoder::RTCVideoDecoder( + webrtc::VideoCodecType type, const scoped_refptr& factories) - : factories_(factories), + : video_codec_type_(type), + factories_(factories), decoder_texture_target_(0), next_picture_buffer_id_(0), state_(UNINITIALIZED), @@ -118,7 +116,10 @@ scoped_ptr RTCVideoDecoder::Create( media::VideoCodecProfile profile; switch (type) { case webrtc::kVideoCodecVP8: - profile = media::VP8PROFILE_MAIN; + profile = media::VP8PROFILE_ANY; + break; + case webrtc::kVideoCodecH264: + profile = media::H264PROFILE_MAIN; break; default: DVLOG(2) << "Video codec not supported:" << type; @@ -126,7 +127,7 @@ scoped_ptr RTCVideoDecoder::Create( } base::WaitableEvent waiter(true, false); - decoder.reset(new RTCVideoDecoder(factories)); + decoder.reset(new RTCVideoDecoder(type, factories)); decoder->factories_->GetTaskRunner()->PostTask( FROM_HERE, base::Bind(&RTCVideoDecoder::CreateVDA, @@ -134,7 +135,7 @@ scoped_ptr RTCVideoDecoder::Create( profile, &waiter)); waiter.Wait(); - // vda can be NULL if VP8 is not supported. + // vda can be NULL if the codec is not supported. if (decoder->vda_ != NULL) { decoder->state_ = INITIALIZED; } else { @@ -146,8 +147,9 @@ scoped_ptr RTCVideoDecoder::Create( int32_t RTCVideoDecoder::InitDecode(const webrtc::VideoCodec* codecSettings, int32_t /*numberOfCores*/) { DVLOG(2) << "InitDecode"; - DCHECK_EQ(codecSettings->codecType, webrtc::kVideoCodecVP8); - if (codecSettings->codecSpecific.VP8.feedbackModeOn) { + DCHECK_EQ(video_codec_type_, codecSettings->codecType); + if (codecSettings->codecType == webrtc::kVideoCodecVP8 && + codecSettings->codecSpecific.VP8.feedbackModeOn) { LOG(ERROR) << "Feedback mode not supported"; return RecordInitDecodeUMA(WEBRTC_VIDEO_CODEC_ERROR); } @@ -227,8 +229,6 @@ int32_t RTCVideoDecoder::Decode( // Create buffer metadata. BufferData buffer_data(next_bitstream_buffer_id_, inputImage._timeStamp, - frame_size_.width(), - frame_size_.height(), inputImage._length); // Mask against 30 bits, to avoid (undefined) wraparound on signed integer. next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & ID_LAST; @@ -364,13 +364,21 @@ void RTCVideoDecoder::PictureReady(const media::Picture& picture) { } const media::PictureBuffer& pb = it->second; + // Validate picture rectangle from GPU. + if (picture.visible_rect().IsEmpty() || + !gfx::Rect(pb.size()).Contains(picture.visible_rect())) { + NOTREACHED() << "Invalid picture size from VDA: " + << picture.visible_rect().ToString() << " should fit in " + << pb.size().ToString(); + NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); + return; + } + // Create a media::VideoFrame. - uint32_t timestamp = 0, width = 0, height = 0; - size_t size = 0; - GetBufferData( - picture.bitstream_buffer_id(), ×tamp, &width, &height, &size); + uint32_t timestamp = 0; + GetBufferData(picture.bitstream_buffer_id(), ×tamp); scoped_refptr frame = - CreateVideoFrame(picture, pb, timestamp, width, height, size); + CreateVideoFrame(picture, pb, timestamp); bool inserted = picture_buffers_at_display_.insert(std::make_pair( picture.picture_buffer_id(), @@ -380,7 +388,11 @@ void RTCVideoDecoder::PictureReady(const media::Picture& picture) { // Create a WebRTC video frame. webrtc::RefCountImpl* handle = new webrtc::RefCountImpl(frame); - webrtc::TextureVideoFrame decoded_image(handle, width, height, timestamp, 0); + webrtc::TextureVideoFrame decoded_image(handle, + picture.visible_rect().width(), + picture.visible_rect().height(), + timestamp, + 0); // Invoke decode callback. WebRTC expects no callback after Reset or Release. { @@ -423,11 +435,8 @@ static void ReadPixelsSync( scoped_refptr RTCVideoDecoder::CreateVideoFrame( const media::Picture& picture, const media::PictureBuffer& pb, - uint32_t timestamp, - uint32_t width, - uint32_t height, - size_t size) { - gfx::Rect visible_rect(width, height); + uint32_t timestamp) { + gfx::Rect visible_rect(picture.visible_rect()); DCHECK(decoder_texture_target_); // Convert timestamp from 90KHz to ms. base::TimeDelta timestamp_ms = base::TimeDelta::FromInternalValue( @@ -777,18 +786,13 @@ void RTCVideoDecoder::RecordBufferData(const BufferData& buffer_data) { } void RTCVideoDecoder::GetBufferData(int32 bitstream_buffer_id, - uint32_t* timestamp, - uint32_t* width, - uint32_t* height, - size_t* size) { + uint32_t* timestamp) { for (std::list::iterator it = input_buffer_data_.begin(); it != input_buffer_data_.end(); ++it) { if (it->bitstream_buffer_id != bitstream_buffer_id) continue; *timestamp = it->timestamp; - *width = it->width; - *height = it->height; return; } NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; diff --git a/content/renderer/media/rtc_video_decoder.h b/content/renderer/media/rtc_video_decoder.h index 0c8e45beda3a8..8c8a6e3685ada 100644 --- a/content/renderer/media/rtc_video_decoder.h +++ b/content/renderer/media/rtc_video_decoder.h @@ -91,15 +91,11 @@ class CONTENT_EXPORT RTCVideoDecoder struct BufferData { BufferData(int32 bitstream_buffer_id, uint32_t timestamp, - int width, - int height, size_t size); BufferData(); ~BufferData(); int32 bitstream_buffer_id; uint32_t timestamp; // in 90KHz - uint32_t width; - uint32_t height; size_t size; // buffer size }; @@ -107,6 +103,7 @@ class CONTENT_EXPORT RTCVideoDecoder FRIEND_TEST_ALL_PREFIXES(RTCVideoDecoderTest, IsFirstBufferAfterReset); RTCVideoDecoder( + webrtc::VideoCodecType type, const scoped_refptr& factories); // Requests a buffer to be decoded by VDA. @@ -138,10 +135,7 @@ class CONTENT_EXPORT RTCVideoDecoder scoped_refptr CreateVideoFrame( const media::Picture& picture, const media::PictureBuffer& pb, - uint32_t timestamp, - uint32_t width, - uint32_t height, - size_t size); + uint32_t timestamp); // Resets VDA. void ResetInternal(); @@ -176,11 +170,7 @@ class CONTENT_EXPORT RTCVideoDecoder // Stores the buffer metadata to |input_buffer_data_|. void RecordBufferData(const BufferData& buffer_data); // Gets the buffer metadata from |input_buffer_data_|. - void GetBufferData(int32 bitstream_buffer_id, - uint32_t* timestamp, - uint32_t* width, - uint32_t* height, - size_t* size); + void GetBufferData(int32 bitstream_buffer_id, uint32_t* timestamp); // Records the result of InitDecode to UMA and returns |status|. int32_t RecordInitDecodeUMA(int32_t status); @@ -202,6 +192,9 @@ class CONTENT_EXPORT RTCVideoDecoder // The hardware video decoder. scoped_ptr vda_; + // The video codec type, as reported by WebRTC. + const webrtc::VideoCodecType video_codec_type_; + // The size of the incoming video frames. gfx::Size frame_size_; diff --git a/content/renderer/media/rtc_video_decoder_unittest.cc b/content/renderer/media/rtc_video_decoder_unittest.cc index e6dfe3eeaa18a..a1e6341f09335 100644 --- a/content/renderer/media/rtc_video_decoder_unittest.cc +++ b/content/renderer/media/rtc_video_decoder_unittest.cc @@ -45,8 +45,6 @@ class RTCVideoDecoderTest : public ::testing::Test, .Times(1) .WillRepeatedly(Return(true)); EXPECT_CALL(*mock_vda_, Destroy()).Times(1); - rtc_decoder_ = - RTCVideoDecoder::Create(webrtc::kVideoCodecVP8, mock_gpu_factories_); } virtual void TearDown() OVERRIDE { @@ -65,9 +63,15 @@ class RTCVideoDecoderTest : public ::testing::Test, return WEBRTC_VIDEO_CODEC_OK; } + void CreateDecoder(webrtc::VideoCodecType codec_type) { + VLOG(2) << "CreateDecoder"; + codec_.codecType = codec_type; + rtc_decoder_ = + RTCVideoDecoder::Create(codec_type, mock_gpu_factories_); + } + void Initialize() { VLOG(2) << "Initialize"; - codec_.codecType = webrtc::kVideoCodecVP8; EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1)); EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->RegisterDecodeCompleteCallback(this)); @@ -104,24 +108,32 @@ class RTCVideoDecoderTest : public ::testing::Test, }; TEST_F(RTCVideoDecoderTest, CreateReturnsNullOnUnsupportedCodec) { + CreateDecoder(webrtc::kVideoCodecVP8); scoped_ptr null_rtc_decoder( RTCVideoDecoder::Create(webrtc::kVideoCodecI420, mock_gpu_factories_)); EXPECT_EQ(NULL, null_rtc_decoder.get()); } +TEST_F(RTCVideoDecoderTest, CreateAndInitSucceedsForH264Codec) { + CreateDecoder(webrtc::kVideoCodecH264); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, rtc_decoder_->InitDecode(&codec_, 1)); +} + TEST_F(RTCVideoDecoderTest, InitDecodeReturnsErrorOnFeedbackMode) { - codec_.codecType = webrtc::kVideoCodecVP8; + CreateDecoder(webrtc::kVideoCodecVP8); codec_.codecSpecific.VP8.feedbackModeOn = true; EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERROR, rtc_decoder_->InitDecode(&codec_, 1)); } TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorWithoutInitDecode) { + CreateDecoder(webrtc::kVideoCodecVP8); webrtc::EncodedImage input_image; EXPECT_EQ(WEBRTC_VIDEO_CODEC_UNINITIALIZED, rtc_decoder_->Decode(input_image, false, NULL, NULL, 0)); } TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnIncompleteFrame) { + CreateDecoder(webrtc::kVideoCodecVP8); Initialize(); webrtc::EncodedImage input_image; input_image._completeFrame = false; @@ -130,6 +142,7 @@ TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnIncompleteFrame) { } TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnMissingFrames) { + CreateDecoder(webrtc::kVideoCodecVP8); Initialize(); webrtc::EncodedImage input_image; input_image._completeFrame = true; @@ -139,6 +152,7 @@ TEST_F(RTCVideoDecoderTest, DecodeReturnsErrorOnMissingFrames) { } TEST_F(RTCVideoDecoderTest, ResetReturnsOk) { + CreateDecoder(webrtc::kVideoCodecVP8); Initialize(); EXPECT_CALL(*mock_vda_, Reset()) .WillOnce(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone)); @@ -146,6 +160,7 @@ TEST_F(RTCVideoDecoderTest, ResetReturnsOk) { } TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) { + CreateDecoder(webrtc::kVideoCodecVP8); Initialize(); EXPECT_CALL(*mock_vda_, Reset()) .WillOnce(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone)); @@ -153,6 +168,7 @@ TEST_F(RTCVideoDecoderTest, ReleaseReturnsOk) { } TEST_F(RTCVideoDecoderTest, InitDecodeAfterRelease) { + CreateDecoder(webrtc::kVideoCodecVP8); EXPECT_CALL(*mock_vda_, Reset()) .WillRepeatedly(Invoke(this, &RTCVideoDecoderTest::NotifyResetDone)); Initialize(); @@ -162,6 +178,7 @@ TEST_F(RTCVideoDecoderTest, InitDecodeAfterRelease) { } TEST_F(RTCVideoDecoderTest, IsBufferAfterReset) { + CreateDecoder(webrtc::kVideoCodecVP8); EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(0, RTCVideoDecoder::ID_INVALID)); EXPECT_TRUE(rtc_decoder_->IsBufferAfterReset(RTCVideoDecoder::ID_LAST, RTCVideoDecoder::ID_INVALID)); @@ -187,6 +204,7 @@ TEST_F(RTCVideoDecoderTest, IsBufferAfterReset) { } TEST_F(RTCVideoDecoderTest, IsFirstBufferAfterReset) { + CreateDecoder(webrtc::kVideoCodecVP8); EXPECT_TRUE( rtc_decoder_->IsFirstBufferAfterReset(0, RTCVideoDecoder::ID_INVALID)); EXPECT_FALSE( diff --git a/content/renderer/media/rtc_video_encoder.cc b/content/renderer/media/rtc_video_encoder.cc index 1b33fc54c90ab..4b3f99ff72ec8 100644 --- a/content/renderer/media/rtc_video_encoder.cc +++ b/content/renderer/media/rtc_video_encoder.cc @@ -16,6 +16,7 @@ #include "media/base/video_frame.h" #include "media/base/video_util.h" #include "media/filters/gpu_video_accelerator_factories.h" +#include "media/filters/h264_parser.h" #include "media/video/video_encode_accelerator.h" #include "third_party/webrtc/system_wrappers/interface/tick_util.h" @@ -27,6 +28,42 @@ namespace content { +namespace { + +// Populates struct webrtc::RTPFragmentationHeader for H264 codec. +// Each entry specifies the offset and length (excluding start code) of a NALU. +// Returns true if successful. +bool GetRTPFragmentationHeaderH264(webrtc::RTPFragmentationHeader* header, + const uint8_t* data, uint32_t length) { + media::H264Parser parser; + parser.SetStream(data, length); + + std::vector nalu_vector; + while (true) { + media::H264NALU nalu; + const media::H264Parser::Result result = parser.AdvanceToNextNALU(&nalu); + if (result == media::H264Parser::kOk) { + nalu_vector.push_back(nalu); + } else if (result == media::H264Parser::kEOStream) { + break; + } else { + DLOG(ERROR) << "Unexpected H264 parser result"; + return false; + } + } + + header->VerifyAndAllocateFragmentationHeader(nalu_vector.size()); + for (size_t i = 0; i < nalu_vector.size(); ++i) { + header->fragmentationOffset[i] = nalu_vector[i].data - data; + header->fragmentationLength[i] = nalu_vector[i].size; + header->fragmentationPlType[i] = 0; + header->fragmentationTimeDiff[i] = 0; + } + return true; +} + +} // namespace + // This private class of RTCVideoEncoder does the actual work of communicating // with a media::VideoEncodeAccelerator for handling video encoding. It can // be created on any thread, but should subsequently be posted to (and Destroy() @@ -648,6 +685,32 @@ void RTCVideoEncoder::ReturnEncodedImage(scoped_ptr image, if (!encoded_image_callback_) return; + webrtc::RTPFragmentationHeader header; + memset(&header, 0, sizeof(header)); + switch (video_codec_type_) { + case webrtc::kVideoCodecVP8: + case webrtc::kVideoCodecGeneric: + // Generate a header describing a single fragment. + // Note that webrtc treats the generic-type payload as an opaque buffer. + header.VerifyAndAllocateFragmentationHeader(1); + header.fragmentationOffset[0] = 0; + header.fragmentationLength[0] = image->_length; + header.fragmentationPlType[0] = 0; + header.fragmentationTimeDiff[0] = 0; + break; + case webrtc::kVideoCodecH264: + if (!GetRTPFragmentationHeaderH264( + &header, image->_buffer, image->_length)) { + DLOG(ERROR) << "Failed to get RTP fragmentation header for H264"; + NotifyError(WEBRTC_VIDEO_CODEC_ERROR); + return; + } + break; + default: + NOTREACHED() << "Invalid video codec type"; + return; + } + webrtc::CodecSpecificInfo info; memset(&info, 0, sizeof(info)); info.codecType = video_codec_type_; @@ -657,15 +720,6 @@ void RTCVideoEncoder::ReturnEncodedImage(scoped_ptr image, info.codecSpecific.VP8.keyIdx = -1; } - // Generate a header describing a single fragment. - webrtc::RTPFragmentationHeader header; - memset(&header, 0, sizeof(header)); - header.VerifyAndAllocateFragmentationHeader(1); - header.fragmentationOffset[0] = 0; - header.fragmentationLength[0] = image->_length; - header.fragmentationPlType[0] = 0; - header.fragmentationTimeDiff[0] = 0; - int32_t retval = encoded_image_callback_->Encoded(*image, &info, &header); if (retval < 0) { DVLOG(2) << "ReturnEncodedImage(): encoded_image_callback_ returned " diff --git a/content/renderer/media/rtc_video_encoder_factory.cc b/content/renderer/media/rtc_video_encoder_factory.cc index 777d69f83c9a1..ec92f5572f9bd 100644 --- a/content/renderer/media/rtc_video_encoder_factory.cc +++ b/content/renderer/media/rtc_video_encoder_factory.cc @@ -4,7 +4,9 @@ #include "content/renderer/media/rtc_video_encoder_factory.h" +#include "base/command_line.h" #include "content/common/gpu/client/gpu_video_encode_accelerator_host.h" +#include "content/public/common/content_switches.h" #include "content/renderer/media/rtc_video_encoder.h" #include "media/filters/gpu_video_accelerator_factories.h" #include "media/video/video_encode_accelerator.h" @@ -14,32 +16,32 @@ namespace content { namespace { // Translate from media::VideoEncodeAccelerator::SupportedProfile to -// cricket::WebRtcVideoEncoderFactory::VideoCodec -cricket::WebRtcVideoEncoderFactory::VideoCodec VEAToWebRTCCodec( +// one or more instances of cricket::WebRtcVideoEncoderFactory::VideoCodec +void VEAToWebRTCCodecs( + std::vector* codecs, const media::VideoEncodeAccelerator::SupportedProfile& profile) { - webrtc::VideoCodecType type = webrtc::kVideoCodecUnknown; - std::string name; - int width = 0, height = 0, fps = 0; + int width = profile.max_resolution.width(); + int height = profile.max_resolution.height(); + int fps = profile.max_framerate.numerator; + DCHECK_EQ(profile.max_framerate.denominator, 1U); + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); if (profile.profile >= media::VP8PROFILE_MIN && profile.profile <= media::VP8PROFILE_MAX) { - type = webrtc::kVideoCodecVP8; - name = "VP8"; + if (cmd_line->HasSwitch(switches::kEnableWebRtcHWVp8Encoding)) { + codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec( + webrtc::kVideoCodecVP8, "VP8", width, height, fps)); + } } else if (profile.profile >= media::H264PROFILE_MIN && profile.profile <= media::H264PROFILE_MAX) { - type = webrtc::kVideoCodecGeneric; - name = "CAST1"; - } - - if (type != webrtc::kVideoCodecUnknown) { - width = profile.max_resolution.width(); - height = profile.max_resolution.height(); - fps = profile.max_framerate.numerator; - DCHECK_EQ(profile.max_framerate.denominator, 1U); + if (cmd_line->HasSwitch(switches::kEnableWebRtcHWH264Encoding)) { + codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec( + webrtc::kVideoCodecH264, "H264", width, height, fps)); + } + // TODO(hshi): remove the generic codec type after CASTv1 deprecation. + codecs->push_back(cricket::WebRtcVideoEncoderFactory::VideoCodec( + webrtc::kVideoCodecGeneric, "CAST1", width, height, fps)); } - - return cricket::WebRtcVideoEncoderFactory::VideoCodec( - type, name, width, height, fps); } // Translate from cricket::WebRtcVideoEncoderFactory::VideoCodec to @@ -48,7 +50,8 @@ media::VideoCodecProfile WebRTCCodecToVideoCodecProfile( webrtc::VideoCodecType type) { switch (type) { case webrtc::kVideoCodecVP8: - return media::VP8PROFILE_MAIN; + return media::VP8PROFILE_ANY; + case webrtc::kVideoCodecH264: case webrtc::kVideoCodecGeneric: return media::H264PROFILE_MAIN; default: @@ -64,11 +67,8 @@ RTCVideoEncoderFactory::RTCVideoEncoderFactory( // Query media::VideoEncodeAccelerator (statically) for our supported codecs. std::vector profiles = GpuVideoEncodeAcceleratorHost::GetSupportedProfiles(); - for (size_t i = 0; i < profiles.size(); ++i) { - VideoCodec codec = VEAToWebRTCCodec(profiles[i]); - if (codec.type != webrtc::kVideoCodecUnknown) - codecs_.push_back(codec); - } + for (size_t i = 0; i < profiles.size(); ++i) + VEAToWebRTCCodecs(&codecs_, profiles[i]); } RTCVideoEncoderFactory::~RTCVideoEncoderFactory() {} diff --git a/content/renderer/media/video_track_adapter.cc b/content/renderer/media/video_track_adapter.cc index 13b79bd5e4cac..12a130e1af701 100644 --- a/content/renderer/media/video_track_adapter.cc +++ b/content/renderer/media/video_track_adapter.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/debug/trace_event.h" #include "base/location.h" +#include "base/metrics/histogram.h" #include "media/base/video_util.h" namespace content { @@ -23,6 +24,10 @@ namespace { const float kFirstFrameTimeoutInFrameIntervals = 100.0f; const float kNormalFrameTimeoutInFrameIntervals = 25.0f; +// Min delta time between two frames allowed without being dropped if a max +// frame rate is specified. +const int kMinTimeInMsBetweenFrames = 5; + // Empty method used for keeping a reference to the original media::VideoFrame // in VideoFrameResolutionAdapter::DeliverFrame if cropping is needed. // The reference to |frame| is kept in the closure that calls this method. @@ -84,7 +89,8 @@ class VideoTrackAdapter::VideoFrameResolutionAdapter // Returns |true| if the input frame rate is higher that the requested max // frame rate and |frame| should be dropped. - bool MaybeDropFrame(const scoped_refptr& frame); + bool MaybeDropFrame(const scoped_refptr& frame, + float source_frame_rate); // Bound to the IO-thread. base::ThreadChecker io_thread_checker_; @@ -147,7 +153,7 @@ void VideoTrackAdapter::VideoFrameResolutionAdapter::DeliverFrame( const base::TimeTicks& estimated_capture_time) { DCHECK(io_thread_checker_.CalledOnValidThread()); - if (MaybeDropFrame(frame)) + if (MaybeDropFrame(frame, format.frame_rate)) return; // TODO(perkj): Allow cropping / scaling of textures once @@ -219,16 +225,36 @@ void VideoTrackAdapter::VideoFrameResolutionAdapter::DeliverFrame( } bool VideoTrackAdapter::VideoFrameResolutionAdapter::MaybeDropFrame( - const scoped_refptr& frame) { - if (max_frame_rate_ == 0.0f) + const scoped_refptr& frame, + float source_frame_rate) { + DCHECK(io_thread_checker_.CalledOnValidThread()); + + // Do not drop frames if max frame rate hasn't been specified or the source + // frame rate is known and is lower than max. + if (max_frame_rate_ == 0.0f || + (source_frame_rate > 0 && + source_frame_rate <= max_frame_rate_)) return false; base::TimeDelta delta = frame->timestamp() - last_time_stamp_; + if (delta.InMilliseconds() < kMinTimeInMsBetweenFrames) { + // We have seen video frames being delivered from camera devices back to + // back. The simple AR filter for frame rate calculation is too short to + // handle that. http://crbug/394315 + // TODO(perkj): Can we come up with a way to fix the times stamps and the + // timing when frames are delivered so all frames can be used? + // The time stamps are generated by Chrome and not the actual device. + // Most likely the back to back problem is caused by software and not the + // actual camera. + DVLOG(3) << "Drop frame since delta time since previous frame is " + << delta.InMilliseconds() << "ms."; + return true; + } last_time_stamp_ = frame->timestamp(); - if (delta.ToInternalValue() == 0 || delta == last_time_stamp_) + if (delta == last_time_stamp_) // First received frame. return false; - // Calculate the moving average frame rate. Use a simple filter with 0.1 - // weight of the current sample. + // Calculate the frame rate using a simple AR filter. + // Use a simple filter with 0.1 weight of the current sample. frame_rate_ = 100 / delta.InMillisecondsF() + 0.9 * frame_rate_; // Prefer to not drop frames. @@ -315,6 +341,8 @@ VideoTrackAdapter::VideoTrackAdapter( VideoTrackAdapter::~VideoTrackAdapter() { DCHECK(adapters_.empty()); + UMA_HISTOGRAM_BOOLEAN("Media.VideoTrackAdapter.FramesReceived", + frame_counter_ > 0); } void VideoTrackAdapter::AddTrack( diff --git a/content/renderer/media/webcontentdecryptionmodulesession_impl.cc b/content/renderer/media/webcontentdecryptionmodulesession_impl.cc index 65975186a5f71..527720f338c60 100644 --- a/content/renderer/media/webcontentdecryptionmodulesession_impl.cc +++ b/content/renderer/media/webcontentdecryptionmodulesession_impl.cc @@ -15,6 +15,8 @@ namespace content { +const char kCreateSessionUMAName[] = "CreateSession"; + // For backwards compatibility with blink not using // WebContentDecryptionModuleResult, reserve an index for |outstanding_results_| // that will not be used when adding a WebContentDecryptionModuleResult. @@ -85,29 +87,31 @@ void WebContentDecryptionModuleSessionImpl::initializeNewSession( const blink::WebString& init_data_type, const uint8* init_data, size_t init_data_length) { - // TODO(ddorwin): Guard against this in supported types check and remove this. - // Chromium only supports ASCII MIME types. - if (!base::IsStringASCII(init_data_type)) { - NOTREACHED(); - OnSessionError(media::MediaKeys::NOT_SUPPORTED_ERROR, - 0, - "The initialization data type " + init_data_type.utf8() + - " is not supported by the key system."); - return; - } + DCHECK(base::IsStringASCII(init_data_type)); std::string init_data_type_as_ascii = base::UTF16ToASCII(init_data_type); DLOG_IF(WARNING, init_data_type_as_ascii.find('/') != std::string::npos) << "init_data_type '" << init_data_type_as_ascii << "' may be a MIME type"; + // Attempt to translate content types. + // TODO(sandersd): Remove once tests stop using content types. + // http://crbug.com/385874 + std::string content_type = base::StringToLowerASCII(init_data_type_as_ascii); + if (content_type == "audio/mp4" || content_type == "video/mp4") { + init_data_type_as_ascii = "cenc"; + } else if (content_type == "audio/webm" || content_type == "video/webm") { + init_data_type_as_ascii = "webm"; + } + scoped_ptr promise( new media::NewSessionCdmPromise( base::Bind(&WebContentDecryptionModuleSessionImpl::SessionCreated, weak_ptr_factory_.GetWeakPtr(), kReservedIndex), base::Bind(&WebContentDecryptionModuleSessionImpl::OnSessionError, - weak_ptr_factory_.GetWeakPtr()))); + weak_ptr_factory_.GetWeakPtr()), + adapter_->GetKeySystemUMAPrefix() + kCreateSessionUMAName)); adapter_->InitializeNewSession(init_data_type_as_ascii, init_data, init_data_length, @@ -168,7 +172,8 @@ void WebContentDecryptionModuleSessionImpl::initializeNewSession( result_index), base::Bind(&WebContentDecryptionModuleSessionImpl::SessionError, weak_ptr_factory_.GetWeakPtr(), - result_index))); + result_index), + adapter_->GetKeySystemUMAPrefix() + kCreateSessionUMAName)); adapter_->InitializeNewSession(init_data_type_as_ascii, init_data, init_data_length, diff --git a/content/renderer/media/webmediaplayer_impl.cc b/content/renderer/media/webmediaplayer_impl.cc index 7aad83b866821..fc723c7e116a1 100644 --- a/content/renderer/media/webmediaplayer_impl.cc +++ b/content/renderer/media/webmediaplayer_impl.cc @@ -61,6 +61,7 @@ #include "media/filters/video_renderer_impl.h" #include "media/filters/vpx_video_decoder.h" #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h" +#include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h" #include "third_party/WebKit/public/platform/WebMediaSource.h" #include "third_party/WebKit/public/platform/WebRect.h" #include "third_party/WebKit/public/platform/WebSize.h" @@ -135,6 +136,10 @@ class SyncPointClientImpl : public media::VideoFrame::SyncPointClient { blink::WebGraphicsContext3D* web_graphics_context_; }; +// Used for calls to decryptor_ready_cb where the result can be ignored. +void DoNothing(bool) { +} + } // namespace namespace content { @@ -531,9 +536,16 @@ bool WebMediaPlayerImpl::didLoadingProgress() { return pipeline_progress || data_progress; } -void WebMediaPlayerImpl::paint(WebCanvas* canvas, - const WebRect& rect, +void WebMediaPlayerImpl::paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, unsigned char alpha) { + paint(canvas, rect, alpha, SkXfermode::kSrcOver_Mode); +} + +void WebMediaPlayerImpl::paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, + unsigned char alpha, + SkXfermode::Mode mode) { DCHECK(main_loop_->BelongsToCurrentThread()); TRACE_EVENT0("media", "WebMediaPlayerImpl:paint"); @@ -547,13 +559,12 @@ void WebMediaPlayerImpl::paint(WebCanvas* canvas, gfx::Rect gfx_rect(rect); - if (pipeline_metadata_.video_rotation == media::VIDEO_ROTATION_90 || - pipeline_metadata_.video_rotation == media::VIDEO_ROTATION_270) { - gfx_rect.set_size(gfx::Size(gfx_rect.size().height(), - gfx_rect.size().width())); - } - - skcanvas_video_renderer_.Paint(video_frame.get(), canvas, gfx_rect, alpha); + skcanvas_video_renderer_.Paint(video_frame.get(), + canvas, + gfx_rect, + alpha, + mode, + pipeline_metadata_.video_rotation); } bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const { @@ -777,7 +788,7 @@ WebMediaPlayerImpl::GenerateKeyRequestInternal(const std::string& key_system, if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) { base::ResetAndReturn(&decryptor_ready_cb_) - .Run(proxy_decryptor_->GetDecryptor()); + .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing)); } current_key_system_ = key_system; @@ -890,7 +901,60 @@ void WebMediaPlayerImpl::setContentDecryptionModule( web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); if (web_cdm_ && !decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&decryptor_ready_cb_).Run(web_cdm_->GetDecryptor()); + base::ResetAndReturn(&decryptor_ready_cb_) + .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing)); +} + +void WebMediaPlayerImpl::setContentDecryptionModule( + blink::WebContentDecryptionModule* cdm, + blink::WebContentDecryptionModuleResult result) { + DCHECK(main_loop_->BelongsToCurrentThread()); + + // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 + if (!cdm) { + result.completeWithError( + blink::WebContentDecryptionModuleExceptionNotSupportedError, + 0, + "Null MediaKeys object is not supported."); + return; + } + + web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); + + if (web_cdm_ && !decryptor_ready_cb_.is_null()) { + base::ResetAndReturn(&decryptor_ready_cb_) + .Run(web_cdm_->GetDecryptor(), + BIND_TO_RENDER_LOOP1( + &WebMediaPlayerImpl::ContentDecryptionModuleAttached, result)); + } else { + // No pipeline/decoder connected, so resolve the promise. When something + // is connected, setting the CDM will happen in SetDecryptorReadyCB(). + ContentDecryptionModuleAttached(result, true); + } +} + +void WebMediaPlayerImpl::setContentDecryptionModuleSync( + blink::WebContentDecryptionModule* cdm) { + DCHECK(main_loop_->BelongsToCurrentThread()); + + // Used when loading media and no pipeline/decoder attached yet. + DCHECK(decryptor_ready_cb_.is_null()); + + web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); +} + +void WebMediaPlayerImpl::ContentDecryptionModuleAttached( + blink::WebContentDecryptionModuleResult result, + bool success) { + if (success) { + result.complete(); + return; + } + + result.completeWithError( + blink::WebContentDecryptionModuleExceptionNotSupportedError, + 0, + "Unable to set MediaKeys object"); } void WebMediaPlayerImpl::OnPipelineSeeked(bool time_changed, @@ -1289,8 +1353,10 @@ void WebMediaPlayerImpl::SetDecryptorReadyCB( // Cancels the previous decryptor request. if (decryptor_ready_cb.is_null()) { - if (!decryptor_ready_cb_.is_null()) - base::ResetAndReturn(&decryptor_ready_cb_).Run(NULL); + if (!decryptor_ready_cb_.is_null()) { + base::ResetAndReturn(&decryptor_ready_cb_) + .Run(NULL, base::Bind(DoNothing)); + } return; } @@ -1305,12 +1371,13 @@ void WebMediaPlayerImpl::SetDecryptorReadyCB( DCHECK(!proxy_decryptor_ || !web_cdm_); if (proxy_decryptor_) { - decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor()); + decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(), + base::Bind(DoNothing)); return; } if (web_cdm_) { - decryptor_ready_cb.Run(web_cdm_->GetDecryptor()); + decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing)); return; } diff --git a/content/renderer/media/webmediaplayer_impl.h b/content/renderer/media/webmediaplayer_impl.h index 8d40a8486d903..7962961fb0bc3 100644 --- a/content/renderer/media/webmediaplayer_impl.h +++ b/content/renderer/media/webmediaplayer_impl.h @@ -34,6 +34,7 @@ class RenderAudioSourceProvider; namespace blink { class WebContentDecryptionModule; +class WebContentDecryptionModuleResult; class WebLocalFrame; } @@ -89,6 +90,11 @@ class WebMediaPlayerImpl virtual double maxTimeSeekable() const; // Methods for painting. + virtual void paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, + unsigned char alpha, + SkXfermode::Mode mode); + // TODO(dshwang): remove it because above method replaces. crbug.com/401027 virtual void paint(blink::WebCanvas* canvas, const blink::WebRect& rect, unsigned char alpha); @@ -152,8 +158,15 @@ class WebMediaPlayerImpl const blink::WebString& key_system, const blink::WebString& session_id); + // TODO(jrummell): Remove this method once Blink updated to use the other + // two methods. virtual void setContentDecryptionModule( blink::WebContentDecryptionModule* cdm); + virtual void setContentDecryptionModule( + blink::WebContentDecryptionModule* cdm, + blink::WebContentDecryptionModuleResult result); + virtual void setContentDecryptionModuleSync( + blink::WebContentDecryptionModule* cdm); void OnPipelineSeeked(bool time_changed, media::PipelineStatus status); void OnPipelineEnded(); @@ -229,6 +242,12 @@ class WebMediaPlayerImpl // NULL immediately and reset. void SetDecryptorReadyCB(const media::DecryptorReadyCB& decryptor_ready_cb); + // Called when the ContentDecryptionModule has been attached to the + // pipeline/decoders. + void ContentDecryptionModuleAttached( + blink::WebContentDecryptionModuleResult result, + bool success); + // Returns the current video frame from |compositor_|. Blocks until the // compositor can return the frame. scoped_refptr GetCurrentFrameFromCompositor(); diff --git a/content/renderer/media/webmediaplayer_ms.cc b/content/renderer/media/webmediaplayer_ms.cc index f433231e09121..8500e439918db 100644 --- a/content/renderer/media/webmediaplayer_ms.cc +++ b/content/renderer/media/webmediaplayer_ms.cc @@ -319,14 +319,26 @@ bool WebMediaPlayerMS::didLoadingProgress() { return true; } -void WebMediaPlayerMS::paint(WebCanvas* canvas, - const WebRect& rect, +void WebMediaPlayerMS::paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, unsigned char alpha) { + paint(canvas, rect, alpha, SkXfermode::kSrcOver_Mode); +} + +void WebMediaPlayerMS::paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, + unsigned char alpha, + SkXfermode::Mode mode) { DVLOG(3) << "WebMediaPlayerMS::paint"; DCHECK(thread_checker_.CalledOnValidThread()); gfx::RectF dest_rect(rect.x, rect.y, rect.width, rect.height); - video_renderer_.Paint(current_frame_.get(), canvas, dest_rect, alpha); + video_renderer_.Paint(current_frame_.get(), + canvas, + dest_rect, + alpha, + mode, + media::VIDEO_ROTATION_0); { base::AutoLock auto_lock(current_frame_lock_); diff --git a/content/renderer/media/webmediaplayer_ms.h b/content/renderer/media/webmediaplayer_ms.h index 0bbfec5d6c7a7..bf563e8126d12 100644 --- a/content/renderer/media/webmediaplayer_ms.h +++ b/content/renderer/media/webmediaplayer_ms.h @@ -77,6 +77,11 @@ class WebMediaPlayerMS virtual double maxTimeSeekable() const; // Methods for painting. + virtual void paint(blink::WebCanvas* canvas, + const blink::WebRect& rect, + unsigned char alpha, + SkXfermode::Mode mode); + // TODO(dshwang): remove it because above method replaces. crbug.com/401027 virtual void paint(blink::WebCanvas* canvas, const blink::WebRect& rect, unsigned char alpha); diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source.cc b/content/renderer/media/webrtc/media_stream_remote_video_source.cc index 933bd5d19bb01..4cfefb33a1b52 100644 --- a/content/renderer/media/webrtc/media_stream_remote_video_source.cc +++ b/content/renderer/media/webrtc/media_stream_remote_video_source.cc @@ -106,7 +106,7 @@ RemoteVideoSourceDelegate::RenderFrame( media::VideoCaptureFormat format( gfx::Size(video_frame->natural_size().width(), video_frame->natural_size().height()), - MediaStreamVideoSource::kDefaultFrameRate, + MediaStreamVideoSource::kUnknownFrameRate, pixel_format); io_message_loop_->PostTask( @@ -154,7 +154,7 @@ void MediaStreamRemoteVideoSource::StartSourceImpl( DCHECK(!delegate_); delegate_ = new RemoteVideoSourceDelegate(io_message_loop(), frame_callback); remote_track_->AddRenderer(delegate_); - OnStartDone(true); + OnStartDone(MEDIA_DEVICE_OK); } void MediaStreamRemoteVideoSource::StopSourceImpl() { diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc b/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc index c6b50ec25eb22..e2a4176d6c7e8 100644 --- a/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc +++ b/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc @@ -84,9 +84,11 @@ class MediaStreamRemoteVideoSourceTest } private: - void OnConstraintsApplied(MediaStreamSource* source, bool success) { + void OnConstraintsApplied(MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name) { ASSERT_EQ(source, remote_source_); - if (success) + if (result == MEDIA_DEVICE_OK) ++number_of_successful_constraints_applied_; else ++number_of_failed_constraints_applied_; diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc index 9b4fcdbb0d004..f5c98179338e2 100644 --- a/content/renderer/media/webrtc/peer_connection_dependency_factory.cc +++ b/content/renderer/media/webrtc/peer_connection_dependency_factory.cc @@ -129,9 +129,10 @@ class P2PPortAllocatorFactory : public webrtc::PortAllocatorFactoryInterface { const std::vector& turn_configurations) OVERRIDE { CHECK(web_frame_); P2PPortAllocator::Config config; - if (stun_servers.size() > 0) { - config.stun_server = stun_servers[0].server.hostname(); - config.stun_server_port = stun_servers[0].server.port(); + for (size_t i = 0; i < stun_servers.size(); ++i) { + config.stun_servers.insert(rtc::SocketAddress( + stun_servers[i].server.hostname(), + stun_servers[i].server.port())); } config.legacy_relay = false; for (size_t i = 0; i < turn_configurations.size(); ++i) { @@ -143,12 +144,11 @@ class P2PPortAllocatorFactory : public webrtc::PortAllocatorFactoryInterface { relay_config.transport_type = turn_configurations[i].transport_type; relay_config.secure = turn_configurations[i].secure; config.relays.push_back(relay_config); - } - // Use first turn server as the stun server. - if (turn_configurations.size() > 0) { - config.stun_server = config.relays[0].server_address; - config.stun_server_port = config.relays[0].port; + // Use turn servers as stun servers. + config.stun_servers.insert(rtc::SocketAddress( + turn_configurations[i].server.hostname(), + turn_configurations[i].server.port())); } return new P2PPortAllocator( diff --git a/content/renderer/media/webrtc/video_destination_handler.cc b/content/renderer/media/webrtc/video_destination_handler.cc index bd388be4939b6..887dbc354b95a 100644 --- a/content/renderer/media/webrtc/video_destination_handler.cc +++ b/content/renderer/media/webrtc/video_destination_handler.cc @@ -100,7 +100,7 @@ void PpFrameWriter::StartSourceImpl( DCHECK(!delegate_); DVLOG(3) << "PpFrameWriter::StartSourceImpl()"; delegate_ = new FrameWriterDelegate(io_message_loop(), frame_callback); - OnStartDone(true); + OnStartDone(MEDIA_DEVICE_OK); } void PpFrameWriter::StopSourceImpl() { @@ -146,7 +146,7 @@ void PpFrameWriter::PutFrame(PPB_ImageData_Impl* image_data, gfx::Rect(frame_size), frame_size, timestamp); media::VideoCaptureFormat format( frame_size, - MediaStreamVideoSource::kDefaultFrameRate, + MediaStreamVideoSource::kUnknownFrameRate, media::PIXEL_FORMAT_YV12); libyuv::BGRAToI420(reinterpret_cast(bitmap->getPixels()), diff --git a/content/renderer/media/webrtc_audio_capturer.cc b/content/renderer/media/webrtc_audio_capturer.cc index 994f77b150c68..a9eb073b9d51f 100644 --- a/content/renderer/media/webrtc_audio_capturer.cc +++ b/content/renderer/media/webrtc_audio_capturer.cc @@ -23,22 +23,6 @@ namespace content { namespace { -// Supported hardware sample rates for input and output sides. -#if defined(OS_WIN) || defined(OS_MACOSX) -// media::GetAudioInputHardwareSampleRate() asks the audio layer -// for its current sample rate (set by the user) on Windows and Mac OS X. -// The listed rates below adds restrictions and WebRtcAudioDeviceImpl::Init() -// will fail if the user selects any rate outside these ranges. -const int kValidInputRates[] = - {192000, 96000, 48000, 44100, 32000, 16000, 8000}; -#elif defined(OS_LINUX) || defined(OS_OPENBSD) -const int kValidInputRates[] = {48000, 44100}; -#elif defined(OS_ANDROID) -const int kValidInputRates[] = {48000, 44100}; -#else -const int kValidInputRates[] = {44100}; -#endif - // Time constant for AudioPowerMonitor. See AudioPowerMonitor ctor comments // for semantics. This value was arbitrarily chosen, but seems to work well. const int kPowerMonitorTimeConstantMs = 10; @@ -196,17 +180,6 @@ bool WebRtcAudioCapturer::Initialize() { device_info_.device.input.sample_rate); } - // Verify that the reported input hardware sample rate is supported - // on the current platform. - if (std::find(&kValidInputRates[0], - &kValidInputRates[0] + arraysize(kValidInputRates), - device_info_.device.input.sample_rate) == - &kValidInputRates[arraysize(kValidInputRates)]) { - DLOG(ERROR) << device_info_.device.input.sample_rate - << " is not a supported input rate."; - return false; - } - // Create and configure the default audio capturing source. SetCapturerSource(AudioDeviceFactory::NewInputDevice(render_view_id_), channel_layout, diff --git a/content/renderer/media/webrtc_audio_capturer_unittest.cc b/content/renderer/media/webrtc_audio_capturer_unittest.cc index 0011f7630597f..274fced6ed975 100644 --- a/content/renderer/media/webrtc_audio_capturer_unittest.cc +++ b/content/renderer/media/webrtc_audio_capturer_unittest.cc @@ -130,7 +130,8 @@ class WebRtcAudioCapturerTest : public testing::Test { EXPECT_CALL(*sink, FormatIsSet()); EXPECT_CALL(*sink, OnDataCallback(_, _, delay_ms, expected_volume_value, - need_audio_processing, key_pressed)); + need_audio_processing, key_pressed)) + .Times(AtLeast(1)); callback->Capture(audio_bus.get(), delay_ms, volume, key_pressed); // Verify the cached values in the capturer fits what we expect. diff --git a/content/renderer/media/webrtc_audio_renderer.cc b/content/renderer/media/webrtc_audio_renderer.cc index 006f12fe4f7b1..b575db8766d93 100644 --- a/content/renderer/media/webrtc_audio_renderer.cc +++ b/content/renderer/media/webrtc_audio_renderer.cc @@ -29,69 +29,6 @@ namespace content { namespace { -// Supported hardware sample rates for output sides. -#if defined(OS_WIN) || defined(OS_MACOSX) -// AudioHardwareConfig::GetOutputSampleRate() asks the audio layer for its -// current sample rate (set by the user) on Windows and Mac OS X. The listed -// rates below adds restrictions and Initialize() will fail if the user selects -// any rate outside these ranges. -const int kValidOutputRates[] = {96000, 48000, 44100, 32000, 16000}; -#elif defined(OS_LINUX) || defined(OS_OPENBSD) -const int kValidOutputRates[] = {48000, 44100}; -#elif defined(OS_ANDROID) -// TODO(leozwang): We want to use native sampling rate on Android to achieve -// low latency, currently 16000 is used to work around audio problem on some -// Android devices. -const int kValidOutputRates[] = {48000, 44100, 16000}; -#else -const int kValidOutputRates[] = {44100}; -#endif - -// TODO(xians): Merge the following code to WebRtcAudioCapturer, or remove. -enum AudioFramesPerBuffer { - k160, - k320, - k440, - k480, - k640, - k880, - k960, - k1440, - k1920, - kUnexpectedAudioBufferSize // Must always be last! -}; - -// Helper method to convert integral values to their respective enum values -// above, or kUnexpectedAudioBufferSize if no match exists. -// We map 441 to k440 to avoid changes in the XML part for histograms. -// It is still possible to map the histogram result to the actual buffer size. -// See http://crbug.com/243450 for details. -AudioFramesPerBuffer AsAudioFramesPerBuffer(int frames_per_buffer) { - switch (frames_per_buffer) { - case 160: return k160; - case 320: return k320; - case 441: return k440; - case 480: return k480; - case 640: return k640; - case 880: return k880; - case 960: return k960; - case 1440: return k1440; - case 1920: return k1920; - } - return kUnexpectedAudioBufferSize; -} - -void AddHistogramFramesPerBuffer(int param) { - AudioFramesPerBuffer afpb = AsAudioFramesPerBuffer(param); - if (afpb != kUnexpectedAudioBufferSize) { - UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer", - afpb, kUnexpectedAudioBufferSize); - } else { - // Report unexpected sample rates using a unique histogram name. - UMA_HISTOGRAM_COUNTS("WebRTC.AudioOutputFramesPerBufferUnexpected", param); - } -} - // This is a simple wrapper class that's handed out to users of a shared // WebRtcAudioRenderer instance. This class maintains the per-user 'playing' // and 'started' states to avoid problems related to incorrect usage which @@ -198,6 +135,39 @@ int GetCurrentDuckingFlag(int render_frame_id) { return media::AudioParameters::NO_EFFECTS; } +// Helper method to get platform specific optimal buffer size. +int GetOptimalBufferSize(int sample_rate, int hardware_buffer_size) { + // Use native hardware buffer size as default. On Windows, we strive to open + // up using this native hardware buffer size to achieve best + // possible performance and to ensure that no FIFO is needed on the browser + // side to match the client request. That is why there is no #if case for + // Windows below. + int frames_per_buffer = hardware_buffer_size; + +#if defined(OS_LINUX) || defined(OS_MACOSX) + // On Linux and MacOS, the low level IO implementations on the browser side + // supports all buffer size the clients want. We use the native peer + // connection buffer size (10ms) to achieve best possible performance. + frames_per_buffer = sample_rate / 100; +#elif defined(OS_ANDROID) + // TODO(henrika): Keep tuning this scheme and espcicially for low-latency + // cases. Might not be possible to come up with the perfect solution using + // the render side only. + int frames_per_10ms = sample_rate / 100; + if (frames_per_buffer < 2 * frames_per_10ms) { + // Examples of low-latency frame sizes and the resulting |buffer_size|: + // Nexus 7 : 240 audio frames => 2*480 = 960 + // Nexus 10 : 256 => 2*441 = 882 + // Galaxy Nexus: 144 => 2*441 = 882 + frames_per_buffer = 2 * frames_per_10ms; + DVLOG(1) << "Low-latency output detected on Android"; + } +#endif + + DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer; + return frames_per_buffer; +} + } // namespace WebRtcAudioRenderer::WebRtcAudioRenderer( @@ -266,16 +236,6 @@ bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { sample_rate); } - // Verify that the reported output hardware sample rate is supported - // on the current platform. - if (std::find(&kValidOutputRates[0], - &kValidOutputRates[0] + arraysize(kValidOutputRates), - sample_rate) == - &kValidOutputRates[arraysize(kValidOutputRates)]) { - DLOG(ERROR) << sample_rate << " is not a supported output rate."; - return false; - } - // Set up audio parameters for the source, i.e., the WebRTC client. // The WebRTC client only supports multiples of 10ms as buffer size where @@ -288,28 +248,8 @@ bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { sink_params_.channel_layout(), sink_params_.channels(), 0, sample_rate, 16, frames_per_10ms); - // Update audio parameters for the sink, i.e., the native audio output stream. - // We strive to open up using native parameters to achieve best possible - // performance and to ensure that no FIFO is needed on the browser side to - // match the client request. Any mismatch between the source and the sink is - // taken care of in this class instead using a pull FIFO. - - // Use native output size as default. - int frames_per_buffer = sink_params_.frames_per_buffer(); -#if defined(OS_ANDROID) - // TODO(henrika): Keep tuning this scheme and espcicially for low-latency - // cases. Might not be possible to come up with the perfect solution using - // the render side only. - if (frames_per_buffer < 2 * frames_per_10ms) { - // Examples of low-latency frame sizes and the resulting |buffer_size|: - // Nexus 7 : 240 audio frames => 2*480 = 960 - // Nexus 10 : 256 => 2*441 = 882 - // Galaxy Nexus: 144 => 2*441 = 882 - frames_per_buffer = 2 * frames_per_10ms; - DVLOG(1) << "Low-latency output detected on Android"; - } -#endif - DVLOG(1) << "Using sink output buffer size: " << frames_per_buffer; + const int frames_per_buffer = + GetOptimalBufferSize(sample_rate, sink_params_.frames_per_buffer()); sink_params_.Reset(sink_params_.format(), sink_params_.channel_layout(), sink_params_.channels(), 0, sample_rate, 16, @@ -351,11 +291,6 @@ bool WebRtcAudioRenderer::Initialize(WebRtcAudioRendererSource* source) { // User must call Play() before any audio can be heard. state_ = PAUSED; - UMA_HISTOGRAM_ENUMERATION("WebRTC.AudioOutputFramesPerBuffer", - source_params.frames_per_buffer(), - kUnexpectedAudioBufferSize); - AddHistogramFramesPerBuffer(source_params.frames_per_buffer()); - return true; } diff --git a/content/renderer/media/webrtc_audio_renderer.h b/content/renderer/media/webrtc_audio_renderer.h index 61b0b24d141ba..580659933c9a8 100644 --- a/content/renderer/media/webrtc_audio_renderer.h +++ b/content/renderer/media/webrtc_audio_renderer.h @@ -99,6 +99,7 @@ class CONTENT_EXPORT WebRtcAudioRenderer // Accessors to the sink audio parameters. int channels() const { return sink_params_.channels(); } int sample_rate() const { return sink_params_.sample_rate(); } + int frames_per_buffer() const { return sink_params_.frames_per_buffer(); } private: // MediaStreamAudioRenderer implementation. This is private since we want diff --git a/content/renderer/media/webrtc_audio_renderer_unittest.cc b/content/renderer/media/webrtc_audio_renderer_unittest.cc index 914dd52b776a9..a481f9e4bb38d 100644 --- a/content/renderer/media/webrtc_audio_renderer_unittest.cc +++ b/content/renderer/media/webrtc_audio_renderer_unittest.cc @@ -24,6 +24,9 @@ namespace content { namespace { +const int kHardwareSampleRate = 44100; +const int kHardwareBufferSize = 512; + class MockAudioOutputIPC : public media::AudioOutputIPC { public: MockAudioOutputIPC() {} @@ -88,7 +91,8 @@ class WebRtcAudioRendererTest : public testing::Test { factory_(new MockAudioDeviceFactory()), source_(new MockAudioRendererSource()), stream_(new rtc::RefCountedObject("label")), - renderer_(new WebRtcAudioRenderer(stream_, 1, 1, 1, 44100, 441)) { + renderer_(new WebRtcAudioRenderer(stream_, 1, 1, 1, 44100, + kHardwareBufferSize)) { EXPECT_CALL(*factory_.get(), CreateOutputDevice(1)) .WillOnce(Return(mock_output_device_)); EXPECT_CALL(*mock_output_device_, Start()); @@ -151,4 +155,25 @@ TEST_F(WebRtcAudioRendererTest, MultipleRenderers) { } } +// Verify that the sink of the renderer is using the expected sample rate and +// buffer size. +TEST_F(WebRtcAudioRendererTest, VerifySinkParameters) { + renderer_proxy_->Start(); +#if defined(OS_LINUX) || defined(OS_MACOSX) + static const int kExpectedBufferSize = kHardwareSampleRate / 100; +#elif defined(OS_ANDROID) + static const int kExpectedBufferSize = 2 * kHardwareSampleRate / 100; +#else + // Windows. + static const int kExpectedBufferSize = kHardwareBufferSize; +#endif + EXPECT_EQ(kExpectedBufferSize, renderer_->frames_per_buffer()); + EXPECT_EQ(kHardwareSampleRate, renderer_->sample_rate()); + EXPECT_EQ(2, renderer_->channels()); + + EXPECT_CALL(*mock_output_device_.get(), Stop()); + EXPECT_CALL(*source_.get(), RemoveAudioRenderer(renderer_.get())); + renderer_proxy_->Stop(); +} + } // namespace content diff --git a/content/renderer/media/webrtc_local_audio_track_unittest.cc b/content/renderer/media/webrtc_local_audio_track_unittest.cc index 3f542fb6e8bea..6bdd034ad624b 100644 --- a/content/renderer/media/webrtc_local_audio_track_unittest.cc +++ b/content/renderer/media/webrtc_local_audio_track_unittest.cc @@ -404,8 +404,16 @@ TEST_F(WebRtcLocalAudioTrackTest, StartAndStopAudioTracks) { capturer_->Stop(); } +// Contains data races reported by tsan: crbug.com/404133 +#if defined(THREAD_SANITIZER) + #define DISABLE_ON_TSAN(function) DISABLED_##function +#else + #define DISABLE_ON_TSAN(function) function +#endif + // Create a new capturer with new source, connect it to a new audio track. -TEST_F(WebRtcLocalAudioTrackTest, ConnectTracksToDifferentCapturers) { +TEST_F(WebRtcLocalAudioTrackTest, + DISABLE_ON_TSAN(ConnectTracksToDifferentCapturers)) { // Setup the first audio track and start it. scoped_refptr adapter_1( WebRtcLocalAudioTrackAdapter::Create(std::string(), NULL)); diff --git a/content/renderer/p2p/ipc_socket_factory.cc b/content/renderer/p2p/ipc_socket_factory.cc index 62428ad0c2dbc..3055016b864fb 100644 --- a/content/renderer/p2p/ipc_socket_factory.cc +++ b/content/renderer/p2p/ipc_socket_factory.cc @@ -235,18 +235,24 @@ bool IpcPacketSocket::Init(P2PSocketType type, } net::IPEndPoint remote_endpoint; - if (!remote_address.IsNil() && !jingle_glue::SocketAddressToIPEndPoint( - remote_address, &remote_endpoint) && !IsTcpClientSocket(type_)) { - // Non TCP sockets must have a resolved remote address. - return false; + if (!remote_address.IsNil()) { + DCHECK(IsTcpClientSocket(type_)); + + if (remote_address.IsUnresolvedIP()) { + remote_endpoint = + net::IPEndPoint(net::IPAddressNumber(), remote_address.port()); + } else { + if (!jingle_glue::SocketAddressToIPEndPoint(remote_address, + &remote_endpoint)) { + return false; + } + } } // We need to send both resolved and unresolved address in Init. Unresolved // address will be used in case of TLS for certificate hostname matching. // Certificate will be tied to domain name not to IP address. - std::string remote_hostname = remote_address.hostname() + ":" + - remote_address.PortAsString(); - P2PHostAndIPEndPoint remote_info(remote_hostname, remote_endpoint); + P2PHostAndIPEndPoint remote_info(remote_address.hostname(), remote_endpoint); client->Init(type, local_endpoint, remote_info, this); diff --git a/content/renderer/p2p/port_allocator.cc b/content/renderer/p2p/port_allocator.cc index be6a72e25a0da..53a71e2b80476 100644 --- a/content/renderer/p2p/port_allocator.cc +++ b/content/renderer/p2p/port_allocator.cc @@ -51,8 +51,7 @@ bool ParsePortNumber( } // namespace P2PPortAllocator::Config::Config() - : stun_server_port(0), - legacy_relay(true), + : legacy_relay(true), disable_tcp_transport(false) { } @@ -259,8 +258,7 @@ void P2PPortAllocatorSession::ParseRelayResponse() { void P2PPortAllocatorSession::AddConfig() { const P2PPortAllocator::Config& config = allocator_->config_; cricket::PortConfiguration* port_config = new cricket::PortConfiguration( - rtc::SocketAddress(config.stun_server, config.stun_server_port), - std::string(), std::string()); + config.stun_servers, std::string(), std::string()); for (size_t i = 0; i < config.relays.size(); ++i) { cricket::RelayCredentials credentials(config.relays[i].username, diff --git a/content/renderer/p2p/port_allocator.h b/content/renderer/p2p/port_allocator.h index 258d7856733af..65250a447f534 100644 --- a/content/renderer/p2p/port_allocator.h +++ b/content/renderer/p2p/port_allocator.h @@ -43,9 +43,7 @@ class P2PPortAllocator : public cricket::BasicPortAllocator { bool secure; }; - // STUN server address and port. - std::string stun_server; - int stun_server_port; + std::set stun_servers; std::vector relays; diff --git a/content/renderer/pepper/content_decryptor_delegate.cc b/content/renderer/pepper/content_decryptor_delegate.cc index 8692c1ce64308..5a9e527d9d389 100644 --- a/content/renderer/pepper/content_decryptor_delegate.cc +++ b/content/renderer/pepper/content_decryptor_delegate.cc @@ -161,10 +161,8 @@ PP_VideoCodec MediaVideoCodecToPpVideoCodec(media::VideoCodec codec) { PP_VideoCodecProfile MediaVideoCodecProfileToPpVideoCodecProfile( media::VideoCodecProfile profile) { switch (profile) { - // TODO(xhwang): VP8 and VP9 do not have profiles. Clean up - // media::VideoCodecProfile and remove these two cases. - case media::VP8PROFILE_MAIN: - case media::VP9PROFILE_MAIN: + case media::VP8PROFILE_ANY: + case media::VP9PROFILE_ANY: return PP_VIDEOCODECPROFILE_NOT_NEEDED; case media::H264PROFILE_BASELINE: return PP_VIDEOCODECPROFILE_H264_BASELINE; @@ -627,26 +625,46 @@ bool ContentDecryptorDelegate::DecryptAndDecodeVideo( void ContentDecryptorDelegate::OnPromiseResolved(uint32 promise_id) { scoped_ptr promise = TakePromise(promise_id); - if (promise) { - SimpleCdmPromise* simple_promise( - static_cast(promise.get())); - simple_promise->resolve(); + + // Special case due to http://crbug.com/408330. CDM is resolving LoadSession() + // with this method when the session is not found. Instead it should call + // PromiseResolvedWithSession(""), so emulate that here until 408330 is fixed. + // TODO(jrummell): Remove this code when the CDM is updated. + if (promise && + promise->GetResolveParameterType() == media::CdmPromise::STRING_TYPE) { + NewSessionCdmPromise* session_promise = + static_cast(promise.get()); + session_promise->resolve(std::string()); + return; + } + + if (!promise || + promise->GetResolveParameterType() != media::CdmPromise::VOID_TYPE) { + NOTREACHED(); + return; } + + SimpleCdmPromise* simple_promise = + static_cast(promise.get()); + simple_promise->resolve(); } void ContentDecryptorDelegate::OnPromiseResolvedWithSession( uint32 promise_id, PP_Var web_session_id) { scoped_ptr promise = TakePromise(promise_id); + if (!promise || + promise->GetResolveParameterType() != media::CdmPromise::STRING_TYPE) { + NOTREACHED(); + return; + } StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id); DCHECK(web_session_id_string); - if (promise) { - NewSessionCdmPromise* session_promise( - static_cast(promise.get())); - session_promise->resolve(web_session_id_string->value()); - } + NewSessionCdmPromise* session_promise = + static_cast(promise.get()); + session_promise->resolve(web_session_id_string->value()); } void ContentDecryptorDelegate::OnPromiseRejected( diff --git a/content/renderer/pepper/host_var_tracker.h b/content/renderer/pepper/host_var_tracker.h index 6abbf869765c0..8d8176a7c6eb2 100644 --- a/content/renderer/pepper/host_var_tracker.h +++ b/content/renderer/pepper/host_var_tracker.h @@ -18,12 +18,14 @@ #include "ppapi/shared_impl/host_resource.h" #include "ppapi/shared_impl/resource_tracker.h" #include "ppapi/shared_impl/var_tracker.h" +#include "v8/include/v8.h" typedef struct NPObject NPObject; namespace ppapi { class ArrayBufferVar; class NPObjectVar; +class V8ObjectVar; class Var; } @@ -53,6 +55,18 @@ class HostVarTracker : public ppapi::VarTracker { // Returns 0 if the instance isn't known. CONTENT_EXPORT int GetLiveNPObjectVarsForInstance(PP_Instance instance) const; + // Tracks all live V8ObjectVar. This is so we can map between instance + + // V8Object and get the V8ObjectVar corresponding to it. This Add/Remove + // function is called by the V8ObjectVar when it is created and destroyed. + void AddV8ObjectVar(ppapi::V8ObjectVar* object_var) { NOTIMPLEMENTED(); } + void RemoveV8ObjectVar(ppapi::V8ObjectVar* object_var) { NOTIMPLEMENTED(); } + // Creates or retrieves a V8ObjectVar. + PP_Var V8ObjectVarForV8Object(PP_Instance instance, + v8::Handle object) { + NOTIMPLEMENTED(); + return PP_MakeUndefined(); + } + // VarTracker public implementation. virtual PP_Var MakeResourcePPVarFromMessage( PP_Instance instance, diff --git a/content/renderer/pepper/message_channel.cc b/content/renderer/pepper/message_channel.cc index c8ede8dbbb31d..150f16e55a382 100644 --- a/content/renderer/pepper/message_channel.cc +++ b/content/renderer/pepper/message_channel.cc @@ -307,36 +307,52 @@ MessageChannel::MessageChannel(PepperPluginInstanceImpl* instance) np_object_->message_channel = weak_ptr_factory_.GetWeakPtr(); } -void MessageChannel::EnqueuePluginMessage(const NPVariant* variant) { - plugin_message_queue_.push_back(VarConversionResult()); - if (variant->type == NPVariantType_Object) { - // Convert NPVariantType_Object in to an appropriate PP_Var like Dictionary, - // Array, etc. Note NPVariantToVar would convert to an "Object" PP_Var, - // which we don't support for Messaging. +MessageChannel::~MessageChannel() { + WebBindings::releaseObject(np_object_); + if (passthrough_object_) + WebBindings::releaseObject(passthrough_object_); +} - // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it - // won't result in a deep copy. - v8::Handle v8_value = WebBindings::toV8Value(variant); - V8VarConverter v8_var_converter(instance_->pp_instance()); - V8VarConverter::VarResult conversion_result = - v8_var_converter.FromV8Value( - v8_value, - v8::Isolate::GetCurrent()->GetCurrentContext(), - base::Bind(&MessageChannel::FromV8ValueComplete, - weak_ptr_factory_.GetWeakPtr(), - &plugin_message_queue_.back())); - if (conversion_result.completed_synchronously) { - plugin_message_queue_.back().ConversionCompleted( - conversion_result.var, - conversion_result.success); - } - } else { - plugin_message_queue_.back().ConversionCompleted( - ScopedPPVar(ScopedPPVar::PassRef(), - NPVariantToPPVar(instance(), variant)), - true); - DCHECK(plugin_message_queue_.back().var().get().type != PP_VARTYPE_OBJECT); +void MessageChannel::Start() { + // We PostTask here instead of draining the message queue directly + // since we haven't finished initializing the PepperWebPluginImpl yet, so + // the plugin isn't available in the DOM. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&MessageChannel::DrainEarlyMessageQueue, + weak_ptr_factory_.GetWeakPtr())); +} + +void MessageChannel::SetPassthroughObject(NPObject* passthrough) { + // Retain the passthrough object; We need to ensure it lives as long as this + // MessageChannel. + if (passthrough) + WebBindings::retainObject(passthrough); + + // If we had a passthrough set already, release it. Note that we retain the + // incoming passthrough object first, so that we behave correctly if anyone + // invokes: + // SetPassthroughObject(passthrough_object()); + if (passthrough_object_) + WebBindings::releaseObject(passthrough_object_); + + passthrough_object_ = passthrough; +} + +bool MessageChannel::GetReadOnlyProperty(NPIdentifier key, + NPVariant* value) const { + std::map::const_iterator it = + internal_properties_.find(key); + if (it != internal_properties_.end()) { + if (value) + return PPVarToNPVariant(it->second.get(), value); + return true; } + return false; +} + +void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) { + internal_properties_[PPVarToNPIdentifier(key)] = ScopedPPVar(value); } void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { @@ -383,89 +399,6 @@ void MessageChannel::PostMessageToJavaScript(PP_Var message_data) { } } -void MessageChannel::Start() { - // We PostTask here instead of draining the message queue directly - // since we haven't finished initializing the PepperWebPluginImpl yet, so - // the plugin isn't available in the DOM. - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&MessageChannel::DrainEarlyMessageQueue, - weak_ptr_factory_.GetWeakPtr())); -} - -void MessageChannel::FromV8ValueComplete(VarConversionResult* result_holder, - const ScopedPPVar& result, - bool success) { - result_holder->ConversionCompleted(result, success); - DrainCompletedPluginMessages(); -} - -void MessageChannel::DrainCompletedPluginMessages() { - if (early_message_queue_state_ == QUEUE_MESSAGES) - return; - - while (!plugin_message_queue_.empty() && - plugin_message_queue_.front().conversion_completed()) { - const VarConversionResult& front = plugin_message_queue_.front(); - if (front.success()) { - instance_->HandleMessage(front.var()); - } else { - PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(), - PP_LOGLEVEL_ERROR, - std::string(), - kV8ToVarConversionError); - } - plugin_message_queue_.pop_front(); - } -} - -void MessageChannel::DrainEarlyMessageQueue() { - DCHECK(early_message_queue_state_ == QUEUE_MESSAGES); - - // Take a reference on the PluginInstance. This is because JavaScript code - // may delete the plugin, which would destroy the PluginInstance and its - // corresponding MessageChannel. - scoped_refptr instance_ref(instance_); - while (!early_message_queue_.empty()) { - PostMessageToJavaScriptImpl(early_message_queue_.front()); - early_message_queue_.pop_front(); - } - early_message_queue_state_ = SEND_DIRECTLY; - - DrainCompletedPluginMessages(); -} - -void MessageChannel::PostMessageToJavaScriptImpl( - const WebSerializedScriptValue& message_data) { - DCHECK(instance_); - - WebPluginContainer* container = instance_->container(); - // It's possible that container() is NULL if the plugin has been removed from - // the DOM (but the PluginInstance is not destroyed yet). - if (!container) - return; - - WebDOMEvent event = - container->element().document().createEvent("MessageEvent"); - WebDOMMessageEvent msg_event = event.to(); - msg_event.initMessageEvent("message", // type - false, // canBubble - false, // cancelable - message_data, // data - "", // origin [*] - NULL, // source [*] - ""); // lastEventId - // [*] Note that the |origin| is only specified for cross-document and server- - // sent messages, while |source| is only specified for cross-document - // messages: - // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html - // This currently behaves like Web Workers. On Firefox, Chrome, and Safari - // at least, postMessage on Workers does not provide the origin or source. - // TODO(dmichael): Add origin if we change to a more iframe-like origin - // policy (see crbug.com/81537) - container->element().dispatchEvent(msg_event); -} - void MessageChannel::PostMessageToNative(const NPVariant* message_data) { EnqueuePluginMessage(message_data); DrainCompletedPluginMessages(); @@ -545,42 +478,109 @@ void MessageChannel::PostBlockingMessageToNative(const NPVariant* message_data, WebBindings::toNPVariant(v8_val, NULL, np_result); } -MessageChannel::~MessageChannel() { - WebBindings::releaseObject(np_object_); - if (passthrough_object_) - WebBindings::releaseObject(passthrough_object_); +void MessageChannel::PostMessageToJavaScriptImpl( + const WebSerializedScriptValue& message_data) { + DCHECK(instance_); + + WebPluginContainer* container = instance_->container(); + // It's possible that container() is NULL if the plugin has been removed from + // the DOM (but the PluginInstance is not destroyed yet). + if (!container) + return; + + WebDOMEvent event = + container->element().document().createEvent("MessageEvent"); + WebDOMMessageEvent msg_event = event.to(); + msg_event.initMessageEvent("message", // type + false, // canBubble + false, // cancelable + message_data, // data + "", // origin [*] + NULL, // source [*] + ""); // lastEventId + // [*] Note that the |origin| is only specified for cross-document and server- + // sent messages, while |source| is only specified for cross-document + // messages: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html + // This currently behaves like Web Workers. On Firefox, Chrome, and Safari + // at least, postMessage on Workers does not provide the origin or source. + // TODO(dmichael): Add origin if we change to a more iframe-like origin + // policy (see crbug.com/81537) + container->element().dispatchEvent(msg_event); } -void MessageChannel::SetPassthroughObject(NPObject* passthrough) { - // Retain the passthrough object; We need to ensure it lives as long as this - // MessageChannel. - if (passthrough) - WebBindings::retainObject(passthrough); +void MessageChannel::EnqueuePluginMessage(const NPVariant* variant) { + plugin_message_queue_.push_back(VarConversionResult()); + if (variant->type == NPVariantType_Object) { + // Convert NPVariantType_Object in to an appropriate PP_Var like Dictionary, + // Array, etc. Note NPVariantToVar would convert to an "Object" PP_Var, + // which we don't support for Messaging. - // If we had a passthrough set already, release it. Note that we retain the - // incoming passthrough object first, so that we behave correctly if anyone - // invokes: - // SetPassthroughObject(passthrough_object()); - if (passthrough_object_) - WebBindings::releaseObject(passthrough_object_); + // Calling WebBindings::toV8Value creates a wrapper around NPVariant so it + // won't result in a deep copy. + v8::Handle v8_value = WebBindings::toV8Value(variant); + V8VarConverter v8_var_converter(instance_->pp_instance()); + V8VarConverter::VarResult conversion_result = + v8_var_converter.FromV8Value( + v8_value, + v8::Isolate::GetCurrent()->GetCurrentContext(), + base::Bind(&MessageChannel::FromV8ValueComplete, + weak_ptr_factory_.GetWeakPtr(), + &plugin_message_queue_.back())); + if (conversion_result.completed_synchronously) { + plugin_message_queue_.back().ConversionCompleted( + conversion_result.var, + conversion_result.success); + } + } else { + plugin_message_queue_.back().ConversionCompleted( + ScopedPPVar(ScopedPPVar::PassRef(), + NPVariantToPPVar(instance(), variant)), + true); + DCHECK(plugin_message_queue_.back().var().get().type != PP_VARTYPE_OBJECT); + } +} - passthrough_object_ = passthrough; +void MessageChannel::FromV8ValueComplete(VarConversionResult* result_holder, + const ScopedPPVar& result, + bool success) { + result_holder->ConversionCompleted(result, success); + DrainCompletedPluginMessages(); } -bool MessageChannel::GetReadOnlyProperty(NPIdentifier key, - NPVariant* value) const { - std::map::const_iterator it = - internal_properties_.find(key); - if (it != internal_properties_.end()) { - if (value) - return PPVarToNPVariant(it->second.get(), value); - return true; +void MessageChannel::DrainCompletedPluginMessages() { + if (early_message_queue_state_ == QUEUE_MESSAGES) + return; + + while (!plugin_message_queue_.empty() && + plugin_message_queue_.front().conversion_completed()) { + const VarConversionResult& front = plugin_message_queue_.front(); + if (front.success()) { + instance_->HandleMessage(front.var()); + } else { + PpapiGlobals::Get()->LogWithSource(instance()->pp_instance(), + PP_LOGLEVEL_ERROR, + std::string(), + kV8ToVarConversionError); + } + plugin_message_queue_.pop_front(); } - return false; } -void MessageChannel::SetReadOnlyProperty(PP_Var key, PP_Var value) { - internal_properties_[PPVarToNPIdentifier(key)] = ScopedPPVar(value); +void MessageChannel::DrainEarlyMessageQueue() { + DCHECK(early_message_queue_state_ == QUEUE_MESSAGES); + + // Take a reference on the PluginInstance. This is because JavaScript code + // may delete the plugin, which would destroy the PluginInstance and its + // corresponding MessageChannel. + scoped_refptr instance_ref(instance_); + while (!early_message_queue_.empty()) { + PostMessageToJavaScriptImpl(early_message_queue_.front()); + early_message_queue_.pop_front(); + } + early_message_queue_state_ = SEND_DIRECTLY; + + DrainCompletedPluginMessages(); } } // namespace content diff --git a/content/renderer/pepper/message_channel.h b/content/renderer/pepper/message_channel.h index 8c744a044191c..4682399939d8c 100644 --- a/content/renderer/pepper/message_channel.h +++ b/content/renderer/pepper/message_channel.h @@ -53,17 +53,10 @@ class MessageChannel { explicit MessageChannel(PepperPluginInstanceImpl* instance); ~MessageChannel(); - // Post a message to the onmessage handler for this channel's instance - // asynchronously. - void PostMessageToJavaScript(PP_Var message_data); - - // Post a message to the plugin's HandleMessage function for this channel's - // instance. - void PostMessageToNative(const NPVariant* message_data); - // Post a message to the plugin's HandleBlocking Message function for this - // channel's instance synchronously, and return a result. - void PostBlockingMessageToNative(const NPVariant* message_data, - NPVariant* np_result); + // Messages are queued initially. After the PepperPluginInstanceImpl is ready + // to send and handle messages, users of MessageChannel should call + // Start(). + void Start(); // Return the NPObject* to which we should forward any calls which aren't // related to postMessage. Note that this can be NULL; it only gets set if @@ -76,24 +69,39 @@ class MessageChannel { PepperPluginInstanceImpl* instance() { return instance_; } - // Messages are queued initially. After the PepperPluginInstanceImpl is ready - // to send and handle messages, users of MessageChannel should call - // Start(). - void Start(); - bool GetReadOnlyProperty(NPIdentifier key, NPVariant* value) const; void SetReadOnlyProperty(PP_Var key, PP_Var value); + // Post a message to the onmessage handler for this channel's instance + // asynchronously. + void PostMessageToJavaScript(PP_Var message_data); + + // Post a message to the plugin's HandleMessage function for this channel's + // instance. + void PostMessageToNative(const NPVariant* message_data); + + // Post a message to the plugin's HandleBlocking Message function for this + // channel's instance synchronously, and return a result. + void PostBlockingMessageToNative(const NPVariant* message_data, + NPVariant* np_result); + private: // Struct for storing the result of a NPVariant being converted to a PP_Var. struct VarConversionResult; + // Post a message to the onmessage handler for this channel's instance + // synchronously. This is used by PostMessageToJavaScript. + void PostMessageToJavaScriptImpl( + const blink::WebSerializedScriptValue& message_data); + void EnqueuePluginMessage(const NPVariant* variant); void FromV8ValueComplete(VarConversionResult* result_holder, const ppapi::ScopedPPVar& result_var, bool success); + void DrainCompletedPluginMessages(); + void DrainEarlyMessageQueue(); PepperPluginInstanceImpl* instance_; @@ -107,16 +115,6 @@ class MessageChannel { // The NPObject we use to expose postMessage to JavaScript. MessageChannelNPObject* np_object_; - // Post a message to the onmessage handler for this channel's instance - // synchronously. This is used by PostMessageToJavaScript. - void PostMessageToJavaScriptImpl( - const blink::WebSerializedScriptValue& message_data); - // Post a message to the PPP_Instance HandleMessage function for this - // channel's instance. This is used by PostMessageToNative. - void PostMessageToNativeImpl(PP_Var message_data); - - void DrainEarlyMessageQueue(); - std::deque early_message_queue_; enum EarlyMessageQueueState { QUEUE_MESSAGES, // Queue JS messages. diff --git a/content/renderer/pepper/pepper_compositor_host.cc b/content/renderer/pepper/pepper_compositor_host.cc index f7e32b65f48de..6116a36be4180 100644 --- a/content/renderer/pepper/pepper_compositor_host.cc +++ b/content/renderer/pepper/pepper_compositor_host.cc @@ -252,7 +252,7 @@ void PepperCompositorHost::UpdateLayer( if (!old_layer || new_layer->common.resource_id != old_layer->common.resource_id) { cc::TextureMailbox mailbox(new_layer->texture->mailbox, - GL_TEXTURE_2D, + new_layer->texture->target, new_layer->texture->sync_point); texture_layer->SetTextureMailbox(mailbox, cc::SingleReleaseCallback::Create( diff --git a/content/renderer/pepper/pepper_media_device_manager.cc b/content/renderer/pepper/pepper_media_device_manager.cc index 3c5abdc80930d..aaf437f0f0228 100644 --- a/content/renderer/pepper/pepper_media_device_manager.cc +++ b/content/renderer/pepper/pepper_media_device_manager.cc @@ -54,8 +54,7 @@ int PepperMediaDeviceManager::EnumerateDevices( request_id, AsWeakPtr(), PepperMediaDeviceManager::FromPepperDeviceType(type), - document_url.GetOrigin(), - false); + document_url.GetOrigin()); #else base::MessageLoop::current()->PostTask( FROM_HERE, diff --git a/content/renderer/pepper/pepper_media_stream_audio_track_host.cc b/content/renderer/pepper/pepper_media_stream_audio_track_host.cc index 3069988773563..e834041482757 100644 --- a/content/renderer/pepper/pepper_media_stream_audio_track_host.cc +++ b/content/renderer/pepper/pepper_media_stream_audio_track_host.cc @@ -16,6 +16,7 @@ #include "ppapi/c/ppb_audio_buffer.h" #include "ppapi/host/dispatch_host_message.h" #include "ppapi/host/host_message_context.h" +#include "ppapi/host/ppapi_host.h" #include "ppapi/proxy/ppapi_messages.h" #include "ppapi/shared_impl/media_stream_audio_track_shared.h" #include "ppapi/shared_impl/media_stream_buffer.h" @@ -88,9 +89,16 @@ void PepperMediaStreamAudioTrackHost::AudioSink::EnqueueBuffer(int32_t index) { buffers_.push_back(index); } -void PepperMediaStreamAudioTrackHost::AudioSink::Configure( - int32_t number_of_buffers, int32_t duration) { +int32_t PepperMediaStreamAudioTrackHost::AudioSink::Configure( + int32_t number_of_buffers, int32_t duration, + const ppapi::host::ReplyMessageContext& context) { DCHECK_EQ(main_message_loop_proxy_, base::MessageLoopProxy::current()); + + if (pending_configure_reply_.is_valid()) { + return PP_ERROR_INPROGRESS; + } + pending_configure_reply_ = context; + bool changed = false; if (number_of_buffers != number_of_buffers_) changed = true; @@ -100,9 +108,25 @@ void PepperMediaStreamAudioTrackHost::AudioSink::Configure( } number_of_buffers_ = number_of_buffers; - // Initialize later in OnSetFormat if bytes_per_second_ is not know yet. - if (changed && bytes_per_second_ > 0 && bytes_per_frame_ > 0) - InitBuffers(); + if (changed) { + // Initialize later in OnSetFormat if bytes_per_second_ is not known yet. + if (bytes_per_second_ > 0 && bytes_per_frame_ > 0) + InitBuffers(); + } else { + SendConfigureReply(PP_OK); + } + return PP_OK_COMPLETIONPENDING; +} + +void PepperMediaStreamAudioTrackHost::AudioSink::SendConfigureReply( + int32_t result) { + if (pending_configure_reply_.is_valid()) { + pending_configure_reply_.params.set_result(result); + host_->host()->SendReply( + pending_configure_reply_, + PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply()); + pending_configure_reply_ = ppapi::host::ReplyMessageContext(); + } } void PepperMediaStreamAudioTrackHost::AudioSink::SetFormatOnMainThread( @@ -140,8 +164,10 @@ void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() { bool result = host_->InitBuffers(number_of_buffers_, buffer_size.ValueOrDie(), kRead); - // TODO(penghuang): Send PP_ERROR_NOMEMORY to plugin. - CHECK(result); + if (!result) { + SendConfigureReply(PP_ERROR_NOMEMORY); + return; + } // Fill the |buffers_|, so the audio thread can continue receiving audio data. base::AutoLock lock(lock_); @@ -151,6 +177,8 @@ void PepperMediaStreamAudioTrackHost::AudioSink::InitBuffers() { DCHECK_GE(index, 0); buffers_.push_back(index); } + + SendConfigureReply(PP_OK); } void PepperMediaStreamAudioTrackHost::AudioSink:: @@ -320,10 +348,8 @@ int32_t PepperMediaStreamAudioTrackHost::OnHostMsgConfigure( int32_t buffers = attributes.buffers ? std::min(kMaxNumberOfBuffers, attributes.buffers) : kDefaultNumberOfBuffers; - audio_sink_.Configure(buffers, attributes.duration); - - context->reply_msg = PpapiPluginMsg_MediaStreamAudioTrack_ConfigureReply(); - return PP_OK; + return audio_sink_.Configure(buffers, attributes.duration, + context->MakeReplyMessageContext()); } void PepperMediaStreamAudioTrackHost::OnClose() { @@ -331,6 +357,7 @@ void PepperMediaStreamAudioTrackHost::OnClose() { MediaStreamAudioSink::RemoveFromAudioTrack(&audio_sink_, track_); connected_ = false; } + audio_sink_.SendConfigureReply(PP_ERROR_ABORTED); } void PepperMediaStreamAudioTrackHost::OnNewBufferEnqueued() { diff --git a/content/renderer/pepper/pepper_media_stream_audio_track_host.h b/content/renderer/pepper/pepper_media_stream_audio_track_host.h index 4104128839a29..c1f33356f0b1f 100644 --- a/content/renderer/pepper/pepper_media_stream_audio_track_host.h +++ b/content/renderer/pepper/pepper_media_stream_audio_track_host.h @@ -15,6 +15,7 @@ #include "content/public/renderer/media_stream_audio_sink.h" #include "content/renderer/pepper/pepper_media_stream_track_host_base.h" #include "media/audio/audio_parameters.h" +#include "ppapi/host/host_message_context.h" #include "ppapi/shared_impl/media_stream_audio_track_shared.h" #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" @@ -45,7 +46,11 @@ class PepperMediaStreamAudioTrackHost : public PepperMediaStreamTrackHostBase { void EnqueueBuffer(int32_t index); // This function is called on the main thread. - void Configure(int32_t number_of_buffers, int32_t duration); + int32_t Configure(int32_t number_of_buffers, int32_t duration, + const ppapi::host::ReplyMessageContext& context); + + // Send a reply to the currently pending |Configure()| request. + void SendConfigureReply(int32_t result); private: // Initializes buffers on the main thread. @@ -137,6 +142,9 @@ class PepperMediaStreamAudioTrackHost : public PepperMediaStreamTrackHostBase { // User-configured buffer duration, in milliseconds. int32_t user_buffer_duration_; + // Pending |Configure()| reply context. + ppapi::host::ReplyMessageContext pending_configure_reply_; + DISALLOW_COPY_AND_ASSIGN(AudioSink); }; diff --git a/content/renderer/pepper/pepper_media_stream_video_track_host.cc b/content/renderer/pepper/pepper_media_stream_video_track_host.cc index 318965e6a2f95..32b16ef26a64a 100644 --- a/content/renderer/pepper/pepper_media_stream_video_track_host.cc +++ b/content/renderer/pepper/pepper_media_stream_video_track_host.cc @@ -539,8 +539,10 @@ void PepperMediaStreamVideoTrackHost::InitBlinkTrack() { } void PepperMediaStreamVideoTrackHost::OnTrackStarted( - MediaStreamSource* source, bool success) { - DVLOG(3) << "OnTrackStarted result: " << success; + MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name) { + DVLOG(3) << "OnTrackStarted result: " << result; } } // namespace content diff --git a/content/renderer/pepper/pepper_media_stream_video_track_host.h b/content/renderer/pepper/pepper_media_stream_video_track_host.h index b51f3ea47f861..ecf6e9eac5576 100644 --- a/content/renderer/pepper/pepper_media_stream_video_track_host.h +++ b/content/renderer/pepper/pepper_media_stream_video_track_host.h @@ -86,7 +86,9 @@ class PepperMediaStreamVideoTrackHost : public PepperMediaStreamTrackHostBase, const ppapi::MediaStreamVideoTrackShared::Attributes& attributes); void InitBlinkTrack(); - void OnTrackStarted(MediaStreamSource* source, bool success); + void OnTrackStarted(MediaStreamSource* source, + MediaStreamRequestResult result, + const blink::WebString& result_name); blink::WebMediaStreamTrack track_; diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc index 15fa1fe0d23f3..8c3db5202e519 100644 --- a/content/renderer/pepper/pepper_plugin_instance_impl.cc +++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc @@ -661,6 +661,18 @@ PepperPluginInstanceImpl::~PepperPluginInstanceImpl() { // If a method needs to access a member of the instance after the call has // returned, then it needs to keep its own reference on the stack. +v8::Local PepperPluginInstanceImpl::GetContext() { + if (!container_) + return v8::Handle(); + WebLocalFrame* frame = container_->element().document().frame(); + if (!frame) + return v8::Handle(); + + v8::Local context = frame->mainWorldScriptContext(); + DCHECK(context->GetIsolate() == isolate_); + return context; +} + void PepperPluginInstanceImpl::Delete() { is_deleted_ = true; diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.h b/content/renderer/pepper/pepper_plugin_instance_impl.h index ee66136bd4a0d..4b86e6e67c2eb 100644 --- a/content/renderer/pepper/pepper_plugin_instance_impl.h +++ b/content/renderer/pepper/pepper_plugin_instance_impl.h @@ -59,6 +59,7 @@ #include "ui/events/latency_info.h" #include "ui/gfx/rect.h" #include "url/gurl.h" +#include "v8/include/v8.h" struct PP_Point; struct _NPP; @@ -94,10 +95,6 @@ struct PPP_Instance_Combined; class ScopedPPVar; } -namespace v8 { -class Isolate; -} - namespace content { class ContentDecryptorDelegate; @@ -146,6 +143,9 @@ class CONTENT_EXPORT PepperPluginInstanceImpl return *resource_creation_.get(); } + // Return the v8 context that the plugin is in. + v8::Local GetContext(); + // Does some pre-destructor cleanup on the instance. This is necessary // because some cleanup depends on the plugin instance still existing (like // calling the plugin's DidDestroy function). This function is called from diff --git a/content/renderer/pepper/pepper_try_catch.cc b/content/renderer/pepper/pepper_try_catch.cc new file mode 100644 index 0000000000000..f5d8b3bf47b04 --- /dev/null +++ b/content/renderer/pepper/pepper_try_catch.cc @@ -0,0 +1,148 @@ +// 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 "content/renderer/pepper/pepper_try_catch.h" + +#include "content/renderer/pepper/pepper_plugin_instance_impl.h" +#include "content/renderer/pepper/v8_var_converter.h" +#include "gin/converter.h" +#include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/var_tracker.h" + +namespace content { + +namespace { + +const char kConversionException[] = + "Error: Failed conversion between PP_Var and V8 value"; +const char kInvalidException[] = "Error: An invalid exception was thrown."; + +} // namespace + +PepperTryCatch::PepperTryCatch(PepperPluginInstanceImpl* instance, + bool convert_objects) + : instance_(instance), + convert_objects_(convert_objects) {} + +PepperTryCatch::~PepperTryCatch() {} + +v8::Handle PepperTryCatch::GetContext() { + return instance_->GetContext(); +} + +v8::Handle PepperTryCatch::ToV8(PP_Var var) { + V8VarConverter converter(instance_->pp_instance(), convert_objects_); + v8::Handle result; + bool success = converter.ToV8Value(var, GetContext(), &result); + if (!success) { + SetException(kConversionException); + return v8::Handle(); + } + return result; +} + +ppapi::ScopedPPVar PepperTryCatch::FromV8(v8::Handle v8_value) { + if (v8_value.IsEmpty()) { + SetException(kConversionException); + return ppapi::ScopedPPVar(); + } + ppapi::ScopedPPVar result; + V8VarConverter converter(instance_->pp_instance(), convert_objects_); + bool success = converter.FromV8ValueSync(v8_value, GetContext(), &result); + if (!success) { + SetException(kConversionException); + return ppapi::ScopedPPVar(); + } + return result; +} + +PepperTryCatchV8::PepperTryCatchV8(PepperPluginInstanceImpl* instance, + bool convert_objects, + v8::Isolate* isolate) + : PepperTryCatch(instance, convert_objects), + exception_(PP_MakeUndefined()) { + // Typically when using PepperTryCatchV8 we are passed an isolate. We verify + // that this isolate is the same as the plugin isolate. + DCHECK(isolate == instance_->GetIsolate()); + // We assume we are already in the plugin context for PepperTryCatchV8. + DCHECK(GetContext() == isolate->GetCurrentContext()); +} + +PepperTryCatchV8::~PepperTryCatchV8() { + ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); +} + +bool PepperTryCatchV8::HasException() { + return exception_.type != PP_VARTYPE_UNDEFINED; +} + +bool PepperTryCatchV8::ThrowException() { + if (!HasException()) + return false; + + std::string message(kInvalidException); + ppapi::StringVar* message_var = ppapi::StringVar::FromPPVar(exception_); + if (message_var) + message = message_var->value(); + instance_->GetIsolate()->ThrowException(v8::Exception::Error( + gin::StringToV8(instance_->GetIsolate(), message))); + + ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(exception_); + exception_ = PP_MakeUndefined(); + return true; +} + +void PepperTryCatchV8::ThrowException(const char* message) { + SetException(message); + ThrowException(); +} + +void PepperTryCatchV8::SetException(const char* message) { + if (HasException()) { + NOTREACHED(); + return; + } + exception_ = ppapi::StringVar::StringToPPVar(message); +} + +PepperTryCatchVar::PepperTryCatchVar(PepperPluginInstanceImpl* instance, + bool convert_objects, + PP_Var* exception) + : PepperTryCatch(instance, convert_objects), + handle_scope_(instance_->GetIsolate()), + exception_(exception), + exception_is_set_(false) { + // We switch to the plugin context. + GetContext()->Enter(); +} + +PepperTryCatchVar::~PepperTryCatchVar() { + GetContext()->Exit(); +} + +bool PepperTryCatchVar::HasException() { + // Check if a v8 exception was caught. + if (!exception_is_set_ && try_catch_.HasCaught()) { + v8::String::Utf8Value utf8(try_catch_.Message()->Get()); + if (exception_) { + *exception_ = ppapi::StringVar::StringToPPVar( + std::string(*utf8, utf8.length())); + } + exception_is_set_ = true; + } + + return exception_is_set_; +} + +void PepperTryCatchVar::SetException(const char* message) { + if (exception_is_set_) { + NOTREACHED(); + return; + } + if (exception_) + *exception_ = ppapi::StringVar::StringToPPVar(message, strlen(message)); + exception_is_set_ = true; +} + +} // namespace content diff --git a/content/renderer/pepper/pepper_try_catch.h b/content/renderer/pepper/pepper_try_catch.h new file mode 100644 index 0000000000000..f82c0c31080ca --- /dev/null +++ b/content/renderer/pepper/pepper_try_catch.h @@ -0,0 +1,94 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_PEPPER_PEPPER_TRY_CATCH_H_ +#define CONTENT_RENDERER_PEPPER_PEPPER_TRY_CATCH_H_ + +#include "base/basictypes.h" +#include "ppapi/c/pp_var.h" +#include "ppapi/shared_impl/scoped_pp_var.h" +#include "v8/include/v8.h" + +namespace content { + +class PepperPluginInstanceImpl; + +// Base class for scripting TryCatch helpers. +class PepperTryCatch { + public: + PepperTryCatch(PepperPluginInstanceImpl* instance, + bool convert_objects); + virtual ~PepperTryCatch(); + + virtual void SetException(const char* message) = 0; + // Gets the plugin context. Virtual so it can be overriden for testing. + virtual v8::Handle GetContext(); + + // Convenience functions for doing conversions to/from V8 values and sets an + // exception if there is an error in the conversion. + v8::Handle ToV8(PP_Var var); + ppapi::ScopedPPVar FromV8(v8::Handle v8_value); + + protected: + PepperPluginInstanceImpl* instance_; + + // Whether To/FromV8 should convert object vars. If set to false, an exception + // should be set if they are encountered during conversion. + bool convert_objects_; +}; + +// Catches var exceptions and emits a v8 exception. +class PepperTryCatchV8 : public PepperTryCatch { + public: + PepperTryCatchV8(PepperPluginInstanceImpl* instance, + bool convert_objects, + v8::Isolate* isolate); + virtual ~PepperTryCatchV8(); + + bool HasException(); + bool ThrowException(); + void ThrowException(const char* message); + PP_Var* exception() { return &exception_; } + + // PepperTryCatch + virtual void SetException(const char* message) OVERRIDE; + + private: + PP_Var exception_; + + DISALLOW_COPY_AND_ASSIGN(PepperTryCatchV8); +}; + +// Catches v8 exceptions and emits a var exception. +class PepperTryCatchVar : public PepperTryCatch { + public: + // The PP_Var exception will be placed in |exception|. The user of this class + // is responsible for managing the lifetime of the exception. It is valid to + // pass NULL for |exception| in which case no exception will be set. + PepperTryCatchVar(PepperPluginInstanceImpl* instance, + bool convert_objects, + PP_Var* exception); + virtual ~PepperTryCatchVar(); + + bool HasException(); + + // PepperTryCatch + virtual void SetException(const char* message) OVERRIDE; + + private: + // Code which uses PepperTryCatchVar doesn't typically have a HandleScope, + // make one for them. Note that this class is always allocated on the stack. + v8::HandleScope handle_scope_; + + v8::TryCatch try_catch_; + + PP_Var* exception_; + bool exception_is_set_; + + DISALLOW_COPY_AND_ASSIGN(PepperTryCatchVar); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_PEPPER_PEPPER_TRY_CATCH_H_ diff --git a/content/renderer/pepper/pepper_video_decoder_host.cc b/content/renderer/pepper/pepper_video_decoder_host.cc index 4fc2d2c8b6e48..3f39c940e74ed 100644 --- a/content/renderer/pepper/pepper_video_decoder_host.cc +++ b/content/renderer/pepper/pepper_video_decoder_host.cc @@ -53,10 +53,10 @@ media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) { return media::H264PROFILE_STEREOHIGH; case PP_VIDEOPROFILE_H264MULTIVIEWHIGH: return media::H264PROFILE_MULTIVIEWHIGH; - case PP_VIDEOPROFILE_VP8MAIN: - return media::VP8PROFILE_MAIN; - case PP_VIDEOPROFILE_VP9MAIN: - return media::VP9PROFILE_MAIN; + case PP_VIDEOPROFILE_VP8_ANY: + return media::VP8PROFILE_ANY; + case PP_VIDEOPROFILE_VP9_ANY: + return media::VP9PROFILE_ANY; // No default case, to catch unhandled PP_VideoProfile values. } @@ -313,6 +313,8 @@ void PepperVideoDecoderHost::ProvidePictureBuffers( } void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) { + // So far picture.visible_rect is not used. If used, visible_rect should + // be validated since it comes from GPU process and may not be trustworthy. host()->SendUnsolicitedReply( pp_resource(), PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(), diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc index d5de5e7139872..78b21427166e9 100644 --- a/content/renderer/pepper/plugin_module.cc +++ b/content/renderer/pepper/plugin_module.cc @@ -281,12 +281,17 @@ void SetMinimumArrayBufferSizeForShmem(PP_Instance /*instance*/, // Does nothing. Not needed in-process. } +void RunV8GC(PP_Instance instance) { + content::PepperPluginInstance::Get(instance)->GetIsolate()-> + RequestGarbageCollectionForTesting(v8::Isolate::kFullGarbageCollection); +} + const PPB_Testing_Private testing_interface = { &ReadImageData, &RunMessageLoop, &QuitMessageLoop, &GetLiveObjectsForInstance, &IsOutOfProcess, &SimulateInputEvent, &GetDocumentURL, &GetLiveVars, - &SetMinimumArrayBufferSizeForShmem}; + &SetMinimumArrayBufferSizeForShmem,&RunV8GC}; // GetInterface ---------------------------------------------------------------- diff --git a/content/renderer/pepper/ppb_video_decoder_impl.cc b/content/renderer/pepper/ppb_video_decoder_impl.cc index 01be7b7f45deb..53b2ef96b911a 100644 --- a/content/renderer/pepper/ppb_video_decoder_impl.cc +++ b/content/renderer/pepper/ppb_video_decoder_impl.cc @@ -65,8 +65,8 @@ media::VideoCodecProfile PPToMediaProfile( return media::H264PROFILE_STEREOHIGH; case PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH: return media::H264PROFILE_MULTIVIEWHIGH; - case PP_VIDEODECODER_VP8PROFILE_MAIN: - return media::VP8PROFILE_MAIN; + case PP_VIDEODECODER_VP8PROFILE_ANY: + return media::VP8PROFILE_ANY; default: return media::VIDEO_CODEC_PROFILE_UNKNOWN; } @@ -246,6 +246,8 @@ void PPB_VideoDecoder_Impl::ProvidePictureBuffers( } void PPB_VideoDecoder_Impl::PictureReady(const media::Picture& picture) { + // So far picture.visible_rect is not used. If used, visible_rect should + // be validated since it comes from GPU process and may not be trustworthy. DCHECK(RenderThreadImpl::current()); if (!ppp_videodecoder_) return; diff --git a/content/renderer/pepper/v8_var_converter.cc b/content/renderer/pepper/v8_var_converter.cc index b09fccd63e4dd..6923776f80b6f 100644 --- a/content/renderer/pepper/v8_var_converter.cc +++ b/content/renderer/pepper/v8_var_converter.cc @@ -15,7 +15,9 @@ #include "base/memory/scoped_ptr.h" #include "content/public/renderer/renderer_ppapi_host.h" #include "content/renderer/pepper/host_array_buffer_var.h" +#include "content/renderer/pepper/host_globals.h" #include "content/renderer/pepper/resource_converter.h" +#include "content/renderer/pepper/v8object_var.h" #include "ppapi/shared_impl/array_var.h" #include "ppapi/shared_impl/dictionary_var.h" #include "ppapi/shared_impl/var.h" @@ -28,6 +30,7 @@ using ppapi::ArrayVar; using ppapi::DictionaryVar; using ppapi::ScopedPPVar; using ppapi::StringVar; +using ppapi::V8ObjectVar; using std::make_pair; namespace { @@ -80,6 +83,7 @@ typedef base::hash_set ParentHandleSet; // value was created as a result of calling the function. bool GetOrCreateV8Value(v8::Handle context, const PP_Var& var, + bool object_vars_allowed, v8::Handle* result, bool* did_create, VarHandleMap* visited_ids, @@ -150,9 +154,17 @@ bool GetOrCreateV8Value(v8::Handle context, case PP_VARTYPE_DICTIONARY: *result = v8::Object::New(isolate); break; - case PP_VARTYPE_OBJECT: - result->Clear(); - return false; + case PP_VARTYPE_OBJECT: { + DCHECK(object_vars_allowed); + scoped_refptr v8_object_var = V8ObjectVar::FromPPVar(var); + if (!v8_object_var) { + NOTREACHED(); + result->Clear(); + return false; + } + *result = v8_object_var->GetHandle(); + break; + } case PP_VARTYPE_RESOURCE: if (!resource_converter->ToV8Value(var, context, result)) { result->Clear(); @@ -174,6 +186,8 @@ bool GetOrCreateV8Value(v8::Handle context, // calling the function. bool GetOrCreateVar(v8::Handle val, v8::Handle context, + PP_Instance instance, + bool object_vars_allowed, PP_Var* result, bool* did_create, HandleVarMap* visited_handles, @@ -197,6 +211,7 @@ bool GetOrCreateVar(v8::Handle val, } } + v8::Isolate* isolate = context->GetIsolate(); if (val->IsUndefined()) { *result = PP_MakeUndefined(); } else if (val->IsNull()) { @@ -214,12 +229,15 @@ bool GetOrCreateVar(v8::Handle val, *result = (new ArrayVar())->GetPPVar(); } else if (val->IsObject()) { scoped_ptr web_array_buffer( - blink::WebArrayBufferConverter::createFromV8Value( - val, context->GetIsolate())); + blink::WebArrayBufferConverter::createFromV8Value(val, isolate)); if (web_array_buffer.get()) { scoped_refptr buffer_var( new HostArrayBufferVar(*web_array_buffer)); *result = buffer_var->GetPPVar(); + } else if (object_vars_allowed) { + v8::Handle object = val->ToObject(); + *result = content::HostGlobals::Get()-> + host_var_tracker()->V8ObjectVarForV8Object(instance, object); } else { bool was_resource; if (!resource_converter->FromV8Value( @@ -252,14 +270,26 @@ bool CanHaveChildren(PP_Var var) { } // namespace V8VarConverter::V8VarConverter(PP_Instance instance) - : message_loop_proxy_(base::MessageLoopProxy::current()) { + : instance_(instance), + object_vars_allowed_(false), + message_loop_proxy_(base::MessageLoopProxy::current()) { + resource_converter_.reset(new ResourceConverterImpl( + instance, RendererPpapiHost::GetForPPInstance(instance))); +} + +V8VarConverter::V8VarConverter(PP_Instance instance, bool object_vars_allowed) + : instance_(instance), + object_vars_allowed_(object_vars_allowed), + message_loop_proxy_(base::MessageLoopProxy::current()) { resource_converter_.reset(new ResourceConverterImpl( instance, RendererPpapiHost::GetForPPInstance(instance))); } V8VarConverter::V8VarConverter(PP_Instance instance, scoped_ptr resource_converter) - : message_loop_proxy_(base::MessageLoopProxy::current()), + : instance_(instance), + object_vars_allowed_(false), + message_loop_proxy_(base::MessageLoopProxy::current()), resource_converter_(resource_converter.release()) {} V8VarConverter::~V8VarConverter() {} @@ -305,6 +335,7 @@ bool V8VarConverter::ToV8Value(const PP_Var& var, bool did_create = false; if (!GetOrCreateV8Value(context, current_var, + object_vars_allowed_, ¤t_v8, &did_create, &visited_ids, @@ -334,6 +365,7 @@ bool V8VarConverter::ToV8Value(const PP_Var& var, v8::Handle child_v8; if (!GetOrCreateV8Value(context, child_var, + object_vars_allowed_, &child_v8, &did_create, &visited_ids, @@ -369,6 +401,7 @@ bool V8VarConverter::ToV8Value(const PP_Var& var, v8::Handle child_v8; if (!GetOrCreateV8Value(context, child_var, + object_vars_allowed_, &child_v8, &did_create, &visited_ids, @@ -455,6 +488,8 @@ bool V8VarConverter::FromV8ValueInternal( bool did_create = false; if (!GetOrCreateVar(current_v8, context, + instance_, + object_vars_allowed_, ¤t_var, &did_create, &visited_handles, @@ -492,6 +527,8 @@ bool V8VarConverter::FromV8ValueInternal( PP_Var child_var; if (!GetOrCreateVar(child_v8, context, + instance_, + object_vars_allowed_, &child_var, &did_create, &visited_handles, @@ -541,6 +578,8 @@ bool V8VarConverter::FromV8ValueInternal( PP_Var child_var; if (!GetOrCreateVar(child_v8, context, + instance_, + object_vars_allowed_, &child_var, &did_create, &visited_handles, diff --git a/content/renderer/pepper/v8_var_converter.h b/content/renderer/pepper/v8_var_converter.h index 42ef7a647f5dc..e6206cd958204 100644 --- a/content/renderer/pepper/v8_var_converter.h +++ b/content/renderer/pepper/v8_var_converter.h @@ -22,6 +22,8 @@ class ResourceConverter; class CONTENT_EXPORT V8VarConverter { public: explicit V8VarConverter(PP_Instance instance); + V8VarConverter(PP_Instance instance, bool object_vars_allowed); + // Constructor for testing. V8VarConverter(PP_Instance instance, scoped_ptr resource_converter); @@ -71,6 +73,11 @@ class CONTENT_EXPORT V8VarConverter { v8::Handle context, ppapi::ScopedPPVar* result_var); + PP_Instance instance_; + + // Whether or not to support conversion to PP_VARTYPE_OBJECT. + bool object_vars_allowed_; + // The message loop to run the callback to |FromV8Value| from. scoped_refptr message_loop_proxy_; diff --git a/content/renderer/pepper/v8object_var.cc b/content/renderer/pepper/v8object_var.cc new file mode 100644 index 0000000000000..faf8bef8e7a49 --- /dev/null +++ b/content/renderer/pepper/v8object_var.cc @@ -0,0 +1,62 @@ +// 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 "content/renderer/pepper/v8object_var.h" + +#include "base/logging.h" +#include "content/public/renderer/pepper_plugin_instance.h" +#include "content/renderer/pepper/host_globals.h" +#include "content/renderer/pepper/pepper_plugin_instance_impl.h" +#include "ppapi/c/pp_var.h" + +namespace ppapi { + +// V8ObjectVar ----------------------------------------------------------------- + +V8ObjectVar::V8ObjectVar(PP_Instance instance, + v8::Handle v8_object) + : instance_(content::HostGlobals::Get()->GetInstance(instance)) { + v8_object_.Reset(instance_->GetIsolate(), v8_object); + content::HostGlobals::Get()->host_var_tracker()->AddV8ObjectVar(this); +} + +V8ObjectVar::~V8ObjectVar() { + if (instance_) + content::HostGlobals::Get()->host_var_tracker()->RemoveV8ObjectVar(this); + v8_object_.Reset(); +} + +V8ObjectVar* V8ObjectVar::AsV8ObjectVar() { + return this; +} + +PP_VarType V8ObjectVar::GetType() const { + return PP_VARTYPE_OBJECT; +} + +v8::Local V8ObjectVar::GetHandle() const { + if (instance_) + return v8::Local::New(instance_->GetIsolate(), v8_object_); + return v8::Local(); +} + +void V8ObjectVar::InstanceDeleted() { + // This is called by the HostVarTracker which will take care of removing us + // from its set. + DCHECK(instance_); + instance_ = NULL; +} + +// static +scoped_refptr V8ObjectVar::FromPPVar(PP_Var var) { + if (var.type != PP_VARTYPE_OBJECT) + return scoped_refptr(NULL); + scoped_refptr var_object( + PpapiGlobals::Get()->GetVarTracker()->GetVar(var)); + if (!var_object.get()) + return scoped_refptr(); + return scoped_refptr(var_object->AsV8ObjectVar()); +} + +} // namespace ppapi diff --git a/content/renderer/pepper/v8object_var.h b/content/renderer/pepper/v8object_var.h new file mode 100644 index 0000000000000..79af9e369d778 --- /dev/null +++ b/content/renderer/pepper/v8object_var.h @@ -0,0 +1,66 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_PEPPER_V8OBJECT_VAR_H_ +#define CONTENT_RENDERER_PEPPER_V8OBJECT_VAR_H_ + +#include + +#include "base/compiler_specific.h" +#include "content/common/content_export.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/var.h" +#include "v8/include/v8.h" + +namespace content { +class PepperPluginInstanceImpl; +} // namespace content + +namespace ppapi { + +// V8ObjectVar ----------------------------------------------------------------- + +// Represents a JavaScript object Var. By itself, this represents random +// v8 objects that a given plugin (identified by the resource's module) wants to +// reference. If two different modules reference the same NPObject (like the +// "window" object), then there will be different V8ObjectVar's (and hence +// PP_Var IDs) for each module. This allows us to track all references owned by +// a given module and free them when the plugin exits independently of other +// plugins that may be running at the same time. +class V8ObjectVar : public Var { + public: + V8ObjectVar(PP_Instance instance, v8::Handle v8_object); + + // Var overrides. + virtual V8ObjectVar* AsV8ObjectVar() OVERRIDE; + virtual PP_VarType GetType() const OVERRIDE; + + // Returns the underlying v8 object corresponding to this V8ObjectVar. This + // should only be used on the stack. + v8::Local GetHandle() const; + + // Notification that the instance was deleted, the internal reference will be + // zeroed out. + void InstanceDeleted(); + + // Possibly NULL if the object has outlived its instance. + content::PepperPluginInstanceImpl* instance() const { return instance_; } + + // Helper function that converts a PP_Var to an object. This will return NULL + // if the PP_Var is not of object type or the object is invalid. + CONTENT_EXPORT static scoped_refptr FromPPVar(PP_Var var); + + private: + virtual ~V8ObjectVar(); + + content::PepperPluginInstanceImpl* instance_; + + v8::Persistent v8_object_; + + DISALLOW_COPY_AND_ASSIGN(V8ObjectVar); +}; + +} // ppapi + +#endif // CONTENT_RENDERER_PEPPER_V8OBJECT_VAR_H_ diff --git a/content/renderer/pepper/video_decoder_shim.cc b/content/renderer/pepper/video_decoder_shim.cc index c52c285a10291..d213ac9f5a277 100644 --- a/content/renderer/pepper/video_decoder_shim.cc +++ b/content/renderer/pepper/video_decoder_shim.cc @@ -47,11 +47,14 @@ VideoDecoderShim::PendingDecode::~PendingDecode() { struct VideoDecoderShim::PendingFrame { explicit PendingFrame(uint32_t decode_id); - PendingFrame(uint32_t decode_id, const gfx::Size& size); + PendingFrame(uint32_t decode_id, + const gfx::Size& coded_size, + const gfx::Rect& visible_rect); ~PendingFrame(); const uint32_t decode_id; - const gfx::Size size; + const gfx::Size coded_size; + const gfx::Rect visible_rect; std::vector argb_pixels; private: @@ -64,10 +67,12 @@ VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id) } VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id, - const gfx::Size& size) + const gfx::Size& coded_size, + const gfx::Rect& visible_rect) : decode_id(decode_id), - size(size), - argb_pixels(size.width() * size.height() * 4) { + coded_size(coded_size), + visible_rect(visible_rect), + argb_pixels(coded_size.width() * coded_size.height() * 4) { } VideoDecoderShim::PendingFrame::~PendingFrame() { @@ -258,7 +263,8 @@ void VideoDecoderShim::DecoderImpl::OnOutputComplete( const scoped_refptr& frame) { scoped_ptr pending_frame; if (!frame->end_of_stream()) { - pending_frame.reset(new PendingFrame(decode_id_, frame->coded_size())); + pending_frame.reset(new PendingFrame( + decode_id_, frame->coded_size(), frame->visible_rect())); // Convert the VideoFrame pixels to ABGR to match VideoDecodeAccelerator. libyuv::I420ToABGR(frame->data(media::VideoFrame::kYPlane), frame->stride(media::VideoFrame::kYPlane), @@ -470,7 +476,7 @@ void VideoDecoderShim::OnOutputComplete(scoped_ptr frame) { DCHECK(host_); if (!frame->argb_pixels.empty()) { - if (texture_size_ != frame->size) { + if (texture_size_ != frame->coded_size) { // If the size has changed, all current textures must be dismissed. Add // all textures to |textures_to_dismiss_| and dismiss any that aren't in // use by the plugin. We will dismiss the rest as they are recycled. @@ -492,10 +498,10 @@ void VideoDecoderShim::OnOutputComplete(scoped_ptr frame) { pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate()); host_->RequestTextures(texture_pool_size_, - frame->size, + frame->coded_size, GL_TEXTURE_2D, pending_texture_mailboxes_); - texture_size_ = frame->size; + texture_size_ = frame->coded_size; } pending_frames_.push(linked_ptr(frame.release())); @@ -527,7 +533,8 @@ void VideoDecoderShim::SendPictures() { GL_UNSIGNED_BYTE, &frame->argb_pixels.front()); - host_->PictureReady(media::Picture(texture_id, frame->decode_id)); + host_->PictureReady( + media::Picture(texture_id, frame->decode_id, frame->visible_rect)); pending_frames_.pop(); } diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index aff64f2ad5059..7458aa41bdf15 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -144,6 +144,11 @@ #include "content/renderer/media/crypto/renderer_cdm_manager.h" #endif +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) +#include "xwalk/tizen/renderer/media/renderer_mediaplayer_manager.h" +#include "xwalk/tizen/renderer/media/mediaplayer_impl.h" +#endif + using blink::WebContextMenuData; using blink::WebData; using blink::WebDataSource; @@ -443,6 +448,9 @@ RenderFrameImpl::RenderFrameImpl(RenderViewImpl* render_view, int routing_id) #endif #if defined(VIDEO_HOLE) contains_media_player_(false), +#endif +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) + media_player_manager_(NULL), #endif geolocation_dispatcher_(NULL), push_messaging_dispatcher_(NULL), @@ -764,6 +772,8 @@ bool RenderFrameImpl::OnMessageReceived(const IPC::Message& msg) { OnJavaScriptExecuteRequest) IPC_MESSAGE_HANDLER(FrameMsg_SetEditableSelectionOffsets, OnSetEditableSelectionOffsets) + IPC_MESSAGE_HANDLER(FrameMsg_SetupTransitionView, OnSetupTransitionView) + IPC_MESSAGE_HANDLER(FrameMsg_BeginExitTransition, OnBeginExitTransition) IPC_MESSAGE_HANDLER(FrameMsg_Reload, OnReload) IPC_MESSAGE_HANDLER(FrameMsg_TextSurroundingSelectionRequest, OnTextSurroundingSelectionRequest) @@ -1292,6 +1302,16 @@ void RenderFrameImpl::OnAddStyleSheetByURL(const std::string& url) { frame_->addStyleSheetByURL(WebString::fromUTF8(url)); } +void RenderFrameImpl::OnSetupTransitionView(const std::string& markup) { + frame_->document().setIsTransitionDocument(); + frame_->navigateToSandboxedMarkup(WebData(markup.data(), markup.length())); +} + +void RenderFrameImpl::OnBeginExitTransition(const std::string& css_selector) { + frame_->document().setIsTransitionDocument(); + frame_->document().beginExitTransition(WebString::fromUTF8(css_selector)); +} + bool RenderFrameImpl::ShouldUpdateSelectionTextFromContextMenuParams( const base::string16& selection_text, size_t selection_text_offset, @@ -1501,11 +1521,29 @@ blink::WebMediaPlayer* RenderFrameImpl::createMediaPlayer( static_cast(this)), RenderThreadImpl::current()->GetAudioRendererMixerManager()->CreateInput( render_view_->routing_id_, routing_id_)); + +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) + tizen::MediaPlayerImpl* media_player = new tizen::MediaPlayerImpl( + frame, client, weak_factory_.GetWeakPtr(), + GetTizenMediaPlayerManager(), params); + return media_player; +#endif + return new WebMediaPlayerImpl(frame, client, weak_factory_.GetWeakPtr(), params); #endif // defined(OS_ANDROID) } +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) +tizen::RendererMediaPlayerManager* +RenderFrameImpl::GetTizenMediaPlayerManager() { + if (!media_player_manager_) + media_player_manager_ = new tizen::RendererMediaPlayerManager(this); + + return media_player_manager_; +} +#endif + blink::WebContentDecryptionModule* RenderFrameImpl::createContentDecryptionModule( blink::WebLocalFrame* frame, @@ -2280,6 +2318,15 @@ void RenderFrameImpl::didUpdateCurrentHistoryItem(blink::WebLocalFrame* frame) { render_view_->didUpdateCurrentHistoryItem(frame); } +void RenderFrameImpl::addNavigationTransitionData( + const blink::WebString& allowed_destination_host_pattern, + const blink::WebString& selector, + const blink::WebString& markup) { + Send(new FrameHostMsg_AddNavigationTransitionData( + routing_id_, allowed_destination_host_pattern.utf8(), selector.utf8(), + markup.utf8())); +} + void RenderFrameImpl::didChangeThemeColor() { if (frame_->parent()) return; diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index dc14873250c59..7fdbe2404589c 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h @@ -56,6 +56,12 @@ class Range; class Rect; } +#if defined(OS_TIZEN) && defined(ENABLE_MURPHY) +namespace tizen { +class RendererMediaPlayerManager; +} +#endif + namespace content { class ChildFrameCompositingHelper; @@ -345,6 +351,10 @@ class CONTENT_EXPORT RenderFrameImpl const blink::WebHistoryItem& item, blink::WebHistoryCommitType commit_type); virtual void didUpdateCurrentHistoryItem(blink::WebLocalFrame* frame); + virtual void addNavigationTransitionData( + const blink::WebString& allowedDestinationOrigin, + const blink::WebString& selector, + const blink::WebString& markup); virtual void didChangeThemeColor(); virtual void requestNotificationPermission( const blink::WebSecurityOrigin& origin, @@ -508,6 +518,8 @@ class CONTENT_EXPORT RenderFrameImpl void OnReload(bool ignore_cache); void OnTextSurroundingSelectionRequest(size_t max_length); void OnAddStyleSheetByURL(const std::string& url); + void OnSetupTransitionView(const std::string& markup); + void OnBeginExitTransition(const std::string& css_selector); void OnSetAccessibilityMode(AccessibilityMode new_mode); #if defined(OS_MACOSX) void OnCopyToFindPboard(); @@ -585,6 +597,8 @@ class CONTENT_EXPORT RenderFrameImpl blink::WebMediaPlayerClient* client); RendererMediaPlayerManager* GetMediaPlayerManager(); +#elif defined(OS_TIZEN) && defined(ENABLE_MURPHY) + tizen::RendererMediaPlayerManager* GetTizenMediaPlayerManager(); #endif #if defined(ENABLE_BROWSER_CDMS) @@ -669,6 +683,8 @@ class CONTENT_EXPORT RenderFrameImpl // real media player in the browser process. It's okay to use a raw pointer // since it's a RenderFrameObserver. RendererMediaPlayerManager* media_player_manager_; +#elif defined(OS_TIZEN) && defined(ENABLE_MURPHY) + tizen::RendererMediaPlayerManager* media_player_manager_; #endif #if defined(ENABLE_BROWSER_CDMS) diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc index d0db6f87ea825..3975856b9b0a1 100644 --- a/content/renderer/render_thread_impl.cc +++ b/content/renderer/render_thread_impl.cc @@ -15,6 +15,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/discardable_memory.h" +#include "base/memory/discardable_memory_emulated.h" #include "base/memory/shared_memory.h" #include "base/metrics/field_trial.h" #include "base/metrics/histogram.h" @@ -82,13 +83,9 @@ #include "content/renderer/media/audio_renderer_mixer_manager.h" #include "content/renderer/media/media_stream_center.h" #include "content/renderer/media/midi_message_filter.h" -#include "content/renderer/media/peer_connection_tracker.h" #include "content/renderer/media/renderer_gpu_video_accelerator_factories.h" -#include "content/renderer/media/rtc_peer_connection_handler.h" #include "content/renderer/media/video_capture_impl_manager.h" #include "content/renderer/media/video_capture_message_filter.h" -#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" -#include "content/renderer/media/webrtc_identity_service.h" #include "content/renderer/net_info_helper.h" #include "content/renderer/p2p/socket_dispatcher.h" #include "content/renderer/render_frame_proxy.h" @@ -153,6 +150,13 @@ #include "content/renderer/npapi/plugin_channel_host.h" #endif +#if defined(ENABLE_WEBRTC) +#include "content/renderer/media/peer_connection_tracker.h" +#include "content/renderer/media/rtc_peer_connection_handler.h" +#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" +#include "content/renderer/media/webrtc_identity_service.h" +#endif + using base::ThreadRestrictions; using blink::WebDocument; using blink::WebFrame; @@ -180,6 +184,9 @@ const int kMaxRasterThreads = 64; // allocation that exceeds this limit. const size_t kImageCacheSingleAllocationByteLimit = 64 * 1024 * 1024; +const size_t kEmulatedDiscardableMemoryBytesToKeepWhenWidgetsHidden = + 4 * 1024 * 1024; + // Keep the global RenderThreadImpl in a TLS slot so it is impossible to access // incorrectly from the wrong thread. base::LazyInstance > @@ -302,6 +309,16 @@ bool ShouldUseMojoChannel() { switches::kEnableRendererMojoChannel); } +blink::WebGraphicsContext3D::Attributes GetOffscreenAttribs() { + blink::WebGraphicsContext3D::Attributes attributes; + attributes.shareResources = true; + attributes.depth = false; + attributes.stencil = false; + attributes.antialias = false; + attributes.noAutomaticFlushes = true; + return attributes; +} + } // namespace // For measuring memory usage after each task. Behind a command line flag. @@ -536,10 +553,6 @@ void RenderThreadImpl::Init() { base::DiscardableMemory::SetPreferredType(type); - // Allow discardable memory implementations to register memory pressure - // listeners. - base::DiscardableMemory::RegisterMemoryPressureListeners(); - // AllocateGpuMemoryBuffer must be used exclusively on one thread but // it doesn't have to be the same thread RenderThreadImpl is created on. allocate_gpu_memory_buffer_thread_checker_.DetachFromThread(); @@ -1132,12 +1145,7 @@ RenderThreadImpl::GetGpuFactories() { scoped_ptr RenderThreadImpl::CreateOffscreenContext3d() { - blink::WebGraphicsContext3D::Attributes attributes; - attributes.shareResources = true; - attributes.depth = false; - attributes.stencil = false; - attributes.antialias = false; - attributes.noAutomaticFlushes = true; + blink::WebGraphicsContext3D::Attributes attributes(GetOffscreenAttribs()); bool lose_context_when_out_of_memory = true; scoped_refptr gpu_channel_host(EstablishGpuChannelSync( @@ -1155,16 +1163,20 @@ RenderThreadImpl::CreateOffscreenContext3d() { scoped_refptr RenderThreadImpl::SharedMainThreadContextProvider() { DCHECK(IsMainThread()); -#if defined(OS_ANDROID) - if (SynchronousCompositorFactory* factory = - SynchronousCompositorFactory::GetInstance()) - return factory->GetSharedOffscreenContextProviderForMainThread(); -#endif - if (!shared_main_thread_contexts_ || shared_main_thread_contexts_->DestroyedOnMainThread()) { - shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create( - CreateOffscreenContext3d(), "Offscreen-MainThread"); + shared_main_thread_contexts_ = NULL; +#if defined(OS_ANDROID) + if (SynchronousCompositorFactory* factory = + SynchronousCompositorFactory::GetInstance()) { + shared_main_thread_contexts_ = factory->CreateOffscreenContextProvider( + GetOffscreenAttribs(), "Offscreen-MainThread"); + } +#endif + if (!shared_main_thread_contexts_) { + shared_main_thread_contexts_ = ContextProviderCommandBuffer::Create( + CreateOffscreenContext3d(), "Offscreen-MainThread"); + } } if (shared_main_thread_contexts_ && !shared_main_thread_contexts_->BindToCurrentThread()) @@ -1453,10 +1465,12 @@ blink::WebMediaStreamCenter* RenderThreadImpl::CreateMediaStreamCenter( return media_stream_center_; } +#if defined(ENABLE_WEBRTC) PeerConnectionDependencyFactory* RenderThreadImpl::GetPeerConnectionDependencyFactory() { return peer_connection_factory_.get(); } +#endif GpuChannelHost* RenderThreadImpl::GetGpuChannel() { if (!gpu_channel_.get()) @@ -1623,6 +1637,12 @@ void RenderThreadImpl::WidgetHidden() { hidden_widget_count_++; if (widget_count_ && hidden_widget_count_ == widget_count_) { + // TODO(reveman): Remove this when we have a better mechanism to prevent + // total discardable memory used by all renderers from growing too large. + base::internal::DiscardableMemoryEmulated:: + ReduceMemoryUsageUntilWithinLimit( + kEmulatedDiscardableMemoryBytesToKeepWhenWidgetsHidden); + if (GetContentClient()->renderer()->RunIdleHandlerWhenWidgetsHidden()) ScheduleIdleHandler(kInitialIdleHandlerDelayMs); } diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h index d5cdab0cb99be..25bd1844dc98c 100644 --- a/content/renderer/render_thread_impl.h +++ b/content/renderer/render_thread_impl.h @@ -272,6 +272,7 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, blink::WebMediaStreamCenter* CreateMediaStreamCenter( blink::WebMediaStreamCenterClient* client); +#if defined(ENABLE_WEBRTC) // Returns a factory used for creating RTC PeerConnection objects. PeerConnectionDependencyFactory* GetPeerConnectionDependencyFactory(); @@ -283,6 +284,7 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, P2PSocketDispatcher* p2p_socket_dispatcher() { return p2p_socket_dispatcher_.get(); } +#endif VideoCaptureImplManager* video_capture_impl_manager() const { return vc_manager_.get(); @@ -483,6 +485,7 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, #endif scoped_refptr devtools_agent_message_filter_; +#if defined(ENABLE_WEBRTC) scoped_ptr peer_connection_factory_; // This is used to communicate to the browser process the status @@ -491,6 +494,7 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, // Dispatches all P2P sockets. scoped_refptr p2p_socket_dispatcher_; +#endif // Used on the render thread. scoped_ptr vc_manager_; @@ -548,7 +552,8 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, scoped_ptr input_handler_manager_; scoped_refptr compositor_output_surface_filter_; - scoped_refptr shared_main_thread_contexts_; + scoped_refptr + shared_main_thread_contexts_; ObserverList observers_; @@ -561,7 +566,9 @@ class CONTENT_EXPORT RenderThreadImpl : public RenderThread, scoped_ptr memory_pressure_listener_; +#if defined(ENABLE_WEBRTC) scoped_ptr webrtc_identity_service_; +#endif scoped_ptr gamepad_shared_memory_reader_; diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc index f5a0d1d7f7df7..099da86004b94 100644 --- a/content/renderer/render_thread_impl_browsertest.cc +++ b/content/renderer/render_thread_impl_browsertest.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "base/command_line.h" +#include "base/memory/discardable_memory.h" +#include "base/memory/scoped_vector.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" #include "content/public/common/content_switches.h" @@ -70,5 +72,63 @@ TEST_F(RenderThreadImplBrowserTest, base::Bind(&CheckRenderThreadInputHandlerManager, thread)); } +// Checks that emulated discardable memory is discarded when the last widget +// is hidden. +// Disabled under LeakSanitizer due to memory leaks. +#if defined(LEAK_SANITIZER) +#define MAYBE_EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden \ + DISABLED_EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden +#else +#define MAYBE_EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden \ + EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden +#endif +TEST_F(RenderThreadImplBrowserTest, + MAYBE_EmulatedDiscardableMemoryDiscardedWhenWidgetsHidden) { + ContentClient content_client; + ContentBrowserClient content_browser_client; + ContentRendererClient content_renderer_client; + SetContentClient(&content_client); + SetBrowserClientForTesting(&content_browser_client); + SetRendererClientForTesting(&content_renderer_client); + base::MessageLoopForIO message_loop_; + + std::string channel_id = + IPC::Channel::GenerateVerifiedChannelID(std::string()); + DummyListener dummy_listener; + scoped_ptr channel( + IPC::Channel::CreateServer(channel_id, &dummy_listener)); + ASSERT_TRUE(channel->Connect()); + + scoped_ptr mock_process(new MockRenderProcess); + // Owned by mock_process. + RenderThreadImpl* thread = new RenderThreadImpl(channel_id); + thread->EnsureWebKitInitialized(); + thread->WidgetCreated(); + + // Allocate 128MB of discardable memory. + ScopedVector discardable_memory; + for (int i = 0; i < 32; ++i) { + discardable_memory.push_back( + base::DiscardableMemory::CreateLockedMemoryWithType( + base::DISCARDABLE_MEMORY_TYPE_EMULATED, 4 * 1024 * 1024).release()); + ASSERT_TRUE(discardable_memory.back()); + discardable_memory.back()->Unlock(); + } + + // Hide all widgets. + thread->WidgetHidden(); + + // Count how much memory is left, should be at most one block. + int blocks_left = 0; + for (auto iter = discardable_memory.begin(); iter != discardable_memory.end(); + ++iter) { + if ((*iter)->Lock() == base::DISCARDABLE_MEMORY_LOCK_STATUS_SUCCESS) + ++blocks_left; + } + EXPECT_LE(blocks_left, 1); + + thread->WidgetDestroyed(); +} + } // namespace } // namespace content diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc index 3f547c2328bfb..671ed9efb9890 100644 --- a/content/renderer/render_view_impl.cc +++ b/content/renderer/render_view_impl.cc @@ -88,7 +88,6 @@ #include "content/renderer/internal_document_state_data.h" #include "content/renderer/media/audio_device_factory.h" #include "content/renderer/media/video_capture_impl_manager.h" -#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" #include "content/renderer/memory_benchmarking_extension.h" #include "content/renderer/mhtml_generator.h" #include "content/renderer/net_info_helper.h" @@ -227,6 +226,7 @@ #if defined(ENABLE_WEBRTC) #include "content/renderer/media/rtc_peer_connection_handler.h" +#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" #endif using blink::WebAXObject; @@ -718,6 +718,7 @@ RenderViewImpl::RenderViewImpl(RenderViewImplParams* params) top_controls_constraints_(cc::BOTH), #endif has_scrolled_focused_editable_node_into_rect_(false), + has_scrolled_main_frame_(false), speech_recognition_dispatcher_(NULL), browser_plugin_manager_(NULL), devtools_agent_(NULL), @@ -992,7 +993,6 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setImagesEnabled(prefs.images_enabled); settings->setPluginsEnabled(prefs.plugins_enabled); settings->setDOMPasteAllowed(prefs.dom_paste_enabled); - settings->setNeedsSiteSpecificQuirks(prefs.site_specific_quirks_enabled); settings->setShrinksStandaloneImagesToFit( prefs.shrinks_standalone_images_to_fit); settings->setUsesEncodingDetector(prefs.uses_universal_detector); @@ -1123,6 +1123,23 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setSelectionIncludesAltImageText(true); + settings->setV8CacheOptions( + static_cast(prefs.v8_cache_options)); + + // Crosswalk shared settings: + settings->setMainFrameClipsContent(false); + +#if defined(OS_TIZEN) + // Scrollbars should not be stylable. + settings->setAllowCustomScrollbarInMainFrame(false); + + // IME support + settings->setAutoZoomFocusedNodeToLegibleScale(true); + + // Enable double tap to zoom when zoomable. + settings->setDoubleTapToZoomEnabled(true); +#endif + #if defined(OS_ANDROID) settings->setAllowCustomScrollbarInMainFrame(false); settings->setTextAutosizingEnabled(prefs.text_autosizing_enabled); @@ -1130,6 +1147,7 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, settings->setDeviceScaleAdjustment(prefs.device_scale_adjustment); settings->setDisallowFullscreenForNonMediaElements( prefs.disallow_fullscreen_for_non_media_elements); + settings->setFullscreenSupported(prefs.fullscreen_supported); web_view->setIgnoreViewportTagScaleLimits(prefs.force_enable_zoom); settings->setAutoZoomFocusedNodeToLegibleScale(true); settings->setDoubleTapToZoomEnabled(prefs.double_tap_to_zoom_enabled); @@ -1144,6 +1162,7 @@ void RenderView::ApplyWebPreferences(const WebPreferences& prefs, prefs.use_legacy_background_size_shorthand_behavior); settings->setWideViewportQuirkEnabled(prefs.wide_viewport_quirk); settings->setUseWideViewport(prefs.use_wide_viewport); + settings->setForceZeroLayoutHeight(prefs.force_zero_layout_height); settings->setViewportMetaLayoutSizeQuirk( prefs.viewport_meta_layout_size_quirk); settings->setViewportMetaMergeContentQuirk( @@ -1301,6 +1320,20 @@ void RenderViewImpl::PluginFocusChanged(bool focused, int plugin_id) { Send(new ViewHostMsg_PluginFocusChanged(routing_id(), focused, plugin_id)); } +void RenderViewImpl::OnGetRenderedText() { + if (!webview()) + return; + // Get rendered text from WebLocalFrame. + // TODO: Currently IPC truncates any data that has a + // size > kMaximumMessageSize. May be split the text into smaller chunks and + // send back using multiple IPC. See http://crbug.com/393444. + static const size_t kMaximumMessageSize = 8 * 1024 * 1024; + std::string text = webview()->mainFrame()->contentAsText( + kMaximumMessageSize).utf8(); + + Send(new ViewMsg_GetRenderedTextCompleted(routing_id(), text)); +} + void RenderViewImpl::StartPluginIme() { IPC::Message* msg = new ViewHostMsg_StartPluginIme(routing_id()); // This message can be sent during event-handling, and needs to be delivered @@ -1412,6 +1445,8 @@ bool RenderViewImpl::OnMessageReceived(const IPC::Message& message) { OnUpdateTopControlsState) IPC_MESSAGE_HANDLER(ViewMsg_ExtractSmartClipData, OnExtractSmartClipData) #elif defined(OS_MACOSX) + IPC_MESSAGE_HANDLER(ViewMsg_GetRenderedText, + OnGetRenderedText) IPC_MESSAGE_HANDLER(ViewMsg_PluginImeCompositionCompleted, OnPluginImeCompositionCompleted) IPC_MESSAGE_HANDLER(ViewMsg_SelectPopupMenuItem, OnSelectPopupMenuItem) @@ -2500,15 +2535,19 @@ BrowserPluginManager* RenderViewImpl::GetBrowserPluginManager() { return browser_plugin_manager_.get(); } -void RenderViewImpl::UpdateScrollState(WebFrame* frame) { - Send(new ViewHostMsg_DidChangeScrollOffset(routing_id_)); +void RenderViewImpl::didCommitAndDrawCompositorFrame() { + RenderWidget::didCommitAndDrawCompositorFrame(); + if (has_scrolled_main_frame_) { + has_scrolled_main_frame_ = false; + Send(new ViewHostMsg_DidChangeScrollOffset(routing_id_)); + } } void RenderViewImpl::didChangeScrollOffset(WebLocalFrame* frame) { StartNavStateSyncTimerIfNecessary(); if (webview()->mainFrame() == frame) - UpdateScrollState(frame); + has_scrolled_main_frame_ = true; FOR_EACH_OBSERVER( RenderViewObserver, observers_, DidChangeScrollOffset(frame)); @@ -3336,7 +3375,7 @@ void RenderViewImpl::OnResize(const ViewMsg_Resize_Params& params) { ShouldDisplayScrollbars(params.new_size.width(), params.new_size.height())); } - UpdateScrollState(webview()->mainFrame()); + has_scrolled_main_frame_ = true; } gfx::Size old_visible_viewport_size = visible_viewport_size_; @@ -3591,8 +3630,9 @@ void RenderViewImpl::OnWasHidden() { #endif // ENABLE_PLUGINS } -void RenderViewImpl::OnWasShown(bool needs_repainting) { - RenderWidget::OnWasShown(needs_repainting); +void RenderViewImpl::OnWasShown(bool needs_repainting, + const ui::LatencyInfo& latency_info) { + RenderWidget::OnWasShown(needs_repainting, latency_info); #if defined(OS_ANDROID) && defined(ENABLE_WEBRTC) RenderThreadImpl::current()->video_capture_impl_manager()-> diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h index 2732aa293bab1..bad09fad1cf80 100644 --- a/content/renderer/render_view_impl.h +++ b/content/renderer/render_view_impl.h @@ -354,6 +354,7 @@ class CONTENT_EXPORT RenderViewImpl // blink::WebWidgetClient implementation ------------------------------------ // Most methods are handled by RenderWidget. + virtual void didCommitAndDrawCompositorFrame(); virtual void didFocus(); virtual void didBlur(); virtual void show(blink::WebNavigationPolicy policy); @@ -517,7 +518,8 @@ class CONTENT_EXPORT RenderViewImpl virtual bool HasTouchEventHandlersAt(const gfx::Point& point) const OVERRIDE; virtual void OnSetFocus(bool enable) OVERRIDE; virtual void OnWasHidden() OVERRIDE; - virtual void OnWasShown(bool needs_repainting) OVERRIDE; + virtual void OnWasShown(bool needs_repainting, + const ui::LatencyInfo& latency_info) OVERRIDE; virtual GURL GetURLForGraphicsContext3D() OVERRIDE; virtual void OnImeSetComposition( const base::string16& text, @@ -570,6 +572,7 @@ class CONTENT_EXPORT RenderViewImpl // code away from this class. friend class RenderFrameImpl; + FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuDisplayNoneTest, SelectItem); FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuRemoveTest, RemoveOnChange); FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuTest, NormalCase); FRIEND_TEST_ALL_PREFIXES(ExternalPopupMenuTest, ShowPopupThenNavigate); @@ -649,9 +652,6 @@ class CONTENT_EXPORT RenderViewImpl // Sends a message and runs a nested message loop. bool SendAndRunNestedMessageLoop(IPC::SyncMessage* message); - // Called when the "pinned to left/right edge" state needs to be updated. - void UpdateScrollState(blink::WebFrame* frame); - // IPC message handlers ------------------------------------------------------ // // The documentation for these functions should be in @@ -746,6 +746,7 @@ class CONTENT_EXPORT RenderViewImpl bool animate); void OnExtractSmartClipData(const gfx::Rect& rect); #elif defined(OS_MACOSX) + void OnGetRenderedText(); void OnPluginImeCompositionCompleted(const base::string16& text, int plugin_id); void OnSelectPopupMenuItem(int selected_index); @@ -1015,6 +1016,8 @@ class CONTENT_EXPORT RenderViewImpl bool has_scrolled_focused_editable_node_into_rect_; gfx::Rect rect_for_scrolled_focused_editable_node_; + bool has_scrolled_main_frame_; + // Helper objects ------------------------------------------------------------ scoped_ptr main_render_frame_; diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc index 226512fe99098..07fcb27019ffd 100644 --- a/content/renderer/render_widget.cc +++ b/content/renderer/render_widget.cc @@ -779,7 +779,8 @@ void RenderWidget::OnWasHidden() { WasHidden()); } -void RenderWidget::OnWasShown(bool needs_repainting) { +void RenderWidget::OnWasShown(bool needs_repainting, + const ui::LatencyInfo& latency_info) { TRACE_EVENT0("renderer", "RenderWidget::OnWasShown"); // During shutdown we can just ignore this message. if (!webwidget_) @@ -794,8 +795,12 @@ void RenderWidget::OnWasShown(bool needs_repainting) { return; // Generate a full repaint. - if (compositor_) + if (compositor_) { + ui::LatencyInfo swap_latency_info(latency_info); + scoped_ptr latency_info_swap_promise_monitor( + compositor_->CreateLatencyInfoSwapPromiseMonitor(&swap_latency_info)); compositor_->SetNeedsForcedRedraw(); + } scheduleComposite(); } @@ -920,7 +925,7 @@ void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, base::AutoReset handling_event_type_resetter( &handling_event_type_, input_event->type); #if defined(OS_ANDROID) - // On Android, when the delete key or forward delete key is pressed using IME, + // On Android, when a key is pressed or sent from the Keyboard using IME, // |AdapterInputConnection| generates input key events to make sure all JS // listeners that monitor KeyUp and KeyDown events receive the proper key // code. Since this input key event comes from IME, we need to set the @@ -930,10 +935,9 @@ void RenderWidget::OnHandleInputEvent(const blink::WebInputEvent* input_event, if (WebInputEvent::isKeyboardEventType(input_event->type)) { const WebKeyboardEvent& key_event = *static_cast(input_event); - if (key_event.nativeKeyCode == AKEYCODE_FORWARD_DEL || - key_event.nativeKeyCode == AKEYCODE_DEL) { + // Some keys are special and it's essential that no events get blocked. + if (key_event.nativeKeyCode != AKEYCODE_TAB) ime_event_guard_maybe.reset(new ImeEventGuard(this)); - } } #endif @@ -1751,6 +1755,7 @@ void RenderWidget::UpdateTextInputState(ShowIme show_ime, ) { ViewHostMsg_TextInputState_Params p; p.type = new_type; + p.flags = new_info.flags; p.mode = new_mode; p.value = new_info.value.utf8(); p.selection_start = new_info.selectionStart; diff --git a/content/renderer/render_widget.h b/content/renderer/render_widget.h index e8b4489d5c163..bd97588d082af 100644 --- a/content/renderer/render_widget.h +++ b/content/renderer/render_widget.h @@ -369,7 +369,8 @@ class CONTENT_EXPORT RenderWidget virtual void OnResize(const ViewMsg_Resize_Params& params); void OnChangeResizeRect(const gfx::Rect& resizer_rect); virtual void OnWasHidden(); - virtual void OnWasShown(bool needs_repainting); + virtual void OnWasShown(bool needs_repainting, + const ui::LatencyInfo& latency_info); virtual void OnWasSwappedOut(); void OnCreateVideoAck(int32 video_id); void OnUpdateVideoAck(int32 video_id); diff --git a/content/renderer/renderer_font_platform_win.cc b/content/renderer/renderer_font_platform_win.cc index 2087046ce0797..4812a4e9f4b0e 100644 --- a/content/renderer/renderer_font_platform_win.cc +++ b/content/renderer/renderer_font_platform_win.cc @@ -11,12 +11,15 @@ #include #include "base/debug/alias.h" +#include "base/debug/crash_logging.h" #include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/memory_mapped_file.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" +#include "base/metrics/histogram.h" #include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" #include "base/time/time.h" #include "base/win/iat_patch_function.h" #include "base/win/registry.h" @@ -27,6 +30,8 @@ namespace { namespace mswr = Microsoft::WRL; namespace mswrw = Microsoft::WRL::Wrappers; +static const char kFontKeyName[] = "font_key_name"; + class FontCollectionLoader : public mswr::RuntimeClass, IDWriteFontCollectionLoader> { @@ -45,6 +50,7 @@ class FontCollectionLoader std::wstring GetFontNameFromKey(UINT32 idx); bool LoadFontListFromRegistry(); + bool LoadRestrictedFontList(); FontCollectionLoader() {}; virtual ~FontCollectionLoader() {}; @@ -67,7 +73,9 @@ class FontFileStream UINT64 file_offset, UINT64 fragment_size, void** context) { - if (!memory_.get() || !memory_->IsValid()) + if (!memory_.get() || !memory_->IsValid() || + file_offset >= memory_->length() || + (file_offset + fragment_size) > memory_->length()) return E_FAIL; *fragment_start = static_cast(memory_->data()) + @@ -106,7 +114,8 @@ class FontFileStream HRESULT RuntimeClassInitialize(UINT32 font_key) { base::FilePath path; PathService::Get(base::DIR_WINDOWS_FONTS, &path); - path = path.Append(g_font_loader->GetFontNameFromKey(font_key).c_str()); + std::wstring font_key_name(g_font_loader->GetFontNameFromKey(font_key)); + path = path.Append(font_key_name.c_str()); memory_.reset(new base::MemoryMappedFile()); // Put some debug information on stack. @@ -120,6 +129,9 @@ class FontFileStream } font_key_ = font_key; + + base::debug::SetCrashKeyValue(kFontKeyName, + base::WideToUTF8(font_key_name)); return S_OK; } @@ -255,6 +267,9 @@ bool FontCollectionLoader::LoadFontListFromRegistry() { return false; } + base::FilePath system_font_path; + PathService::Get(base::DIR_WINDOWS_FONTS, &system_font_path); + std::wstring name; std::wstring value; for (DWORD idx = 0; idx < regkey.GetValueCount(); idx++) { @@ -265,11 +280,57 @@ bool FontCollectionLoader::LoadFontListFromRegistry() { // we will ignore all other registry entries. std::vector components; path.GetComponents(&components); - if (components.size() == 1) { - reg_fonts_.push_back(value.c_str()); + if (components.size() == 1 || + base::FilePath::CompareEqualIgnoreCase(system_font_path.value(), + path.DirName().value())) { + reg_fonts_.push_back(path.BaseName().value()); } } } + UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Loaded", reg_fonts_.size()); + UMA_HISTOGRAM_COUNTS("DirectWrite.Fonts.Ignored", + regkey.GetValueCount() - reg_fonts_.size()); + return true; +} + +// This list is mainly based on prefs/prefs_tab_helper.cc kFontDefaults. +const wchar_t* kRestrictedFontSet[] = { + L"times.ttf", // IDS_STANDARD_FONT_FAMILY + L"timesbd.ttf", // IDS_STANDARD_FONT_FAMILY + L"timesbi.ttf", // IDS_STANDARD_FONT_FAMILY + L"timesi.ttf", // IDS_STANDARD_FONT_FAMILY + L"cour.ttf", // IDS_FIXED_FONT_FAMILY + L"courbd.ttf", // IDS_FIXED_FONT_FAMILY + L"courbi.ttf", // IDS_FIXED_FONT_FAMILY + L"couri.ttf", // IDS_FIXED_FONT_FAMILY + L"consola.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"consolab.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"consolai.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"consolaz.ttf", // IDS_FIXED_FONT_FAMILY_ALT_WIN + L"arial.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"arialbd.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"arialbi.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"ariali.ttf", // IDS_SANS_SERIF_FONT_FAMILY + L"comic.ttf", // IDS_CURSIVE_FONT_FAMILY + L"comicbd.ttf", // IDS_CURSIVE_FONT_FAMILY + L"comici.ttf", // IDS_CURSIVE_FONT_FAMILY + L"comicz.ttf", // IDS_CURSIVE_FONT_FAMILY + L"impact.ttf", // IDS_FANTASY_FONT_FAMILY + L"segoeui.ttf", // IDS_PICTOGRAPH_FONT_FAMILY + L"segoeuib.ttf", // IDS_PICTOGRAPH_FONT_FAMILY + L"segoeuii.ttf", // IDS_PICTOGRAPH_FONT_FAMILY + L"msgothic.ttc", // IDS_STANDARD_FONT_FAMILY_JAPANESE + L"msmincho.ttc", // IDS_SERIF_FONT_FAMILY_JAPANESE + L"gulim.ttc", // IDS_FIXED_FONT_FAMILY_KOREAN + L"batang.ttc", // IDS_SERIF_FONT_FAMILY_KOREAN + L"simsun.ttc", // IDS_STANDARD_FONT_FAMILY_SIMPLIFIED_HAN + L"mingliu.ttc", // IDS_SERIF_FONT_FAMILY_TRADITIONAL_HAN +}; + +bool FontCollectionLoader::LoadRestrictedFontList() { + reg_fonts_.clear(); + reg_fonts_.assign(kRestrictedFontSet, + kRestrictedFontSet + _countof(kRestrictedFontSet)); return true; } @@ -287,18 +348,37 @@ IDWriteFontCollection* GetCustomFontCollection(IDWriteFactory* factory) { FontCollectionLoader::Initialize(factory); - HRESULT hr = factory->CreateCustomFontCollection( - g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); + // We try here to put arbitrary limit on max number of fonts that could + // be loaded, otherwise we fallback to restricted set of fonts. + const UINT32 kMaxFontThreshold = 1000; + HRESULT hr = E_FAIL; + if (g_font_loader->GetFontMapSize() < kMaxFontThreshold) { + hr = factory->CreateCustomFontCollection( + g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); + } + + bool loadingRestricted = false; + if (FAILED(hr) || !g_font_collection.Get()) { + // We will try here just one more time with restricted font set. + g_font_loader->LoadRestrictedFontList(); + hr = factory->CreateCustomFontCollection( + g_font_loader.Get(), NULL, 0, g_font_collection.GetAddressOf()); + } base::TimeDelta time_delta = base::TimeTicks::Now() - start_tick; int64 delta = time_delta.ToInternalValue(); base::debug::Alias(&delta); UINT32 size = g_font_loader->GetFontMapSize(); base::debug::Alias(&size); + base::debug::Alias(&loadingRestricted); CHECK(SUCCEEDED(hr)); CHECK(g_font_collection.Get() != NULL); + UMA_HISTOGRAM_TIMES("DirectWrite.Fonts.LoadTime", time_delta); + + base::debug::ClearCrashKey(kFontKeyName); + return g_font_collection.Get(); } diff --git a/content/renderer/renderer_webkitplatformsupport_impl.cc b/content/renderer/renderer_webkitplatformsupport_impl.cc index b582f816bc515..c150e0c624ae7 100644 --- a/content/renderer/renderer_webkitplatformsupport_impl.cc +++ b/content/renderer/renderer_webkitplatformsupport_impl.cc @@ -14,8 +14,8 @@ #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "content/child/blink_glue.h" #include "content/child/database_util.h" +#include "content/child/file_info_util.h" #include "content/child/fileapi/webfilesystem_impl.h" #include "content/child/indexed_db/webidbfactory_impl.h" #include "content/child/npapi/npobject_util.h" @@ -33,6 +33,7 @@ #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h" #include "content/common/gpu/gpu_process_launch_causes.h" #include "content/common/mime_registry_messages.h" +#include "content/common/screen_orientation_messages.h" #include "content/common/view_messages.h" #include "content/public/common/content_switches.h" #include "content/public/common/webplugininfo.h" @@ -49,7 +50,6 @@ #include "content/renderer/media/renderer_webaudiodevice_impl.h" #include "content/renderer/media/renderer_webmidiaccessor_impl.h" #include "content/renderer/media/webcontentdecryptionmodule_impl.h" -#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" #include "content/renderer/render_thread_impl.h" #include "content/renderer/renderer_clipboard_client.h" #include "content/renderer/webclipboard_impl.h" @@ -117,6 +117,10 @@ #define WebScrollbarBehaviorImpl blink::WebScrollbarBehavior #endif +#if defined(ENABLE_WEBRTC) +#include "content/renderer/media/webrtc/peer_connection_dependency_factory.h" +#endif + using blink::Platform; using blink::WebAudioDevice; using blink::WebBlobRegistry; @@ -332,26 +336,11 @@ RendererWebKitPlatformSupportImpl::prescientNetworking() { return GetContentClient()->renderer()->GetPrescientNetworking(); } -bool -RendererWebKitPlatformSupportImpl::CheckPreparsedJsCachingEnabled() const { - static bool checked = false; - static bool result = false; - if (!checked) { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - result = command_line.HasSwitch(switches::kEnablePreparsedJsCaching); - checked = true; - } - return result; -} - void RendererWebKitPlatformSupportImpl::cacheMetadata( const blink::WebURL& url, double response_time, const char* data, size_t size) { - if (!CheckPreparsedJsCachingEnabled()) - return; - // Let the browser know we generated cacheable metadata for this resource. The // browser may cache it and return it on subsequent responses to speed // the processing of this resource. @@ -1074,6 +1063,9 @@ void RendererWebKitPlatformSupportImpl::startListening( SetGamepadListener( static_cast(listener)); break; + case blink::WebPlatformEventScreenOrientation: + RenderThread::Get()->Send(new ScreenOrientationHostMsg_StartListening()); + break; default: // A default statement is required to prevent compilation errors when Blink // adds a new type. @@ -1100,6 +1092,9 @@ void RendererWebKitPlatformSupportImpl::stopListening( case blink::WebPlatformEventGamepad: SetGamepadListener(0); break; + case blink::WebPlatformEventScreenOrientation: + RenderThread::Get()->Send(new ScreenOrientationHostMsg_StopListening()); + break; default: // A default statement is required to prevent compilation errors when Blink // adds a new type. diff --git a/content/renderer/service_worker/embedded_worker_context_client.cc b/content/renderer/service_worker/embedded_worker_context_client.cc index 461c1d96a9c83..dedbc8ea2a7b0 100644 --- a/content/renderer/service_worker/embedded_worker_context_client.cc +++ b/content/renderer/service_worker/embedded_worker_context_client.cc @@ -123,6 +123,11 @@ blink::WebURL EmbeddedWorkerContextClient::scope() const { return service_worker_scope_; } +blink::WebServiceWorkerCacheStorage* + EmbeddedWorkerContextClient::cacheStorage() { + return script_context_->cache_storage(); +} + void EmbeddedWorkerContextClient::didPauseAfterDownload() { Send(new EmbeddedWorkerHostMsg_DidPauseAfterDownload(embedded_worker_id_)); } diff --git a/content/renderer/service_worker/embedded_worker_context_client.h b/content/renderer/service_worker/embedded_worker_context_client.h index bb0d87d283006..bf261fc871050 100644 --- a/content/renderer/service_worker/embedded_worker_context_client.h +++ b/content/renderer/service_worker/embedded_worker_context_client.h @@ -63,6 +63,7 @@ class EmbeddedWorkerContextClient // WebServiceWorkerContextClient overrides, some of them are just dispatched // on to script_context_. virtual blink::WebURL scope() const; + virtual blink::WebServiceWorkerCacheStorage* cacheStorage(); virtual void didPauseAfterDownload(); virtual void getClients(blink::WebServiceWorkerClientsCallbacks*); virtual void workerContextFailedToStart(); diff --git a/content/renderer/service_worker/service_worker_cache_storage_dispatcher.cc b/content/renderer/service_worker/service_worker_cache_storage_dispatcher.cc new file mode 100644 index 0000000000000..750713ca032aa --- /dev/null +++ b/content/renderer/service_worker/service_worker_cache_storage_dispatcher.cc @@ -0,0 +1,207 @@ +// 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 "content/renderer/service_worker/service_worker_cache_storage_dispatcher.h" + +#include "base/logging.h" +#include "content/common/service_worker/service_worker_messages.h" +#include "content/public/renderer/render_thread.h" +#include "content/renderer/service_worker/service_worker_script_context.h" + +namespace content { + +namespace { + +template +void ClearCallbacksMapWithErrors(T* callbacks_map) { + typename T::iterator iter(callbacks_map); + while (!iter.IsAtEnd()) { + blink::WebServiceWorkerCacheError reason = + blink::WebServiceWorkerCacheErrorNotFound; + iter.GetCurrentValue()->onError(&reason); + callbacks_map->Remove(iter.GetCurrentKey()); + iter.Advance(); + } +} + +} // namespace + +ServiceWorkerCacheStorageDispatcher::ServiceWorkerCacheStorageDispatcher( + ServiceWorkerScriptContext* script_context) + : script_context_(script_context) {} + +ServiceWorkerCacheStorageDispatcher::~ServiceWorkerCacheStorageDispatcher() { + ClearCallbacksMapWithErrors(&get_callbacks_); + ClearCallbacksMapWithErrors(&has_callbacks_); + ClearCallbacksMapWithErrors(&create_callbacks_); + ClearCallbacksMapWithErrors(&delete_callbacks_); + ClearCallbacksMapWithErrors(&keys_callbacks_); +} + +bool ServiceWorkerCacheStorageDispatcher::OnMessageReceived( + const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(ServiceWorkerCacheStorageDispatcher, message) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageGetSuccess, + OnCacheStorageGetSuccess) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageHasSuccess, + OnCacheStorageHasSuccess) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageCreateSuccess, + OnCacheStorageCreateSuccess) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageDeleteSuccess, + OnCacheStorageDeleteSuccess) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageKeysSuccess, + OnCacheStorageKeysSuccess) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageGetError, + OnCacheStorageGetError) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageHasError, + OnCacheStorageHasError) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageCreateError, + OnCacheStorageCreateError) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageDeleteError, + OnCacheStorageDeleteError) + IPC_MESSAGE_HANDLER(ServiceWorkerMsg_CacheStorageKeysError, + OnCacheStorageKeysError) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + + return handled; +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageGetSuccess( + int request_id, + int cache_id) { + CacheStorageWithCacheCallbacks* callbacks = + get_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onSuccess(NULL); + get_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageHasSuccess( + int request_id) { + CacheStorageCallbacks* callbacks = has_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onSuccess(); + has_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageCreateSuccess( + int request_id, + int cache_id) { + CacheStorageWithCacheCallbacks* callbacks = + create_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onSuccess(NULL); + create_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageDeleteSuccess( + int request_id) { + CacheStorageCallbacks* callbacks = delete_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onSuccess(); + delete_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageKeysSuccess( + int request_id, + const std::vector& keys) { + CacheStorageKeysCallbacks* callbacks = keys_callbacks_.Lookup(request_id); + DCHECK(callbacks); + blink::WebVector webKeys(keys.size()); + for (size_t i = 0; i < keys.size(); ++i) + webKeys[i] = keys[i]; + + callbacks->onSuccess(&webKeys); + keys_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageGetError( + int request_id, + blink::WebServiceWorkerCacheError reason) { + CacheStorageWithCacheCallbacks* callbacks = + get_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onError(&reason); + get_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageHasError( + int request_id, + blink::WebServiceWorkerCacheError reason) { + CacheStorageCallbacks* callbacks = has_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onError(&reason); + has_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageCreateError( + int request_id, + blink::WebServiceWorkerCacheError reason) { + CacheStorageWithCacheCallbacks* callbacks = + create_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onError(&reason); + create_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageDeleteError( + int request_id, + blink::WebServiceWorkerCacheError reason) { + CacheStorageCallbacks* callbacks = delete_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onError(&reason); + delete_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::OnCacheStorageKeysError( + int request_id, + blink::WebServiceWorkerCacheError reason) { + CacheStorageKeysCallbacks* callbacks = keys_callbacks_.Lookup(request_id); + DCHECK(callbacks); + callbacks->onError(&reason); + keys_callbacks_.Remove(request_id); +} + +void ServiceWorkerCacheStorageDispatcher::dispatchGet( + CacheStorageWithCacheCallbacks* callbacks, + const blink::WebString& cacheName) { + int request_id = get_callbacks_.Add(callbacks); + script_context_->Send(new ServiceWorkerHostMsg_CacheStorageGet( + script_context_->GetRoutingID(), request_id, cacheName)); +} + +void ServiceWorkerCacheStorageDispatcher::dispatchHas( + CacheStorageCallbacks* callbacks, + const blink::WebString& cacheName) { + int request_id = delete_callbacks_.Add(callbacks); + script_context_->Send(new ServiceWorkerHostMsg_CacheStorageDelete( + script_context_->GetRoutingID(), request_id, cacheName)); +} + +void ServiceWorkerCacheStorageDispatcher::dispatchCreate( + CacheStorageWithCacheCallbacks* callbacks, + const blink::WebString& cacheName) { + int request_id = create_callbacks_.Add(callbacks); + script_context_->Send(new ServiceWorkerHostMsg_CacheStorageCreate( + script_context_->GetRoutingID(), request_id, cacheName)); +} + +void ServiceWorkerCacheStorageDispatcher::dispatchDelete( + CacheStorageCallbacks* callbacks, + const blink::WebString& cacheName) { + int request_id = delete_callbacks_.Add(callbacks); + script_context_->Send(new ServiceWorkerHostMsg_CacheStorageDelete( + script_context_->GetRoutingID(), request_id, cacheName)); +} + +void ServiceWorkerCacheStorageDispatcher::dispatchKeys( + CacheStorageKeysCallbacks* callbacks) { + int request_id = keys_callbacks_.Add(callbacks); + script_context_->Send(new ServiceWorkerHostMsg_CacheStorageKeys( + script_context_->GetRoutingID(), request_id)); +} + +} // namespace content diff --git a/content/renderer/service_worker/service_worker_cache_storage_dispatcher.h b/content/renderer/service_worker/service_worker_cache_storage_dispatcher.h new file mode 100644 index 0000000000000..ec454dca2c96a --- /dev/null +++ b/content/renderer/service_worker/service_worker_cache_storage_dispatcher.h @@ -0,0 +1,85 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_DISPATCHER_H_ +#define CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_FETCH_STORES_DISPATCHER_H_ + +#include + +#include "base/id_map.h" +#include "base/macros.h" +#include "base/strings/string16.h" +#include "content/public/renderer/render_process_observer.h" +#include "third_party/WebKit/public/platform/WebServiceWorkerCacheError.h" +#include "third_party/WebKit/public/platform/WebServiceWorkerCacheStorage.h" + +namespace content { + +class ServiceWorkerScriptContext; + +// There is one ServiceWorkerCacheStorageDispatcher per +// ServiceWorkerScriptContext. It starts CacheStorage operations with messages +// to the browser process and runs callbacks at operation completion. + +class ServiceWorkerCacheStorageDispatcher + : public blink::WebServiceWorkerCacheStorage { + public: + explicit ServiceWorkerCacheStorageDispatcher( + ServiceWorkerScriptContext* script_context); + virtual ~ServiceWorkerCacheStorageDispatcher(); + + bool OnMessageReceived(const IPC::Message& message); + + // Message handlers for messages from the browser process. + void OnCacheStorageGetSuccess(int request_id, int cache_id); + void OnCacheStorageHasSuccess(int request_id); + void OnCacheStorageCreateSuccess(int request_id, int cache_id); + void OnCacheStorageDeleteSuccess(int request_id); + void OnCacheStorageKeysSuccess(int request_id, + const std::vector& keys); + + void OnCacheStorageGetError(int request_id, + blink::WebServiceWorkerCacheError reason); + void OnCacheStorageHasError(int request_id, + blink::WebServiceWorkerCacheError reason); + void OnCacheStorageCreateError(int request_id, + blink::WebServiceWorkerCacheError reason); + void OnCacheStorageDeleteError(int request_id, + blink::WebServiceWorkerCacheError reason); + void OnCacheStorageKeysError(int request_id, + blink::WebServiceWorkerCacheError reason); + + // From WebServiceWorkerCacheStorage: + virtual void dispatchGet(CacheStorageWithCacheCallbacks* callbacks, + const blink::WebString& cacheName); + virtual void dispatchHas(CacheStorageCallbacks* callbacks, + const blink::WebString& cacheName); + virtual void dispatchCreate(CacheStorageWithCacheCallbacks* callbacks, + const blink::WebString& cacheName); + virtual void dispatchDelete(CacheStorageCallbacks* callbacks, + const blink::WebString& cacheName); + virtual void dispatchKeys(CacheStorageKeysCallbacks* callbacks); + + private: + typedef IDMap CallbacksMap; + typedef IDMap + WithCacheCallbacksMap; + typedef IDMap + KeysCallbacksMap; + + // Not owned. The script context containing this object. + ServiceWorkerScriptContext* script_context_; + + WithCacheCallbacksMap get_callbacks_; + CallbacksMap has_callbacks_; + WithCacheCallbacksMap create_callbacks_; + CallbacksMap delete_callbacks_; + KeysCallbacksMap keys_callbacks_; + + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerCacheStorageDispatcher); +}; + +} // namespace content + +#endif // CONTENT_RENDERER_SERVICE_WORKER_SERVICE_WORKER_CACHE_STORAGE_DISPATCHER_H_ diff --git a/content/renderer/service_worker/service_worker_script_context.cc b/content/renderer/service_worker/service_worker_script_context.cc index 6d246f8a22da2..117997f075636 100644 --- a/content/renderer/service_worker/service_worker_script_context.cc +++ b/content/renderer/service_worker/service_worker_script_context.cc @@ -39,7 +39,8 @@ void SendPostMessageToDocumentOnMainThread( ServiceWorkerScriptContext::ServiceWorkerScriptContext( EmbeddedWorkerContextClient* embedded_context, blink::WebServiceWorkerContextProxy* proxy) - : embedded_context_(embedded_context), + : cache_storage_dispatcher_(new ServiceWorkerCacheStorageDispatcher(this)), + embedded_context_(embedded_context), proxy_(proxy) { } @@ -59,6 +60,12 @@ void ServiceWorkerScriptContext::OnMessageReceived( OnDidGetClientDocuments) IPC_MESSAGE_UNHANDLED(handled = false) IPC_END_MESSAGE_MAP() + + // TODO(gavinp): Would it be preferable to put an AddListener() method to + // EmbeddedWorkerContextClient? + if (!handled) + handled = cache_storage_dispatcher_->OnMessageReceived(message); + DCHECK(handled); } @@ -116,6 +123,10 @@ void ServiceWorkerScriptContext::Send(IPC::Message* message) { embedded_context_->Send(message); } +int ServiceWorkerScriptContext::GetRoutingID() const { + return embedded_context_->embedded_worker_id(); +} + void ServiceWorkerScriptContext::OnActivateEvent(int request_id) { proxy_->dispatchActivateEvent(request_id); } @@ -187,8 +198,4 @@ void ServiceWorkerScriptContext::OnDidGetClientDocuments( pending_clients_callbacks_.Remove(request_id); } -int ServiceWorkerScriptContext::GetRoutingID() const { - return embedded_context_->embedded_worker_id(); -} - } // namespace content diff --git a/content/renderer/service_worker/service_worker_script_context.h b/content/renderer/service_worker/service_worker_script_context.h index d7f59b30ea1ad..078228ab16bce 100644 --- a/content/renderer/service_worker/service_worker_script_context.h +++ b/content/renderer/service_worker/service_worker_script_context.h @@ -10,9 +10,11 @@ #include "base/basictypes.h" #include "base/id_map.h" +#include "base/memory/scoped_ptr.h" #include "base/strings/string16.h" #include "content/child/webmessageportchannel_impl.h" #include "content/common/service_worker/service_worker_types.h" +#include "content/renderer/service_worker/service_worker_cache_storage_dispatcher.h" #include "third_party/WebKit/public/platform/WebMessagePortChannel.h" #include "third_party/WebKit/public/platform/WebServiceWorkerClientsInfo.h" #include "third_party/WebKit/public/platform/WebServiceWorkerEventResult.h" @@ -57,12 +59,21 @@ class ServiceWorkerScriptContext { const base::string16& message, scoped_ptr channels); + // Send a message to the browser. Takes ownership of |message|. + void Send(IPC::Message* message); + + // Get routing_id for sending message to the ServiceWorkerVersion + // in the browser process. + int GetRoutingID() const; + + blink::WebServiceWorkerCacheStorage* cache_storage() { + return cache_storage_dispatcher_.get(); + } + private: typedef IDMap ClientsCallbacksMap; - // Send a message to the browser. - void Send(IPC::Message* message); void OnActivateEvent(int request_id); void OnInstallEvent(int request_id, int active_version_id); @@ -75,9 +86,7 @@ class ServiceWorkerScriptContext { void OnDidGetClientDocuments( int request_id, const std::vector& client_ids); - // Get routing_id for sending message to the ServiceWorkerVersion - // in the browser process. - int GetRoutingID() const; + scoped_ptr cache_storage_dispatcher_; // Not owned; embedded_context_ owns this. EmbeddedWorkerContextClient* embedded_context_; diff --git a/content/shell/BUILD.gn b/content/shell/BUILD.gn index 591e3956bdb51..1ae453cbe5665 100644 --- a/content/shell/BUILD.gn +++ b/content/shell/BUILD.gn @@ -125,8 +125,6 @@ static_library("content_shell_lib") { "renderer/shell_render_process_observer.h", "renderer/shell_render_view_observer.cc", "renderer/shell_render_view_observer.h", - "renderer/test_runner/TestInterfaces.cpp", - "renderer/test_runner/TestInterfaces.h", "renderer/test_runner/TestPlugin.cpp", "renderer/test_runner/TestPlugin.h", "renderer/test_runner/WebTask.cpp", @@ -178,6 +176,8 @@ static_library("content_shell_lib") { "renderer/test_runner/spell_check_client.h", "renderer/test_runner/test_common.cc", "renderer/test_runner/test_common.h", + "renderer/test_runner/test_interfaces.cc", + "renderer/test_runner/test_interfaces.h", "renderer/test_runner/test_runner.cc", "renderer/test_runner/test_runner.h", "renderer/test_runner/text_input_controller.cc", @@ -206,6 +206,7 @@ static_library("content_shell_lib") { "//components/breakpad/app", "//content:resources", "//content/app:both", + "//content/app/strings", "//content/gpu", "//content/ppapi_plugin", "//content/public/browser", @@ -221,9 +222,10 @@ static_library("content_shell_lib") { "//net", "//net:net_resources", "//skia", - "//third_party/icu", "//third_party/WebKit/public:blink", + "//third_party/WebKit/public:resources", "//third_party/WebKit/public:test_support", + "//third_party/icu", "//ui/base", "//ui/events:events_base", "//ui/gfx", @@ -232,8 +234,8 @@ static_library("content_shell_lib") { "//ui/gl", "//url", "//v8", - "//webkit:resources", "//webkit/browser:storage", + "//webkit/glue/resources", #'copy_test_netscape_plugin', TODO(GYP) ] @@ -387,28 +389,29 @@ group("resources") { repack("pak") { sources = [ - "$root_gen_dir/content/content_resources.pak", + "$root_gen_dir/blink/public/resources/blink_resources.pak", + "$root_gen_dir/content/app/strings/content_strings_en-US.pak", "$root_gen_dir/content/browser/tracing/tracing_resources.pak", + "$root_gen_dir/content/content_resources.pak", "$root_gen_dir/content/shell/shell_resources.pak", "$root_gen_dir/net/net_resources.pak", "$root_gen_dir/ui/resources/ui_resources_100_percent.pak", "$root_gen_dir/ui/resources/webui_resources.pak", "$root_gen_dir/ui/strings/app_locale_settings_en-US.pak", "$root_gen_dir/ui/strings/ui_strings_en-US.pak", - "$root_gen_dir/webkit/blink_resources.pak", "$root_gen_dir/webkit/webkit_resources_100_percent.pak", - "$root_gen_dir/webkit/webkit_strings_en-US.pak", ] deps = [ ":resources", "//content:resources", + "//content/app/strings", "//content/browser/tracing:resources", "//net:net_resources", + "//third_party/WebKit/public:resources", "//ui/resources", "//ui/strings", - "//webkit:resources", - "//webkit:strings", + "//webkit/glue/resources", ] if (is_android) { diff --git a/content/shell/DEPS b/content/shell/DEPS index 386f7d095163e..a75220ec93581 100644 --- a/content/shell/DEPS +++ b/content/shell/DEPS @@ -6,6 +6,7 @@ include_rules = [ # The content_shell is the canonical sample embedder, so it only uses # content's public API. + "+content/app/strings/grit", # For generated headers "+content/public", # The content_shell is an embedder so it must work with resource bundles. diff --git a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java index 0fa41b6fea2ed..b1e0e654766d2 100644 --- a/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java +++ b/content/shell/android/javatests/src/org/chromium/content_shell_apk/ContentShellPreconditionsTest.java @@ -4,7 +4,9 @@ package org.chromium.content_shell_apk; +import android.annotation.TargetApi; import android.content.Context; +import android.os.Build; import android.os.PowerManager; import android.test.suitebuilder.annotation.Smoke; @@ -14,12 +16,18 @@ * Test that verifies preconditions for tests to run. */ public class ContentShellPreconditionsTest extends ContentShellTestBase { + @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) + @SuppressWarnings("deprecation") @Smoke @Feature({"TestInfrastructure"}) public void testScreenIsOn() throws Exception { PowerManager pm = (PowerManager) getInstrumentation().getContext().getSystemService( Context.POWER_SERVICE); - assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn()); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { + assertTrue("Many tests will fail if the screen is not on.", pm.isInteractive()); + } else { + assertTrue("Many tests will fail if the screen is not on.", pm.isScreenOn()); + } } } diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc index 1af3303c832c5..05c8c0419424f 100644 --- a/content/shell/app/shell_main_delegate.cc +++ b/content/shell/app/shell_main_delegate.cc @@ -268,7 +268,7 @@ void ShellMainDelegate::InitializeResourceBundle() { if (pak_fd >= 0) { // This is clearly wrong. See crbug.com/330930 ui::ResourceBundle::InitSharedInstanceWithPakFileRegion( - base::File(pak_fd), base::MemoryMappedFile::Region::kWholeFile, false); + base::File(pak_fd), base::MemoryMappedFile::Region::kWholeFile); ResourceBundle::GetSharedInstance().AddDataPackFromFile( base::File(pak_fd), ui::SCALE_FACTOR_100P); return; diff --git a/content/shell/browser/shell_devtools_delegate.cc b/content/shell/browser/shell_devtools_delegate.cc index 2768f8b7e6018..0f4214f1d4236 100644 --- a/content/shell/browser/shell_devtools_delegate.cc +++ b/content/shell/browser/shell_devtools_delegate.cc @@ -107,8 +107,7 @@ class Target : public content::DevToolsTarget { }; Target::Target(WebContents* web_contents) { - agent_host_ = - DevToolsAgentHost::GetOrCreateFor(web_contents->GetRenderViewHost()); + agent_host_ = DevToolsAgentHost::GetOrCreateFor(web_contents); id_ = agent_host_->GetId(); title_ = base::UTF16ToUTF8(web_contents->GetTitle()); url_ = web_contents->GetURL(); @@ -120,10 +119,7 @@ Target::Target(WebContents* web_contents) { } bool Target::Activate() const { - RenderViewHost* rvh = agent_host_->GetRenderViewHost(); - if (!rvh) - return false; - WebContents* web_contents = WebContents::FromRenderViewHost(rvh); + WebContents* web_contents = agent_host_->GetWebContents(); if (!web_contents) return false; web_contents->GetDelegate()->ActivateContents(web_contents); @@ -131,10 +127,10 @@ bool Target::Activate() const { } bool Target::Close() const { - RenderViewHost* rvh = agent_host_->GetRenderViewHost(); - if (!rvh) + WebContents* web_contents = agent_host_->GetWebContents(); + if (!web_contents) return false; - rvh->ClosePage(); + web_contents->GetRenderViewHost()->ClosePage(); return true; } @@ -198,13 +194,12 @@ ShellDevToolsDelegate::CreateNewTarget(const GURL& url) { void ShellDevToolsDelegate::EnumerateTargets(TargetCallback callback) { TargetList targets; - std::vector rvh_list = - content::DevToolsAgentHost::GetValidRenderViewHosts(); - for (std::vector::iterator it = rvh_list.begin(); - it != rvh_list.end(); ++it) { - WebContents* web_contents = WebContents::FromRenderViewHost(*it); - if (web_contents) - targets.push_back(new Target(web_contents)); + std::vector wc_list = + content::DevToolsAgentHost::GetInspectableWebContents(); + for (std::vector::iterator it = wc_list.begin(); + it != wc_list.end(); + ++it) { + targets.push_back(new Target(*it)); } callback.Run(targets); } diff --git a/content/shell/browser/shell_devtools_frontend.cc b/content/shell/browser/shell_devtools_frontend.cc index 2b11e8eaa2481..a426020d5678e 100644 --- a/content/shell/browser/shell_devtools_frontend.cc +++ b/content/shell/browser/shell_devtools_frontend.cc @@ -66,8 +66,7 @@ ShellDevToolsFrontend* ShellDevToolsFrontend::Show( const std::string& settings, const std::string& frontend_url) { scoped_refptr agent( - DevToolsAgentHost::GetOrCreateFor( - inspected_contents->GetRenderViewHost())); + DevToolsAgentHost::GetOrCreateFor(inspected_contents)); Shell* shell = Shell::CreateNewWindow(inspected_contents->GetBrowserContext(), GURL(), NULL, diff --git a/content/shell/common/shell_content_client.cc b/content/shell/common/shell_content_client.cc index 3d1891df9e1fe..6afa6b974c37c 100644 --- a/content/shell/common/shell_content_client.cc +++ b/content/shell/common/shell_content_client.cc @@ -7,12 +7,12 @@ #include "base/command_line.h" #include "base/strings/string_piece.h" #include "base/strings/utf_string_conversions.h" +#include "content/app/strings/grit/content_strings.h" #include "content/public/common/content_switches.h" #include "content/public/common/user_agent.h" #include "content/shell/common/shell_switches.h" #include "grit/shell_resources.h" #include "grit/webkit_resources.h" -#include "grit/webkit_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" diff --git a/content/shell/renderer/leak_detector.cc b/content/shell/renderer/leak_detector.cc index 01cbb08003fd1..86ca169b2aa4d 100644 --- a/content/shell/renderer/leak_detector.cc +++ b/content/shell/renderer/leak_detector.cc @@ -21,14 +21,21 @@ namespace content { // RefCoutned objects whose initial state is diffcult to estimate, we stop using // hard-coded values. Instead, we need to load about:blank ahead of the layout // tests actually and initialize LeakDetector by the got values. +const int kInitialNumberOfLiveAudioNodes = 0; const int kInitialNumberOfLiveDocuments = 1; const int kInitialNumberOfLiveNodes = 4; +const int kInitialNumberOfLiveRenderObjects = 3; +const int kInitialNumberOfLiveResources = 0; LeakDetector::LeakDetector(WebKitTestRunner* test_runner) : test_runner_(test_runner), web_leak_detector_(blink::WebLeakDetector::create(this)) { + previous_result_.numberOfLiveAudioNodes = kInitialNumberOfLiveAudioNodes; previous_result_.numberOfLiveDocuments = kInitialNumberOfLiveDocuments; previous_result_.numberOfLiveNodes = kInitialNumberOfLiveNodes; + previous_result_.numberOfLiveRenderObjects = + kInitialNumberOfLiveRenderObjects; + previous_result_.numberOfLiveResources = kInitialNumberOfLiveResources; } LeakDetector::~LeakDetector() { @@ -41,25 +48,46 @@ void LeakDetector::TryLeakDetection(blink::WebLocalFrame* frame) { void LeakDetector::onLeakDetectionComplete( const WebLeakDetectorClient::Result& result) { LeakDetectionResult report; - report.leaked = - (previous_result_.numberOfLiveDocuments < result.numberOfLiveDocuments || - previous_result_.numberOfLiveNodes < result.numberOfLiveNodes); + report.leaked = false; + base::DictionaryValue detail; - if (report.leaked) { - base::DictionaryValue detail; + if (previous_result_.numberOfLiveAudioNodes < result.numberOfLiveAudioNodes) { + base::ListValue* list = new base::ListValue(); + list->AppendInteger(previous_result_.numberOfLiveAudioNodes); + list->AppendInteger(result.numberOfLiveAudioNodes); + detail.Set("numberOfLiveAudioNodes", list); + } + if (previous_result_.numberOfLiveDocuments < result.numberOfLiveDocuments) { base::ListValue* list = new base::ListValue(); list->AppendInteger(previous_result_.numberOfLiveDocuments); list->AppendInteger(result.numberOfLiveDocuments); detail.Set("numberOfLiveDocuments", list); - - list = new base::ListValue(); + } + if (previous_result_.numberOfLiveNodes < result.numberOfLiveNodes) { + base::ListValue* list = new base::ListValue(); list->AppendInteger(previous_result_.numberOfLiveNodes); list->AppendInteger(result.numberOfLiveNodes); detail.Set("numberOfLiveNodes", list); + } + if (previous_result_.numberOfLiveRenderObjects < + result.numberOfLiveRenderObjects) { + base::ListValue* list = new base::ListValue(); + list->AppendInteger(previous_result_.numberOfLiveRenderObjects); + list->AppendInteger(result.numberOfLiveRenderObjects); + detail.Set("numberOfLiveRenderObjects", list); + } + if (previous_result_.numberOfLiveResources < result.numberOfLiveResources) { + base::ListValue* list = new base::ListValue(); + list->AppendInteger(previous_result_.numberOfLiveResources); + list->AppendInteger(result.numberOfLiveResources); + detail.Set("numberOfLiveResources", list); + } + if (!detail.empty()) { std::string detail_str; base::JSONWriter::Write(&detail, &detail_str); report.detail = detail_str; + report.leaked = true; } previous_result_ = result; diff --git a/content/shell/renderer/test_runner/TestInterfaces.cpp b/content/shell/renderer/test_runner/TestInterfaces.cpp deleted file mode 100644 index 9f086d79955d2..0000000000000 --- a/content/shell/renderer/test_runner/TestInterfaces.cpp +++ /dev/null @@ -1,203 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/shell/renderer/test_runner/TestInterfaces.h" - -#include - -#include "base/logging.h" -#include "base/command_line.h" -#include "base/strings/stringprintf.h" -#include "content/shell/common/shell_switches.h" -#include "content/shell/renderer/test_runner/accessibility_controller.h" -#include "content/shell/renderer/test_runner/event_sender.h" -#include "content/shell/renderer/test_runner/gamepad_controller.h" -#include "content/shell/renderer/test_runner/text_input_controller.h" -#include "content/shell/renderer/test_runner/test_runner.h" -#include "content/shell/renderer/test_runner/web_test_proxy.h" -#include "third_party/WebKit/public/platform/WebString.h" -#include "third_party/WebKit/public/platform/WebURL.h" -#include "third_party/WebKit/public/web/WebCache.h" -#include "third_party/WebKit/public/web/WebKit.h" -#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" -#include "third_party/WebKit/public/web/WebView.h" - -using namespace blink; - -namespace content { - -TestInterfaces::TestInterfaces() - : m_accessibilityController(new AccessibilityController()) - , m_eventSender(new EventSender(this)) - , m_gamepadController(new GamepadController()) - , m_textInputController(new TextInputController()) - , m_testRunner(new TestRunner(this)) - , m_delegate(0) -{ - blink::setLayoutTestMode(true); - if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableFontAntialiasing)) - blink::setFontAntialiasingEnabledForTest(true); - - // NOTE: please don't put feature specific enable flags here, - // instead add them to RuntimeEnabledFeatures.in - - resetAll(); -} - -TestInterfaces::~TestInterfaces() -{ - m_accessibilityController->SetWebView(0); - m_eventSender->SetWebView(0); - // m_gamepadController doesn't depend on WebView. - m_textInputController->SetWebView(NULL); - m_testRunner->SetWebView(0, 0); - - m_accessibilityController->SetDelegate(0); - m_eventSender->SetDelegate(0); - m_gamepadController->SetDelegate(0); - // m_textInputController doesn't depend on WebTestDelegate. - m_testRunner->SetDelegate(0); -} - -void TestInterfaces::setWebView(WebView* webView, WebTestProxyBase* proxy) -{ - m_proxy = proxy; - m_accessibilityController->SetWebView(webView); - m_eventSender->SetWebView(webView); - // m_gamepadController doesn't depend on WebView. - m_textInputController->SetWebView(webView); - m_testRunner->SetWebView(webView, proxy); -} - -void TestInterfaces::setDelegate(WebTestDelegate* delegate) -{ - m_accessibilityController->SetDelegate(delegate); - m_eventSender->SetDelegate(delegate); - m_gamepadController->SetDelegate(delegate); - // m_textInputController doesn't depend on WebTestDelegate. - m_testRunner->SetDelegate(delegate); - m_delegate = delegate; -} - -void TestInterfaces::bindTo(WebFrame* frame) -{ - m_accessibilityController->Install(frame); - m_eventSender->Install(frame); - m_gamepadController->Install(frame); - m_textInputController->Install(frame); - m_testRunner->Install(frame); -} - -void TestInterfaces::resetTestHelperControllers() -{ - m_accessibilityController->Reset(); - m_eventSender->Reset(); - m_gamepadController->Reset(); - // m_textInputController doesn't have any state to reset. - WebCache::clear(); -} - -void TestInterfaces::resetAll() -{ - resetTestHelperControllers(); - m_testRunner->Reset(); -} - -void TestInterfaces::setTestIsRunning(bool running) -{ - m_testRunner->SetTestIsRunning(running); -} - -void TestInterfaces::configureForTestWithURL(const WebURL& testURL, bool generatePixels) -{ - std::string spec = GURL(testURL).spec(); - m_testRunner->setShouldGeneratePixelResults(generatePixels); - if (spec.find("loading/") != std::string::npos) - m_testRunner->setShouldDumpFrameLoadCallbacks(true); - if (spec.find("/dumpAsText/") != std::string::npos) { - m_testRunner->setShouldDumpAsText(true); - m_testRunner->setShouldGeneratePixelResults(false); - } - if (spec.find("/inspector/") != std::string::npos - || spec.find("/inspector-enabled/") != std::string::npos) - m_testRunner->clearDevToolsLocalStorage(); - if (spec.find("/inspector/") != std::string::npos) { - // Subfolder name determines default panel to open. - std::string settings = ""; - std::string test_path = spec.substr(spec.find("/inspector/") + 11); - size_t slash_index = test_path.find("/"); - if (slash_index != std::string::npos) { - settings = base::StringPrintf( - "{\"lastActivePanel\":\"\\\"%s\\\"\"}", - test_path.substr(0, slash_index).c_str()); - } - m_testRunner->showDevTools(settings, std::string()); - } - if (spec.find("/viewsource/") != std::string::npos) { - m_testRunner->setShouldEnableViewSource(true); - m_testRunner->setShouldGeneratePixelResults(false); - m_testRunner->setShouldDumpAsMarkup(true); - } -} - -void TestInterfaces::windowOpened(WebTestProxyBase* proxy) -{ - m_windowList.push_back(proxy); -} - -void TestInterfaces::windowClosed(WebTestProxyBase* proxy) -{ - std::vector::iterator pos = std::find(m_windowList.begin(), m_windowList.end(), proxy); - if (pos == m_windowList.end()) { - NOTREACHED(); - return; - } - m_windowList.erase(pos); -} - -AccessibilityController* TestInterfaces::accessibilityController() -{ - return m_accessibilityController.get(); -} - -EventSender* TestInterfaces::eventSender() -{ - return m_eventSender.get(); -} - -TestRunner* TestInterfaces::testRunner() -{ - return m_testRunner.get(); -} - -WebTestDelegate* TestInterfaces::delegate() -{ - return m_delegate; -} - -WebTestProxyBase* TestInterfaces::proxy() -{ - return m_proxy; -} - -const std::vector& TestInterfaces::windowList() -{ - return m_windowList; -} - -WebThemeEngine* TestInterfaces::themeEngine() -{ - if (!m_testRunner->UseMockTheme()) - return 0; -#if defined(__APPLE__) - if (!m_themeEngine.get()) - m_themeEngine.reset(new MockWebThemeEngineMac()); -#else - if (!m_themeEngine.get()) - m_themeEngine.reset(new MockWebThemeEngine()); -#endif - return m_themeEngine.get(); -} - -} // namespace content diff --git a/content/shell/renderer/test_runner/TestInterfaces.h b/content/shell/renderer/test_runner/TestInterfaces.h deleted file mode 100644 index b2034760566a1..0000000000000 --- a/content/shell/renderer/test_runner/TestInterfaces.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_SHELL_RENDERER_TEST_RUNNER_TESTINTERFACES_H_ -#define CONTENT_SHELL_RENDERER_TEST_RUNNER_TESTINTERFACES_H_ - -#include - -#include "base/basictypes.h" -#include "base/memory/scoped_ptr.h" -#include "third_party/WebKit/public/platform/WebNonCopyable.h" - -#if defined(__APPLE__) -#include "content/shell/renderer/test_runner/mock_web_theme_engine_mac.h" -#else -#include "content/shell/renderer/test_runner/mock_web_theme_engine.h" -#endif - -namespace blink { -class WebFrame; -class WebThemeEngine; -class WebURL; -class WebView; -} - -namespace content { - -class AccessibilityController; -class EventSender; -class GamepadController; -class TestRunner; -class TextInputController; -class WebTestDelegate; -class WebTestProxyBase; - -class TestInterfaces { -public: - TestInterfaces(); - ~TestInterfaces(); - - void setWebView(blink::WebView*, WebTestProxyBase*); - void setDelegate(WebTestDelegate*); - void bindTo(blink::WebFrame*); - void resetTestHelperControllers(); - void resetAll(); - void setTestIsRunning(bool); - void configureForTestWithURL(const blink::WebURL&, bool generatePixels); - - void windowOpened(WebTestProxyBase*); - void windowClosed(WebTestProxyBase*); - - AccessibilityController* accessibilityController(); - EventSender* eventSender(); - TestRunner* testRunner(); - WebTestDelegate* delegate(); - WebTestProxyBase* proxy(); - const std::vector& windowList(); - blink::WebThemeEngine* themeEngine(); - -private: - scoped_ptr m_accessibilityController; - scoped_ptr m_eventSender; - scoped_ptr m_gamepadController; - scoped_ptr m_textInputController; - scoped_ptr m_testRunner; - WebTestDelegate* m_delegate; - WebTestProxyBase* m_proxy; - - std::vector m_windowList; -#if defined(__APPLE__) - scoped_ptr m_themeEngine; -#else - scoped_ptr m_themeEngine; -#endif - - DISALLOW_COPY_AND_ASSIGN(TestInterfaces); -}; - -} // namespace content - -#endif // CONTENT_SHELL_RENDERER_TEST_RUNNER_TESTINTERFACES_H_ diff --git a/content/shell/renderer/test_runner/WebTestInterfaces.cpp b/content/shell/renderer/test_runner/WebTestInterfaces.cpp index 01475d823a9fc..513cc591bda6c 100644 --- a/content/shell/renderer/test_runner/WebTestInterfaces.cpp +++ b/content/shell/renderer/test_runner/WebTestInterfaces.cpp @@ -4,11 +4,11 @@ #include "content/shell/renderer/test_runner/WebTestInterfaces.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/mock_web_audio_device.h" #include "content/shell/renderer/test_runner/mock_web_media_stream_center.h" #include "content/shell/renderer/test_runner/mock_web_midi_accessor.h" #include "content/shell/renderer/test_runner/mock_webrtc_peer_connection_handler.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "content/shell/renderer/test_runner/test_runner.h" using namespace blink; @@ -26,42 +26,42 @@ WebTestInterfaces::~WebTestInterfaces() void WebTestInterfaces::setWebView(WebView* webView, WebTestProxyBase* proxy) { - m_interfaces->setWebView(webView, proxy); + m_interfaces->SetWebView(webView, proxy); } void WebTestInterfaces::setDelegate(WebTestDelegate* delegate) { - m_interfaces->setDelegate(delegate); + m_interfaces->SetDelegate(delegate); } void WebTestInterfaces::bindTo(WebFrame* frame) { - m_interfaces->bindTo(frame); + m_interfaces->BindTo(frame); } void WebTestInterfaces::resetAll() { - m_interfaces->resetAll(); + m_interfaces->ResetAll(); } void WebTestInterfaces::setTestIsRunning(bool running) { - m_interfaces->setTestIsRunning(running); + m_interfaces->SetTestIsRunning(running); } void WebTestInterfaces::configureForTestWithURL(const WebURL& testURL, bool generatePixels) { - m_interfaces->configureForTestWithURL(testURL, generatePixels); + m_interfaces->ConfigureForTestWithURL(testURL, generatePixels); } WebTestRunner* WebTestInterfaces::testRunner() { - return m_interfaces->testRunner(); + return m_interfaces->GetTestRunner(); } WebThemeEngine* WebTestInterfaces::themeEngine() { - return m_interfaces->themeEngine(); + return m_interfaces->GetThemeEngine(); } TestInterfaces* WebTestInterfaces::testInterfaces() diff --git a/content/shell/renderer/test_runner/event_sender.cc b/content/shell/renderer/test_runner/event_sender.cc index ccdf6e91dce02..f4cf3106f4c32 100644 --- a/content/shell/renderer/test_runner/event_sender.cc +++ b/content/shell/renderer/test_runner/event_sender.cc @@ -8,9 +8,9 @@ #include "base/logging.h" #include "base/strings/stringprintf.h" #include "content/public/common/page_zoom.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "content/shell/renderer/test_runner/mock_spell_check.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "content/shell/renderer/test_runner/web_test_proxy.h" #include "gin/handle.h" #include "gin/object_template_builder.h" @@ -1426,7 +1426,8 @@ void EventSender::TextZoomOut() { } void EventSender::ZoomPageIn() { - const std::vector& window_list = interfaces_->windowList(); + const std::vector& window_list = + interfaces_->GetWindowList(); for (size_t i = 0; i < window_list.size(); ++i) { window_list.at(i)->GetWebView()->setZoomLevel( @@ -1435,7 +1436,8 @@ void EventSender::ZoomPageIn() { } void EventSender::ZoomPageOut() { - const std::vector& window_list = interfaces_->windowList(); + const std::vector& window_list = + interfaces_->GetWindowList(); for (size_t i = 0; i < window_list.size(); ++i) { window_list.at(i)->GetWebView()->setZoomLevel( @@ -1444,7 +1446,8 @@ void EventSender::ZoomPageOut() { } void EventSender::SetPageZoomFactor(double zoom_factor) { - const std::vector& window_list = interfaces_->windowList(); + const std::vector& window_list = + interfaces_->GetWindowList(); for (size_t i = 0; i < window_list.size(); ++i) { window_list.at(i)->GetWebView()->setZoomLevel( diff --git a/content/shell/renderer/test_runner/gamepad_controller.cc b/content/shell/renderer/test_runner/gamepad_controller.cc index 8c4eedf47bce4..4ec597e3dd70b 100644 --- a/content/shell/renderer/test_runner/gamepad_controller.cc +++ b/content/shell/renderer/test_runner/gamepad_controller.cc @@ -4,7 +4,6 @@ #include "content/shell/renderer/test_runner/gamepad_controller.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "gin/arguments.h" #include "gin/handle.h" diff --git a/content/shell/renderer/test_runner/mock_web_media_stream_center.cc b/content/shell/renderer/test_runner/mock_web_media_stream_center.cc index 7b3432c01d08d..766648278e251 100644 --- a/content/shell/renderer/test_runner/mock_web_media_stream_center.cc +++ b/content/shell/renderer/test_runner/mock_web_media_stream_center.cc @@ -5,8 +5,8 @@ #include "content/shell/renderer/test_runner/mock_web_media_stream_center.h" #include "base/logging.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "third_party/WebKit/public/platform/WebAudioDestinationConsumer.h" #include "third_party/WebKit/public/platform/WebAudioSourceProvider.h" #include "third_party/WebKit/public/platform/WebMediaStream.h" @@ -140,7 +140,7 @@ void MockWebMediaStreamCenter::didCreateMediaStream( delete consumer; } } - interfaces_->delegate()->postTask(new NewTrackTask(this, stream)); + interfaces_->GetDelegate()->postTask(new NewTrackTask(this, stream)); } blink::WebAudioSourceProvider* diff --git a/content/shell/renderer/test_runner/mock_web_midi_accessor.cc b/content/shell/renderer/test_runner/mock_web_midi_accessor.cc index 90f14c34de024..362c287e6ce40 100644 --- a/content/shell/renderer/test_runner/mock_web_midi_accessor.cc +++ b/content/shell/renderer/test_runner/mock_web_midi_accessor.cc @@ -4,8 +4,8 @@ #include "content/shell/renderer/test_runner/mock_web_midi_accessor.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "content/shell/renderer/test_runner/test_runner.h" #include "content/shell/renderer/test_runner/web_test_runner.h" #include "third_party/WebKit/public/platform/WebMIDIAccessorClient.h" @@ -54,8 +54,8 @@ void MockWebMIDIAccessor::startSession() { "MockOutputManufacturer", "MockOutputName", "MockOutputVersion"); - interfaces_->delegate()->postTask(new DidStartSessionTask( - this, client_, interfaces_->testRunner()->midiAccessorResult())); + interfaces_->GetDelegate()->postTask(new DidStartSessionTask( + this, client_, interfaces_->GetTestRunner()->midiAccessorResult())); } } // namespace content diff --git a/content/shell/renderer/test_runner/mock_webrtc_peer_connection_handler.cc b/content/shell/renderer/test_runner/mock_webrtc_peer_connection_handler.cc index 1a610dc9731c0..52236d9a95222 100644 --- a/content/shell/renderer/test_runner/mock_webrtc_peer_connection_handler.cc +++ b/content/shell/renderer/test_runner/mock_webrtc_peer_connection_handler.cc @@ -4,11 +4,11 @@ #include "content/shell/renderer/test_runner/mock_webrtc_peer_connection_handler.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "content/shell/renderer/test_runner/mock_constraints.h" #include "content/shell/renderer/test_runner/mock_webrtc_data_channel_handler.h" #include "content/shell/renderer/test_runner/mock_webrtc_dtmf_sender_handler.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "third_party/WebKit/public/platform/WebMediaConstraints.h" #include "third_party/WebKit/public/platform/WebMediaStream.h" #include "third_party/WebKit/public/platform/WebMediaStreamTrack.h" @@ -160,7 +160,7 @@ bool MockWebRTCPeerConnectionHandler::initialize( const WebRTCConfiguration& configuration, const WebMediaConstraints& constraints) { if (MockConstraints::VerifyConstraints(constraints)) { - interfaces_->delegate()->postTask(new RTCPeerConnectionStateTask( + interfaces_->GetDelegate()->postTask(new RTCPeerConnectionStateTask( this, client_, WebRTCPeerConnectionHandlerClient::ICEConnectionStateCompleted, @@ -179,18 +179,18 @@ void MockWebRTCPeerConnectionHandler::createOffer( should_succeed == "true") { WebRTCSessionDescription session_description; session_description.initialize("offer", "local"); - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCSessionDescriptionRequestSuccededTask( this, request, session_description)); } else - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCSessionDescriptionRequestFailedTask(this, request)); } void MockWebRTCPeerConnectionHandler::createOffer( const WebRTCSessionDescriptionRequest& request, const blink::WebRTCOfferOptions& options) { - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCSessionDescriptionRequestFailedTask(this, request)); } @@ -200,11 +200,11 @@ void MockWebRTCPeerConnectionHandler::createAnswer( if (!remote_description_.isNull()) { WebRTCSessionDescription session_description; session_description.initialize("answer", "local"); - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCSessionDescriptionRequestSuccededTask( this, request, session_description)); } else - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCSessionDescriptionRequestFailedTask(this, request)); } @@ -213,10 +213,10 @@ void MockWebRTCPeerConnectionHandler::setLocalDescription( const WebRTCSessionDescription& local_description) { if (!local_description.isNull() && local_description.sdp() == "local") { local_description_ = local_description; - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCVoidRequestTask(this, request, true)); } else - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCVoidRequestTask(this, request, false)); } @@ -225,10 +225,10 @@ void MockWebRTCPeerConnectionHandler::setRemoteDescription( const WebRTCSessionDescription& remote_description) { if (!remote_description.isNull() && remote_description.sdp() == "remote") { remote_description_ = remote_description; - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCVoidRequestTask(this, request, true)); } else - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCVoidRequestTask(this, request, false)); } @@ -255,7 +255,7 @@ bool MockWebRTCPeerConnectionHandler::addICECandidate( bool MockWebRTCPeerConnectionHandler::addICECandidate( const WebRTCVoidRequest& request, const WebRTCICECandidate& ice_candidate) { - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCVoidRequestTask(this, request, true)); return true; } @@ -277,7 +277,8 @@ void MockWebRTCPeerConnectionHandler::removeStream( void MockWebRTCPeerConnectionHandler::getStats( const WebRTCStatsRequest& request) { WebRTCStatsResponse response = request.createResponse(); - double current_date = interfaces_->delegate()->getCurrentTimeInMillisecond(); + double current_date = + interfaces_->GetDelegate()->getCurrentTimeInMillisecond(); if (request.hasSelector()) { // FIXME: There is no check that the fetched values are valid. size_t report_index = @@ -292,22 +293,23 @@ void MockWebRTCPeerConnectionHandler::getStats( response.addStatistic(report_index, "type", "video"); } } - interfaces_->delegate()->postTask( + interfaces_->GetDelegate()->postTask( new RTCStatsRequestSucceededTask(this, request, response)); } WebRTCDataChannelHandler* MockWebRTCPeerConnectionHandler::createDataChannel( const WebString& label, const blink::WebRTCDataChannelInit& init) { - interfaces_->delegate()->postTask( - new RemoteDataChannelTask(this, client_, interfaces_->delegate())); + interfaces_->GetDelegate()->postTask( + new RemoteDataChannelTask(this, client_, interfaces_->GetDelegate())); - return new MockWebRTCDataChannelHandler(label, init, interfaces_->delegate()); + return new MockWebRTCDataChannelHandler( + label, init, interfaces_->GetDelegate()); } WebRTCDTMFSenderHandler* MockWebRTCPeerConnectionHandler::createDTMFSender( const WebMediaStreamTrack& track) { - return new MockWebRTCDTMFSenderHandler(track, interfaces_->delegate()); + return new MockWebRTCDTMFSenderHandler(track, interfaces_->GetDelegate()); } void MockWebRTCPeerConnectionHandler::stop() { diff --git a/content/shell/renderer/test_runner/test_interfaces.cc b/content/shell/renderer/test_runner/test_interfaces.cc new file mode 100644 index 0000000000000..38b675f1acef2 --- /dev/null +++ b/content/shell/renderer/test_runner/test_interfaces.cc @@ -0,0 +1,184 @@ +// 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 "content/shell/renderer/test_runner/test_interfaces.h" + +#include + +#include "base/command_line.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "content/shell/common/shell_switches.h" +#include "content/shell/renderer/test_runner/accessibility_controller.h" +#include "content/shell/renderer/test_runner/event_sender.h" +#include "content/shell/renderer/test_runner/gamepad_controller.h" +#include "content/shell/renderer/test_runner/test_runner.h" +#include "content/shell/renderer/test_runner/text_input_controller.h" +#include "content/shell/renderer/test_runner/web_test_proxy.h" +#include "third_party/WebKit/public/platform/WebURL.h" +#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebView.h" + +namespace content { + +TestInterfaces::TestInterfaces() + : accessibility_controller_(new AccessibilityController()), + event_sender_(new EventSender(this)), + gamepad_controller_(new GamepadController()), + text_input_controller_(new TextInputController()), + test_runner_(new TestRunner(this)), + delegate_(0) { + blink::setLayoutTestMode(true); + if (CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableFontAntialiasing)) + blink::setFontAntialiasingEnabledForTest(true); + + // NOTE: please don't put feature specific enable flags here, + // instead add them to RuntimeEnabledFeatures.in + + ResetAll(); +} + +TestInterfaces::~TestInterfaces() { + accessibility_controller_->SetWebView(0); + event_sender_->SetWebView(0); + // gamepad_controller_ doesn't depend on WebView. + text_input_controller_->SetWebView(NULL); + test_runner_->SetWebView(0, 0); + + accessibility_controller_->SetDelegate(0); + event_sender_->SetDelegate(0); + gamepad_controller_->SetDelegate(0); + // text_input_controller_ doesn't depend on WebTestDelegate. + test_runner_->SetDelegate(0); +} + +void TestInterfaces::SetWebView(blink::WebView* web_view, + WebTestProxyBase* proxy) { + proxy_ = proxy; + accessibility_controller_->SetWebView(web_view); + event_sender_->SetWebView(web_view); + // gamepad_controller_ doesn't depend on WebView. + text_input_controller_->SetWebView(web_view); + test_runner_->SetWebView(web_view, proxy); +} + +void TestInterfaces::SetDelegate(WebTestDelegate* delegate) { + accessibility_controller_->SetDelegate(delegate); + event_sender_->SetDelegate(delegate); + gamepad_controller_->SetDelegate(delegate); + // text_input_controller_ doesn't depend on WebTestDelegate. + test_runner_->SetDelegate(delegate); + delegate_ = delegate; +} + +void TestInterfaces::BindTo(blink::WebFrame* frame) { + accessibility_controller_->Install(frame); + event_sender_->Install(frame); + gamepad_controller_->Install(frame); + text_input_controller_->Install(frame); + test_runner_->Install(frame); +} + +void TestInterfaces::ResetTestHelperControllers() { + accessibility_controller_->Reset(); + event_sender_->Reset(); + gamepad_controller_->Reset(); + // text_input_controller_ doesn't have any state to reset. + blink::WebCache::clear(); +} + +void TestInterfaces::ResetAll() { + ResetTestHelperControllers(); + test_runner_->Reset(); +} + +void TestInterfaces::SetTestIsRunning(bool running) { + test_runner_->SetTestIsRunning(running); +} + +void TestInterfaces::ConfigureForTestWithURL(const blink::WebURL& test_url, + bool generate_pixels) { + std::string spec = GURL(test_url).spec(); + test_runner_->setShouldGeneratePixelResults(generate_pixels); + if (spec.find("loading/") != std::string::npos) + test_runner_->setShouldDumpFrameLoadCallbacks(true); + if (spec.find("/dumpAsText/") != std::string::npos) { + test_runner_->setShouldDumpAsText(true); + test_runner_->setShouldGeneratePixelResults(false); + } + if (spec.find("/inspector/") != std::string::npos || + spec.find("/inspector-enabled/") != std::string::npos) + test_runner_->clearDevToolsLocalStorage(); + if (spec.find("/inspector/") != std::string::npos) { + // Subfolder name determines default panel to open. + std::string settings = ""; + std::string test_path = spec.substr(spec.find("/inspector/") + 11); + size_t slash_index = test_path.find("/"); + if (slash_index != std::string::npos) { + settings = base::StringPrintf("{\"lastActivePanel\":\"\\\"%s\\\"\"}", + test_path.substr(0, slash_index).c_str()); + } + test_runner_->showDevTools(settings, std::string()); + } + if (spec.find("/viewsource/") != std::string::npos) { + test_runner_->setShouldEnableViewSource(true); + test_runner_->setShouldGeneratePixelResults(false); + test_runner_->setShouldDumpAsMarkup(true); + } +} + +void TestInterfaces::WindowOpened(WebTestProxyBase* proxy) { + window_list_.push_back(proxy); +} + +void TestInterfaces::WindowClosed(WebTestProxyBase* proxy) { + std::vector::iterator pos = + std::find(window_list_.begin(), window_list_.end(), proxy); + if (pos == window_list_.end()) { + NOTREACHED(); + return; + } + window_list_.erase(pos); +} + +AccessibilityController* TestInterfaces::GetAccessibilityController() { + return accessibility_controller_.get(); +} + +EventSender* TestInterfaces::GetEventSender() { + return event_sender_.get(); +} + +TestRunner* TestInterfaces::GetTestRunner() { + return test_runner_.get(); +} + +WebTestDelegate* TestInterfaces::GetDelegate() { + return delegate_; +} + +WebTestProxyBase* TestInterfaces::GetProxy() { + return proxy_; +} + +const std::vector& TestInterfaces::GetWindowList() { + return window_list_; +} + +blink::WebThemeEngine* TestInterfaces::GetThemeEngine() { + if (!test_runner_->UseMockTheme()) + return 0; +#if defined(OS_MACOSX) + if (!theme_engine_.get()) + theme_engine_.reset(new MockWebThemeEngineMac()); +#else + if (!theme_engine_.get()) + theme_engine_.reset(new MockWebThemeEngine()); +#endif + return theme_engine_.get(); +} + +} // namespace content diff --git a/content/shell/renderer/test_runner/test_interfaces.h b/content/shell/renderer/test_runner/test_interfaces.h new file mode 100644 index 0000000000000..2edfda0abeb6e --- /dev/null +++ b/content/shell/renderer/test_runner/test_interfaces.h @@ -0,0 +1,83 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_SHELL_RENDERER_TEST_RUNNER_TEST_INTERFACES_H_ +#define CONTENT_SHELL_RENDERER_TEST_RUNNER_TEST_INTERFACES_H_ + +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "third_party/WebKit/public/platform/WebNonCopyable.h" + +#if defined(OS_MACOSX) +#include "content/shell/renderer/test_runner/mock_web_theme_engine_mac.h" +#else +#include "content/shell/renderer/test_runner/mock_web_theme_engine.h" +#endif + +namespace blink { +class WebFrame; +class WebThemeEngine; +class WebURL; +class WebView; +} + +namespace content { + +class AccessibilityController; +class EventSender; +class GamepadController; +class TestRunner; +class TextInputController; +class WebTestDelegate; +class WebTestProxyBase; + +class TestInterfaces { + public: + TestInterfaces(); + ~TestInterfaces(); + + void SetWebView(blink::WebView* web_view, WebTestProxyBase* proxy); + void SetDelegate(WebTestDelegate* delegate); + void BindTo(blink::WebFrame* frame); + void ResetTestHelperControllers(); + void ResetAll(); + void SetTestIsRunning(bool running); + void ConfigureForTestWithURL(const blink::WebURL& test_url, + bool generate_pixels); + + void WindowOpened(WebTestProxyBase* proxy); + void WindowClosed(WebTestProxyBase* proxy); + + AccessibilityController* GetAccessibilityController(); + EventSender* GetEventSender(); + TestRunner* GetTestRunner(); + WebTestDelegate* GetDelegate(); + WebTestProxyBase* GetProxy(); + const std::vector& GetWindowList(); + blink::WebThemeEngine* GetThemeEngine(); + + private: + scoped_ptr accessibility_controller_; + scoped_ptr event_sender_; + scoped_ptr gamepad_controller_; + scoped_ptr text_input_controller_; + scoped_ptr test_runner_; + WebTestDelegate* delegate_; + WebTestProxyBase* proxy_; + + std::vector window_list_; +#if defined(OS_MACOSX) + scoped_ptr theme_engine_; +#else + scoped_ptr theme_engine_; +#endif + + DISALLOW_COPY_AND_ASSIGN(TestInterfaces); +}; + +} // namespace content + +#endif // CONTENT_SHELL_RENDERER_TEST_RUNNER_TEST_INTERFACES_H_ diff --git a/content/shell/renderer/test_runner/test_runner.cc b/content/shell/renderer/test_runner/test_runner.cc index 2c58b3a48bb26..1569e57744601 100644 --- a/content/shell/renderer/test_runner/test_runner.cc +++ b/content/shell/renderer/test_runner/test_runner.cc @@ -8,11 +8,11 @@ #include "base/logging.h" #include "content/shell/common/test_runner/test_preferences.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "content/shell/renderer/test_runner/mock_web_push_client.h" #include "content/shell/renderer/test_runner/mock_web_speech_recognizer.h" #include "content/shell/renderer/test_runner/notification_presenter.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "content/shell/renderer/test_runner/web_permissions.h" #include "content/shell/renderer/test_runner/web_test_proxy.h" #include "gin/arguments.h" @@ -2076,7 +2076,7 @@ void TestRunner::WaitForPolicyDelegate() { } int TestRunner::WindowCount() { - return test_interfaces_->windowList().size(); + return test_interfaces_->GetWindowList().size(); } void TestRunner::SetCloseRemainingWindowsWhenComplete( @@ -2085,7 +2085,7 @@ void TestRunner::SetCloseRemainingWindowsWhenComplete( } void TestRunner::ResetTestHelperControllers() { - test_interfaces_->resetTestHelperControllers(); + test_interfaces_->ResetTestHelperControllers(); } void TestRunner::SetTabKeyCyclesThroughElements( @@ -2744,7 +2744,7 @@ void TestRunner::SetMIDIAccessorResult(bool result) { void TestRunner::SetMIDISysexPermission(bool value) { const std::vector& windowList = - test_interfaces_->windowList(); + test_interfaces_->GetWindowList(); for (unsigned i = 0; i < windowList.size(); ++i) windowList.at(i)->GetMIDIClientMock()->setSysexPermission(value); } 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 a280199c46eb4..1a75dd4e1d303 100644 --- a/content/shell/renderer/test_runner/web_frame_test_proxy.h +++ b/content/shell/renderer/test_runner/web_frame_test_proxy.h @@ -6,9 +6,9 @@ #define CONTENT_SHELL_RENDERER_TEST_RUNNER_WEB_FRAME_TEST_PROXY_H_ #include "base/basictypes.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "content/shell/renderer/test_runner/mock_screen_orientation_client.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "content/shell/renderer/test_runner/test_runner.h" #include "content/shell/renderer/test_runner/web_test_proxy.h" #include "content/test/test_media_stream_renderer_factory.h" @@ -169,7 +169,7 @@ class WebFrameTestProxy : public Base { const blink::WebString& message) { base_proxy_->delegate_->printMessage(std::string("CONFIRM NAVIGATION: ") + message.utf8().data() + "\n"); - return !base_proxy_->test_interfaces_->testRunner() + return !base_proxy_->test_interfaces_->GetTestRunner() ->shouldStayOnPageAfterHandlingBeforeUnload(); } diff --git a/content/shell/renderer/test_runner/web_test_proxy.cc b/content/shell/renderer/test_runner/web_test_proxy.cc index d4524ee9ac1d2..7e36976482f62 100644 --- a/content/shell/renderer/test_runner/web_test_proxy.cc +++ b/content/shell/renderer/test_runner/web_test_proxy.cc @@ -10,7 +10,6 @@ #include "base/debug/trace_event.h" #include "base/logging.h" #include "base/strings/stringprintf.h" -#include "content/shell/renderer/test_runner/TestInterfaces.h" #include "content/shell/renderer/test_runner/TestPlugin.h" #include "content/shell/renderer/test_runner/WebTestDelegate.h" #include "content/shell/renderer/test_runner/WebTestInterfaces.h" @@ -22,6 +21,7 @@ #include "content/shell/renderer/test_runner/mock_web_speech_recognizer.h" #include "content/shell/renderer/test_runner/mock_web_user_media_client.h" #include "content/shell/renderer/test_runner/spell_check_client.h" +#include "content/shell/renderer/test_runner/test_interfaces.h" #include "content/shell/renderer/test_runner/test_runner.h" #include "content/shell/renderer/test_runner/web_test_runner.h" // FIXME: Including platform_canvas.h here is a layering violation. @@ -299,7 +299,8 @@ std::string DumpFrameScrollPosition(blink::WebFrame* frame, bool recursive) { std::string DumpAllBackForwardLists(TestInterfaces* interfaces, WebTestDelegate* delegate) { std::string result; - const std::vector& window_list = interfaces->windowList(); + const std::vector& window_list = + interfaces->GetWindowList(); for (size_t i = 0; i < window_list.size(); ++i) result.append(delegate->dumpHistoryForWindow(window_list.at(i))); return result; @@ -316,7 +317,7 @@ WebTestProxyBase::WebTestProxyBase() } WebTestProxyBase::~WebTestProxyBase() { - test_interfaces_->windowClosed(this); + test_interfaces_->WindowClosed(this); // Tests must wait for readback requests to finish before notifying that // they are done. CHECK_EQ(0u, composite_and_readback_callbacks_.size()); @@ -324,7 +325,7 @@ WebTestProxyBase::~WebTestProxyBase() { void WebTestProxyBase::SetInterfaces(WebTestInterfaces* interfaces) { test_interfaces_ = interfaces->testInterfaces(); - test_interfaces_->windowOpened(this); + test_interfaces_->WindowOpened(this); } void WebTestProxyBase::SetDelegate(WebTestDelegate* delegate) { @@ -383,30 +384,31 @@ void WebTestProxyBase::ShowValidationMessage( std::string WebTestProxyBase::CaptureTree(bool debug_render_tree) { bool should_dump_custom_text = - test_interfaces_->testRunner()->shouldDumpAsCustomText(); - bool should_dump_as_text = test_interfaces_->testRunner()->shouldDumpAsText(); + test_interfaces_->GetTestRunner()->shouldDumpAsCustomText(); + bool should_dump_as_text = + test_interfaces_->GetTestRunner()->shouldDumpAsText(); bool should_dump_as_markup = - test_interfaces_->testRunner()->shouldDumpAsMarkup(); - bool should_dump_as_printed = test_interfaces_->testRunner()->isPrinting(); + test_interfaces_->GetTestRunner()->shouldDumpAsMarkup(); + bool should_dump_as_printed = test_interfaces_->GetTestRunner()->isPrinting(); blink::WebFrame* frame = GetWebView()->mainFrame(); std::string data_utf8; if (should_dump_custom_text) { // Append a newline for the test driver. - data_utf8 = test_interfaces_->testRunner()->customDumpText() + "\n"; + data_utf8 = test_interfaces_->GetTestRunner()->customDumpText() + "\n"; } else if (should_dump_as_text) { bool recursive = - test_interfaces_->testRunner()->shouldDumpChildFramesAsText(); + test_interfaces_->GetTestRunner()->shouldDumpChildFramesAsText(); data_utf8 = should_dump_as_printed ? DumpFramesAsPrintedText(frame, recursive) : DumpFramesAsText(frame, recursive); } else if (should_dump_as_markup) { bool recursive = - test_interfaces_->testRunner()->shouldDumpChildFramesAsMarkup(); + test_interfaces_->GetTestRunner()->shouldDumpChildFramesAsMarkup(); // Append a newline for the test driver. data_utf8 = DumpFramesAsMarkup(frame, recursive); } else { - bool recursive = - test_interfaces_->testRunner()->shouldDumpChildFrameScrollPositions(); + bool recursive = test_interfaces_->GetTestRunner() + ->shouldDumpChildFrameScrollPositions(); blink::WebFrame::RenderAsTextControls render_text_behavior = blink::WebFrame::RenderAsTextNormal; if (should_dump_as_printed) @@ -417,7 +419,7 @@ std::string WebTestProxyBase::CaptureTree(bool debug_render_tree) { data_utf8 += DumpFrameScrollPosition(frame, recursive); } - if (test_interfaces_->testRunner()->ShouldDumpBackForwardList()) + if (test_interfaces_->GetTestRunner()->ShouldDumpBackForwardList()) data_utf8 += DumpAllBackForwardLists(test_interfaces_, delegate_); return data_utf8; @@ -427,7 +429,7 @@ void WebTestProxyBase::DrawSelectionRect(SkCanvas* canvas) { // See if we need to draw the selection bounds rect. Selection bounds // rect is the rect enclosing the (possibly transformed) selection. // The rect should be drawn after everything is laid out and painted. - if (!test_interfaces_->testRunner()->shouldDumpSelectionRect()) + if (!test_interfaces_->GetTestRunner()->shouldDumpSelectionRect()) return; // If there is a selection rect - draw a red 1px border enclosing rect blink::WebRect wr = GetWebView()->mainFrame()->selectionBoundsRect(); @@ -521,7 +523,7 @@ void WebTestProxyBase::CapturePixelsAsync( DCHECK(web_widget_->isAcceleratedCompositingActive()); DCHECK(!callback.is_null()); - if (test_interfaces_->testRunner()->isPrinting()) { + if (test_interfaces_->GetTestRunner()->isPrinting()) { base::MessageLoopProxy::current()->PostTask( FROM_HERE, base::Bind(&WebTestProxyBase::CapturePixelsForPrinting, @@ -589,7 +591,7 @@ MockWebSpeechRecognizer* WebTestProxyBase::GetSpeechRecognizerMock() { } void WebTestProxyBase::ScheduleAnimation() { - if (!test_interfaces_->testRunner()->TestIsRunning()) + if (!test_interfaces_->GetTestRunner()->TestIsRunning()) return; if (!animate_scheduled_) { @@ -615,11 +617,11 @@ void WebTestProxyBase::PostAccessibilityEvent(const blink::WebAXObject& obj, // accessibility events, // and AccessibilityController will hold on to their target nodes if we don't // ignore them here. - if (!test_interfaces_->testRunner()->TestIsRunning()) + if (!test_interfaces_->GetTestRunner()->TestIsRunning()) return; if (event == blink::WebAXEventFocus) - test_interfaces_->accessibilityController()->SetFocusedElement(obj); + test_interfaces_->GetAccessibilityController()->SetFocusedElement(obj); const char* event_name = NULL; switch (event) { @@ -712,10 +714,10 @@ void WebTestProxyBase::PostAccessibilityEvent(const blink::WebAXObject& obj, break; } - test_interfaces_->accessibilityController()->NotificationReceived(obj, - event_name); + test_interfaces_->GetAccessibilityController()->NotificationReceived( + obj, event_name); - if (test_interfaces_->accessibilityController() + if (test_interfaces_->GetAccessibilityController() ->ShouldLogAccessibilityEvents()) { std::string message("AccessibilityNotification - "); message += event_name; @@ -740,21 +742,21 @@ void WebTestProxyBase::StartDragging(blink::WebLocalFrame* frame, const blink::WebPoint& point) { // When running a test, we need to fake a drag drop operation otherwise // Windows waits for real mouse events to know when the drag is over. - test_interfaces_->eventSender()->DoDragDrop(data, mask); + test_interfaces_->GetEventSender()->DoDragDrop(data, mask); } // The output from these methods in layout test mode should match that // expected by the layout tests. See EditingDelegate.m in DumpRenderTree. void WebTestProxyBase::DidChangeSelection(bool is_empty_callback) { - if (test_interfaces_->testRunner()->shouldDumpEditingCallbacks()) + if (test_interfaces_->GetTestRunner()->shouldDumpEditingCallbacks()) delegate_->printMessage( "EDITING DELEGATE: " "webViewDidChangeSelection:WebViewDidChangeSelectionNotification\n"); } void WebTestProxyBase::DidChangeContents() { - if (test_interfaces_->testRunner()->shouldDumpEditingCallbacks()) + if (test_interfaces_->GetTestRunner()->shouldDumpEditingCallbacks()) delegate_->printMessage( "EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification\n"); } @@ -765,9 +767,9 @@ bool WebTestProxyBase::CreateView(blink::WebLocalFrame* frame, const blink::WebString& frame_name, blink::WebNavigationPolicy policy, bool suppress_opener) { - if (!test_interfaces_->testRunner()->canOpenWindows()) + if (!test_interfaces_->GetTestRunner()->canOpenWindows()) return false; - if (test_interfaces_->testRunner()->shouldDumpCreateView()) + if (test_interfaces_->GetTestRunner()->shouldDumpCreateView()) delegate_->printMessage(std::string("createView(") + URLDescription(request.url()) + ")\n"); return true; @@ -782,7 +784,7 @@ blink::WebPlugin* WebTestProxyBase::CreatePlugin( } void WebTestProxyBase::SetStatusText(const blink::WebString& text) { - if (!test_interfaces_->testRunner()->shouldDumpStatusCallbacks()) + if (!test_interfaces_->GetTestRunner()->shouldDumpStatusCallbacks()) return; delegate_->printMessage( std::string("UI DELEGATE STATUS CALLBACK: setStatusText:") + @@ -790,14 +792,14 @@ void WebTestProxyBase::SetStatusText(const blink::WebString& text) { } void WebTestProxyBase::DidStopLoading() { - if (test_interfaces_->testRunner()->shouldDumpProgressFinishedCallback()) + if (test_interfaces_->GetTestRunner()->shouldDumpProgressFinishedCallback()) delegate_->printMessage("postProgressFinishedNotification\n"); } void WebTestProxyBase::ShowContextMenu( blink::WebLocalFrame* frame, const blink::WebContextMenuData& context_menu_data) { - test_interfaces_->eventSender()->SetContextMenuData(context_menu_data); + test_interfaces_->GetEventSender()->SetContextMenuData(context_menu_data); } blink::WebUserMediaClient* WebTestProxyBase::GetUserMediaClient() { @@ -817,7 +819,7 @@ void WebTestProxyBase::PrintPage(blink::WebLocalFrame* frame) { } blink::WebNotificationPresenter* WebTestProxyBase::GetNotificationPresenter() { - return test_interfaces_->testRunner()->notification_presenter(); + return test_interfaces_->GetTestRunner()->notification_presenter(); } blink::WebMIDIClient* WebTestProxyBase::GetWebMIDIClient() { @@ -829,15 +831,15 @@ blink::WebSpeechRecognizer* WebTestProxyBase::GetSpeechRecognizer() { } bool WebTestProxyBase::RequestPointerLock() { - return test_interfaces_->testRunner()->RequestPointerLock(); + return test_interfaces_->GetTestRunner()->RequestPointerLock(); } void WebTestProxyBase::RequestPointerUnlock() { - test_interfaces_->testRunner()->RequestPointerUnlock(); + test_interfaces_->GetTestRunner()->RequestPointerUnlock(); } bool WebTestProxyBase::IsPointerLocked() { - return test_interfaces_->testRunner()->isPointerLocked(); + return test_interfaces_->GetTestRunner()->isPointerLocked(); } void WebTestProxyBase::DidFocus() { @@ -850,7 +852,7 @@ void WebTestProxyBase::DidBlur() { void WebTestProxyBase::SetToolTipText(const blink::WebString& text, blink::WebTextDirection direction) { - test_interfaces_->testRunner()->setToolTipText(text); + test_interfaces_->GetTestRunner()->setToolTipText(text); } void WebTestProxyBase::DidOpenChooser() { @@ -870,7 +872,7 @@ void WebTestProxyBase::LoadURLExternally( const blink::WebURLRequest& request, blink::WebNavigationPolicy policy, const blink::WebString& suggested_name) { - if (test_interfaces_->testRunner()->shouldWaitUntilExternalURLLoad()) { + if (test_interfaces_->GetTestRunner()->shouldWaitUntilExternalURLLoad()) { if (policy == blink::WebNavigationPolicyDownload) { delegate_->printMessage( std::string("Downloading URL with suggested filename \"") + @@ -884,15 +886,15 @@ void WebTestProxyBase::LoadURLExternally( } void WebTestProxyBase::DidStartProvisionalLoad(blink::WebLocalFrame* frame) { - if (!test_interfaces_->testRunner()->topLoadingFrame()) - test_interfaces_->testRunner()->setTopLoadingFrame(frame, false); + if (!test_interfaces_->GetTestRunner()->topLoadingFrame()) + test_interfaces_->GetTestRunner()->setTopLoadingFrame(frame, false); - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didStartProvisionalLoadForFrame\n"); } - if (test_interfaces_->testRunner() + if (test_interfaces_->GetTestRunner() ->shouldDumpUserGestureInFrameLoadCallbacks()) { PrintFrameuserGestureStatus( delegate_, frame, " - in didStartProvisionalLoadForFrame\n"); @@ -901,7 +903,7 @@ void WebTestProxyBase::DidStartProvisionalLoad(blink::WebLocalFrame* frame) { void WebTestProxyBase::DidReceiveServerRedirectForProvisionalLoad( blink::WebLocalFrame* frame) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage( " - didReceiveServerRedirectForProvisionalLoadForFrame\n"); @@ -910,7 +912,7 @@ void WebTestProxyBase::DidReceiveServerRedirectForProvisionalLoad( bool WebTestProxyBase::DidFailProvisionalLoad(blink::WebLocalFrame* frame, const blink::WebURLError& error) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didFailProvisionalLoadWithError\n"); } @@ -922,7 +924,7 @@ void WebTestProxyBase::DidCommitProvisionalLoad( blink::WebLocalFrame* frame, const blink::WebHistoryItem& history_item, blink::WebHistoryCommitType history_type) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didCommitLoadForFrame\n"); } @@ -933,27 +935,27 @@ void WebTestProxyBase::DidReceiveTitle(blink::WebLocalFrame* frame, blink::WebTextDirection direction) { blink::WebCString title8 = title.utf8(); - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(std::string(" - didReceiveTitle: ") + title8.data() + "\n"); } - if (test_interfaces_->testRunner()->shouldDumpTitleChanges()) + if (test_interfaces_->GetTestRunner()->shouldDumpTitleChanges()) delegate_->printMessage(std::string("TITLE CHANGED: '") + title8.data() + "'\n"); } void WebTestProxyBase::DidChangeIcon(blink::WebLocalFrame* frame, blink::WebIconURL::Type icon_type) { - if (test_interfaces_->testRunner()->shouldDumpIconChanges()) { + if (test_interfaces_->GetTestRunner()->shouldDumpIconChanges()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(std::string(" - didChangeIcons\n")); } } void WebTestProxyBase::DidFinishDocumentLoad(blink::WebLocalFrame* frame) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didFinishDocumentLoadForFrame\n"); } else { @@ -967,7 +969,7 @@ void WebTestProxyBase::DidFinishDocumentLoad(blink::WebLocalFrame* frame) { } void WebTestProxyBase::DidHandleOnloadEvents(blink::WebLocalFrame* frame) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didHandleOnloadEventsForFrame\n"); } @@ -975,7 +977,7 @@ void WebTestProxyBase::DidHandleOnloadEvents(blink::WebLocalFrame* frame) { void WebTestProxyBase::DidFailLoad(blink::WebLocalFrame* frame, const blink::WebURLError& error) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didFailLoadWithError\n"); } @@ -983,7 +985,7 @@ void WebTestProxyBase::DidFailLoad(blink::WebLocalFrame* frame, } void WebTestProxyBase::DidFinishLoad(blink::WebLocalFrame* frame) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(" - didFinishLoadForFrame\n"); } @@ -993,13 +995,13 @@ void WebTestProxyBase::DidFinishLoad(blink::WebLocalFrame* frame) { void WebTestProxyBase::DidDetectXSS(blink::WebLocalFrame* frame, const blink::WebURL& insecure_url, bool did_block_entire_page) { - if (test_interfaces_->testRunner()->shouldDumpFrameLoadCallbacks()) + if (test_interfaces_->GetTestRunner()->shouldDumpFrameLoadCallbacks()) delegate_->printMessage("didDetectXSS\n"); } void WebTestProxyBase::DidDispatchPingLoader(blink::WebLocalFrame* frame, const blink::WebURL& url) { - if (test_interfaces_->testRunner()->shouldDumpPingLoaderCallbacks()) + if (test_interfaces_->GetTestRunner()->shouldDumpPingLoaderCallbacks()) delegate_->printMessage(std::string("PingLoader dispatched to '") + URLDescription(url).c_str() + "'.\n"); } @@ -1007,7 +1009,7 @@ void WebTestProxyBase::DidDispatchPingLoader(blink::WebLocalFrame* frame, void WebTestProxyBase::WillRequestResource( blink::WebLocalFrame* frame, const blink::WebCachedURLRequest& request) { - if (test_interfaces_->testRunner()->shouldDumpResourceRequestCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpResourceRequestCallbacks()) { PrintFrameDescription(delegate_, frame); delegate_->printMessage(std::string(" - ") + request.initiatorName().utf8().data()); @@ -1029,15 +1031,15 @@ void WebTestProxyBase::WillSendRequest( GURL main_document_url = request.firstPartyForCookies(); if (redirect_response.isNull() && - (test_interfaces_->testRunner()->shouldDumpResourceLoadCallbacks() || - test_interfaces_->testRunner()->shouldDumpResourcePriorities())) { + (test_interfaces_->GetTestRunner()->shouldDumpResourceLoadCallbacks() || + test_interfaces_->GetTestRunner()->shouldDumpResourcePriorities())) { DCHECK(resource_identifier_map_.find(identifier) == resource_identifier_map_.end()); resource_identifier_map_[identifier] = DescriptionSuitableForTestResult(request_url); } - if (test_interfaces_->testRunner()->shouldDumpResourceLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpResourceLoadCallbacks()) { if (resource_identifier_map_.find(identifier) == resource_identifier_map_.end()) delegate_->printMessage(""); @@ -1055,7 +1057,7 @@ void WebTestProxyBase::WillSendRequest( delegate_->printMessage("\n"); } - if (test_interfaces_->testRunner()->shouldDumpResourcePriorities()) { + if (test_interfaces_->GetTestRunner()->shouldDumpResourcePriorities()) { delegate_->printMessage( DescriptionSuitableForTestResult(request_url).c_str()); delegate_->printMessage(" has priority "); @@ -1063,9 +1065,9 @@ void WebTestProxyBase::WillSendRequest( delegate_->printMessage("\n"); } - if (test_interfaces_->testRunner()->httpHeadersToClear()) { + if (test_interfaces_->GetTestRunner()->httpHeadersToClear()) { const std::set* clearHeaders = - test_interfaces_->testRunner()->httpHeadersToClear(); + test_interfaces_->GetTestRunner()->httpHeadersToClear(); for (std::set::const_iterator header = clearHeaders->begin(); header != clearHeaders->end(); ++header) @@ -1094,7 +1096,7 @@ void WebTestProxyBase::DidReceiveResponse( blink::WebLocalFrame* frame, unsigned identifier, const blink::WebURLResponse& response) { - if (test_interfaces_->testRunner()->shouldDumpResourceLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpResourceLoadCallbacks()) { if (resource_identifier_map_.find(identifier) == resource_identifier_map_.end()) delegate_->printMessage(""); @@ -1104,7 +1106,8 @@ void WebTestProxyBase::DidReceiveResponse( PrintResponseDescription(delegate_, response); delegate_->printMessage("\n"); } - if (test_interfaces_->testRunner()->shouldDumpResourceResponseMIMETypes()) { + if (test_interfaces_->GetTestRunner() + ->shouldDumpResourceResponseMIMETypes()) { GURL url = response.url(); blink::WebString mime_type = response.mimeType(); delegate_->printMessage(url.ExtractFileName()); @@ -1122,7 +1125,7 @@ void WebTestProxyBase::DidChangeResourcePriority( unsigned identifier, const blink::WebURLRequest::Priority& priority, int intra_priority_value) { - if (test_interfaces_->testRunner()->shouldDumpResourcePriorities()) { + if (test_interfaces_->GetTestRunner()->shouldDumpResourcePriorities()) { if (resource_identifier_map_.find(identifier) == resource_identifier_map_.end()) delegate_->printMessage(""); @@ -1137,7 +1140,7 @@ void WebTestProxyBase::DidChangeResourcePriority( void WebTestProxyBase::DidFinishResourceLoad(blink::WebLocalFrame* frame, unsigned identifier) { - if (test_interfaces_->testRunner()->shouldDumpResourceLoadCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpResourceLoadCallbacks()) { if (resource_identifier_map_.find(identifier) == resource_identifier_map_.end()) delegate_->printMessage(""); @@ -1191,28 +1194,28 @@ void WebTestProxyBase::DidAddMessageToConsole( } void WebTestProxyBase::LocationChangeDone(blink::WebFrame* frame) { - if (frame != test_interfaces_->testRunner()->topLoadingFrame()) + if (frame != test_interfaces_->GetTestRunner()->topLoadingFrame()) return; - test_interfaces_->testRunner()->setTopLoadingFrame(frame, true); + test_interfaces_->GetTestRunner()->setTopLoadingFrame(frame, true); } blink::WebNavigationPolicy WebTestProxyBase::DecidePolicyForNavigation( const blink::WebFrameClient::NavigationPolicyInfo& info) { blink::WebNavigationPolicy result; - if (!test_interfaces_->testRunner()->policyDelegateEnabled()) + if (!test_interfaces_->GetTestRunner()->policyDelegateEnabled()) return info.defaultPolicy; delegate_->printMessage(std::string("Policy delegate: attempt to load ") + URLDescription(info.urlRequest.url()) + " with navigation type '" + WebNavigationTypeToString(info.navigationType) + "'\n"); - if (test_interfaces_->testRunner()->policyDelegateIsPermissive()) + if (test_interfaces_->GetTestRunner()->policyDelegateIsPermissive()) result = blink::WebNavigationPolicyCurrentTab; else result = blink::WebNavigationPolicyIgnore; - if (test_interfaces_->testRunner()->policyDelegateShouldNotifyDone()) - test_interfaces_->testRunner()->policyDelegateDone(); + if (test_interfaces_->GetTestRunner()->policyDelegateShouldNotifyDone()) + test_interfaces_->GetTestRunner()->policyDelegateDone(); return result; } @@ -1221,7 +1224,7 @@ bool WebTestProxyBase::WillCheckAndDispatchMessageEvent( blink::WebFrame* target_frame, blink::WebSecurityOrigin target, blink::WebDOMMessageEvent event) { - if (test_interfaces_->testRunner()->shouldInterceptPostMessage()) { + if (test_interfaces_->GetTestRunner()->shouldInterceptPostMessage()) { delegate_->printMessage("intercepted postMessage\n"); return true; } @@ -1230,7 +1233,7 @@ bool WebTestProxyBase::WillCheckAndDispatchMessageEvent( } void WebTestProxyBase::PostSpellCheckEvent(const blink::WebString& event_name) { - if (test_interfaces_->testRunner()->shouldDumpSpellCheckCallbacks()) { + if (test_interfaces_->GetTestRunner()->shouldDumpSpellCheckCallbacks()) { delegate_->printMessage(std::string("SpellCheckEvent: ") + event_name.utf8().data() + "\n"); } diff --git a/content/test/blink_test_environment.cc b/content/test/blink_test_environment.cc new file mode 100644 index 0000000000000..db556309f6a36 --- /dev/null +++ b/content/test/blink_test_environment.cc @@ -0,0 +1,121 @@ +// 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 "content/test/blink_test_environment.h" + +#include + +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/string_tokenizer.h" +#include "content/public/common/content_switches.h" +#include "content/public/common/user_agent.h" +#include "content/test/test_webkit_platform_support.h" +#include "third_party/WebKit/public/web/WebCache.h" +#include "third_party/WebKit/public/web/WebKit.h" +#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" +#include "url/url_util.h" + +#if defined(OS_WIN) +#include "ui/gfx/win/dpi.h" +#endif + +#if defined(OS_ANDROID) +#include "base/android/jni_android.h" +#include "net/android/network_library.h" +#endif + +#if defined(OS_MACOSX) +#include "base/test/mock_chrome_application_mac.h" +#endif + +namespace content { + +namespace { + +void EnableBlinkPlatformLogChannels(const std::string& channels) { + if (channels.empty()) + return; + base::StringTokenizer t(channels, ", "); + while (t.GetNext()) + blink::enableLogChannel(t.token().c_str()); +} + +void ParseBlinkCommandLineArgumentsForUnitTests() { + const CommandLine& command_line = *CommandLine::ForCurrentProcess(); + EnableBlinkPlatformLogChannels( + command_line.GetSwitchValueASCII(switches::kBlinkPlatformLogChannels)); +} + +class TestEnvironment { + public: +#if defined(OS_ANDROID) + // Android UI message loop goes through Java, so don't use it in tests. + typedef base::MessageLoop MessageLoopType; +#else + typedef base::MessageLoopForUI MessageLoopType; +#endif + + TestEnvironment() { + main_message_loop_.reset(new MessageLoopType); + + // TestWebKitPlatformSupport must be instantiated after MessageLoopType. + webkit_platform_support_.reset(new TestWebKitPlatformSupport); + } + + ~TestEnvironment() { + } + + TestWebKitPlatformSupport* webkit_platform_support() const { + return webkit_platform_support_.get(); + } + + private: + scoped_ptr main_message_loop_; + scoped_ptr webkit_platform_support_; +}; + +TestEnvironment* test_environment; + +} // namespace + +void SetUpBlinkTestEnvironment() { + ParseBlinkCommandLineArgumentsForUnitTests(); + + blink::WebRuntimeFeatures::enableExperimentalFeatures(true); + blink::WebRuntimeFeatures::enableTestOnlyFeatures(true); + +#if defined(OS_ANDROID) + JNIEnv* env = base::android::AttachCurrentThread(); + net::android::RegisterNetworkLibrary(env); +#endif + +#if defined(OS_MACOSX) + mock_cr_app::RegisterMockCrApp(); +#endif + +#if defined(OS_WIN) + gfx::InitDeviceScaleFactor(1.0f); +#endif + + // Explicitly initialize the GURL library before spawning any threads. + // Otherwise crash may happend when different threads try to create a GURL + // at same time. + url::Initialize(); + test_environment = new TestEnvironment; +} + +void TearDownBlinkTestEnvironment() { + // Flush any remaining messages before we kill ourselves. + // http://code.google.com/p/chromium/issues/detail?id=9500 + base::RunLoop().RunUntilIdle(); + + if (RunningOnValgrind()) + blink::WebCache::clear(); + delete test_environment; + test_environment = NULL; +} + +} // namespace content diff --git a/content/test/blink_test_environment.h b/content/test/blink_test_environment.h new file mode 100644 index 0000000000000..6ae05d5613cd0 --- /dev/null +++ b/content/test/blink_test_environment.h @@ -0,0 +1,19 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_TEST_BLINK_TEST_ENVIRONMENT_H_ +#define CONTENT_TEST_BLINK_TEST_ENVIRONMENT_H_ + +// This package provides functions used by webkit_unit_tests. +namespace content { + +// Initializes Blink test environment for unit tests. +void SetUpBlinkTestEnvironment(); + +// Terminates Blink test environment for unit tests. +void TearDownBlinkTestEnvironment(); + +} // namespace content + +#endif // CONTENT_TEST_BLINK_TEST_ENVIRONMENT_H_ diff --git a/content/test/data/accessibility/modal-dialog-stack.html b/content/test/data/accessibility/modal-dialog-stack.html index 3b552968bd38d..5e15665e233f6 100644 --- a/content/test/data/accessibility/modal-dialog-stack.html +++ b/content/test/data/accessibility/modal-dialog-stack.html @@ -3,7 +3,7 @@ --> -Test the accessiblity tree after a pending modal dialog becomes active. +Test the accessibility tree after a pending modal dialog becomes active.
    This was the top dialog and should not be in the tree. diff --git a/content/test/data/overscroll_navigation.html b/content/test/data/overscroll_navigation.html index e4f2500db14b4..fb1d7412a996c 100644 --- a/content/test/data/overscroll_navigation.html +++ b/content/test/data/overscroll_navigation.html @@ -28,6 +28,8 @@ diff --git a/content/test/data/service_worker/sync.js.mock-http-headers b/content/test/data/service_worker/sync.js.mock-http-headers new file mode 100644 index 0000000000000..c9a3625203e74 --- /dev/null +++ b/content/test/data/service_worker/sync.js.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript diff --git a/content/test/data/service_worker/worker.js.mock-http-headers b/content/test/data/service_worker/worker.js.mock-http-headers new file mode 100644 index 0000000000000..c9a3625203e74 --- /dev/null +++ b/content/test/data/service_worker/worker.js.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript diff --git a/content/test/data/service_worker/worker_activate_rejected.js.mock-http-headers b/content/test/data/service_worker/worker_activate_rejected.js.mock-http-headers new file mode 100644 index 0000000000000..c9a3625203e74 --- /dev/null +++ b/content/test/data/service_worker/worker_activate_rejected.js.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript diff --git a/content/test/data/service_worker/worker_install_fulfilled.js.mock-http-headers b/content/test/data/service_worker/worker_install_fulfilled.js.mock-http-headers new file mode 100644 index 0000000000000..c9a3625203e74 --- /dev/null +++ b/content/test/data/service_worker/worker_install_fulfilled.js.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript diff --git a/content/test/data/service_worker/worker_install_rejected.js.mock-http-headers b/content/test/data/service_worker/worker_install_rejected.js.mock-http-headers new file mode 100644 index 0000000000000..c9a3625203e74 --- /dev/null +++ b/content/test/data/service_worker/worker_install_rejected.js.mock-http-headers @@ -0,0 +1,2 @@ +HTTP/1.1 200 OK +Content-Type: text/javascript diff --git a/content/test/gpu/gpu_tests/context_lost.py b/content/test/gpu/gpu_tests/context_lost.py index eb4e334ae1a2a..89eabfba0eabf 100644 --- a/content/test/gpu/gpu_tests/context_lost.py +++ b/content/test/gpu/gpu_tests/context_lost.py @@ -65,7 +65,7 @@ def CustomizeBrowserOptions(self, options): # Required for about:gpucrash handling from Telemetry. options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): if page.kill_gpu_process: # Doing the GPU process kill operation cooperatively -- in the # same page's context -- is much more stressful than restarting diff --git a/content/test/gpu/gpu_tests/gpu_process.py b/content/test/gpu/gpu_tests/gpu_process.py index 1f06100fb5358..531319cf6a0c8 100644 --- a/content/test/gpu/gpu_tests/gpu_process.py +++ b/content/test/gpu/gpu_tests/gpu_process.py @@ -27,7 +27,7 @@ def __init__(self): def CustomizeBrowserOptions(self, options): options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): has_gpu_process_js = 'chrome.gpuBenchmarking.hasGpuProcess()' has_gpu_process = tab.EvaluateJavaScript(has_gpu_process_js) if not has_gpu_process: diff --git a/content/test/gpu/gpu_tests/gpu_rasterization.py b/content/test/gpu/gpu_tests/gpu_rasterization.py index ab824491632f1..0e4fd06643979 100644 --- a/content/test/gpu/gpu_tests/gpu_rasterization.py +++ b/content/test/gpu/gpu_tests/gpu_rasterization.py @@ -34,7 +34,7 @@ def CustomizeBrowserOptions(self, options): '--force-gpu-rasterization', '--enable-gpu-benchmarking']) - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): if not _DidTestSucceed(tab): raise page_test.Failure('Page indicated a failure') diff --git a/content/test/gpu/gpu_tests/hardware_accelerated_feature.py b/content/test/gpu/gpu_tests/hardware_accelerated_feature.py index 45253c478b447..98e3ff19ebf3b 100644 --- a/content/test/gpu/gpu_tests/hardware_accelerated_feature.py +++ b/content/test/gpu/gpu_tests/hardware_accelerated_feature.py @@ -26,7 +26,7 @@ """; class _HardwareAcceleratedFeatureValidator(page_test.PageTest): - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): feature = page.feature if not tab.EvaluateJavaScript('VerifyHardwareAccelerated("%s")' % feature): print 'Test failed. Printing page contents:' diff --git a/content/test/gpu/gpu_tests/maps.py b/content/test/gpu/gpu_tests/maps.py index 66ee376dcdeb7..c5885e98e45cf 100644 --- a/content/test/gpu/gpu_tests/maps.py +++ b/content/test/gpu/gpu_tests/maps.py @@ -24,7 +24,7 @@ class _MapsValidator(cloud_storage_test_base.ValidatorBase): def CustomizeBrowserOptions(self, options): options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): # TODO: This should not be necessary, but it's not clear if the test is # failing on the bots in it's absence. Remove once we can verify that it's # safe to do so. diff --git a/content/test/gpu/gpu_tests/memory.py b/content/test/gpu/gpu_tests/memory.py index aa392670693cb..dbb111f9ebbd5 100644 --- a/content/test/gpu/gpu_tests/memory.py +++ b/content/test/gpu/gpu_tests/memory.py @@ -11,7 +11,7 @@ MEMORY_LIMIT_MB = 192 SINGLE_TAB_LIMIT_MB = 192 -WIGGLE_ROOM_MB = 8 +WIGGLE_ROOM_MB = 12 test_harness_script = r""" var domAutomationController = {}; @@ -63,7 +63,7 @@ """ % MEMORY_LIMIT_MB class _MemoryValidator(page_test.PageTest): - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): timeline_data = tab.browser.StopTracing() timeline_model = model.TimelineModel(timeline_data) for process in timeline_model.GetAllProcesses(): diff --git a/content/test/gpu/gpu_tests/pixel.py b/content/test/gpu/gpu_tests/pixel.py index 81767966a8fcc..90fcaa619dc26 100644 --- a/content/test/gpu/gpu_tests/pixel.py +++ b/content/test/gpu/gpu_tests/pixel.py @@ -53,7 +53,7 @@ class _PixelValidator(cloud_storage_test_base.ValidatorBase): def CustomizeBrowserOptions(self, options): options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): if not _DidTestSucceed(tab): raise page_test.Failure('Page indicated a failure') diff --git a/content/test/gpu/gpu_tests/screenshot_sync.py b/content/test/gpu/gpu_tests/screenshot_sync.py index c280b25377281..c7f5b3ecda39f 100644 --- a/content/test/gpu/gpu_tests/screenshot_sync.py +++ b/content/test/gpu/gpu_tests/screenshot_sync.py @@ -19,7 +19,7 @@ class _ScreenshotSyncValidator(page_test.PageTest): def CustomizeBrowserOptions(self, options): options.AppendExtraBrowserArgs('--enable-gpu-benchmarking') - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): test_success = tab.EvaluateJavaScript('window.__testSuccess') if not test_success: message = tab.EvaluateJavaScript('window.__testMessage') diff --git a/content/test/gpu/gpu_tests/webgl_conformance.py b/content/test/gpu/gpu_tests/webgl_conformance.py index 3dbaa1c230aba..df297ee877977 100644 --- a/content/test/gpu/gpu_tests/webgl_conformance.py +++ b/content/test/gpu/gpu_tests/webgl_conformance.py @@ -71,7 +71,7 @@ class WebglConformanceValidator(page_test.PageTest): def __init__(self): super(WebglConformanceValidator, self).__init__(attempts=1, max_failures=10) - def ValidatePage(self, page, tab, results): + def ValidateAndMeasurePage(self, page, tab, results): if not _DidWebGLTestSucceed(tab): raise page_test.Failure(_WebGLTestMessages(tab)) diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py index d681ce38d3857..239e1c38d5f30 100644 --- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py +++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py @@ -35,9 +35,16 @@ def SetExpectations(self): # 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/rendering/negative-one-index.html', ['win'], bug=396058) + # Win D3D9 failures (unfortunately can not target just this + # configuration with test expectations) + self.Fail('conformance/extensions/angle-instanced-arrays.html', + ['win'], bug=401713) + # Win7 / Intel failures self.Fail('conformance/rendering/gl-scissor-test.html', ['win7', 'intel'], bug=314997) @@ -49,8 +56,6 @@ def SetExpectations(self): ['win7', 'intel'], bug=372511) self.Fail('conformance/glsl/misc/shader-with-array-of-structs-uniform.html', ['win7', 'intel', 'nvidia'], bug=373972) - self.Fail('conformance/extensions/angle-instanced-arrays.html', - ['win7', 'intel'], bug=398337) # Mac failures self.Fail('conformance/glsl/misc/shader-struct-scope.html', diff --git a/content/test/gpu/run_unittests b/content/test/gpu/run_unittests index 9e31864049133..62d8cfee0ec61 100755 --- a/content/test/gpu/run_unittests +++ b/content/test/gpu/run_unittests @@ -15,13 +15,13 @@ import sys sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir, 'tools', 'telemetry')) -from telemetry.unittest import gtest_output_formatter +from telemetry.unittest import gtest_progress_reporter from telemetry.unittest import run_tests if __name__ == '__main__': base_dir = os.path.dirname(os.path.realpath(__file__)) - output_formatters = [ - gtest_output_formatter.GTestOutputFormatter(sys.stdout)] - run_tests.config = run_tests.Config(base_dir, [base_dir], output_formatters) + progress_reporters = [ + gtest_progress_reporter.GTestProgressReporter(sys.stdout)] + run_tests.config = run_tests.Config(base_dir, [base_dir], progress_reporters) sys.exit(run_tests.RunTestsCommand.main()) diff --git a/content/test/layouttest_support.cc b/content/test/layouttest_support.cc index b57dadcbbec80..016d459c4cd59 100644 --- a/content/test/layouttest_support.cc +++ b/content/test/layouttest_support.cc @@ -193,8 +193,69 @@ void SetDeviceColorProfile(RenderView* render_view, const std::string& name) { } size_t size() { - const size_t kColorProfileSizeInBytes = 490u; - return kColorProfileSizeInBytes; + const size_t kTestColorProfileSizeInBytes = 490u; + return kTestColorProfileSizeInBytes; + } + }; + + struct AdobeRGBColorProfile { + char* data() { + static unsigned char color_profile_data[] = { + 0x00,0x00,0x02,0x30,0x41,0x44,0x42,0x45,0x02,0x10,0x00,0x00, + 0x6d,0x6e,0x74,0x72,0x52,0x47,0x42,0x20,0x58,0x59,0x5a,0x20, + 0x07,0xd0,0x00,0x08,0x00,0x0b,0x00,0x13,0x00,0x33,0x00,0x3b, + 0x61,0x63,0x73,0x70,0x41,0x50,0x50,0x4c,0x00,0x00,0x00,0x00, + 0x6e,0x6f,0x6e,0x65,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xf6,0xd6, + 0x00,0x01,0x00,0x00,0x00,0x00,0xd3,0x2d,0x41,0x44,0x42,0x45, + 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,0x0a, + 0x63,0x70,0x72,0x74,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x32, + 0x64,0x65,0x73,0x63,0x00,0x00,0x01,0x30,0x00,0x00,0x00,0x6b, + 0x77,0x74,0x70,0x74,0x00,0x00,0x01,0x9c,0x00,0x00,0x00,0x14, + 0x62,0x6b,0x70,0x74,0x00,0x00,0x01,0xb0,0x00,0x00,0x00,0x14, + 0x72,0x54,0x52,0x43,0x00,0x00,0x01,0xc4,0x00,0x00,0x00,0x0e, + 0x67,0x54,0x52,0x43,0x00,0x00,0x01,0xd4,0x00,0x00,0x00,0x0e, + 0x62,0x54,0x52,0x43,0x00,0x00,0x01,0xe4,0x00,0x00,0x00,0x0e, + 0x72,0x58,0x59,0x5a,0x00,0x00,0x01,0xf4,0x00,0x00,0x00,0x14, + 0x67,0x58,0x59,0x5a,0x00,0x00,0x02,0x08,0x00,0x00,0x00,0x14, + 0x62,0x58,0x59,0x5a,0x00,0x00,0x02,0x1c,0x00,0x00,0x00,0x14, + 0x74,0x65,0x78,0x74,0x00,0x00,0x00,0x00,0x43,0x6f,0x70,0x79, + 0x72,0x69,0x67,0x68,0x74,0x20,0x32,0x30,0x30,0x30,0x20,0x41, + 0x64,0x6f,0x62,0x65,0x20,0x53,0x79,0x73,0x74,0x65,0x6d,0x73, + 0x20,0x49,0x6e,0x63,0x6f,0x72,0x70,0x6f,0x72,0x61,0x74,0x65, + 0x64,0x00,0x00,0x00,0x64,0x65,0x73,0x63,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x11,0x41,0x64,0x6f,0x62,0x65,0x20,0x52,0x47, + 0x42,0x20,0x28,0x31,0x39,0x39,0x38,0x29,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,0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00, + 0x00,0x00,0xf3,0x51,0x00,0x01,0x00,0x00,0x00,0x01,0x16,0xcc, + 0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x63,0x75,0x72,0x76, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x33,0x00,0x00, + 0x63,0x75,0x72,0x76,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, + 0x02,0x33,0x00,0x00,0x63,0x75,0x72,0x76,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x02,0x33,0x00,0x00,0x58,0x59,0x5a,0x20, + 0x00,0x00,0x00,0x00,0x00,0x00,0x9c,0x18,0x00,0x00,0x4f,0xa5, + 0x00,0x00,0x04,0xfc,0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00, + 0x00,0x00,0x34,0x8d,0x00,0x00,0xa0,0x2c,0x00,0x00,0x0f,0x95, + 0x58,0x59,0x5a,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x26,0x31, + 0x00,0x00,0x10,0x2f,0x00,0x00,0xbe,0x9c + }; + + return reinterpret_cast(color_profile_data); + } + + size_t size() { + const size_t kAdobeRGBColorProfileSizeInBytes = 560u; + return kAdobeRGBColorProfileSizeInBytes; } }; @@ -203,6 +264,9 @@ void SetDeviceColorProfile(RenderView* render_view, const std::string& name) { } else if (name == "test") { TestColorProfile test; color_profile.assign(test.data(), test.data() + test.size()); + } else if (name == "adobeRGB") { + AdobeRGBColorProfile test; + color_profile.assign(test.data(), test.data() + test.size()); } static_cast(render_view)-> diff --git a/content/test/ppapi/ppapi_test.cc b/content/test/ppapi/ppapi_test.cc index 20d4c6dc8ac36..50f7423eb4821 100644 --- a/content/test/ppapi/ppapi_test.cc +++ b/content/test/ppapi/ppapi_test.cc @@ -49,6 +49,9 @@ void PPAPITestBase::SetUpCommandLine(base::CommandLine* command_line) { // Smooth scrolling confuses the scrollbar test. command_line->AppendSwitch(switches::kDisableSmoothScrolling); + + // Allow manual garbage collection. + command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose_gc"); } GURL PPAPITestBase::GetTestFileUrl(const std::string& test_case) { diff --git a/content/test/test_render_frame_host.cc b/content/test/test_render_frame_host.cc index f5c656ff1e1ef..6b4c0a28ba805 100644 --- a/content/test/test_render_frame_host.cc +++ b/content/test/test_render_frame_host.cc @@ -51,10 +51,16 @@ TestRenderFrameHost::TestRenderFrameHost(RenderViewHostImpl* render_view_host, TestRenderFrameHost::~TestRenderFrameHost() {} -RenderFrameHost* TestRenderFrameHost::AppendChild( +TestRenderViewHost* TestRenderFrameHost::GetRenderViewHost() { + return static_cast( + RenderFrameHostImpl::GetRenderViewHost()); +} + +TestRenderFrameHost* TestRenderFrameHost::AppendChild( const std::string& frame_name) { OnCreateChildFrame(GetProcess()->GetNextRoutingID(), frame_name); - return child_creation_observer_.last_created_frame(); + return static_cast( + child_creation_observer_.last_created_frame()); } void TestRenderFrameHost::SendNavigateWithTransition( @@ -165,7 +171,7 @@ void TestRenderFrameHost::SendBeginNavigationWithURL(const GURL& url) { params.method = "GET"; params.url = url; params.referrer_policy = blink::WebReferrerPolicyDefault; - params.load_flags = net::LOAD_NORMAL | net::LOAD_ENABLE_LOAD_TIMING; + params.load_flags = net::LOAD_NORMAL; params.has_user_gesture = false; params.transition_type = PAGE_TRANSITION_LINK; params.should_replace_current_entry = false; diff --git a/content/test/test_render_frame_host.h b/content/test/test_render_frame_host.h index 6bdbd89d7efe1..eeea620eace24 100644 --- a/content/test/test_render_frame_host.h +++ b/content/test/test_render_frame_host.h @@ -12,6 +12,7 @@ #include "content/public/browser/web_contents_observer.h" #include "content/public/common/page_transition_types.h" #include "content/public/test/test_renderer_host.h" +#include "content/test/test_render_view_host.h" struct FrameHostMsg_DidCommitProvisionalLoad_Params; @@ -42,8 +43,12 @@ class TestRenderFrameHost : public RenderFrameHostImpl, bool is_swapped_out); virtual ~TestRenderFrameHost(); + // RenderFrameHostImpl overrides (same values, but in Test* types) + virtual TestRenderViewHost* GetRenderViewHost() OVERRIDE; + // RenderFrameHostTester implementation. - virtual RenderFrameHost* AppendChild(const std::string& frame_name) OVERRIDE; + virtual TestRenderFrameHost* AppendChild( + const std::string& frame_name) OVERRIDE; virtual void SendNavigateWithTransition(int page_id, const GURL& url, PageTransition transition) OVERRIDE; diff --git a/content/test/test_render_view_host.cc b/content/test/test_render_view_host.cc index 3573a1f21a26c..06029fc125925 100644 --- a/content/test/test_render_view_host.cc +++ b/content/test/test_render_view_host.cc @@ -17,6 +17,7 @@ #include "content/public/common/content_client.h" #include "content/public/common/page_state.h" #include "content/public/common/web_preferences.h" +#include "content/test/test_render_frame_host.h" #include "content/test/test_web_contents.h" #include "media/base/video_frame.h" #include "ui/gfx/rect.h" @@ -333,7 +334,7 @@ void TestRenderViewHost::SimulateWasHidden() { } void TestRenderViewHost::SimulateWasShown() { - WasShown(); + WasShown(ui::LatencyInfo()); } void TestRenderViewHost::TestOnStartDragging( @@ -345,9 +346,9 @@ void TestRenderViewHost::TestOnStartDragging( } void TestRenderViewHost::TestOnUpdateStateWithFile( - int page_id, + int process_id, const base::FilePath& file_path) { - OnUpdateState(page_id, + OnUpdateState(process_id, PageState::CreateForTesting(GURL("http://www.google.com"), false, "data", @@ -374,11 +375,13 @@ RenderViewHostImplTestHarness::~RenderViewHostImplTestHarness() { } TestRenderViewHost* RenderViewHostImplTestHarness::test_rvh() { - return static_cast(rvh()); + return contents()->GetRenderViewHost(); } TestRenderViewHost* RenderViewHostImplTestHarness::pending_test_rvh() { - return static_cast(pending_rvh()); + return contents()->GetPendingMainFrame() ? + contents()->GetPendingMainFrame()->GetRenderViewHost() : + NULL; } TestRenderViewHost* RenderViewHostImplTestHarness::active_test_rvh() { @@ -386,7 +389,7 @@ TestRenderViewHost* RenderViewHostImplTestHarness::active_test_rvh() { } TestRenderFrameHost* RenderViewHostImplTestHarness::main_test_rfh() { - return static_cast(main_rfh()); + return contents()->GetMainFrame(); } TestWebContents* RenderViewHostImplTestHarness::contents() { diff --git a/content/test/test_render_view_host.h b/content/test/test_render_view_host.h index 3a48ec7af38df..65fb470e538ef 100644 --- a/content/test/test_render_view_host.h +++ b/content/test/test_render_view_host.h @@ -15,7 +15,6 @@ #include "content/browser/renderer_host/render_widget_host_view_base.h" #include "content/public/common/page_transition_types.h" #include "content/public/test/test_renderer_host.h" -#include "content/test/test_render_frame_host.h" #include "ui/base/ime/dummy_text_input_client.h" #include "ui/base/layout.h" #include "ui/gfx/vector2d_f.h" @@ -251,7 +250,7 @@ class TestRenderViewHost FrameHostMsg_DidCommitProvisionalLoad_Params* params); void TestOnUpdateStateWithFile( - int page_id, const base::FilePath& file_path); + int process_id, const base::FilePath& file_path); void TestOnStartDragging(const DropData& drop_data); @@ -364,11 +363,38 @@ class RenderViewHostImplTestHarness : public RenderViewHostTestHarness { RenderViewHostImplTestHarness(); virtual ~RenderViewHostImplTestHarness(); + // contents() is equivalent to static_cast(web_contents()) + TestWebContents* contents(); + + // RVH/RFH getters are shorthand for oft-used bits of web_contents(). + + // test_rvh() is equivalent to any of the following: + // contents()->GetMainFrame()->GetRenderViewHost() + // contents()->GetRenderViewHost() + // static_cast(rvh()) + // + // Since most functionality will eventually shift from RVH to RFH, you may + // prefer to use the GetMainFrame() method in tests. TestRenderViewHost* test_rvh(); + + // pending_test_rvh() is equivalent to all of the following: + // contents()->GetPendingMainFrame()->GetRenderViewHost() [if frame exists] + // contents()->GetPendingRenderViewHost() + // static_cast(pending_rvh()) + // + // Since most functionality will eventually shift from RVH to RFH, you may + // prefer to use the GetPendingMainFrame() method in tests. TestRenderViewHost* pending_test_rvh(); + + // active_test_rvh() is equivalent to: + // contents()->GetPendingRenderViewHost() ? + // contents()->GetPendingRenderViewHost() : + // contents()->GetRenderViewHost(); TestRenderViewHost* active_test_rvh(); + + // main_test_rfh() is equivalent to contents()->GetMainFrame() + // TODO(nick): Replace all uses with contents()->GetMainFrame() TestRenderFrameHost* main_test_rfh(); - TestWebContents* contents(); private: typedef scoped_ptr diff --git a/content/test/test_web_contents.cc b/content/test/test_web_contents.cc index c337e2b06ab04..9824a0c49a389 100644 --- a/content/test/test_web_contents.cc +++ b/content/test/test_web_contents.cc @@ -44,19 +44,25 @@ TestWebContents::~TestWebContents() { EXPECT_FALSE(expect_set_history_length_and_prune_); } -RenderViewHost* TestWebContents::GetPendingRenderViewHost() const { - return GetRenderManager()->pending_render_view_host(); +TestRenderFrameHost* TestWebContents::GetMainFrame() { + return static_cast(WebContentsImpl::GetMainFrame()); } -TestRenderViewHost* TestWebContents::pending_test_rvh() const { - return static_cast(GetPendingRenderViewHost()); +TestRenderViewHost* TestWebContents::GetRenderViewHost() const { + return static_cast( + WebContentsImpl::GetRenderViewHost()); } -void TestWebContents::TestDidNavigate(RenderViewHost* render_view_host, +TestRenderFrameHost* TestWebContents::GetPendingMainFrame() const { + return static_cast( + GetRenderManager()->pending_frame_host()); +} + +void TestWebContents::TestDidNavigate(RenderFrameHost* render_frame_host, int page_id, const GURL& url, PageTransition transition) { - TestDidNavigateWithReferrer(render_view_host, + TestDidNavigateWithReferrer(render_frame_host, page_id, url, Referrer(), @@ -64,7 +70,7 @@ void TestWebContents::TestDidNavigate(RenderViewHost* render_view_host, } void TestWebContents::TestDidNavigateWithReferrer( - RenderViewHost* render_view_host, + RenderFrameHost* render_frame_host, int page_id, const GURL& url, const Referrer& referrer, @@ -85,14 +91,13 @@ void TestWebContents::TestDidNavigateWithReferrer( params.is_post = false; params.page_state = PageState::CreateFromURL(url); - RenderViewHostImpl* rvh = static_cast(render_view_host); - RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromID( - rvh->GetProcess()->GetID(), rvh->main_frame_routing_id()); - frame_tree_.root()->navigator()->DidNavigate(rfh, params); + RenderFrameHostImpl* rfhi = + static_cast(render_frame_host); + rfhi->frame_tree_node()->navigator()->DidNavigate(rfhi, params); } -WebPreferences TestWebContents::TestGetWebkitPrefs() { - return GetWebkitPrefs(); +WebPreferences TestWebContents::TestComputeWebkitPrefs() { + return ComputeWebkitPrefs(); } bool TestWebContents::CreateRenderViewForRenderManager( @@ -139,33 +144,30 @@ void TestWebContents::CommitPendingNavigation() { // notifying that it has unloaded so the pending RVH is resumed and can // navigate. ProceedWithCrossSiteNavigation(); - RenderViewHost* old_rvh = GetRenderViewHost(); - TestRenderViewHost* rvh = - static_cast(GetPendingRenderViewHost()); - if (!rvh) - rvh = static_cast(old_rvh); + TestRenderFrameHost* old_rfh = GetMainFrame(); + TestRenderFrameHost* rfh = GetPendingMainFrame(); + if (!rfh) + rfh = old_rfh; const NavigationEntry* entry = GetController().GetPendingEntry(); DCHECK(entry); int page_id = entry->GetPageID(); if (page_id == -1) { // It's a new navigation, assign a never-seen page id to it. - page_id = GetMaxPageIDForSiteInstance(rvh->GetSiteInstance()) + 1; + page_id = GetMaxPageIDForSiteInstance(rfh->GetSiteInstance()) + 1; } - rvh->SendNavigate(page_id, entry->GetURL()); + rfh->SendNavigate(page_id, entry->GetURL()); // Simulate the SwapOut_ACK. This is needed when cross-site navigation happens - // (old_rvh != rvh). - if (old_rvh != rvh) - static_cast(old_rvh)->OnSwappedOut(false); + // (old_rfh != rfh). + if (old_rfh != rfh) + old_rfh->OnSwappedOut(false); } void TestWebContents::ProceedWithCrossSiteNavigation() { - if (!GetPendingRenderViewHost()) + if (!GetPendingMainFrame()) return; - TestRenderViewHost* rvh = static_cast( - GetRenderViewHost()); - rvh->SendBeforeUnloadACK(true); + GetMainFrame()->GetRenderViewHost()->SendBeforeUnloadACK(true); } RenderViewHostDelegateView* TestWebContents::GetDelegateView() { diff --git a/content/test/test_web_contents.h b/content/test/test_web_contents.h index 5061bfbf676e3..2daf7f362b0b4 100644 --- a/content/test/test_web_contents.h +++ b/content/test/test_web_contents.h @@ -9,6 +9,8 @@ #include "content/public/common/page_transition_types.h" #include "content/public/common/web_preferences.h" #include "content/public/test/web_contents_tester.h" +#include "content/test/test_render_frame_host.h" +#include "content/test/test_render_view_host.h" class SiteInstanceImpl; @@ -27,24 +29,26 @@ class TestWebContents : public WebContentsImpl, public WebContentsTester { static TestWebContents* Create(BrowserContext* browser_context, SiteInstance* instance); + // WebContentsImpl overrides (returning the same values, but in Test* types) + virtual TestRenderFrameHost* GetMainFrame() OVERRIDE; + virtual TestRenderViewHost* GetRenderViewHost() const OVERRIDE; + // WebContentsTester implementation. virtual void CommitPendingNavigation() OVERRIDE; - virtual RenderViewHost* GetPendingRenderViewHost() const OVERRIDE; + virtual TestRenderFrameHost* GetPendingMainFrame() const OVERRIDE; virtual void NavigateAndCommit(const GURL& url) OVERRIDE; virtual void TestSetIsLoading(bool value) OVERRIDE; virtual void ProceedWithCrossSiteNavigation() OVERRIDE; - virtual void TestDidNavigate(RenderViewHost* render_view_host, + virtual void TestDidNavigate(RenderFrameHost* render_frame_host, int page_id, const GURL& url, PageTransition transition) OVERRIDE; - virtual void TestDidNavigateWithReferrer(RenderViewHost* render_view_host, + virtual void TestDidNavigateWithReferrer(RenderFrameHost* render_frame_host, int page_id, const GURL& url, const Referrer& referrer, PageTransition transition) OVERRIDE; - virtual WebPreferences TestGetWebkitPrefs() OVERRIDE; - - TestRenderViewHost* pending_test_rvh() const; + virtual WebPreferences TestComputeWebkitPrefs() OVERRIDE; // State accessor. bool cross_navigation_pending() { diff --git a/content/test/webkit_support.cc b/content/test/webkit_support.cc deleted file mode 100644 index a3e48edd92cce..0000000000000 --- a/content/test/webkit_support.cc +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "content/test/webkit_support.h" - -#include - -#include "base/command_line.h" -#include "base/message_loop/message_loop.h" -#include "base/run_loop.h" -#include "base/strings/string_tokenizer.h" -#include "content/public/common/content_switches.h" -#include "content/public/common/user_agent.h" -#include "content/test/test_webkit_platform_support.h" -#include "third_party/WebKit/public/web/WebCache.h" -#include "third_party/WebKit/public/web/WebKit.h" -#include "third_party/WebKit/public/web/WebRuntimeFeatures.h" -#include "url/url_util.h" - -#if defined(OS_WIN) -#include "ui/gfx/win/dpi.h" -#endif - -#if defined(OS_ANDROID) -#include "base/android/jni_android.h" -#include "net/android/network_library.h" -#endif - -#if defined(OS_MACOSX) -#include "base/test/mock_chrome_application_mac.h" -#endif - -namespace content { - -namespace { - -void EnableBlinkPlatformLogChannels(const std::string& channels) { - if (channels.empty()) - return; - base::StringTokenizer t(channels, ", "); - while (t.GetNext()) - blink::enableLogChannel(t.token().c_str()); -} - -void ParseBlinkCommandLineArgumentsForUnitTests() { - const CommandLine& command_line = *CommandLine::ForCurrentProcess(); - EnableBlinkPlatformLogChannels( - command_line.GetSwitchValueASCII(switches::kBlinkPlatformLogChannels)); -} - -class TestEnvironment { - public: -#if defined(OS_ANDROID) - // Android UI message loop goes through Java, so don't use it in tests. - typedef base::MessageLoop MessageLoopType; -#else - typedef base::MessageLoopForUI MessageLoopType; -#endif - - TestEnvironment() { - main_message_loop_.reset(new MessageLoopType); - - // TestWebKitPlatformSupport must be instantiated after MessageLoopType. - webkit_platform_support_.reset(new TestWebKitPlatformSupport); - } - - ~TestEnvironment() { - } - - TestWebKitPlatformSupport* webkit_platform_support() const { - return webkit_platform_support_.get(); - } - - private: - scoped_ptr main_message_loop_; - scoped_ptr webkit_platform_support_; -}; - -TestEnvironment* test_environment; - -} // namespace - -void SetUpTestEnvironmentForUnitTests() { - ParseBlinkCommandLineArgumentsForUnitTests(); - - blink::WebRuntimeFeatures::enableExperimentalFeatures(true); - blink::WebRuntimeFeatures::enableTestOnlyFeatures(true); - -#if defined(OS_ANDROID) - JNIEnv* env = base::android::AttachCurrentThread(); - net::android::RegisterNetworkLibrary(env); -#endif - -#if defined(OS_MACOSX) - mock_cr_app::RegisterMockCrApp(); -#endif - -#if defined(OS_WIN) - gfx::InitDeviceScaleFactor(1.0f); -#endif - - // Explicitly initialize the GURL library before spawning any threads. - // Otherwise crash may happend when different threads try to create a GURL - // at same time. - url::Initialize(); - test_environment = new TestEnvironment; -} - -void TearDownTestEnvironment() { - // Flush any remaining messages before we kill ourselves. - // http://code.google.com/p/chromium/issues/detail?id=9500 - base::RunLoop().RunUntilIdle(); - - if (RunningOnValgrind()) - blink::WebCache::clear(); - delete test_environment; - test_environment = NULL; -} - -} // namespace content diff --git a/content/test/webkit_support.h b/content/test/webkit_support.h deleted file mode 100644 index c4f97cf570b77..0000000000000 --- a/content/test/webkit_support.h +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CONTENT_TEST_WEBKIT_SUPPORT_H_ -#define CONTENT_TEST_WEBKIT_SUPPORT_H_ - -// This package provides functions used by webkit_unit_tests. -namespace content { - -// Initializes or terminates a test environment for unit tests. -void SetUpTestEnvironmentForUnitTests(); -void TearDownTestEnvironment(); - -} // namespace content - -#endif // CONTENT_TEST_WEBKIT_SUPPORT_H_ diff --git a/crypto/openssl_util.cc b/crypto/openssl_util.cc index 6f348ff1a54c0..79944891e2fbd 100644 --- a/crypto/openssl_util.cc +++ b/crypto/openssl_util.cc @@ -17,6 +17,7 @@ #if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL) #include +#include "base/cpu.h" #endif namespace crypto { @@ -62,10 +63,9 @@ class OpenSSLInitSingleton { (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; if (has_neon) CRYPTO_set_NEON_capable(1); - // In all cases, currently, mark the NEON unit as broken because some - // phones can't execute the Poly1305 code correctly. See - // https://code.google.com/p/chromium/issues/detail?id=341598 - CRYPTO_set_NEON_functional(0); + // See https://code.google.com/p/chromium/issues/detail?id=341598 + base::CPU cpu; + CRYPTO_set_NEON_functional(!cpu.has_broken_neon()); #endif } diff --git a/dbus/object_proxy.cc b/dbus/object_proxy.cc index 5167cab76035e..d0b660b600dbc 100644 --- a/dbus/object_proxy.cc +++ b/dbus/object_proxy.cc @@ -24,6 +24,7 @@ namespace dbus { namespace { const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown"; +const char kErrorObjectUnknown[] = "org.freedesktop.DBus.Error.UnknownObject"; // Used for success ratio histograms. 1 for success, 0 for failure. const int kSuccessRatioHistogramMaxValue = 2; @@ -561,12 +562,20 @@ void ObjectProxy::LogMethodCallFailure( const base::StringPiece& method_name, const base::StringPiece& error_name, const base::StringPiece& error_message) const { - if (ignore_service_unknown_errors_ && error_name == kErrorServiceUnknown) + if (ignore_service_unknown_errors_ && + (error_name == kErrorServiceUnknown || error_name == kErrorObjectUnknown)) return; - LOG(ERROR) << "Failed to call method: " - << interface_name << "." << method_name - << ": object_path= " << object_path_.value() - << ": " << error_name << ": " << error_message; + logging::LogSeverity severity = logging::LOG_ERROR; + // "UnknownObject" indicates that an object or service is no longer available, + // e.g. a Shill network service has gone out of range. Treat these as warnings + // not errors. + if (error_name == kErrorObjectUnknown) + severity = logging::LOG_WARNING; + std::ostringstream msg; + msg << "Failed to call method: " << interface_name << "." << method_name + << ": object_path= " << object_path_.value() + << ": " << error_name << ": " << error_message; + logging::LogAtLevel(severity, msg.str()); } void ObjectProxy::OnCallMethodError(const std::string& interface_name, diff --git a/dbus/object_proxy.h b/dbus/object_proxy.h index 48c9c03208ab9..2d4ea68542c00 100644 --- a/dbus/object_proxy.h +++ b/dbus/object_proxy.h @@ -46,7 +46,8 @@ class CHROME_DBUS_EXPORT ObjectProxy // Options to be OR-ed together when calling Bus::GetObjectProxyWithOptions(). // Set the IGNORE_SERVICE_UNKNOWN_ERRORS option to silence logging of - // org.freedesktop.DBus.Error.ServiceUnknown errors. + // org.freedesktop.DBus.Error.ServiceUnknown errors and + // org.freedesktop.DBus.Error.ObjectUnknown errors. enum Options { DEFAULT_OPTIONS = 0, IGNORE_SERVICE_UNKNOWN_ERRORS = 1 << 0 diff --git a/dbus/property.cc b/dbus/property.cc index 9475a0728a6cc..89cb9574e70d7 100644 --- a/dbus/property.cc +++ b/dbus/property.cc @@ -89,6 +89,11 @@ void PropertySet::ChangedConnected(const std::string& interface_name, bool success) { LOG_IF(WARNING, !success) << "Failed to connect to " << signal_name << "signal."; + + // This is a simple workaround for crbug.com/407109, which causes signals to + // be missed if they are received before a match rule is added for them. This + // is a branch-only workaround that is only present in 2125 (38). + GetAll(); } diff --git a/dbus/property_unittest.cc b/dbus/property_unittest.cc index e078c0044d1f9..3e851264f4a88 100644 --- a/dbus/property_unittest.cc +++ b/dbus/property_unittest.cc @@ -82,8 +82,9 @@ class PropertyTest : public testing::Test { object_proxy_, base::Bind(&PropertyTest::OnPropertyChanged, base::Unretained(this)))); + + // GetAll is called once the signals are connected. properties_->ConnectSignals(); - properties_->GetAll(); } virtual void TearDown() { diff --git a/device/bluetooth/bluetooth_chromeos_unittest.cc b/device/bluetooth/bluetooth_chromeos_unittest.cc index 379d1dfd25fc6..87b454f17a2c6 100644 --- a/device/bluetooth/bluetooth_chromeos_unittest.cc +++ b/device/bluetooth/bluetooth_chromeos_unittest.cc @@ -235,6 +235,10 @@ class BluetoothChromeOSTest : public testing::Test { public: virtual void SetUp() { FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager; + // We need to initialize DBusThreadManager early to prevent + // Bluetooth*::Create() methods from picking the real instead of fake + // implementations. + DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); fake_bluetooth_adapter_client_ = new FakeBluetoothAdapterClient; fake_dbus_thread_manager->SetBluetoothAdapterClient( scoped_ptr(fake_bluetooth_adapter_client_)); @@ -249,7 +253,6 @@ class BluetoothChromeOSTest : public testing::Test { fake_dbus_thread_manager->SetBluetoothGattServiceClient( scoped_ptr( new FakeBluetoothGattServiceClient)); - DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager); fake_bluetooth_adapter_client_->SetSimulationIntervalMs(10); diff --git a/device/bluetooth/bluetooth_device_chromeos.cc b/device/bluetooth/bluetooth_device_chromeos.cc index 0f1f273cb54b9..0fe723ea51a03 100644 --- a/device/bluetooth/bluetooth_device_chromeos.cc +++ b/device/bluetooth/bluetooth_device_chromeos.cc @@ -432,7 +432,21 @@ void BluetoothDeviceChromeOS::ConnectToService( scoped_refptr socket = BluetoothSocketChromeOS::CreateBluetoothSocket( ui_task_runner_, socket_thread_); - socket->Connect(this, uuid, base::Bind(callback, socket), error_callback); + socket->Connect(this, uuid, BluetoothSocketChromeOS::SECURITY_LEVEL_MEDIUM, + base::Bind(callback, socket), error_callback); +} + +void BluetoothDeviceChromeOS::ConnectToServiceInsecurely( + const BluetoothUUID& uuid, + const ConnectToServiceCallback& callback, + const ConnectToServiceErrorCallback& error_callback) { + VLOG(1) << object_path_.value() << ": Connecting insecurely to service: " + << uuid.canonical_value(); + scoped_refptr socket = + BluetoothSocketChromeOS::CreateBluetoothSocket( + ui_task_runner_, socket_thread_); + socket->Connect(this, uuid, BluetoothSocketChromeOS::SECURITY_LEVEL_LOW, + base::Bind(callback, socket), error_callback); } void BluetoothDeviceChromeOS::CreateGattConnection( diff --git a/device/bluetooth/bluetooth_device_chromeos.h b/device/bluetooth/bluetooth_device_chromeos.h index 6f335603cd630..85ed0709a39d4 100644 --- a/device/bluetooth/bluetooth_device_chromeos.h +++ b/device/bluetooth/bluetooth_device_chromeos.h @@ -73,6 +73,19 @@ class BluetoothDeviceChromeOS const base::Closure& callback, const ErrorCallback& error_callback) OVERRIDE; + // Attempts to initiate an insecure outgoing L2CAP or RFCOMM connection to the + // advertised service on this device matching |uuid|, performing an SDP lookup + // if necessary to determine the correct protocol and channel for the + // connection. Unlike ConnectToService, the outgoing connection will request + // no bonding rather than general bonding. |callback| will be called on a + // successful connection with a BluetoothSocket instance that is to be owned + // by the receiver. |error_callback| will be called on failure with a message + // indicating the cause. + void ConnectToServiceInsecurely( + const device::BluetoothUUID& uuid, + const ConnectToServiceCallback& callback, + const ConnectToServiceErrorCallback& error_callback); + // Creates a pairing object with the given delegate |pairing_delegate| and // establishes it as the pairing context for this device. All pairing-related // method calls will be forwarded to this object until it is released. diff --git a/device/bluetooth/bluetooth_socket_chromeos.cc b/device/bluetooth/bluetooth_socket_chromeos.cc index e7fb6bdfb6476..5eedf5b1c5a27 100644 --- a/device/bluetooth/bluetooth_socket_chromeos.cc +++ b/device/bluetooth/bluetooth_socket_chromeos.cc @@ -92,6 +92,7 @@ BluetoothSocketChromeOS::~BluetoothSocketChromeOS() { void BluetoothSocketChromeOS::Connect( const BluetoothDeviceChromeOS* device, const BluetoothUUID& uuid, + SecurityLevel security_level, const base::Closure& success_callback, const ErrorCompletionCallback& error_callback) { DCHECK(ui_task_runner()->RunsTasksOnCurrentThread()); @@ -107,6 +108,8 @@ void BluetoothSocketChromeOS::Connect( device_path_ = device->object_path(); uuid_ = uuid; options_.reset(new BluetoothProfileManagerClient::Options()); + if (security_level == SECURITY_LEVEL_LOW) + options_->require_authentication.reset(new bool(false)); RegisterProfile(success_callback, error_callback); } @@ -157,6 +160,15 @@ void BluetoothSocketChromeOS::Close() { if (profile_) UnregisterProfile(); + // In the case below, where an asynchronous task gets posted on the socket + // thread in BluetoothSocketNet::Close, a reference will be held to this + // socket by the callback. This may cause the BluetoothAdapter to outlive + // DBusThreadManager during shutdown if that callback executes too late. + if (adapter_.get()) { + adapter_->RemoveObserver(this); + adapter_ = NULL; + } + if (!device_path_.value().empty()) { BluetoothSocketNet::Close(); } else { diff --git a/device/bluetooth/bluetooth_socket_chromeos.h b/device/bluetooth/bluetooth_socket_chromeos.h index 949abf6603869..ff8ee7703ddbe 100644 --- a/device/bluetooth/bluetooth_socket_chromeos.h +++ b/device/bluetooth/bluetooth_socket_chromeos.h @@ -34,6 +34,11 @@ class CHROMEOS_EXPORT BluetoothSocketChromeOS public device::BluetoothAdapter::Observer, public BluetoothProfileServiceProvider::Delegate { public: + enum SecurityLevel { + SECURITY_LEVEL_LOW, + SECURITY_LEVEL_MEDIUM + }; + static scoped_refptr CreateBluetoothSocket( scoped_refptr ui_task_runner, scoped_refptr socket_thread); @@ -45,6 +50,7 @@ class CHROMEOS_EXPORT BluetoothSocketChromeOS // with a message explaining the cause of the failure. virtual void Connect(const BluetoothDeviceChromeOS* device, const device::BluetoothUUID& uuid, + SecurityLevel security_level, const base::Closure& success_callback, const ErrorCompletionCallback& error_callback); diff --git a/device/device_tests.gyp b/device/device_tests.gyp index b5303c837c3e8..09be87e3d7610 100644 --- a/device/device_tests.gyp +++ b/device/device_tests.gyp @@ -47,6 +47,7 @@ 'hid/hid_report_descriptor_unittest.cc', 'hid/hid_service_unittest.cc', 'hid/input_service_linux_unittest.cc', + 'serial/data_source_unittest.cc', 'serial/serial_connection_unittest.cc', 'serial/serial_service_unittest.cc', 'test/run_all_unittests.cc', diff --git a/device/hid/hid_connection.cc b/device/hid/hid_connection.cc index 04fe32947c398..c916d6aae7237 100644 --- a/device/hid/hid_connection.cc +++ b/device/hid/hid_connection.cc @@ -157,12 +157,15 @@ void HidConnection::SendFeatureReport( } bool HidConnection::CompleteRead(scoped_refptr buffer, + int bytes_read, const IOCallback& callback) { - if (buffer->size() == 0 || IsReportIdProtected(buffer->data()[0])) { + DCHECK_LE(bytes_read, buffer->size()); + + if (bytes_read == 0 || IsReportIdProtected(buffer->data()[0])) { return false; } - callback.Run(true, buffer->size()); + callback.Run(true, bytes_read); return true; } diff --git a/device/hid/hid_connection.h b/device/hid/hid_connection.h index bf29985aad85b..631db1bebe16e 100644 --- a/device/hid/hid_connection.h +++ b/device/hid/hid_connection.h @@ -66,6 +66,7 @@ class HidConnection : public base::RefCountedThreadSafe { // and this method returns false. Otherwise it runs the callback // and returns true. bool CompleteRead(scoped_refptr buffer, + int bytes_read, const IOCallback& callback); private: diff --git a/device/hid/hid_connection_linux.cc b/device/hid/hid_connection_linux.cc index 3c2c1da43b46e..5d6dd248b7b60 100644 --- a/device/hid/hid_connection_linux.cc +++ b/device/hid/hid_connection_linux.cc @@ -224,7 +224,7 @@ void HidConnectionLinux::ProcessReadQueue() { memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); pending_reports_.pop(); - if (CompleteRead(report.buffer, read.callback)) { + if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) { pending_reads_.pop(); } } diff --git a/device/hid/hid_connection_mac.cc b/device/hid/hid_connection_mac.cc index d497ac0140283..3df6e6064d804 100644 --- a/device/hid/hid_connection_mac.cc +++ b/device/hid/hid_connection_mac.cc @@ -17,21 +17,28 @@ HidConnectionMac::HidConnectionMac(HidDeviceInfo device_info) message_loop_ = base::MessageLoopProxy::current(); DCHECK(device_.get()); + size_t expected_report_size = device_info.max_input_report_size; if (device_info.has_report_id) { expected_report_size++; } - inbound_buffer_.reset((uint8_t*)malloc(expected_report_size)); - IOHIDDeviceRegisterInputReportCallback(device_.get(), - inbound_buffer_.get(), - expected_report_size, - &HidConnectionMac::InputReportCallback, - this); - IOHIDDeviceOpen(device_, kIOHIDOptionsTypeNone); + inbound_buffer_.resize(expected_report_size); + if (inbound_buffer_.size() > 0) { + IOHIDDeviceRegisterInputReportCallback( + device_.get(), + &inbound_buffer_[0], + inbound_buffer_.size(), + &HidConnectionMac::InputReportCallback, + this); + } } HidConnectionMac::~HidConnectionMac() { - IOHIDDeviceClose(device_, kIOHIDOptionsTypeNone); + if (inbound_buffer_.size() > 0) { + // Unregister the input report callback before this object is freed. + IOHIDDeviceRegisterInputReportCallback( + device_.get(), &inbound_buffer_[0], inbound_buffer_.size(), NULL, this); + } Flush(); } @@ -74,8 +81,10 @@ void HidConnectionMac::PlatformGetFeatureReport( &report_size); if (result == kIOReturnSuccess) callback.Run(true, report_size); - else + else { + VLOG(1) << "Failed to get feature report: " << result; callback.Run(false, 0); + } } void HidConnectionMac::PlatformSendFeatureReport( @@ -92,6 +101,11 @@ void HidConnectionMac::InputReportCallback(void* context, uint32_t report_id, uint8_t* report_bytes, CFIndex report_length) { + if (result != kIOReturnSuccess) { + VLOG(1) << "Failed to read input report: " << result; + return; + } + HidConnectionMac* connection = static_cast(context); // report_id is already contained in report_bytes scoped_refptr buffer; @@ -130,6 +144,7 @@ void HidConnectionMac::WriteReport(IOHIDReportType type, if (res != kIOReturnSuccess) { callback.Run(false, 0); } else { + VLOG(1) << "Failed to set report: " << res; callback.Run(true, output_buffer->size()); } } @@ -163,7 +178,7 @@ void HidConnectionMac::ProcessReadQueue() { memcpy(read.buffer->data(), report.buffer->data(), report.buffer->size()); pending_reports_.pop(); - if (CompleteRead(report.buffer, read.callback)) { + if (CompleteRead(read.buffer, report.buffer->size(), read.callback)) { pending_reads_.pop(); } } diff --git a/device/hid/hid_connection_mac.h b/device/hid/hid_connection_mac.h index 02dde044d66e5..33deadfe1ef44 100644 --- a/device/hid/hid_connection_mac.h +++ b/device/hid/hid_connection_mac.h @@ -65,7 +65,7 @@ class HidConnectionMac : public HidConnection { base::ScopedCFTypeRef device_; scoped_refptr message_loop_; - scoped_ptr inbound_buffer_; + std::vector inbound_buffer_; std::queue pending_reports_; std::queue pending_reads_; diff --git a/device/hid/hid_connection_win.cc b/device/hid/hid_connection_win.cc index ccdb224bbf995..dfebcba971c54 100644 --- a/device/hid/hid_connection_win.cc +++ b/device/hid/hid_connection_win.cc @@ -124,12 +124,10 @@ HidConnectionWin::~HidConnectionWin() { void HidConnectionWin::PlatformRead(scoped_refptr buffer, const HidConnection::IOCallback& callback) { - int expected_report_size = device_info().max_input_report_size; - if (device_info().has_report_id) { - expected_report_size++; - } + // Windows will always include the report ID (including zero if report IDs + // are not in use) in the buffer. scoped_refptr receive_buffer = - new net::IOBufferWithSize(expected_report_size); + new net::IOBufferWithSize(device_info().max_input_report_size + 1); scoped_refptr transfer( new PendingHidTransfer(this, buffer, receive_buffer, callback)); @@ -148,8 +146,8 @@ void HidConnectionWin::PlatformWrite( const HidConnection::IOCallback& callback) { // The Windows API always wants either a report ID (if supported) or // zero at the front of every output report. - scoped_refptr output_buffer(buffer); - output_buffer = new net::IOBufferWithSize(buffer->size() + 1); + scoped_refptr output_buffer( + new net::IOBufferWithSize(buffer->size() + 1)); output_buffer->data()[0] = report_id; memcpy(output_buffer->data() + 1, buffer->data(), buffer->size()); @@ -252,8 +250,10 @@ void HidConnectionWin::OnTransferFinished( } } - CompleteRead(transfer->target_buffer_, transfer->callback_); + CompleteRead( + transfer->target_buffer_, bytes_transferred, transfer->callback_); } else { + VPLOG(1) << "HID transfer failed"; transfer->callback_.Run(false, 0); } } diff --git a/device/hid/hid_service_win.cc b/device/hid/hid_service_win.cc index 130b3cb801bbe..23afa4f40affe 100644 --- a/device/hid/hid_service_win.cc +++ b/device/hid/hid_service_win.cc @@ -251,13 +251,16 @@ void HidServiceWin::PlatformAddDevice(const std::string& device_path) { } device_info.collections.push_back(collection_info); } - if (device_info.has_report_id && device_info.max_input_report_size > 0) { + // Whether or not the device includes report IDs in its reports the size + // of the report ID is included in the value provided by Windows. This + // appears contrary to the MSDN documentation. + if (device_info.max_input_report_size > 0) { device_info.max_input_report_size--; } - if (device_info.has_report_id && device_info.max_output_report_size > 0) { + if (device_info.max_output_report_size > 0) { device_info.max_output_report_size--; } - if (device_info.has_report_id && device_info.max_feature_report_size > 0) { + if (device_info.max_feature_report_size > 0) { device_info.max_feature_report_size--; } HidD_FreePreparsedData(preparsed_data); diff --git a/device/serial/BUILD.gn b/device/serial/BUILD.gn index daab723f15834..58cc637db1e5b 100644 --- a/device/serial/BUILD.gn +++ b/device/serial/BUILD.gn @@ -9,8 +9,14 @@ static_library("serial") { output_name = "device_serial" sources = [ + "async_waiter.cc", + "async_waiter.h", "buffer.cc", "buffer.h", + "data_receiver.cc", + "data_receiver.h", + "data_source_sender.cc", + "data_source_sender.h", "serial_connection.cc", "serial_connection.h", "serial_connection_factory.cc", @@ -64,6 +70,7 @@ mojom("serial_mojo") { ] sources = [ + "data_stream.mojom", "serial.mojom", ] } diff --git a/device/serial/async_waiter.cc b/device/serial/async_waiter.cc new file mode 100644 index 0000000000000..eb533fd2cecd5 --- /dev/null +++ b/device/serial/async_waiter.cc @@ -0,0 +1,37 @@ +// 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 "device/serial/async_waiter.h" + +namespace device { + +AsyncWaiter::AsyncWaiter(mojo::Handle handle, + MojoHandleSignals signals, + const Callback& callback) + : waiter_(mojo::Environment::GetDefaultAsyncWaiter()), + id_(0), + callback_(callback) { + id_ = waiter_->AsyncWait(handle.value(), + signals, + MOJO_DEADLINE_INDEFINITE, + &AsyncWaiter::WaitComplete, + this); +} + +AsyncWaiter::~AsyncWaiter() { + if (id_) + waiter_->CancelWait(id_); +} + +// static +void AsyncWaiter::WaitComplete(void* waiter, MojoResult result) { + static_cast(waiter)->WaitCompleteInternal(result); +} + +void AsyncWaiter::WaitCompleteInternal(MojoResult result) { + id_ = 0; + callback_.Run(result); +} + +} // namespace device diff --git a/device/serial/async_waiter.h b/device/serial/async_waiter.h new file mode 100644 index 0000000000000..f39bf32a60bdb --- /dev/null +++ b/device/serial/async_waiter.h @@ -0,0 +1,40 @@ +// 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 DEVICE_SERIAL_ASYNC_WAITER_H_ +#define DEVICE_SERIAL_ASYNC_WAITER_H_ + +#include "base/callback.h" +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/handle.h" + +namespace device { + +// A class that waits until a handle is ready and calls |callback| with the +// result. If the AsyncWaiter is deleted before the handle is ready, the wait is +// cancelled and the callback will not be called. +class AsyncWaiter { + public: + typedef base::Callback Callback; + + AsyncWaiter(mojo::Handle handle, + MojoHandleSignals signals, + const Callback& callback); + ~AsyncWaiter(); + + private: + static void WaitComplete(void* waiter, MojoResult result); + void WaitCompleteInternal(MojoResult result); + + const MojoAsyncWaiter* waiter_; + MojoAsyncWaitID id_; + const Callback callback_; + + DISALLOW_COPY_AND_ASSIGN(AsyncWaiter); +}; + +} // namespace device + +#endif // DEVICE_SERIAL_ASYNC_WAITER_H_ diff --git a/device/serial/data_receiver.cc b/device/serial/data_receiver.cc new file mode 100644 index 0000000000000..521143797216b --- /dev/null +++ b/device/serial/data_receiver.cc @@ -0,0 +1,340 @@ +// 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 "device/serial/data_receiver.h" + +#include + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "device/serial/async_waiter.h" + +namespace device { + +// Represents a receive that is not yet fulfilled. +class DataReceiver::PendingReceive { + public: + PendingReceive(DataReceiver* receiver, + const ReceiveDataCallback& callback, + const ReceiveErrorCallback& error_callback, + int32_t fatal_error_value); + + // Dispatches |data| to |receive_callback_|. + void DispatchData(const void* data, uint32_t num_bytes); + + // Reports |error| to |receive_error_callback_| if it is an appropriate time. + // Returns whether it dispatched |error|. + bool DispatchError(DataReceiver::PendingError* error, + uint32_t bytes_received); + + // Reports |fatal_error_value_| to |receive_error_callback_|. + void DispatchFatalError(); + + private: + class Buffer; + + // Invoked when the user is finished with the ReadOnlyBuffer provided to + // |receive_callback_|. + void Done(uint32_t num_bytes); + + // The DataReceiver that owns this. + DataReceiver* receiver_; + + // The callback to dispatch data. + ReceiveDataCallback receive_callback_; + + // The callback to report errors. + ReceiveErrorCallback receive_error_callback_; + + // The error value to report when DispatchFatalError() is called. + const int32_t fatal_error_value_; + + // True if the user owns a buffer passed to |receive_callback_| as part of + // DispatchData(). + bool buffer_in_use_; +}; + +// A ReadOnlyBuffer implementation that provides a view of a data pipe owned by +// a DataReceiver. +class DataReceiver::PendingReceive::Buffer : public ReadOnlyBuffer { + public: + Buffer(scoped_refptr pipe, + PendingReceive* receive, + const char* buffer, + uint32_t buffer_size); + virtual ~Buffer(); + + // ReadOnlyBuffer overrides. + virtual const char* GetData() OVERRIDE; + virtual uint32_t GetSize() OVERRIDE; + virtual void Done(uint32_t bytes_consumed) OVERRIDE; + virtual void DoneWithError(uint32_t bytes_consumed, int32_t error) OVERRIDE; + + private: + // The DataReceiver whose data pipe we are providing a view. + scoped_refptr receiver_; + + // The PendingReceive to which this buffer has been created in response. + PendingReceive* pending_receive_; + + const char* buffer_; + uint32_t buffer_size_; +}; + +// Represents an error received from the DataSource. +struct DataReceiver::PendingError { + PendingError(uint32_t offset, int32_t error) + : offset(offset), error(error), dispatched(false) {} + + // The location within the data stream where the error occurred. + const uint32_t offset; + + // The value of the error that occurred. + const int32_t error; + + // Whether the error has been dispatched to the user. + bool dispatched; +}; + +DataReceiver::DataReceiver(mojo::InterfacePtr source, + uint32_t buffer_size, + int32_t fatal_error_value) + : source_(source.Pass()), + fatal_error_value_(fatal_error_value), + bytes_received_(0), + shut_down_(false), + weak_factory_(this) { + MojoCreateDataPipeOptions options = { + sizeof(options), MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, 1, buffer_size, + }; + mojo::ScopedDataPipeProducerHandle remote_handle; + MojoResult result = mojo::CreateDataPipe(&options, &remote_handle, &handle_); + DCHECK_EQ(MOJO_RESULT_OK, result); + source_->Init(remote_handle.Pass()); + source_.set_client(this); +} + +bool DataReceiver::Receive(const ReceiveDataCallback& callback, + const ReceiveErrorCallback& error_callback) { + DCHECK(!callback.is_null() && !error_callback.is_null()); + if (pending_receive_ || shut_down_) + return false; + // When the DataSource encounters an error, it pauses transmission. When the + // user starts a new receive following notification of the error (via + // |error_callback| of the previous Receive call) of the error we can tell the + // DataSource to resume transmission of data. + if (pending_error_ && pending_error_->dispatched) { + source_->Resume(); + pending_error_.reset(); + } + + pending_receive_.reset( + new PendingReceive(this, callback, error_callback, fatal_error_value_)); + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DataReceiver::ReceiveInternal, weak_factory_.GetWeakPtr())); + return true; +} + +DataReceiver::~DataReceiver() { + ShutDown(); +} + +void DataReceiver::OnError(uint32_t offset, int32_t error) { + if (shut_down_) + return; + + if (pending_error_) { + // When OnError is called by the DataSource, transmission of data is + // suspended. Thus we shouldn't receive another call to OnError until we + // have fully dealt with the error and called Resume to resume transmission + // (see Receive()). Under normal operation we should never get here, but if + // we do (e.g. in the case of a hijacked service process) just shut down. + ShutDown(); + return; + } + pending_error_.reset(new PendingError(offset, error)); + if (pending_receive_ && + pending_receive_->DispatchError(pending_error_.get(), bytes_received_)) { + pending_receive_.reset(); + waiter_.reset(); + } +} + +void DataReceiver::OnConnectionError() { + ShutDown(); +} + +void DataReceiver::Done(uint32_t bytes_consumed) { + if (shut_down_) + return; + + DCHECK(pending_receive_); + MojoResult result = mojo::EndReadDataRaw(handle_.get(), bytes_consumed); + DCHECK_EQ(MOJO_RESULT_OK, result); + pending_receive_.reset(); + bytes_received_ += bytes_consumed; +} + +void DataReceiver::OnDoneWaiting(MojoResult result) { + DCHECK(pending_receive_ && !shut_down_ && waiter_); + waiter_.reset(); + if (result != MOJO_RESULT_OK) { + ShutDown(); + return; + } + ReceiveInternal(); +} + +void DataReceiver::ReceiveInternal() { + if (shut_down_) + return; + DCHECK(pending_receive_); + if (pending_error_ && + pending_receive_->DispatchError(pending_error_.get(), bytes_received_)) { + pending_receive_.reset(); + waiter_.reset(); + return; + } + + const void* data; + uint32_t num_bytes = std::numeric_limits::max(); + MojoResult result = mojo::BeginReadDataRaw( + handle_.get(), &data, &num_bytes, MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_OK) { + if (!CheckErrorNotInReadRange(num_bytes)) { + ShutDown(); + return; + } + + pending_receive_->DispatchData(data, num_bytes); + return; + } + if (result == MOJO_RESULT_SHOULD_WAIT) { + waiter_.reset(new AsyncWaiter( + handle_.get(), + MOJO_HANDLE_SIGNAL_READABLE, + base::Bind(&DataReceiver::OnDoneWaiting, weak_factory_.GetWeakPtr()))); + return; + } + ShutDown(); +} + +bool DataReceiver::CheckErrorNotInReadRange(uint32_t num_bytes) { + DCHECK(pending_receive_); + if (!pending_error_) + return true; + + DCHECK_NE(bytes_received_, pending_error_->offset); + DCHECK_NE(num_bytes, 0u); + uint32_t potential_bytes_received = bytes_received_ + num_bytes; + // bytes_received_ can overflow so we must consider two cases: + // 1. Both |bytes_received_| and |pending_error_->offset| have overflowed an + // equal number of times. In this case, |potential_bytes_received| must + // be in the range (|bytes_received|, |pending_error_->offset|]. Below + // this range can only occur if |bytes_received_| overflows before + // |pending_error_->offset|. Above can only occur if |bytes_received_| + // overtakes |pending_error_->offset|. + // 2. |pending_error_->offset| has overflowed once more than + // |bytes_received_|. In this case, |potential_bytes_received| must not + // be in the range (|pending_error_->offset|, |bytes_received_|]. + if ((bytes_received_ < pending_error_->offset && + (potential_bytes_received > pending_error_->offset || + potential_bytes_received <= bytes_received_)) || + (bytes_received_ > pending_error_->offset && + potential_bytes_received > pending_error_->offset && + potential_bytes_received <= bytes_received_)) { + return false; + } + return true; +} + +void DataReceiver::ShutDown() { + shut_down_ = true; + if (pending_receive_) + pending_receive_->DispatchFatalError(); + pending_error_.reset(); + waiter_.reset(); +} + +DataReceiver::PendingReceive::PendingReceive( + DataReceiver* receiver, + const ReceiveDataCallback& callback, + const ReceiveErrorCallback& error_callback, + int32_t fatal_error_value) + : receiver_(receiver), + receive_callback_(callback), + receive_error_callback_(error_callback), + fatal_error_value_(fatal_error_value), + buffer_in_use_(false) { +} + +void DataReceiver::PendingReceive::DispatchData(const void* data, + uint32_t num_bytes) { + DCHECK(!buffer_in_use_); + buffer_in_use_ = true; + receive_callback_.Run(scoped_ptr( + new Buffer(receiver_, this, static_cast(data), num_bytes))); +} + +bool DataReceiver::PendingReceive::DispatchError(PendingError* error, + uint32_t bytes_received) { + DCHECK(!error->dispatched); + if (buffer_in_use_ || bytes_received != error->offset) + return false; + + error->dispatched = true; + receive_error_callback_.Run(error->error); + return true; +} + +void DataReceiver::PendingReceive::DispatchFatalError() { + receive_error_callback_.Run(fatal_error_value_); +} + +void DataReceiver::PendingReceive::Done(uint32_t bytes_consumed) { + DCHECK(buffer_in_use_); + buffer_in_use_ = false; + receiver_->Done(bytes_consumed); +} + +DataReceiver::PendingReceive::Buffer::Buffer( + scoped_refptr receiver, + PendingReceive* receive, + const char* buffer, + uint32_t buffer_size) + : receiver_(receiver), + pending_receive_(receive), + buffer_(buffer), + buffer_size_(buffer_size) { +} + +DataReceiver::PendingReceive::Buffer::~Buffer() { + if (pending_receive_) + pending_receive_->Done(0); +} + +const char* DataReceiver::PendingReceive::Buffer::GetData() { + return buffer_; +} + +uint32_t DataReceiver::PendingReceive::Buffer::GetSize() { + return buffer_size_; +} + +void DataReceiver::PendingReceive::Buffer::Done(uint32_t bytes_consumed) { + pending_receive_->Done(bytes_consumed); + pending_receive_ = NULL; + receiver_ = NULL; + buffer_ = NULL; + buffer_size_ = 0; +} + +void DataReceiver::PendingReceive::Buffer::DoneWithError( + uint32_t bytes_consumed, + int32_t error) { + Done(bytes_consumed); +} + +} // namespace device diff --git a/device/serial/data_receiver.h b/device/serial/data_receiver.h new file mode 100644 index 0000000000000..7e878c3d3e830 --- /dev/null +++ b/device/serial/data_receiver.h @@ -0,0 +1,112 @@ +// 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 DEVICE_SERIAL_DATA_RECEIVER_H_ +#define DEVICE_SERIAL_DATA_RECEIVER_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" +#include "device/serial/buffer.h" +#include "device/serial/data_stream.mojom.h" +#include "mojo/public/cpp/system/data_pipe.h" + +namespace device { + +class AsyncWaiter; + +// A DataReceiver receives data from a DataSource. +class DataReceiver : public base::RefCounted, + public serial::DataSourceClient, + public mojo::ErrorHandler { + public: + typedef base::Callback)> ReceiveDataCallback; + typedef base::Callback ReceiveErrorCallback; + + // Constructs a DataReceiver to receive data from |source|, using a data + // pipe with a buffer size of |buffer_size|, with connection errors reported + // as |fatal_error_value|. + DataReceiver(mojo::InterfacePtr source, + uint32_t buffer_size, + int32_t fatal_error_value); + + // Begins a receive operation. If this returns true, exactly one of |callback| + // and |error_callback| will eventually be called. If there is already a + // receive in progress or there has been a connection error, this will have no + // effect and return false. + bool Receive(const ReceiveDataCallback& callback, + const ReceiveErrorCallback& error_callback); + + private: + class PendingReceive; + struct PendingError; + friend class base::RefCounted; + + virtual ~DataReceiver(); + + // serial::DataSourceClient override. Invoked by the DataSource to report + // errors. + virtual void OnError(uint32_t bytes_since_last_error, int32_t error) OVERRIDE; + + // mojo::ErrorHandler override. Calls ShutDown(). + virtual void OnConnectionError() OVERRIDE; + + // Invoked by the PendingReceive to report that the user is done with the + // receive buffer, having read |bytes_read| bytes from it. + void Done(uint32_t bytes_read); + + // Invoked when |handle_| is ready to read. Unless an error has occurred, this + // calls ReceiveInternal(). + void OnDoneWaiting(MojoResult result); + + // The implementation of Receive(). If a |pending_error_| is ready to + // dispatch, it does so. Otherwise, this attempts to read from |handle_| and + // dispatches the contents to |pending_receive_|. If |handle_| is not ready, + // |waiter_| is used to wait until it is ready. If an error occurs in reading + // |handle_|, ShutDown() is called. + void ReceiveInternal(); + + // We may have been notified of an error that occurred at some future point in + // the stream. We should never be able to read past the point at which the + // error occurred until we have dealt with the error and called Resume() on + // the DataSource. If this has occurred, something bad has happened on the + // service side, so we shut down. + bool CheckErrorNotInReadRange(uint32_t num_bytes); + + // Called when we encounter a fatal error. If a receive is in progress, + // |fatal_error_value_| will be reported to the user. + void ShutDown(); + + // The control connection to the data source. + mojo::InterfacePtr source_; + + // The data connection to the data source. + mojo::ScopedDataPipeConsumerHandle handle_; + + // The error value to report in the event of a fatal error. + const int32_t fatal_error_value_; + + // The number of bytes received from the data source. + uint32_t bytes_received_; + + // Whether we have encountered a fatal error and shut down. + bool shut_down_; + + // A waiter used to wait until |handle_| is readable if we are waiting. + scoped_ptr waiter_; + + // The current pending receive operation if there is one. + scoped_ptr pending_receive_; + + // The current pending error if there is one. + scoped_ptr pending_error_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(DataReceiver); +}; + +} // namespace device + +#endif // DEVICE_SERIAL_DATA_RECEIVER_H_ diff --git a/device/serial/data_source_sender.cc b/device/serial/data_source_sender.cc new file mode 100644 index 0000000000000..876a5e5557fff --- /dev/null +++ b/device/serial/data_source_sender.cc @@ -0,0 +1,249 @@ +// 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 "device/serial/data_source_sender.h" + +#include + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "device/serial/async_waiter.h" + +namespace device { + +// Represents a send that is not yet fulfilled. +class DataSourceSender::PendingSend { + public: + PendingSend(DataSourceSender* sender, const ReadyCallback& callback); + + // Asynchronously fills |data| with up to |num_bytes| of data. Following this, + // one of Done() and DoneWithError() will be called with the result. + void GetData(void* data, uint32_t num_bytes); + + private: + class Buffer; + // Reports a successful write of |bytes_written|. + void Done(uint32_t bytes_written); + + // Reports a partially successful or unsuccessful write of |bytes_written| + // with an error of |error|. + void DoneWithError(uint32_t bytes_written, int32_t error); + + // The DataSourceSender that owns this. + DataSourceSender* sender_; + + // The callback to call to get data. + ReadyCallback callback_; + + // Whether the buffer specified by GetData() has been passed to |callback_|, + // but has not yet called Done() or DoneWithError(). + bool buffer_in_use_; +}; + +// A Writable implementation that provides a view of a data pipe owned by a +// DataSourceSender. +class DataSourceSender::PendingSend::Buffer : public WritableBuffer { + public: + Buffer(scoped_refptr sender, + PendingSend* send, + char* buffer, + uint32_t buffer_size); + virtual ~Buffer(); + + // WritableBuffer overrides. + virtual char* GetData() OVERRIDE; + virtual uint32_t GetSize() OVERRIDE; + virtual void Done(uint32_t bytes_written) OVERRIDE; + virtual void DoneWithError(uint32_t bytes_written, int32_t error) OVERRIDE; + + private: + // The DataSourceSender whose data pipe we are providing a view. + scoped_refptr sender_; + + // The PendingSend to which this buffer has been created in response. + PendingSend* pending_send_; + + char* buffer_; + uint32_t buffer_size_; +}; + +DataSourceSender::DataSourceSender(const ReadyCallback& ready_callback, + const ErrorCallback& error_callback) + : ready_callback_(ready_callback), + error_callback_(error_callback), + bytes_sent_(0), + shut_down_(false) { + DCHECK(!ready_callback.is_null() && !error_callback.is_null()); +} + +void DataSourceSender::ShutDown() { + shut_down_ = true; + waiter_.reset(); + ready_callback_.Reset(); + error_callback_.Reset(); +} + +DataSourceSender::~DataSourceSender() { + DCHECK(shut_down_); +} + +void DataSourceSender::Init(mojo::ScopedDataPipeProducerHandle handle) { + // This should never occur. |handle_| is only valid and |pending_send_| is + // only set after Init is called. Receiving an invalid |handle| from the + // client is also unrecoverable. + if (pending_send_ || handle_.is_valid() || !handle.is_valid() || shut_down_) { + DispatchFatalError(); + return; + } + handle_ = handle.Pass(); + pending_send_.reset(new PendingSend(this, ready_callback_)); + StartWaiting(); +} + +void DataSourceSender::Resume() { + if (pending_send_ || !handle_.is_valid()) { + DispatchFatalError(); + return; + } + + pending_send_.reset(new PendingSend(this, ready_callback_)); + StartWaiting(); +} + +void DataSourceSender::OnConnectionError() { + DispatchFatalError(); +} + +void DataSourceSender::StartWaiting() { + DCHECK(pending_send_ && !waiter_); + waiter_.reset( + new AsyncWaiter(handle_.get(), + MOJO_HANDLE_SIGNAL_WRITABLE, + base::Bind(&DataSourceSender::OnDoneWaiting, this))); +} + +void DataSourceSender::OnDoneWaiting(MojoResult result) { + DCHECK(pending_send_ && !shut_down_ && waiter_); + waiter_.reset(); + if (result != MOJO_RESULT_OK) { + DispatchFatalError(); + return; + } + void* data = NULL; + uint32_t num_bytes = std::numeric_limits::max(); + result = mojo::BeginWriteDataRaw( + handle_.get(), &data, &num_bytes, MOJO_WRITE_DATA_FLAG_NONE); + if (result != MOJO_RESULT_OK) { + DispatchFatalError(); + return; + } + pending_send_->GetData(static_cast(data), num_bytes); +} + +void DataSourceSender::Done(uint32_t bytes_written) { + DoneInternal(bytes_written); + if (!shut_down_) + StartWaiting(); +} + +void DataSourceSender::DoneWithError(uint32_t bytes_written, int32_t error) { + DoneInternal(bytes_written); + pending_send_.reset(); + if (!shut_down_) + client()->OnError(bytes_sent_, error); + // We don't call StartWaiting here so we don't send any additional data until + // Resume() is called. +} + +void DataSourceSender::DoneInternal(uint32_t bytes_written) { + DCHECK(pending_send_); + if (shut_down_) + return; + + bytes_sent_ += bytes_written; + MojoResult result = mojo::EndWriteDataRaw(handle_.get(), bytes_written); + if (result != MOJO_RESULT_OK) { + DispatchFatalError(); + return; + } +} + +void DataSourceSender::DispatchFatalError() { + if (shut_down_) + return; + + error_callback_.Run(); + ShutDown(); +} + +DataSourceSender::PendingSend::PendingSend(DataSourceSender* sender, + const ReadyCallback& callback) + : sender_(sender), callback_(callback), buffer_in_use_(false) { +} + +void DataSourceSender::PendingSend::GetData(void* data, uint32_t num_bytes) { + DCHECK(!buffer_in_use_); + buffer_in_use_ = true; + callback_.Run(scoped_ptr( + new Buffer(sender_, this, static_cast(data), num_bytes))); +} + +void DataSourceSender::PendingSend::Done(uint32_t bytes_written) { + DCHECK(buffer_in_use_); + buffer_in_use_ = false; + sender_->Done(bytes_written); +} + +void DataSourceSender::PendingSend::DoneWithError(uint32_t bytes_written, + int32_t error) { + DCHECK(buffer_in_use_); + buffer_in_use_ = false; + sender_->DoneWithError(bytes_written, error); +} + +DataSourceSender::PendingSend::Buffer::Buffer( + scoped_refptr sender, + PendingSend* send, + char* buffer, + uint32_t buffer_size) + : sender_(sender), + pending_send_(send), + buffer_(buffer), + buffer_size_(buffer_size) { +} + +DataSourceSender::PendingSend::Buffer::~Buffer() { + if (sender_) + pending_send_->Done(0); +} + +char* DataSourceSender::PendingSend::Buffer::GetData() { + return buffer_; +} + +uint32_t DataSourceSender::PendingSend::Buffer::GetSize() { + return buffer_size_; +} + +void DataSourceSender::PendingSend::Buffer::Done(uint32_t bytes_written) { + DCHECK(sender_); + pending_send_->Done(bytes_written); + sender_ = NULL; + pending_send_ = NULL; + buffer_ = NULL; + buffer_size_ = 0; +} + +void DataSourceSender::PendingSend::Buffer::DoneWithError( + uint32_t bytes_written, + int32_t error) { + DCHECK(sender_); + pending_send_->DoneWithError(bytes_written, error); + sender_ = NULL; + pending_send_ = NULL; + buffer_ = NULL; + buffer_size_ = 0; +} + +} // namespace device diff --git a/device/serial/data_source_sender.h b/device/serial/data_source_sender.h new file mode 100644 index 0000000000000..9bbabbad46678 --- /dev/null +++ b/device/serial/data_source_sender.h @@ -0,0 +1,94 @@ +// 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 DEVICE_SERIAL_DATA_SOURCE_SENDER_H_ +#define DEVICE_SERIAL_DATA_SOURCE_SENDER_H_ + +#include "base/callback.h" +#include "base/memory/ref_counted.h" +#include "device/serial/buffer.h" +#include "device/serial/data_stream.mojom.h" +#include "mojo/public/cpp/system/data_pipe.h" + +namespace device { + +class AsyncWaiter; + +// A DataSourceSender is an interface between a source of data and a data pipe. +class DataSourceSender : public base::RefCounted, + public mojo::InterfaceImpl { + public: + typedef base::Callback)> ReadyCallback; + typedef base::Callback ErrorCallback; + + // Constructs a DataSourceSender. Whenever the pipe is ready for writing, the + // |ready_callback| will be called with the WritableBuffer to be filled. + // |ready_callback| will not be called again until the previous WritableBuffer + // is destroyed. If a connection error occurs, |error_callback| will be + // called and the DataSourceSender will act as if ShutDown() had been called. + DataSourceSender(const ReadyCallback& ready_callback, + const ErrorCallback& error_callback); + + // Shuts down this DataSourceSender. After shut down, |ready_callback| and + // |error_callback| will never be called. + void ShutDown(); + + private: + friend class base::RefCounted; + class PendingSend; + + virtual ~DataSourceSender(); + + // mojo::InterfaceImpl overrides. + virtual void Init(mojo::ScopedDataPipeProducerHandle handle) OVERRIDE; + virtual void Resume() OVERRIDE; + // Invoked in the event of a connection error. Calls DispatchFatalError(). + virtual void OnConnectionError() OVERRIDE; + + // Starts waiting for |handle_| to be ready for writes. + void StartWaiting(); + + // Invoked when |handle_| is ready for writes. + void OnDoneWaiting(MojoResult result); + + // Reports a successful write of |bytes_written|. + void Done(uint32_t bytes_written); + + // Reports a partially successful or unsuccessful write of |bytes_written| + // with an error of |error|. + void DoneWithError(uint32_t bytes_written, int32_t error); + + // Finishes the two-phase data pipe write. + void DoneInternal(uint32_t bytes_written); + + // Reports a fatal error to the client and shuts down. + void DispatchFatalError(); + + // The data connection to the data receiver. + mojo::ScopedDataPipeProducerHandle handle_; + + // The callback to call when |handle_| is ready for more data. + ReadyCallback ready_callback_; + + // The callback to call if a fatal error occurs. + ErrorCallback error_callback_; + + // The current pending send operation if there is one. + scoped_ptr pending_send_; + + // A waiter used to wait until |handle_| is writable if we are waiting. + scoped_ptr waiter_; + + // The number of bytes sent to the data receiver. + uint32_t bytes_sent_; + + // Whether we have encountered a fatal error and shut down. + bool shut_down_; + + DISALLOW_COPY_AND_ASSIGN(DataSourceSender); +}; + +} // namespace device + +#endif // DEVICE_SERIAL_DATA_SOURCE_SENDER_H_ diff --git a/device/serial/data_source_unittest.cc b/device/serial/data_source_unittest.cc new file mode 100644 index 0000000000000..e185731522cbd --- /dev/null +++ b/device/serial/data_source_unittest.cc @@ -0,0 +1,252 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/strings/string_piece.h" +#include "device/serial/async_waiter.h" +#include "device/serial/buffer.h" +#include "device/serial/data_receiver.h" +#include "device/serial/data_source_sender.h" +#include "device/serial/data_stream.mojom.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device { + +class DataSourceTest : public testing::Test { + public: + enum Event { + EVENT_NONE, + EVENT_WRITE_BUFFER_READY, + EVENT_RECEIVE_COMPLETE, + EVENT_ERROR, + }; + + DataSourceTest() + : error_(0), seen_connection_error_(false), expected_event_(EVENT_NONE) {} + + virtual void SetUp() OVERRIDE { + message_loop_.reset(new base::MessageLoop); + mojo::InterfacePtr source_sender_handle; + source_sender_ = mojo::WeakBindToProxy( + new DataSourceSender( + base::Bind(&DataSourceTest::CanWriteData, base::Unretained(this)), + base::Bind(&DataSourceTest::OnError, base::Unretained(this))), + &source_sender_handle); + receiver_ = new DataReceiver(source_sender_handle.Pass(), 100, kFatalError); + } + + virtual void TearDown() OVERRIDE { + write_buffer_.reset(); + buffer_.reset(); + message_loop_.reset(); + } + + void OnError() { + seen_connection_error_ = true; + EventReceived(EVENT_ERROR); + } + + void WaitForEvent(Event event) { + expected_event_ = event; + base::RunLoop run_loop; + stop_run_loop_ = run_loop.QuitClosure(); + run_loop.Run(); + } + + void EventReceived(Event event) { + if (event == expected_event_ && !stop_run_loop_.is_null()) + stop_run_loop_.Run(); + } + + bool Receive() { + return receiver_->Receive( + base::Bind(&DataSourceTest::OnDataReceived, base::Unretained(this)), + base::Bind(&DataSourceTest::OnReceiveError, base::Unretained(this))); + } + + void FillWriteBuffer(const base::StringPiece& data, int32_t error) { + if (!write_buffer_) { + // Run the message loop until CanWriteData is called. + WaitForEvent(EVENT_WRITE_BUFFER_READY); + } + ASSERT_TRUE(write_buffer_); + ASSERT_GE(write_buffer_->GetSize(), static_cast(data.size())); + memcpy(write_buffer_->GetData(), data.data(), data.size()); + if (error) + write_buffer_->DoneWithError(static_cast(data.size()), error); + else + write_buffer_->Done(static_cast(data.size())); + write_buffer_.reset(); + } + + void ReceiveAndWait() { + ASSERT_TRUE(Receive()); + // Run the message loop until OnDataReceived or OnReceiveError is called. + WaitForEvent(EVENT_RECEIVE_COMPLETE); + } + + void OnDataReceived(scoped_ptr buffer) { + ASSERT_TRUE(buffer); + error_ = 0; + buffer_ = buffer.Pass(); + buffer_contents_ = std::string(buffer_->GetData(), buffer_->GetSize()); + EventReceived(EVENT_RECEIVE_COMPLETE); + } + + void OnReceiveError(int32_t error) { + buffer_contents_.clear(); + error_ = error; + EventReceived(EVENT_RECEIVE_COMPLETE); + } + + void CanWriteData(scoped_ptr buffer) { + write_buffer_ = buffer.Pass(); + EventReceived(EVENT_WRITE_BUFFER_READY); + } + + protected: + static const int32_t kFatalError; + scoped_ptr message_loop_; + base::Closure stop_run_loop_; + + scoped_refptr source_sender_; + scoped_refptr receiver_; + + scoped_ptr buffer_; + std::string buffer_contents_; + int32_t error_; + scoped_ptr write_buffer_; + + bool seen_connection_error_; + + Event expected_event_; + + private: + DISALLOW_COPY_AND_ASSIGN(DataSourceTest); +}; + +const int32_t DataSourceTest::kFatalError = -10; + +// Test that data is successfully transmitted from the source to the receiver. +TEST_F(DataSourceTest, Basic) { + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("a", 0)); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_EQ(0, error_); + ASSERT_TRUE(buffer_); + EXPECT_EQ("a", buffer_contents_); + buffer_->Done(1); + + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("b", 0)); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_EQ(0, error_); + ASSERT_TRUE(buffer_); + EXPECT_EQ("b", buffer_contents_); +} + +// Test that the receiver does not discard any data that is not read by the +// client. +TEST_F(DataSourceTest, PartialReceive) { + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("ab", 0)); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_EQ(0, error_); + ASSERT_TRUE(buffer_); + EXPECT_EQ("ab", buffer_contents_); + buffer_->Done(1); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_EQ(0, error_); + ASSERT_TRUE(buffer_); + EXPECT_EQ("b", buffer_contents_); +} + +// Test that an error is correctly reported to the Receive() call immediately +// after the data has been read by the client. +TEST_F(DataSourceTest, ErrorAndData) { + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("abc", -1)); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + ASSERT_TRUE(buffer_); + EXPECT_EQ("abc", buffer_contents_); + buffer_->Done(1); + EXPECT_EQ(0, error_); + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + ASSERT_TRUE(buffer_); + EXPECT_EQ("bc", buffer_contents_); + buffer_->Done(1); + EXPECT_EQ(0, error_); + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + ASSERT_TRUE(buffer_); + EXPECT_EQ("c", buffer_contents_); + buffer_->Done(1); + EXPECT_EQ(0, error_); + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_EQ(-1, error_); + ASSERT_FALSE(write_buffer_); + + ASSERT_TRUE(Receive()); + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("d", 0)); + + WaitForEvent(EVENT_RECEIVE_COMPLETE); + ASSERT_TRUE(buffer_); + EXPECT_EQ("d", buffer_contents_); + buffer_->Done(1); + EXPECT_EQ(0, error_); + + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("e", -2)); + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + ASSERT_TRUE(buffer_); + EXPECT_EQ("e", buffer_contents_); + buffer_->Done(1); + EXPECT_EQ(0, error_); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_EQ(-2, error_); +} + +// Test that an error is correctly reported when the source encounters an error +// without sending any data. +TEST_F(DataSourceTest, ErrorOnly) { + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("", -1)); + + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_FALSE(buffer_); + EXPECT_EQ(-1, error_); + ASSERT_FALSE(write_buffer_); + + ASSERT_TRUE(Receive()); + ASSERT_NO_FATAL_FAILURE(FillWriteBuffer("", -2)); + + WaitForEvent(EVENT_RECEIVE_COMPLETE); + EXPECT_FALSE(buffer_); + EXPECT_EQ(-2, error_); + ASSERT_FALSE(write_buffer_); +} + +// Test that the source shutting down is correctly reported to the client. +TEST_F(DataSourceTest, SourceShutdown) { + source_sender_->ShutDown(); + source_sender_ = NULL; + ASSERT_NO_FATAL_FAILURE(ReceiveAndWait()); + EXPECT_FALSE(buffer_); + EXPECT_EQ(kFatalError, error_); + ASSERT_FALSE(write_buffer_); + ASSERT_FALSE(Receive()); +} + +// Test that the receiver shutting down is correctly reported to the source. +TEST_F(DataSourceTest, ReceiverShutdown) { + Receive(); + receiver_ = NULL; + EXPECT_EQ(kFatalError, error_); + WaitForEvent(EVENT_ERROR); + EXPECT_TRUE(seen_connection_error_); +} + +} // namespace device diff --git a/device/serial/data_stream.mojom b/device/serial/data_stream.mojom new file mode 100644 index 0000000000000..f303f2034747e --- /dev/null +++ b/device/serial/data_stream.mojom @@ -0,0 +1,24 @@ +// 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. + +module device.serial { + +[Client=DataSourceClient] +interface DataSource { + // Initializes this DataSource with a data pipe handle to use for data + // transmission. + Init(handle producer_handle); + + // Resumes sending data after it has been stopped due to an error. + Resume(); +}; + +interface DataSourceClient { + // Invoked to report |error| from the DataSource, at |error_location| bytes + // into the data stream. No further bytes beyond |error_location| will be + // transmitted from the DataSource until Resume() is called. + OnError(uint32 error_location, int32 error); +}; + +} diff --git a/device/serial/serial.gyp b/device/serial/serial.gyp index 304a9c348c038..f2110da8add30 100644 --- a/device/serial/serial.gyp +++ b/device/serial/serial.gyp @@ -19,6 +19,7 @@ ], 'sources': [ 'serial.mojom', + 'data_stream.mojom', ], }, { @@ -42,10 +43,18 @@ '../../mojo/mojo_base.gyp:mojo_cpp_bindings', ], 'sources': [ + '<(SHARED_INTERMEDIATE_DIR)/device/serial/data_stream.mojom.cc', + '<(SHARED_INTERMEDIATE_DIR)/device/serial/data_stream.mojom.h', '<(SHARED_INTERMEDIATE_DIR)/device/serial/serial.mojom.cc', '<(SHARED_INTERMEDIATE_DIR)/device/serial/serial.mojom.h', + 'async_waiter.cc', + 'async_waiter.h', 'buffer.cc', 'buffer.h', + 'data_receiver.cc', + 'data_receiver.h', + 'data_source_sender.cc', + 'data_source_sender.h', 'serial_connection.cc', 'serial_connection.h', 'serial_connection_factory.cc', diff --git a/device/serial/serial_io_handler.cc b/device/serial/serial_io_handler.cc index 379dee3f1bfc3..ee6fab3095c6f 100644 --- a/device/serial/serial_io_handler.cc +++ b/device/serial/serial_io_handler.cc @@ -114,12 +114,12 @@ void SerialIoHandler::ReadCompleted(int bytes_read, serial::ReceiveError error) { DCHECK(CalledOnValidThread()); DCHECK(IsReadPending()); + scoped_ptr pending_read_buffer = pending_read_buffer_.Pass(); if (error == serial::RECEIVE_ERROR_NONE) { - pending_read_buffer_->Done(bytes_read); + pending_read_buffer->Done(bytes_read); } else { - pending_read_buffer_->DoneWithError(bytes_read, error); + pending_read_buffer->DoneWithError(bytes_read, error); } - pending_read_buffer_.reset(); Release(); } @@ -127,12 +127,13 @@ void SerialIoHandler::WriteCompleted(int bytes_written, serial::SendError error) { DCHECK(CalledOnValidThread()); DCHECK(IsWritePending()); + scoped_ptr pending_write_buffer = + pending_write_buffer_.Pass(); if (error == serial::SEND_ERROR_NONE) { - pending_write_buffer_->Done(bytes_written); + pending_write_buffer->Done(bytes_written); } else { - pending_write_buffer_->DoneWithError(bytes_written, error); + pending_write_buffer->DoneWithError(bytes_written, error); } - pending_write_buffer_.reset(); Release(); } @@ -148,7 +149,7 @@ bool SerialIoHandler::IsWritePending() const { void SerialIoHandler::CancelRead(serial::ReceiveError reason) { DCHECK(CalledOnValidThread()); - if (IsReadPending()) { + if (IsReadPending() && !read_canceled_) { read_canceled_ = true; read_cancel_reason_ = reason; CancelReadImpl(); @@ -157,7 +158,7 @@ void SerialIoHandler::CancelRead(serial::ReceiveError reason) { void SerialIoHandler::CancelWrite(serial::SendError reason) { DCHECK(CalledOnValidThread()); - if (IsWritePending()) { + if (IsWritePending() && !write_canceled_) { write_canceled_ = true; write_cancel_reason_ = reason; CancelWriteImpl(); diff --git a/device/usb/usb_service_unittest.cc b/device/usb/usb_service_unittest.cc index 79a5bb74925bb..1f8b8e2c36606 100644 --- a/device/usb/usb_service_unittest.cc +++ b/device/usb/usb_service_unittest.cc @@ -32,9 +32,22 @@ TEST_F(UsbServiceTest, ClaimGadget) { ASSERT_TRUE(gadget.get()); scoped_refptr handle = gadget->GetDevice()->Open(); - base::string16 serial_utf16; - ASSERT_TRUE(handle->GetSerial(&serial_utf16)); - ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(serial_utf16)); + + base::string16 utf16; + ASSERT_TRUE(handle->GetManufacturer(&utf16)); + ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16)); + // Check again to make sure string descriptor caching works. + ASSERT_EQ("Google Inc.", base::UTF16ToUTF8(utf16)); + + ASSERT_TRUE(handle->GetProduct(&utf16)); + ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16)); + // Check again to make sure string descriptor caching works. + ASSERT_EQ("Test Gadget (default state)", base::UTF16ToUTF8(utf16)); + + ASSERT_TRUE(handle->GetSerial(&utf16)); + ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16)); + // Check again to make sure string descriptor caching works. + ASSERT_EQ(gadget->GetSerial(), base::UTF16ToUTF8(utf16)); } TEST_F(UsbServiceTest, DisconnectAndReconnect) { diff --git a/extensions/BUILD.gn b/extensions/BUILD.gn index 0c0b453fc3748..5cb0f488783a8 100644 --- a/extensions/BUILD.gn +++ b/extensions/BUILD.gn @@ -82,12 +82,12 @@ source_set("test_support") { # GYP version: //extensions/extensions.gyp:extensions_shell_and_test_pak repack("shell_and_test_pak") { sources = [ - "$root_gen_dir/chrome/common/common_resources.pak", - "$root_gen_dir/chrome/common/extensions_api_resources.pak", + "$root_gen_dir/chrome/common_resources.pak", + "$root_gen_dir/chrome/extensions_api_resources.pak", # TODO(jamescook): Extract the extension/app related resources # from generated_resources_en-US.pak. http://crbug.com/397250 - "$root_gen_dir/chrome/app/generated_resources_en-US.pak", - "$root_gen_dir/chrome/renderer/renderer_resources_100_percent.pak", + "$root_gen_dir/chrome/generated_resources_en-US.pak", + "$root_gen_dir/chrome/renderer_resources_100_percent.pak", "$root_gen_dir/content/content_resources.pak", "$root_gen_dir/content/shell/shell_resources.pak", "$root_gen_dir/extensions/extensions_renderer_resources.pak", @@ -166,6 +166,7 @@ test("unittests") { "//base/test:test_support", "//content/test:test_support", "//device/serial", + "//device/serial:test_util", "//extensions/common", "//extensions/renderer", "//extensions/strings", diff --git a/extensions/DEPS b/extensions/DEPS index fede72fd4e448..7e2496a3617e9 100644 --- a/extensions/DEPS +++ b/extensions/DEPS @@ -1,5 +1,6 @@ include_rules = [ # Do not add Chrome dependencies. Much work went into removing them. + "+components/crx_file", "+components/url_matcher", "-content", "+content/public/common", diff --git a/extensions/browser/BUILD.gn b/extensions/browser/BUILD.gn index f75b247a48199..700f48ce03a77 100644 --- a/extensions/browser/BUILD.gn +++ b/extensions/browser/BUILD.gn @@ -47,6 +47,10 @@ source_set("browser") { "api/cast_channel/cast_message_util.h", "api/cast_channel/cast_socket.cc", "api/cast_channel/cast_socket.h", + "api/cast_channel/logger.cc", + "api/cast_channel/logger.h", + "api/cast_channel/logger_util.cc", + "api/cast_channel/logger_util.h", "api/dns/dns_api.cc", "api/dns/dns_api.h", "api/dns/host_resolver_wrapper.cc", @@ -232,6 +236,9 @@ source_set("browser") { "renderer_startup_helper.h", "runtime_data.cc", "runtime_data.h", + "script_execution_observer.h", + "script_executor.cc", + "script_executor.h", "state_store.cc", "state_store.h", "uninstall_reason.h", diff --git a/extensions/browser/DEPS b/extensions/browser/DEPS index 9b4dd6f798d3e..08dfbdf0645aa 100644 --- a/extensions/browser/DEPS +++ b/extensions/browser/DEPS @@ -8,5 +8,6 @@ include_rules = [ "+sync", "+third_party/leveldatabase", "+third_party/skia/include", + "+third_party/WebKit/public/web/WebInputEvent.h", "+webkit/browser/fileapi", ] diff --git a/extensions/browser/api/cast_channel/BUILD.gn b/extensions/browser/api/cast_channel/BUILD.gn index e5a46da4b322d..8a8dc36d3388c 100644 --- a/extensions/browser/api/cast_channel/BUILD.gn +++ b/extensions/browser/api/cast_channel/BUILD.gn @@ -6,5 +6,8 @@ import("//third_party/protobuf/proto_library.gni") # GYP version: extensions/extensions.gyp:cast_channel_proto proto_library("cast_channel_proto") { - sources = [ "cast_channel.proto" ] + sources = [ + "cast_channel.proto", + "logging.proto", + ] } diff --git a/extensions/browser/api/cast_channel/DEPS b/extensions/browser/api/cast_channel/DEPS new file mode 100644 index 0000000000000..c6e9550543b2f --- /dev/null +++ b/extensions/browser/api/cast_channel/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+third_party/zlib", +] diff --git a/extensions/browser/api/cast_channel/cast_auth_util.cc b/extensions/browser/api/cast_channel/cast_auth_util.cc new file mode 100644 index 0000000000000..767ef48506649 --- /dev/null +++ b/extensions/browser/api/cast_channel/cast_auth_util.cc @@ -0,0 +1,40 @@ +// 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 "extensions/browser/api/cast_channel/cast_auth_util.h" + +namespace extensions { +namespace core_api { +namespace cast_channel { + +AuthResult::AuthResult() : error_type(ERROR_NONE), nss_error_code(0) { +} + +AuthResult::~AuthResult() { +} + +// static +AuthResult AuthResult::Create(const std::string& error_message, + ErrorType error_type) { + return AuthResult(error_message, error_type, 0); +} + +// static +AuthResult AuthResult::CreateWithNSSError(const std::string& error_message, + ErrorType error_type, + int nss_error_code) { + return AuthResult(error_message, error_type, nss_error_code); +} + +AuthResult::AuthResult(const std::string& error_message, + ErrorType error_type, + int nss_error_code) + : error_message(error_message), + error_type(error_type), + nss_error_code(nss_error_code) { +} + +} // namespace cast_channel +} // namespace core_api +} // namespace extensions diff --git a/extensions/browser/api/cast_channel/cast_auth_util.h b/extensions/browser/api/cast_channel/cast_auth_util.h index 9fac2f4d23bcb..560f698d2b574 100644 --- a/extensions/browser/api/cast_channel/cast_auth_util.h +++ b/extensions/browser/api/cast_channel/cast_auth_util.h @@ -13,11 +13,50 @@ namespace cast_channel { class CastMessage; +struct AuthResult { + public: + enum ErrorType { + ERROR_NONE, + ERROR_PEER_CERT_EMPTY, + ERROR_WRONG_PAYLOAD_TYPE, + ERROR_NO_PAYLOAD, + ERROR_PAYLOAD_PARSING_FAILED, + ERROR_MESSAGE_ERROR, + ERROR_NO_RESPONSE, + ERROR_FINGERPRINT_NOT_FOUND, + ERROR_NSS_CERT_PARSING_FAILED, + ERROR_NSS_CERT_NOT_SIGNED_BY_TRUSTED_CA, + ERROR_NSS_CANNOT_EXTRACT_PUBLIC_KEY, + ERROR_NSS_SIGNED_BLOBS_MISMATCH + }; + + // Constructs a AuthResult that corresponds to success. + AuthResult(); + ~AuthResult(); + + static AuthResult Create(const std::string& error_message, + ErrorType error_type); + static AuthResult CreateWithNSSError(const std::string& error_message, + ErrorType error_type, + int nss_error_code); + + bool success() const { return error_type == ERROR_NONE; } + + std::string error_message; + ErrorType error_type; + int nss_error_code; + + private: + AuthResult(const std::string& error_message, + ErrorType error_type, + int nss_error_code); +}; + // Authenticates the given |challenge_reply|: // 1. Signature contained in the reply is valid. // 2. Certficate used to sign is rooted to a trusted CA. -bool AuthenticateChallengeReply(const CastMessage& challenge_reply, - const std::string& peer_cert); +AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply, + const std::string& peer_cert); } // namespace cast_channel } // namespace core_api diff --git a/extensions/browser/api/cast_channel/cast_auth_util_nss.cc b/extensions/browser/api/cast_channel/cast_auth_util_nss.cc index 9937286671113..34d2ab27ac938 100644 --- a/extensions/browser/api/cast_channel/cast_auth_util_nss.cc +++ b/extensions/browser/api/cast_channel/cast_auth_util_nss.cc @@ -11,6 +11,7 @@ #include #include "base/logging.h" +#include "base/strings/stringprintf.h" #include "crypto/nss_util.h" #include "crypto/scoped_nss_types.h" #include "extensions/browser/api/cast_channel/cast_channel.pb.h" @@ -187,37 +188,49 @@ static int GetICAWithFingerprint(const net::SHA1HashValue& fingerprint) { } // Parses out DeviceAuthMessage from CastMessage -static bool ParseAuthMessage(const CastMessage& challenge_reply, - DeviceAuthMessage* auth_message) { +static AuthResult ParseAuthMessage(const CastMessage& challenge_reply, + DeviceAuthMessage* auth_message) { + const std::string kErrorPrefix("Failed to parse auth message: "); if (challenge_reply.payload_type() != CastMessage_PayloadType_BINARY) { - VLOG(1) << "Wrong payload type in challenge reply"; - return false; + return AuthResult::Create( + kErrorPrefix + "Wrong payload type in challenge reply", + AuthResult::ERROR_WRONG_PAYLOAD_TYPE); } if (!challenge_reply.has_payload_binary()) { - VLOG(1) << "Payload type is binary but payload_binary field not set"; - return false; + return AuthResult::Create( + kErrorPrefix + + "Payload type is binary but payload_binary field not set", + AuthResult::ERROR_NO_PAYLOAD); } if (!auth_message->ParseFromString(challenge_reply.payload_binary())) { - VLOG(1) << "Cannot parse binary payload into DeviceAuthMessage"; - return false; + return AuthResult::Create( + kErrorPrefix + "Cannot parse binary payload into DeviceAuthMessage", + AuthResult::ERROR_PAYLOAD_PARSING_FAILED); } + VLOG(1) << "Auth message: " << AuthMessageToString(*auth_message); + if (auth_message->has_error()) { - VLOG(1) << "Auth message error: " << auth_message->error().error_type(); - return false; + std::string error_format_str = kErrorPrefix + "Auth message error: %d"; + return AuthResult::Create( + base::StringPrintf(error_format_str.c_str(), + auth_message->error().error_type()), + AuthResult::ERROR_MESSAGE_ERROR); } if (!auth_message->has_response()) { - VLOG(1) << "Auth message has no response field"; - return false; + return AuthResult::Create( + kErrorPrefix + "Auth message has no response field", + AuthResult::ERROR_NO_RESPONSE); } - return true; + return AuthResult(); } // Authenticates the given credentials: // 1. |signature| verification of |data| using |certificate|. // 2. |certificate| is signed by a trusted CA. -bool VerifyCredentials(const AuthResponse& response, - const std::string& data) { +AuthResult VerifyCredentials(const AuthResponse& response, + const std::string& data) { + const std::string kErrorPrefix("Failed to verify credentials: "); const std::string& certificate = response.client_auth_certificate(); const std::string& signature = response.signature(); @@ -228,7 +241,9 @@ bool VerifyCredentials(const AuthResponse& response, // Otherwise, use the first intermediate in the list as long as it // is in the allowed list of intermediates. int num_intermediates = response.intermediate_certificate_size(); + VLOG(1) << "Response has " << num_intermediates << " intermediates"; + if (num_intermediates <= 0) { trusted_ca_key_der = &kAllowedICAs[0].public_key; } else { @@ -237,8 +252,8 @@ bool VerifyCredentials(const AuthResponse& response, = net::X509Certificate::CreateFromBytes(ica.data(), ica.length()); int index = GetICAWithFingerprint(ica_cert->fingerprint()); if (index == -1) { - VLOG(1) << "Disallowed intermdiate cert"; - return false; + return AuthResult::Create(kErrorPrefix + "Disallowed intermediate cert", + AuthResult::ERROR_FINGERPRINT_NOT_FOUND); } trusted_ca_key_der = &kAllowedICAs[index].public_key; } @@ -255,8 +270,10 @@ bool VerifyCredentials(const AuthResponse& response, ScopedCERTCertificate cert(CERT_NewTempCertificate( CERT_GetDefaultCertDB(), &der_cert, NULL, PR_FALSE, PR_TRUE)); if (!cert.get()) { - VLOG(1) << "Failed to parse certificate."; - return false; + return AuthResult::CreateWithNSSError( + kErrorPrefix + "Failed to parse certificate.", + AuthResult::ERROR_NSS_CERT_PARSING_FAILED, + PORT_GetError()); } // Check that the certificate is signed by trusted CA. @@ -269,16 +286,21 @@ bool VerifyCredentials(const AuthResponse& response, SECStatus verified = CERT_VerifySignedDataWithPublicKey( &cert->signatureWrap, ca_public_key.get(), NULL); if (verified != SECSuccess) { - VLOG(1)<< "Cert not signed by trusted CA"; - return false; + return AuthResult::CreateWithNSSError( + kErrorPrefix + "Cert not signed by trusted CA", + AuthResult::ERROR_NSS_CERT_NOT_SIGNED_BY_TRUSTED_CA, + PORT_GetError()); } + VLOG(1) << "Cert signed by trusted CA"; // Verify that the |signature| matches |data|. crypto::ScopedSECKEYPublicKey public_key(CERT_ExtractPublicKey(cert.get())); if (!public_key.get()) { - VLOG(1) << "Unable to extract public key from certificate."; - return false; + return AuthResult::CreateWithNSSError( + kErrorPrefix + "Unable to extract public key from certificate", + AuthResult::ERROR_NSS_CANNOT_EXTRACT_PUBLIC_KEY, + PORT_GetError()); } SECItem signature_item; signature_item.type = siBuffer; @@ -294,27 +316,45 @@ bool VerifyCredentials(const AuthResponse& response, SEC_OID_SHA1, NULL, NULL); if (verified != SECSuccess) { - VLOG(1) << "Signed blobs did not match."; - return false; + return AuthResult::CreateWithNSSError( + kErrorPrefix + "Signed blobs did not match", + AuthResult::ERROR_NSS_SIGNED_BLOBS_MISMATCH, + PORT_GetError()); } + VLOG(1) << "Signature verification succeeded"; - return true; + + return AuthResult(); } } // namespace -bool AuthenticateChallengeReply(const CastMessage& challenge_reply, - const std::string& peer_cert) { - if (peer_cert.empty()) - return false; +AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply, + const std::string& peer_cert) { + if (peer_cert.empty()) { + AuthResult result = AuthResult::Create("Peer cert was empty.", + AuthResult::ERROR_PEER_CERT_EMPTY); + VLOG(1) << result.error_message; + return result; + } VLOG(1) << "Challenge reply: " << CastMessageToString(challenge_reply); DeviceAuthMessage auth_message; - if (!ParseAuthMessage(challenge_reply, &auth_message)) - return false; + AuthResult result = ParseAuthMessage(challenge_reply, &auth_message); + if (!result.success()) { + VLOG(1) << result.error_message; + return result; + } const AuthResponse& response = auth_message.response(); - return VerifyCredentials(response, peer_cert); + result = VerifyCredentials(response, peer_cert); + if (!result.success()) { + VLOG(1) << result.error_message + << ", NSS error code: " << result.nss_error_code; + return result; + } + + return AuthResult(); } } // namespace cast_channel diff --git a/extensions/browser/api/cast_channel/cast_auth_util_openssl.cc b/extensions/browser/api/cast_channel/cast_auth_util_openssl.cc index 04795ca9c40c8..3ebceed1f107a 100644 --- a/extensions/browser/api/cast_channel/cast_auth_util_openssl.cc +++ b/extensions/browser/api/cast_channel/cast_auth_util_openssl.cc @@ -10,10 +10,10 @@ namespace extensions { namespace core_api { namespace cast_channel { -bool AuthenticateChallengeReply(const CastMessage& challenge_reply, - const std::string& peer_cert) { +AuthResult AuthenticateChallengeReply(const CastMessage& challenge_reply, + const std::string& peer_cert) { NOTREACHED(); - return false; + return AuthResult(); } } // namespace cast_channel diff --git a/extensions/browser/api/cast_channel/cast_channel_api.cc b/extensions/browser/api/cast_channel/cast_channel_api.cc index bf710534488da..4bae9c139d7b6 100644 --- a/extensions/browser/api/cast_channel/cast_channel_api.cc +++ b/extensions/browser/api/cast_channel/cast_channel_api.cc @@ -10,9 +10,12 @@ #include "base/json/json_writer.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" +#include "base/time/default_tick_clock.h" #include "base/values.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/api/cast_channel/cast_socket.h" +#include "extensions/browser/api/cast_channel/logger.h" +#include "extensions/browser/api/cast_channel/logging.pb.h" #include "extensions/browser/event_router.h" #include "net/base/ip_endpoint.h" #include "net/base/net_errors.h" @@ -35,6 +38,9 @@ using cast_channel::ChannelAuthType; using cast_channel::ChannelError; using cast_channel::ChannelInfo; using cast_channel::ConnectInfo; +using cast_channel::ErrorInfo; +using cast_channel::LastErrors; +using cast_channel::Logger; using cast_channel::MessageInfo; using cast_channel::ReadyState; using content::BrowserThread; @@ -63,6 +69,24 @@ void FillChannelInfo(const CastSocket& socket, ChannelInfo* channel_info) { channel_info->error_state = socket.error_state(); } +// Fills |error_info| from |error_state| and |last_errors|. +void FillErrorInfo(ChannelError error_state, + const LastErrors& last_errors, + ErrorInfo* error_info) { + error_info->error_state = error_state; + if (last_errors.event_type != cast_channel::proto::EVENT_TYPE_UNKNOWN) + error_info->event_type.reset(new int(last_errors.event_type)); + if (last_errors.challenge_reply_error_type != + cast_channel::proto::CHALLENGE_REPLY_ERROR_NONE) { + error_info->challenge_reply_error_type.reset( + new int(last_errors.challenge_reply_error_type)); + } + if (last_errors.net_return_value <= 0) + error_info->net_return_value.reset(new int(last_errors.net_return_value)); + if (last_errors.nss_error_code < 0) + error_info->nss_error_code.reset(new int(last_errors.nss_error_code)); +} + bool IsValidConnectInfoPort(const ConnectInfo& connect_info) { return connect_info.port > 0 && connect_info.port < std::numeric_limits::max(); @@ -81,7 +105,10 @@ bool IsValidConnectInfoIpAddress(const ConnectInfo& connect_info) { } // namespace CastChannelAPI::CastChannelAPI(content::BrowserContext* context) - : browser_context_(context) { + : browser_context_(context), + logger_( + new Logger(scoped_ptr(new base::DefaultTickClock), + base::TimeTicks::UnixEpoch())) { DCHECK(browser_context_); } @@ -90,6 +117,10 @@ CastChannelAPI* CastChannelAPI::Get(content::BrowserContext* context) { return BrowserContextKeyedAPIFactory::Get(context); } +scoped_refptr CastChannelAPI::GetLogger() { + return logger_; +} + static base::LazyInstance > g_factory = LAZY_INSTANCE_INITIALIZER; @@ -106,9 +137,13 @@ scoped_ptr CastChannelAPI::CreateCastSocket( return socket_for_test_.Pass(); } else { return scoped_ptr( - new CastSocket(extension_id, ip_endpoint, channel_auth, this, + new CastSocket(extension_id, + ip_endpoint, + channel_auth, + this, ExtensionsBrowserClient::Get()->GetNetLog(), - timeout)); + timeout, + logger_)); } } @@ -117,12 +152,16 @@ void CastChannelAPI::SetSocketForTest(scoped_ptr socket_for_test) { } void CastChannelAPI::OnError(const CastSocket* socket, - cast_channel::ChannelError error) { + cast_channel::ChannelError error_state, + const cast_channel::LastErrors& last_errors) { DCHECK_CURRENTLY_ON(BrowserThread::IO); ChannelInfo channel_info; FillChannelInfo(*socket, &channel_info); - channel_info.error_state = error; - scoped_ptr results = OnError::Create(channel_info); + channel_info.error_state = error_state; + ErrorInfo error_info; + FillErrorInfo(error_state, last_errors, &error_info); + scoped_ptr results = + OnError::Create(channel_info, error_info); scoped_ptr event(new Event(OnError::kEventName, results.Pass())); extensions::EventRouter::Get(browser_context_) ->DispatchEventToExtension(socket->owner_extension_id(), event.Pass()); @@ -335,8 +374,9 @@ void CastChannelOpenFunction::AsyncWorkStart() { ? *connect_info_->timeout : kDefaultConnectTimeoutMillis)); new_channel_id_ = AddSocket(socket.release()); - GetSocket(new_channel_id_)->Connect( - base::Bind(&CastChannelOpenFunction::OnOpen, this)); + CastSocket* new_socket = GetSocket(new_channel_id_); + api_->GetLogger()->LogNewSocketEvent(*new_socket); + new_socket->Connect(base::Bind(&CastChannelOpenFunction::OnOpen, this)); } void CastChannelOpenFunction::OnOpen(int result) { @@ -437,4 +477,35 @@ void CastChannelCloseFunction::OnClose(int result) { AsyncWorkCompleted(); } +CastChannelGetLogsFunction::CastChannelGetLogsFunction() { +} + +CastChannelGetLogsFunction::~CastChannelGetLogsFunction() { +} + +bool CastChannelGetLogsFunction::PrePrepare() { + api_ = CastChannelAPI::Get(browser_context()); + return CastChannelAsyncApiFunction::PrePrepare(); +} + +bool CastChannelGetLogsFunction::Prepare() { + return true; +} + +void CastChannelGetLogsFunction::AsyncWorkStart() { + DCHECK(api_); + + size_t length = 0; + scoped_ptr out = api_->GetLogger()->GetLogs(&length); + if (out.get()) { + SetResult(new base::BinaryValue(out.Pass(), length)); + } else { + SetError("Unable to get logs."); + } + + api_->GetLogger()->Reset(); + + AsyncWorkCompleted(); +} + } // namespace extensions diff --git a/extensions/browser/api/cast_channel/cast_channel_api.h b/extensions/browser/api/cast_channel/cast_channel_api.h index 3e077119ba144..ebb3173a54e97 100644 --- a/extensions/browser/api/cast_channel/cast_channel_api.h +++ b/extensions/browser/api/cast_channel/cast_channel_api.h @@ -8,6 +8,7 @@ #include #include "base/basictypes.h" +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/threading/thread_checker.h" #include "extensions/browser/api/api_resource_manager.h" @@ -29,6 +30,12 @@ class IPEndPoint; namespace extensions { +namespace core_api { +namespace cast_channel { +class Logger; +} // namespace cast_channel +} // namespace core_api + namespace cast_channel = core_api::cast_channel; class CastChannelAPI : public BrowserContextKeyedAPI, @@ -49,6 +56,13 @@ class CastChannelAPI : public BrowserContextKeyedAPI, cast_channel::ChannelAuthType channel_auth, const base::TimeDelta& timeout); + // Returns a pointer to the Logger member variable. + // TODO(imcheng): Consider whether it is possible for this class to own the + // CastSockets and make this class the sole owner of Logger. Alternatively, + // consider making Logger not ref-counted by passing a weak + // reference of Logger to the CastSockets instead. + scoped_refptr GetLogger(); + // Sets the CastSocket instance to be returned by CreateCastSocket for // testing. void SetSocketForTest(scoped_ptr socket_for_test); @@ -61,7 +75,8 @@ class CastChannelAPI : public BrowserContextKeyedAPI, // CastSocket::Delegate. Called on IO thread. virtual void OnError(const cast_channel::CastSocket* socket, - cast_channel::ChannelError error) OVERRIDE; + cast_channel::ChannelError error_state, + const cast_channel::LastErrors& last_errors) OVERRIDE; virtual void OnMessage(const cast_channel::CastSocket* socket, const cast_channel::MessageInfo& message) OVERRIDE; @@ -69,6 +84,7 @@ class CastChannelAPI : public BrowserContextKeyedAPI, static const char* service_name() { return "CastChannelAPI"; } content::BrowserContext* const browser_context_; + scoped_refptr logger_; scoped_ptr socket_for_test_; DISALLOW_COPY_AND_ASSIGN(CastChannelAPI); @@ -204,6 +220,28 @@ class CastChannelCloseFunction : public CastChannelAsyncApiFunction { DISALLOW_COPY_AND_ASSIGN(CastChannelCloseFunction); }; +class CastChannelGetLogsFunction : public CastChannelAsyncApiFunction { + public: + CastChannelGetLogsFunction(); + + protected: + virtual ~CastChannelGetLogsFunction(); + + // AsyncApiFunction: + virtual bool PrePrepare() OVERRIDE; + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + private: + DECLARE_EXTENSION_FUNCTION("cast.channel.getLogs", CAST_CHANNEL_GETLOGS) + + void OnClose(int result); + + CastChannelAPI* api_; + + DISALLOW_COPY_AND_ASSIGN(CastChannelGetLogsFunction); +}; + } // namespace extensions #endif // EXTENSIONS_BROWSER_API_CAST_CHANNEL_CAST_CHANNEL_API_H_ diff --git a/extensions/browser/api/cast_channel/cast_channel_apitest.cc b/extensions/browser/api/cast_channel/cast_channel_apitest.cc index 9f56d2d6e7158..1b47473813ab8 100644 --- a/extensions/browser/api/cast_channel/cast_channel_apitest.cc +++ b/extensions/browser/api/cast_channel/cast_channel_apitest.cc @@ -12,6 +12,7 @@ #include "content/public/browser/browser_thread.h" #include "extensions/browser/api/cast_channel/cast_channel_api.h" #include "extensions/browser/api/cast_channel/cast_socket.h" +#include "extensions/browser/api/cast_channel/logger.h" #include "extensions/common/api/cast_channel.h" #include "extensions/common/switches.h" #include "net/base/capturing_net_log.h" @@ -26,6 +27,8 @@ namespace cast_channel = extensions::core_api::cast_channel; using cast_channel::CastSocket; using cast_channel::ChannelError; +using cast_channel::ErrorInfo; +using cast_channel::Logger; using cast_channel::MessageInfo; using cast_channel::ReadyState; using extensions::Extension; @@ -66,10 +69,15 @@ class MockCastSocket : public CastSocket { public: explicit MockCastSocket(CastSocket::Delegate* delegate, net::IPEndPoint ip_endpoint, - net::NetLog* net_log) - : CastSocket(kTestExtensionId, ip_endpoint, - cast_channel::CHANNEL_AUTH_TYPE_SSL, delegate, net_log, - base::TimeDelta::FromMilliseconds(kTimeoutMs)) {} + net::NetLog* net_log, + Logger* logger) + : CastSocket(kTestExtensionId, + ip_endpoint, + cast_channel::CHANNEL_AUTH_TYPE_SSL, + delegate, + net_log, + base::TimeDelta::FromMilliseconds(kTimeoutMs), + logger) {} virtual ~MockCastSocket() {} MOCK_METHOD1(Connect, void(const net::CompletionCallback& callback)); @@ -98,15 +106,32 @@ class CastChannelAPITest : public ExtensionApiTest { net::IPAddressNumber ip_number; net::ParseIPLiteralToNumber("192.168.1.1", &ip_number); net::IPEndPoint ip_endpoint(ip_number, 8009); - mock_cast_socket_ = new MockCastSocket(api, ip_endpoint, - &capturing_net_log_); + mock_cast_socket_ = new MockCastSocket( + api, ip_endpoint, &capturing_net_log_, api->GetLogger()); // Transfers ownership of the socket. api->SetSocketForTest( make_scoped_ptr(mock_cast_socket_).Pass()); + } - // Set expectations on error_state(). + void SetUpOpenSendClose() { + SetUpMockCastSocket(); EXPECT_CALL(*mock_cast_socket_, error_state()) - .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_NONE)); + .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_NONE)); + { + InSequence sequence; + EXPECT_CALL(*mock_cast_socket_, Connect(_)) + .WillOnce(InvokeCompletionCallback<0>(net::OK)); + EXPECT_CALL(*mock_cast_socket_, ready_state()) + .WillOnce(Return(cast_channel::READY_STATE_OPEN)); + EXPECT_CALL(*mock_cast_socket_, SendMessage(A(), _)) + .WillOnce(InvokeCompletionCallback<1>(net::OK)); + EXPECT_CALL(*mock_cast_socket_, ready_state()) + .WillOnce(Return(cast_channel::READY_STATE_OPEN)); + EXPECT_CALL(*mock_cast_socket_, Close(_)) + .WillOnce(InvokeCompletionCallback<0>(net::OK)); + EXPECT_CALL(*mock_cast_socket_, ready_state()) + .WillOnce(Return(cast_channel::READY_STATE_CLOSED)); + } } extensions::CastChannelAPI* GetApi() { @@ -114,8 +139,13 @@ class CastChannelAPITest : public ExtensionApiTest { } void CallOnError(extensions::CastChannelAPI* api) { + cast_channel::LastErrors last_errors; + last_errors.challenge_reply_error_type = + cast_channel::proto::CHALLENGE_REPLY_ERROR_NSS_CERT_PARSING_FAILED; + last_errors.nss_error_code = -8164; api->OnError(mock_cast_socket_, - cast_channel::CHANNEL_ERROR_CONNECT_ERROR); + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + last_errors); } protected: @@ -165,23 +195,7 @@ class CastChannelAPITest : public ExtensionApiTest { // Test loading extension, opening a channel with ConnectInfo, adding a // listener, writing, reading, and closing. IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendClose) { - SetUpMockCastSocket(); - - { - InSequence dummy; - EXPECT_CALL(*mock_cast_socket_, Connect(_)) - .WillOnce(InvokeCompletionCallback<0>(net::OK)); - EXPECT_CALL(*mock_cast_socket_, ready_state()) - .WillOnce(Return(cast_channel::READY_STATE_OPEN)); - EXPECT_CALL(*mock_cast_socket_, SendMessage(A(), _)) - .WillOnce(InvokeCompletionCallback<1>(net::OK)); - EXPECT_CALL(*mock_cast_socket_, ready_state()) - .WillOnce(Return(cast_channel::READY_STATE_OPEN)); - EXPECT_CALL(*mock_cast_socket_, Close(_)) - .WillOnce(InvokeCompletionCallback<0>(net::OK)); - EXPECT_CALL(*mock_cast_socket_, ready_state()) - .WillOnce(Return(cast_channel::READY_STATE_CLOSED)); - } + SetUpOpenSendClose(); EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", "test_open_send_close.html")); @@ -197,23 +211,7 @@ IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendClose) { // Test loading extension, opening a channel with a URL, adding a listener, // writing, reading, and closing. IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendCloseWithUrl) { - SetUpMockCastSocket(); - - { - InSequence dummy; - EXPECT_CALL(*mock_cast_socket_, Connect(_)) - .WillOnce(InvokeCompletionCallback<0>(net::OK)); - EXPECT_CALL(*mock_cast_socket_, ready_state()) - .WillOnce(Return(cast_channel::READY_STATE_OPEN)); - EXPECT_CALL(*mock_cast_socket_, SendMessage(A(), _)) - .WillOnce(InvokeCompletionCallback<1>(net::OK)); - EXPECT_CALL(*mock_cast_socket_, ready_state()) - .WillOnce(Return(cast_channel::READY_STATE_OPEN)); - EXPECT_CALL(*mock_cast_socket_, Close(_)) - .WillOnce(InvokeCompletionCallback<0>(net::OK)); - EXPECT_CALL(*mock_cast_socket_, ready_state()) - .WillOnce(Return(cast_channel::READY_STATE_CLOSED)); - } + SetUpOpenSendClose(); EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", "test_open_send_close_url.html")); @@ -230,9 +228,11 @@ IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenSendCloseWithUrl) { // writing, reading, and closing. IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenReceiveClose) { SetUpMockCastSocket(); + EXPECT_CALL(*mock_cast_socket_, error_state()) + .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_NONE)); { - InSequence dummy; + InSequence sequence; EXPECT_CALL(*mock_cast_socket_, Connect(_)) .WillOnce(InvokeCompletionCallback<0>(net::OK)); EXPECT_CALL(*mock_cast_socket_, ready_state()) @@ -253,6 +253,20 @@ IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenReceiveClose) { EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } +// TODO(imcheng): Win Dbg has a workaround that makes RunExtensionSubtest +// always return true without actually running the test. Remove when fixed. +#if defined(OS_WIN) && !defined(NDEBUG) +#define MAYBE_TestGetLogs DISABLED_TestGetLogs +#else +#define MAYBE_TestGetLogs TestGetLogs +#endif +// Test loading extension, execute a open-send-close sequence, then get logs. +IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestGetLogs) { + SetUpOpenSendClose(); + + EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", "test_get_logs.html")); +} + // TODO(munjal): Win Dbg has a workaround that makes RunExtensionSubtest // always return true without actually running the test. Remove when fixed. // Flaky on mac: crbug.com/393969 @@ -266,12 +280,14 @@ IN_PROC_BROWSER_TEST_F(CastChannelAPITest, MAYBE_TestOpenError) { SetUpMockCastSocket(); EXPECT_CALL(*mock_cast_socket_, Connect(_)) - .WillOnce(DoAll( - InvokeDelegateOnError(this, GetApi()), - InvokeCompletionCallback<0>(net::ERR_FAILED))); + .WillOnce(DoAll(InvokeDelegateOnError(this, GetApi()), + InvokeCompletionCallback<0>(net::ERR_CONNECTION_FAILED))); + EXPECT_CALL(*mock_cast_socket_, error_state()) + .WillRepeatedly(Return(cast_channel::CHANNEL_ERROR_CONNECT_ERROR)); EXPECT_CALL(*mock_cast_socket_, ready_state()) .WillRepeatedly(Return(cast_channel::READY_STATE_CLOSED)); - EXPECT_CALL(*mock_cast_socket_, Close(_)); + EXPECT_CALL(*mock_cast_socket_, Close(_)) + .WillOnce(InvokeCompletionCallback<0>(net::OK)); EXPECT_TRUE(RunExtensionSubtest("cast_channel/api", "test_open_error.html")); diff --git a/extensions/browser/api/cast_channel/cast_socket.cc b/extensions/browser/api/cast_channel/cast_socket.cc index 80c9e85783f5e..dac6e0d395b84 100644 --- a/extensions/browser/api/cast_channel/cast_socket.cc +++ b/extensions/browser/api/cast_channel/cast_socket.cc @@ -9,13 +9,17 @@ #include "base/bind.h" #include "base/callback_helpers.h" +#include "base/format_macros.h" #include "base/lazy_instance.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" #include "base/sys_byteorder.h" #include "extensions/browser/api/cast_channel/cast_auth_util.h" #include "extensions/browser/api/cast_channel/cast_channel.pb.h" #include "extensions/browser/api/cast_channel/cast_message_util.h" +#include "extensions/browser/api/cast_channel/logger.h" +#include "extensions/browser/api/cast_channel/logger_util.h" #include "net/base/address_list.h" #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" @@ -63,28 +67,140 @@ ApiResourceManager::GetFactoryInstance() { namespace core_api { namespace cast_channel { +namespace { + +proto::ReadyState ReadyStateToProto(ReadyState state) { + switch (state) { + case READY_STATE_NONE: + return proto::READY_STATE_NONE; + case READY_STATE_CONNECTING: + return proto::READY_STATE_CONNECTING; + case READY_STATE_OPEN: + return proto::READY_STATE_OPEN; + case READY_STATE_CLOSING: + return proto::READY_STATE_CLOSING; + case READY_STATE_CLOSED: + return proto::READY_STATE_CLOSED; + default: + NOTREACHED(); + return proto::READY_STATE_NONE; + } +} + +proto::ConnectionState ConnectStateToProto(CastSocket::ConnectionState state) { + switch (state) { + case CastSocket::CONN_STATE_NONE: + return proto::CONN_STATE_NONE; + case CastSocket::CONN_STATE_TCP_CONNECT: + return proto::CONN_STATE_TCP_CONNECT; + case CastSocket::CONN_STATE_TCP_CONNECT_COMPLETE: + return proto::CONN_STATE_TCP_CONNECT_COMPLETE; + case CastSocket::CONN_STATE_SSL_CONNECT: + return proto::CONN_STATE_SSL_CONNECT; + case CastSocket::CONN_STATE_SSL_CONNECT_COMPLETE: + return proto::CONN_STATE_SSL_CONNECT_COMPLETE; + case CastSocket::CONN_STATE_AUTH_CHALLENGE_SEND: + return proto::CONN_STATE_AUTH_CHALLENGE_SEND; + case CastSocket::CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE: + return proto::CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE; + case CastSocket::CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE: + return proto::CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE; + default: + NOTREACHED(); + return proto::CONN_STATE_NONE; + } +} + +proto::ReadState ReadStateToProto(CastSocket::ReadState state) { + switch (state) { + case CastSocket::READ_STATE_NONE: + return proto::READ_STATE_NONE; + case CastSocket::READ_STATE_READ: + return proto::READ_STATE_READ; + case CastSocket::READ_STATE_READ_COMPLETE: + return proto::READ_STATE_READ_COMPLETE; + case CastSocket::READ_STATE_DO_CALLBACK: + return proto::READ_STATE_DO_CALLBACK; + case CastSocket::READ_STATE_ERROR: + return proto::READ_STATE_ERROR; + default: + NOTREACHED(); + return proto::READ_STATE_NONE; + } +} + +proto::WriteState WriteStateToProto(CastSocket::WriteState state) { + switch (state) { + case CastSocket::WRITE_STATE_NONE: + return proto::WRITE_STATE_NONE; + case CastSocket::WRITE_STATE_WRITE: + return proto::WRITE_STATE_WRITE; + case CastSocket::WRITE_STATE_WRITE_COMPLETE: + return proto::WRITE_STATE_WRITE_COMPLETE; + case CastSocket::WRITE_STATE_DO_CALLBACK: + return proto::WRITE_STATE_DO_CALLBACK; + case CastSocket::WRITE_STATE_ERROR: + return proto::WRITE_STATE_ERROR; + default: + NOTREACHED(); + return proto::WRITE_STATE_NONE; + } +} + +proto::ErrorState ErrorStateToProto(ChannelError state) { + switch (state) { + case CHANNEL_ERROR_NONE: + return proto::CHANNEL_ERROR_NONE; + case CHANNEL_ERROR_CHANNEL_NOT_OPEN: + return proto::CHANNEL_ERROR_CHANNEL_NOT_OPEN; + case CHANNEL_ERROR_AUTHENTICATION_ERROR: + return proto::CHANNEL_ERROR_AUTHENTICATION_ERROR; + case CHANNEL_ERROR_CONNECT_ERROR: + return proto::CHANNEL_ERROR_CONNECT_ERROR; + case CHANNEL_ERROR_SOCKET_ERROR: + return proto::CHANNEL_ERROR_SOCKET_ERROR; + case CHANNEL_ERROR_TRANSPORT_ERROR: + return proto::CHANNEL_ERROR_TRANSPORT_ERROR; + case CHANNEL_ERROR_INVALID_MESSAGE: + return proto::CHANNEL_ERROR_INVALID_MESSAGE; + case CHANNEL_ERROR_INVALID_CHANNEL_ID: + return proto::CHANNEL_ERROR_INVALID_CHANNEL_ID; + case CHANNEL_ERROR_CONNECT_TIMEOUT: + return proto::CHANNEL_ERROR_CONNECT_TIMEOUT; + case CHANNEL_ERROR_UNKNOWN: + return proto::CHANNEL_ERROR_UNKNOWN; + default: + NOTREACHED(); + return proto::CHANNEL_ERROR_NONE; + } +} + +} // namespace + CastSocket::CastSocket(const std::string& owner_extension_id, const net::IPEndPoint& ip_endpoint, ChannelAuthType channel_auth, CastSocket::Delegate* delegate, net::NetLog* net_log, - const base::TimeDelta& timeout) : - ApiResource(owner_extension_id), - channel_id_(0), - ip_endpoint_(ip_endpoint), - channel_auth_(channel_auth), - delegate_(delegate), - current_message_size_(0), - current_message_(new CastMessage()), - net_log_(net_log), - connect_timeout_(timeout), - connect_timeout_timer_(new base::OneShotTimer), - is_canceled_(false), - connect_state_(CONN_STATE_NONE), - write_state_(WRITE_STATE_NONE), - read_state_(READ_STATE_NONE), - error_state_(CHANNEL_ERROR_NONE), - ready_state_(READY_STATE_NONE) { + const base::TimeDelta& timeout, + const scoped_refptr& logger) + : ApiResource(owner_extension_id), + channel_id_(0), + ip_endpoint_(ip_endpoint), + channel_auth_(channel_auth), + delegate_(delegate), + current_message_size_(0), + current_message_(new CastMessage()), + net_log_(net_log), + logger_(logger), + connect_timeout_(timeout), + connect_timeout_timer_(new base::OneShotTimer), + is_canceled_(false), + connect_state_(CONN_STATE_NONE), + write_state_(WRITE_STATE_NONE), + read_state_(READ_STATE_NONE), + error_state_(CHANNEL_ERROR_NONE), + ready_state_(READY_STATE_NONE) { DCHECK(net_log_); DCHECK(channel_auth_ == CHANNEL_AUTH_TYPE_SSL || channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED); @@ -132,6 +248,7 @@ scoped_ptr CastSocket::CreateSslSocket( cert_and_status.cert_status = net::CERT_STATUS_AUTHORITY_INVALID; cert_and_status.der_cert = peer_cert_; ssl_config.allowed_bad_certs.push_back(cert_and_status); + logger_->LogSocketEvent(channel_id_, proto::SSL_CERT_WHITELISTED); } cert_verifier_.reset(net::CertVerifier::CreateDefault()); @@ -157,32 +274,44 @@ bool CastSocket::ExtractPeerCert(std::string* cert) { net::SSLInfo ssl_info; if (!socket_->GetSSLInfo(&ssl_info) || !ssl_info.cert.get()) return false; + + logger_->LogSocketEvent(channel_id_, proto::SSL_INFO_OBTAINED); + bool result = net::X509Certificate::GetDEREncoded( ssl_info.cert->os_cert_handle(), cert); if (result) VLOG_WITH_CONNECTION(1) << "Successfully extracted peer certificate: " << *cert; + + logger_->LogSocketEventWithRv( + channel_id_, proto::DER_ENCODED_CERT_OBTAIN, result ? 1 : 0); return result; } bool CastSocket::VerifyChallengeReply() { - return AuthenticateChallengeReply(*challenge_reply_, peer_cert_); + AuthResult result = AuthenticateChallengeReply(*challenge_reply_, peer_cert_); + logger_->LogSocketChallengeReplyEvent(channel_id_, result); + return result.success(); } void CastSocket::Connect(const net::CompletionCallback& callback) { DCHECK(CalledOnValidThread()); VLOG_WITH_CONNECTION(1) << "Connect readyState = " << ready_state_; if (ready_state_ != READY_STATE_NONE) { + logger_->LogSocketEventWithDetails( + channel_id_, proto::CONNECT_FAILED, "ReadyState not NONE"); callback.Run(net::ERR_CONNECTION_FAILED); return; } - ready_state_ = READY_STATE_CONNECTING; + connect_callback_ = callback; - connect_state_ = CONN_STATE_TCP_CONNECT; + SetReadyState(READY_STATE_CONNECTING); + SetConnectState(CONN_STATE_TCP_CONNECT); + if (connect_timeout_.InMicroseconds() > 0) { DCHECK(connect_timeout_callback_.IsCancelled()); - connect_timeout_callback_.Reset(base::Bind(&CastSocket::CancelConnect, - base::Unretained(this))); + connect_timeout_callback_.Reset( + base::Bind(&CastSocket::OnConnectTimeout, base::Unretained(this))); GetTimer()->Start(FROM_HERE, connect_timeout_, connect_timeout_callback_.callback()); @@ -200,10 +329,11 @@ void CastSocket::PostTaskToStartConnectLoop(int result) { connect_loop_callback_.callback()); } -void CastSocket::CancelConnect() { +void CastSocket::OnConnectTimeout() { DCHECK(CalledOnValidThread()); // Stop all pending connection setup tasks and report back to the client. is_canceled_ = true; + logger_->LogSocketEvent(channel_id_, proto::CONNECT_TIMED_OUT); VLOG_WITH_CONNECTION(1) << "Timeout while establishing a connection."; DoConnectCallback(net::ERR_TIMED_OUT); } @@ -260,6 +390,13 @@ void CastSocket::DoConnectLoop(int result) { // a. A network operation is pending, OR // b. The Do* method called did not change state + // No state change occurred in do-while loop above. This means state has + // transitioned to NONE. + if (connect_state_ == CONN_STATE_NONE) { + logger_->LogSocketConnectState(channel_id_, + ConnectStateToProto(connect_state_)); + } + // Connect loop is finished: if there is no pending IO invoke the callback. if (rv != net::ERR_IO_PENDING) { GetTimer()->Stop(); @@ -270,10 +407,13 @@ void CastSocket::DoConnectLoop(int result) { int CastSocket::DoTcpConnect() { DCHECK(connect_loop_callback_.IsCancelled()); VLOG_WITH_CONNECTION(1) << "DoTcpConnect"; - connect_state_ = CONN_STATE_TCP_CONNECT_COMPLETE; + SetConnectState(CONN_STATE_TCP_CONNECT_COMPLETE); tcp_socket_ = CreateTcpSocket(); - return tcp_socket_->Connect( + + int rv = tcp_socket_->Connect( base::Bind(&CastSocket::DoConnectLoop, base::Unretained(this))); + logger_->LogSocketEventWithRv(channel_id_, proto::TCP_SOCKET_CONNECT, rv); + return rv; } int CastSocket::DoTcpConnectComplete(int result) { @@ -282,7 +422,9 @@ int CastSocket::DoTcpConnectComplete(int result) { // Enable TCP protocol-level keep-alive. bool result = tcp_socket_->SetKeepAlive(true, kTcpKeepAliveDelaySecs); LOG_IF(WARNING, !result) << "Failed to SetKeepAlive."; - connect_state_ = CONN_STATE_SSL_CONNECT; + logger_->LogSocketEventWithRv( + channel_id_, proto::TCP_SOCKET_SET_KEEP_ALIVE, result ? 1 : 0); + SetConnectState(CONN_STATE_SSL_CONNECT); } return result; } @@ -290,27 +432,31 @@ int CastSocket::DoTcpConnectComplete(int result) { int CastSocket::DoSslConnect() { DCHECK(connect_loop_callback_.IsCancelled()); VLOG_WITH_CONNECTION(1) << "DoSslConnect"; - connect_state_ = CONN_STATE_SSL_CONNECT_COMPLETE; + SetConnectState(CONN_STATE_SSL_CONNECT_COMPLETE); socket_ = CreateSslSocket(tcp_socket_.PassAs()); - return socket_->Connect( + + int rv = socket_->Connect( base::Bind(&CastSocket::DoConnectLoop, base::Unretained(this))); + logger_->LogSocketEventWithRv(channel_id_, proto::SSL_SOCKET_CONNECT, rv); + return rv; } int CastSocket::DoSslConnectComplete(int result) { VLOG_WITH_CONNECTION(1) << "DoSslConnectComplete: " << result; if (result == net::ERR_CERT_AUTHORITY_INVALID && peer_cert_.empty() && ExtractPeerCert(&peer_cert_)) { - connect_state_ = CONN_STATE_TCP_CONNECT; + SetConnectState(CONN_STATE_TCP_CONNECT); } else if (result == net::OK && channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED) { - connect_state_ = CONN_STATE_AUTH_CHALLENGE_SEND; + SetConnectState(CONN_STATE_AUTH_CHALLENGE_SEND); } return result; } int CastSocket::DoAuthChallengeSend() { VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSend"; - connect_state_ = CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE; + SetConnectState(CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE); + CastMessage challenge_message; CreateAuthChallengeMessage(&challenge_message); VLOG_WITH_CONNECTION(1) << "Sending challenge: " @@ -344,7 +490,8 @@ int CastSocket::DoAuthChallengeSendComplete(int result) { VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSendComplete: " << result; if (result < 0) return result; - connect_state_ = CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE; + SetConnectState(CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE); + // Post a task to start read loop so that DoReadLoop is not nested inside // DoConnectLoop. This is not strictly necessary but keeps the read loop // code decoupled from connect loop code. @@ -364,17 +511,20 @@ int CastSocket::DoAuthChallengeReplyComplete(int result) { } void CastSocket::DoConnectCallback(int result) { - ready_state_ = (result == net::OK) ? READY_STATE_OPEN : READY_STATE_CLOSED; + SetReadyState((result == net::OK) ? READY_STATE_OPEN : READY_STATE_CLOSED); if (result == net::OK) { - error_state_ = CHANNEL_ERROR_NONE; + SetErrorState(CHANNEL_ERROR_NONE); PostTaskToStartReadLoop(); + VLOG_WITH_CONNECTION(1) << "Calling Connect_Callback"; + base::ResetAndReturn(&connect_callback_).Run(result); + return; } else if (result == net::ERR_TIMED_OUT) { - error_state_ = CHANNEL_ERROR_CONNECT_TIMEOUT; + SetErrorState(CHANNEL_ERROR_CONNECT_TIMEOUT); } else { - error_state_ = CHANNEL_ERROR_CONNECT_ERROR; + SetErrorState(CHANNEL_ERROR_CONNECT_ERROR); } - VLOG_WITH_CONNECTION(1) << "Calling Connect_Callback"; - base::ResetAndReturn(&connect_callback_).Run(result); + // Calls the connect callback. + CloseWithError(); } void CastSocket::Close(const net::CompletionCallback& callback) { @@ -391,6 +541,7 @@ void CastSocket::CloseInternal() { if (ready_state_ == READY_STATE_CLOSED) { return; } + VLOG_WITH_CONNECTION(1) << "Close ReadyState = " << ready_state_; tcp_socket_.reset(); socket_.reset(); @@ -404,7 +555,8 @@ void CastSocket::CloseInternal() { send_auth_challenge_callback_.Cancel(); read_loop_callback_.Cancel(); connect_timeout_callback_.Cancel(); - ready_state_ = READY_STATE_CLOSED; + SetReadyState(READY_STATE_CLOSED); + logger_->LogSocketEvent(channel_id_, proto::SOCKET_CLOSED); } void CastSocket::RunPendingCallbacksOnClose() { @@ -424,11 +576,19 @@ void CastSocket::SendMessage(const MessageInfo& message, const net::CompletionCallback& callback) { DCHECK(CalledOnValidThread()); if (ready_state_ != READY_STATE_OPEN) { + logger_->LogSocketEventForMessage(channel_id_, + proto::SEND_MESSAGE_FAILED, + message.namespace_, + "Ready state not OPEN"); callback.Run(net::ERR_FAILED); return; } CastMessage message_proto; if (!MessageInfoToCastMessage(message, &message_proto)) { + logger_->LogSocketEventForMessage(channel_id_, + proto::SEND_MESSAGE_FAILED, + message.namespace_, + "Failed to convert to CastMessage"); callback.Run(net::ERR_FAILED); return; } @@ -440,13 +600,22 @@ void CastSocket::SendCastMessageInternal( const net::CompletionCallback& callback) { WriteRequest write_request(callback); if (!write_request.SetContent(message)) { + logger_->LogSocketEventForMessage(channel_id_, + proto::SEND_MESSAGE_FAILED, + message.namespace_(), + "SetContent failed"); callback.Run(net::ERR_FAILED); return; } write_queue_.push(write_request); + logger_->LogSocketEventForMessage( + channel_id_, + proto::MESSAGE_ENQUEUED, + message.namespace_(), + base::StringPrintf("Queue size: %" PRIuS, write_queue_.size())); if (write_state_ == WRITE_STATE_NONE) { - write_state_ = WRITE_STATE_WRITE; + SetWriteState(WRITE_STATE_WRITE); DoWriteLoop(net::OK); } } @@ -456,7 +625,7 @@ void CastSocket::DoWriteLoop(int result) { VLOG_WITH_CONNECTION(1) << "DoWriteLoop queue size: " << write_queue_.size(); if (write_queue_.empty()) { - write_state_ = WRITE_STATE_NONE; + SetWriteState(WRITE_STATE_NONE); return; } @@ -489,14 +658,20 @@ void CastSocket::DoWriteLoop(int result) { rv != net::ERR_IO_PENDING && write_state_ != WRITE_STATE_NONE); + // No state change occurred in do-while loop above. This means state has + // transitioned to NONE. + if (write_state_ == WRITE_STATE_NONE) { + logger_->LogSocketWriteState(channel_id_, WriteStateToProto(write_state_)); + } + // If write loop is done because the queue is empty then set write // state to NONE if (write_queue_.empty()) - write_state_ = WRITE_STATE_NONE; + SetWriteState(WRITE_STATE_NONE); // Write loop is done - if the result is ERR_FAILED then close with error. if (rv == net::ERR_FAILED) - CloseWithError(error_state_); + CloseWithError(); } int CastSocket::DoWrite() { @@ -507,18 +682,22 @@ int CastSocket::DoWrite() { << request.io_buffer->size() << " bytes_written " << request.io_buffer->BytesConsumed(); - write_state_ = WRITE_STATE_WRITE_COMPLETE; - return socket_->Write( + SetWriteState(WRITE_STATE_WRITE_COMPLETE); + + int rv = socket_->Write( request.io_buffer.get(), request.io_buffer->BytesRemaining(), base::Bind(&CastSocket::DoWriteLoop, base::Unretained(this))); + logger_->LogSocketEventWithRv(channel_id_, proto::SOCKET_WRITE, rv); + + return rv; } int CastSocket::DoWriteComplete(int result) { DCHECK(!write_queue_.empty()); if (result <= 0) { // NOTE that 0 also indicates an error - error_state_ = CHANNEL_ERROR_SOCKET_ERROR; - write_state_ = WRITE_STATE_ERROR; + SetErrorState(CHANNEL_ERROR_SOCKET_ERROR); + SetWriteState(WRITE_STATE_ERROR); return result == 0 ? net::ERR_FAILED : result; } @@ -527,18 +706,25 @@ int CastSocket::DoWriteComplete(int result) { scoped_refptr io_buffer = request.io_buffer; io_buffer->DidConsume(result); if (io_buffer->BytesRemaining() == 0) // Message fully sent - write_state_ = WRITE_STATE_DO_CALLBACK; + SetWriteState(WRITE_STATE_DO_CALLBACK); else - write_state_ = WRITE_STATE_WRITE; + SetWriteState(WRITE_STATE_WRITE); return net::OK; } int CastSocket::DoWriteCallback() { DCHECK(!write_queue_.empty()); - write_state_ = WRITE_STATE_WRITE; + + SetWriteState(WRITE_STATE_WRITE); + WriteRequest& request = write_queue_.front(); int bytes_consumed = request.io_buffer->BytesConsumed(); + logger_->LogSocketEventForMessage( + channel_id_, + proto::MESSAGE_WRITTEN, + request.message_namespace, + base::StringPrintf("Bytes: %d", bytes_consumed)); request.callback.Run(bytes_consumed); write_queue_.pop(); return net::OK; @@ -580,7 +766,7 @@ void CastSocket::StartReadLoop() { read_loop_callback_.Cancel(); // Read loop would have already been started if read state is not NONE if (read_state_ == READ_STATE_NONE) { - read_state_ = READ_STATE_READ; + SetReadState(READ_STATE_READ); DoReadLoop(net::OK); } } @@ -616,21 +802,28 @@ void CastSocket::DoReadLoop(int result) { } } while (rv != net::ERR_IO_PENDING && read_state_ != READ_STATE_NONE); + // No state change occurred in do-while loop above. This means state has + // transitioned to NONE. + if (read_state_ == READ_STATE_NONE) { + logger_->LogSocketReadState(channel_id_, ReadStateToProto(read_state_)); + } + if (rv == net::ERR_FAILED) { if (ready_state_ == READY_STATE_CONNECTING) { - // Read errors during the handshake should notify the caller via - // the connect callback, rather than the message event delegate. + // Read errors during the handshake should notify the caller via the + // connect callback. This will also send error status via the OnError + // delegate. PostTaskToStartConnectLoop(net::ERR_FAILED); } else { - // Connection is already established. - // Close and send error status via the message event delegate. - CloseWithError(error_state_); + // Connection is already established. Close and send error status via the + // OnError delegate. + CloseWithError(); } } } int CastSocket::DoRead() { - read_state_ = READ_STATE_READ_COMPLETE; + SetReadState(READ_STATE_READ_COMPLETE); // Figure out whether to read header or body, and the remaining bytes. uint32 num_bytes_to_read = 0; if (header_read_buffer_->RemainingCapacity() > 0) { @@ -646,10 +839,13 @@ int CastSocket::DoRead() { CHECK_GT(num_bytes_to_read, 0U); // Read up to num_bytes_to_read into |current_read_buffer_|. - return socket_->Read( + int rv = socket_->Read( current_read_buffer_.get(), num_bytes_to_read, base::Bind(&CastSocket::DoReadLoop, base::Unretained(this))); + logger_->LogSocketEventWithRv(channel_id_, proto::SOCKET_READ, rv); + + return rv; } int CastSocket::DoReadComplete(int result) { @@ -659,8 +855,8 @@ int CastSocket::DoReadComplete(int result) { << " body offset = " << body_read_buffer_->offset(); if (result <= 0) { // 0 means EOF: the peer closed the socket VLOG_WITH_CONNECTION(1) << "Read error, peer closed the socket"; - error_state_ = CHANNEL_ERROR_SOCKET_ERROR; - read_state_ = READ_STATE_ERROR; + SetErrorState(CHANNEL_ERROR_SOCKET_ERROR); + SetReadState(READ_STATE_ERROR); return result == 0 ? net::ERR_FAILED : result; } @@ -668,41 +864,55 @@ int CastSocket::DoReadComplete(int result) { CHECK_LE(current_read_buffer_->offset() + result, current_read_buffer_->capacity()); current_read_buffer_->set_offset(current_read_buffer_->offset() + result); - read_state_ = READ_STATE_READ; if (current_read_buffer_.get() == header_read_buffer_.get() && current_read_buffer_->RemainingCapacity() == 0) { // A full header is read, process the contents. if (!ProcessHeader()) { - error_state_ = cast_channel::CHANNEL_ERROR_INVALID_MESSAGE; - read_state_ = READ_STATE_ERROR; + SetErrorState(CHANNEL_ERROR_INVALID_MESSAGE); + SetReadState(READ_STATE_ERROR); + } else { + // Processed header, now read the body. + SetReadState(READ_STATE_READ); } } else if (current_read_buffer_.get() == body_read_buffer_.get() && static_cast(current_read_buffer_->offset()) == current_message_size_) { + // Store a copy of current_message_size_ since it will be reset by + // ProcessBody(). + uint32 message_size = current_message_size_; // Full body is read, process the contents. if (ProcessBody()) { - read_state_ = READ_STATE_DO_CALLBACK; + logger_->LogSocketEventForMessage( + channel_id_, + proto::MESSAGE_READ, + current_message_->namespace_(), + base::StringPrintf("Message size: %u", message_size)); + SetReadState(READ_STATE_DO_CALLBACK); } else { - error_state_ = cast_channel::CHANNEL_ERROR_INVALID_MESSAGE; - read_state_ = READ_STATE_ERROR; + SetErrorState(CHANNEL_ERROR_INVALID_MESSAGE); + SetReadState(READ_STATE_ERROR); } + } else { + // Have not received full header or full body yet; keep reading. + SetReadState(READ_STATE_READ); } return net::OK; } int CastSocket::DoReadCallback() { - read_state_ = READ_STATE_READ; + SetReadState(READ_STATE_READ); const CastMessage& message = *current_message_; if (ready_state_ == READY_STATE_CONNECTING) { if (IsAuthMessage(message)) { challenge_reply_.reset(new CastMessage(message)); + logger_->LogSocketEvent(channel_id_, proto::RECEIVED_CHALLENGE_REPLY); PostTaskToStartConnectLoop(net::OK); return net::OK; } else { - // Expected an auth message, got something else instead. Handle as error. - read_state_ = READ_STATE_ERROR; + SetReadState(READ_STATE_ERROR); + SetErrorState(CHANNEL_ERROR_INVALID_MESSAGE); return net::ERR_INVALID_RESPONSE; } } @@ -710,11 +920,18 @@ int CastSocket::DoReadCallback() { MessageInfo message_info; if (!CastMessageToMessageInfo(message, &message_info)) { current_message_->Clear(); - read_state_ = READ_STATE_ERROR; + SetReadState(READ_STATE_ERROR); + SetErrorState(CHANNEL_ERROR_INVALID_MESSAGE); return net::ERR_INVALID_RESPONSE; } + + logger_->LogSocketEventForMessage(channel_id_, + proto::NOTIFY_ON_MESSAGE, + message.namespace_(), + std::string()); delegate_->OnMessage(this, message_info); current_message_->Clear(); + return net::OK; } @@ -767,13 +984,14 @@ bool CastSocket::Serialize(const CastMessage& message_proto, return true; } -void CastSocket::CloseWithError(ChannelError error) { +void CastSocket::CloseWithError() { DCHECK(CalledOnValidThread()); CloseInternal(); - error_state_ = error; RunPendingCallbacksOnClose(); - if (delegate_) - delegate_->OnError(this, error); + if (delegate_) { + logger_->LogSocketEvent(channel_id_, proto::NOTIFY_ON_ERROR); + delegate_->OnError(this, error_state_, logger_->GetLastErrors(channel_id_)); + } } std::string CastSocket::CastUrl() const { @@ -789,6 +1007,42 @@ base::Timer* CastSocket::GetTimer() { return connect_timeout_timer_.get(); } +void CastSocket::SetConnectState(ConnectionState connect_state) { + if (connect_state_ != connect_state) { + connect_state_ = connect_state; + logger_->LogSocketConnectState(channel_id_, + ConnectStateToProto(connect_state_)); + } +} + +void CastSocket::SetReadyState(ReadyState ready_state) { + if (ready_state_ != ready_state) { + ready_state_ = ready_state; + logger_->LogSocketReadyState(channel_id_, ReadyStateToProto(ready_state_)); + } +} + +void CastSocket::SetErrorState(ChannelError error_state) { + if (error_state_ != error_state) { + error_state_ = error_state; + logger_->LogSocketErrorState(channel_id_, ErrorStateToProto(error_state_)); + } +} + +void CastSocket::SetReadState(ReadState read_state) { + if (read_state_ != read_state) { + read_state_ = read_state; + logger_->LogSocketReadState(channel_id_, ReadStateToProto(read_state_)); + } +} + +void CastSocket::SetWriteState(WriteState write_state) { + if (write_state_ != write_state) { + write_state_ = write_state; + logger_->LogSocketWriteState(channel_id_, WriteStateToProto(write_state_)); + } +} + CastSocket::MessageHeader::MessageHeader() : message_size(0) { } void CastSocket::MessageHeader::SetMessageSize(size_t size) { @@ -833,6 +1087,7 @@ bool CastSocket::WriteRequest::SetContent(const CastMessage& message_proto) { std::string message_data; if (!Serialize(message_proto, &message_data)) return false; + message_namespace = message_proto.namespace_(); io_buffer = new net::DrainableIOBuffer(new net::StringIOBuffer(message_data), message_data.size()); return true; diff --git a/extensions/browser/api/cast_channel/cast_socket.h b/extensions/browser/api/cast_channel/cast_socket.h index 5ff17147a3d4d..1de7ac3717430 100644 --- a/extensions/browser/api/cast_channel/cast_socket.h +++ b/extensions/browser/api/cast_channel/cast_socket.h @@ -36,6 +36,8 @@ namespace core_api { namespace cast_channel { class CastMessage; +class Logger; +struct LastErrors; // This class implements a channel between Chrome and a Cast device using a TCP // socket with SSL. The channel may authenticate that the receiver is a genuine @@ -50,8 +52,11 @@ class CastSocket : public ApiResource { // or in the callback to Close(). class Delegate { public: - // An error occurred on the channel. - virtual void OnError(const CastSocket* socket, ChannelError error) = 0; + // An error occurred on the channel. |last_errors| contains the last errors + // logged for the channel from the implementation. + virtual void OnError(const CastSocket* socket, + ChannelError error_state, + const LastErrors& last_errors) = 0; // A message was received on the channel. virtual void OnMessage(const CastSocket* socket, const MessageInfo& message) = 0; @@ -68,7 +73,8 @@ class CastSocket : public ApiResource { ChannelAuthType channel_auth, CastSocket::Delegate* delegate, net::NetLog* net_log, - const base::TimeDelta& connect_timeout); + const base::TimeDelta& connect_timeout, + const scoped_refptr& logger); // Ensures that the socket is closed. virtual ~CastSocket(); @@ -119,6 +125,36 @@ class CastSocket : public ApiResource { // It is fine to delete the CastSocket object in |callback|. virtual void Close(const net::CompletionCallback& callback); + // Internal connection states. + enum ConnectionState { + CONN_STATE_NONE, + CONN_STATE_TCP_CONNECT, + CONN_STATE_TCP_CONNECT_COMPLETE, + CONN_STATE_SSL_CONNECT, + CONN_STATE_SSL_CONNECT_COMPLETE, + CONN_STATE_AUTH_CHALLENGE_SEND, + CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE, + CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE, + }; + + // Internal write states. + enum WriteState { + WRITE_STATE_NONE, + WRITE_STATE_WRITE, + WRITE_STATE_WRITE_COMPLETE, + WRITE_STATE_DO_CALLBACK, + WRITE_STATE_ERROR, + }; + + // Internal read states. + enum ReadState { + READ_STATE_NONE, + READ_STATE_READ, + READ_STATE_READ_COMPLETE, + READ_STATE_DO_CALLBACK, + READ_STATE_ERROR, + }; + protected: // Message header struct. If fields are added, be sure to update // header_size(). Protected to allow use of *_size() methods in unit tests. @@ -150,36 +186,6 @@ class CastSocket : public ApiResource { static const char* service_name() { return "CastSocketManager"; } - // Internal connection states. - enum ConnectionState { - CONN_STATE_NONE, - CONN_STATE_TCP_CONNECT, - CONN_STATE_TCP_CONNECT_COMPLETE, - CONN_STATE_SSL_CONNECT, - CONN_STATE_SSL_CONNECT_COMPLETE, - CONN_STATE_AUTH_CHALLENGE_SEND, - CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE, - CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE, - }; - - // Internal write states. - enum WriteState { - WRITE_STATE_NONE, - WRITE_STATE_WRITE, - WRITE_STATE_WRITE_COMPLETE, - WRITE_STATE_DO_CALLBACK, - WRITE_STATE_ERROR, - }; - - // Internal read states. - enum ReadState { - READ_STATE_NONE, - READ_STATE_READ, - READ_STATE_READ_COMPLETE, - READ_STATE_DO_CALLBACK, - READ_STATE_ERROR, - }; - // Creates an instance of TCPClientSocket. virtual scoped_ptr CreateTcpSocket(); // Creates an instance of SSLClientSocket with the given underlying |socket|. @@ -196,7 +202,7 @@ class CastSocket : public ApiResource { // Invoked by a cancelable closure when connection setup time // exceeds the interval specified at |connect_timeout|. - void CancelConnect(); + void OnConnectTimeout(); ///////////////////////////////////////////////////////////////////////////// // Following methods work together to implement the following flow: @@ -266,9 +272,8 @@ class CastSocket : public ApiResource { // Parses the contents of body_read_buffer_ and sets current_message_ to // the message received. bool ProcessBody(); - // Closes the socket, sets |error_state_| to |error| and signals the - // delegate that |error| has occurred. - void CloseWithError(ChannelError error); + // Closes socket, signaling the delegate that |error| has occurred. + void CloseWithError(); // Frees resources and cancels pending callbacks. |ready_state_| will be set // READY_STATE_CLOSED on completion. A no-op if |ready_state_| is already // READY_STATE_CLOSED. @@ -276,7 +281,6 @@ class CastSocket : public ApiResource { // Runs pending callbacks that are passed into us to notify API clients that // pending operations will fail because the socket has been closed. void RunPendingCallbacksOnClose(); - // Serializes the content of message_proto (with a header) to |message_data|. static bool Serialize(const CastMessage& message_proto, std::string* message_data); @@ -285,6 +289,12 @@ class CastSocket : public ApiResource { virtual base::Timer* GetTimer(); + void SetConnectState(ConnectionState connect_state); + void SetReadyState(ReadyState ready_state); + void SetErrorState(ChannelError error_state); + void SetReadState(ReadState read_state); + void SetWriteState(WriteState write_state); + base::ThreadChecker thread_checker_; // The id of the channel. @@ -313,6 +323,9 @@ class CastSocket : public ApiResource { // The NetLog source for this service. net::NetLog::Source net_log_source_; + // Logger used to track multiple CastSockets. Does NOT own this object. + scoped_refptr logger_; + // CertVerifier is owned by us but should be deleted AFTER SSLClientSocket // since in some cases the destructor of SSLClientSocket may call a method // to cancel a cert verification request. @@ -373,6 +386,7 @@ class CastSocket : public ApiResource { bool SetContent(const CastMessage& message_proto); net::CompletionCallback callback; + std::string message_namespace; scoped_refptr io_buffer; }; // Queue of pending writes. The message at the front of the queue is the one diff --git a/extensions/browser/api/cast_channel/cast_socket_unittest.cc b/extensions/browser/api/cast_channel/cast_socket_unittest.cc index 7a2b724ac6ae0..4090426cf0604 100644 --- a/extensions/browser/api/cast_channel/cast_socket_unittest.cc +++ b/extensions/browser/api/cast_channel/cast_socket_unittest.cc @@ -9,9 +9,11 @@ #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "base/sys_byteorder.h" +#include "base/test/simple_test_tick_clock.h" #include "base/timer/mock_timer.h" #include "extensions/browser/api/cast_channel/cast_channel.pb.h" #include "extensions/browser/api/cast_channel/cast_message_util.h" +#include "extensions/browser/api/cast_channel/logger.h" #include "net/base/address_list.h" #include "net/base/capturing_net_log.h" #include "net/base/net_errors.h" @@ -71,8 +73,10 @@ static void CreateBinaryMessage(const std::string& namespace_, class MockCastSocketDelegate : public CastSocket::Delegate { public: - MOCK_METHOD2(OnError, void(const CastSocket* socket, - ChannelError error)); + MOCK_METHOD3(OnError, + void(const CastSocket* socket, + ChannelError error, + const LastErrors& last_errors)); MOCK_METHOD2(OnMessage, void(const CastSocket* socket, const MessageInfo& message)); }; @@ -143,32 +147,38 @@ class CompleteHandler { class TestCastSocket : public CastSocket { public: - static scoped_ptr Create( - MockCastSocketDelegate* delegate) { - return scoped_ptr( - new TestCastSocket(delegate, CreateIPEndPoint(), - CHANNEL_AUTH_TYPE_SSL, - kDistantTimeoutMillis)); + static scoped_ptr Create(MockCastSocketDelegate* delegate, + Logger* logger) { + return scoped_ptr(new TestCastSocket(delegate, + CreateIPEndPoint(), + CHANNEL_AUTH_TYPE_SSL, + kDistantTimeoutMillis, + logger)); } static scoped_ptr CreateSecure( - MockCastSocketDelegate* delegate) { + MockCastSocketDelegate* delegate, + Logger* logger) { return scoped_ptr( - new TestCastSocket(delegate, CreateIPEndPoint(), + new TestCastSocket(delegate, + CreateIPEndPoint(), CHANNEL_AUTH_TYPE_SSL_VERIFIED, - kDistantTimeoutMillis)); + kDistantTimeoutMillis, + logger)); } explicit TestCastSocket(MockCastSocketDelegate* delegate, const net::IPEndPoint& ip_endpoint, ChannelAuthType channel_auth, - int64 timeout_ms) + int64 timeout_ms, + Logger* logger) : CastSocket("abcdefg", ip_endpoint, channel_auth, delegate, &capturing_net_log_, - base::TimeDelta::FromMilliseconds(timeout_ms)), + base::TimeDelta::FromMilliseconds(timeout_ms), + logger), ip_(ip_endpoint), connect_index_(0), extract_cert_result_(true), @@ -334,7 +344,10 @@ class TestCastSocket : public CastSocket { class CastSocketTest : public testing::Test { public: - CastSocketTest() {} + CastSocketTest() + : logger_(new Logger( + scoped_ptr(new base::SimpleTestTickClock), + base::TimeTicks())) {} virtual ~CastSocketTest() {} virtual void SetUp() OVERRIDE { @@ -373,11 +386,11 @@ class CastSocketTest : public testing::Test { } void CreateCastSocket() { - socket_ = TestCastSocket::Create(&mock_delegate_); + socket_ = TestCastSocket::Create(&mock_delegate_, logger_); } void CreateCastSocketSecure() { - socket_ = TestCastSocket::CreateSecure(&mock_delegate_); + socket_ = TestCastSocket::CreateSecure(&mock_delegate_, logger_); } // Sets up CastSocket::Connect to succeed. @@ -403,6 +416,7 @@ class CastSocketTest : public testing::Test { base::MessageLoop message_loop_; MockCastSocketDelegate mock_delegate_; + scoped_refptr logger_; scoped_ptr socket_; CompleteHandler handler_; MessageInfo test_messages_[arraysize(kTestData)]; @@ -485,7 +499,11 @@ TEST_F(CastSocketTest, TestConnectMaxTwoAttempts) { socket_->SetupTcp2Connect(net::ASYNC, net::OK); socket_->SetupSsl2Connect(net::ASYNC, net::ERR_CERT_AUTHORITY_INVALID); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -562,7 +580,11 @@ TEST_F(CastSocketTest, TestConnectAuthMessageCorrupted) { // Guard against VerifyChallengeResult() being triggered. socket_->DisallowVerifyChallengeResult(); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -578,7 +600,11 @@ TEST_F(CastSocketTest, TestConnectTcpConnectErrorAsync) { socket_->SetupTcp1Connect(net::ASYNC, net::ERR_FAILED); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -594,7 +620,11 @@ TEST_F(CastSocketTest, TestConnectTcpConnectErrorSync) { socket_->SetupTcp1Connect(net::SYNCHRONOUS, net::ERR_FAILED); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -607,7 +637,11 @@ TEST_F(CastSocketTest, TestConnectTcpConnectErrorSync) { TEST_F(CastSocketTest, TestConnectTcpTimeoutError) { CreateCastSocketSecure(); socket_->SetupTcp1ConnectUnresponsive(); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_TIMED_OUT)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_TIMEOUT, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -630,7 +664,11 @@ TEST_F(CastSocketTest, TestConnectSslConnectErrorAsync) { socket_->SetupTcp1Connect(net::SYNCHRONOUS, net::OK); socket_->SetupSsl1Connect(net::SYNCHRONOUS, net::ERR_FAILED); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -647,7 +685,11 @@ TEST_F(CastSocketTest, TestConnectSslConnectErrorSync) { socket_->SetupTcp1Connect(net::SYNCHRONOUS, net::OK); socket_->SetupSsl1Connect(net::ASYNC, net::ERR_FAILED); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -665,7 +707,11 @@ TEST_F(CastSocketTest, TestConnectCertExtractionErrorAsync) { // Set cert extraction to fail socket_->SetExtractCertResult(false); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -683,7 +729,11 @@ TEST_F(CastSocketTest, TestConnectCertExtractionErrorSync) { // Set cert extraction to fail socket_->SetExtractCertResult(false); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CERT_AUTHORITY_INVALID)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -701,7 +751,11 @@ TEST_F(CastSocketTest, TestConnectChallengeSendError) { socket_->SetupSsl1Connect(net::SYNCHRONOUS, net::OK); socket_->AddWriteResult(net::SYNCHRONOUS, net::ERR_FAILED); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -720,7 +774,11 @@ TEST_F(CastSocketTest, TestConnectChallengeReplyReceiveError) { socket_->AddWriteResultForMessage(net::ASYNC, auth_request_); socket_->AddReadResult(net::SYNCHRONOUS, net::ERR_FAILED); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -741,7 +799,11 @@ TEST_F(CastSocketTest, TestConnectChallengeVerificationFails) { socket_->AddReadResult(net::ASYNC, net::ERR_IO_PENDING); socket_->SetVerifyChallengeResult(false); - EXPECT_CALL(handler_, OnConnectComplete(net::ERR_FAILED)); + EXPECT_CALL(handler_, OnConnectComplete(net::ERR_CONNECTION_FAILED)); + EXPECT_CALL(mock_delegate_, + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_CONNECT_ERROR, + A())); socket_->Connect(base::Bind(&CompleteHandler::OnConnectComplete, base::Unretained(&handler_))); RunPendingTasks(); @@ -903,7 +965,9 @@ TEST_F(CastSocketTest, TestWriteNetworkErrorSync) { EXPECT_CALL(handler_, OnWriteComplete(net::ERR_FAILED)); EXPECT_CALL(mock_delegate_, - OnError(socket_.get(), cast_channel::CHANNEL_ERROR_SOCKET_ERROR)); + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_SOCKET_ERROR, + A())); socket_->SendMessage(test_messages_[0], base::Bind(&CompleteHandler::OnWriteComplete, base::Unretained(&handler_))); @@ -922,7 +986,9 @@ TEST_F(CastSocketTest, TestWriteErrorAsync) { EXPECT_CALL(handler_, OnWriteComplete(net::ERR_FAILED)); EXPECT_CALL(mock_delegate_, - OnError(socket_.get(), cast_channel::CHANNEL_ERROR_SOCKET_ERROR)); + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_SOCKET_ERROR, + A())); socket_->SendMessage(test_messages_[0], base::Bind(&CompleteHandler::OnWriteComplete, base::Unretained(&handler_))); @@ -941,7 +1007,9 @@ TEST_F(CastSocketTest, TestWriteErrorZeroBytesWritten) { EXPECT_CALL(handler_, OnWriteComplete(net::ERR_FAILED)); EXPECT_CALL(mock_delegate_, - OnError(socket_.get(), cast_channel::CHANNEL_ERROR_SOCKET_ERROR)); + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_SOCKET_ERROR, + A())); socket_->SendMessage(test_messages_[0], base::Bind(&CompleteHandler::OnWriteComplete, base::Unretained(&handler_))); @@ -963,7 +1031,9 @@ TEST_F(CastSocketTest, TestWriteErrorWithMultiplePendingWritesAsync) { EXPECT_CALL(handler_, OnWriteComplete(net::ERR_SOCKET_NOT_CONNECTED)) .Times(num_writes); EXPECT_CALL(mock_delegate_, - OnError(socket_.get(), cast_channel::CHANNEL_ERROR_SOCKET_ERROR)); + OnError(socket_.get(), + cast_channel::CHANNEL_ERROR_SOCKET_ERROR, + A())); for (int i = 0; i < num_writes; i++) { socket_->SendMessage(test_messages_[i], base::Bind(&CompleteHandler::OnWriteComplete, @@ -1066,7 +1136,8 @@ TEST_F(CastSocketTest, TestReadErrorAsync) { socket_->AddReadResult(net::ASYNC, net::ERR_SOCKET_NOT_CONNECTED); EXPECT_CALL(mock_delegate_, OnError(socket_.get(), - cast_channel::CHANNEL_ERROR_SOCKET_ERROR)); + cast_channel::CHANNEL_ERROR_SOCKET_ERROR, + A())); ConnectHelper(); EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state()); @@ -1080,7 +1151,8 @@ TEST_F(CastSocketTest, TestReadErrorSync) { socket_->AddReadResult(net::SYNCHRONOUS, net::ERR_SOCKET_NOT_CONNECTED); EXPECT_CALL(mock_delegate_, OnError(socket_.get(), - cast_channel::CHANNEL_ERROR_SOCKET_ERROR)); + cast_channel::CHANNEL_ERROR_SOCKET_ERROR, + A())); ConnectHelper(); EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state()); @@ -1099,7 +1171,8 @@ TEST_F(CastSocketTest, TestReadHeaderParseError) { socket_->AddReadResult(net::SYNCHRONOUS, header, arraysize(header)); EXPECT_CALL(mock_delegate_, OnError(socket_.get(), - cast_channel::CHANNEL_ERROR_INVALID_MESSAGE)); + cast_channel::CHANNEL_ERROR_INVALID_MESSAGE, + A())); ConnectHelper(); EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state()); @@ -1119,7 +1192,8 @@ TEST_F(CastSocketTest, TestReadBodyParseError) { socket_->AddReadResult(net::SYNCHRONOUS, body, arraysize(body)); EXPECT_CALL(mock_delegate_, OnError(socket_.get(), - cast_channel::CHANNEL_ERROR_INVALID_MESSAGE)); + cast_channel::CHANNEL_ERROR_INVALID_MESSAGE, + A())); ConnectHelper(); EXPECT_EQ(cast_channel::READY_STATE_CLOSED, socket_->ready_state()); diff --git a/extensions/browser/api/cast_channel/logger.cc b/extensions/browser/api/cast_channel/logger.cc new file mode 100644 index 0000000000000..823b4c320877e --- /dev/null +++ b/extensions/browser/api/cast_channel/logger.cc @@ -0,0 +1,360 @@ +// 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 "extensions/browser/api/cast_channel/logger.h" + +#include "base/strings/string_util.h" +#include "base/time/tick_clock.h" +#include "extensions/browser/api/cast_channel/cast_auth_util.h" +#include "extensions/browser/api/cast_channel/logger_util.h" +#include "net/base/net_errors.h" +#include "third_party/zlib/zlib.h" + +namespace extensions { +namespace core_api { +namespace cast_channel { + +using net::IPEndPoint; +using proto::AggregatedSocketEvent; +using proto::EventType; +using proto::Log; +using proto::SocketEvent; + +namespace { + +const char* kInternalNamespacePrefix = "com.google.cast"; + +proto::ChallengeReplyErrorType ChallegeReplyErrorToProto( + AuthResult::ErrorType error_type) { + switch (error_type) { + case AuthResult::ERROR_NONE: + return proto::CHALLENGE_REPLY_ERROR_NONE; + case AuthResult::ERROR_PEER_CERT_EMPTY: + return proto::CHALLENGE_REPLY_ERROR_PEER_CERT_EMPTY; + case AuthResult::ERROR_WRONG_PAYLOAD_TYPE: + return proto::CHALLENGE_REPLY_ERROR_WRONG_PAYLOAD_TYPE; + case AuthResult::ERROR_NO_PAYLOAD: + return proto::CHALLENGE_REPLY_ERROR_NO_PAYLOAD; + case AuthResult::ERROR_PAYLOAD_PARSING_FAILED: + return proto::CHALLENGE_REPLY_ERROR_PAYLOAD_PARSING_FAILED; + case AuthResult::ERROR_MESSAGE_ERROR: + return proto::CHALLENGE_REPLY_ERROR_MESSAGE_ERROR; + case AuthResult::ERROR_NO_RESPONSE: + return proto::CHALLENGE_REPLY_ERROR_NO_RESPONSE; + case AuthResult::ERROR_FINGERPRINT_NOT_FOUND: + return proto::CHALLENGE_REPLY_ERROR_FINGERPRINT_NOT_FOUND; + case AuthResult::ERROR_NSS_CERT_PARSING_FAILED: + return proto::CHALLENGE_REPLY_ERROR_NSS_CERT_PARSING_FAILED; + case AuthResult::ERROR_NSS_CERT_NOT_SIGNED_BY_TRUSTED_CA: + return proto::CHALLENGE_REPLY_ERROR_NSS_CERT_NOT_SIGNED_BY_TRUSTED_CA; + case AuthResult::ERROR_NSS_CANNOT_EXTRACT_PUBLIC_KEY: + return proto::CHALLENGE_REPLY_ERROR_NSS_CANNOT_EXTRACT_PUBLIC_KEY; + case AuthResult::ERROR_NSS_SIGNED_BLOBS_MISMATCH: + return proto::CHALLENGE_REPLY_ERROR_NSS_SIGNED_BLOBS_MISMATCH; + default: + NOTREACHED(); + return proto::CHALLENGE_REPLY_ERROR_NONE; + } +} + +scoped_ptr Compress(const std::string& input, size_t* length) { + *length = 0; + z_stream stream = {0}; + int result = deflateInit2(&stream, + Z_DEFAULT_COMPRESSION, + Z_DEFLATED, + // 16 is added to produce a gzip header + trailer. + MAX_WBITS + 16, + 8, // memLevel = 8 is default. + Z_DEFAULT_STRATEGY); + DCHECK_EQ(Z_OK, result); + + size_t out_size = deflateBound(&stream, input.size()); + scoped_ptr out(new char[out_size]); + + COMPILE_ASSERT(sizeof(uint8) == sizeof(char), uint8_char_different_sizes); + + stream.next_in = reinterpret_cast(const_cast(input.data())); + stream.avail_in = input.size(); + stream.next_out = reinterpret_cast(out.get()); + stream.avail_out = out_size; + + // Do a one-shot compression. This will return Z_STREAM_END only if |output| + // is large enough to hold all compressed data. + result = deflate(&stream, Z_FINISH); + + bool success = (result == Z_STREAM_END); + + if (!success) + VLOG(2) << "deflate() failed. Result: " << result; + + result = deflateEnd(&stream); + DCHECK(result == Z_OK || result == Z_DATA_ERROR); + + if (success) + *length = out_size - stream.avail_out; + + return out.Pass(); +} + +} // namespace + +Logger::AggregatedSocketEventLog::AggregatedSocketEventLog() { +} + +Logger::AggregatedSocketEventLog::~AggregatedSocketEventLog() { +} + +Logger::Logger(scoped_ptr clock, + base::TimeTicks unix_epoch_time_ticks) + : clock_(clock.Pass()), unix_epoch_time_ticks_(unix_epoch_time_ticks) { + DCHECK(clock_); + + // Logger may not be necessarily be created on the IO thread, but logging + // happens exclusively there. + thread_checker_.DetachFromThread(); +} + +Logger::~Logger() { +} + +void Logger::LogNewSocketEvent(const CastSocket& cast_socket) { + DCHECK(thread_checker_.CalledOnValidThread()); + + int channel_id = cast_socket.id(); + SocketEvent event = CreateEvent(proto::CAST_SOCKET_CREATED); + AggregatedSocketEvent& aggregated_socket_event = + LogSocketEvent(channel_id, event); + + const net::IPAddressNumber& ip = cast_socket.ip_endpoint().address(); + aggregated_socket_event.set_endpoint_id(ip.back()); + aggregated_socket_event.set_channel_auth_type(cast_socket.channel_auth() == + CHANNEL_AUTH_TYPE_SSL + ? proto::SSL + : proto::SSL_VERIFIED); +} + +void Logger::LogSocketEvent(int channel_id, EventType event_type) { + DCHECK(thread_checker_.CalledOnValidThread()); + + LogSocketEventWithDetails(channel_id, event_type, std::string()); +} + +void Logger::LogSocketEventWithDetails(int channel_id, + EventType event_type, + const std::string& details) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(event_type); + if (!details.empty()) + event.set_details(details); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketEventWithRv(int channel_id, + EventType event_type, + int rv) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(event_type); + event.set_net_return_value(rv); + + AggregatedSocketEvent& aggregated_socket_event = + LogSocketEvent(channel_id, event); + + if ((event_type == proto::SOCKET_READ || event_type == proto::SOCKET_WRITE) && + rv > 0) { + if (event_type == proto::SOCKET_READ) { + aggregated_socket_event.set_bytes_read( + aggregated_socket_event.bytes_read() + rv); + } else { + aggregated_socket_event.set_bytes_written( + aggregated_socket_event.bytes_written() + rv); + } + } +} + +void Logger::LogSocketReadyState(int channel_id, proto::ReadyState new_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(proto::READY_STATE_CHANGED); + event.set_ready_state(new_state); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketConnectState(int channel_id, + proto::ConnectionState new_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(proto::CONNECTION_STATE_CHANGED); + event.set_connection_state(new_state); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketReadState(int channel_id, proto::ReadState new_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(proto::READ_STATE_CHANGED); + event.set_read_state(new_state); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketWriteState(int channel_id, proto::WriteState new_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(proto::WRITE_STATE_CHANGED); + event.set_write_state(new_state); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketErrorState(int channel_id, proto::ErrorState new_state) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(proto::ERROR_STATE_CHANGED); + event.set_error_state(new_state); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketEventForMessage(int channel_id, + EventType event_type, + const std::string& message_namespace, + const std::string& details) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(event_type); + if (StartsWithASCII(message_namespace, kInternalNamespacePrefix, false)) + event.set_message_namespace(message_namespace); + event.set_details(details); + + LogSocketEvent(channel_id, event); +} + +void Logger::LogSocketChallengeReplyEvent(int channel_id, + const AuthResult& auth_result) { + DCHECK(thread_checker_.CalledOnValidThread()); + + SocketEvent event = CreateEvent(proto::AUTH_CHALLENGE_REPLY); + event.set_challenge_reply_error_type( + ChallegeReplyErrorToProto(auth_result.error_type)); + if (auth_result.nss_error_code != 0) + event.set_nss_error_code(auth_result.nss_error_code); + + LogSocketEvent(channel_id, event); +} + +SocketEvent Logger::CreateEvent(EventType event_type) { + SocketEvent event; + event.set_type(event_type); + event.set_timestamp_micros(clock_->NowTicks().ToInternalValue() - + unix_epoch_time_ticks_.ToInternalValue()); + return event; +} + +AggregatedSocketEvent& Logger::LogSocketEvent(int channel_id, + const SocketEvent& socket_event) { + AggregatedSocketEventLogMap::iterator it = + aggregated_socket_events_.find(channel_id); + if (it == aggregated_socket_events_.end()) { + if (aggregated_socket_events_.size() >= kMaxSocketsToLog) { + AggregatedSocketEventLogMap::iterator erase_it = + aggregated_socket_events_.begin(); + + log_.set_num_evicted_aggregated_socket_events( + log_.num_evicted_aggregated_socket_events() + 1); + log_.set_num_evicted_socket_events( + log_.num_evicted_socket_events() + + erase_it->second->socket_events.size()); + + aggregated_socket_events_.erase(erase_it); + } + + it = aggregated_socket_events_ + .insert(std::make_pair( + channel_id, make_linked_ptr(new AggregatedSocketEventLog))) + .first; + it->second->aggregated_socket_event.set_id(channel_id); + } + + std::deque& socket_events = it->second->socket_events; + if (socket_events.size() >= kMaxEventsPerSocket) { + socket_events.pop_front(); + log_.set_num_evicted_socket_events(log_.num_evicted_socket_events() + 1); + } + + socket_events.push_back(socket_event); + + it->second->last_errors.event_type = socket_event.type(); + if (socket_event.has_net_return_value()) { + it->second->last_errors.net_return_value = socket_event.net_return_value(); + } + if (socket_event.has_challenge_reply_error_type()) { + it->second->last_errors.challenge_reply_error_type = + socket_event.challenge_reply_error_type(); + } + if (socket_event.has_nss_error_code()) + it->second->last_errors.nss_error_code = socket_event.nss_error_code(); + + return it->second->aggregated_socket_event; +} + +scoped_ptr Logger::GetLogs(size_t* length) const { + *length = 0; + + Log log; + // Copy "global" values from |log_|. Don't use |log_| directly since this + // function is const. + log.CopyFrom(log_); + + for (AggregatedSocketEventLogMap::const_iterator it = + aggregated_socket_events_.begin(); + it != aggregated_socket_events_.end(); + ++it) { + AggregatedSocketEvent* new_aggregated_socket_event = + log.add_aggregated_socket_event(); + new_aggregated_socket_event->CopyFrom(it->second->aggregated_socket_event); + + const std::deque& socket_events = it->second->socket_events; + for (std::deque::const_iterator socket_event_it = + socket_events.begin(); + socket_event_it != socket_events.end(); + ++socket_event_it) { + SocketEvent* socket_event = + new_aggregated_socket_event->add_socket_event(); + socket_event->CopyFrom(*socket_event_it); + } + } + + std::string serialized; + if (!log.SerializeToString(&serialized)) { + VLOG(2) << "Failed to serialized proto to string."; + return scoped_ptr(); + } + + return Compress(serialized, length); +} + +void Logger::Reset() { + aggregated_socket_events_.clear(); + log_.Clear(); +} + +LastErrors Logger::GetLastErrors(int channel_id) const { + AggregatedSocketEventLogMap::const_iterator it = + aggregated_socket_events_.find(channel_id); + if (it != aggregated_socket_events_.end()) { + return it->second->last_errors; + } else { + return LastErrors(); + } +} + +} // namespace cast_channel +} // namespace api +} // namespace extensions diff --git a/extensions/browser/api/cast_channel/logger.h b/extensions/browser/api/cast_channel/logger.h new file mode 100644 index 0000000000000..e7e22ac84a740 --- /dev/null +++ b/extensions/browser/api/cast_channel/logger.h @@ -0,0 +1,140 @@ +// 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 EXTENSIONS_BROWSER_API_CAST_CHANNEL_LOGGER_H_ +#define EXTENSIONS_BROWSER_API_CAST_CHANNEL_LOGGER_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "extensions/browser/api/cast_channel/cast_socket.h" +#include "extensions/browser/api/cast_channel/logger_util.h" +#include "extensions/browser/api/cast_channel/logging.pb.h" +#include "net/base/ip_endpoint.h" + +namespace base { +class TickClock; +} + +namespace extensions { +namespace core_api { +namespace cast_channel { + +struct AuthResult; + +static const int kMaxSocketsToLog = 50; +static const int kMaxEventsPerSocket = 2000; + +// Logs information of each channel and sockets and exports the log as +// a blob. Logger is done on the IO thread. +class Logger : public base::RefCounted { + public: + // |clock|: Clock used for generating timestamps for the events. Owned by + // this class. + // |unix_epoch_time_ticks|: The TimeTicks that corresponds to Unix epoch. + Logger(scoped_ptr clock, + base::TimeTicks unix_epoch_time_ticks); + + // For newly created sockets. Will create an event and log a + // CAST_SOCKET_CREATED event. + void LogNewSocketEvent(const CastSocket& cast_socket); + + void LogSocketEvent(int channel_id, proto::EventType event_type); + void LogSocketEventWithDetails(int channel_id, + proto::EventType event_type, + const std::string& details); + + // For events that involves socket / crypto operations that returns a value. + void LogSocketEventWithRv(int channel_id, + proto::EventType event_type, + int rv); + + // For *_STATE_CHANGED events. + void LogSocketReadyState(int channel_id, proto::ReadyState new_state); + void LogSocketConnectState(int channel_id, proto::ConnectionState new_state); + void LogSocketReadState(int channel_id, proto::ReadState new_state); + void LogSocketWriteState(int channel_id, proto::WriteState new_state); + void LogSocketErrorState(int channel_id, proto::ErrorState new_state); + + // For AUTH_CHALLENGE_REPLY event. + void LogSocketChallengeReplyEvent(int channel_id, + const AuthResult& auth_result); + + void LogSocketEventForMessage(int channel_id, + proto::EventType event_type, + const std::string& message_namespace, + const std::string& details); + + // Assembles logs collected so far and return it as a serialized Log proto, + // compressed in gzip format. + // If serialization or compression failed, returns a NULL pointer. + // |length|: If successful, assigned with size of compressed content. + scoped_ptr GetLogs(size_t* length) const; + + // Clears the internal map. + void Reset(); + + // Returns the last errors logged for |channel_id|. If the the logs for + // |channel_id| are evicted before this is called, returns a LastErrors with + // no errors. This may happen if errors are logged and retrieved in different + // tasks. + LastErrors GetLastErrors(int channel_id) const; + + private: + friend class base::RefCounted; + ~Logger(); + + struct AggregatedSocketEventLog { + public: + AggregatedSocketEventLog(); + ~AggregatedSocketEventLog(); + + // Partially constructed AggregatedSocketEvent proto populated by Logger. + // Contains top level info such as channel ID, IP end point and channel + // auth type. + proto::AggregatedSocketEvent aggregated_socket_event; + // Events to be assigned to the AggregatedSocketEvent proto. Contains the + // most recent |kMaxEventsPerSocket| entries. The oldest events are + // evicted as new events are logged. + std::deque socket_events; + + // The most recent errors logged for the socket. + LastErrors last_errors; + }; + + typedef std::map > + AggregatedSocketEventLogMap; + + // Returns a SocketEvent proto with common fields (EventType, timestamp) + // populated. + proto::SocketEvent CreateEvent(proto::EventType event_type); + + // Records |event| associated with |channel_id|. + // If the internal map is already logging maximum number of sockets and this + // is a new socket, the socket with the smallest channel id will be discarded. + // Returns a reference to the AggregatedSocketEvent proto created/modified. + proto::AggregatedSocketEvent& LogSocketEvent( + int channel_id, + const proto::SocketEvent& socket_event); + + scoped_ptr clock_; + AggregatedSocketEventLogMap aggregated_socket_events_; + base::TimeTicks unix_epoch_time_ticks_; + + // Log proto holding global statistics. + proto::Log log_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(Logger); +}; +} // namespace cast_channel +} // namespace api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_CAST_CHANNEL_LOGGER_H_ diff --git a/extensions/browser/api/cast_channel/logger_unittest.cc b/extensions/browser/api/cast_channel/logger_unittest.cc new file mode 100644 index 0000000000000..3d1f964e42f58 --- /dev/null +++ b/extensions/browser/api/cast_channel/logger_unittest.cc @@ -0,0 +1,278 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test/simple_test_tick_clock.h" +#include "extensions/browser/api/cast_channel/cast_auth_util.h" +#include "extensions/browser/api/cast_channel/logger.h" +#include "extensions/browser/api/cast_channel/logger_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/zlib/zlib.h" + +namespace extensions { +namespace core_api { +namespace cast_channel { + +const int kTestNssErrorCode = -8164; + +using proto::AggregatedSocketEvent; +using proto::EventType; +using proto::Log; +using proto::SocketEvent; + +class CastChannelLoggerTest : public testing::Test { + public: + // |logger_| will take ownership of |clock_|. + CastChannelLoggerTest() + : clock_(new base::SimpleTestTickClock), + logger_(new Logger(scoped_ptr(clock_), + base::TimeTicks())) {} + virtual ~CastChannelLoggerTest() {} + + bool Uncompress(const char* input, int length, std::string* output) { + z_stream stream = {0}; + + stream.next_in = reinterpret_cast(const_cast(input)); + stream.avail_in = length; + stream.next_out = reinterpret_cast(&(*output)[0]); + stream.avail_out = output->size(); + + bool success = false; + while (stream.avail_in > 0 && stream.avail_out > 0) { + // 16 is added to read in gzip format. + int result = inflateInit2(&stream, MAX_WBITS + 16); + DCHECK_EQ(Z_OK, result); + + result = inflate(&stream, Z_FINISH); + success = (result == Z_STREAM_END); + if (!success) { + DVLOG(2) << "inflate() failed. Result: " << result; + break; + } + + result = inflateEnd(&stream); + DCHECK(result == Z_OK); + } + + if (stream.avail_in == 0) { + success = true; + output->resize(output->size() - stream.avail_out); + } + return success; + } + + scoped_ptr GetLog() { + size_t length = 0; + scoped_ptr output = logger_->GetLogs(&length); + if (!output.get()) + return scoped_ptr(); + + // 20kb should be enough for test purposes. + std::string uncompressed(20000, 0); + if (!Uncompress(output.get(), length, &uncompressed)) + return scoped_ptr(); + + scoped_ptr log(new Log); + if (!log->ParseFromString(uncompressed)) + return scoped_ptr(); + + return log.Pass(); + } + + protected: + base::SimpleTestTickClock* clock_; + scoped_refptr logger_; +}; + +TEST_F(CastChannelLoggerTest, BasicLogging) { + logger_->LogSocketEvent(1, EventType::CAST_SOCKET_CREATED); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithDetails( + 1, EventType::TCP_SOCKET_CONNECT, "TCP socket"); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEvent(2, EventType::CAST_SOCKET_CREATED); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithRv(1, EventType::SSL_SOCKET_CONNECT, -1); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventForMessage( + 2, EventType::MESSAGE_ENQUEUED, "foo_namespace", "details"); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + + AuthResult auth_result = + AuthResult::Create("No response", AuthResult::ERROR_NO_RESPONSE); + + logger_->LogSocketChallengeReplyEvent(2, auth_result); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + + auth_result = + AuthResult::CreateWithNSSError("Parsing failed", + AuthResult::ERROR_NSS_CERT_PARSING_FAILED, + kTestNssErrorCode); + logger_->LogSocketChallengeReplyEvent(2, auth_result); + + LastErrors last_errors = logger_->GetLastErrors(2); + EXPECT_EQ(last_errors.event_type, proto::AUTH_CHALLENGE_REPLY); + EXPECT_EQ(last_errors.net_return_value, 0); + EXPECT_EQ(last_errors.challenge_reply_error_type, + proto::CHALLENGE_REPLY_ERROR_NSS_CERT_PARSING_FAILED); + EXPECT_EQ(last_errors.nss_error_code, kTestNssErrorCode); + + scoped_ptr log = GetLog(); + ASSERT_TRUE(log.get() != NULL); + + ASSERT_EQ(2, log->aggregated_socket_event_size()); + { + const AggregatedSocketEvent& aggregated_socket_event = + log->aggregated_socket_event(0); + EXPECT_EQ(1, aggregated_socket_event.id()); + EXPECT_EQ(3, aggregated_socket_event.socket_event_size()); + { + const SocketEvent& event = aggregated_socket_event.socket_event(0); + EXPECT_EQ(EventType::CAST_SOCKET_CREATED, event.type()); + EXPECT_EQ(0, event.timestamp_micros()); + } + { + const SocketEvent& event = aggregated_socket_event.socket_event(1); + EXPECT_EQ(EventType::TCP_SOCKET_CONNECT, event.type()); + EXPECT_EQ(1, event.timestamp_micros()); + EXPECT_EQ("TCP socket", event.details()); + } + { + const SocketEvent& event = aggregated_socket_event.socket_event(2); + EXPECT_EQ(EventType::SSL_SOCKET_CONNECT, event.type()); + EXPECT_EQ(3, event.timestamp_micros()); + EXPECT_EQ(-1, event.net_return_value()); + } + } + { + const AggregatedSocketEvent& aggregated_socket_event = + log->aggregated_socket_event(1); + EXPECT_EQ(2, aggregated_socket_event.id()); + EXPECT_EQ(4, aggregated_socket_event.socket_event_size()); + { + const SocketEvent& event = aggregated_socket_event.socket_event(0); + EXPECT_EQ(EventType::CAST_SOCKET_CREATED, event.type()); + EXPECT_EQ(2, event.timestamp_micros()); + } + { + const SocketEvent& event = aggregated_socket_event.socket_event(1); + EXPECT_EQ(EventType::MESSAGE_ENQUEUED, event.type()); + EXPECT_EQ(4, event.timestamp_micros()); + EXPECT_FALSE(event.has_message_namespace()); + EXPECT_EQ("details", event.details()); + } + { + const SocketEvent& event = aggregated_socket_event.socket_event(2); + EXPECT_EQ(EventType::AUTH_CHALLENGE_REPLY, event.type()); + EXPECT_EQ(5, event.timestamp_micros()); + EXPECT_EQ(proto::CHALLENGE_REPLY_ERROR_NO_RESPONSE, + event.challenge_reply_error_type()); + EXPECT_FALSE(event.has_net_return_value()); + EXPECT_FALSE(event.has_nss_error_code()); + } + { + const SocketEvent& event = aggregated_socket_event.socket_event(3); + EXPECT_EQ(EventType::AUTH_CHALLENGE_REPLY, event.type()); + EXPECT_EQ(6, event.timestamp_micros()); + EXPECT_EQ(proto::CHALLENGE_REPLY_ERROR_NSS_CERT_PARSING_FAILED, + event.challenge_reply_error_type()); + EXPECT_FALSE(event.has_net_return_value()); + EXPECT_EQ(kTestNssErrorCode, event.nss_error_code()); + } + } +} + +TEST_F(CastChannelLoggerTest, LogSocketReadWrite) { + logger_->LogSocketEventWithRv(1, EventType::SOCKET_READ, 50); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithRv(1, EventType::SOCKET_READ, 30); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithRv(1, EventType::SOCKET_READ, -1); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithRv(1, EventType::SOCKET_WRITE, 20); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + + logger_->LogSocketEventWithRv(2, EventType::SOCKET_READ, 100); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithRv(2, EventType::SOCKET_WRITE, 100); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + logger_->LogSocketEventWithRv(2, EventType::SOCKET_WRITE, -5); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + + scoped_ptr log = GetLog(); + ASSERT_TRUE(log.get() != NULL); + + ASSERT_EQ(2, log->aggregated_socket_event_size()); + { + const AggregatedSocketEvent& aggregated_socket_event = + log->aggregated_socket_event(0); + EXPECT_EQ(1, aggregated_socket_event.id()); + EXPECT_EQ(4, aggregated_socket_event.socket_event_size()); + EXPECT_EQ(80, aggregated_socket_event.bytes_read()); + EXPECT_EQ(20, aggregated_socket_event.bytes_written()); + } + { + const AggregatedSocketEvent& aggregated_socket_event = + log->aggregated_socket_event(1); + EXPECT_EQ(2, aggregated_socket_event.id()); + EXPECT_EQ(3, aggregated_socket_event.socket_event_size()); + EXPECT_EQ(100, aggregated_socket_event.bytes_read()); + EXPECT_EQ(100, aggregated_socket_event.bytes_written()); + } +} + +TEST_F(CastChannelLoggerTest, TooManySockets) { + for (int i = 0; i < kMaxSocketsToLog + 5; i++) { + logger_->LogSocketEvent(i, EventType::CAST_SOCKET_CREATED); + } + + scoped_ptr log = GetLog(); + ASSERT_TRUE(log.get() != NULL); + + ASSERT_EQ(kMaxSocketsToLog, log->aggregated_socket_event_size()); + EXPECT_EQ(5, log->num_evicted_aggregated_socket_events()); + EXPECT_EQ(5, log->num_evicted_socket_events()); + + const AggregatedSocketEvent& aggregated_socket_event = + log->aggregated_socket_event(0); + EXPECT_EQ(5, aggregated_socket_event.id()); +} + +TEST_F(CastChannelLoggerTest, TooManyEvents) { + for (int i = 0; i < kMaxEventsPerSocket + 5; i++) { + logger_->LogSocketEvent(1, EventType::CAST_SOCKET_CREATED); + clock_->Advance(base::TimeDelta::FromMicroseconds(1)); + } + + scoped_ptr log = GetLog(); + ASSERT_TRUE(log.get() != NULL); + + ASSERT_EQ(1, log->aggregated_socket_event_size()); + EXPECT_EQ(0, log->num_evicted_aggregated_socket_events()); + EXPECT_EQ(5, log->num_evicted_socket_events()); + + const AggregatedSocketEvent& aggregated_socket_event = + log->aggregated_socket_event(0); + ASSERT_EQ(kMaxEventsPerSocket, aggregated_socket_event.socket_event_size()); + EXPECT_EQ(5, aggregated_socket_event.socket_event(0).timestamp_micros()); +} + +TEST_F(CastChannelLoggerTest, Reset) { + logger_->LogSocketEvent(1, EventType::CAST_SOCKET_CREATED); + + scoped_ptr log = GetLog(); + ASSERT_TRUE(log.get() != NULL); + + EXPECT_EQ(1, log->aggregated_socket_event_size()); + + logger_->Reset(); + + log = GetLog(); + ASSERT_TRUE(log.get() != NULL); + + EXPECT_EQ(0, log->aggregated_socket_event_size()); +} + +} // namespace cast_channel +} // namespace api +} // namespace extensions diff --git a/extensions/browser/api/cast_channel/logger_util.cc b/extensions/browser/api/cast_channel/logger_util.cc new file mode 100644 index 0000000000000..08e0857232074 --- /dev/null +++ b/extensions/browser/api/cast_channel/logger_util.cc @@ -0,0 +1,24 @@ +// 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 "extensions/browser/api/cast_channel/logger_util.h" +#include "net/base/net_errors.h" + +namespace extensions { +namespace core_api { +namespace cast_channel { + +LastErrors::LastErrors() + : event_type(proto::EVENT_TYPE_UNKNOWN), + challenge_reply_error_type(proto::CHALLENGE_REPLY_ERROR_NONE), + net_return_value(net::OK), + nss_error_code(0) { +} + +LastErrors::~LastErrors() { +} + +} // namespace cast_channel +} // namespace api +} // namespace extensions diff --git a/extensions/browser/api/cast_channel/logger_util.h b/extensions/browser/api/cast_channel/logger_util.h new file mode 100644 index 0000000000000..b00e8fcca166a --- /dev/null +++ b/extensions/browser/api/cast_channel/logger_util.h @@ -0,0 +1,40 @@ +// 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 EXTENSIONS_BROWSER_API_CAST_CHANNEL_LOGGER_UTIL_H_ +#define EXTENSIONS_BROWSER_API_CAST_CHANNEL_LOGGER_UTIL_H_ + +#include "extensions/browser/api/cast_channel/logging.pb.h" + +namespace extensions { +namespace core_api { +namespace cast_channel { + +// TODO(mfoltz): Move the *ToProto functions from cast_socket.cc here. +// CastSocket should not need to know details of the logging protocol message. + +// Holds the most recent errors encountered by a CastSocket. +struct LastErrors { + public: + LastErrors(); + ~LastErrors(); + + // The most recent event that occurred at the time of the error. + proto::EventType event_type; + + // The most recent ChallengeReplyErrorType logged for the socket. + proto::ChallengeReplyErrorType challenge_reply_error_type; + + // The most recent net_return_value logged for the socket. + int net_return_value; + + // The most recent NSS error logged for the socket. + int nss_error_code; +}; + +} // namespace cast_channel +} // namespace api +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_CAST_CHANNEL_LOGGER_UTIL_H_ diff --git a/extensions/browser/api/cast_channel/logging.proto b/extensions/browser/api/cast_channel/logging.proto new file mode 100644 index 0000000000000..58d86ac68aa4b --- /dev/null +++ b/extensions/browser/api/cast_channel/logging.proto @@ -0,0 +1,149 @@ +// 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. + +syntax = "proto2"; + +option optimize_for = LITE_RUNTIME; + +package extensions.core_api.cast_channel.proto; + +enum EventType { + EVENT_TYPE_UNKNOWN = 0; + CAST_SOCKET_CREATED = 1; + READY_STATE_CHANGED = 2; + CONNECTION_STATE_CHANGED = 3; + READ_STATE_CHANGED = 4; + WRITE_STATE_CHANGED = 5; + ERROR_STATE_CHANGED = 6; + CONNECT_FAILED = 7; + TCP_SOCKET_CONNECT = 8; // Logged with RV. + TCP_SOCKET_SET_KEEP_ALIVE = 9; + SSL_CERT_WHITELISTED = 10; + SSL_SOCKET_CONNECT = 11; // Logged with RV. + SSL_INFO_OBTAINED = 12; + DER_ENCODED_CERT_OBTAIN = 13; // Logged with RV. + RECEIVED_CHALLENGE_REPLY = 14; + AUTH_CHALLENGE_REPLY = 15; + CONNECT_TIMED_OUT = 16; + SEND_MESSAGE_FAILED = 17; + MESSAGE_ENQUEUED = 18; // Message + SOCKET_WRITE = 19; // Logged with RV. + MESSAGE_WRITTEN = 20; // Message + SOCKET_READ = 21; // Logged with RV. + MESSAGE_READ = 22; // Message + NOTIFY_ON_MESSAGE = 23; // Message + NOTIFY_ON_ERROR = 24; + SOCKET_CLOSED = 25; +} + +enum ChannelAuth { + // SSL over TCP. + SSL = 1; + // SSL over TCP with challenge and receiver signature verification. + SSL_VERIFIED = 2; +} + +enum ReadyState { + READY_STATE_NONE = 1; + READY_STATE_CONNECTING = 2; + READY_STATE_OPEN = 3; + READY_STATE_CLOSING = 4; + READY_STATE_CLOSED = 5; +} + +enum ConnectionState { + CONN_STATE_NONE = 1; + CONN_STATE_TCP_CONNECT = 2; + CONN_STATE_TCP_CONNECT_COMPLETE = 3; + CONN_STATE_SSL_CONNECT = 4; + CONN_STATE_SSL_CONNECT_COMPLETE = 5; + CONN_STATE_AUTH_CHALLENGE_SEND = 6; + CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE = 7; + CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE = 8; +} + +enum ReadState { + READ_STATE_NONE = 1; + READ_STATE_READ = 2; + READ_STATE_READ_COMPLETE = 3; + READ_STATE_DO_CALLBACK = 4; + READ_STATE_ERROR = 5; +} + +enum WriteState { + WRITE_STATE_NONE = 1; + WRITE_STATE_WRITE = 2; + WRITE_STATE_WRITE_COMPLETE = 3; + WRITE_STATE_DO_CALLBACK = 4; + WRITE_STATE_ERROR = 5; +} + +enum ErrorState { + CHANNEL_ERROR_NONE = 1; + CHANNEL_ERROR_CHANNEL_NOT_OPEN = 2; + CHANNEL_ERROR_AUTHENTICATION_ERROR = 3; + CHANNEL_ERROR_CONNECT_ERROR = 4; + CHANNEL_ERROR_SOCKET_ERROR = 5; + CHANNEL_ERROR_TRANSPORT_ERROR = 6; + CHANNEL_ERROR_INVALID_MESSAGE = 7; + CHANNEL_ERROR_INVALID_CHANNEL_ID = 8; + CHANNEL_ERROR_CONNECT_TIMEOUT = 9; + CHANNEL_ERROR_UNKNOWN = 10; +} + +enum ChallengeReplyErrorType { + CHALLENGE_REPLY_ERROR_NONE = 1; + CHALLENGE_REPLY_ERROR_PEER_CERT_EMPTY = 2; + CHALLENGE_REPLY_ERROR_WRONG_PAYLOAD_TYPE = 3; + CHALLENGE_REPLY_ERROR_NO_PAYLOAD = 4; + CHALLENGE_REPLY_ERROR_PAYLOAD_PARSING_FAILED = 5; + CHALLENGE_REPLY_ERROR_MESSAGE_ERROR = 6; + CHALLENGE_REPLY_ERROR_NO_RESPONSE = 7; + CHALLENGE_REPLY_ERROR_FINGERPRINT_NOT_FOUND = 8; + CHALLENGE_REPLY_ERROR_NSS_CERT_PARSING_FAILED = 9; + CHALLENGE_REPLY_ERROR_NSS_CERT_NOT_SIGNED_BY_TRUSTED_CA = 10; + CHALLENGE_REPLY_ERROR_NSS_CANNOT_EXTRACT_PUBLIC_KEY = 11; + CHALLENGE_REPLY_ERROR_NSS_SIGNED_BLOBS_MISMATCH = 12; +} + +message SocketEvent { + // Required + optional EventType type = 1; + optional int64 timestamp_micros = 2; + + optional string details = 3; + + optional int32 net_return_value = 4; + + optional string message_namespace = 5; + + optional ReadyState ready_state = 6; + optional ConnectionState connection_state = 7; + optional ReadState read_state = 8; + optional WriteState write_state = 9; + optional ErrorState error_state = 10; + + optional ChallengeReplyErrorType challenge_reply_error_type = 11; + optional int32 nss_error_code = 12; +} + +message AggregatedSocketEvent { + optional int32 id = 1; + optional int32 endpoint_id = 2; + optional ChannelAuth channel_auth_type = 3; + repeated SocketEvent socket_event = 4; + optional int64 bytes_read = 5; + optional int64 bytes_written = 6; +} + +message Log { + // Each AggregatedSocketEvent represents events recorded for a socket. + repeated AggregatedSocketEvent aggregated_socket_event = 1; + + // Number of socket log entries evicted by the logger due to size constraints. + optional int32 num_evicted_aggregated_socket_events = 2; + + // Number of event log entries evicted by the logger due to size constraints. + optional int32 num_evicted_socket_events = 3; +} diff --git a/extensions/browser/api/dns/dns_apitest.cc b/extensions/browser/api/dns/dns_apitest.cc index fa2ec2fa7858c..9d4150348fd34 100644 --- a/extensions/browser/api/dns/dns_apitest.cc +++ b/extensions/browser/api/dns/dns_apitest.cc @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/values.h" @@ -10,9 +9,8 @@ #include "extensions/browser/api/dns/host_resolver_wrapper.h" #include "extensions/browser/api/dns/mock_host_resolver_creator.h" #include "extensions/browser/api_test_utils.h" -#include "extensions/browser/extension_function_dispatcher.h" #include "extensions/common/extension.h" -#include "extensions/common/extension_builder.h" +#include "extensions/common/test_util.h" #include "extensions/shell/test/shell_test.h" #include "net/base/net_errors.h" @@ -20,21 +18,6 @@ using extensions::api_test_utils::RunFunctionAndReturnSingleResult; namespace extensions { -namespace { - -class TestFunctionDispatcherDelegate - : public ExtensionFunctionDispatcher::Delegate { - public: - TestFunctionDispatcherDelegate() {} - virtual ~TestFunctionDispatcherDelegate() {} - - // NULL implementation. - private: - DISALLOW_COPY_AND_ASSIGN(TestFunctionDispatcherDelegate); -}; - -} // namespace - class DnsApiTest : public AppShellTest { public: DnsApiTest() : resolver_creator_(new MockHostResolverCreator()) {} @@ -60,24 +43,13 @@ class DnsApiTest : public AppShellTest { IN_PROC_BROWSER_TEST_F(DnsApiTest, DnsResolveIPLiteral) { scoped_refptr resolve_function(new DnsResolveFunction()); - scoped_refptr empty_extension( - ExtensionBuilder() - .SetManifest( - DictionaryBuilder().Set("name", "Test").Set("version", "1.0")) - .Build()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); resolve_function->set_extension(empty_extension.get()); resolve_function->set_has_callback(true); - TestFunctionDispatcherDelegate delegate; - scoped_ptr dispatcher( - new ExtensionFunctionDispatcher(browser_context(), &delegate)); - - scoped_ptr result( - RunFunctionAndReturnSingleResult(resolve_function.get(), - "[\"127.0.0.1\"]", - browser_context(), - dispatcher.Pass())); + scoped_ptr result(RunFunctionAndReturnSingleResult( + resolve_function.get(), "[\"127.0.0.1\"]", browser_context())); base::DictionaryValue* dict = NULL; ASSERT_TRUE(result->GetAsDictionary(&dict)); @@ -92,27 +64,16 @@ IN_PROC_BROWSER_TEST_F(DnsApiTest, DnsResolveIPLiteral) { IN_PROC_BROWSER_TEST_F(DnsApiTest, DnsResolveHostname) { scoped_refptr resolve_function(new DnsResolveFunction()); - scoped_refptr empty_extension( - ExtensionBuilder() - .SetManifest( - DictionaryBuilder().Set("name", "Test").Set("version", "1.0")) - .Build()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); resolve_function->set_extension(empty_extension.get()); resolve_function->set_has_callback(true); - TestFunctionDispatcherDelegate delegate; - scoped_ptr dispatcher( - new ExtensionFunctionDispatcher(browser_context(), &delegate)); - std::string function_arguments("[\""); function_arguments += MockHostResolverCreator::kHostname; function_arguments += "\"]"; - scoped_ptr result( - RunFunctionAndReturnSingleResult(resolve_function.get(), - function_arguments, - browser_context(), - dispatcher.Pass())); + scoped_ptr result(RunFunctionAndReturnSingleResult( + resolve_function.get(), function_arguments, browser_context())); base::DictionaryValue* dict = NULL; ASSERT_TRUE(result->GetAsDictionary(&dict)); diff --git a/extensions/browser/api/extensions_api_client.h b/extensions/browser/api/extensions_api_client.h index dc45299b49b32..a13b8fe32adad 100644 --- a/extensions/browser/api/extensions_api_client.h +++ b/extensions/browser/api/extensions_api_client.h @@ -71,6 +71,8 @@ class ExtensionsAPIClient { // Returns the HidService instance for this embedder. virtual device::HidService* GetHidService(); + virtual void RegisterGuestViewTypes() {} + // NOTE: If this interface gains too many methods (perhaps more than 20) it // should be split into one interface per API. }; diff --git a/extensions/browser/api/hid/hid_api.cc b/extensions/browser/api/hid/hid_api.cc index 1c5235fb77407..3f0e25829a5e6 100644 --- a/extensions/browser/api/hid/hid_api.cc +++ b/extensions/browser/api/hid/hid_api.cc @@ -184,8 +184,11 @@ void HidReceiveFunction::AsyncWorkStart() { scoped_refptr connection = resource->connection(); has_report_id_ = connection->device_info().has_report_id; - const int size = connection->device_info().max_input_report_size; - buffer_ = new net::IOBufferWithSize(size + 1); // 1 byte for the report ID + int size = connection->device_info().max_input_report_size; + if (has_report_id_) { + ++size; // One byte at the beginning of the buffer for the report ID. + } + buffer_ = new net::IOBufferWithSize(size); connection->Read(buffer_, base::Bind(&HidReceiveFunction::OnFinished, this)); } @@ -307,6 +310,7 @@ void HidSendFeatureReportFunction::AsyncWorkStart() { } scoped_refptr buffer( new net::IOBufferWithSize(parameters_->data.size())); + memcpy(buffer->data(), parameters_->data.c_str(), parameters_->data.size()); resource->connection()->SendFeatureReport( static_cast(parameters_->report_id), buffer, diff --git a/extensions/browser/api/socket/socket_apitest.cc b/extensions/browser/api/socket/socket_apitest.cc new file mode 100644 index 0000000000000..edc5a84541cd8 --- /dev/null +++ b/extensions/browser/api/socket/socket_apitest.cc @@ -0,0 +1,71 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/browser/api/dns/host_resolver_wrapper.h" +#include "extensions/browser/api/dns/mock_host_resolver_creator.h" +#include "extensions/browser/api/socket/socket_api.h" +#include "extensions/browser/api_test_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/test_util.h" +#include "extensions/shell/test/shell_test.h" + +using extensions::api_test_utils::RunFunctionAndReturnSingleResult; + +namespace extensions { + +class SocketApiTest : public AppShellTest {}; + +IN_PROC_BROWSER_TEST_F(SocketApiTest, SocketUDPCreateGood) { + scoped_refptr socket_create_function( + new extensions::SocketCreateFunction()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); + + socket_create_function->set_extension(empty_extension.get()); + socket_create_function->set_has_callback(true); + + scoped_ptr result(RunFunctionAndReturnSingleResult( + socket_create_function.get(), "[\"udp\"]", browser_context())); + base::DictionaryValue* value = NULL; + ASSERT_TRUE(result->GetAsDictionary(&value)); + int socket_id = -1; + EXPECT_TRUE(value->GetInteger("socketId", &socket_id)); + EXPECT_GT(socket_id, 0); +} + +IN_PROC_BROWSER_TEST_F(SocketApiTest, SocketTCPCreateGood) { + scoped_refptr socket_create_function( + new extensions::SocketCreateFunction()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); + + socket_create_function->set_extension(empty_extension.get()); + socket_create_function->set_has_callback(true); + + scoped_ptr result(RunFunctionAndReturnSingleResult( + socket_create_function.get(), "[\"tcp\"]", browser_context())); + base::DictionaryValue* value = NULL; + ASSERT_TRUE(result->GetAsDictionary(&value)); + int socket_id = -1; + EXPECT_TRUE(value->GetInteger("socketId", &socket_id)); + ASSERT_GT(socket_id, 0); +} + +IN_PROC_BROWSER_TEST_F(SocketApiTest, GetNetworkList) { + scoped_refptr socket_function( + new extensions::SocketGetNetworkListFunction()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); + + socket_function->set_extension(empty_extension.get()); + socket_function->set_has_callback(true); + + scoped_ptr result(RunFunctionAndReturnSingleResult( + socket_function.get(), "[]", browser_context())); + + // If we're invoking socket tests, all we can confirm is that we have at + // least one address, but not what it is. + base::ListValue* value = NULL; + ASSERT_TRUE(result->GetAsList(&value)); + ASSERT_GT(value->GetSize(), 0U); +} + +} // namespace extensions diff --git a/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc new file mode 100644 index 0000000000000..df0ddeacca171 --- /dev/null +++ b/extensions/browser/api/sockets_tcp/sockets_tcp_apitest.cc @@ -0,0 +1,37 @@ +// 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 "extensions/browser/api/sockets_tcp/sockets_tcp_api.h" +#include "extensions/browser/api_test_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/test_util.h" +#include "extensions/shell/test/shell_test.h" + +using extensions::api_test_utils::RunFunctionAndReturnSingleResult; + +namespace extensions { + +class SocketsTcpApiTest : public AppShellTest {}; + +IN_PROC_BROWSER_TEST_F(SocketsTcpApiTest, SocketsTcpCreateGood) { + scoped_refptr + socket_create_function( + new extensions::core_api::SocketsTcpCreateFunction()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); + + socket_create_function->set_extension(empty_extension.get()); + socket_create_function->set_has_callback(true); + + scoped_ptr result(RunFunctionAndReturnSingleResult( + socket_create_function.get(), "[]", browser_context())); + + ASSERT_EQ(base::Value::TYPE_DICTIONARY, result->GetType()); + base::DictionaryValue* value = + static_cast(result.get()); + int socketId = -1; + EXPECT_TRUE(value->GetInteger("socketId", &socketId)); + ASSERT_TRUE(socketId > 0); +} + +} // namespace extensions diff --git a/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc new file mode 100644 index 0000000000000..64736bbb12dfb --- /dev/null +++ b/extensions/browser/api/sockets_udp/sockets_udp_apitest.cc @@ -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. + +#include "extensions/browser/api/dns/host_resolver_wrapper.h" +#include "extensions/browser/api/dns/mock_host_resolver_creator.h" +#include "extensions/browser/api/sockets_udp/sockets_udp_api.h" +#include "extensions/browser/api_test_utils.h" +#include "extensions/common/extension.h" +#include "extensions/common/test_util.h" +#include "extensions/shell/test/shell_test.h" + +using extensions::api_test_utils::RunFunctionAndReturnSingleResult; + +namespace extensions { + +class SocketsUdpApiTest : public AppShellTest {}; + +IN_PROC_BROWSER_TEST_F(SocketsUdpApiTest, SocketsUdpCreateGood) { + scoped_refptr + socket_create_function( + new extensions::core_api::SocketsUdpCreateFunction()); + scoped_refptr empty_extension = test_util::CreateEmptyExtension(); + + socket_create_function->set_extension(empty_extension.get()); + socket_create_function->set_has_callback(true); + + scoped_ptr result(RunFunctionAndReturnSingleResult( + socket_create_function.get(), "[]", browser_context())); + + base::DictionaryValue* value = NULL; + ASSERT_TRUE(result->GetAsDictionary(&value)); + int socketId = -1; + EXPECT_TRUE(value->GetInteger("socketId", &socketId)); + ASSERT_TRUE(socketId > 0); +} + +} // namespace extensions diff --git a/extensions/browser/api/usb/usb_api.cc b/extensions/browser/api/usb/usb_api.cc index 7490f10f4e857..18b0e53846e3d 100644 --- a/extensions/browser/api/usb/usb_api.cc +++ b/extensions/browser/api/usb/usb_api.cc @@ -325,12 +325,12 @@ void RequestUsbDevicesAccessHelper( callback.Run(devices.Pass()); return; } - (*i)->RequestUsbAcess(interface_id, - base::Bind(RequestUsbDevicesAccessHelper, - base::Passed(devices.Pass()), - i, - interface_id, - callback)); + (*i)->RequestUsbAccess(interface_id, + base::Bind(RequestUsbDevicesAccessHelper, + base::Passed(devices.Pass()), + i, + interface_id, + callback)); } void RequestUsbDevicesAccess( @@ -342,12 +342,12 @@ void RequestUsbDevicesAccess( return; } std::vector >::iterator i = devices->begin(); - (*i)->RequestUsbAcess(interface_id, - base::Bind(RequestUsbDevicesAccessHelper, - base::Passed(devices.Pass()), - i, - interface_id, - callback)); + (*i)->RequestUsbAccess(interface_id, + base::Bind(RequestUsbDevicesAccessHelper, + base::Passed(devices.Pass()), + i, + interface_id, + callback)); } #endif // OS_CHROMEOS @@ -680,7 +680,7 @@ void UsbRequestAccessFunction::AsyncWorkStart() { if (!device) return; - device->RequestUsbAcess( + device->RequestUsbAccess( parameters_->interface_id, base::Bind(&UsbRequestAccessFunction::OnCompleted, this)); #else diff --git a/extensions/browser/api/usb/usb_apitest.cc b/extensions/browser/api/usb/usb_apitest.cc index 8d6fb55ccec0b..ee2fc6d1f4a0b 100644 --- a/extensions/browser/api/usb/usb_apitest.cc +++ b/extensions/browser/api/usb/usb_apitest.cc @@ -90,6 +90,8 @@ class MockUsbDeviceHandle : public UsbDeviceHandle { MOCK_METHOD1(ReleaseInterface, bool(const int interface_number)); MOCK_METHOD2(SetInterfaceAlternateSetting, bool(const int interface_number, const int alternate_setting)); + MOCK_METHOD1(GetManufacturer, bool(base::string16* manufacturer)); + MOCK_METHOD1(GetProduct, bool(base::string16* product)); MOCK_METHOD1(GetSerial, bool(base::string16* serial)); virtual scoped_refptr GetDevice() const OVERRIDE { @@ -131,7 +133,7 @@ class MockUsbDevice : public UsbDevice { } #if defined(OS_CHROMEOS) - virtual void RequestUsbAcess( + virtual void RequestUsbAccess( int interface_id, const base::Callback& callback) OVERRIDE { BrowserThread::PostTask( diff --git a/extensions/browser/api/usb_private/DEPS b/extensions/browser/api/usb_private/DEPS new file mode 100644 index 0000000000000..d8eab5dbe5a3f --- /dev/null +++ b/extensions/browser/api/usb_private/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+components/usb_service", + "+device/usb", +] diff --git a/extensions/browser/api/usb_private/usb_private_api.cc b/extensions/browser/api/usb_private/usb_private_api.cc new file mode 100644 index 0000000000000..c09939a82f2ad --- /dev/null +++ b/extensions/browser/api/usb_private/usb_private_api.cc @@ -0,0 +1,172 @@ +// 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 "extensions/browser/api/usb_private/usb_private_api.h" + +#include +#include + +#include "base/memory/ref_counted.h" +#include "base/strings/utf_string_conversions.h" +#include "components/usb_service/usb_device_filter.h" +#include "components/usb_service/usb_device_handle.h" +#include "components/usb_service/usb_service.h" +#include "device/usb/usb_ids.h" +#include "extensions/common/api/usb_private.h" + +namespace usb_private = extensions::core_api::usb_private; +namespace GetDevices = usb_private::GetDevices; +namespace GetDeviceInfo = usb_private::GetDeviceInfo; + +using usb_service::UsbDevice; +using usb_service::UsbDeviceFilter; +using usb_service::UsbDeviceHandle; +using usb_service::UsbService; + +namespace { + +const char kErrorInitService[] = "Failed to initialize USB service."; +const char kErrorNoDevice[] = "No such device."; +const char kErrorOpen[] = "Failed to open device."; + +} // namespace + +namespace extensions { + +UsbPrivateGetDevicesFunction::UsbPrivateGetDevicesFunction() { +} + +UsbPrivateGetDevicesFunction::~UsbPrivateGetDevicesFunction() { +} + +bool UsbPrivateGetDevicesFunction::Prepare() { + parameters_ = GetDevices::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + return true; +} + +void UsbPrivateGetDevicesFunction::AsyncWorkStart() { + UsbService* service = UsbService::GetInstance(); + if (!service) { + CompleteWithError(kErrorInitService); + return; + } + + std::vector filters; + filters.resize(parameters_->filters.size()); + for (size_t i = 0; i < parameters_->filters.size(); ++i) { + UsbDeviceFilter& filter = filters[i]; + const usb_private::DeviceFilter* filter_param = + parameters_->filters[i].get(); + + if (filter_param->vendor_id) { + filter.SetVendorId(*filter_param->vendor_id); + } + if (filter_param->product_id) { + filter.SetProductId(*filter_param->product_id); + } + if (filter_param->interface_class) { + filter.SetInterfaceClass(*filter_param->interface_class); + } + if (filter_param->interface_subclass) { + filter.SetInterfaceSubclass(*filter_param->interface_subclass); + } + if (filter_param->interface_protocol) { + filter.SetInterfaceProtocol(*filter_param->interface_protocol); + } + } + + std::vector > devices; + service->GetDevices(&devices); + + scoped_ptr result(new base::ListValue()); + for (size_t i = 0; i < devices.size(); ++i) { + scoped_refptr device = devices[i]; + bool matched = false; + + if (filters.empty()) { + matched = true; + } else { + for (size_t j = 0; !matched && j < filters.size(); ++j) { + if (filters[j].Matches(device)) { + matched = true; + } + } + } + + if (matched) { + result->Append(new base::FundamentalValue((int)device->unique_id())); + } + } + + SetResult(result.release()); + AsyncWorkCompleted(); +} + +UsbPrivateGetDeviceInfoFunction::UsbPrivateGetDeviceInfoFunction() { +} + +UsbPrivateGetDeviceInfoFunction::~UsbPrivateGetDeviceInfoFunction() { +} + +bool UsbPrivateGetDeviceInfoFunction::Prepare() { + parameters_ = GetDeviceInfo::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters_.get()); + return true; +} + +void UsbPrivateGetDeviceInfoFunction::AsyncWorkStart() { + UsbService* service = UsbService::GetInstance(); + if (!service) { + CompleteWithError(kErrorInitService); + return; + } + + scoped_refptr device = + service->GetDeviceById(parameters_->device_id); + if (!device) { + CompleteWithError(kErrorNoDevice); + return; + } + + usb_private::DeviceInfo device_info; + device_info.vendor_id = device->vendor_id(); + device_info.product_id = device->product_id(); + + const char* name = device::UsbIds::GetVendorName(device_info.vendor_id); + if (name) { + device_info.vendor_name.reset(new std::string(name)); + } + + name = device::UsbIds::GetProductName(device_info.vendor_id, + device_info.product_id); + if (name) { + device_info.product_name.reset(new std::string(name)); + } + + scoped_refptr device_handle = device->Open(); + if (!device_handle) { + CompleteWithError(kErrorOpen); + return; + } + + base::string16 utf16; + if (device_handle->GetManufacturer(&utf16)) { + device_info.manufacturer_string.reset( + new std::string(base::UTF16ToUTF8(utf16))); + } + + if (device_handle->GetProduct(&utf16)) { + device_info.product_string.reset(new std::string(base::UTF16ToUTF8(utf16))); + } + + if (device_handle->GetSerial(&utf16)) { + device_info.serial_string.reset(new std::string(base::UTF16ToUTF8(utf16))); + } + + SetResult(device_info.ToValue().release()); + AsyncWorkCompleted(); +} + +} // namespace extensions diff --git a/extensions/browser/api/usb_private/usb_private_api.h b/extensions/browser/api/usb_private/usb_private_api.h new file mode 100644 index 0000000000000..f91ce7cdb9c47 --- /dev/null +++ b/extensions/browser/api/usb_private/usb_private_api.h @@ -0,0 +1,49 @@ +// 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 EXTENSIONS_BROWSER_API_USB_USB_PRIVATE_API_H_ +#define EXTENSIONS_BROWSER_API_USB_USB_PRIVATE_API_H_ + +#include "extensions/browser/api/usb/usb_api.h" +#include "extensions/common/api/usb_private.h" + +namespace extensions { + +class UsbPrivateGetDevicesFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usbPrivate.getDevices", USBPRIVATE_GETDEVICES) + + UsbPrivateGetDevicesFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + protected: + virtual ~UsbPrivateGetDevicesFunction(); + + private: + scoped_ptr parameters_; +}; + +class UsbPrivateGetDeviceInfoFunction : public UsbAsyncApiFunction { + public: + DECLARE_EXTENSION_FUNCTION("usbPrivate.getDeviceInfo", + USBPRIVATE_GETDEVICEINFO) + + UsbPrivateGetDeviceInfoFunction(); + + virtual bool Prepare() OVERRIDE; + virtual void AsyncWorkStart() OVERRIDE; + + protected: + virtual ~UsbPrivateGetDeviceInfoFunction(); + + private: + scoped_ptr + parameters_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_API_USB_USB_API_H_ diff --git a/extensions/browser/api_test_utils.cc b/extensions/browser/api_test_utils.cc index 4c9a58b95cdbc..b18b249e58dd5 100644 --- a/extensions/browser/api_test_utils.cc +++ b/extensions/browser/api_test_utils.cc @@ -11,10 +11,24 @@ #include "content/public/test/test_utils.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function_dispatcher.h" +#include "extensions/common/extension_builder.h" #include "testing/gtest/include/gtest/gtest.h" +using extensions::ExtensionFunctionDispatcher; + namespace { +class TestFunctionDispatcherDelegate + : public ExtensionFunctionDispatcher::Delegate { + public: + TestFunctionDispatcherDelegate() {} + virtual ~TestFunctionDispatcherDelegate() {} + + // NULL implementation. + private: + DISALLOW_COPY_AND_ASSIGN(TestFunctionDispatcherDelegate); +}; + base::Value* ParseJSON(const std::string& data) { return base::JSONReader::Read(data); } @@ -69,16 +83,16 @@ namespace extensions { namespace api_test_utils { -base::Value* RunFunctionAndReturnSingleResult( +base::Value* RunFunctionWithDelegateAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, scoped_ptr dispatcher) { - return RunFunctionAndReturnSingleResult( + return RunFunctionWithDelegateAndReturnSingleResult( function, args, context, dispatcher.Pass(), NONE); } -base::Value* RunFunctionAndReturnSingleResult( +base::Value* RunFunctionWithDelegateAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, @@ -98,6 +112,26 @@ base::Value* RunFunctionAndReturnSingleResult( return NULL; } +base::Value* RunFunctionAndReturnSingleResult( + UIThreadExtensionFunction* function, + const std::string& args, + content::BrowserContext* context) { + return RunFunctionAndReturnSingleResult(function, args, context, NONE); +} + +base::Value* RunFunctionAndReturnSingleResult( + UIThreadExtensionFunction* function, + const std::string& args, + content::BrowserContext* context, + RunFunctionFlags flags) { + TestFunctionDispatcherDelegate delegate; + scoped_ptr dispatcher( + new ExtensionFunctionDispatcher(context, &delegate)); + + return RunFunctionWithDelegateAndReturnSingleResult( + function, args, context, dispatcher.Pass(), flags); +} + bool RunFunction(UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, diff --git a/extensions/browser/api_test_utils.h b/extensions/browser/api_test_utils.h index 119b090cdee7d..19af3525bbcc7 100644 --- a/extensions/browser/api_test_utils.h +++ b/extensions/browser/api_test_utils.h @@ -31,18 +31,30 @@ enum RunFunctionFlags { NONE = 0, INCLUDE_INCOGNITO = 1 << 0 }; // Run |function| with |args| and return the result. Adds an error to the // current test if |function| returns an error. Takes ownership of // |function|. The caller takes ownership of the result. -base::Value* RunFunctionAndReturnSingleResult( +base::Value* RunFunctionWithDelegateAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, scoped_ptr dispatcher); -base::Value* RunFunctionAndReturnSingleResult( +base::Value* RunFunctionWithDelegateAndReturnSingleResult( UIThreadExtensionFunction* function, const std::string& args, content::BrowserContext* context, scoped_ptr dispatcher, RunFunctionFlags flags); +// RunFunctionWithDelegateAndReturnSingleResult, except with a NULL +// implementation of the Delegate. +base::Value* RunFunctionAndReturnSingleResult( + UIThreadExtensionFunction* function, + const std::string& args, + content::BrowserContext* context); +base::Value* RunFunctionAndReturnSingleResult( + UIThreadExtensionFunction* function, + const std::string& args, + content::BrowserContext* context, + RunFunctionFlags flags); + // Create and run |function| with |args|. Works with both synchronous and async // functions. Ownership of |function| remains with the caller. // diff --git a/extensions/browser/content_hash_fetcher.cc b/extensions/browser/content_hash_fetcher.cc index 421271125eb7b..a8cc0f79738b1 100644 --- a/extensions/browser/content_hash_fetcher.cc +++ b/extensions/browser/content_hash_fetcher.cc @@ -22,7 +22,6 @@ #include "extensions/browser/computed_hashes.h" #include "extensions/browser/content_hash_tree.h" #include "extensions/browser/content_verifier_delegate.h" -#include "extensions/browser/extension_registry.h" #include "extensions/browser/verified_contents.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" @@ -85,6 +84,13 @@ class ContentHashFetcherJob friend class base::RefCountedThreadSafe; virtual ~ContentHashFetcherJob(); + // Tries to load a verified_contents.json file at |path|. On successfully + // reading and validing the file, the verified_contents_ member variable will + // be set and this function will return true. If the file does not exist, or + // exists but is invalid, it will return false. Also, any invalid + // file will be removed from disk and + bool LoadVerifiedContents(const base::FilePath& path); + // Callback for when we're done doing file I/O to see if we already have // a verified contents file. If we don't, this will kick off a network // request to get one. @@ -134,6 +140,10 @@ class ContentHashFetcherJob // The key used to validate verified_contents.json. ContentVerifierKey key_; + // The parsed contents of the verified_contents.json file, either read from + // disk or fetched from the network and then written to disk. + scoped_ptr verified_contents_; + // Whether this job succeeded. bool success_; @@ -184,7 +194,9 @@ void ContentHashFetcherJob::Start() { base::PostTaskAndReplyWithResult( content::BrowserThread::GetBlockingPool(), FROM_HERE, - base::Bind(&base::PathExists, verified_contents_path), + base::Bind(&ContentHashFetcherJob::LoadVerifiedContents, + this, + verified_contents_path), base::Bind(&ContentHashFetcherJob::DoneCheckingForVerifiedContents, this)); } @@ -203,6 +215,19 @@ bool ContentHashFetcherJob::IsCancelled() { ContentHashFetcherJob::~ContentHashFetcherJob() { } +bool ContentHashFetcherJob::LoadVerifiedContents(const base::FilePath& path) { + if (!base::PathExists(path)) + return false; + verified_contents_.reset(new VerifiedContents(key_.data, key_.size)); + if (!verified_contents_->InitFrom(path, false)) { + verified_contents_.reset(); + if (!base::DeleteFile(path, false)) + LOG(WARNING) << "Failed to delete " << path.value(); + return false; + } + return true; +} + void ContentHashFetcherJob::DoneCheckingForVerifiedContents(bool found) { if (IsCancelled()) return; @@ -319,11 +344,14 @@ bool ContentHashFetcherJob::CreateHashes(const base::FilePath& hashes_file) { if (!base::CreateDirectoryAndGetError(hashes_file.DirName(), NULL)) return false; - base::FilePath verified_contents_path = - file_util::GetVerifiedContentsPath(extension_path_); - VerifiedContents verified_contents(key_.data, key_.size); - if (!verified_contents.InitFrom(verified_contents_path, false)) - return false; + if (!verified_contents_.get()) { + base::FilePath verified_contents_path = + file_util::GetVerifiedContentsPath(extension_path_); + verified_contents_.reset(new VerifiedContents(key_.data, key_.size)); + if (!verified_contents_->InitFrom(verified_contents_path, false)) + return false; + verified_contents_.reset(); + } base::FileEnumerator enumerator(extension_path_, true, /* recursive */ @@ -352,7 +380,7 @@ bool ContentHashFetcherJob::CreateHashes(const base::FilePath& hashes_file) { relative_path = relative_path.NormalizePathSeparatorsTo('/'); const std::string* expected_root = - verified_contents.GetTreeHashRoot(relative_path); + verified_contents_->GetTreeHashRoot(relative_path); if (!expected_root) continue; diff --git a/extensions/browser/content_hash_reader.cc b/extensions/browser/content_hash_reader.cc index c1e6e95642cab..1c9ae97c62a16 100644 --- a/extensions/browser/content_hash_reader.cc +++ b/extensions/browser/content_hash_reader.cc @@ -35,6 +35,7 @@ ContentHashReader::ContentHashReader(const std::string& extension_id, relative_path_(relative_path), key_(key), status_(NOT_INITIALIZED), + content_exists_(false), have_verified_contents_(false), have_computed_hashes_(false), block_size_(0) { @@ -50,6 +51,13 @@ bool ContentHashReader::Init() { base::FilePath verified_contents_path = file_util::GetVerifiedContentsPath(extension_root_); + // 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)) + return false; + + content_exists_ = true; + if (!base::PathExists(verified_contents_path)) return false; diff --git a/extensions/browser/content_hash_reader.h b/extensions/browser/content_hash_reader.h index cc2ea38b925bb..516897b5d37b4 100644 --- a/extensions/browser/content_hash_reader.h +++ b/extensions/browser/content_hash_reader.h @@ -40,11 +40,15 @@ class ContentHashReader : public base::RefCountedThreadSafe { // should likely be discarded. bool Init(); + // Indicates whether the content in question exists in the local extension + // installation. This may be |false| if Init fails. + bool content_exists() const { return content_exists_; } + // These return whether we found valid verified_contents.json / // computed_hashes.json files respectively. Note that both of these can be // true but we still didn't find an entry for |relative_path_| in them. - bool have_verified_contents() { return have_verified_contents_; } - bool have_computed_hashes() { return have_computed_hashes_; } + bool have_verified_contents() const { return have_verified_contents_; } + bool have_computed_hashes() const { return have_computed_hashes_; } // Return the number of blocks and block size, respectively. Only valid after // calling Init(). @@ -69,6 +73,8 @@ class ContentHashReader : public base::RefCountedThreadSafe { InitStatus status_; + bool content_exists_; + bool have_verified_contents_; bool have_computed_hashes_; diff --git a/extensions/browser/content_verify_job.cc b/extensions/browser/content_verify_job.cc index f8618bc02e9bf..e7e8cab9dd69a 100644 --- a/extensions/browser/content_verify_job.cc +++ b/extensions/browser/content_verify_job.cc @@ -152,11 +152,15 @@ bool ContentVerifyJob::FinishBlock() { void ContentVerifyJob::OnHashesReady(bool success) { if (!success && !g_test_delegate) { - if (hash_reader_->have_verified_contents() && - hash_reader_->have_computed_hashes()) + if (!hash_reader_->content_exists()) { + // Ignore verification of non-existent resources. + return; + } else if (hash_reader_->have_verified_contents() && + hash_reader_->have_computed_hashes()) { DispatchFailureCallback(NO_HASHES_FOR_FILE); - else + } else { DispatchFailureCallback(MISSING_ALL_HASHES); + } return; } diff --git a/extensions/browser/event_router.cc b/extensions/browser/event_router.cc index 1feb1ca41a52f..d006d1e9a7588 100644 --- a/extensions/browser/event_router.cc +++ b/extensions/browser/event_router.cc @@ -539,7 +539,8 @@ void EventRouter::DispatchEventToProcess(const std::string& extension_id, BrowserContext* listener_context = process->GetBrowserContext(); ProcessMap* process_map = ProcessMap::Get(listener_context); - // TODO(kalman): Convert this method to use ProcessMap::GuessContextType. + // TODO(kalman): Convert this method to use + // ProcessMap::GetMostLikelyContextType. const Extension* extension = ExtensionRegistry::Get(browser_context_)->enabled_extensions().GetByID( diff --git a/extensions/browser/extension_function_dispatcher.cc b/extensions/browser/extension_function_dispatcher.cc index b26ef2d395ddd..4dfb7fe09c568 100644 --- a/extensions/browser/extension_function_dispatcher.cc +++ b/extensions/browser/extension_function_dispatcher.cc @@ -455,7 +455,7 @@ ExtensionFunction* ExtensionFunctionDispatcher::CreateExtensionFunction( function->set_response_callback(callback); function->set_source_tab_id(params.source_tab_id); function->set_source_context_type( - process_map.GuessContextType(extension, requesting_process_id)); + process_map.GetMostLikelyContextType(extension, requesting_process_id)); return function; } diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 5d11e7fce3f3a..ce88dd91a6a08 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -926,8 +926,25 @@ enum HistogramValue { COPRESENCEPRIVATE_SENDSAMPLES, COPRESENCEPRIVATE_SENDDETECT, COPRESENCEPRIVATE_SENDINITIALIZED, + COPRESENCE_EXECUTE, + COPRESENCE_SETAPIKEY, + FILESYSTEM_OBSERVEDIRECTORY, + FILESYSTEM_UNOBSERVEENTRY, + FILESYSTEM_GETOBSERVEDENTRIES, + BROWSINGDATA_REMOVESERVICEWORKERS, + USBPRIVATE_GETDEVICES, + USBPRIVATE_GETDEVICEINFO, + EASYUNLOCKPRIVATE_UPDATESCREENLOCKSTATE, + CAST_CHANNEL_GETLOGS, + EASYUNLOCKPRIVATE_SETPERMITACCESS, + EASYUNLOCKPRIVATE_GETPERMITACCESS, + EASYUNLOCKPRIVATE_CLEARPERMITACCESS, + EASYUNLOCKPRIVATE_SETREMOTEDEVICES, + EASYUNLOCKPRIVATE_GETREMOTEDEVICES, + FILESYSTEMPROVIDER_GETALL, + EASYUNLOCKPRIVATE_CONNECTTOBLUETOOTHSERVICEINSECURELY, // Last entry: Add new entries above and ensure to update - // tools/metrics/histograms/histograms/histograms.xml. + // tools/metrics/histograms/histograms.xml. ENUM_BOUNDARY }; diff --git a/extensions/browser/extension_icon_image_unittest.cc b/extensions/browser/extension_icon_image_unittest.cc index 62462ff04cc2e..ead9dc04eabfe 100644 --- a/extensions/browser/extension_icon_image_unittest.cc +++ b/extensions/browser/extension_icon_image_unittest.cc @@ -7,11 +7,11 @@ #include "base/json/json_file_value_serializer.h" #include "base/message_loop/message_loop.h" #include "base/path_service.h" -#include "chrome/common/chrome_paths.h" #include "chrome/test/base/testing_profile.h" #include "content/public/test/test_browser_thread.h" #include "extensions/browser/image_loader.h" #include "extensions/common/extension.h" +#include "extensions/common/extension_paths.h" #include "extensions/common/manifest.h" #include "extensions/common/manifest_handlers/icons_handler.h" #include "skia/ext/image_operations.h" @@ -141,14 +141,14 @@ class ExtensionIconImageTest : public testing::Test, Manifest::Location location) { // Create and load an extension. base::FilePath test_file; - if (!PathService::Get(chrome::DIR_TEST_DATA, &test_file)) { + if (!PathService::Get(extensions::DIR_TEST_DATA, &test_file)) { EXPECT_FALSE(true); return NULL; } - test_file = test_file.AppendASCII("extensions").AppendASCII(name); + test_file = test_file.AppendASCII(name); int error_code = 0; std::string error; - JSONFileValueSerializer serializer(test_file.AppendASCII("app.json")); + JSONFileValueSerializer serializer(test_file.AppendASCII("manifest.json")); scoped_ptr valid_value( static_cast(serializer.Deserialize(&error_code, &error))); diff --git a/extensions/browser/extension_prefs.cc b/extensions/browser/extension_prefs.cc index 502816ca2d1df..d7774c56689aa 100644 --- a/extensions/browser/extension_prefs.cc +++ b/extensions/browser/extension_prefs.cc @@ -1337,23 +1337,14 @@ scoped_ptr ExtensionPrefs::GetInstalledInfoHelper( if (!extension->GetInteger(kPrefLocation, &location_value)) return scoped_ptr(); - base::FilePath::StringType path; - if (!extension->GetString(kPrefPath, &path)) - return scoped_ptr(); - - // Make path absolute. Unpacked extensions will already have absolute paths, - // otherwise make it so. Manifest::Location location = static_cast(location_value); -#if !defined(OS_CHROMEOS) - if (!Manifest::IsUnpackedLocation(location)) { - DCHECK(location == Manifest::COMPONENT || - !base::FilePath(path).IsAbsolute()); -#else - // On Chrome OS some extensions can be installed to shared location and - // thus use absolute paths in prefs. - if (!base::FilePath(path).IsAbsolute()) { -#endif // !defined(OS_CHROMEOS) - path = install_directory_.Append(path).value(); + if (location == Manifest::COMPONENT) { + // Component extensions are ignored. Component extensions may have data + // saved in preferences, but they are already loaded at this point (by + // ComponentLoader) and shouldn't be populated into the result of + // GetInstalledExtensionsInfo, otherwise InstalledLoader would also want to + // load them. + return scoped_ptr(); } // Only the following extension types have data saved in the preferences. @@ -1371,6 +1362,14 @@ scoped_ptr ExtensionPrefs::GetInstalledInfoHelper( // Just a warning for now. } + base::FilePath::StringType path; + if (!extension->GetString(kPrefPath, &path)) + return scoped_ptr(); + + // Make path absolute. Most (but not all) extension types have relative paths. + if (!base::FilePath(path).IsAbsolute()) + path = install_directory_.Append(path).value(); + return scoped_ptr(new ExtensionInfo( manifest, extension_id, base::FilePath(path), location)); } @@ -1384,13 +1383,8 @@ scoped_ptr ExtensionPrefs::GetInstalledExtensionInfo( !extensions->GetDictionaryWithoutPathExpansion(extension_id, &ext)) return scoped_ptr(); int state_value; - if (!ext->GetInteger(kPrefState, &state_value) || - state_value == Extension::ENABLED_COMPONENT) { - // Old preferences files may not have kPrefState for component extensions. - return scoped_ptr(); - } - - if (state_value == Extension::EXTERNAL_EXTENSION_UNINSTALLED) { + if (ext->GetInteger(kPrefState, &state_value) && + state_value == Extension::EXTERNAL_EXTENSION_UNINSTALLED) { LOG(WARNING) << "External extension with id " << extension_id << " has been uninstalled by the user"; return scoped_ptr(); @@ -1984,17 +1978,6 @@ void ExtensionPrefs::RegisterProfilePrefs( pref_names::kLastChromeVersion, std::string(), // default value user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -#if defined(OS_MACOSX) - registry->RegisterDoublePref( - pref_names::kBrowserActionContainerWidth, - 0, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -#else - registry->RegisterIntegerPref( - pref_names::kBrowserActionContainerWidth, - 0, - user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); -#endif registry->RegisterDictionaryPref( kInstallSignature, user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); @@ -2058,12 +2041,7 @@ void ExtensionPrefs::PopulateExtensionInfoPrefs( int install_flags, const std::string& install_parameter, base::DictionaryValue* extension_dict) { - // Leave the state blank for component extensions so that old chrome versions - // loading new profiles do not fail in GetInstalledExtensionInfo. Older - // Chrome versions would only check for an omitted state. - if (initial_state != Extension::ENABLED_COMPONENT) - extension_dict->Set(kPrefState, new base::FundamentalValue(initial_state)); - + extension_dict->Set(kPrefState, new base::FundamentalValue(initial_state)); extension_dict->Set(kPrefLocation, new base::FundamentalValue(extension->location())); extension_dict->Set(kPrefCreationFlags, diff --git a/extensions/browser/extension_system.h b/extensions/browser/extension_system.h index baa8b5d998582..3f297ee1e50d7 100644 --- a/extensions/browser/extension_system.h +++ b/extensions/browser/extension_system.h @@ -27,6 +27,7 @@ namespace extensions { class Blacklist; class ContentVerifier; +class DeclarativeUserScriptMaster; class ErrorConsole; class EventRouter; class Extension; @@ -40,8 +41,8 @@ class OneShotEvent; class ProcessManager; class QuotaService; class RuntimeData; +class SharedUserScriptMaster; class StateStore; -class UserScriptMaster; // ExtensionSystem manages the lifetime of many of the services used by the // extensions and apps system, and it handles startup and shutdown as needed. @@ -72,8 +73,8 @@ class ExtensionSystem : public KeyedService { // The ManagementPolicy is created at startup. virtual ManagementPolicy* management_policy() = 0; - // The UserScriptMaster is created at startup. - virtual UserScriptMaster* user_script_master() = 0; + // The SharedUserScriptMaster is created at startup. + virtual SharedUserScriptMaster* shared_user_script_master() = 0; // The ProcessManager is created at startup. virtual ProcessManager* process_manager() = 0; @@ -135,6 +136,11 @@ class ExtensionSystem : public KeyedService { // so it can be retrieved from ExtensionSystem directly. virtual scoped_ptr GetDependentExtensions( const Extension* extension) = 0; + + // Get the user script master for declarative scripts, if any. + virtual DeclarativeUserScriptMaster* + GetDeclarativeUserScriptMasterByExtension( + const ExtensionId& extension_id) = 0; }; } // namespace extensions diff --git a/extensions/browser/guest_view/OWNERS b/extensions/browser/guest_view/OWNERS new file mode 100644 index 0000000000000..f9575e8fb77b5 --- /dev/null +++ b/extensions/browser/guest_view/OWNERS @@ -0,0 +1,3 @@ +fsamuel@chromium.org +lazyboy@chromium.org +hanxi@chromium.org diff --git a/chrome/browser/guest_view/guest_view.h b/extensions/browser/guest_view/guest_view.h similarity index 88% rename from chrome/browser/guest_view/guest_view.h rename to extensions/browser/guest_view/guest_view.h index b4435001f730d..02355ba8f4763 100644 --- a/chrome/browser/guest_view/guest_view.h +++ b/extensions/browser/guest_view/guest_view.h @@ -2,12 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_H_ -#define CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_H_ +#ifndef EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_H_ #include "base/bind.h" -#include "chrome/browser/guest_view/guest_view_base.h" #include "content/public/browser/render_frame_host.h" +#include "extensions/browser/guest_view/guest_view_base.h" + +namespace extensions { // A GuestView is the templated base class for out-of-process frames in the // chrome layer. GuestView is templated on its derived type to allow for type- @@ -69,4 +71,6 @@ class GuestView : public GuestViewBase { DISALLOW_COPY_AND_ASSIGN(GuestView); }; -#endif // CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_H_ +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_H_ diff --git a/chrome/browser/guest_view/guest_view_base.cc b/extensions/browser/guest_view/guest_view_base.cc similarity index 86% rename from chrome/browser/guest_view/guest_view_base.cc rename to extensions/browser/guest_view/guest_view_base.cc index 1300cfbb50d4b..a1557d55ad8ec 100644 --- a/chrome/browser/guest_view/guest_view_base.cc +++ b/extensions/browser/guest_view/guest_view_base.cc @@ -2,26 +2,29 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/guest_view/guest_view_base.h" +#include "extensions/browser/guest_view/guest_view_base.h" #include "base/lazy_instance.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/guest_view/app_view/app_view_guest.h" -#include "chrome/browser/guest_view/extension_options/extension_options_guest.h" -#include "chrome/browser/guest_view/guest_view_constants.h" -#include "chrome/browser/guest_view/guest_view_manager.h" -#include "chrome/browser/guest_view/web_view/web_view_guest.h" -#include "chrome/common/content_settings.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/web_contents.h" #include "content/public/common/url_constants.h" +#include "extensions/browser/api/extensions_api_client.h" #include "extensions/browser/event_router.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/browser/guest_view/guest_view_constants.h" +#include "extensions/browser/guest_view/guest_view_manager.h" +#include "extensions/browser/process_map.h" +#include "extensions/common/features/feature.h" +#include "extensions/common/features/feature_provider.h" #include "third_party/WebKit/public/web/WebInputEvent.h" using content::WebContents; +namespace extensions { + namespace { typedef std::map @@ -97,27 +100,47 @@ GuestViewBase::GuestViewBase(content::BrowserContext* browser_context, weak_ptr_factory_(this) { } -void GuestViewBase::Init( - const std::string& embedder_extension_id, - int embedder_render_process_id, - const base::DictionaryValue& create_params, - const WebContentsCreatedCallback& callback) { +void GuestViewBase::Init(const std::string& embedder_extension_id, + content::WebContents* embedder_web_contents, + const base::DictionaryValue& create_params, + const WebContentsCreatedCallback& callback) { if (initialized_) return; initialized_ = true; - if (!CanEmbedderUseGuestView(embedder_extension_id)) { + Feature* feature = FeatureProvider::GetAPIFeatures()->GetFeature( + GetAPINamespace()); + CHECK(feature); + + ProcessMap* process_map = ProcessMap::Get(browser_context()); + CHECK(process_map); + + const Extension* embedder_extension = ExtensionRegistry::Get(browser_context_) + ->enabled_extensions() + .GetByID(embedder_extension_id); + // Ok for |embedder_extension| to be NULL, the embedder might be WebUI. + + CHECK(embedder_web_contents); + int embedder_process_id = + embedder_web_contents->GetRenderProcessHost()->GetID(); + + Feature::Availability availability = feature->IsAvailableToContext( + embedder_extension, + process_map->GetMostLikelyContextType(embedder_extension, + embedder_process_id), + embedder_web_contents->GetLastCommittedURL()); + if (!availability.is_available()) { callback.Run(NULL); return; } CreateWebContents(embedder_extension_id, - embedder_render_process_id, + embedder_process_id, create_params, base::Bind(&GuestViewBase::CompleteInit, AsWeakPtr(), embedder_extension_id, - embedder_render_process_id, + embedder_process_id, callback)); } @@ -228,25 +251,6 @@ bool GuestViewBase::IsGuest(WebContents* web_contents) { return !!GuestViewBase::FromWebContents(web_contents); } -// static -void GuestViewBase::GetDefaultContentSettingRules( - RendererContentSettingRules* rules, - bool incognito) { - rules->image_rules.push_back( - ContentSettingPatternSource(ContentSettingsPattern::Wildcard(), - ContentSettingsPattern::Wildcard(), - CONTENT_SETTING_ALLOW, - std::string(), - incognito)); - - rules->script_rules.push_back( - ContentSettingPatternSource(ContentSettingsPattern::Wildcard(), - ContentSettingsPattern::Wildcard(), - CONTENT_SETTING_ALLOW, - std::string(), - incognito)); -} - base::WeakPtr GuestViewBase::AsWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } @@ -389,28 +393,24 @@ GuestViewBase::~GuestViewBase() { void GuestViewBase::DispatchEventToEmbedder(Event* event) { scoped_ptr event_ptr(event); - if (!in_extension()) { - NOTREACHED(); - return; - } if (!attached()) { pending_events_.push_back(linked_ptr(event_ptr.release())); return; } - extensions::EventFilteringInfo info; + EventFilteringInfo info; info.SetInstanceID(view_instance_id_); scoped_ptr args(new base::ListValue()); args->Append(event->GetArguments().release()); - extensions::EventRouter::DispatchEvent( + EventRouter::DispatchEvent( embedder_web_contents_, browser_context_, embedder_extension_id_, event->name(), args.Pass(), - extensions::EventRouter::USER_GESTURE_UNKNOWN, + EventRouter::USER_GESTURE_UNKNOWN, info); } @@ -443,7 +443,7 @@ void GuestViewBase::CompleteInit(const std::string& embedder_extension_id, // static void GuestViewBase::RegisterGuestViewTypes() { - AppViewGuest::Register(); - ExtensionOptionsGuest::Register(); - WebViewGuest::Register(); + ExtensionsAPIClient::Get()->RegisterGuestViewTypes(); } + +} // namespace extensions diff --git a/chrome/browser/guest_view/guest_view_base.h b/extensions/browser/guest_view/guest_view_base.h similarity index 94% rename from chrome/browser/guest_view/guest_view_base.h rename to extensions/browser/guest_view/guest_view_base.h index e42b47ce4a5be..2360ee0bad1fb 100644 --- a/chrome/browser/guest_view/guest_view_base.h +++ b/extensions/browser/guest_view/guest_view_base.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_GUEST_VIEW_GUEST_VIEW_BASE_H_ -#define CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_BASE_H_ +#ifndef EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_BASE_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_BASE_H_ #include @@ -17,6 +17,8 @@ struct RendererContentSettingRules; +namespace extensions { + // A GuestViewBase is the base class browser-side API implementation for a // <*view> tag. GuestViewBase maintains an association between a guest // WebContents and an embedder WebContents. It receives events issued from @@ -67,10 +69,6 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, static bool IsGuest(content::WebContents* web_contents); - // By default, JavaScript and images are enabled in guest content. - static void GetDefaultContentSettingRules(RendererContentSettingRules* rules, - bool incognito); - virtual const char* GetViewType() const = 0; // This method is called after the guest has been attached to an embedder and @@ -141,11 +139,13 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, // to destruction. virtual void WillDestroy() {} - // This method is to be implemented by the derived class. It determines - // whether the guest view type of the derived class can be used by the - // provided embedder extension ID. - virtual bool CanEmbedderUseGuestView( - const std::string& embedder_extension_id) = 0; + // This method is to be implemented by the derived class. Access to guest + // views are determined by the availability of the internal extension API + // used to implement the guest view. + // + // This should be the name of the API as it appears in the _api_features.json + // file. + virtual const char* GetAPINamespace() = 0; // This method is to be implemented by the derived class. Given a set of // initialization parameters, a concrete subclass of GuestViewBase can @@ -161,7 +161,7 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, // This creates a WebContents and initializes |this| GuestViewBase to use the // newly created WebContents. void Init(const std::string& embedder_extension_id, - int embedder_render_process_id, + content::WebContents* embedder_web_contents, const base::DictionaryValue& create_params, const WebContentsCreatedCallback& callback); @@ -330,4 +330,6 @@ class GuestViewBase : public content::BrowserPluginGuestDelegate, DISALLOW_COPY_AND_ASSIGN(GuestViewBase); }; -#endif // CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_BASE_H_ +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_BASE_H_ diff --git a/chrome/browser/guest_view/guest_view_constants.cc b/extensions/browser/guest_view/guest_view_constants.cc similarity index 90% rename from chrome/browser/guest_view/guest_view_constants.cc rename to extensions/browser/guest_view/guest_view_constants.cc index b12c41c6d3a80..e8669bb3822d7 100644 --- a/chrome/browser/guest_view/guest_view_constants.cc +++ b/extensions/browser/guest_view/guest_view_constants.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/guest_view/guest_view_constants.h" +#include "extensions/browser/guest_view/guest_view_constants.h" namespace guestview { diff --git a/chrome/browser/guest_view/guest_view_constants.h b/extensions/browser/guest_view/guest_view_constants.h similarity index 76% rename from chrome/browser/guest_view/guest_view_constants.h rename to extensions/browser/guest_view/guest_view_constants.h index a32d0397a2b5f..1689c14bc83e7 100644 --- a/chrome/browser/guest_view/guest_view_constants.h +++ b/extensions/browser/guest_view/guest_view_constants.h @@ -4,8 +4,8 @@ // Constants used for the WebView API. -#ifndef CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_CONSTANTS_H_ -#define CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_CONSTANTS_H_ +#ifndef EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_CONSTANTS_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_CONSTANTS_H_ namespace guestview { @@ -25,5 +25,5 @@ extern const int kInstanceIDNone; } // namespace guestview -#endif // CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_CONSTANTS_H_ +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_CONSTANTS_H_ diff --git a/chrome/browser/guest_view/guest_view_manager.cc b/extensions/browser/guest_view/guest_view_manager.cc similarity index 91% rename from chrome/browser/guest_view/guest_view_manager.cc rename to extensions/browser/guest_view/guest_view_manager.cc index ac5473534c324..3a2412e43d93e 100644 --- a/chrome/browser/guest_view/guest_view_manager.cc +++ b/extensions/browser/guest_view/guest_view_manager.cc @@ -2,15 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/guest_view/guest_view_manager.h" +#include "extensions/browser/guest_view/guest_view_manager.h" #include "base/strings/stringprintf.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/guest_view/guest_view_base.h" -#include "chrome/browser/guest_view/guest_view_constants.h" -#include "chrome/browser/guest_view/guest_view_manager_factory.h" -#include "chrome/browser/guest_view/web_view/web_view_guest.h" -#include "chrome/browser/profiles/profile.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/user_metrics.h" @@ -18,6 +12,9 @@ #include "content/public/common/result_codes.h" #include "content/public/common/url_constants.h" #include "extensions/browser/extension_system.h" +#include "extensions/browser/guest_view/guest_view_base.h" +#include "extensions/browser/guest_view/guest_view_constants.h" +#include "extensions/browser/guest_view/guest_view_manager_factory.h" #include "net/base/escape.h" #include "url/gurl.h" @@ -25,6 +22,8 @@ using content::BrowserContext; using content::SiteInstance; using content::WebContents; +namespace extensions { + // static GuestViewManagerFactory* GuestViewManager::factory_ = NULL; @@ -65,12 +64,11 @@ int GuestViewManager::GetNextInstanceID() { return ++current_instance_id_; } -void GuestViewManager::CreateGuest( - const std::string& view_type, - const std::string& embedder_extension_id, - int embedder_render_process_id, - const base::DictionaryValue& create_params, - const WebContentsCreatedCallback& callback) { +void GuestViewManager::CreateGuest(const std::string& view_type, + const std::string& embedder_extension_id, + content::WebContents* embedder_web_contents, + const base::DictionaryValue& create_params, + const WebContentsCreatedCallback& callback) { int guest_instance_id = GetNextInstanceID(); GuestViewBase* guest = GuestViewBase::Create(context_, guest_instance_id, view_type); @@ -78,10 +76,8 @@ void GuestViewManager::CreateGuest( callback.Run(NULL); return; } - guest->Init(embedder_extension_id, - embedder_render_process_id, - create_params, - callback); + guest->Init( + embedder_extension_id, embedder_web_contents, create_params, callback); } content::WebContents* GuestViewManager::CreateGuestWithWebContentsParams( @@ -242,3 +238,5 @@ bool GuestViewManager::CanEmbedderAccessInstanceID( return embedder_render_process_id == guest_view->embedder_render_process_id(); } + +} // namespace extensions diff --git a/chrome/browser/guest_view/guest_view_manager.h b/extensions/browser/guest_view/guest_view_manager.h similarity index 87% rename from chrome/browser/guest_view/guest_view_manager.h rename to extensions/browser/guest_view/guest_view_manager.h index bf46ae9e3b6e0..44f4249a9babd 100644 --- a/chrome/browser/guest_view/guest_view_manager.h +++ b/extensions/browser/guest_view/guest_view_manager.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_GUEST_VIEW_GUEST_VIEW_MANAGER_H_ -#define CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_H_ +#ifndef EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_H_ #include @@ -14,18 +14,16 @@ #include "content/public/browser/site_instance.h" #include "content/public/browser/web_contents.h" -class AppViewGuest; -class GuestViewBase; -class GuestViewManagerFactory; class GURL; namespace content { class BrowserContext; +class WebContents; } // namespace content -namespace guestview { -class TestGuestViewManager; -} // namespace guestview +namespace extensions{ +class GuestViewBase; +class GuestViewManagerFactory; class GuestViewManager : public content::BrowserPluginGuestManager, public base::SupportsUserData::Data { @@ -53,12 +51,11 @@ class GuestViewManager : public content::BrowserPluginGuestManager, typedef base::Callback WebContentsCreatedCallback; - void CreateGuest( - const std::string& view_type, - const std::string& embedder_extension_id, - int embedder_render_process_id, - const base::DictionaryValue& create_params, - const WebContentsCreatedCallback& callback); + void CreateGuest(const std::string& view_type, + const std::string& embedder_extension_id, + content::WebContents* embedder_web_contents, + const base::DictionaryValue& create_params, + const WebContentsCreatedCallback& callback); content::WebContents* CreateGuestWithWebContentsParams( const std::string& view_type, @@ -78,9 +75,7 @@ class GuestViewManager : public content::BrowserPluginGuestManager, const GuestCallback& callback) OVERRIDE; protected: - friend class AppViewGuest; friend class GuestViewBase; - friend class guestview::TestGuestViewManager; FRIEND_TEST_ALL_PREFIXES(GuestViewManagerTest, AddRemove); // Can be overriden in tests. @@ -127,4 +122,6 @@ class GuestViewManager : public content::BrowserPluginGuestManager, DISALLOW_COPY_AND_ASSIGN(GuestViewManager); }; -#endif // CHROME_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_H_ +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_H_ diff --git a/extensions/browser/guest_view/guest_view_manager_factory.h b/extensions/browser/guest_view/guest_view_manager_factory.h new file mode 100644 index 0000000000000..1d9a660d720ac --- /dev/null +++ b/extensions/browser/guest_view/guest_view_manager_factory.h @@ -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. + +#ifndef EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_FACTORY_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_FACTORY_H_ + +namespace extensions { + +class GuestViewManagerFactory { + public: + virtual GuestViewManager* CreateGuestViewManager( + content::BrowserContext* context) = 0; + + protected: + virtual ~GuestViewManagerFactory() {} +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_GUEST_VIEW_MANAGER_FACTORY_H_ + diff --git a/chrome/browser/guest_view/guest_view_manager_unittest.cc b/extensions/browser/guest_view/guest_view_manager_unittest.cc similarity index 78% rename from chrome/browser/guest_view/guest_view_manager_unittest.cc rename to extensions/browser/guest_view/guest_view_manager_unittest.cc index 05b53d581bbf4..aef377603d3eb 100644 --- a/chrome/browser/guest_view/guest_view_manager_unittest.cc +++ b/extensions/browser/guest_view/guest_view_manager_unittest.cc @@ -2,16 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/browser/guest_view/guest_view_manager.h" +#include "extensions/browser/guest_view/guest_view_manager.h" -#include "chrome/test/base/testing_profile.h" +#include "content/public/browser/notification_service.h" +#include "content/public/test/test_browser_context.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/web_contents_tester.h" -#include "testing/gtest/include/gtest/gtest.h" +#include "extensions/browser/extensions_test.h" using content::WebContents; using content::WebContentsTester; +namespace extensions { + namespace guestview { // This class allows us to access some private variables in @@ -38,19 +41,21 @@ class TestGuestViewManager : public GuestViewManager { namespace { -class GuestViewManagerTest : public testing::Test { +class GuestViewManagerTest : public extensions::ExtensionsTest { public: - GuestViewManagerTest() {} + GuestViewManagerTest() : + notification_service_(content::NotificationService::Create()) {} virtual ~GuestViewManagerTest() {} scoped_ptr CreateWebContents() { return scoped_ptr( - WebContentsTester::CreateTestWebContents(&profile_, NULL)); + WebContentsTester::CreateTestWebContents(&browser_context_, NULL)); } private: + scoped_ptr notification_service_; content::TestBrowserThreadBundle thread_bundle_; - TestingProfile profile_; + content::TestBrowserContext browser_context_; DISALLOW_COPY_AND_ASSIGN(GuestViewManagerTest); }; @@ -58,9 +63,9 @@ class GuestViewManagerTest : public testing::Test { } // namespace TEST_F(GuestViewManagerTest, AddRemove) { - TestingProfile profile; + content::TestBrowserContext browser_context; scoped_ptr manager( - new guestview::TestGuestViewManager(&profile)); + new guestview::TestGuestViewManager(&browser_context)); scoped_ptr web_contents1(CreateWebContents()); scoped_ptr web_contents2(CreateWebContents()); @@ -100,3 +105,5 @@ TEST_F(GuestViewManagerTest, AddRemove) { EXPECT_EQ(0u, manager->GetRemovedInstanceIdSize()); } + +} // namespace extensions diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.cc b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.cc new file mode 100644 index 0000000000000..d53d64d1f861e --- /dev/null +++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.cc @@ -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. + +#include "extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h" + +namespace extensions { + +WebViewPermissionHelperDelegate::WebViewPermissionHelperDelegate( + content::WebContents* contents) + : content::WebContentsObserver(contents) { +} + +WebViewPermissionHelperDelegate::~WebViewPermissionHelperDelegate() { +} + +} // namespace extensions diff --git a/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h new file mode 100644 index 0000000000000..ac0aefc75d1b3 --- /dev/null +++ b/extensions/browser/guest_view/web_view/web_view_permission_helper_delegate.h @@ -0,0 +1,92 @@ +// 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 EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIWE_PERMISSION_HELPER_DELEGATE_H_ +#define EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIWE_PERMISSION_HELPER_DELEGATE_H_ + +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_contents_observer.h" +#include "content/public/common/media_stream_request.h" + +namespace extensions { + +// A delegate class of WebViewPermissionHelper to request permissions that are +// not a part of extensions. +class WebViewPermissionHelperDelegate : public content::WebContentsObserver { + public: + explicit WebViewPermissionHelperDelegate(content::WebContents* contents); + virtual ~WebViewPermissionHelperDelegate(); + + virtual void RequestMediaAccessPermission( + content::WebContents* source, + const content::MediaStreamRequest& request, + const content::MediaResponseCallback& callback) {} + + virtual void CanDownload( + content::RenderViewHost* render_view_host, + const GURL& url, + const std::string& request_method, + const base::Callback& callback) {} + + virtual void RequestPointerLockPermission( + bool user_gesture, + bool last_unlocked_by_target, + const base::Callback& callback) {} + + // Requests Geolocation Permission from the embedder. + virtual void RequestGeolocationPermission( + int bridge_id, + const GURL& requesting_frame, + bool user_gesture, + const base::Callback& callback) {} + + virtual void CancelGeolocationPermissionRequest(int bridge_id) {} + + virtual void RequestFileSystemPermission( + const GURL& url, + bool allowed_by_default, + const base::Callback& callback) {} + + // Called when file system access is requested by the guest content using the + // asynchronous HTML5 file system API. The request is plumbed through the + // permission request API. The request will be: + // - Allowed if the embedder explicitly allowed it. + // - Denied if the embedder explicitly denied. + // - Determined by the guest's content settings if the embedder does not + // perform an explicit action. + // If access was blocked due to the page's content settings, + // |blocked_by_policy| should be true, and this function should invoke + // OnContentBlocked. + virtual void FileSystemAccessedAsync( + int render_process_id, + int render_frame_id, + int request_id, + const GURL& url, + bool blocked_by_policy) {} + + // Called when file system access is requested by the guest content using the + // synchronous HTML5 file system API in a worker thread or shared worker. The + // request is plumbed through the permission request API. The + // request will be: + // - Allowed if the embedder explicitly allowed it. + // - Denied if the embedder explicitly denied. + // - Determined by the guest's content settings if the embedder does not + // perform an explicit action. + // If access was blocked due to the page's content settings, + // |blocked_by_policy| should be true, and this function should invoke + // OnContentBlocked. + virtual void FileSystemAccessedSync( + int render_process_id, + int render_frame_id, + const GURL& url, + bool blocked_by_policy, + IPC::Message* reply_msg) {} + + private: + DISALLOW_COPY_AND_ASSIGN(WebViewPermissionHelperDelegate); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_GUEST_VIEW_WEB_VIEW_WEB_VIWE_PERMISSION_HELPER_DELEGATE_H_ diff --git a/extensions/browser/info_map.cc b/extensions/browser/info_map.cc index 1042f63812764..ced52c9e978fe 100644 --- a/extensions/browser/info_map.cc +++ b/extensions/browser/info_map.cc @@ -4,13 +4,17 @@ #include "extensions/browser/info_map.h" +#include "base/strings/string_util.h" #include "content/public/browser/browser_thread.h" #include "extensions/browser/content_verifier.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" +#include "extensions/common/extension_resource.h" #include "extensions/common/extension_set.h" #include "extensions/common/manifest_handlers/incognito_info.h" +#include "extensions/common/manifest_handlers/shared_module_info.h" #include "extensions/common/permissions/permissions_data.h" +#include "url/gurl.h" using content::BrowserThread; @@ -36,16 +40,13 @@ struct InfoMap::ExtraData { ~ExtraData(); }; -InfoMap::ExtraData::ExtraData() : incognito_enabled(false) {} +InfoMap::ExtraData::ExtraData() + : incognito_enabled(false), notifications_disabled(false) { +} InfoMap::ExtraData::~ExtraData() {} -InfoMap::InfoMap() : signin_process_id_(-1) {} - -const ProcessMap& InfoMap::process_map() const { return process_map_; } - -const ProcessMap& InfoMap::worker_process_map() const { - return worker_process_map_; +InfoMap::InfoMap() : signin_process_id_(-1) { } void InfoMap::AddExtension(const Extension* extension, @@ -179,6 +180,66 @@ bool InfoMap::SecurityOriginHasAPIPermission(const GURL& origin, return !extensions.is_empty(); } +// This function is security sensitive. Bugs could cause problems that break +// restrictions on local file access or NaCl's validation caching. If you modify +// this function, please get a security review from a NaCl person. +bool InfoMap::MapUrlToLocalFilePath(const GURL& file_url, + bool use_blocking_api, + base::FilePath* file_path) { + // Check that the URL is recognized by the extension system. + const Extension* extension = extensions_.GetExtensionOrAppByURL(file_url); + if (!extension) + return false; + + // This is a short-cut which avoids calling a blocking file operation + // (GetFilePath()), so that this can be called on the IO thread. It only + // handles a subset of the urls. + if (!use_blocking_api) { + if (file_url.SchemeIs(extensions::kExtensionScheme)) { + std::string path = file_url.path(); + base::TrimString(path, "/", &path); // Remove first slash + *file_path = extension->path().AppendASCII(path); + return true; + } + return false; + } + + std::string path = file_url.path(); + ExtensionResource resource; + + if (SharedModuleInfo::IsImportedPath(path)) { + // Check if this is a valid path that is imported for this extension. + std::string new_extension_id; + std::string new_relative_path; + SharedModuleInfo::ParseImportedPath( + path, &new_extension_id, &new_relative_path); + const Extension* new_extension = extensions_.GetByID(new_extension_id); + if (!new_extension) + return false; + + if (!SharedModuleInfo::ImportsExtensionById(extension, new_extension_id) || + !SharedModuleInfo::IsExportAllowed(new_extension, new_relative_path)) { + return false; + } + + resource = new_extension->GetResource(new_relative_path); + } else { + // Check that the URL references a resource in the extension. + resource = extension->GetResource(path); + } + + if (resource.empty()) + return false; + + // GetFilePath is a blocking function call. + const base::FilePath resource_file_path = resource.GetFilePath(); + if (resource_file_path.empty()) + return false; + + *file_path = resource_file_path; + return true; +} + QuotaService* InfoMap::GetQuotaService() { CheckOnValidThread(); if (!quota_service_) diff --git a/extensions/browser/info_map.h b/extensions/browser/info_map.h index 5b1f8a37f55b7..7c4267c0fe5e7 100644 --- a/extensions/browser/info_map.h +++ b/extensions/browser/info_map.h @@ -16,6 +16,10 @@ #include "extensions/common/extension_set.h" #include "extensions/common/permissions/api_permission.h" +namespace base { +class FilePath; +} + namespace extensions { class ContentVerifier; class Extension; @@ -33,9 +37,9 @@ class InfoMap : public base::RefCountedThreadSafe { } // Information about which extensions are assigned to which render processes. - const extensions::ProcessMap& process_map() const; + const ProcessMap& process_map() const { return process_map_; } // Information about which extensions are assigned to which worker processes. - const extensions::ProcessMap& worker_process_map() const; + const ProcessMap& worker_process_map() const { return worker_process_map_; } // Callback for when new extensions are loaded. void AddExtension(const extensions::Extension* extension, @@ -92,6 +96,13 @@ class InfoMap : public base::RefCountedThreadSafe { extensions::APIPermission::ID permission) const; + // Maps a |file_url| to a |file_path| on the local filesystem, including + // resources in extensions. Returns true on success. See NaClBrowserDelegate + // for full details. + bool MapUrlToLocalFilePath(const GURL& file_url, + bool use_blocking_api, + base::FilePath* file_path); + // Returns the IO thread QuotaService. Creates the instance on first call. QuotaService* GetQuotaService(); diff --git a/extensions/browser/info_map_unittest.cc b/extensions/browser/info_map_unittest.cc index cbbeb3fe770c6..7247706068c48 100644 --- a/extensions/browser/info_map_unittest.cc +++ b/extensions/browser/info_map_unittest.cc @@ -33,11 +33,9 @@ class InfoMapTest : public testing::Test { // Returns a barebones test Extension object with the given name. static scoped_refptr CreateExtension(const std::string& name) { -#if defined(OS_WIN) - base::FilePath path(FILE_PATH_LITERAL("c:\\foo")); -#elif defined(OS_POSIX) - base::FilePath path(FILE_PATH_LITERAL("/foo")); -#endif + base::FilePath path; + PathService::Get(chrome::DIR_TEST_DATA, &path); + path = path.AppendASCII("extensions"); base::DictionaryValue manifest; manifest.SetString(keys::kVersion, "1.0.0.0"); @@ -183,4 +181,38 @@ TEST_F(InfoMapTest, TestNotificationsDisabled) { info_map->SetNotificationsDisabled(app->id(), false); } +// Tests that extension URLs are properly mapped to local file paths. +TEST_F(InfoMapTest, MapUrlToLocalFilePath) { + scoped_refptr info_map(new InfoMap()); + scoped_refptr app(CreateExtension("app")); + info_map->AddExtension(app.get(), base::Time(), false, false); + + // Non-extension URLs don't map to anything. + base::FilePath non_extension_path; + GURL non_extension_url("http://not-an-extension.com/"); + EXPECT_FALSE(info_map->MapUrlToLocalFilePath( + non_extension_url, false, &non_extension_path)); + EXPECT_TRUE(non_extension_path.empty()); + + // Valid resources return a valid path. + base::FilePath valid_path; + GURL valid_url = app->GetResourceURL("manifest.json"); + EXPECT_TRUE(info_map->MapUrlToLocalFilePath( + valid_url, true /* use_blocking_api */, &valid_path)); + EXPECT_FALSE(valid_path.empty()); + + // A file must exist to be mapped to a path using the blocking API. + base::FilePath does_not_exist_path; + GURL does_not_exist_url = app->GetResourceURL("does-not-exist.html"); + EXPECT_FALSE(info_map->MapUrlToLocalFilePath( + does_not_exist_url, true /* use_blocking_api */, &does_not_exist_path)); + EXPECT_TRUE(does_not_exist_path.empty()); + + // A file does not need to exist to be mapped to a path with the non-blocking + // API. This avoids hitting the disk to see if it exists. + EXPECT_TRUE(info_map->MapUrlToLocalFilePath( + does_not_exist_url, false /* use_blocking_api */, &does_not_exist_path)); + EXPECT_FALSE(does_not_exist_path.empty()); +} + } // namespace extensions diff --git a/extensions/browser/lazy_background_task_queue_unittest.cc b/extensions/browser/lazy_background_task_queue_unittest.cc index d215007af33e4..200fb79f57df8 100644 --- a/extensions/browser/lazy_background_task_queue_unittest.cc +++ b/extensions/browser/lazy_background_task_queue_unittest.cc @@ -13,11 +13,11 @@ #include "extensions/browser/extension_system.h" #include "extensions/browser/extension_system_provider.h" #include "extensions/browser/extensions_test.h" +#include "extensions/browser/mock_extension_system.h" #include "extensions/browser/process_manager.h" #include "extensions/browser/test_extensions_browser_client.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" -#include "extensions/common/one_shot_event.h" #include "testing/gtest/include/gtest/gtest.h" using content::BrowserContext; @@ -53,70 +53,18 @@ class TestProcessManager : public ProcessManager { }; // A simple ExtensionSystem that returns a TestProcessManager. -class MockExtensionSystem : public ExtensionSystem { +class MockExtensionSystemWithProcessManager : public MockExtensionSystem { public: - explicit MockExtensionSystem(BrowserContext* context) - : test_process_manager_(context) {} - virtual ~MockExtensionSystem() {} - - virtual void InitForRegularProfile(bool extensions_enabled) OVERRIDE {} - virtual ExtensionService* extension_service() OVERRIDE { return NULL; } - virtual RuntimeData* runtime_data() OVERRIDE { return NULL; } - virtual ManagementPolicy* management_policy() OVERRIDE { return NULL; } - virtual UserScriptMaster* user_script_master() OVERRIDE { return NULL; } + explicit MockExtensionSystemWithProcessManager(BrowserContext* context) + : MockExtensionSystem(context), test_process_manager_(context) {} + virtual ~MockExtensionSystemWithProcessManager() {} + virtual ProcessManager* process_manager() OVERRIDE { return &test_process_manager_; } - virtual StateStore* state_store() OVERRIDE { return NULL; } - virtual StateStore* rules_store() OVERRIDE { return NULL; } - virtual InfoMap* info_map() OVERRIDE { return NULL; } - virtual LazyBackgroundTaskQueue* lazy_background_task_queue() OVERRIDE { - return NULL; - } - virtual EventRouter* event_router() OVERRIDE { return NULL; } - virtual ExtensionWarningService* warning_service() OVERRIDE { return NULL; } - virtual Blacklist* blacklist() OVERRIDE { return NULL; } - virtual ErrorConsole* error_console() OVERRIDE { return NULL; } - virtual InstallVerifier* install_verifier() OVERRIDE { return NULL; } - virtual QuotaService* quota_service() OVERRIDE { return NULL; } - virtual const OneShotEvent& ready() const OVERRIDE { return ready_; } - virtual ContentVerifier* content_verifier() OVERRIDE { return NULL; } - virtual scoped_ptr GetDependentExtensions( - const Extension* extension) OVERRIDE { - return scoped_ptr(); - } private: TestProcessManager test_process_manager_; - OneShotEvent ready_; -}; - -// A factory to create a MockExtensionSystem. -class MockExtensionSystemFactory : public ExtensionSystemProvider { - public: - MockExtensionSystemFactory() - : ExtensionSystemProvider( - "MockExtensionSystem", - BrowserContextDependencyManager::GetInstance()) { - DependsOn(ExtensionRegistryFactory::GetInstance()); - } - virtual ~MockExtensionSystemFactory() {} - - // BrowserContextKeyedServiceFactory overrides: - virtual KeyedService* BuildServiceInstanceFor( - BrowserContext* context) const OVERRIDE { - return new MockExtensionSystem(context); - } - - // ExtensionSystemProvider overrides: - virtual ExtensionSystem* GetForBrowserContext( - BrowserContext* context) OVERRIDE { - return static_cast( - GetServiceForBrowserContext(context, true)); - } - - private: - DISALLOW_COPY_AND_ASSIGN(MockExtensionSystemFactory); }; } // namespace @@ -172,7 +120,8 @@ class LazyBackgroundTaskQueueTest : public ExtensionsTest { private: scoped_ptr notification_service_; - MockExtensionSystemFactory extension_system_factory_; + MockExtensionSystemFactory + extension_system_factory_; // The total number of pending tasks that have been executed. int task_run_count_; diff --git a/extensions/browser/mock_extension_system.cc b/extensions/browser/mock_extension_system.cc new file mode 100644 index 0000000000000..d11c398dc87a8 --- /dev/null +++ b/extensions/browser/mock_extension_system.cc @@ -0,0 +1,104 @@ +// 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 "extensions/browser/mock_extension_system.h" + +#include "extensions/common/extension_set.h" + +namespace extensions { + +// +// MockExtensionSystem +// + +MockExtensionSystem::MockExtensionSystem(content::BrowserContext* context) + : browser_context_(context) { +} + +MockExtensionSystem::~MockExtensionSystem() { +} + +void MockExtensionSystem::InitForRegularProfile(bool extensions_enabled) { +} + +ExtensionService* MockExtensionSystem::extension_service() { + return NULL; +} + +RuntimeData* MockExtensionSystem::runtime_data() { + return NULL; +} + +ManagementPolicy* MockExtensionSystem::management_policy() { + return NULL; +} + +SharedUserScriptMaster* MockExtensionSystem::shared_user_script_master() { + return NULL; +} + +ProcessManager* MockExtensionSystem::process_manager() { + return NULL; +} + +StateStore* MockExtensionSystem::state_store() { + return NULL; +} + +StateStore* MockExtensionSystem::rules_store() { + return NULL; +} + +InfoMap* MockExtensionSystem::info_map() { + return NULL; +} + +LazyBackgroundTaskQueue* MockExtensionSystem::lazy_background_task_queue() { + return NULL; +} + +EventRouter* MockExtensionSystem::event_router() { + return NULL; +} + +ExtensionWarningService* MockExtensionSystem::warning_service() { + return NULL; +} + +Blacklist* MockExtensionSystem::blacklist() { + return NULL; +} + +ErrorConsole* MockExtensionSystem::error_console() { + return NULL; +} + +InstallVerifier* MockExtensionSystem::install_verifier() { + return NULL; +} + +QuotaService* MockExtensionSystem::quota_service() { + return NULL; +} + +const OneShotEvent& MockExtensionSystem::ready() const { + return ready_; +} + +ContentVerifier* MockExtensionSystem::content_verifier() { + return NULL; +} + +DeclarativeUserScriptMaster* + MockExtensionSystem::GetDeclarativeUserScriptMasterByExtension( + const ExtensionId& extension_id) { + return NULL; +} + +scoped_ptr MockExtensionSystem::GetDependentExtensions( + const Extension* extension) { + return scoped_ptr(); +} + +} // namespace extensions diff --git a/extensions/browser/mock_extension_system.h b/extensions/browser/mock_extension_system.h new file mode 100644 index 0000000000000..4b227bd853799 --- /dev/null +++ b/extensions/browser/mock_extension_system.h @@ -0,0 +1,95 @@ +// 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 EXTENSIONS_BROWSER_MOCK_EXTENSIONS_SYSTEM_H_ +#define EXTENSIONS_BROWSER_MOCK_EXTENSIONS_SYSTEM_H_ + +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/extension_registry_factory.h" +#include "extensions/browser/extension_system.h" +#include "extensions/browser/extension_system_provider.h" +#include "extensions/common/one_shot_event.h" + +namespace extensions { + +// An empty ExtensionSystem for testing. Tests that need only specific +// parts of ExtensionSystem should derive from this class and override +// functions as needed. To use this, use +// TestExtensionsBrowserClient::set_extension_system_factory +// with the MockExtensionSystemFactory below. +class MockExtensionSystem : public ExtensionSystem { + public: + explicit MockExtensionSystem(content::BrowserContext* context); + virtual ~MockExtensionSystem(); + + content::BrowserContext* browser_context() { return browser_context_; } + + // ExtensionSystem overrides: + virtual void InitForRegularProfile(bool extensions_enabled) OVERRIDE; + virtual ExtensionService* extension_service() OVERRIDE; + virtual RuntimeData* runtime_data() OVERRIDE; + virtual ManagementPolicy* management_policy() OVERRIDE; + virtual SharedUserScriptMaster* shared_user_script_master() OVERRIDE; + virtual ProcessManager* process_manager() OVERRIDE; + virtual StateStore* state_store() OVERRIDE; + virtual StateStore* rules_store() OVERRIDE; + virtual InfoMap* info_map() OVERRIDE; + virtual LazyBackgroundTaskQueue* lazy_background_task_queue() OVERRIDE; + virtual EventRouter* event_router() OVERRIDE; + virtual ExtensionWarningService* warning_service() OVERRIDE; + virtual Blacklist* blacklist() OVERRIDE; + virtual ErrorConsole* error_console() OVERRIDE; + virtual InstallVerifier* install_verifier() OVERRIDE; + virtual QuotaService* quota_service() OVERRIDE; + virtual const OneShotEvent& ready() const OVERRIDE; + virtual ContentVerifier* content_verifier() OVERRIDE; + virtual scoped_ptr GetDependentExtensions( + const Extension* extension) OVERRIDE; + virtual DeclarativeUserScriptMaster* + GetDeclarativeUserScriptMasterByExtension( + const ExtensionId& extension_id) OVERRIDE; + + private: + content::BrowserContext* browser_context_; + OneShotEvent ready_; + + DISALLOW_COPY_AND_ASSIGN(MockExtensionSystem); +}; + +// A factory to create a MockExtensionSystem. Sample use: +// +// MockExtensionSystemFactory factory; +// TestExtensionsBrowserClient::set_extension_system_factory(factory); +template +class MockExtensionSystemFactory : public ExtensionSystemProvider { + public: + MockExtensionSystemFactory() + : ExtensionSystemProvider( + "MockExtensionSystem", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ExtensionRegistryFactory::GetInstance()); + } + + virtual ~MockExtensionSystemFactory() {} + + // BrowserContextKeyedServiceFactory overrides: + virtual KeyedService* BuildServiceInstanceFor( + content::BrowserContext* context) const OVERRIDE { + return new T(context); + } + + // ExtensionSystemProvider overrides: + virtual ExtensionSystem* GetForBrowserContext( + content::BrowserContext* context) OVERRIDE { + return static_cast( + GetServiceForBrowserContext(context, true)); + } + + private: + DISALLOW_COPY_AND_ASSIGN(MockExtensionSystemFactory); +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_MOCK_EXTENSIONS_SYSTEM_H_ diff --git a/extensions/browser/pref_names.cc b/extensions/browser/pref_names.cc index b693d218e0b2c..f08b689420b92 100644 --- a/extensions/browser/pref_names.cc +++ b/extensions/browser/pref_names.cc @@ -32,8 +32,6 @@ const char kAllowedInstallSites[] = "extensions.allowed_install_sites"; const char kAllowedTypes[] = "extensions.allowed_types"; const char kBookmarkAppCreationLaunchType[] = "extensions.bookmark_app_creation_launch_type"; -const char kBrowserActionContainerWidth[] = - "extensions.browseractions.container.width"; const char kExtensions[] = "extensions.settings"; const char kInstallAllowList[] = "extensions.install.allowlist"; const char kInstallDenyList[] = "extensions.install.denylist"; diff --git a/extensions/browser/pref_names.h b/extensions/browser/pref_names.h index 131a93936e4fc..20f39f7493007 100644 --- a/extensions/browser/pref_names.h +++ b/extensions/browser/pref_names.h @@ -40,10 +40,6 @@ extern const char kAllowedTypes[]; // by default. extern const char kBookmarkAppCreationLaunchType[]; -// Integer boolean representing the width (in pixels) of the container for -// browser actions. -extern const char kBrowserActionContainerWidth[]; - // Dictionary pref that keeps track of per-extension settings. The keys are // extension ids. extern const char kExtensions[]; diff --git a/extensions/browser/process_manager.cc b/extensions/browser/process_manager.cc index 5ba8c13eadf7a..fc45e6933cd0a 100644 --- a/extensions/browser/process_manager.cc +++ b/extensions/browser/process_manager.cc @@ -759,16 +759,14 @@ void ProcessManager::Observe(int type, void ProcessManager::OnDevToolsStateChanged( content::DevToolsAgentHost* agent_host, bool attached) { - RenderViewHost* rvh = agent_host->GetRenderViewHost(); + WebContents* web_contents = agent_host->GetWebContents(); // Ignore unrelated notifications. - if (!rvh || - rvh->GetSiteInstance()->GetProcess()->GetBrowserContext() != - GetBrowserContext()) + if (!web_contents || web_contents->GetBrowserContext() != GetBrowserContext()) return; - if (GetViewType(WebContents::FromRenderViewHost(rvh)) != - VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) + if (GetViewType(web_contents) != VIEW_TYPE_EXTENSION_BACKGROUND_PAGE) return; - const Extension* extension = GetExtensionForRenderViewHost(rvh); + const Extension* extension = + GetExtensionForRenderViewHost(web_contents->GetRenderViewHost()); if (!extension) return; if (attached) { diff --git a/extensions/browser/process_map.cc b/extensions/browser/process_map.cc index 12fb5380916ba..0850bc6b27986 100644 --- a/extensions/browser/process_map.cc +++ b/extensions/browser/process_map.cc @@ -123,8 +123,9 @@ std::set ProcessMap::GetExtensionsInProcess(int process_id) const { return result; } -Feature::Context ProcessMap::GuessContextType(const Extension* extension, - int process_id) const { +Feature::Context ProcessMap::GetMostLikelyContextType( + const Extension* extension, + int process_id) const { // WARNING: This logic must match Dispatcher::ClassifyJavaScriptContext, as // much as possible. diff --git a/extensions/browser/process_map.h b/extensions/browser/process_map.h index d7ae8b39e61c4..731bf4f802b6f 100644 --- a/extensions/browser/process_map.h +++ b/extensions/browser/process_map.h @@ -95,24 +95,36 @@ class ProcessMap : public KeyedService { std::set GetExtensionsInProcess(int process_id) const; - // Guesses the most permissive context type for the process with ID - // |process_id|. Context types are renderer (JavaScript) concepts but the - // browser can do a decent job in guessing what the process hosts. + // Gets the most likely context type for the process with ID |process_id| + // which hosts Extension |extension|, if any (may be NULL). Context types are + // renderer (JavaScript) concepts but the browser can do a decent job in + // guessing what the process hosts. // + // |extension| is the funky part - unfortunately we need to trust the + // caller of this method to be correct that indeed the context does feature + // an extension. This matters for iframes, where an extension could be + // hosted in another extension's process (privilege level needs to be + // downgraded) or in a web page's process (privilege level needs to be + // upgraded). + // + // The latter of these is slightly problematic from a security perspective; + // if a web page renderer gets owned it could try to pretend it's an + // extension and get access to some unprivileged APIs. Luckly, when OOP + // iframes lauch, it won't be an issue. + // + // Anyhow, the expected behaviour is: // - For hosted app processes, this will be blessed_web_page. // - For other extension processes, this will be blessed_extension. // - For WebUI processes, this will be a webui. - // - For anything else we have the choice of unblessed_extension or + // - For any other extension we have the choice of unblessed_extension or // content_script. Since content scripts are more common, guess that. // We *could* in theory track which web processes have extension frames // in them, and those would be unblessed_extension, but we don't at the // moment, and once OOP iframes exist then there won't even be such a // thing as an unblessed_extension context. - // - // |extension| isn't used to upgrade the process trust level, but rather used - // as a tiebreaker if a process is found to contain multiple extensions. - Feature::Context GuessContextType(const Extension* extension, - int process_id) const; + // - For anything else, web_page. + Feature::Context GetMostLikelyContextType(const Extension* extension, + int process_id) const; private: struct Item; diff --git a/extensions/browser/script_execution_observer.h b/extensions/browser/script_execution_observer.h new file mode 100644 index 0000000000000..6c7b5cace51f5 --- /dev/null +++ b/extensions/browser/script_execution_observer.h @@ -0,0 +1,44 @@ +// 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 CHROME_BROWSER_EXTENSIONS_SCRIPT_EXECUTION_OBSERVER_H_ +#define CHROME_BROWSER_EXTENSIONS_SCRIPT_EXECUTION_OBSERVER_H_ + +#include +#include +#include + +class GURL; + +namespace content { +class WebContents; +} + +namespace extensions { + +// Observer base class for classes that need to be notified when content +// scripts and/or tabs.executeScript calls run on a page. +class ScriptExecutionObserver { + public: + // Map of extensions IDs to the executing script paths. + typedef std::map > ExecutingScriptsMap; + + // Called when script(s) have executed on a page. + // + // |executing_scripts_map| contains all extensions that are executing + // scripts, mapped to the paths for those scripts. This may be an empty set + // if the script has no path associated with it (e.g. in the case of + // tabs.executeScript). + virtual void OnScriptsExecuted( + const content::WebContents* web_contents, + const ExecutingScriptsMap& executing_scripts_map, + const GURL& on_url) = 0; + + protected: + virtual ~ScriptExecutionObserver(); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_SCRIPT_EXECUTION_OBSERVER_H_ diff --git a/chrome/browser/extensions/script_executor.cc b/extensions/browser/script_executor.cc similarity index 82% rename from chrome/browser/extensions/script_executor.cc rename to extensions/browser/script_executor.cc index 92029e5e9d3ba..a2ce27b121a15 100644 --- a/chrome/browser/extensions/script_executor.cc +++ b/extensions/browser/script_executor.cc @@ -1,18 +1,18 @@ -// Copyright (c) 2012 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. -#include "chrome/browser/extensions/script_executor.h" +#include "extensions/browser/script_executor.h" #include "base/bind.h" #include "base/callback.h" #include "base/logging.h" #include "base/pickle.h" -#include "chrome/browser/extensions/tab_helper.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/script_execution_observer.h" #include "extensions/common/extension_messages.h" #include "ipc/ipc_message.h" #include "ipc/ipc_message_macros.h" @@ -32,15 +32,15 @@ const char* kRendererDestroyed = "The tab was closed."; // corresponding response comes from the renderer, or the renderer is destroyed. class Handler : public content::WebContentsObserver { public: - Handler(ObserverList* script_observers, + Handler(ObserverList* script_observers, content::WebContents* web_contents, const ExtensionMsg_ExecuteCode_Params& params, const ScriptExecutor::ExecuteScriptCallback& callback) - : content::WebContentsObserver(web_contents), - script_observers_(AsWeakPtr(script_observers)), - extension_id_(params.extension_id), - request_id_(params.request_id), - callback_(callback) { + : content::WebContentsObserver(web_contents), + script_observers_(AsWeakPtr(script_observers)), + extension_id_(params.extension_id), + request_id_(params.request_id), + callback_(callback) { content::RenderViewHost* rvh = web_contents->GetRenderViewHost(); rvh->Send(new ExtensionMsg_ExecuteCode(rvh->GetRoutingID(), params)); } @@ -79,20 +79,18 @@ class Handler : public content::WebContentsObserver { const GURL& on_url, const base::ListValue& script_result) { if (script_observers_.get() && error.empty()) { - TabHelper::ScriptExecutionObserver::ExecutingScriptsMap id_map; + ScriptExecutionObserver::ExecutingScriptsMap id_map; id_map[extension_id_] = std::set(); - FOR_EACH_OBSERVER(TabHelper::ScriptExecutionObserver, *script_observers_, - OnScriptsExecuted(web_contents(), - id_map, - on_url)); + FOR_EACH_OBSERVER(ScriptExecutionObserver, + *script_observers_, + OnScriptsExecuted(web_contents(), id_map, on_url)); } callback_.Run(error, on_url, script_result); delete this; } - base::WeakPtr > - script_observers_; + base::WeakPtr > script_observers_; std::string extension_id_; int request_id_; ScriptExecutor::ExecuteScriptCallback callback_; @@ -100,16 +98,20 @@ class Handler : public content::WebContentsObserver { } // namespace +ScriptExecutionObserver::~ScriptExecutionObserver() { +} + ScriptExecutor::ScriptExecutor( content::WebContents* web_contents, - ObserverList* script_observers) + ObserverList* script_observers) : next_request_id_(0), web_contents_(web_contents), script_observers_(script_observers) { CHECK(web_contents_); } -ScriptExecutor::~ScriptExecutor() {} +ScriptExecutor::~ScriptExecutor() { +} void ScriptExecutor::ExecuteScript(const std::string& extension_id, ScriptExecutor::ScriptType script_type, diff --git a/chrome/browser/extensions/script_executor.h b/extensions/browser/script_executor.h similarity index 87% rename from chrome/browser/extensions/script_executor.h rename to extensions/browser/script_executor.h index 9804bf0e00351..6c8b4c77e5e72 100644 --- a/chrome/browser/extensions/script_executor.h +++ b/extensions/browser/script_executor.h @@ -1,15 +1,12 @@ -// Copyright (c) 2012 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. #ifndef CHROME_BROWSER_EXTENSIONS_SCRIPT_EXECUTOR_H_ #define CHROME_BROWSER_EXTENSIONS_SCRIPT_EXECUTOR_H_ -#include - #include "base/callback_forward.h" #include "base/observer_list.h" -#include "chrome/browser/extensions/tab_helper.h" #include "extensions/common/user_script.h" class GURL; @@ -24,6 +21,7 @@ class WebContents; } namespace extensions { +class ScriptExecutionObserver; // Interface for executing extension content scripts (e.g. executeScript) as // described by the ExtensionMsg_ExecuteCode_Params IPC, and notifying the @@ -34,7 +32,7 @@ class ScriptExecutor { content::WebContents* web_contents, // |script_observers| is assumed to be owned by |this|'s owner, and in // such a way that |this| is destroyed first. - ObserverList* script_observers); + ObserverList* script_observers); ~ScriptExecutor(); @@ -77,9 +75,8 @@ class ScriptExecutor { // Callback from ExecuteScript. The arguments are (error, on_url, result). // Success is implied by an empty error. - typedef base::Callback + typedef base::Callback< + void(const std::string&, const GURL&, const base::ListValue&)> ExecuteScriptCallback; // Executes a script. The arguments match ExtensionMsg_ExecuteCode_Params in @@ -108,7 +105,7 @@ class ScriptExecutor { content::WebContents* web_contents_; - ObserverList* script_observers_; + ObserverList* script_observers_; }; } // namespace extensions diff --git a/extensions/common/BUILD.gn b/extensions/common/BUILD.gn index be874e42f6c5b..204d9dbf67203 100644 --- a/extensions/common/BUILD.gn +++ b/extensions/common/BUILD.gn @@ -18,8 +18,6 @@ source_set("common") { "common_manifest_handlers.h", "constants.cc", "constants.h", - "crx_file.cc", - "crx_file.h", "csp_validator.cc", "csp_validator.h", "dom_action_types.h", diff --git a/extensions/common/DEPS b/extensions/common/DEPS index 1739f9de4d363..9cfa5dea4ada1 100644 --- a/extensions/common/DEPS +++ b/extensions/common/DEPS @@ -1,6 +1,8 @@ include_rules = [ "+device/usb", "+grit/extensions_strings.h", + "+libxml", "+net", + "+third_party/libxml", "+third_party/re2", ] diff --git a/extensions/common/api/BUILD.gn b/extensions/common/api/BUILD.gn index c2d02254f704f..1c924bdc7f34e 100644 --- a/extensions/common/api/BUILD.gn +++ b/extensions/common/api/BUILD.gn @@ -23,6 +23,7 @@ generated_extensions_api("api") { "storage.json", "test.json", "usb.idl", + "usb_private.idl", ] root_namespace = "extensions::core_api::%(namespace)s" impl_dir = "//extensions/browser/api" diff --git a/extensions/common/api/_api_features.json b/extensions/common/api/_api_features.json index 3a5b8a4e01238..8812cd4b5c889 100644 --- a/extensions/common/api/_api_features.json +++ b/extensions/common/api/_api_features.json @@ -155,5 +155,9 @@ "usb": { "dependencies": ["permission:usb"], "contexts": ["blessed_extension"] + }, + "usbPrivate": { + "channel": "dev", + "contexts": ["webui"] } } diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json index 483a2d80d16b1..512dd861f4a77 100644 --- a/extensions/common/api/_permission_features.json +++ b/extensions/common/api/_permission_features.json @@ -38,7 +38,7 @@ ], "hid": [ { - "channel": "dev", + "channel": "stable", "extension_types": ["platform_app"] }, { diff --git a/extensions/common/api/api.gyp b/extensions/common/api/api.gyp index 229a5bc482cc8..c95217cae0861 100644 --- a/extensions/common/api/api.gyp +++ b/extensions/common/api/api.gyp @@ -41,6 +41,7 @@ 'storage.json', 'test.json', 'usb.idl', + 'usb_private.idl', ], }, { # TODO: Eliminate these on Android. See crbug.com/305852. diff --git a/extensions/common/api/app_runtime.idl b/extensions/common/api/app_runtime.idl index 73200e3cc7186..41ebeac743a04 100644 --- a/extensions/common/api/app_runtime.idl +++ b/extensions/common/api/app_runtime.idl @@ -49,6 +49,10 @@ namespace app.runtime { dictionary EmbedRequest { DOMString embedderId; + // Optional developer specified data that the app to be embedded can use + // when making an embedding decision. + any? data; + // Allows embedderId to embed this app in an <appview> // element. The url specifies the content to embed. [nocompile] static void allow(DOMString url); diff --git a/extensions/common/api/cast_channel.idl b/extensions/common/api/cast_channel.idl index ebefa990cc7eb..3d2f7d827dda5 100644 --- a/extensions/common/api/cast_channel.idl +++ b/extensions/common/api/cast_channel.idl @@ -124,16 +124,48 @@ namespace cast.channel { any data; }; + // Describes a terminal error encountered by the channel with details of the + // error that caused the channel to be closed. One or more of the optional + // fields may be set with specific error codes from the underlying + // implementation. + dictionary ErrorInfo { + // The type of error encountered by the channel. + ChannelError errorState; + + // The event that was occurring when the error happened. Values are defined + // in the enum EventType in logging.proto. + long? eventType; + + // An error encountered when processing the authentication handshake. + // Values are defined in the enum ChallengeReplyErrorType in logging.proto. + long? challengeReplyErrorType; + + // A return value from the underlying net:: socket libraries. Values are + // defined in net/base/net_error_list.h. + long? netReturnValue; + + // An error code returned by NSS. Values are defined in secerr.h. + long? nssErrorCode; + }; + // Callback holding the result of a channel operation. callback ChannelInfoCallback = void (ChannelInfo result); + // Callback from getLogs method. + // |log|: compressed serialized raw bytes containing the logs. + // The log is formatted using protocol buffer. + // See extensions/browser/api/cast_channel/logging.proto for definition. + // Compression is in gzip format. + callback GetLogsCallback = void (ArrayBuffer log); + interface Functions { // Opens a new channel to the Cast receiver specified by connectInfo. Only // one channel may be connected to same receiver from the same extension at // a time. If the open request is successful, the callback will be invoked // with a ChannelInfo with readyState == 'connecting'. If unsuccessful, the // callback will be invoked with a ChannelInfo with channel.readyState == - // 'closed' and channel.errorState will be set to the error condition. + // 'closed', channel.errorState will be set to the error condition, and + // onError will be fired with error details. // // TODO(mfoltz): Convert 'any' to ConnectInfo once all clients are updated // to not send URLs. @@ -143,7 +175,8 @@ namespace cast.channel { // Sends a message on the channel and invokes callback with the resulting // channel status. The channel must be in readyState == 'open'. If // unsuccessful, channel.readyState will be set to 'closed', - // channel.errorState will be set to the error condition. + // channel.errorState will be set to the error condition, and onError will + // be fired with error details. static void send(ChannelInfo channel, MessageInfo message, ChannelInfoCallback callback); @@ -155,6 +188,12 @@ namespace cast.channel { // and channel.errorState will be set to the error condition. static void close(ChannelInfo channel, ChannelInfoCallback callback); + + // Get logs in compressed serialized format. See GetLogsCallback for + // details. + // |callback|: If successful, |callback| is invoked with data. Otherwise, + // an error will be raised. + static void getLogs(GetLogsCallback callback); }; // Events on the channel. @@ -163,8 +202,8 @@ namespace cast.channel { static void onMessage(ChannelInfo channel, MessageInfo message); - // Fired when an error occurs as a result of a channel method or a network - // event. - static void onError(ChannelInfo channel); + // Fired when an error occurs as a result of a channel operation or a + // network event. |error| contains details of the error. + static void onError(ChannelInfo channel, ErrorInfo error); }; }; diff --git a/extensions/common/api/usb_private.idl b/extensions/common/api/usb_private.idl new file mode 100644 index 0000000000000..457da232416c8 --- /dev/null +++ b/extensions/common/api/usb_private.idl @@ -0,0 +1,55 @@ +// 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. + +// Use the chrome.usbPrivate API to interact with connected USB +// devices. This API provides private extensions to the chrome.usb +// API which should only be available to trusted pages. +namespace usbPrivate { + + // Properties for matching devices. A device matches of any of its interfaces + // match the given properties. An empty dictionary matches any device. + dictionary DeviceFilter { + // Device-level matching criteria: + long? vendorId; + // Checked only if the vendorId matches. + long? productId; + + // Per-interface matching criteria: + long? interfaceClass; + // Checked only if the interfaceClass matches. + long? interfaceSubclass; + // Checked only if the interfaceSubclass matches. + long? interfaceProtocol; + }; + + dictionary DeviceInfo { + long vendorId; // idVendor from the device + long productId; // idProduct from the device + + // Vendor and product names from an internal database. + DOMString? vendorName; + DOMString? productName; + + // iManufacturer, iProduct and iSerial strings from the device. + DOMString? manufacturerString; + DOMString? productString; + DOMString? serialString; + }; + + callback GetDevicesCallback = void (long[] deviceIds); + callback GetDeviceInfoCallback = void (DeviceInfo deviceInfo); + + interface Functions { + // Lists USB devices matching any of the given filters. + // |filters|: The properties to search for on target devices. + // |callback|: Invoked with a list of device IDs on complete. + static void getDevices(DeviceFilter[] filters, + GetDevicesCallback callback); + + // Gets basic display information about a device. + // |deviceId|: The device ID (from |getDevices|). + // |callback|: Invoked with |DeviceInfo| from the device. + static void getDeviceInfo(long deviceId, GetDeviceInfoCallback callback); + }; +}; diff --git a/extensions/common/constants.h b/extensions/common/constants.h index 66fe2ba4d1e6b..3dde500ab3589 100644 --- a/extensions/common/constants.h +++ b/extensions/common/constants.h @@ -110,13 +110,6 @@ const int kUnknownWindowId = -1; // Matches chrome.windows.WINDOW_ID_CURRENT. const int kCurrentWindowId = -2; -// Note: this structure is an ASN.1 which encodes the algorithm used -// with its parameters. This is defined in PKCS #1 v2.1 (RFC 3447). -// It is encoding: { OID sha1WithRSAEncryption PARAMETERS NULL } -const uint8 kSignatureAlgorithm[15] = {0x30, 0x0d, 0x06, 0x09, 0x2a, - 0x86, 0x48, 0x86, 0xf7, 0x0d, - 0x01, 0x01, 0x05, 0x05, 0x00}; - // NOTE: If you change this list, you should also change kExtensionIconSizes // in cc file. enum ExtensionIcons { diff --git a/extensions/common/extension.h b/extensions/common/extension.h index a429d59286d67..47f85f33ff942 100644 --- a/extensions/common/extension.h +++ b/extensions/common/extension.h @@ -62,9 +62,9 @@ class Extension : public base::RefCountedThreadSafe { // An external extension that the user uninstalled. We should not reinstall // such extensions on startup. EXTERNAL_EXTENSION_UNINSTALLED, - // Special state for component extensions, since they are always loaded by - // the component loader, and should never be auto-installed on startup. - ENABLED_COMPONENT, + // DEPRECATED: Special state for component extensions. + // Maintained as a placeholder since states may be stored to disk. + ENABLED_COMPONENT_DEPRECATED, NUM_STATES }; diff --git a/extensions/common/manifest_constants.cc b/extensions/common/manifest_constants.cc index 95d6d8b43fcb0..fbf32291a92e6 100644 --- a/extensions/common/manifest_constants.cc +++ b/extensions/common/manifest_constants.cc @@ -68,6 +68,8 @@ const char kKioskMode[] = "kiosk_mode"; const char kLanguage[] = "language"; const char kLaunch[] = "app.launch"; const char kLaunchContainer[] = "app.launch.container"; +const char kLauncherPage[] = "launcher_page"; +const char kLauncherPagePage[] = "launcher_page.page"; const char kLaunchHeight[] = "app.launch.height"; const char kLaunchLocalPath[] = "app.launch.local_path"; const char kLaunchWebURL[] = "app.launch.web_url"; diff --git a/extensions/common/manifest_constants.h b/extensions/common/manifest_constants.h index 9852e88ce1b4b..f9246cbf5e5c1 100644 --- a/extensions/common/manifest_constants.h +++ b/extensions/common/manifest_constants.h @@ -77,6 +77,8 @@ extern const char kKioskMode[]; extern const char kLanguage[]; extern const char kLaunch[]; extern const char kLaunchContainer[]; +extern const char kLauncherPage[]; +extern const char kLauncherPagePage[]; extern const char kLaunchHeight[]; extern const char kLaunchLocalPath[]; extern const char kLaunchWebURL[]; diff --git a/extensions/common/permissions/api_permission.h b/extensions/common/permissions/api_permission.h index 4072c0338f2ff..70d805fa5bf63 100644 --- a/extensions/common/permissions/api_permission.h +++ b/extensions/common/permissions/api_permission.h @@ -41,6 +41,7 @@ class APIPermission { kActiveTab, kActivityLogPrivate, kAlarms, + kAlphaEnabled, kAlwaysOnTopWindows, kAppView, kAudio, @@ -63,6 +64,7 @@ class APIPermission { kContentSettings, kContextMenus, kCookie, + kCopresence, kCopresencePrivate, kDiagnostics, kDial, diff --git a/extensions/common/permissions/permission_message.h b/extensions/common/permissions/permission_message.h index 312697f9871b2..1e1212a1b6661 100644 --- a/extensions/common/permissions/permission_message.h +++ b/extensions/common/permissions/permission_message.h @@ -87,6 +87,7 @@ class PermissionMessage { kBluetoothPrivate, kIdentityEmail, kExperienceSamplingPrivate, + kCopresence, kEnumBoundary, }; COMPILE_ASSERT(PermissionMessage::kNone > PermissionMessage::kUnknown, diff --git a/extensions/common/permissions/permissions_data.cc b/extensions/common/permissions/permissions_data.cc index 6eb819993c474..958c5e95a410a 100644 --- a/extensions/common/permissions/permissions_data.cc +++ b/extensions/common/permissions/permissions_data.cc @@ -25,12 +25,6 @@ namespace { PermissionsData::PolicyDelegate* g_policy_delegate = NULL; -// Returns true if this extension id is from a trusted provider. -bool ShouldSkipPermissionWarnings(const std::string& extension_id) { - // See http://b/4946060 for more details. - return extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); -} - } // namespace PermissionsData::PermissionsData(const Extension* extension) @@ -72,6 +66,12 @@ bool PermissionsData::CanExecuteScriptEverywhere(const Extension* extension) { whitelist.end(); } +bool PermissionsData::ShouldSkipPermissionWarnings( + const std::string& extension_id) { + // See http://b/4946060 for more details. + return extension_id == std::string("nckgahadagoaajjgafhacjanaoiihapd"); +} + // static bool PermissionsData::IsRestrictedUrl(const GURL& document_url, const GURL& top_frame_url, diff --git a/extensions/common/permissions/permissions_data.h b/extensions/common/permissions/permissions_data.h index 40c6946c47ebb..dd74d28dfa74c 100644 --- a/extensions/common/permissions/permissions_data.h +++ b/extensions/common/permissions/permissions_data.h @@ -76,6 +76,10 @@ class PermissionsData { // whitelist of extensions that can script all pages. static bool CanExecuteScriptEverywhere(const Extension* extension); + // Returns true if we should skip the permisisons warning for the extension + // with the given |extension_id|. + static bool ShouldSkipPermissionWarnings(const std::string& extension_id); + // Returns true if the given |url| is restricted for the given |extension|, // as is commonly the case for chrome:// urls. // NOTE: You probably want to use CanAccessPage(). diff --git a/extensions/common/switches.cc b/extensions/common/switches.cc index 9b17ad2c4b282..7856a7b4cfb13 100644 --- a/extensions/common/switches.cc +++ b/extensions/common/switches.cc @@ -29,6 +29,11 @@ const char kEnableEmbeddedExtensionOptions[] = const char kEnableExperimentalExtensionApis[] = "enable-experimental-extension-apis"; +// Hack so that feature switch can work with about_flags. See +// kEnableScriptsRequireAction. +const char kEnableExtensionActionRedesign[] = + "enable-extension-action-redesign"; + // Enables extensions to hide bookmarks UI elements. const char kEnableOverrideBookmarksUI[] = "enable-override-bookmarks-ui"; diff --git a/extensions/common/switches.h b/extensions/common/switches.h index 475650f24274d..bb2b2cc8c96d7 100644 --- a/extensions/common/switches.h +++ b/extensions/common/switches.h @@ -16,6 +16,7 @@ extern const char kAllowLegacyExtensionManifests[]; extern const char kEmbeddedExtensionOptions[]; extern const char kEnableEmbeddedExtensionOptions[]; extern const char kEnableExperimentalExtensionApis[]; +extern const char kEnableExtensionActionRedesign[]; extern const char kEnableOverrideBookmarksUI[]; extern const char kErrorConsole[]; extern const char kEventPageIdleTime[]; diff --git a/extensions/common/test_util.cc b/extensions/common/test_util.cc index 05de2c7779a48..03b35b8480152 100644 --- a/extensions/common/test_util.cc +++ b/extensions/common/test_util.cc @@ -22,6 +22,15 @@ ExtensionBuilder& BuildExtension(ExtensionBuilder& builder) { .Set("manifest_version", 2)); } +scoped_refptr CreateEmptyExtension() { + scoped_refptr empty_extension( + ExtensionBuilder() + .SetManifest( + DictionaryBuilder().Set("name", "Test").Set("version", "1.0")) + .Build()); + return empty_extension; +} + scoped_refptr CreateExtensionWithID(const std::string& id) { return ExtensionBuilder() .SetManifest( diff --git a/extensions/common/test_util.h b/extensions/common/test_util.h index 54d985be472cb..46ac5d08c3f26 100644 --- a/extensions/common/test_util.h +++ b/extensions/common/test_util.h @@ -23,6 +23,10 @@ namespace test_util { // Adds an extension manifest to a builder. ExtensionBuilder& BuildExtension(ExtensionBuilder& builder); +// Creates an extension instance that can be attached to an ExtensionFunction +// before running it. +scoped_refptr CreateEmptyExtension(); + // Return a very simple extension with a given |id|. scoped_refptr CreateExtensionWithID(const std::string& id); diff --git a/chrome/common/extensions/update_manifest.cc b/extensions/common/update_manifest.cc similarity index 98% rename from chrome/common/extensions/update_manifest.cc rename to extensions/common/update_manifest.cc index 0ed653ff76a8e..26d794f11f4ec 100644 --- a/chrome/common/extensions/update_manifest.cc +++ b/extensions/common/update_manifest.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 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. -#include "chrome/common/extensions/update_manifest.h" +#include "extensions/common/update_manifest.h" #include diff --git a/chrome/common/extensions/update_manifest.h b/extensions/common/update_manifest.h similarity index 92% rename from chrome/common/extensions/update_manifest.h rename to extensions/common/update_manifest.h index 0076a734d0413..aa78085224990 100644 --- a/chrome/common/extensions/update_manifest.h +++ b/extensions/common/update_manifest.h @@ -1,9 +1,9 @@ -// Copyright (c) 2010 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. -#ifndef CHROME_COMMON_EXTENSIONS_UPDATE_MANIFEST_H_ -#define CHROME_COMMON_EXTENSIONS_UPDATE_MANIFEST_H_ +#ifndef EXTENSIONS_COMMON_UPDATE_MANIFEST_H_ +#define EXTENSIONS_COMMON_UPDATE_MANIFEST_H_ #include #include @@ -92,4 +92,4 @@ class UpdateManifest { DISALLOW_COPY_AND_ASSIGN(UpdateManifest); }; -#endif // CHROME_COMMON_EXTENSIONS_UPDATE_MANIFEST_H_ +#endif // EXTENSIONS_COMMON_UPDATE_MANIFEST_H_ diff --git a/extensions/common/url_pattern.cc b/extensions/common/url_pattern.cc index 735e71e9fa029..71b522e1cd0d6 100644 --- a/extensions/common/url_pattern.cc +++ b/extensions/common/url_pattern.cc @@ -466,6 +466,12 @@ bool URLPattern::ImpliesAllHosts() const { return registry_length > 0; } +bool URLPattern::MatchesSingleOrigin() const { + // Strictly speaking, the port is part of the origin, but in URLPattern it + // defaults to *. It's not very interesting anyway, so leave it out. + return !ImpliesAllHosts() && scheme_ != "*" && !match_subdomains_; +} + bool URLPattern::MatchesPath(const std::string& test) const { // Make the behaviour of OverlapsWith consistent with MatchesURL, which is // need to match hosted apps on e.g. 'google.com' also run on 'google.com/'. diff --git a/extensions/common/url_pattern.h b/extensions/common/url_pattern.h index 5a0e8c6ffd326..c58c3ef00a8df 100644 --- a/extensions/common/url_pattern.h +++ b/extensions/common/url_pattern.h @@ -162,6 +162,10 @@ class URLPattern { // cached. bool ImpliesAllHosts() const; + // Returns true if the pattern only matches a single origin. The pattern may + // include a path. + bool MatchesSingleOrigin() const; + // Sets the port. Returns false if the port is invalid. bool SetPort(const std::string& port); const std::string& port() const { return port_; } diff --git a/extensions/common/url_pattern_set.cc b/extensions/common/url_pattern_set.cc index ee5ea933830f3..bc1d0d57b34a9 100644 --- a/extensions/common/url_pattern_set.cc +++ b/extensions/common/url_pattern_set.cc @@ -142,6 +142,18 @@ void URLPatternSet::ClearPatterns() { patterns_.clear(); } +bool URLPatternSet::AddOrigin(int valid_schemes, const GURL& origin) { + DCHECK_EQ(origin.GetOrigin(), origin); + URLPattern origin_pattern(valid_schemes); + // Origin adding could fail if |origin| does not match |valid_schemes|. + if (origin_pattern.Parse(origin.GetOrigin().spec()) != + URLPattern::PARSE_SUCCESS) { + return false; + } + origin_pattern.SetPath("/*"); + return AddPattern(origin_pattern); +} + bool URLPatternSet::Contains(const URLPatternSet& other) const { for (URLPatternSet::const_iterator it = other.begin(); it != other.end(); ++it) { diff --git a/extensions/common/url_pattern_set.h b/extensions/common/url_pattern_set.h index c17f613442dd0..30db55827b926 100644 --- a/extensions/common/url_pattern_set.h +++ b/extensions/common/url_pattern_set.h @@ -69,6 +69,9 @@ class URLPatternSet { void ClearPatterns(); + // Adds a pattern based on |origin| to the set. + bool AddOrigin(int valid_schemes, const GURL& origin); + // Returns true if every URL that matches |set| is matched by this. In other // words, if every pattern in |set| is encompassed by a pattern in this. bool Contains(const URLPatternSet& set) const; diff --git a/extensions/common/url_pattern_set_unittest.cc b/extensions/common/url_pattern_set_unittest.cc index 1ad223ec53ac8..ce2c94e5fe607 100644 --- a/extensions/common/url_pattern_set_unittest.cc +++ b/extensions/common/url_pattern_set_unittest.cc @@ -421,4 +421,22 @@ TEST(URLPatternSetTest, NwayUnion) { } } +TEST(URLPatternSetTest, AddOrigin) { + URLPatternSet set; + EXPECT_TRUE(set.AddOrigin( + URLPattern::SCHEME_ALL, GURL("https://www.google.com/"))); + EXPECT_TRUE(set.MatchesURL(GURL("https://www.google.com/foo/bar"))); + EXPECT_FALSE(set.MatchesURL(GURL("http://www.google.com/foo/bar"))); + EXPECT_FALSE(set.MatchesURL(GURL("https://en.google.com/foo/bar"))); + set.ClearPatterns(); + + EXPECT_TRUE(set.AddOrigin( + URLPattern::SCHEME_ALL, GURL("https://google.com/"))); + EXPECT_FALSE(set.MatchesURL(GURL("https://www.google.com/foo/bar"))); + EXPECT_TRUE(set.MatchesURL(GURL("https://google.com/foo/bar"))); + + EXPECT_FALSE(set.AddOrigin( + URLPattern::SCHEME_HTTP, GURL("https://google.com/"))); +} + } // namespace extensions diff --git a/extensions/common/url_pattern_unittest.cc b/extensions/common/url_pattern_unittest.cc index 9618aeb16a175..d4398a7509296 100644 --- a/extensions/common/url_pattern_unittest.cc +++ b/extensions/common/url_pattern_unittest.cc @@ -810,4 +810,35 @@ TEST(ExtensionURLPatternTest, Subset) { EXPECT_TRUE(StrictlyContains(pattern12, pattern13)); } +TEST(ExtensionURLPatternTest, MatchesSingleOrigin) { + EXPECT_FALSE( + URLPattern(URLPattern::SCHEME_ALL, "http://*/").MatchesSingleOrigin()); + EXPECT_FALSE(URLPattern(URLPattern::SCHEME_ALL, "https://*.google.com/*") + .MatchesSingleOrigin()); + EXPECT_TRUE(URLPattern(URLPattern::SCHEME_ALL, "http://google.com/") + .MatchesSingleOrigin()); + EXPECT_TRUE(URLPattern(URLPattern::SCHEME_ALL, "http://google.com/*") + .MatchesSingleOrigin()); + EXPECT_TRUE(URLPattern(URLPattern::SCHEME_ALL, "http://www.google.com/") + .MatchesSingleOrigin()); + EXPECT_FALSE(URLPattern(URLPattern::SCHEME_ALL, "*://www.google.com/") + .MatchesSingleOrigin()); + EXPECT_FALSE(URLPattern(URLPattern::SCHEME_ALL, "http://*.com/") + .MatchesSingleOrigin()); + EXPECT_FALSE(URLPattern(URLPattern::SCHEME_ALL, "http://*.google.com/foo/bar") + .MatchesSingleOrigin()); + EXPECT_TRUE( + URLPattern(URLPattern::SCHEME_ALL, "http://www.google.com/foo/bar") + .MatchesSingleOrigin()); + EXPECT_FALSE(URLPattern(URLPattern::SCHEME_HTTPS, "*://*.google.com/foo/bar") + .MatchesSingleOrigin()); + EXPECT_TRUE(URLPattern(URLPattern::SCHEME_HTTPS, "https://www.google.com/") + .MatchesSingleOrigin()); + EXPECT_FALSE(URLPattern(URLPattern::SCHEME_HTTP, + "http://*.google.com/foo/bar").MatchesSingleOrigin()); + EXPECT_TRUE( + URLPattern(URLPattern::SCHEME_HTTP, "http://www.google.com/foo/bar") + .MatchesSingleOrigin()); +} + } // namespace diff --git a/extensions/common/user_script.cc b/extensions/common/user_script.cc index 128ac55b5a52c..2e705ce86fc6e 100644 --- a/extensions/common/user_script.cc +++ b/extensions/common/user_script.cc @@ -240,4 +240,11 @@ void UserScript::UnpickleScripts(const ::Pickle& pickle, PickleIterator* iter, } } +bool operator<(const UserScript& script1, const UserScript& script2) { + // The only kind of script that should be compared is the kind that has its + // IDs initialized to a meaningful value. + DCHECK(script1.id() != -1 && script2.id() != -1); + return script1.id() < script2.id(); +} + } // namespace extensions diff --git a/extensions/common/user_script.h b/extensions/common/user_script.h index b95607857489a..bfa23dd50096f 100644 --- a/extensions/common/user_script.h +++ b/extensions/common/user_script.h @@ -287,6 +287,9 @@ class UserScript { bool incognito_enabled_; }; +// For storing UserScripts with unique IDs in sets. +bool operator<(const UserScript& script1, const UserScript& script2); + typedef std::vector UserScriptList; } // namespace extensions diff --git a/extensions/common/view_type.cc b/extensions/common/view_type.cc index 6fbfa5f3b0141..084e49bbd4176 100644 --- a/extensions/common/view_type.cc +++ b/extensions/common/view_type.cc @@ -13,6 +13,7 @@ const char kViewTypePanel[] = "PANEL"; const char kViewTypeInfobar[] = "INFOBAR"; const char kViewTypeExtensionDialog[] = "EXTENSION_DIALOG"; const char kViewTypeAppWindow[] = "APP_WINDOW"; +const char kViewTypeLauncherPage[] = "LAUNCHER_PAGE"; const char kViewTypeAll[] = "ALL"; } // namespace extensions diff --git a/extensions/common/view_type.h b/extensions/common/view_type.h index f6d2eeb8fbce3..8fd5e6fa41dcf 100644 --- a/extensions/common/view_type.h +++ b/extensions/common/view_type.h @@ -20,6 +20,7 @@ enum ViewType { VIEW_TYPE_EXTENSION_DIALOG, VIEW_TYPE_EXTENSION_INFOBAR, VIEW_TYPE_EXTENSION_POPUP, + VIEW_TYPE_LAUNCHER_PAGE, VIEW_TYPE_PANEL, VIEW_TYPE_TAB_CONTENTS, VIEW_TYPE_VIRTUAL_KEYBOARD, @@ -33,6 +34,7 @@ extern const char kViewTypeAppWindow[]; extern const char kViewTypeBackgroundPage[]; extern const char kViewTypeExtensionDialog[]; extern const char kViewTypeInfobar[]; +extern const char kViewTypeLauncherPage[]; extern const char kViewTypePanel[]; extern const char kViewTypePopup[]; extern const char kViewTypeTabContents[]; diff --git a/extensions/extensions.gyp b/extensions/extensions.gyp index 4365ad1268e7b..e86b5b5d4dc8e 100644 --- a/extensions/extensions.gyp +++ b/extensions/extensions.gyp @@ -16,6 +16,7 @@ # api resources compiled into the chrome resource bundle. # http://crbug.com/162530 '../chrome/chrome_resources.gyp:chrome_resources', + '../components/components.gyp:crx_file', '../components/components.gyp:url_matcher', '../content/content.gyp:content_common', '../crypto/crypto.gyp:crypto', @@ -28,6 +29,7 @@ '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gfx/ipc/gfx_ipc.gyp:gfx_ipc', '../url/url.gyp:url_lib', + '../third_party/libxml/libxml.gyp:libxml', 'common/api/api.gyp:extensions_api', 'extensions_strings.gyp:extensions_strings', ], @@ -47,8 +49,6 @@ 'common/common_manifest_handlers.h', 'common/constants.cc', 'common/constants.h', - 'common/crx_file.cc', - 'common/crx_file.h', 'common/csp_validator.cc', 'common/csp_validator.h', 'common/dom_action_types.h', @@ -202,6 +202,8 @@ 'common/url_pattern.h', 'common/url_pattern_set.cc', 'common/url_pattern_set.h', + 'common/update_manifest.cc', + 'common/update_manifest.h', 'common/user_script.cc', 'common/user_script.h', 'common/value_counter.cc', @@ -285,6 +287,7 @@ 'browser/api/app_view/app_view_internal_api.h', 'browser/api/async_api_function.cc', 'browser/api/async_api_function.h', + 'browser/api/cast_channel/cast_auth_util.cc', 'browser/api/cast_channel/cast_auth_util.h', 'browser/api/cast_channel/cast_channel_api.cc', 'browser/api/cast_channel/cast_channel_api.h', @@ -292,6 +295,10 @@ 'browser/api/cast_channel/cast_message_util.h', 'browser/api/cast_channel/cast_socket.cc', 'browser/api/cast_channel/cast_socket.h', + 'browser/api/cast_channel/logger.cc', + 'browser/api/cast_channel/logger.h', + 'browser/api/cast_channel/logger_util.cc', + 'browser/api/cast_channel/logger_util.h', 'browser/api/dns/dns_api.cc', 'browser/api/dns/dns_api.h', 'browser/api/dns/host_resolver_wrapper.cc', @@ -364,6 +371,8 @@ 'browser/api/usb/usb_api.h', 'browser/api/usb/usb_device_resource.cc', 'browser/api/usb/usb_device_resource.h', + 'browser/api/usb_private/usb_private_api.cc', + 'browser/api/usb_private/usb_private_api.h', 'browser/api_activity_monitor.h', 'browser/app_sorting.h', 'browser/blacklist_state.h', @@ -444,6 +453,16 @@ 'browser/external_provider_interface.h', 'browser/granted_file_entry.cc', 'browser/granted_file_entry.h', + 'browser/guest_view/guest_view_base.cc', + 'browser/guest_view/guest_view_base.h', + 'browser/guest_view/guest_view_constants.cc', + 'browser/guest_view/guest_view_constants.h', + 'browser/guest_view/guest_view_manager_factory.h', + 'browser/guest_view/guest_view_manager.cc', + 'browser/guest_view/guest_view_manager.h', + 'browser/guest_view/guest_view.h', + 'browser/guest_view/web_view/web_view_permission_helper_delegate.cc', + 'browser/guest_view/web_view/web_view_permission_helper_delegate.h', 'browser/image_loader.cc', 'browser/image_loader.h', 'browser/image_loader_factory.cc', @@ -478,6 +497,9 @@ 'browser/renderer_startup_helper.h', 'browser/runtime_data.cc', 'browser/runtime_data.h', + 'browser/script_execution_observer.h', + 'browser/script_executor.cc', + 'browser/script_executor.h', 'browser/state_store.cc', 'browser/state_store.h', 'browser/uninstall_reason.h', @@ -725,6 +747,8 @@ 'browser/api_test_utils.h', 'browser/extensions_test.cc', 'browser/extensions_test.h', + 'browser/mock_extension_system.cc', + 'browser/mock_extension_system.h', 'browser/test_extensions_browser_client.cc', 'browser/test_extensions_browser_client.h', 'browser/test_management_policy.cc', @@ -807,6 +831,7 @@ '../components/components.gyp:keyed_service_content', '../content/content_shell_and_tests.gyp:test_support_content', '../device/serial/serial.gyp:device_serial', + '../device/serial/serial.gyp:device_serial_test_util', '../mojo/mojo_base.gyp:mojo_environment_chromium', '../mojo/mojo_base.gyp:mojo_cpp_bindings', '../mojo/mojo_base.gyp:mojo_js_bindings_lib', @@ -836,6 +861,7 @@ 'browser/extension_registry_unittest.cc', 'browser/file_highlighter_unittest.cc', 'browser/file_reader_unittest.cc', + 'browser/guest_view/guest_view_manager_unittest.cc', 'browser/image_loader_unittest.cc', 'browser/image_util_unittest.cc', 'browser/lazy_background_task_queue_unittest.cc', @@ -893,7 +919,10 @@ # GN version: //extensions/browser/api/cast_channel:cast_channel_proto 'target_name': 'cast_channel_proto', 'type': 'static_library', - 'sources': [ 'browser/api/cast_channel/cast_channel.proto' ], + 'sources': [ + 'browser/api/cast_channel/cast_channel.proto', + 'browser/api/cast_channel/logging.proto' + ], 'variables': { 'proto_in_dir': 'browser/api/cast_channel', 'proto_out_dir': 'extensions/browser/api/cast_channel', diff --git a/extensions/renderer/api/serial/serial_api_unittest.cc b/extensions/renderer/api/serial/serial_api_unittest.cc index 3f551ca4b84d1..530ce2e93b0ed 100644 --- a/extensions/renderer/api/serial/serial_api_unittest.cc +++ b/extensions/renderer/api/serial/serial_api_unittest.cc @@ -4,6 +4,7 @@ #include "device/serial/serial_device_enumerator.h" #include "device/serial/serial_service_impl.h" +#include "device/serial/test_serial_io_handler.h" #include "extensions/renderer/api_test_base.h" #include "grit/extensions_renderer_resources.h" @@ -32,34 +33,415 @@ class FakeSerialDeviceEnumerator : public device::SerialDeviceEnumerator { } }; -} // namespace +enum OptionalValue { + OPTIONAL_VALUE_UNSET, + OPTIONAL_VALUE_FALSE, + OPTIONAL_VALUE_TRUE, +}; -void CreateSerialService( - mojo::InterfaceRequest request) { - mojo::BindToRequest( - new device::SerialServiceImpl( - new device::SerialConnectionFactory( - device::SerialConnectionFactory::IoHandlerFactory(), - base::MessageLoopProxy::current()), - scoped_ptr( - new FakeSerialDeviceEnumerator)), - &request); +device::serial::HostControlSignals GenerateControlSignals(OptionalValue dtr, + OptionalValue rts) { + device::serial::HostControlSignals result; + switch (dtr) { + case OPTIONAL_VALUE_UNSET: + break; + case OPTIONAL_VALUE_FALSE: + result.dtr = false; + result.has_dtr = true; + break; + case OPTIONAL_VALUE_TRUE: + result.dtr = true; + result.has_dtr = true; + break; + } + switch (rts) { + case OPTIONAL_VALUE_UNSET: + break; + case OPTIONAL_VALUE_FALSE: + result.rts = false; + result.has_rts = true; + break; + case OPTIONAL_VALUE_TRUE: + result.rts = true; + result.has_rts = true; + break; + } + return result; +} + +device::serial::ConnectionOptions GenerateConnectionOptions( + int bitrate, + device::serial::DataBits data_bits, + device::serial::ParityBit parity_bit, + device::serial::StopBits stop_bits, + OptionalValue cts_flow_control) { + device::serial::ConnectionOptions result; + result.bitrate = bitrate; + result.data_bits = data_bits; + result.parity_bit = parity_bit; + result.stop_bits = stop_bits; + switch (cts_flow_control) { + case OPTIONAL_VALUE_UNSET: + break; + case OPTIONAL_VALUE_FALSE: + result.cts_flow_control = false; + result.has_cts_flow_control = true; + break; + case OPTIONAL_VALUE_TRUE: + result.cts_flow_control = true; + result.has_cts_flow_control = true; + break; + } + return result; } +class TestIoHandlerBase : public device::TestSerialIoHandler { + public: + TestIoHandlerBase() : calls_(0) {} + + size_t num_calls() const { return calls_; } + + protected: + virtual ~TestIoHandlerBase() {} + void record_call() const { calls_++; } + + private: + mutable size_t calls_; + + DISALLOW_COPY_AND_ASSIGN(TestIoHandlerBase); +}; + +class SetControlSignalsTestIoHandler : public TestIoHandlerBase { + public: + SetControlSignalsTestIoHandler() {} + + virtual bool SetControlSignals( + const device::serial::HostControlSignals& signals) OVERRIDE { + static const device::serial::HostControlSignals expected_signals[] = { + GenerateControlSignals(OPTIONAL_VALUE_UNSET, OPTIONAL_VALUE_UNSET), + GenerateControlSignals(OPTIONAL_VALUE_FALSE, OPTIONAL_VALUE_UNSET), + GenerateControlSignals(OPTIONAL_VALUE_TRUE, OPTIONAL_VALUE_UNSET), + GenerateControlSignals(OPTIONAL_VALUE_UNSET, OPTIONAL_VALUE_FALSE), + GenerateControlSignals(OPTIONAL_VALUE_FALSE, OPTIONAL_VALUE_FALSE), + GenerateControlSignals(OPTIONAL_VALUE_TRUE, OPTIONAL_VALUE_FALSE), + GenerateControlSignals(OPTIONAL_VALUE_UNSET, OPTIONAL_VALUE_TRUE), + GenerateControlSignals(OPTIONAL_VALUE_FALSE, OPTIONAL_VALUE_TRUE), + GenerateControlSignals(OPTIONAL_VALUE_TRUE, OPTIONAL_VALUE_TRUE), + }; + if (num_calls() >= arraysize(expected_signals)) + return false; + + EXPECT_EQ(expected_signals[num_calls()].has_dtr, signals.has_dtr); + EXPECT_EQ(expected_signals[num_calls()].dtr, signals.dtr); + EXPECT_EQ(expected_signals[num_calls()].has_rts, signals.has_rts); + EXPECT_EQ(expected_signals[num_calls()].rts, signals.rts); + record_call(); + return true; + } + + private: + virtual ~SetControlSignalsTestIoHandler() {} + + DISALLOW_COPY_AND_ASSIGN(SetControlSignalsTestIoHandler); +}; + +class GetControlSignalsTestIoHandler : public TestIoHandlerBase { + public: + GetControlSignalsTestIoHandler() {} + + virtual device::serial::DeviceControlSignalsPtr GetControlSignals() + const OVERRIDE { + device::serial::DeviceControlSignalsPtr signals( + device::serial::DeviceControlSignals::New()); + signals->dcd = num_calls() & 1; + signals->cts = num_calls() & 2; + signals->ri = num_calls() & 4; + signals->dsr = num_calls() & 8; + record_call(); + return signals.Pass(); + } + + private: + virtual ~GetControlSignalsTestIoHandler() {} + + DISALLOW_COPY_AND_ASSIGN(GetControlSignalsTestIoHandler); +}; + +class ConfigurePortTestIoHandler : public TestIoHandlerBase { + public: + ConfigurePortTestIoHandler() {} + virtual bool ConfigurePort( + const device::serial::ConnectionOptions& options) OVERRIDE { + static const device::serial::ConnectionOptions expected_options[] = { + GenerateConnectionOptions(9600, + device::serial::DATA_BITS_EIGHT, + device::serial::PARITY_BIT_NO, + device::serial::STOP_BITS_ONE, + OPTIONAL_VALUE_FALSE), + GenerateConnectionOptions(57600, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_SEVEN, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_EIGHT, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_NO, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_ODD, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_EVEN, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_ONE, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_TWO, + OPTIONAL_VALUE_UNSET), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_FALSE), + GenerateConnectionOptions(0, + device::serial::DATA_BITS_NONE, + device::serial::PARITY_BIT_NONE, + device::serial::STOP_BITS_NONE, + OPTIONAL_VALUE_TRUE), + }; + if (num_calls() >= arraysize(expected_options)) + return false; + + EXPECT_EQ(expected_options[num_calls()].bitrate, options.bitrate); + EXPECT_EQ(expected_options[num_calls()].data_bits, options.data_bits); + EXPECT_EQ(expected_options[num_calls()].parity_bit, options.parity_bit); + EXPECT_EQ(expected_options[num_calls()].stop_bits, options.stop_bits); + EXPECT_EQ(expected_options[num_calls()].has_cts_flow_control, + options.has_cts_flow_control); + EXPECT_EQ(expected_options[num_calls()].cts_flow_control, + options.cts_flow_control); + record_call(); + return TestSerialIoHandler::ConfigurePort(options); + } + + private: + virtual ~ConfigurePortTestIoHandler() {} + + DISALLOW_COPY_AND_ASSIGN(ConfigurePortTestIoHandler); +}; + +class FlushTestIoHandler : public TestIoHandlerBase { + public: + FlushTestIoHandler() {} + + virtual bool Flush() const OVERRIDE { + record_call(); + return true; + } + + private: + virtual ~FlushTestIoHandler() {} + + DISALLOW_COPY_AND_ASSIGN(FlushTestIoHandler); +}; + +class FailToConnectTestIoHandler : public TestIoHandlerBase { + public: + FailToConnectTestIoHandler() {} + virtual void Open(const std::string& port, + const OpenCompleteCallback& callback) OVERRIDE { + callback.Run(false); + return; + } + + private: + virtual ~FailToConnectTestIoHandler() {} + + DISALLOW_COPY_AND_ASSIGN(FailToConnectTestIoHandler); +}; + +class FailToGetInfoTestIoHandler : public TestIoHandlerBase { + public: + explicit FailToGetInfoTestIoHandler(int times_to_succeed) + : times_to_succeed_(times_to_succeed) {} + virtual device::serial::ConnectionInfoPtr GetPortInfo() const OVERRIDE { + if (times_to_succeed_-- > 0) + return device::TestSerialIoHandler::GetPortInfo(); + return device::serial::ConnectionInfoPtr(); + } + + private: + virtual ~FailToGetInfoTestIoHandler() {} + + mutable int times_to_succeed_; + + DISALLOW_COPY_AND_ASSIGN(FailToGetInfoTestIoHandler); +}; + +} // namespace + class SerialApiTest : public ApiTestBase { public: + SerialApiTest() {} + virtual void SetUp() OVERRIDE { ApiTestBase::SetUp(); env()->RegisterModule("serial", IDR_SERIAL_CUSTOM_BINDINGS_JS); env()->RegisterModule("serial_service", IDR_SERIAL_SERVICE_JS); env()->RegisterModule("device/serial/serial.mojom", IDR_SERIAL_MOJOM_JS); - service_provider()->AddService( - base::Bind(CreateSerialService)); + service_provider()->AddService(base::Bind( + &SerialApiTest::CreateSerialService, base::Unretained(this))); + } + + virtual void TearDown() OVERRIDE { + if (io_handler_) + EXPECT_TRUE(io_handler_->HasOneRef()); + ApiTestBase::TearDown(); + } + + scoped_refptr io_handler_; + + private: + scoped_refptr GetIoHandler() { + if (!io_handler_) + io_handler_ = new TestIoHandlerBase; + return io_handler_; + } + + void CreateSerialService( + mojo::InterfaceRequest request) { + mojo::BindToRequest(new device::SerialServiceImpl( + new device::SerialConnectionFactory( + base::Bind(&SerialApiTest::GetIoHandler, + base::Unretained(this)), + base::MessageLoopProxy::current()), + scoped_ptr( + new FakeSerialDeviceEnumerator)), + &request); } + + DISALLOW_COPY_AND_ASSIGN(SerialApiTest); }; TEST_F(SerialApiTest, GetDevices) { RunTest("serial_unittest.js", "testGetDevices"); } +TEST_F(SerialApiTest, ConnectFail) { + io_handler_ = new FailToConnectTestIoHandler; + RunTest("serial_unittest.js", "testConnectFail"); +} + +TEST_F(SerialApiTest, GetInfoFailOnConnect) { + io_handler_ = new FailToGetInfoTestIoHandler(0); + RunTest("serial_unittest.js", "testGetInfoFailOnConnect"); +} + +TEST_F(SerialApiTest, Connect) { + RunTest("serial_unittest.js", "testConnect"); +} + +TEST_F(SerialApiTest, ConnectDefaultOptions) { + RunTest("serial_unittest.js", "testConnectDefaultOptions"); +} + +TEST_F(SerialApiTest, ConnectInvalidBitrate) { + RunTest("serial_unittest.js", "testConnectInvalidBitrate"); +} + +TEST_F(SerialApiTest, GetInfo) { + RunTest("serial_unittest.js", "testGetInfo"); +} + +TEST_F(SerialApiTest, GetInfoFailToGetPortInfo) { + io_handler_ = new FailToGetInfoTestIoHandler(1); + RunTest("serial_unittest.js", "testGetInfoFailToGetPortInfo"); +} + +TEST_F(SerialApiTest, GetConnections) { + RunTest("serial_unittest.js", "testGetConnections"); +} + +TEST_F(SerialApiTest, GetControlSignals) { + io_handler_ = new GetControlSignalsTestIoHandler; + RunTest("serial_unittest.js", "testGetControlSignals"); + EXPECT_EQ(16u, io_handler_->num_calls()); +} + +TEST_F(SerialApiTest, SetControlSignals) { + io_handler_ = new SetControlSignalsTestIoHandler; + RunTest("serial_unittest.js", "testSetControlSignals"); + EXPECT_EQ(9u, io_handler_->num_calls()); +} + +TEST_F(SerialApiTest, Update) { + io_handler_ = new ConfigurePortTestIoHandler; + RunTest("serial_unittest.js", "testUpdate"); + EXPECT_EQ(11u, io_handler_->num_calls()); +} + +TEST_F(SerialApiTest, UpdateInvalidBitrate) { + io_handler_ = new ConfigurePortTestIoHandler; + RunTest("serial_unittest.js", "testUpdateInvalidBitrate"); + EXPECT_EQ(1u, io_handler_->num_calls()); +} + +TEST_F(SerialApiTest, Flush) { + io_handler_ = new FlushTestIoHandler; + RunTest("serial_unittest.js", "testFlush"); + EXPECT_EQ(1u, io_handler_->num_calls()); +} + +TEST_F(SerialApiTest, SetPaused) { + RunTest("serial_unittest.js", "testSetPaused"); +} + +TEST_F(SerialApiTest, DisconnectUnknownConnectionId) { + RunTest("serial_unittest.js", "testDisconnectUnknownConnectionId"); +} + +TEST_F(SerialApiTest, GetInfoUnknownConnectionId) { + RunTest("serial_unittest.js", "testGetInfoUnknownConnectionId"); +} + +TEST_F(SerialApiTest, UpdateUnknownConnectionId) { + RunTest("serial_unittest.js", "testUpdateUnknownConnectionId"); +} + +TEST_F(SerialApiTest, SetControlSignalsUnknownConnectionId) { + RunTest("serial_unittest.js", "testSetControlSignalsUnknownConnectionId"); +} + +TEST_F(SerialApiTest, GetControlSignalsUnknownConnectionId) { + RunTest("serial_unittest.js", "testGetControlSignalsUnknownConnectionId"); +} + +TEST_F(SerialApiTest, FlushUnknownConnectionId) { + RunTest("serial_unittest.js", "testFlushUnknownConnectionId"); +} + +TEST_F(SerialApiTest, SetPausedUnknownConnectionId) { + RunTest("serial_unittest.js", "testSetPausedUnknownConnectionId"); +} + } // namespace extensions diff --git a/extensions/renderer/dispatcher.cc b/extensions/renderer/dispatcher.cc index 130f585a18cf8..f09d5fdcdb705 100644 --- a/extensions/renderer/dispatcher.cc +++ b/extensions/renderer/dispatcher.cc @@ -1174,7 +1174,7 @@ Feature::Context Dispatcher::ClassifyJavaScriptContext( int extension_group, const GURL& url, const blink::WebSecurityOrigin& origin) { - // WARNING: This logic must match ProcessMap::GuessContextType, as much as + // WARNING: This logic must match ProcessMap::GetContextType, as much as // possible. DCHECK_GE(extension_group, 0); diff --git a/extensions/renderer/event_bindings.cc b/extensions/renderer/event_bindings.cc index 948d3dedd98b3..bf89669b45280 100644 --- a/extensions/renderer/event_bindings.cc +++ b/extensions/renderer/event_bindings.cc @@ -210,10 +210,6 @@ void EventBindings::AttachFilteredEvent( return; std::string extension_id = context()->GetExtensionID(); - if (extension_id.empty()) { - args.GetReturnValue().Set(static_cast(-1)); - return; - } scoped_ptr filter; scoped_ptr converter( @@ -256,8 +252,6 @@ void EventBindings::DetachFilteredEvent( bool is_manual = args[1]->BooleanValue(); std::string extension_id = context()->GetExtensionID(); - if (extension_id.empty()) - return; int matcher_id = args[0]->Int32Value(); EventFilter& event_filter = g_event_filter.Get(); diff --git a/extensions/renderer/resources/serial_custom_bindings.js b/extensions/renderer/resources/serial_custom_bindings.js index 3d15acff1c7fc..31f6daec467a7 100644 --- a/extensions/renderer/resources/serial_custom_bindings.js +++ b/extensions/renderer/resources/serial_custom_bindings.js @@ -2,6 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * Custom bindings for the Serial API. + * + * The bindings are implemented by asynchronously delegating to the + * serial_service module. The functions that apply to a particular connection + * are delegated to the appropriate method on the Connection object specified by + * the ID parameter. + */ + var binding = require('binding').Binding.create('serial'); function createAsyncProxy(targetPromise, functionNames) { @@ -12,19 +21,64 @@ function createAsyncProxy(targetPromise, functionNames) { return targetPromise.then(function(target) { return $Function.apply(target[name], target, args); }); - } + }; }); return functionProxies; } var serialService = createAsyncProxy(requireAsync('serial_service'), [ 'getDevices', + 'createConnection', + 'getConnection', + 'getConnections', ]); +function forwardToConnection(methodName) { + return function(connectionId) { + var args = $Array.slice(arguments, 1); + return serialService.getConnection(connectionId).then(function(connection) { + return $Function.apply(connection[methodName], connection, args); + }); + }; +} + binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequestWithPromise('getDevices', serialService.getDevices); + + apiFunctions.setHandleRequestWithPromise('connect', function(path, options) { + return serialService.createConnection(path, options).then(function(result) { + return result.info; + }).catch (function(e) { + throw new Error('Failed to connect to the port.'); + }); + }); + + apiFunctions.setHandleRequestWithPromise( + 'disconnect', forwardToConnection('close')); + apiFunctions.setHandleRequestWithPromise( + 'getInfo', forwardToConnection('getInfo')); + apiFunctions.setHandleRequestWithPromise( + 'update', forwardToConnection('setOptions')); + apiFunctions.setHandleRequestWithPromise( + 'getControlSignals', forwardToConnection('getControlSignals')); + apiFunctions.setHandleRequestWithPromise( + 'setControlSignals', forwardToConnection('setControlSignals')); + apiFunctions.setHandleRequestWithPromise( + 'flush', forwardToConnection('flush')); + apiFunctions.setHandleRequestWithPromise( + 'setPaused', forwardToConnection('setPaused')); + + apiFunctions.setHandleRequestWithPromise('getConnections', function() { + return serialService.getConnections().then(function(connections) { + var promises = []; + for (var id in connections) { + promises.push(connections[id].getInfo()); + } + return Promise.all(promises); + }); + }); }); exports.binding = binding.generate(); diff --git a/extensions/renderer/resources/serial_service.js b/extensions/renderer/resources/serial_service.js index 92e17d06ec2b8..00d137b1ea1ca 100644 --- a/extensions/renderer/resources/serial_service.js +++ b/extensions/renderer/resources/serial_service.js @@ -5,21 +5,20 @@ define('serial_service', [ 'content/public/renderer/service_provider', 'device/serial/serial.mojom', + 'mojo/public/js/bindings/core', 'mojo/public/js/bindings/router', -], function(serviceProvider, serialMojom, routerModule) { +], function(serviceProvider, serialMojom, core, routerModule) { + /** + * A Javascript client for the serial service and connection Mojo services. + * + * This provides a thick client around the Mojo services, exposing a JS-style + * interface to serial connections and information about serial devices. This + * converts parameters and result between the Apps serial API types and the + * Mojo types. + */ - function defineService(proxy, handle) { - if (!handle) - handle = serviceProvider.connectToService(proxy.NAME_); - var router = new routerModule.Router(handle); - var service = new proxy(router); - return { - service: service, - router: router, - }; - } - - var service = defineService(serialMojom.SerialServiceProxy).service; + var service = new serialMojom.SerialServiceProxy(new routerModule.Router( + serviceProvider.connectToService(serialMojom.SerialServiceProxy.NAME_))); function getDevices() { return service.getDevices().then(function(response) { @@ -36,7 +35,240 @@ define('serial_service', [ }); } + var DEFAULT_CLIENT_OPTIONS = { + persistent: false, + name: '', + receiveTimeout: 0, + sendTimeout: 0, + bufferSize: 4096, + }; + + var DATA_BITS_TO_MOJO = { + undefined: serialMojom.DataBits.NONE, + 'seven': serialMojom.DataBits.SEVEN, + 'eight': serialMojom.DataBits.EIGHT, + }; + var STOP_BITS_TO_MOJO = { + undefined: serialMojom.StopBits.NONE, + 'one': serialMojom.StopBits.ONE, + 'two': serialMojom.StopBits.TWO, + }; + var PARITY_BIT_TO_MOJO = { + undefined: serialMojom.ParityBit.NONE, + 'no': serialMojom.ParityBit.NO, + 'odd': serialMojom.ParityBit.ODD, + 'even': serialMojom.ParityBit.EVEN, + }; + + function invertMap(input) { + var output = {}; + for (var key in input) { + if (key == 'undefined') + output[input[key]] = undefined; + else + output[input[key]] = key; + } + return output; + } + var DATA_BITS_FROM_MOJO = invertMap(DATA_BITS_TO_MOJO); + var STOP_BITS_FROM_MOJO = invertMap(STOP_BITS_TO_MOJO); + var PARITY_BIT_FROM_MOJO = invertMap(PARITY_BIT_TO_MOJO); + + function getServiceOptions(options) { + var out = {}; + if (options.dataBits) + out.data_bits = DATA_BITS_TO_MOJO[options.dataBits]; + if (options.stopBits) + out.stop_bits = STOP_BITS_TO_MOJO[options.stopBits]; + if (options.parityBit) + out.parity_bit = PARITY_BIT_TO_MOJO[options.parityBit]; + if ('ctsFlowControl' in options) { + out.has_cts_flow_control = true; + out.cts_flow_control = options.ctsFlowControl; + } + if ('bitrate' in options) + out.bitrate = options.bitrate; + return out; + } + + function convertServiceInfo(result) { + if (!result.info) + throw new Error('Failed to get ConnectionInfo.'); + return { + ctsFlowControl: !!result.info.cts_flow_control, + bitrate: result.info.bitrate || undefined, + dataBits: DATA_BITS_FROM_MOJO[result.info.data_bits], + stopBits: STOP_BITS_FROM_MOJO[result.info.stop_bits], + parityBit: PARITY_BIT_FROM_MOJO[result.info.parity_bit], + }; + } + + function Connection(remoteConnection, router, id, options) { + this.remoteConnection_ = remoteConnection; + this.router_ = router; + this.id_ = id; + getConnections().then(function(connections) { + connections[this.id_] = this; + }.bind(this)); + this.paused_ = false; + this.options_ = {}; + for (var key in DEFAULT_CLIENT_OPTIONS) { + this.options_[key] = DEFAULT_CLIENT_OPTIONS[key]; + } + this.setClientOptions_(options); + } + + Connection.create = function(path, options) { + options = options || {}; + var serviceOptions = getServiceOptions(options); + var pipe = core.createMessagePipe(); + service.connect(path, serviceOptions, pipe.handle0); + var router = new routerModule.Router(pipe.handle1); + var connection = new serialMojom.ConnectionProxy(router); + return connection.getInfo().then(convertServiceInfo).then( + function(info) { + return Promise.all([info, allocateConnectionId()]); + }).catch(function(e) { + router.close(); + throw e; + }).then(function(results) { + var info = results[0]; + var id = results[1]; + var serialConnectionClient = new Connection( + connection, router, id, options); + var clientInfo = serialConnectionClient.getClientInfo_(); + for (var key in clientInfo) { + info[key] = clientInfo[key]; + } + return { + connection: serialConnectionClient, + info: info, + }; + }); + }; + + Connection.prototype.close = function() { + this.router_.close(); + return getConnections().then(function(connections) { + delete connections[this.id_] + return true; + }.bind(this)); + }; + + Connection.prototype.getClientInfo_ = function() { + var info = { + connectionId: this.id_, + paused: this.paused_, + } + for (var key in this.options_) { + info[key] = this.options_[key]; + } + return info; + }; + + Connection.prototype.getInfo = function() { + var info = this.getClientInfo_(); + return this.remoteConnection_.getInfo().then(convertServiceInfo).then( + function(result) { + for (var key in result) { + info[key] = result[key]; + } + return info; + }).catch(function() { + return info; + }); + }; + + Connection.prototype.setClientOptions_ = function(options) { + if ('name' in options) + this.options_.name = options.name; + if ('receiveTimeout' in options) + this.options_.receiveTimeout = options.receiveTimeout; + if ('sendTimeout' in options) + this.options_.sendTimeout = options.sendTimeout; + if ('bufferSize' in options) + this.options_.bufferSize = options.bufferSize; + }; + + Connection.prototype.setOptions = function(options) { + this.setClientOptions_(options); + var serviceOptions = getServiceOptions(options); + if ($Object.keys(serviceOptions).length == 0) + return true; + return this.remoteConnection_.setOptions(serviceOptions).then( + function(result) { + return !!result.success; + }).catch(function() { + return false; + }); + }; + + Connection.prototype.getControlSignals = function() { + return this.remoteConnection_.getControlSignals().then(function(result) { + if (!result.signals) + throw new Error('Failed to get control signals.'); + var signals = result.signals; + return { + dcd: !!signals.dcd, + cts: !!signals.cts, + ri: !!signals.ri, + dsr: !!signals.dsr, + }; + }); + }; + + Connection.prototype.setControlSignals = function(signals) { + var controlSignals = {}; + if ('dtr' in signals) { + controlSignals.has_dtr = true; + controlSignals.dtr = signals.dtr; + } + if ('rts' in signals) { + controlSignals.has_rts = true; + controlSignals.rts = signals.rts; + } + return this.remoteConnection_.setControlSignals(controlSignals).then( + function(result) { + return !!result.success; + }); + }; + + Connection.prototype.flush = function() { + return this.remoteConnection_.flush().then(function(result) { + return !!result.success; + }); + }; + + Connection.prototype.setPaused = function(paused) { + this.paused_ = paused; + }; + + var connections_ = {}; + var nextConnectionId_ = 0; + + // Wrap all access to |connections_| through getConnections to avoid adding + // any synchronous dependencies on it. This will likely be important when + // supporting persistent connections by stashing them. + function getConnections() { + return Promise.resolve(connections_); + } + + function getConnection(id) { + return getConnections().then(function(connections) { + if (!connections[id]) + throw new Error ('Serial connection not found.'); + return connections[id]; + }); + } + + function allocateConnectionId() { + return Promise.resolve(nextConnectionId_++); + } + return { getDevices: getDevices, + createConnection: Connection.create, + getConnection: getConnection, + getConnections: getConnections, }; }); diff --git a/extensions/renderer/runtime_custom_bindings.cc b/extensions/renderer/runtime_custom_bindings.cc index 3815abcd1ee44..fb6851e0f5392 100644 --- a/extensions/renderer/runtime_custom_bindings.cc +++ b/extensions/renderer/runtime_custom_bindings.cc @@ -147,6 +147,8 @@ void RuntimeCustomBindings::GetExtensionViews( view_type = VIEW_TYPE_EXTENSION_DIALOG; } else if (view_type_string == kViewTypeAppWindow) { view_type = VIEW_TYPE_APP_WINDOW; + } else if (view_type_string == kViewTypeLauncherPage) { + view_type = VIEW_TYPE_LAUNCHER_PAGE; } else if (view_type_string == kViewTypePanel) { view_type = VIEW_TYPE_PANEL; } else if (view_type_string != kViewTypeAll) { diff --git a/extensions/shell/app_shell.gyp b/extensions/shell/app_shell.gyp index 51fc6a26c2294..f304af90b748d 100644 --- a/extensions/shell/app_shell.gyp +++ b/extensions/shell/app_shell.gyp @@ -12,6 +12,7 @@ 'type': 'static_library', 'defines!': ['CONTENT_IMPLEMENTATION'], 'dependencies': [ + 'app_shell_version_header', '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/base.gyp:base_prefs_test_support', '<(DEPTH)/components/components.gyp:omaha_query_params', @@ -157,6 +158,9 @@ # TODO(yoz): Something is off here; should this .gyp file be # in the parent directory? Test target extensions_browsertests? '../browser/api/dns/dns_apitest.cc', + '../browser/api/socket/socket_apitest.cc', + '../browser/api/sockets_tcp/sockets_tcp_apitest.cc', + '../browser/api/sockets_udp/sockets_udp_apitest.cc', 'browser/shell_browsertest.cc', 'test/shell_test.h', 'test/shell_test.cc', @@ -165,5 +169,42 @@ 'test/shell_tests_main.cc', ], }, + { + 'target_name': 'app_shell_version_header', + 'type': 'none', + 'direct_dependent_settings': { + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + }, + 'actions': [ + { + 'action_name': 'version_header', + 'message': 'Generating version header file: <@(_outputs)', + 'variables': { + 'lastchange_path': '<(DEPTH)/build/util/LASTCHANGE', + }, + 'inputs': [ + '<(version_path)', + '<(lastchange_path)', + 'common/version.h.in', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/extensions/shell/common/version.h', + ], + 'action': [ + 'python', + '<(version_py_path)', + '-e', 'VERSION_FULL="<(version_full)"', + '-f', '<(lastchange_path)', + 'common/version.h.in', + '<@(_outputs)', + ], + 'includes': [ + '../../build/util/version.gypi', + ], + }, + ], + }, ], # targets } diff --git a/extensions/shell/browser/shell_browser_main_parts.cc b/extensions/shell/browser/shell_browser_main_parts.cc index 3a33566a9ad25..4ab9360004c65 100644 --- a/extensions/shell/browser/shell_browser_main_parts.cc +++ b/extensions/shell/browser/shell_browser_main_parts.cc @@ -92,7 +92,7 @@ void ShellBrowserMainParts::PreMainMessageLoopRun() { desktop_controller_->CreateRootWindow(); // NOTE: Much of this is culled from chrome/test/base/chrome_test_suite.cc - // TODO(jamescook): Initialize chromeos::UserManager. + // TODO(jamescook): Initialize user_manager::UserManager. net_log_.reset(new content::ShellNetLog("app_shell")); extensions_client_.reset(new ShellExtensionsClient()); diff --git a/extensions/shell/browser/shell_extension_system.cc b/extensions/shell/browser/shell_extension_system.cc index 8c20401989a4d..47db358ba0c30 100644 --- a/extensions/shell/browser/shell_extension_system.cc +++ b/extensions/shell/browser/shell_extension_system.cc @@ -112,7 +112,7 @@ ManagementPolicy* ShellExtensionSystem::management_policy() { return NULL; } -UserScriptMaster* ShellExtensionSystem::user_script_master() { +SharedUserScriptMaster* ShellExtensionSystem::shared_user_script_master() { return NULL; } @@ -193,4 +193,10 @@ scoped_ptr ShellExtensionSystem::GetDependentExtensions( return empty.PassAs(); } +DeclarativeUserScriptMaster* +ShellExtensionSystem::GetDeclarativeUserScriptMasterByExtension( + const ExtensionId& extension_id) { + return NULL; +} + } // namespace extensions diff --git a/extensions/shell/browser/shell_extension_system.h b/extensions/shell/browser/shell_extension_system.h index b47c9a5857ebe..966bfaf8e3d0f 100644 --- a/extensions/shell/browser/shell_extension_system.h +++ b/extensions/shell/browser/shell_extension_system.h @@ -23,11 +23,13 @@ class BrowserContext; namespace extensions { +class DeclarativeUserScriptMaster; class EventRouter; class InfoMap; class LazyBackgroundTaskQueue; class ProcessManager; class RendererStartupHelper; +class SharedUserScriptMaster; // A simplified version of ExtensionSystem for app_shell. Allows // app_shell to skip initialization of services it doesn't need. @@ -52,7 +54,7 @@ class ShellExtensionSystem : public ExtensionSystem { virtual ExtensionService* extension_service() OVERRIDE; virtual RuntimeData* runtime_data() OVERRIDE; virtual ManagementPolicy* management_policy() OVERRIDE; - virtual UserScriptMaster* user_script_master() OVERRIDE; + virtual SharedUserScriptMaster* shared_user_script_master() OVERRIDE; virtual ProcessManager* process_manager() OVERRIDE; virtual StateStore* state_store() OVERRIDE; virtual StateStore* rules_store() OVERRIDE; @@ -73,6 +75,9 @@ class ShellExtensionSystem : public ExtensionSystem { virtual ContentVerifier* content_verifier() OVERRIDE; virtual scoped_ptr GetDependentExtensions( const Extension* extension) OVERRIDE; + virtual DeclarativeUserScriptMaster* + GetDeclarativeUserScriptMasterByExtension( + const ExtensionId& extension_id) OVERRIDE; private: content::BrowserContext* browser_context_; // Not owned. diff --git a/extensions/shell/common/version.h.in b/extensions/shell/common/version.h.in new file mode 100644 index 0000000000000..70f269aad821c --- /dev/null +++ b/extensions/shell/common/version.h.in @@ -0,0 +1,13 @@ +// 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. + +// version.h is generated from version.h.in. Edit the source! + +#ifndef EXTENSIONS_SHELL_COMMON_VERSION_INFO_H_ +#define EXTENSIONS_SHELL_COMMON_VERSION_INFO_H_ + +#define PRODUCT_VERSION "@VERSION_FULL@" +#define LAST_CHANGE "@LASTCHANGE@" + +#endif // EXTENSIONS_SHELL_COMMON_VERSION_INFO_H_ diff --git a/extensions/strings/extensions_strings_da.xtb b/extensions/strings/extensions_strings_da.xtb index 8d23df154fff1..a1719ab62288a 100644 --- a/extensions/strings/extensions_strings_da.xtb +++ b/extensions/strings/extensions_strings_da.xtb @@ -6,27 +6,27 @@ Læse og redigere dine data på Læse og redigere dine data på , og Få adgang til USB-enheden -Skift dine søgeindstillinger til: -Udveksl data med en computer på det lokale netværk eller internettet +Skifte dine søgeindstillinger til: +Udveksle data med en computer på det lokale netværk eller internettet Få adgang til USB-enheden fra Baggrundsscriptet "" kunne ikke indlæses. -Udveksl data med den computer, der er navngivet +Udveksle data med computeren ved navn Denne udvidelse inkluderer nøglefilen "". Det ønsker du sandsynligvis ikke at gøre. Lokalisering anvendt, men default_locale blev ikke angivet i manifestet. Få adgang til USB-enheden fra Få adgang til USB-enheder Udvidelsesikonet '' kunne ikke indlæses. Få adgang til listen over netværksforbindelser -Udveksl data med computerne, der er navngivet: +Udveksle data med computerne ved navn: -Udveksl data med en computer på domænet -Udveksl data med en computer på domænerne: -Skift din startside til: +Udveksle data med en computer på domænet +Udveksle data med en computer på domænerne: +Skifte din startside til: Læse og redigere dine data på websites Manifestfil mangler eller er ulæselig. -få adgang til billeder, musik og andre medier fra din computer +Få adgang til billeder, musik og andre medier fra din computer Administratoren af denne computer kræver, at skal installeres. Den kan ikke fjernes eller ændres. -Skift din startside til: +Skifte din startside til: Læse og redigere dine data på og Baggrundssiden '' kunne ikke indlæses. Læse og redigere dine data på websites diff --git a/chrome/test/data/extensions/extension_icon_image/16.png b/extensions/test/data/extension_icon_image/16.png similarity index 100% rename from chrome/test/data/extensions/extension_icon_image/16.png rename to extensions/test/data/extension_icon_image/16.png diff --git a/chrome/test/data/extensions/extension_icon_image/24.png b/extensions/test/data/extension_icon_image/24.png similarity index 100% rename from chrome/test/data/extensions/extension_icon_image/24.png rename to extensions/test/data/extension_icon_image/24.png diff --git a/chrome/test/data/extensions/extension_icon_image/48.png b/extensions/test/data/extension_icon_image/48.png similarity index 100% rename from chrome/test/data/extensions/extension_icon_image/48.png rename to extensions/test/data/extension_icon_image/48.png diff --git a/chrome/test/data/extensions/extension_icon_image/app.json b/extensions/test/data/extension_icon_image/manifest.json similarity index 100% rename from chrome/test/data/extensions/extension_icon_image/app.json rename to extensions/test/data/extension_icon_image/manifest.json diff --git a/extensions/test/data/serial_unittest.js b/extensions/test/data/serial_unittest.js index 0755ed752a0b8..dc7bbea56e06c 100644 --- a/extensions/test/data/serial_unittest.js +++ b/extensions/test/data/serial_unittest.js @@ -2,10 +2,62 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +/** + * Unit tests for the JS serial service client. + * + * These test that configuration and data are correctly transmitted between the + * client and the service. + */ + var test = require('test').binding; var serial = require('serial').binding; var unittestBindings = require('test_environment_specific_bindings'); +var connectionId = null; + +function connect(callback, options) { + options = options || { + name: 'test connection', + bufferSize: 8192, + receiveTimeout: 12345, + sendTimeout: 6789, + persistent: true, + } + serial.connect('device', options, test.callbackPass(function(connectionInfo) { + connectionId = connectionInfo.connectionId; + callback(connectionInfo); + })); +} + +function disconnect() { + serial.disconnect(connectionId, test.callbackPass(function(success) { + test.assertTrue(success); + connectionId = null; + })); +} + +function checkClientConnectionInfo(connectionInfo) { + test.assertFalse(connectionInfo.persistent); + test.assertEq('test connection', connectionInfo.name); + test.assertEq(12345, connectionInfo.receiveTimeout); + test.assertEq(6789, connectionInfo.sendTimeout); + test.assertEq(8192, connectionInfo.bufferSize); + test.assertFalse(connectionInfo.paused); +} + +function checkServiceConnectionInfo(connectionInfo) { + test.assertEq(9600, connectionInfo.bitrate); + test.assertEq('eight', connectionInfo.dataBits); + test.assertEq('no', connectionInfo.parityBit); + test.assertEq('one', connectionInfo.stopBits); + test.assertFalse(connectionInfo.ctsFlowControl); +} + +function checkConnectionInfo(connectionInfo) { + checkClientConnectionInfo(connectionInfo); + checkServiceConnectionInfo(connectionInfo); +} + unittestBindings.exportTests([ function testGetDevices() { serial.getDevices(test.callbackPass(function(devices) { @@ -21,4 +73,240 @@ unittestBindings.exportTests([ test.assertEq('', devices[2].path); })); }, + + function testConnectFail() { + serial.connect('device', + test.callbackFail('Failed to connect to the port.')); + }, + + function testGetInfoFailOnConnect() { + serial.connect('device', + test.callbackFail('Failed to connect to the port.')); + }, + + function testConnectInvalidBitrate() { + serial.connect('device', {bitrate: -1}, test.callbackFail( + 'Failed to connect to the port.')); + }, + + function testConnect() { + connect(function(connectionInfo) { + disconnect(); + checkConnectionInfo(connectionInfo); + }); + }, + + function testConnectDefaultOptions() { + connect(function(connectionInfo) { + disconnect(); + test.assertEq(9600, connectionInfo.bitrate); + test.assertEq('eight', connectionInfo.dataBits); + test.assertEq('no', connectionInfo.parityBit); + test.assertEq('one', connectionInfo.stopBits); + test.assertFalse(connectionInfo.ctsFlowControl); + test.assertFalse(connectionInfo.persistent); + test.assertEq('', connectionInfo.name); + test.assertEq(0, connectionInfo.receiveTimeout); + test.assertEq(0, connectionInfo.sendTimeout); + test.assertEq(4096, connectionInfo.bufferSize); + }, {}); + }, + + function testGetInfo() { + connect(function() { + serial.getInfo(connectionId, + test.callbackPass(function(connectionInfo) { + disconnect(); + checkConnectionInfo(connectionInfo); + })); + }); + }, + + function testGetInfoFailToGetPortInfo() { + connect(function() { + serial.getInfo(connectionId, + test.callbackPass(function(connectionInfo) { + disconnect(); + checkClientConnectionInfo(connectionInfo); + test.assertFalse('bitrate' in connectionInfo); + test.assertFalse('dataBits' in connectionInfo); + test.assertFalse('parityBit' in connectionInfo); + test.assertFalse('stopBit' in connectionInfo); + test.assertFalse('ctsFlowControl' in connectionInfo); + })); + }); + }, + + function testGetConnections() { + connect(function() { + serial.getConnections(test.callbackPass(function(connections) { + disconnect(); + test.assertEq(1, connections.length); + checkConnectionInfo(connections[0]); + })); + }); + }, + + function testGetControlSignals() { + connect(function() { + var calls = 0; + function checkControlSignals(signals) { + if (calls == 15) { + disconnect(); + } else { + serial.getControlSignals( + connectionId, + test.callbackPass(checkControlSignals)); + } + test.assertEq(!!(calls & 1), signals.dcd); + test.assertEq(!!(calls & 2), signals.cts); + test.assertEq(!!(calls & 4), signals.ri); + test.assertEq(!!(calls & 8), signals.dsr); + calls++; + } + serial.getControlSignals(connectionId, + test.callbackPass(checkControlSignals)); + }); + }, + + function testSetControlSignals() { + connect(function() { + var signalsValues = [ + {}, + {dtr: false}, + {dtr: true}, + {rts: false}, + {dtr: false, rts: false}, + {dtr: true, rts: false}, + {rts: true}, + {dtr: false, rts: true}, + {dtr: true, rts: true}, + ]; + var calls = 0; + function setControlSignals(success) { + if (calls == signalsValues.length) { + disconnect(); + } else { + serial.setControlSignals(connectionId, + signalsValues[calls++], + test.callbackPass(setControlSignals)); + } + test.assertTrue(success); + } + setControlSignals(true); + }); + }, + + function testUpdate() { + connect(function() { + var optionsValues = [ + {}, // SetPortOptions is called once during connection. + {bitrate: 57600}, + {dataBits: 'seven'}, + {dataBits: 'eight'}, + {parityBit: 'no'}, + {parityBit: 'odd'}, + {parityBit: 'even'}, + {stopBits: 'one'}, + {stopBits: 'two'}, + {ctsFlowControl: false}, + {ctsFlowControl: true}, + {bufferSize: 1}, + {sendTimeout: 0}, + {receiveTimeout: 0}, + {persistent: false}, + {name: 'name'}, + ]; + var calls = 0; + function checkInfo(info) { + for (var key in optionsValues[calls]) { + test.assertEq(optionsValues[calls][key], info[key]); + } + setOptions(); + } + function setOptions() { + if (++calls == optionsValues.length) { + disconnect(); + } else { + serial.update(connectionId, + optionsValues[calls], + test.callbackPass(function(success) { + serial.getInfo(connectionId, test.callbackPass(checkInfo)); + test.assertTrue(success); + })); + } + } + setOptions(); + }); + }, + + function testUpdateInvalidBitrate() { + connect(function() { + serial.update(connectionId, + {bitrate: -1}, + test.callbackPass(function(success) { + disconnect(); + test.assertFalse(success); + })); + }); + }, + + function testFlush() { + connect(function() { + serial.flush(connectionId, + test.callbackPass(function(success) { + disconnect(); + test.assertTrue(success); + })); + }); + }, + + function testSetPaused() { + connect(function() { + serial.setPaused(connectionId, true, test.callbackPass(function() { + serial.getInfo(connectionId, test.callbackPass(function(info) { + serial.setPaused(connectionId, false, test.callbackPass(function() { + serial.getInfo(connectionId, test.callbackPass(function(info) { + test.assertFalse(info.paused); + disconnect(); + })); + })); + test.assertTrue(info.paused); + })); + })); + }); + }, + + function testDisconnectUnknownConnectionId() { + serial.disconnect(-1, test.callbackFail('Serial connection not found.')); + }, + + function testGetInfoUnknownConnectionId() { + serial.getInfo(-1, test.callbackFail('Serial connection not found.')); + }, + + function testUpdateUnknownConnectionId() { + serial.update(-1, {}, test.callbackFail('Serial connection not found.')); + }, + + function testSetControlSignalsUnknownConnectionId() { + serial.setControlSignals(-1, {}, test.callbackFail( + 'Serial connection not found.')); + }, + + function testGetControlSignalsUnknownConnectionId() { + serial.getControlSignals(-1, test.callbackFail( + 'Serial connection not found.')); + }, + + function testFlushUnknownConnectionId() { + serial.flush(-1, test.callbackFail('Serial connection not found.')); + }, + + function testSetPausedUnknownConnectionId() { + serial.setPaused( + -1, true, test.callbackFail('Serial connection not found.')); + serial.setPaused( + -1, false, test.callbackFail('Serial connection not found.')); + }, ], test.runTests, exports); diff --git a/gin/interceptor.cc b/gin/interceptor.cc index 7efc32ee68bf2..617fd08e26d61 100644 --- a/gin/interceptor.cc +++ b/gin/interceptor.cc @@ -26,9 +26,11 @@ v8::Local NamedPropertyInterceptor::GetNamedProperty( return v8::Local(); } -void NamedPropertyInterceptor::SetNamedProperty(v8::Isolate* isolate, +bool NamedPropertyInterceptor::SetNamedProperty(v8::Isolate* isolate, const std::string& property, - v8::Local value) {} + v8::Local value) { + return false; +} std::vector NamedPropertyInterceptor::EnumerateNamedProperties( v8::Isolate* isolate) { @@ -51,10 +53,12 @@ v8::Local IndexedPropertyInterceptor::GetIndexedProperty( return v8::Local(); } -void IndexedPropertyInterceptor::SetIndexedProperty( +bool IndexedPropertyInterceptor::SetIndexedProperty( v8::Isolate* isolate, uint32_t index, - v8::Local value) {} + v8::Local value) { + return false; +} std::vector IndexedPropertyInterceptor::EnumerateIndexedProperties( v8::Isolate* isolate) { diff --git a/gin/interceptor.h b/gin/interceptor.h index 802929b83770b..43cb3464fe97f 100644 --- a/gin/interceptor.h +++ b/gin/interceptor.h @@ -25,7 +25,8 @@ class GIN_EXPORT NamedPropertyInterceptor { virtual v8::Local GetNamedProperty(v8::Isolate* isolate, const std::string& property); - virtual void SetNamedProperty(v8::Isolate* isolate, + // Return true if the set was interecepted. + virtual bool SetNamedProperty(v8::Isolate* isolate, const std::string& property, v8::Local value); virtual std::vector EnumerateNamedProperties( @@ -45,7 +46,8 @@ class GIN_EXPORT IndexedPropertyInterceptor { virtual v8::Local GetIndexedProperty(v8::Isolate* isolate, uint32_t index); - virtual void SetIndexedProperty(v8::Isolate* isolate, + // Return true if the set was interecepted. + virtual bool SetIndexedProperty(v8::Isolate* isolate, uint32_t index, v8::Local value); virtual std::vector EnumerateIndexedProperties( diff --git a/gin/interceptor_unittest.cc b/gin/interceptor_unittest.cc index 965ad6c57f390..e25005b4e0ef4 100644 --- a/gin/interceptor_unittest.cc +++ b/gin/interceptor_unittest.cc @@ -42,12 +42,14 @@ class MyInterceptor : public Wrappable, return v8::Local(); } } - virtual void SetNamedProperty(v8::Isolate* isolate, + virtual bool SetNamedProperty(v8::Isolate* isolate, const std::string& property, v8::Local value) OVERRIDE { - if (property != "value") - return; - ConvertFromV8(isolate, value, &value_); + if (property == "value") { + ConvertFromV8(isolate, value, &value_); + return true; + } + return false; } virtual std::vector EnumerateNamedProperties( v8::Isolate* isolate) OVERRIDE { @@ -64,12 +66,15 @@ class MyInterceptor : public Wrappable, return ConvertToV8(isolate, value_); return v8::Local(); } - virtual void SetIndexedProperty(v8::Isolate* isolate, + virtual bool SetIndexedProperty(v8::Isolate* isolate, uint32_t index, v8::Local value) OVERRIDE { - if (index != 0) - return; - ConvertFromV8(isolate, value, &value_); + if (index == 0) { + ConvertFromV8(isolate, value, &value_); + return true; + } + // Don't allow bypassing the interceptor. + return true; } virtual std::vector EnumerateIndexedProperties(v8::Isolate* isolate) OVERRIDE { @@ -172,4 +177,20 @@ TEST_F(InterceptorTest, IndexedInterceptor) { " else obj[0] = 191; })"); } +TEST_F(InterceptorTest, BypassInterceptorAllowed) { + RunInterceptorTest( + "(function (obj) {" + " obj.value = 191 /* make test happy */;" + " obj.foo = 23;" + " if (obj.foo !== 23) throw 'FAIL'; })"); +} + +TEST_F(InterceptorTest, BypassInterceptorForbidden) { + RunInterceptorTest( + "(function (obj) {" + " obj.value = 191 /* make test happy */;" + " obj[1] = 23;" + " if (obj[1] === 23) throw 'FAIL'; })"); +} + } // namespace gin diff --git a/gin/object_template_builder.cc b/gin/object_template_builder.cc index 603166cfabff1..f649d34a9ff2f 100644 --- a/gin/object_template_builder.cc +++ b/gin/object_template_builder.cc @@ -69,7 +69,8 @@ void NamedPropertySetter(v8::Local property, return; std::string name; ConvertFromV8(isolate, property, &name); - interceptor->SetNamedProperty(isolate, name, value); + if (interceptor->SetNamedProperty(isolate, name, value)) + info.GetReturnValue().Set(value); } void NamedPropertyQuery(v8::Local property, @@ -114,7 +115,8 @@ void IndexedPropertySetter(uint32_t index, IndexedInterceptorFromV8(isolate, info.Holder()); if (!interceptor) return; - interceptor->SetIndexedProperty(isolate, index, value); + if (interceptor->SetIndexedProperty(isolate, index, value)) + info.GetReturnValue().Set(value); } void IndexedPropertyEnumerator( diff --git a/gin/shell/gin_shell_unittest.cc b/gin/shell/gin_shell_unittest.cc index 0df979de71b6d..f80d6b0c3c290 100644 --- a/gin/shell/gin_shell_unittest.cc +++ b/gin/shell/gin_shell_unittest.cc @@ -3,7 +3,7 @@ // found in the LICENSE file. #include "base/command_line.h" -#include "base/files/file_path.h" +#include "base/file_util.h" #include "base/path_service.h" #include "base/process/launch.h" #include "base/strings/string_util.h" @@ -25,8 +25,13 @@ base::FilePath HelloWorldPath() { } TEST(GinShellTest, HelloWorld) { - CommandLine cmd(GinShellPath()); - cmd.AppendArgPath(HelloWorldPath()); + base::FilePath gin_shell_path(GinShellPath()); + base::FilePath hello_world_path(HelloWorldPath()); + ASSERT_TRUE(base::PathExists(gin_shell_path)); + ASSERT_TRUE(base::PathExists(hello_world_path)); + + CommandLine cmd(gin_shell_path); + cmd.AppendArgPath(hello_world_path); std::string output; ASSERT_TRUE(base::GetAppOutput(cmd, &output)); base::TrimWhitespaceASCII(output, base::TRIM_ALL, &output); diff --git a/gin/test/file.cc b/gin/test/file.cc index cfd0af24e4730..87b7f4b100c92 100644 --- a/gin/test/file.cc +++ b/gin/test/file.cc @@ -59,7 +59,8 @@ v8::Handle GetFilesInDirectory(gin::Arguments* args) { names.push_back(name.BaseName().AsUTF8Unsafe()); } - return gin::Converter>::ToV8(args->isolate(), names); + return gin::Converter >::ToV8(args->isolate(), + names); } gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; diff --git a/google_apis/drive/base_requests_unittest.cc b/google_apis/drive/base_requests_unittest.cc index 819f3e79d283c..11f887c34372e 100644 --- a/google_apis/drive/base_requests_unittest.cc +++ b/google_apis/drive/base_requests_unittest.cc @@ -27,9 +27,9 @@ const char kInvalidJsonString[] = "$$$"; class FakeUrlFetchRequest : public UrlFetchRequestBase { public: - explicit FakeUrlFetchRequest(RequestSender* sender, - const EntryActionCallback& callback, - const GURL& url) + FakeUrlFetchRequest(RequestSender* sender, + const EntryActionCallback& callback, + const GURL& url) : UrlFetchRequestBase(sender), callback_(callback), url_(url) { diff --git a/google_apis/drive/drive_api_requests.h b/google_apis/drive/drive_api_requests.h index 355d37e57ec93..a983dd5f9ebaf 100644 --- a/google_apis/drive/drive_api_requests.h +++ b/google_apis/drive/drive_api_requests.h @@ -121,7 +121,9 @@ class DriveApiDataRequest : public DriveApiPartialFieldRequest { // Receives the parsed result and invokes the callback. void OnDataParsed(GDataErrorCode error, scoped_ptr value) { - callback_.Run(value ? error : GDATA_PARSE_ERROR, value.Pass()); + if (!value) + error = GDATA_PARSE_ERROR; + callback_.Run(error, value.Pass()); OnProcessURLFetchResultsComplete(); } diff --git a/google_apis/drive/gdata_wapi_requests.cc b/google_apis/drive/gdata_wapi_requests.cc index 58ed9c04f5d89..7c3e532d1472c 100644 --- a/google_apis/drive/gdata_wapi_requests.cc +++ b/google_apis/drive/gdata_wapi_requests.cc @@ -72,7 +72,9 @@ void GetResourceEntryRequest::RunCallbackOnPrematureFailure( void GetResourceEntryRequest::OnDataParsed(GDataErrorCode error, scoped_ptr entry) { - callback_.Run(entry ? error : GDATA_PARSE_ERROR, entry.Pass()); + if (!entry) + error = GDATA_PARSE_ERROR; + callback_.Run(error, entry.Pass()); OnProcessURLFetchResultsComplete(); } diff --git a/google_apis/gaia/account_tracker.cc b/google_apis/gaia/account_tracker.cc index 0ec2ec1aa9676..50c4e7083fe1c 100644 --- a/google_apis/gaia/account_tracker.cc +++ b/google_apis/gaia/account_tracker.cc @@ -274,8 +274,10 @@ AccountIdFetcher::~AccountIdFetcher() { } void AccountIdFetcher::Start() { + OAuth2TokenService::ScopeSet scopes; + scopes.insert("https://www.googleapis.com/auth/userinfo.profile"); login_token_request_ = token_service_->StartRequest( - account_key_, OAuth2TokenService::ScopeSet(), this); + account_key_, scopes, this); } void AccountIdFetcher::OnGetTokenSuccess( diff --git a/google_apis/gaia/gaia_auth_consumer.h b/google_apis/gaia/gaia_auth_consumer.h index d5ab306c35573..5ff96d55a1342 100644 --- a/google_apis/gaia/gaia_auth_consumer.h +++ b/google_apis/gaia/gaia_auth_consumer.h @@ -85,6 +85,10 @@ class GaiaAuthConsumer { virtual void OnListAccountsSuccess(const std::string& data) {} virtual void OnListAccountsFailure(const GoogleServiceAuthError& error) {} + + virtual void OnGetCheckConnectionInfoSuccess(const std::string& data) {} + virtual void OnGetCheckConnectionInfoError( + const GoogleServiceAuthError& error) {} }; #endif // GOOGLE_APIS_GAIA_GAIA_AUTH_CONSUMER_H_ diff --git a/google_apis/gaia/gaia_auth_fetcher.cc b/google_apis/gaia/gaia_auth_fetcher.cc index ca4cdc0588a22..b0046d5eb3009 100644 --- a/google_apis/gaia/gaia_auth_fetcher.cc +++ b/google_apis/gaia/gaia_auth_fetcher.cc @@ -186,6 +186,8 @@ GaiaAuthFetcher::GaiaAuthFetcher(GaiaAuthConsumer* consumer, base::StringPrintf(kUberAuthTokenURLFormat, source.c_str()))), oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()), list_accounts_gurl_(GaiaUrls::GetInstance()->list_accounts_url()), + get_check_connection_info_url_( + GaiaUrls::GetInstance()->get_check_connection_info_url()), client_login_to_oauth2_gurl_( GaiaUrls::GetInstance()->client_login_to_oauth2_url()), fetch_pending_(false) {} @@ -349,16 +351,24 @@ std::string GaiaAuthFetcher::MakeGetUserInfoBody(const std::string& lsid) { // static std::string GaiaAuthFetcher::MakeMergeSessionBody( const std::string& auth_token, + const std::string& external_cc_result, const std::string& continue_url, const std::string& source) { std::string encoded_auth_token = net::EscapeUrlEncodedData(auth_token, true); std::string encoded_continue_url = net::EscapeUrlEncodedData(continue_url, true); std::string encoded_source = net::EscapeUrlEncodedData(source, true); - return base::StringPrintf(kMergeSessionFormat, - encoded_auth_token.c_str(), - encoded_continue_url.c_str(), - encoded_source.c_str()); + std::string result = base::StringPrintf(kMergeSessionFormat, + encoded_auth_token.c_str(), + encoded_continue_url.c_str(), + encoded_source.c_str()); + if (!external_cc_result.empty()) { + base::StringAppendF(&result, "&externalCcResult=%s", + net::EscapeUrlEncodedData( + external_cc_result, true).c_str()); + } + + return result; } // static @@ -622,7 +632,8 @@ void GaiaAuthFetcher::StartGetUserInfo(const std::string& lsid) { fetcher_->Start(); } -void GaiaAuthFetcher::StartMergeSession(const std::string& uber_token) { +void GaiaAuthFetcher::StartMergeSession(const std::string& uber_token, + const std::string& external_cc_result) { DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; DVLOG(1) << "Starting MergeSession with uber_token=" << uber_token; @@ -636,7 +647,8 @@ void GaiaAuthFetcher::StartMergeSession(const std::string& uber_token) { // created such that it sends the cookies with the request, which is // different from all other requests the fetcher can make. std::string continue_url("http://www.google.com"); - request_body_ = MakeMergeSessionBody(uber_token, continue_url, source_); + request_body_ = MakeMergeSessionBody(uber_token, external_cc_result, + continue_url, source_); fetcher_.reset(CreateGaiaFetcher(getter_, request_body_, std::string(), @@ -695,6 +707,19 @@ void GaiaAuthFetcher::StartListAccounts() { fetcher_->Start(); } +void GaiaAuthFetcher::StartGetCheckConnectionInfo() { + DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; + + fetcher_.reset(CreateGaiaFetcher(getter_, + std::string(), + std::string(), + get_check_connection_info_url_, + kLoadFlagsIgnoreCookies, + this)); + fetch_pending_ = true; + fetcher_->Start(); +} + // static GoogleServiceAuthError GaiaAuthFetcher::GenerateAuthError( const std::string& data, @@ -778,8 +803,14 @@ void GaiaAuthFetcher::OnClientLoginToOAuth2Fetched( int response_code) { if (status.is_success() && response_code == net::HTTP_OK) { std::string auth_code; - ParseClientLoginToOAuth2Response(cookies, &auth_code); - StartAuthCodeForOAuth2TokenExchange(auth_code); + if (ParseClientLoginToOAuth2Response(cookies, &auth_code)) { + StartAuthCodeForOAuth2TokenExchange(auth_code); + } else { + GoogleServiceAuthError auth_error( + GoogleServiceAuthError::FromUnexpectedServiceResponse( + "ClientLogin response cookies didn't contain an auth code")); + consumer_->OnClientOAuthFailure(auth_error); + } } else { GoogleServiceAuthError auth_error(GenerateAuthError(data, status)); consumer_->OnClientOAuthFailure(auth_error); @@ -885,6 +916,17 @@ void GaiaAuthFetcher::OnOAuthLoginFetched(const std::string& data, } } +void GaiaAuthFetcher::OnGetCheckConnectionInfoFetched( + const std::string& data, + const net::URLRequestStatus& status, + int response_code) { + if (status.is_success() && response_code == net::HTTP_OK) { + consumer_->OnGetCheckConnectionInfoSuccess(data); + } else { + consumer_->OnGetCheckConnectionInfoError(GenerateAuthError(data, status)); + } +} + void GaiaAuthFetcher::OnURLFetchComplete(const net::URLFetcher* source) { fetch_pending_ = false; // Some of the GAIA requests perform redirects, which results in the final @@ -926,6 +968,8 @@ void GaiaAuthFetcher::OnURLFetchComplete(const net::URLFetcher* source) { OnOAuth2RevokeTokenFetched(data, status, response_code); } else if (url == list_accounts_gurl_) { OnListAccountsFetched(data, status, response_code); + } else if (url == get_check_connection_info_url_) { + OnGetCheckConnectionInfoFetched(data, status, response_code); } else { NOTREACHED(); } diff --git a/google_apis/gaia/gaia_auth_fetcher.h b/google_apis/gaia/gaia_auth_fetcher.h index aa66a49e03263..57afc22faadda 100644 --- a/google_apis/gaia/gaia_auth_fetcher.h +++ b/google_apis/gaia/gaia_auth_fetcher.h @@ -148,9 +148,17 @@ class GaiaAuthFetcher : public net::URLFetcherDelegate { // credentials represented by the account whose uber-auth token is // |uber_token|. This method will modify the cookies of the current profile. // + // The |external_cc_result| string can specify the result of connetion checks + // for various google properties, and MergeSession will set cookies on those + // properties too if appropriate. See StartGetCheckConnectionInfo() for + // details. The string is a comma separated list of token/result pairs, where + // token and result are separated by a colon. This string may be empty, in + // which case no specific handling is performed. + // // Either OnMergeSessionSuccess or OnMergeSessionFailure will be // called on the consumer on the original thread. - void StartMergeSession(const std::string& uber_token); + void StartMergeSession(const std::string& uber_token, + const std::string& external_cc_result); // Start a request to exchange an OAuthLogin-scoped oauth2 access token for an // uber-auth token. The returned token can be used with the method @@ -174,6 +182,11 @@ class GaiaAuthFetcher : public net::URLFetcherDelegate { // Starts a request to list the accounts in the GAIA cookie. void StartListAccounts(); + // Starts a request to get the list of URLs to check for connection info. + // Returns token/URL pairs to check, and the resulting status can be given to + // /MergeSession requests. + void StartGetCheckConnectionInfo(); + // Implementation of net::URLFetcherDelegate virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; @@ -293,6 +306,10 @@ class GaiaAuthFetcher : public net::URLFetcherDelegate { const net::URLRequestStatus& status, int response_code); + void OnGetCheckConnectionInfoFetched(const std::string& data, + const net::URLRequestStatus& status, + int response_code); + // Tokenize the results of a ClientLogin fetch. static void ParseClientLoginResponse(const std::string& data, std::string* sid, @@ -342,8 +359,9 @@ class GaiaAuthFetcher : public net::URLFetcherDelegate { // Supply the authentication token returned from StartIssueAuthToken. static std::string MakeMergeSessionBody(const std::string& auth_token, - const std::string& continue_url, - const std::string& source); + const std::string& external_cc_result, + const std::string& continue_url, + const std::string& source); static std::string MakeGetAuthCodeHeader(const std::string& auth_token); @@ -384,6 +402,7 @@ class GaiaAuthFetcher : public net::URLFetcherDelegate { const GURL uberauth_token_gurl_; const GURL oauth_login_gurl_; const GURL list_accounts_gurl_; + const GURL get_check_connection_info_url_; // While a fetch is going on: scoped_ptr fetcher_; diff --git a/google_apis/gaia/gaia_auth_fetcher_unittest.cc b/google_apis/gaia/gaia_auth_fetcher_unittest.cc index 75a8db73afe9e..971d47d319798 100644 --- a/google_apis/gaia/gaia_auth_fetcher_unittest.cc +++ b/google_apis/gaia/gaia_auth_fetcher_unittest.cc @@ -188,6 +188,7 @@ class MockGaiaConsumer : public GaiaAuthConsumer { MOCK_METHOD1(OnUberAuthTokenFailure, void( const GoogleServiceAuthError& error)); MOCK_METHOD1(OnListAccountsSuccess, void(const std::string& data)); + MOCK_METHOD1(OnGetCheckConnectionInfoSuccess, void(const std::string& data)); }; #if defined(OS_WIN) @@ -655,7 +656,7 @@ TEST_F(GaiaAuthFetcherTest, MergeSessionSuccess) { net::TestURLFetcherFactory factory; GaiaAuthFetcher auth(&consumer, std::string(), GetRequestContext()); - auth.StartMergeSession("myubertoken"); + auth.StartMergeSession("myubertoken", std::string()); EXPECT_TRUE(auth.HasPendingFetch()); MockFetcher mock_fetcher( @@ -675,7 +676,7 @@ TEST_F(GaiaAuthFetcherTest, MergeSessionSuccessRedirect) { net::TestURLFetcherFactory factory; GaiaAuthFetcher auth(&consumer, std::string(), GetRequestContext()); - auth.StartMergeSession("myubertoken"); + auth.StartMergeSession("myubertoken", std::string()); // Make sure the fetcher created has the expected flags. Set its url() // properties to reflect a redirect. @@ -791,3 +792,17 @@ TEST_F(GaiaAuthFetcherTest, ListAccounts) { status, net::HTTP_OK, cookies_, data, net::URLFetcher::GET, &auth); auth.OnURLFetchComplete(&mock_fetcher); } + +TEST_F(GaiaAuthFetcherTest, GetCheckConnectionInfo) { + std::string data( + "[{\"carryBackToken\": \"token1\", \"url\": \"http://www.google.com\"}]"); + MockGaiaConsumer consumer; + EXPECT_CALL(consumer, OnGetCheckConnectionInfoSuccess(data)).Times(1); + + GaiaAuthFetcher auth(&consumer, std::string(), GetRequestContext()); + net::URLRequestStatus status(net::URLRequestStatus::SUCCESS, 0); + MockFetcher mock_fetcher( + GaiaUrls::GetInstance()->get_check_connection_info_url(), + status, net::HTTP_OK, cookies_, data, net::URLFetcher::GET, &auth); + auth.OnURLFetchComplete(&mock_fetcher); +} diff --git a/google_apis/gaia/gaia_constants.cc b/google_apis/gaia/gaia_constants.cc index 95a5945f74e96..d3d0cd9cc95fa 100644 --- a/google_apis/gaia/gaia_constants.cc +++ b/google_apis/gaia/gaia_constants.cc @@ -45,7 +45,12 @@ const char kChromeSyncSupervisedOAuth2Scope[] = const char kGoogleTalkOAuth2Scope[] = "https://www.googleapis.com/auth/googletalk"; -// Used to mint uber auth tokens when needed. +const char kGoogleUserInfoEmail[] = + "https://www.googleapis.com/auth/userinfo.email"; +const char kGoogleUserInfoProfile[] = + "https://www.googleapis.com/auth/userinfo.profile"; + + // Used to mint uber auth tokens when needed. const char kGaiaSid[] = "sid"; const char kGaiaLsid[] = "lsid"; const char kGaiaOAuthToken[] = "oauthToken"; diff --git a/google_apis/gaia/gaia_constants.h b/google_apis/gaia/gaia_constants.h index ea8cc0f2f2c8d..4786ac88bbf5b 100644 --- a/google_apis/gaia/gaia_constants.h +++ b/google_apis/gaia/gaia_constants.h @@ -18,6 +18,8 @@ extern const char kGaiaService[]; // uber token extern const char kPicasaService[]; extern const char kSyncService[]; extern const char kRemotingService[]; + +// OAuth2 scopes. extern const char kOAuth1LoginScope[]; extern const char kOAuthWrapBridgeUserInfoScope[]; extern const char kDeviceManagementServiceOAuth[]; @@ -25,6 +27,8 @@ extern const char kAnyApiOAuth2Scope[]; extern const char kChromeSyncOAuth2Scope[]; extern const char kChromeSyncSupervisedOAuth2Scope[]; extern const char kGoogleTalkOAuth2Scope[]; +extern const char kGoogleUserInfoEmail[]; +extern const char kGoogleUserInfoProfile[]; // Used with uber auth tokens when needed. extern const char kGaiaSid[]; diff --git a/google_apis/gaia/gaia_oauth_client.cc b/google_apis/gaia/gaia_oauth_client.cc index 1113ff6730921..8e2d29bb12f78 100644 --- a/google_apis/gaia/gaia_oauth_client.cc +++ b/google_apis/gaia/gaia_oauth_client.cc @@ -56,6 +56,9 @@ class GaiaOAuthClient::Core void GetUserId(const std::string& oauth_access_token, int max_retries, Delegate* delegate); + void GetUserInfo(const std::string& oauth_access_token, + int max_retries, + Delegate* delegate); void GetTokenInfo(const std::string& oauth_access_token, int max_retries, Delegate* delegate); @@ -73,13 +76,15 @@ class GaiaOAuthClient::Core TOKEN_INFO, USER_EMAIL, USER_ID, + USER_INFO, }; virtual ~Core() {} - void GetUserInfo(const std::string& oauth_access_token, - int max_retries, - Delegate* delegate); + void GetUserInfoImpl(RequestType type, + const std::string& oauth_access_token, + int max_retries, + Delegate* delegate); void MakeGaiaRequest(const GURL& url, const std::string& post_body, int max_retries, @@ -142,24 +147,29 @@ void GaiaOAuthClient::Core::RefreshToken( void GaiaOAuthClient::Core::GetUserEmail(const std::string& oauth_access_token, int max_retries, Delegate* delegate) { - DCHECK_EQ(request_type_, NO_PENDING_REQUEST); - DCHECK(!request_.get()); - request_type_ = USER_EMAIL; - GetUserInfo(oauth_access_token, max_retries, delegate); + GetUserInfoImpl(USER_EMAIL, oauth_access_token, max_retries, delegate); } void GaiaOAuthClient::Core::GetUserId(const std::string& oauth_access_token, int max_retries, Delegate* delegate) { - DCHECK_EQ(request_type_, NO_PENDING_REQUEST); - DCHECK(!request_.get()); - request_type_ = USER_ID; - GetUserInfo(oauth_access_token, max_retries, delegate); + GetUserInfoImpl(USER_ID, oauth_access_token, max_retries, delegate); } void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token, - int max_retries, - Delegate* delegate) { + int max_retries, + Delegate* delegate) { + GetUserInfoImpl(USER_INFO, oauth_access_token, max_retries, delegate); +} + +void GaiaOAuthClient::Core::GetUserInfoImpl( + RequestType type, + const std::string& oauth_access_token, + int max_retries, + Delegate* delegate) { + DCHECK_EQ(request_type_, NO_PENDING_REQUEST); + DCHECK(!request_.get()); + request_type_ = type; delegate_ = delegate; num_retries_ = 0; request_.reset(net::URLFetcher::Create( @@ -295,6 +305,11 @@ void GaiaOAuthClient::Core::HandleResponse( break; } + case USER_INFO: { + delegate_->OnGetUserInfoResponse(response_dict.Pass()); + break; + } + case TOKEN_INFO: { delegate_->OnGetTokenInfoResponse(response_dict.Pass()); break; @@ -372,6 +387,12 @@ void GaiaOAuthClient::GetUserId(const std::string& access_token, return core_->GetUserId(access_token, max_retries, delegate); } +void GaiaOAuthClient::GetUserInfo(const std::string& access_token, + int max_retries, + Delegate* delegate) { + return core_->GetUserInfo(access_token, max_retries, delegate); +} + void GaiaOAuthClient::GetTokenInfo(const std::string& access_token, int max_retries, Delegate* delegate) { diff --git a/google_apis/gaia/gaia_oauth_client.h b/google_apis/gaia/gaia_oauth_client.h index 8e01ef6a83a3b..68eb7665bcd1b 100644 --- a/google_apis/gaia/gaia_oauth_client.h +++ b/google_apis/gaia/gaia_oauth_client.h @@ -47,6 +47,9 @@ class GaiaOAuthClient { virtual void OnGetUserEmailResponse(const std::string& user_email) {} // Invoked on a successful response to the GetUserId request. virtual void OnGetUserIdResponse(const std::string& user_id) {} + // Invoked on a successful response to the GetUserInfo request. + virtual void OnGetUserInfoResponse( + scoped_ptr user_info) {} // Invoked on a successful response to the GetTokenInfo request. virtual void OnGetTokenInfoResponse( scoped_ptr token_info) {} @@ -104,6 +107,16 @@ class GaiaOAuthClient { int max_retries, Delegate* delegate); + // Call the userinfo API, returning all the user info associated + // with the given access token. The provided access token must have + // https://www.googleapis.com/auth/userinfo.profile in its scopes. If + // email addresses are also to be retrieved, then + // https://www.googleapis.com/auth/userinfo.email must also be specified. + // See |max_retries| docs above. + void GetUserInfo(const std::string& oauth_access_token, + int max_retries, + Delegate* delegate); + // Call the tokeninfo API, returning a dictionary of response values. The // provided access token may have any scope, and basic results will be // returned: issued_to, audience, scope, expires_in, access_type. In diff --git a/google_apis/gaia/gaia_oauth_client_unittest.cc b/google_apis/gaia/gaia_oauth_client_unittest.cc index d4014f736794b..7ca60f36946fb 100644 --- a/google_apis/gaia/gaia_oauth_client_unittest.cc +++ b/google_apis/gaia/gaia_oauth_client_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include "base/json/json_reader.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" #include "google_apis/gaia/gaia_oauth_client.h" @@ -134,19 +135,31 @@ const std::string kTestUserId = "8675309"; const int kTestExpiresIn = 3920; const std::string kDummyGetTokensResult = - "{\"access_token\":\"" + kTestAccessToken + "\"," - "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "," - "\"refresh_token\":\"" + kTestRefreshToken + "\"}"; + "{\"access_token\":\"" + kTestAccessToken + "\"," + "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "," + "\"refresh_token\":\"" + kTestRefreshToken + "\"}"; const std::string kDummyRefreshTokenResult = - "{\"access_token\":\"" + kTestAccessToken + "\"," - "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}"; + "{\"access_token\":\"" + kTestAccessToken + "\"," + "\"expires_in\":" + base::IntToString(kTestExpiresIn) + "}"; const std::string kDummyUserInfoResult = - "{\"email\":\"" + kTestUserEmail + "\"}"; + "{\"email\":\"" + kTestUserEmail + "\"}"; const std::string kDummyUserIdResult = - "{\"id\":\"" + kTestUserId + "\"}"; + "{\"id\":\"" + kTestUserId + "\"}"; + +const std::string kDummyFullUserInfoResult = + "{" + "\"family_name\": \"Bar\", " + "\"name\": \"Foo Bar\", " + "\"picture\": \"https://lh4.googleusercontent.com/hash/photo.jpg\", " + "\"locale\": \"en\", " + "\"gender\": \"male\", " + "\"link\": \"https://plus.google.com/+FooBar\", " + "\"given_name\": \"Foo\", " + "\"id\": \"12345678901234567890\"" + "}"; const std::string kDummyTokenInfoResult = "{\"issued_to\": \"1234567890.apps.googleusercontent.com\"," @@ -198,6 +211,13 @@ class MockGaiaOAuthClientDelegate : public gaia::GaiaOAuthClient::Delegate { // work-around is to create a mock method that takes a raw ptr, and // override the problematic method to call through to it. // https://groups.google.com/a/chromium.org/d/msg/chromium-dev/01sDxsJ1OYw/I_S0xCBRF2oJ + MOCK_METHOD1(OnGetUserInfoResponsePtr, + void(const base::DictionaryValue* user_info)); + virtual void OnGetUserInfoResponse( + scoped_ptr user_info) OVERRIDE { + user_info_.reset(user_info.release()); + OnGetUserInfoResponsePtr(user_info_.get()); + } MOCK_METHOD1(OnGetTokenInfoResponsePtr, void(const base::DictionaryValue* token_info)); virtual void OnGetTokenInfoResponse( @@ -207,6 +227,7 @@ class MockGaiaOAuthClientDelegate : public gaia::GaiaOAuthClient::Delegate { } private: + scoped_ptr user_info_; scoped_ptr token_info_; DISALLOW_COPY_AND_ASSIGN(MockGaiaOAuthClientDelegate); }; @@ -327,6 +348,29 @@ TEST_F(GaiaOAuthClientTest, GetUserId) { auth.GetUserId("access_token", 1, &delegate); } +TEST_F(GaiaOAuthClientTest, GetUserInfo) { + const base::DictionaryValue* captured_result; + + MockGaiaOAuthClientDelegate delegate; + EXPECT_CALL(delegate, OnGetUserInfoResponsePtr(_)) + .WillOnce(SaveArg<0>(&captured_result)); + + MockOAuthFetcherFactory factory; + factory.set_results(kDummyFullUserInfoResult); + + GaiaOAuthClient auth(GetRequestContext()); + auth.GetUserInfo("access_token", 1, &delegate); + + scoped_ptr value( + base::JSONReader::Read(kDummyFullUserInfoResult)); + DCHECK(value); + ASSERT_TRUE(value->IsType(base::Value::TYPE_DICTIONARY)); + base::DictionaryValue* expected_result; + value->GetAsDictionary(&expected_result); + + ASSERT_TRUE(expected_result->Equals(captured_result)); +} + TEST_F(GaiaOAuthClientTest, GetTokenInfo) { const base::DictionaryValue* captured_result; diff --git a/google_apis/gaia/gaia_urls.cc b/google_apis/gaia/gaia_urls.cc index 0b44a911c7434..acb4c19e38a46 100644 --- a/google_apis/gaia/gaia_urls.cc +++ b/google_apis/gaia/gaia_urls.cc @@ -31,6 +31,7 @@ const char kOAuthRevokeTokenUrlSuffix[] = "AuthSubRevokeToken"; const char kListAccountsSuffix[] = "ListAccounts?json=standard"; const char kEmbeddedSigninSuffix[] = "EmbeddedSignIn"; const char kAddAccountSuffix[] = "AddSession"; +const char kGetCheckConnectionInfoSuffix[] = "GetCheckConnectionInfo"; // API calls from accounts.google.com (LSO) const char kGetOAuthTokenUrlSuffix[] = "o/oauth/GetOAuthToken/"; @@ -104,6 +105,8 @@ GaiaUrls::GaiaUrls() { list_accounts_url_ = gaia_url_.Resolve(kListAccountsSuffix); embedded_signin_url_ = gaia_url_.Resolve(kEmbeddedSigninSuffix); add_account_url_ = gaia_url_.Resolve(kAddAccountSuffix); + get_check_connection_info_url_ = + gaia_url_.Resolve(kGetCheckConnectionInfoSuffix); // URLs from accounts.google.com (LSO). get_oauth_token_url_ = lso_origin_url_.Resolve(kGetOAuthTokenUrlSuffix); @@ -203,6 +206,10 @@ const GURL& GaiaUrls::add_account_url() const { return add_account_url_; } +const GURL& GaiaUrls::get_check_connection_info_url() const { + return get_check_connection_info_url_; +} + const std::string& GaiaUrls::oauth2_chrome_client_id() const { return oauth2_chrome_client_id_; } diff --git a/google_apis/gaia/gaia_urls.h b/google_apis/gaia/gaia_urls.h index f96e89ce230fe..70919ab9cd35d 100644 --- a/google_apis/gaia/gaia_urls.h +++ b/google_apis/gaia/gaia_urls.h @@ -35,6 +35,7 @@ class GaiaUrls { const GURL& list_accounts_url() const; const GURL& embedded_signin_url() const; const GURL& add_account_url() const; + const GURL& get_check_connection_info_url() const; const std::string& oauth2_chrome_client_id() const; const std::string& oauth2_chrome_client_secret() const; @@ -76,6 +77,7 @@ class GaiaUrls { GURL list_accounts_url_; GURL embedded_signin_url_; GURL add_account_url_; + GURL get_check_connection_info_url_; std::string oauth2_chrome_client_id_; std::string oauth2_chrome_client_secret_; diff --git a/google_apis/gaia/merge_session_helper.cc b/google_apis/gaia/merge_session_helper.cc index cf2fa95730dd2..9a8f26577e4af 100644 --- a/google_apis/gaia/merge_session_helper.cc +++ b/google_apis/gaia/merge_session_helper.cc @@ -4,19 +4,159 @@ #include "google_apis/gaia/merge_session_helper.h" +#include + +#include "base/json/json_reader.h" +#include "base/stl_util.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/time/time.h" +#include "base/values.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/gaia_urls.h" #include "google_apis/gaia/oauth2_token_service.h" +#include "net/base/load_flags.h" +#include "net/http/http_status_code.h" #include "net/url_request/url_fetcher.h" #include "net/url_request/url_fetcher_delegate.h" +MergeSessionHelper::ExternalCcResultFetcher::ExternalCcResultFetcher( + MergeSessionHelper* helper) : helper_(helper) { + DCHECK(helper_); +} + +MergeSessionHelper::ExternalCcResultFetcher::~ExternalCcResultFetcher() { + CleanupTransientState(); +} + +std::string MergeSessionHelper::ExternalCcResultFetcher::GetExternalCcResult() { + std::vector results; + for (ResultMap::const_iterator it = results_.begin(); it != results_.end(); + ++it) { + results.push_back(it->first + ":" + it->second); + } + return JoinString(results, ","); +} + +void MergeSessionHelper::ExternalCcResultFetcher::Start() { + CleanupTransientState(); + results_.clear(); + gaia_auth_fetcher_.reset( + new GaiaAuthFetcher(this, GaiaConstants::kChromeSource, + helper_->request_context())); + gaia_auth_fetcher_->StartGetCheckConnectionInfo(); +} + +bool MergeSessionHelper::ExternalCcResultFetcher::IsRunning() { + return gaia_auth_fetcher_ || fetchers_.size() > 0u; +} + +void MergeSessionHelper::ExternalCcResultFetcher::TimeoutForTests() { + Timeout(); +} + +void +MergeSessionHelper::ExternalCcResultFetcher::OnGetCheckConnectionInfoSuccess( + const std::string& data) { + base::Value* value = base::JSONReader::Read(data); + const base::ListValue* list; + if (!value || !value->GetAsList(&list)) + return; + + // Start a fetcher for each connection URL that needs to be checked. + for (size_t i = 0; i < list->GetSize(); ++i) { + const base::DictionaryValue* dict; + if (list->GetDictionary(i, &dict)) { + std::string token; + std::string url; + if (dict->GetString("carryBackToken", &token) && + dict->GetString("url", &url)) { + results_[token] = "null"; + net::URLFetcher* fetcher = CreateFetcher(GURL(url)); + fetchers_[fetcher->GetOriginalURL()] = std::make_pair(token, fetcher); + fetcher->Start(); + } + } + } + + // Some fetches may timeout. Start a timer to decide when the result fetcher + // has waited long enough. + // TODO(rogerta): I have no idea how long to wait before timing out. + // Gaia folks say this should take no more than 2 second even in mobile. + // This will need to be tweaked. + timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(5), + this, &MergeSessionHelper::ExternalCcResultFetcher::Timeout); +} + +net::URLFetcher* MergeSessionHelper::ExternalCcResultFetcher::CreateFetcher( + const GURL& url) { + net::URLFetcher* fetcher = net::URLFetcher::Create( + 0, + url, + net::URLFetcher::GET, + this); + fetcher->SetRequestContext(helper_->request_context()); + fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | + net::LOAD_DO_NOT_SAVE_COOKIES); + + // Fetchers are sometimes cancelled because a network change was detected, + // especially at startup and after sign-in on ChromeOS. + fetcher->SetAutomaticallyRetryOnNetworkChanges(1); + return fetcher; +} + +void MergeSessionHelper::ExternalCcResultFetcher::OnURLFetchComplete( + const net::URLFetcher* source) { + const GURL& url = source->GetOriginalURL(); + const net::URLRequestStatus& status = source->GetStatus(); + int response_code = source->GetResponseCode(); + if (status.is_success() && response_code == net::HTTP_OK && + fetchers_.count(url) > 0) { + std::string data; + source->GetResponseAsString(&data); + // Only up to the first 16 characters of the response are important to GAIA. + // Truncate if needed to keep amount data sent back to GAIA down. + if (data.size() > 16) + data.resize(16); + results_[fetchers_[url].first] = data; + + // Clean up tracking of this fetcher. The rest will be cleaned up after + // the timer expires in CleanupTransientState(). + DCHECK_EQ(source, fetchers_[url].second); + fetchers_.erase(url); + delete source; + + // If all expected responses have been received, cancel the timer and + // report the result. + if (fetchers_.empty()) { + timer_.Stop(); + CleanupTransientState(); + } + } +} + +void MergeSessionHelper::ExternalCcResultFetcher::Timeout() { + CleanupTransientState(); +} + +void MergeSessionHelper::ExternalCcResultFetcher::CleanupTransientState() { + gaia_auth_fetcher_.reset(); + + for (URLToTokenAndFetcher::const_iterator it = fetchers_.begin(); + it != fetchers_.end(); ++it) { + delete it->second.second; + } + fetchers_.clear(); +} + MergeSessionHelper::MergeSessionHelper( OAuth2TokenService* token_service, net::URLRequestContextGetter* request_context, Observer* observer) : token_service_(token_service), - request_context_(request_context) { + request_context_(request_context), + result_fetcher_(this) { if (observer) AddObserver(observer); } @@ -111,6 +251,14 @@ void MergeSessionHelper::SignalComplete( MergeSessionCompleted(account_id, error)); } +void MergeSessionHelper::StartFetchingExternalCcResult() { + result_fetcher_.Start(); +} + +bool MergeSessionHelper::StillFetchingExternalCcResult() { + return result_fetcher_.IsRunning(); +} + void MergeSessionHelper::StartLogOutUrlFetch() { DCHECK(accounts_.front().empty()); VLOG(1) << "MergeSessionHelper::StartLogOutUrlFetch"; @@ -127,7 +275,11 @@ void MergeSessionHelper::OnUbertokenSuccess(const std::string& uber_token) { gaia_auth_fetcher_.reset(new GaiaAuthFetcher(this, GaiaConstants::kChromeSource, request_context_)); - gaia_auth_fetcher_->StartMergeSession(uber_token); + + // It's possible that not all external checks have completed. + // GetExternalCcResult() returns results for those that have. + gaia_auth_fetcher_->StartMergeSession(uber_token, + result_fetcher_.GetExternalCcResult()); } void MergeSessionHelper::OnUbertokenFailure( diff --git a/google_apis/gaia/merge_session_helper.h b/google_apis/gaia/merge_session_helper.h index 962eaa5ea6d09..ff91777cfd001 100644 --- a/google_apis/gaia/merge_session_helper.h +++ b/google_apis/gaia/merge_session_helper.h @@ -8,6 +8,7 @@ #include #include "base/observer_list.h" +#include "base/timer/timer.h" #include "google_apis/gaia/gaia_auth_consumer.h" #include "google_apis/gaia/ubertoken_fetcher.h" #include "net/url_request/url_fetcher_delegate.h" @@ -17,6 +18,7 @@ class GoogleServiceAuthError; class OAuth2TokenService; namespace net { +class URLFetcher; class URLRequestContextGetter; } @@ -41,6 +43,65 @@ class MergeSessionHelper : public GaiaAuthConsumer, virtual ~Observer() {} }; + // Class to retrieve the external connection check results from gaia. + // Declared publicly for unit tests. + class ExternalCcResultFetcher : public GaiaAuthConsumer, + public net::URLFetcherDelegate { + public: + // Maps connection URLs, as returned by StartGetCheckConnectionInfo() to + // token and URLFetcher used to fetch the URL. + typedef std::map > + URLToTokenAndFetcher; + + // Maps tokens to the fetched result for that token. + typedef std::map ResultMap; + + ExternalCcResultFetcher(MergeSessionHelper* helper); + virtual ~ExternalCcResultFetcher(); + + // Gets the current value of the external connection check result string. + std::string GetExternalCcResult(); + + // Start fetching the external CC result. If a fetch is already in progress + // it is canceled. + void Start(); + + // Are external URLs still being checked? + bool IsRunning(); + + // Returns a copy of the internal token to fetcher map. + URLToTokenAndFetcher get_fetcher_map_for_testing() { + return fetchers_; + } + + // Simulate a timeout for tests. + void TimeoutForTests(); + + private: + // Overridden from GaiaAuthConsumer. + virtual void OnGetCheckConnectionInfoSuccess( + const std::string& data) OVERRIDE; + + // Creates and initializes a URL fetcher for doing a connection check. + net::URLFetcher* CreateFetcher(const GURL& url); + + // Overridden from URLFetcherDelgate. + virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; + + // Any fetches still ongoing after this call are considered timed out. + void Timeout(); + + void CleanupTransientState(); + + MergeSessionHelper* helper_; + base::OneShotTimer timer_; + scoped_ptr gaia_auth_fetcher_; + URLToTokenAndFetcher fetchers_; + ResultMap results_; + + DISALLOW_COPY_AND_ASSIGN(ExternalCcResultFetcher); + }; + MergeSessionHelper(OAuth2TokenService* token_service, net::URLRequestContextGetter* request_context, Observer* observer); @@ -74,7 +135,17 @@ class MergeSessionHelper : public GaiaAuthConsumer, // Returns true of there are pending log ins or outs. bool is_running() const { return accounts_.size() > 0; } + // Start the process of fetching the external check connection result so that + // its ready when we try to perform a merge session. + void StartFetchingExternalCcResult(); + + // Returns true if the helper is still fetching external check connection + // results. + bool StillFetchingExternalCcResult(); + private: + net::URLRequestContextGetter* request_context() { return request_context_; } + // Overridden from UbertokenConsumer. virtual void OnUbertokenSuccess(const std::string& token) OVERRIDE; virtual void OnUbertokenFailure(const GoogleServiceAuthError& error) OVERRIDE; @@ -104,6 +175,7 @@ class MergeSessionHelper : public GaiaAuthConsumer, net::URLRequestContextGetter* request_context_; scoped_ptr gaia_auth_fetcher_; scoped_ptr uber_token_fetcher_; + ExternalCcResultFetcher result_fetcher_; // A worklist for this class. Accounts names are stored here if // we are pending a signin action for that account. Empty strings diff --git a/google_apis/gaia/merge_session_helper_unittest.cc b/google_apis/gaia/merge_session_helper_unittest.cc index 1fbe8a6898abf..144a7f1d79842 100644 --- a/google_apis/gaia/merge_session_helper_unittest.cc +++ b/google_apis/gaia/merge_session_helper_unittest.cc @@ -98,10 +98,32 @@ class MergeSessionHelperTest : public testing::Test { consumer->OnURLFetchComplete(NULL); } + void SimulateGetCheckConnctionInfoSuccess( + net::TestURLFetcher* fetcher, + const std::string& data) { + fetcher->set_status(net::URLRequestStatus()); + fetcher->set_response_code(200); + fetcher->SetResponseString(data); + fetcher->delegate()->OnURLFetchComplete(fetcher); + } + + void SimulateGetCheckConnctionInfoResult( + net::URLFetcher* fetcher, + const std::string& result) { + net::TestURLFetcher* test_fetcher = + static_cast(fetcher); + test_fetcher->set_status(net::URLRequestStatus()); + test_fetcher->set_response_code(200); + test_fetcher->SetResponseString(result); + test_fetcher->delegate()->OnURLFetchComplete(fetcher); + } + const GoogleServiceAuthError& no_error() { return no_error_; } const GoogleServiceAuthError& error() { return error_; } const GoogleServiceAuthError& canceled() { return canceled_; } + net::TestURLFetcherFactory* factory() { return &factory_; } + private: base::MessageLoop message_loop_; net::TestURLFetcherFactory factory_; @@ -305,3 +327,84 @@ TEST_F(MergeSessionHelperTest, DoubleSignout) { SimulateLogoutSuccess(&helper); SimulateMergeSessionSuccess(&helper, "token1"); } + +TEST_F(MergeSessionHelperTest, ExternalCcResultFetcher) { + InstrumentedMergeSessionHelper helper(token_service(), request_context()); + MergeSessionHelper::ExternalCcResultFetcher result_fetcher(&helper); + result_fetcher.Start(); + + // Simulate a successful completion of GetCheckConnctionInfo. + net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0); + ASSERT_TRUE(NULL != fetcher); + SimulateGetCheckConnctionInfoSuccess(fetcher, + "[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}," + " {\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]"); + + // Simulate responses for the two connection URLs. + MergeSessionHelper::ExternalCcResultFetcher::URLToTokenAndFetcher fetchers = + result_fetcher.get_fetcher_map_for_testing(); + ASSERT_EQ(2u, fetchers.size()); + ASSERT_EQ(1u, fetchers.count(GURL("http://www.yt.com"))); + ASSERT_EQ(1u, fetchers.count(GURL("http://www.bl.com"))); + + ASSERT_EQ("bl:null,yt:null", result_fetcher.GetExternalCcResult()); + SimulateGetCheckConnctionInfoResult( + fetchers[GURL("http://www.yt.com")].second, "yt_result"); + ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult()); + SimulateGetCheckConnctionInfoResult( + fetchers[GURL("http://www.bl.com")].second, "bl_result"); + ASSERT_EQ("bl:bl_result,yt:yt_result", result_fetcher.GetExternalCcResult()); +} + +TEST_F(MergeSessionHelperTest, ExternalCcResultFetcherTimeout) { + InstrumentedMergeSessionHelper helper(token_service(), request_context()); + MergeSessionHelper::ExternalCcResultFetcher result_fetcher(&helper); + result_fetcher.Start(); + + // Simulate a successful completion of GetCheckConnctionInfo. + net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0); + ASSERT_TRUE(NULL != fetcher); + SimulateGetCheckConnctionInfoSuccess(fetcher, + "[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}," + " {\"carryBackToken\": \"bl\", \"url\": \"http://www.bl.com\"}]"); + + MergeSessionHelper::ExternalCcResultFetcher::URLToTokenAndFetcher fetchers = + result_fetcher.get_fetcher_map_for_testing(); + ASSERT_EQ(2u, fetchers.size()); + ASSERT_EQ(1u, fetchers.count(GURL("http://www.yt.com"))); + ASSERT_EQ(1u, fetchers.count(GURL("http://www.bl.com"))); + + // Simulate response only for "yt". + ASSERT_EQ("bl:null,yt:null", result_fetcher.GetExternalCcResult()); + SimulateGetCheckConnctionInfoResult( + fetchers[GURL("http://www.yt.com")].second, "yt_result"); + ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult()); + + // Now timeout. + result_fetcher.TimeoutForTests(); + ASSERT_EQ("bl:null,yt:yt_result", result_fetcher.GetExternalCcResult()); + fetchers = result_fetcher.get_fetcher_map_for_testing(); + ASSERT_EQ(0u, fetchers.size()); +} + +TEST_F(MergeSessionHelperTest, ExternalCcResultFetcherTruncate) { + InstrumentedMergeSessionHelper helper(token_service(), request_context()); + MergeSessionHelper::ExternalCcResultFetcher result_fetcher(&helper); + result_fetcher.Start(); + + // Simulate a successful completion of GetCheckConnctionInfo. + net::TestURLFetcher* fetcher = factory()->GetFetcherByID(0); + ASSERT_TRUE(NULL != fetcher); + SimulateGetCheckConnctionInfoSuccess(fetcher, + "[{\"carryBackToken\": \"yt\", \"url\": \"http://www.yt.com\"}]"); + + MergeSessionHelper::ExternalCcResultFetcher::URLToTokenAndFetcher fetchers = + result_fetcher.get_fetcher_map_for_testing(); + ASSERT_EQ(1u, fetchers.size()); + ASSERT_EQ(1u, fetchers.count(GURL("http://www.yt.com"))); + + // Simulate response for "yt" with a string that is too long. + SimulateGetCheckConnctionInfoResult( + fetchers[GURL("http://www.yt.com")].second, "1234567890123456trunc"); + ASSERT_EQ("yt:1234567890123456", result_fetcher.GetExternalCcResult()); +} diff --git a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc index f4cd4f04f1e46..08e45d2e18b42 100644 --- a/google_apis/gaia/oauth2_access_token_fetcher_impl.cc +++ b/google_apis/gaia/oauth2_access_token_fetcher_impl.cc @@ -183,9 +183,8 @@ void OAuth2AccessTokenFetcherImpl::EndGetAccessToken( case net::HTTP_OK: break; case net::HTTP_FORBIDDEN: - case net::HTTP_INTERNAL_SERVER_ERROR: // HTTP_FORBIDDEN (403) is treated as temporary error, because it may be - // '403 Rate Limit Exeeded.' 500 is always treated as transient. + // '403 Rate Limit Exeeded.' OnGetTokenFailure( GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); return; @@ -212,11 +211,20 @@ void OAuth2AccessTokenFetcherImpl::EndGetAccessToken( : GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_ERROR)); return; } - default: - // The other errors are treated as permanent error. - OnGetTokenFailure(GoogleServiceAuthError( - GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + default: { + if (source->GetResponseCode() >= net::HTTP_INTERNAL_SERVER_ERROR) { + // 5xx is always treated as transient. + OnGetTokenFailure(GoogleServiceAuthError( + GoogleServiceAuthError::SERVICE_UNAVAILABLE)); + } else { + // The other errors are treated as permanent error. + DLOG(ERROR) << "Unexpected persistent error: http_status=" + << source->GetResponseCode(); + OnGetTokenFailure(GoogleServiceAuthError( + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); + } return; + } } // The request was successfully fetched and it returned OK. diff --git a/google_apis/gaia/oauth2_api_call_flow.cc b/google_apis/gaia/oauth2_api_call_flow.cc index 08312ebe5d39c..07f32082817f3 100644 --- a/google_apis/gaia/oauth2_api_call_flow.cc +++ b/google_apis/gaia/oauth2_api_call_flow.cc @@ -90,7 +90,8 @@ void OAuth2ApiCallFlow::EndApiCall(const net::URLFetcher* source) { return; } - if (source->GetResponseCode() != net::HTTP_OK) { + if (source->GetResponseCode() != net::HTTP_OK && + source->GetResponseCode() != net::HTTP_NO_CONTENT) { state_ = ERROR_STATE; ProcessApiCallFailure(source); return; @@ -125,6 +126,10 @@ void OAuth2ApiCallFlow::EndMintAccessToken( } } +std::string OAuth2ApiCallFlow::CreateApiCallBodyContentType() { + return "application/x-www-form-urlencoded"; +} + OAuth2AccessTokenFetcher* OAuth2ApiCallFlow::CreateAccessTokenFetcher() { return new OAuth2AccessTokenFetcherImpl(this, context_, refresh_token_); } @@ -166,7 +171,7 @@ URLFetcher* OAuth2ApiCallFlow::CreateURLFetcher() { result->SetAutomaticallyRetryOnNetworkChanges(3); if (!empty_body) - result->SetUploadData("application/x-www-form-urlencoded", body); + result->SetUploadData(CreateApiCallBodyContentType(), body); return result; } diff --git a/google_apis/gaia/oauth2_api_call_flow.h b/google_apis/gaia/oauth2_api_call_flow.h index b29bc8a13904e..70c9b5131d5c5 100644 --- a/google_apis/gaia/oauth2_api_call_flow.h +++ b/google_apis/gaia/oauth2_api_call_flow.h @@ -66,6 +66,7 @@ class OAuth2ApiCallFlow // Methods to help create HTTP request. virtual GURL CreateApiCallUrl() = 0; virtual std::string CreateApiCallBody() = 0; + virtual std::string CreateApiCallBodyContentType(); // Sub-classes can expose an appropriate observer interface by implementing // these template methods. diff --git a/google_apis/gaia/oauth2_token_service_request.cc b/google_apis/gaia/oauth2_token_service_request.cc index 672015241779a..0c737ad5ce4a0 100644 --- a/google_apis/gaia/oauth2_token_service_request.cc +++ b/google_apis/gaia/oauth2_token_service_request.cc @@ -40,7 +40,8 @@ class OAuth2TokenServiceRequest::Core public: // Note the thread where an instance of Core is constructed is referred to as // the "owner thread" here. - Core(OAuth2TokenServiceRequest* owner, TokenServiceProvider* provider); + Core(OAuth2TokenServiceRequest* owner, + const scoped_refptr& provider); // Starts the core. Must be called on the owner thread. void Start(); @@ -75,12 +76,18 @@ class OAuth2TokenServiceRequest::Core scoped_refptr token_service_task_runner_; OAuth2TokenServiceRequest* owner_; - TokenServiceProvider* provider_; + + // Clear on owner thread. OAuth2TokenServiceRequest promises to clear its + // last reference to TokenServiceProvider on the owner thread so the caller + // can ensure it is destroyed on the owner thread if desired. + scoped_refptr provider_; + DISALLOW_COPY_AND_ASSIGN(Core); }; -OAuth2TokenServiceRequest::Core::Core(OAuth2TokenServiceRequest* owner, - TokenServiceProvider* provider) +OAuth2TokenServiceRequest::Core::Core( + OAuth2TokenServiceRequest* owner, + const scoped_refptr& provider) : owner_(owner), provider_(provider) { DCHECK(owner_); DCHECK(provider_); @@ -149,7 +156,8 @@ class RequestCore : public OAuth2TokenServiceRequest::Core, public OAuth2TokenService::Consumer { public: RequestCore(OAuth2TokenServiceRequest* owner, - OAuth2TokenServiceRequest::TokenServiceProvider* provider, + const scoped_refptr< + OAuth2TokenServiceRequest::TokenServiceProvider>& provider, OAuth2TokenService::Consumer* consumer, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes); @@ -189,7 +197,8 @@ class RequestCore : public OAuth2TokenServiceRequest::Core, RequestCore::RequestCore( OAuth2TokenServiceRequest* owner, - OAuth2TokenServiceRequest::TokenServiceProvider* provider, + const scoped_refptr& + provider, OAuth2TokenService::Consumer* consumer, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) @@ -260,7 +269,8 @@ void RequestCore::InformOwnerOnGetTokenFailure(GoogleServiceAuthError error) { class InvalidateCore : public OAuth2TokenServiceRequest::Core { public: InvalidateCore(OAuth2TokenServiceRequest* owner, - OAuth2TokenServiceRequest::TokenServiceProvider* provider, + const scoped_refptr< + OAuth2TokenServiceRequest::TokenServiceProvider>& provider, const std::string& access_token, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes); @@ -284,7 +294,8 @@ class InvalidateCore : public OAuth2TokenServiceRequest::Core { InvalidateCore::InvalidateCore( OAuth2TokenServiceRequest* owner, - OAuth2TokenServiceRequest::TokenServiceProvider* provider, + const scoped_refptr& + provider, const std::string& access_token, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes) @@ -314,7 +325,7 @@ void InvalidateCore::StopOnTokenServiceThread() { // static scoped_ptr OAuth2TokenServiceRequest::CreateAndStart( - TokenServiceProvider* provider, + const scoped_refptr& provider, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, OAuth2TokenService::Consumer* consumer) { @@ -328,7 +339,7 @@ scoped_ptr OAuth2TokenServiceRequest::CreateAndStart( // static void OAuth2TokenServiceRequest::InvalidateToken( - OAuth2TokenServiceRequest::TokenServiceProvider* provider, + const scoped_refptr& provider, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, const std::string& access_token) { diff --git a/google_apis/gaia/oauth2_token_service_request.h b/google_apis/gaia/oauth2_token_service_request.h index 972e5184b85c8..b1f0e57629d28 100644 --- a/google_apis/gaia/oauth2_token_service_request.h +++ b/google_apis/gaia/oauth2_token_service_request.h @@ -8,6 +8,7 @@ #include #include +#include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/single_thread_task_runner.h" #include "base/threading/non_thread_safe.h" @@ -23,10 +24,16 @@ class OAuth2TokenServiceRequest : public OAuth2TokenService::Request, class Core; // Interface for providing an OAuth2TokenService. - class TokenServiceProvider { + // + // Ref-counted so that OAuth2TokenServiceRequest can ensure this object isn't + // destroyed out from under the token service task runner thread. Because + // OAuth2TokenServiceRequest has a reference, implementations of + // TokenServiceProvider must be capable of being destroyed on the same thread + // on which the OAuth2TokenServiceRequest was created. + class TokenServiceProvider + : public base::RefCountedThreadSafe { public: TokenServiceProvider(); - virtual ~TokenServiceProvider(); // Returns the task runner on which the token service lives. // @@ -42,12 +49,15 @@ class OAuth2TokenServiceRequest : public OAuth2TokenService::Request, // This method may only be called from the task runner returned by // |GetTokenServiceTaskRunner|. virtual OAuth2TokenService* GetTokenService() = 0; + + protected: + friend class base::RefCountedThreadSafe; + virtual ~TokenServiceProvider(); }; // Creates and starts an access token request for |account_id| and |scopes|. // - // |provider| is used to get the OAuth2TokenService and must outlive the - // returned request object. + // |provider| is used to get the OAuth2TokenService. // // |account_id| must not be empty. // @@ -59,23 +69,23 @@ class OAuth2TokenServiceRequest : public OAuth2TokenService::Request, // network activities may not be canceled and the cache in OAuth2TokenService // may be populated with the fetched results. static scoped_ptr CreateAndStart( - TokenServiceProvider* provider, + const scoped_refptr& provider, const std::string& account_id, const OAuth2TokenService::ScopeSet& scopes, OAuth2TokenService::Consumer* consumer); // Invalidates |access_token| for |account_id| and |scopes|. // - // |provider| is used to get the OAuth2TokenService and must outlive the - // returned request object. + // |provider| is used to get the OAuth2TokenService. // // |account_id| must not be empty. // // |scopes| must not be empty. - static void InvalidateToken(TokenServiceProvider* provider, - const std::string& account_id, - const OAuth2TokenService::ScopeSet& scopes, - const std::string& access_token); + static void InvalidateToken( + const scoped_refptr& provider, + const std::string& account_id, + const OAuth2TokenService::ScopeSet& scopes, + const std::string& access_token); virtual ~OAuth2TokenServiceRequest(); diff --git a/google_apis/gaia/oauth2_token_service_request_unittest.cc b/google_apis/gaia/oauth2_token_service_request_unittest.cc index 0a86cfd0e2da9..c2683a83edae6 100644 --- a/google_apis/gaia/oauth2_token_service_request_unittest.cc +++ b/google_apis/gaia/oauth2_token_service_request_unittest.cc @@ -159,6 +159,8 @@ class OAuth2TokenServiceRequestTest : public testing::Test { virtual OAuth2TokenService* GetTokenService() OVERRIDE; private: + virtual ~Provider(); + scoped_refptr task_runner_; OAuth2TokenService* token_service_; }; @@ -166,7 +168,7 @@ class OAuth2TokenServiceRequestTest : public testing::Test { base::MessageLoop ui_loop_; OAuth2TokenService::ScopeSet scopes_; scoped_ptr oauth2_service_; - scoped_ptr provider_; + scoped_refptr provider_; TestingOAuth2TokenServiceConsumer consumer_; }; @@ -174,8 +176,8 @@ void OAuth2TokenServiceRequestTest::SetUp() { scopes_.insert(kScope); oauth2_service_.reset(new MockOAuth2TokenService); oauth2_service_->AddAccount(kAccountId); - provider_.reset( - new Provider(base::MessageLoopProxy::current(), oauth2_service_.get())); + provider_ = + new Provider(base::MessageLoopProxy::current(), oauth2_service_.get()); } void OAuth2TokenServiceRequestTest::TearDown() { @@ -198,6 +200,9 @@ OAuth2TokenService* OAuth2TokenServiceRequestTest::Provider::GetTokenService() { return token_service_; } +OAuth2TokenServiceRequestTest::Provider::~Provider() { +} + TEST_F(OAuth2TokenServiceRequestTest, CreateAndStart_Failure) { oauth2_service_->SetResponse( GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE), diff --git a/google_apis/gcm/BUILD.gn b/google_apis/gcm/BUILD.gn index ee4f90170bd64..491332436d845 100644 --- a/google_apis/gcm/BUILD.gn +++ b/google_apis/gcm/BUILD.gn @@ -95,6 +95,7 @@ executable("mcs_probe") { deps = [ ":gcm", + ":proto", ":test_support", "//base", "//net", @@ -120,6 +121,7 @@ test("gcm_unit_tests") { ] deps = [ + ":proto", ":test_support", "//base", "//base/test:run_all_unittests", diff --git a/google_apis/gcm/engine/connection_factory_impl.cc b/google_apis/gcm/engine/connection_factory_impl.cc index cf90aed89c316..4963f13271095 100644 --- a/google_apis/gcm/engine/connection_factory_impl.cc +++ b/google_apis/gcm/engine/connection_factory_impl.cc @@ -536,7 +536,7 @@ int ConnectionFactoryImpl::ReconsiderProxyAfterError(int error) { void ConnectionFactoryImpl::ReportSuccessfulProxyConnection() { if (gcm_network_session_ && gcm_network_session_->proxy_service()) - gcm_network_session_->proxy_service()->ReportSuccess(proxy_info_); + gcm_network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL); } void ConnectionFactoryImpl::CloseSocket() { diff --git a/google_apis/gcm/engine/gcm_store_impl.cc b/google_apis/gcm/engine/gcm_store_impl.cc index 3088ab31db363..f1f46ee356332 100644 --- a/google_apis/gcm/engine/gcm_store_impl.cc +++ b/google_apis/gcm/engine/gcm_store_impl.cc @@ -358,7 +358,8 @@ void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id, leveldb::WriteOptions write_options; write_options.sync = true; - leveldb::Status status = db_->Delete(write_options, MakeSlice(app_id)); + leveldb::Status status = + db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id))); if (status.ok()) { foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true)); return; diff --git a/google_apis/gcm/engine/gcm_store_impl_unittest.cc b/google_apis/gcm/engine/gcm_store_impl_unittest.cc index de229462558ab..bbe408f40d62d 100644 --- a/google_apis/gcm/engine/gcm_store_impl_unittest.cc +++ b/google_apis/gcm/engine/gcm_store_impl_unittest.cc @@ -260,6 +260,25 @@ TEST_F(GCMStoreImplTest, Registrations) { load_result->registrations["app2"]->sender_ids[0]); EXPECT_EQ(registration2->sender_ids[1], load_result->registrations["app2"]->sender_ids[1]); + + gcm_store->RemoveRegistration( + "app2", + base::Bind(&GCMStoreImplTest::UpdateCallback, base::Unretained(this))); + PumpLoop(); + + gcm_store = BuildGCMStore().Pass(); + gcm_store->Load(base::Bind( + &GCMStoreImplTest::LoadCallback, base::Unretained(this), &load_result)); + PumpLoop(); + + ASSERT_EQ(1u, load_result->registrations.size()); + ASSERT_TRUE(load_result->registrations.find("app1") != + load_result->registrations.end()); + EXPECT_EQ(registration1->registration_id, + load_result->registrations["app1"]->registration_id); + ASSERT_EQ(1u, load_result->registrations["app1"]->sender_ids.size()); + EXPECT_EQ(registration1->sender_ids[0], + load_result->registrations["app1"]->sender_ids[0]); } // Verify saving some incoming messages, reopening the directory, and then diff --git a/gpu/BUILD.gn b/gpu/BUILD.gn index 7c8acc8f63ee8..fc7a4ac489192 100644 --- a/gpu/BUILD.gn +++ b/gpu/BUILD.gn @@ -209,7 +209,6 @@ test("gpu_unittests") { "config/gpu_control_list_entry_unittest.cc", "config/gpu_control_list_number_info_unittest.cc", "config/gpu_control_list_os_info_unittest.cc", - "config/gpu_control_list_string_info_unittest.cc", "config/gpu_control_list_unittest.cc", "config/gpu_control_list_version_info_unittest.cc", "config/gpu_driver_bug_list_unittest.cc", diff --git a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_copy_texture.txt b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_copy_texture.txt index 4431e4938fa5d..fdaa7d64b7b13 100644 --- a/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_copy_texture.txt +++ b/gpu/GLES2/extensions/CHROMIUM/CHROMIUM_copy_texture.txt @@ -47,7 +47,18 @@ New Procedures and Functions destination texture. The level parameter must be 0 at present. The internal format of the destination texture is converted to that - specified by . + specified by . Must be one of the following symbolic + constants: GL_ALPHA, GL_LUMINANCE, GL_LUMINANCE_ALPHA, GL_RGB, GL_RGBA + When texture doens't contain a superset of the component + required by , fill the components by following rules. + + source format color components + ================================================= + GL_ALPHA (0, 0, 0, A) + GL_LUMINANCE (L, L, L, 1) + GL_LUMINANCE_ALPHA (L, L, L, A) + GL_RGB (R, G, B, 1) + GL_RGBA (R, G, B, A) The format type of the destination texture is converted to that specified by . diff --git a/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_get_sync_values.txt b/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_get_sync_values.txt new file mode 100644 index 0000000000000..d95b348c84500 --- /dev/null +++ b/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_get_sync_values.txt @@ -0,0 +1,131 @@ +Name + + CHROMIUM_get_sync_values + +Name Strings + + EGL_CHROMIUM_get_sync_values + +Contact + + Stéphane Marchesin, Google (marcheu 'at' google.com) + +Status + + Draft. + +Version + + Last Modified Date: N/A Revision: 1.0 + + Based on GLX_OML_sync_control Revision 6.0 + +Number + + ??? + +Dependencies + + The extension is written against the EGL 1.2 Specification, although it + should work on other versions of these specifications. This extension + also requires an operating system which supports CLOCK_MONOTONIC. + +Overview + + This extension provides counters which let applications know about the + timing of the last vertical retrace. By looking at the system clock, as + well as the refresh rate of the monitor, this should enable applications + to predict the position of future retraces so as to schedule an optimal + workload. + + This extension incorporates the use of three counters that provide + the necessary synchronization. The Unadjusted System Time (or UST) + is the 64-bit CLOCK_MONOTONIC clock; in particular this lets the + application schedule future vertical retraces by querying this clock. + The graphics Media Stream Counter (or graphics MSC) is a counter + that is unique to the graphics subsystem and increments for each + vertical retrace that occurs. The Swap Buffer Counter (SBC) is an + attribute of an EGLSurface and is incremented each time a swap + buffer action is performed on the associated surface. + + The use of these three counters allows the application to + synchronize graphics rendering to vertical retraces and/or swap + buffer actions. For example, by querying the synchronization values for + a given surface, the application can accurately predict the timing for + the next vertical retraces and schedule rendering accordingly. + +Issues + + None. + +IP Status + + No known issues. + +New Procedures and Functions + + Bool eglGetSyncValuesCHROMIUM(EGLDisplay dpy, + EGLSurface surface, + int64_t* ust, + int64_t* msc, + int64_t* sbc) + + +New Tokens + + None + +Additions to the EGL 1.3 Specification + + eglGetSyncValuesCHROMIUM returns the current UST/MSC/SBC triple. A UST + timestamp is obtained each time the graphics MSC is incremented. + If this value does not reflect the value of the UST at the time the + first scan line of the display begins passing through the video + output port, it will be adjusted by the graphics driver to do so + prior to being returned by any of the functions defined by this + extension. + + This UST timestamp, together with the current graphics MSC and the + current SBC, comprise the current UST/MSC/SBC triple. The UST, + graphics MSC, and SBC values are not part of the render context + state. These values cannot be pushed or popped. The graphics MSC + value is initialized to 0 when the graphics device is initialized. + The SBC is per-surface state and is initialized to 0 when the + EGLSurface data structure is initialized. + + The SBC value is incremented by the graphics driver at the completion + of each buffer swap (e.g., the pixel copy has been completed or the + hardware register that swaps memory banks has been written). For pixel + formats that do not contain a back buffer, the SBC will always be + returned as 0. + + The graphics MSC value is incremented once for each screen refresh. + For a non-interlaced display, this means that the graphics MSC value + is incremented for each frame. For an interlaced display, it means + that it will be incremented for each field. For a multi-monitor + system, the monitor used to determine MSC is the one where the surface + is located. If the surface spans multiple monitors, the monitor used + to determine MSC is the one with the biggest coverage in pixels. + + The function eglGetSyncValuesCHROMIUM will return TRUE if the function + completed successfully, FALSE otherwise. + + Each time eglSwapBuffer succeeds, the SBC will be increased within a + finite time period. + +Errors + + eglGetSyncValuesCHROMIUM will return FALSE if there is no current + EGLContext. + +New State + + Get Value Get Command Type Initial Value + --------- ----------- ---- ------------- + [UST] eglGetSyncValuesCHROMIUM Z unspecified + [MSC] eglGetSyncValuesCHROMIUM Z 0 + [SBC] eglGetSyncValuesCHROMIUM Z 0 + +New Implementation Dependent State + + None diff --git a/gpu/command_buffer/build_gles2_cmd_buffer.py b/gpu/command_buffer/build_gles2_cmd_buffer.py index 1216a7f45ae9e..56f59fcfde1df 100755 --- a/gpu/command_buffer/build_gles2_cmd_buffer.py +++ b/gpu/command_buffer/build_gles2_cmd_buffer.py @@ -425,6 +425,7 @@ 'type': 'GLfloat', 'default': '1.0f', 'range_checks': [{'check': "<= 0.0f", 'test_value': "0.0f"}], + 'nan_check': True, }], }, 'DepthMask': { @@ -1382,7 +1383,7 @@ 'count': 64, # GL_MAILBOX_SIZE_CHROMIUM 'unit_test': False, 'client_test': False, - 'extension': True, + 'extension': "CHROMIUM_texture_mailbox", 'chromium': True, 'trace_level': 1, }, @@ -1393,7 +1394,7 @@ 'data_transfer_methods': ['immediate'], 'unit_test': False, 'client_test': False, - 'extension': True, + 'extension': "CHROMIUM_texture_mailbox", 'chromium': True, }, 'ClearStencil': { @@ -1648,7 +1649,7 @@ 'GenMailboxCHROMIUM': { 'type': 'HandWritten', 'impl_func': False, - 'extension': True, + 'extension': "CHROMIUM_texture_mailbox", 'chromium': True, }, 'GenFramebuffers': { @@ -1976,7 +1977,7 @@ 'count': 64, # GL_MAILBOX_SIZE_CHROMIUM 'unit_test': False, 'client_test': False, - 'extension': True, + 'extension': "CHROMIUM_texture_mailbox", 'chromium': True, 'trace_level': 1, }, @@ -1987,7 +1988,7 @@ 'count': 64, # GL_MAILBOX_SIZE_CHROMIUM 'unit_test': False, 'client_test': False, - 'extension': True, + 'extension': "CHROMIUM_texture_mailbox", 'chromium': True, 'trace_level': 1, }, @@ -2559,13 +2560,13 @@ 'InsertSyncPointCHROMIUM': { 'type': 'HandWritten', 'impl_func': False, - 'extension': True, + 'extension': "CHROMIUM_sync_point", 'chromium': True, }, 'WaitSyncPointCHROMIUM': { 'type': 'Custom', 'impl_func': True, - 'extension': True, + 'extension': "CHROMIUM_sync_point", 'chromium': True, 'trace_level': 1, }, @@ -3324,19 +3325,27 @@ def WriteHandlerImplementation(self, func, file): state = _STATES[state_name] states = state['states'] args = func.GetOriginalArgs() - code = [] for ndx,item in enumerate(states): + code = [] if 'range_checks' in item: for range_check in item['range_checks']: code.append("%s %s" % (args[ndx].name, range_check['check'])) - if len(code): - file.Write(" if (%s) {\n" % " ||\n ".join(code)) - file.Write( - ' LOCAL_SET_GL_ERROR(GL_INVALID_VALUE,' - ' "%s", "%s out of range");\n' % - (func.name, args[ndx].name)) - file.Write(" return error::kNoError;\n") - file.Write(" }\n") + if 'nan_check' in item: + # Drivers might generate an INVALID_VALUE error when a value is set + # to NaN. This is allowed behavior under GLES 3.0 section 2.1.1 or + # OpenGL 4.5 section 2.3.4.1 - providing NaN allows undefined results. + # Make this behavior consistent within Chromium, and avoid leaking GL + # errors by generating the error in the command buffer instead of + # letting the GL driver generate it. + code.append("base::IsNaN(%s)" % args[ndx].name) + if len(code): + file.Write(" if (%s) {\n" % " ||\n ".join(code)) + file.Write( + ' LOCAL_SET_GL_ERROR(GL_INVALID_VALUE,' + ' "%s", "%s out of range");\n' % + (func.name, args[ndx].name)) + file.Write(" return error::kNoError;\n") + file.Write(" }\n") code = [] for ndx,item in enumerate(states): code.append("state_.%s != %s" % (item['name'], args[ndx].name)) @@ -3387,6 +3396,30 @@ def WriteServiceUnitTest(self, func, file): 'args': ", ".join(arg_strings), } file.Write(valid_test % vars) + if 'nan_check' in item: + valid_test = """ +TEST_P(%(test_name)s, %(name)sNaNValue%(ndx)d) { + SpecializedSetup(false); + cmds::%(name)s cmd; + cmd.Init(%(args)s); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); +} +""" + name = func.name + arg_strings = [ + arg.GetValidArg(func) \ + for arg in func.GetOriginalArgs() if not arg.IsConstant() + ] + + arg_strings[ndx] = 'nanf("")' + vars = { + 'test_name': 'GLES2DecoderTest%d' % file.file_num, + 'name': name, + 'ndx': ndx, + 'args': ", ".join(arg_strings), + } + file.Write(valid_test % vars) class StateSetRGBAlphaHandler(TypeHandler): @@ -8225,6 +8258,22 @@ def WriteMojoGLCallVisitor(self, filename): file.Close() + def WriteMojoGLCallVisitorForExtension(self, filename, extension): + """Provides the GL implementation for mojo for a particular extension""" + file = CWriter(filename) + file.Write(_LICENSE) + file.Write(_DO_NOT_EDIT_WARNING) + + for func in self.original_functions: + if func.GetInfo("extension") != extension: + continue + file.Write("VISIT_GL_CALL(%s, %s, (%s), (%s))\n" % + (func.name, func.return_type, + func.MakeTypedOriginalArgString(""), + func.MakeOriginalArgString(""))) + + file.Close() + def Format(generated_files): for filename in generated_files: call(["clang-format", "-i", "-style=chromium", filename]) @@ -8306,8 +8355,14 @@ def main(argv): gen.WriteCommonUtilsHeader("common/gles2_cmd_utils_autogen.h") gen.WriteCommonUtilsImpl("common/gles2_cmd_utils_implementation_autogen.h") gen.WriteGLES2Header("../GLES2/gl2chromium_autogen.h") - gen.WriteMojoGLCallVisitor( - "../../mojo/public/c/gles2/gles2_call_visitor_autogen.h") + mojo_gles2_prefix = "../../mojo/public/c/gles2/gles2_call_visitor" + gen.WriteMojoGLCallVisitor(mojo_gles2_prefix + "_autogen.h") + gen.WriteMojoGLCallVisitorForExtension( + mojo_gles2_prefix + "_chromium_texture_mailbox_autogen.h", + "CHROMIUM_texture_mailbox") + gen.WriteMojoGLCallVisitorForExtension( + mojo_gles2_prefix + "_chromium_sync_point_autogen.h", + "CHROMIUM_sync_point") Format([ "common/gles2_cmd_format_autogen.h", @@ -8337,9 +8392,12 @@ def main(argv): "service/gles2_cmd_validation_autogen.h", "service/gles2_cmd_validation_implementation_autogen.h"]) os.chdir("../..") + mojo_gles2_prefix = "mojo/public/c/gles2/gles2_call_visitor" Format([ "gpu/GLES2/gl2chromium_autogen.h", - "mojo/public/c/gles2/gles2_call_visitor_autogen.h", + mojo_gles2_prefix + "_autogen.h", + mojo_gles2_prefix + "_chromium_texture_mailbox_autogen.h", + mojo_gles2_prefix + "_chromium_sync_point_autogen.h", "ppapi/c/dev/ppb_opengles2ext_dev.h", "ppapi/c/ppb_opengles2.h", "ppapi/lib/gl/gles2/gles2.c", diff --git a/gpu/command_buffer/client/BUILD.gn b/gpu/command_buffer/client/BUILD.gn index bbfe4d0a182cb..d3e9b425900a5 100644 --- a/gpu/command_buffer/client/BUILD.gn +++ b/gpu/command_buffer/client/BUILD.gn @@ -152,6 +152,7 @@ component("gl_in_process_context") { deps = [ ":gles2_implementation", "//gpu", + "//gpu/command_buffer/common:gles2_utils", "//base", "//base/third_party/dynamic_annotations", "//ui/gfx/geometry", diff --git a/gpu/command_buffer/client/gl_in_process_context.cc b/gpu/command_buffer/client/gl_in_process_context.cc index bfcea4ab878ef..a5648dfd6e957 100644 --- a/gpu/command_buffer/client/gl_in_process_context.cc +++ b/gpu/command_buffer/client/gl_in_process_context.cc @@ -37,18 +37,17 @@ namespace gpu { namespace { -const int32 kCommandBufferSize = 1024 * 1024; -// TODO(kbr): make the transfer buffer size configurable via context -// creation attributes. -const size_t kStartTransferBufferSize = 4 * 1024 * 1024; -const size_t kMinTransferBufferSize = 1 * 256 * 1024; -const size_t kMaxTransferBufferSize = 16 * 1024 * 1024; +const int32 kDefaultCommandBufferSize = 1024 * 1024; +const unsigned int kDefaultStartTransferBufferSize = 4 * 1024 * 1024; +const unsigned int kDefaultMinTransferBufferSize = 1 * 256 * 1024; +const unsigned int kDefaultMaxTransferBufferSize = 16 * 1024 * 1024; class GLInProcessContextImpl : public GLInProcessContext, public base::SupportsWeakPtr { public: - explicit GLInProcessContextImpl(); + explicit GLInProcessContextImpl( + const GLInProcessContextSharedMemoryLimits& mem_limits); virtual ~GLInProcessContextImpl(); bool Initialize( @@ -58,13 +57,14 @@ class GLInProcessContextImpl GLInProcessContext* share_context, gfx::AcceleratedWidget window, const gfx::Size& size, - const GLInProcessContextAttribs& attribs, + const gpu::gles2::ContextCreationAttribHelper& attribs, gfx::GpuPreference gpu_preference, const scoped_refptr& service); // GLInProcessContext implementation: virtual void SetContextLostCallback(const base::Closure& callback) OVERRIDE; virtual gles2::GLES2Implementation* GetImplementation() OVERRIDE; + virtual size_t GetMappedMemoryLimit() OVERRIDE; #if defined(OS_ANDROID) virtual scoped_refptr GetSurfaceTexture( @@ -81,6 +81,7 @@ class GLInProcessContextImpl scoped_ptr gles2_implementation_; scoped_ptr command_buffer_; + const GLInProcessContextSharedMemoryLimits mem_limits_; bool context_lost_; base::Closure context_lost_callback_; @@ -92,8 +93,10 @@ base::LazyInstance g_all_shared_contexts_lock = base::LazyInstance > g_all_shared_contexts = LAZY_INSTANCE_INITIALIZER; -GLInProcessContextImpl::GLInProcessContextImpl() - : context_lost_(false) {} +GLInProcessContextImpl::GLInProcessContextImpl( + const GLInProcessContextSharedMemoryLimits& mem_limits) + : mem_limits_(mem_limits), context_lost_(false) { +} GLInProcessContextImpl::~GLInProcessContextImpl() { { @@ -107,6 +110,10 @@ gles2::GLES2Implementation* GLInProcessContextImpl::GetImplementation() { return gles2_implementation_.get(); } +size_t GLInProcessContextImpl::GetMappedMemoryLimit() { + return mem_limits_.mapped_memory_reclaim_limit; +} + void GLInProcessContextImpl::SetContextLostCallback( const base::Closure& callback) { context_lost_callback_ = callback; @@ -126,71 +133,14 @@ bool GLInProcessContextImpl::Initialize( GLInProcessContext* share_context, gfx::AcceleratedWidget window, const gfx::Size& size, - const GLInProcessContextAttribs& attribs, + const gles2::ContextCreationAttribHelper& attribs, gfx::GpuPreference gpu_preference, const scoped_refptr& service) { DCHECK(!use_global_share_group || !share_context); DCHECK(size.width() >= 0 && size.height() >= 0); - // Changes to these values should also be copied to - // gpu/command_buffer/client/gl_in_process_context.cc and to - // content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h - const int32 ALPHA_SIZE = 0x3021; - const int32 BLUE_SIZE = 0x3022; - const int32 GREEN_SIZE = 0x3023; - const int32 RED_SIZE = 0x3024; - const int32 DEPTH_SIZE = 0x3025; - const int32 STENCIL_SIZE = 0x3026; - const int32 SAMPLES = 0x3031; - const int32 SAMPLE_BUFFERS = 0x3032; - const int32 NONE = 0x3038; - - // Chromium-specific attributes - const int32 FAIL_IF_MAJOR_PERF_CAVEAT = 0x10002; - const int32 LOSE_CONTEXT_WHEN_OUT_OF_MEMORY = 0x10003; - std::vector attrib_vector; - if (attribs.alpha_size >= 0) { - attrib_vector.push_back(ALPHA_SIZE); - attrib_vector.push_back(attribs.alpha_size); - } - if (attribs.blue_size >= 0) { - attrib_vector.push_back(BLUE_SIZE); - attrib_vector.push_back(attribs.blue_size); - } - if (attribs.green_size >= 0) { - attrib_vector.push_back(GREEN_SIZE); - attrib_vector.push_back(attribs.green_size); - } - if (attribs.red_size >= 0) { - attrib_vector.push_back(RED_SIZE); - attrib_vector.push_back(attribs.red_size); - } - if (attribs.depth_size >= 0) { - attrib_vector.push_back(DEPTH_SIZE); - attrib_vector.push_back(attribs.depth_size); - } - if (attribs.stencil_size >= 0) { - attrib_vector.push_back(STENCIL_SIZE); - attrib_vector.push_back(attribs.stencil_size); - } - if (attribs.samples >= 0) { - attrib_vector.push_back(SAMPLES); - attrib_vector.push_back(attribs.samples); - } - if (attribs.sample_buffers >= 0) { - attrib_vector.push_back(SAMPLE_BUFFERS); - attrib_vector.push_back(attribs.sample_buffers); - } - if (attribs.fail_if_major_perf_caveat > 0) { - attrib_vector.push_back(FAIL_IF_MAJOR_PERF_CAVEAT); - attrib_vector.push_back(attribs.fail_if_major_perf_caveat); - } - if (attribs.lose_context_when_out_of_memory > 0) { - attrib_vector.push_back(LOSE_CONTEXT_WHEN_OUT_OF_MEMORY); - attrib_vector.push_back(attribs.lose_context_when_out_of_memory); - } - attrib_vector.push_back(NONE); + attribs.Serialize(&attrib_vector); base::Closure wrapped_callback = base::Bind(&GLInProcessContextImpl::OnContextLost, AsWeakPtr()); @@ -238,7 +188,7 @@ bool GLInProcessContextImpl::Initialize( // Create the GLES2 helper, which writes the command buffer protocol. gles2_helper_.reset(new gles2::GLES2CmdHelper(command_buffer_.get())); - if (!gles2_helper_->Initialize(kCommandBufferSize)) { + if (!gles2_helper_->Initialize(mem_limits_.command_buffer_size)) { LOG(ERROR) << "Failed to initialize GLES2CmdHelper"; Destroy(); return false; @@ -247,15 +197,17 @@ bool GLInProcessContextImpl::Initialize( // Create a transfer buffer. transfer_buffer_.reset(new TransferBuffer(gles2_helper_.get())); - bool bind_generates_resources = false; + // Check for consistency. + DCHECK(!attribs.bind_generates_resource); + bool bind_generates_resource = false; // Create the object exposing the OpenGL API. gles2_implementation_.reset(new gles2::GLES2Implementation( gles2_helper_.get(), share_group, transfer_buffer_.get(), - bind_generates_resources, - attribs.lose_context_when_out_of_memory > 0, + bind_generates_resource, + attribs.lose_context_when_out_of_memory, command_buffer_.get())); if (use_global_share_group) { @@ -264,10 +216,10 @@ bool GLInProcessContextImpl::Initialize( } if (!gles2_implementation_->Initialize( - kStartTransferBufferSize, - kMinTransferBufferSize, - kMaxTransferBufferSize, - gles2::GLES2Implementation::kNoLimit)) { + mem_limits_.start_transfer_buffer_size, + mem_limits_.min_transfer_buffer_size, + mem_limits_.max_transfer_buffer_size, + mem_limits_.mapped_memory_reclaim_limit)) { return false; } @@ -300,18 +252,15 @@ GLInProcessContextImpl::GetSurfaceTexture(uint32 stream_id) { } // anonymous namespace -GLInProcessContextAttribs::GLInProcessContextAttribs() - : alpha_size(-1), - blue_size(-1), - green_size(-1), - red_size(-1), - depth_size(-1), - stencil_size(-1), - samples(-1), - sample_buffers(-1), - fail_if_major_perf_caveat(-1), - lose_context_when_out_of_memory(-1) {} +GLInProcessContextSharedMemoryLimits::GLInProcessContextSharedMemoryLimits() + : command_buffer_size(kDefaultCommandBufferSize), + start_transfer_buffer_size(kDefaultStartTransferBufferSize), + min_transfer_buffer_size(kDefaultMinTransferBufferSize), + max_transfer_buffer_size(kDefaultMaxTransferBufferSize), + mapped_memory_reclaim_limit(gles2::GLES2Implementation::kNoLimit) { +} +// static GLInProcessContext* GLInProcessContext::Create( scoped_refptr service, scoped_refptr surface, @@ -320,8 +269,9 @@ GLInProcessContext* GLInProcessContext::Create( const gfx::Size& size, GLInProcessContext* share_context, bool use_global_share_group, - const GLInProcessContextAttribs& attribs, - gfx::GpuPreference gpu_preference) { + const ::gpu::gles2::ContextCreationAttribHelper& attribs, + gfx::GpuPreference gpu_preference, + const GLInProcessContextSharedMemoryLimits& memory_limits) { DCHECK(!use_global_share_group || !share_context); if (surface.get()) { DCHECK_EQ(surface->IsOffscreen(), is_offscreen); @@ -329,7 +279,8 @@ GLInProcessContext* GLInProcessContext::Create( DCHECK_EQ(gfx::kNullAcceleratedWidget, window); } - scoped_ptr context(new GLInProcessContextImpl()); + scoped_ptr context( + new GLInProcessContextImpl(memory_limits)); if (!context->Initialize(surface, is_offscreen, use_global_share_group, diff --git a/gpu/command_buffer/client/gl_in_process_context.h b/gpu/command_buffer/client/gl_in_process_context.h index 6c3a485972b60..33b1348967932 100644 --- a/gpu/command_buffer/client/gl_in_process_context.h +++ b/gpu/command_buffer/client/gl_in_process_context.h @@ -8,6 +8,7 @@ #include "base/callback.h" #include "base/compiler_specific.h" #include "gl_in_process_context_export.h" +#include "gpu/command_buffer/common/gles2_cmd_utils.h" #include "gpu/command_buffer/service/in_process_command_buffer.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/gl_surface.h" @@ -29,20 +30,14 @@ namespace gles2 { class GLES2Implementation; } -// The default uninitialized value is -1. -struct GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContextAttribs { - GLInProcessContextAttribs(); - - int32 alpha_size; - int32 blue_size; - int32 green_size; - int32 red_size; - int32 depth_size; - int32 stencil_size; - int32 samples; - int32 sample_buffers; - int32 fail_if_major_perf_caveat; - int32 lose_context_when_out_of_memory; +struct GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContextSharedMemoryLimits { + GLInProcessContextSharedMemoryLimits(); + + int32 command_buffer_size; + unsigned int start_transfer_buffer_size; + unsigned int min_transfer_buffer_size; + unsigned int max_transfer_buffer_size; + unsigned int mapped_memory_reclaim_limit; }; class GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContext { @@ -67,8 +62,9 @@ class GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContext { const gfx::Size& size, GLInProcessContext* share_context, bool use_global_share_group, - const GLInProcessContextAttribs& attribs, - gfx::GpuPreference gpu_preference); + const gpu::gles2::ContextCreationAttribHelper& attribs, + gfx::GpuPreference gpu_preference, + const GLInProcessContextSharedMemoryLimits& memory_limits); virtual void SetContextLostCallback(const base::Closure& callback) = 0; @@ -76,6 +72,8 @@ class GL_IN_PROCESS_CONTEXT_EXPORT GLInProcessContext { // can be used without making it current. virtual gles2::GLES2Implementation* GetImplementation() = 0; + virtual size_t GetMappedMemoryLimit() = 0; + #if defined(OS_ANDROID) virtual scoped_refptr GetSurfaceTexture( uint32 stream_id) = 0; diff --git a/gpu/command_buffer/client/gles2_implementation.cc b/gpu/command_buffer/client/gles2_implementation.cc index cb92e395e97c7..974fd7541a844 100644 --- a/gpu/command_buffer/client/gles2_implementation.cc +++ b/gpu/command_buffer/client/gles2_implementation.cc @@ -135,6 +135,7 @@ GLES2Implementation::GLES2Implementation( share_group_ = (share_group ? share_group : new ShareGroup(bind_generates_resource)); + DCHECK(share_group_->bind_generates_resource() == bind_generates_resource); memset(&reserved_ids_, 0, sizeof(reserved_ids_)); } diff --git a/gpu/command_buffer/client/gles2_implementation_unittest.cc b/gpu/command_buffer/client/gles2_implementation_unittest.cc index a318ab8f33f2a..80a2e41613945 100644 --- a/gpu/command_buffer/client/gles2_implementation_unittest.cc +++ b/gpu/command_buffer/client/gles2_implementation_unittest.cc @@ -393,7 +393,8 @@ class GLES2ImplementationTest : public testing::Test { TestContext() : commands_(NULL), token_(0) {} bool Initialize(ShareGroup* share_group, - bool bind_generates_resource, + bool bind_generates_resource_client, + bool bind_generates_resource_service, bool lose_context_when_out_of_memory) { command_buffer_.reset(new StrictMock()); if (!command_buffer_->Initialize()) @@ -428,7 +429,7 @@ class GLES2ImplementationTest : public testing::Test { int_state.num_compressed_texture_formats = kNumCompressedTextureFormats; int_state.num_shader_binary_formats = kNumShaderBinaryFormats; int_state.bind_generates_resource_chromium = - bind_generates_resource ? 1 : 0; + bind_generates_resource_service ? 1 : 0; // This just happens to work for now because IntState has 1 GLint per // state. @@ -449,7 +450,7 @@ class GLES2ImplementationTest : public testing::Test { gl_.reset(new GLES2Implementation(helper_.get(), share_group, transfer_buffer_.get(), - bind_generates_resource, + bind_generates_resource_client, lose_context_when_out_of_memory, gpu_control_.get())); @@ -537,12 +538,13 @@ class GLES2ImplementationTest : public testing::Test { bool Initialize(const ContextInitOptions& init_options) { bool success = true; - share_group_ = new ShareGroup(init_options.bind_generates_resource_service); + share_group_ = new ShareGroup(init_options.bind_generates_resource_client); for (int i = 0; i < kNumTestContexts; i++) { if (!test_contexts_[i].Initialize( share_group_.get(), init_options.bind_generates_resource_client, + init_options.bind_generates_resource_service, init_options.lose_context_when_out_of_memory)) success = false; } diff --git a/gpu/command_buffer/client/share_group.h b/gpu/command_buffer/client/share_group.h index 341dd58683922..c66704b7fd673 100644 --- a/gpu/command_buffer/client/share_group.h +++ b/gpu/command_buffer/client/share_group.h @@ -71,8 +71,6 @@ class GLES2_IMPL_EXPORT ShareGroup return bind_generates_resource_; } - bool Initialize(); - IdHandlerInterface* GetIdHandler(int namespace_id) const { return id_handlers_[namespace_id].get(); } diff --git a/gpu/command_buffer/common/gles2_cmd_utils.cc b/gpu/command_buffer/common/gles2_cmd_utils.cc index 37d10447e3d38..3b5097cd43835 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.cc +++ b/gpu/command_buffer/common/gles2_cmd_utils.cc @@ -784,6 +784,10 @@ bool GLES2Util::ParseUniformName( namespace { +// WebGraphicsContext3DCommandBufferImpl configuration attributes. Those in +// the 16-bit range are the same as used by EGL. Those outside the 16-bit range +// are unique to Chromium. Attributes are matched using a closest fit algorithm. + // From . const int32 kAlphaSize = 0x3021; // EGL_ALPHA_SIZE const int32 kBlueSize = 0x3022; // EGL_BLUE_SIZE @@ -799,71 +803,67 @@ const int32 kBufferPreserved = 0x3094; // EGL_BUFFER_PRESERVED const int32 kBufferDestroyed = 0x3095; // EGL_BUFFER_DESTROYED // Chromium only. -const int32 kShareResources = 0x10000; -const int32 kBindGeneratesResource = 0x10001; -const int32 kFailIfMajorPerfCaveat = 0x10002; -const int32 kLoseContextWhenOutOfMemory = 0x10003; +const int32 kBindGeneratesResource = 0x10000; +const int32 kFailIfMajorPerfCaveat = 0x10001; +const int32 kLoseContextWhenOutOfMemory = 0x10002; } // namespace ContextCreationAttribHelper::ContextCreationAttribHelper() - : alpha_size_(-1), - blue_size_(-1), - green_size_(-1), - red_size_(-1), - depth_size_(-1), - stencil_size_(-1), - samples_(-1), - sample_buffers_(-1), - buffer_preserved_(true), - share_resources_(false), - bind_generates_resource_(true), - fail_if_major_perf_caveat_(false), - lose_context_when_out_of_memory_(false) {} - -void ContextCreationAttribHelper::Serialize(std::vector* attribs) { - if (alpha_size_ != -1) { + : alpha_size(-1), + blue_size(-1), + green_size(-1), + red_size(-1), + depth_size(-1), + stencil_size(-1), + samples(-1), + sample_buffers(-1), + buffer_preserved(true), + bind_generates_resource(true), + fail_if_major_perf_caveat(false), + lose_context_when_out_of_memory(false) {} + +void ContextCreationAttribHelper::Serialize(std::vector* attribs) const { + if (alpha_size != -1) { attribs->push_back(kAlphaSize); - attribs->push_back(alpha_size_); + attribs->push_back(alpha_size); } - if (blue_size_ != -1) { + if (blue_size != -1) { attribs->push_back(kBlueSize); - attribs->push_back(blue_size_); + attribs->push_back(blue_size); } - if (green_size_ != -1) { + if (green_size != -1) { attribs->push_back(kGreenSize); - attribs->push_back(green_size_); + attribs->push_back(green_size); } - if (red_size_ != -1) { + if (red_size != -1) { attribs->push_back(kRedSize); - attribs->push_back(red_size_); + attribs->push_back(red_size); } - if (depth_size_ != -1) { + if (depth_size != -1) { attribs->push_back(kDepthSize); - attribs->push_back(depth_size_); + attribs->push_back(depth_size); } - if (stencil_size_ != -1) { + if (stencil_size != -1) { attribs->push_back(kStencilSize); - attribs->push_back(stencil_size_); + attribs->push_back(stencil_size); } - if (samples_ != -1) { + if (samples != -1) { attribs->push_back(kSamples); - attribs->push_back(samples_); + attribs->push_back(samples); } - if (sample_buffers_ != -1) { + if (sample_buffers != -1) { attribs->push_back(kSampleBuffers); - attribs->push_back(sample_buffers_); + attribs->push_back(sample_buffers); } attribs->push_back(kSwapBehavior); - attribs->push_back(buffer_preserved_ ? kBufferPreserved : kBufferDestroyed); - attribs->push_back(kShareResources); - attribs->push_back(share_resources_ ? 1 : 0); + attribs->push_back(buffer_preserved ? kBufferPreserved : kBufferDestroyed); attribs->push_back(kBindGeneratesResource); - attribs->push_back(bind_generates_resource_ ? 1 : 0); + attribs->push_back(bind_generates_resource ? 1 : 0); attribs->push_back(kFailIfMajorPerfCaveat); - attribs->push_back(fail_if_major_perf_caveat_ ? 1 : 0); + attribs->push_back(fail_if_major_perf_caveat ? 1 : 0); attribs->push_back(kLoseContextWhenOutOfMemory); - attribs->push_back(lose_context_when_out_of_memory_ ? 1 : 0); + attribs->push_back(lose_context_when_out_of_memory ? 1 : 0); attribs->push_back(kNone); } @@ -883,43 +883,40 @@ bool ContextCreationAttribHelper::Parse(const std::vector& attribs) { const int32 value = attribs[i+1]; switch (attrib) { case kAlphaSize: - alpha_size_ = value; + alpha_size = value; break; case kBlueSize: - blue_size_ = value; + blue_size = value; break; case kGreenSize: - green_size_ = value; + green_size = value; break; case kRedSize: - red_size_ = value; + red_size = value; break; case kDepthSize: - depth_size_ = value; + depth_size = value; break; case kStencilSize: - stencil_size_ = value; + stencil_size = value; break; case kSamples: - samples_ = value; + samples = value; break; case kSampleBuffers: - sample_buffers_ = value; + sample_buffers = value; break; case kSwapBehavior: - buffer_preserved_ = value == kBufferPreserved; - break; - case kShareResources: - share_resources_ = value != 0; + buffer_preserved = value == kBufferPreserved; break; case kBindGeneratesResource: - bind_generates_resource_ = value != 0; + bind_generates_resource = value != 0; break; case kFailIfMajorPerfCaveat: - fail_if_major_perf_caveat_ = value != 0; + fail_if_major_perf_caveat = value != 0; break; case kLoseContextWhenOutOfMemory: - lose_context_when_out_of_memory_ = value != 0; + lose_context_when_out_of_memory = value != 0; break; case kNone: // Terminate list, even if more attributes. diff --git a/gpu/command_buffer/common/gles2_cmd_utils.h b/gpu/command_buffer/common/gles2_cmd_utils.h index 15f3b57e0a551..163ffc008c206 100644 --- a/gpu/command_buffer/common/gles2_cmd_utils.h +++ b/gpu/command_buffer/common/gles2_cmd_utils.h @@ -196,27 +196,25 @@ class GLES2_UTILS_EXPORT GLES2Util { int num_shader_binary_formats_; }; -class GLES2_UTILS_EXPORT ContextCreationAttribHelper { - public: +struct GLES2_UTILS_EXPORT ContextCreationAttribHelper { ContextCreationAttribHelper(); - void Serialize(std::vector* attribs); + void Serialize(std::vector* attribs) const; bool Parse(const std::vector& attribs); // -1 if invalid or unspecified. - int32_t alpha_size_; - int32_t blue_size_; - int32_t green_size_; - int32_t red_size_; - int32_t depth_size_; - int32_t stencil_size_; - int32_t samples_; - int32_t sample_buffers_; - bool buffer_preserved_; - bool share_resources_; - bool bind_generates_resource_; - bool fail_if_major_perf_caveat_; - bool lose_context_when_out_of_memory_; + int32_t alpha_size; + int32_t blue_size; + int32_t green_size; + int32_t red_size; + int32_t depth_size; + int32_t stencil_size; + int32_t samples; + int32_t sample_buffers; + bool buffer_preserved; + bool bind_generates_resource; + bool fail_if_major_perf_caveat; + bool lose_context_when_out_of_memory; }; } // namespace gles2 diff --git a/gpu/command_buffer/service/async_pixel_transfer_manager_android.cc b/gpu/command_buffer/service/async_pixel_transfer_manager_android.cc index 618bd0e953810..eadc34f07a196 100644 --- a/gpu/command_buffer/service/async_pixel_transfer_manager_android.cc +++ b/gpu/command_buffer/service/async_pixel_transfer_manager_android.cc @@ -10,33 +10,55 @@ #include "gpu/command_buffer/service/async_pixel_transfer_manager_idle.h" #include "gpu/command_buffer/service/async_pixel_transfer_manager_stub.h" #include "gpu/command_buffer/service/async_pixel_transfer_manager_sync.h" -#include "gpu/command_buffer/service/mailbox_synchronizer.h" #include "ui/gl/gl_context.h" #include "ui/gl/gl_implementation.h" namespace gpu { namespace { -bool IsBroadcom() { - const char* vendor = reinterpret_cast(glGetString(GL_VENDOR)); - if (vendor) - return std::string(vendor).find("Broadcom") != std::string::npos; - return false; +enum GpuType { + GPU_BROADCOM, + GPU_IMAGINATION, + GPU_NVIDIA_ES31, + GPU_ADRENO_420, + GPU_OTHER, +}; + +std::string MakeString(const char* s) { + return std::string(s ? s : ""); } -bool IsImagination() { - const char* vendor = reinterpret_cast(glGetString(GL_VENDOR)); - if (vendor) - return std::string(vendor).find("Imagination") != std::string::npos; - return false; +GpuType GetGpuType() { + const std::string vendor = MakeString( + reinterpret_cast(glGetString(GL_VENDOR))); + const std::string renderer = MakeString( + reinterpret_cast(glGetString(GL_RENDERER))); + const std::string version = MakeString( + reinterpret_cast(glGetString(GL_VERSION))); + + if (vendor.find("Broadcom") != std::string::npos) + return GPU_BROADCOM; + + if (vendor.find("Imagination") != std::string::npos) + return GPU_IMAGINATION; + + if (vendor.find("NVIDIA") != std::string::npos && + version.find("OpenGL ES 3.1") != std::string::npos) { + return GPU_NVIDIA_ES31; + } + + if (vendor.find("Qualcomm") != std::string::npos && + renderer.find("Adreno (TM) 420") != std::string::npos) { + return GPU_ADRENO_420; + } + + return GPU_OTHER; } -bool IsNvidia31() { - const char* vendor = reinterpret_cast(glGetString(GL_VENDOR)); - const char* version = reinterpret_cast(glGetString(GL_VERSION)); - return vendor && version && - std::string(vendor).find("NVIDIA") != std::string::npos && - std::string(version).find("OpenGL ES 3.1") != std::string::npos; +bool AllowTransferThreadForGpu() { + GpuType gpu = GetGpuType(); + return gpu != GPU_BROADCOM && gpu != GPU_IMAGINATION && + gpu != GPU_NVIDIA_ES31 && gpu != GPU_ADRENO_420; } } @@ -52,25 +74,25 @@ bool IsNvidia31() { // resolution of crbug.com/271929 AsyncPixelTransferManager* AsyncPixelTransferManager::Create( gfx::GLContext* context) { - TRACE_EVENT0("gpu", "AsyncPixelTransferManager::Create"); + DCHECK(context->IsCurrent(NULL)); switch (gfx::GetGLImplementation()) { case gfx::kGLImplementationEGLGLES2: DCHECK(context); - if (context->HasExtension("EGL_KHR_fence_sync") && + if (!base::SysInfo::IsLowEndDevice() && + context->HasExtension("EGL_KHR_fence_sync") && context->HasExtension("EGL_KHR_image") && context->HasExtension("EGL_KHR_image_base") && context->HasExtension("EGL_KHR_gl_texture_2D_image") && context->HasExtension("GL_OES_EGL_image") && - !IsBroadcom() && - !IsImagination() && - !IsNvidia31() && - !base::SysInfo::IsLowEndDevice() && - !gles2::MailboxSynchronizer::GetInstance()) { + AllowTransferThreadForGpu()) { + TRACE_EVENT0("gpu", "AsyncPixelTransferManager_CreateWithThread"); return new AsyncPixelTransferManagerEGL; } return new AsyncPixelTransferManagerIdle; - case gfx::kGLImplementationOSMesaGL: + case gfx::kGLImplementationOSMesaGL: { + TRACE_EVENT0("gpu", "AsyncPixelTransferManager_CreateIdle"); return new AsyncPixelTransferManagerIdle; + } case gfx::kGLImplementationMockGL: return new AsyncPixelTransferManagerStub; default: diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc index bda65ac6a6057..f98ca2e242400 100644 --- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc +++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.cc @@ -167,7 +167,7 @@ FragmentShaderId GetFragmentShaderId(bool premultiply_alpha, } NOTREACHED(); - return shader_ids[index][SAMPLER_2D]; + return shader_ids[0][SAMPLER_2D]; } void CompileShader(GLuint shader, const char* shader_source) { @@ -186,6 +186,68 @@ void DeleteShader(GLuint shader) { glDeleteShader(shader); } +bool BindFramebufferTexture2D(GLenum target, + GLuint texture_id, + GLint level, + GLuint framebuffer) { + DCHECK(target == GL_TEXTURE_2D || target == GL_TEXTURE_RECTANGLE_ARB); + glActiveTexture(GL_TEXTURE0); + glBindTexture(target, texture_id); + // NVidia drivers require texture settings to be a certain way + // or they won't report FRAMEBUFFER_COMPLETE. + glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer); + glFramebufferTexture2DEXT( + GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, texture_id, level); + +#ifndef NDEBUG + GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); + if (GL_FRAMEBUFFER_COMPLETE != fb_status) { + DLOG(ERROR) << "CopyTextureCHROMIUM: Incomplete framebuffer."; + return false; + } +#endif + return true; +} + +void DoCopyTexImage2D(const gpu::gles2::GLES2Decoder* decoder, + GLenum source_target, + GLuint source_id, + GLuint dest_id, + GLint dest_level, + GLenum dest_internal_format, + GLsizei width, + GLsizei height, + GLuint framebuffer) { + DCHECK(source_target == GL_TEXTURE_2D || + source_target == GL_TEXTURE_RECTANGLE_ARB); + if (BindFramebufferTexture2D( + source_target, source_id, 0 /* level */, framebuffer)) { + glBindTexture(GL_TEXTURE_2D, dest_id); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glCopyTexImage2D(GL_TEXTURE_2D, + dest_level, + dest_internal_format, + 0 /* x */, + 0 /* y */, + width, + height, + 0 /* border */); + } + + decoder->RestoreTextureState(source_id); + decoder->RestoreTextureState(dest_id); + decoder->RestoreTextureUnitBindings(0); + decoder->RestoreActiveTexture(); + decoder->RestoreFramebufferBindings(); +} + } // namespace namespace gpu { @@ -197,13 +259,19 @@ CopyTextureCHROMIUMResourceManager::CopyTextureCHROMIUMResourceManager() buffer_id_(0u), framebuffer_(0u) {} -CopyTextureCHROMIUMResourceManager::~CopyTextureCHROMIUMResourceManager() {} +CopyTextureCHROMIUMResourceManager::~CopyTextureCHROMIUMResourceManager() { + DCHECK(!buffer_id_); + DCHECK(!framebuffer_); +} void CopyTextureCHROMIUMResourceManager::Initialize( const gles2::GLES2Decoder* decoder) { COMPILE_ASSERT( kVertexPositionAttrib == 0u, Position_attribs_must_be_0); + DCHECK(!buffer_id_); + DCHECK(!framebuffer_); + DCHECK(programs_.empty()); // Initialize all of the GPU resources required to perform the copy. glGenBuffersARB(1, &buffer_id_); @@ -227,6 +295,7 @@ void CopyTextureCHROMIUMResourceManager::Destroy() { return; glDeleteFramebuffersEXT(1, &framebuffer_); + framebuffer_ = 0; std::for_each(vertex_shaders_.begin(), vertex_shaders_.end(), DeleteShader); std::for_each( @@ -239,37 +308,70 @@ void CopyTextureCHROMIUMResourceManager::Destroy() { } glDeleteBuffersARB(1, &buffer_id_); + buffer_id_ = 0; } void CopyTextureCHROMIUMResourceManager::DoCopyTexture( const gles2::GLES2Decoder* decoder, GLenum source_target, - GLenum dest_target, GLuint source_id, + GLenum source_internal_format, GLuint dest_id, - GLint level, + GLint dest_level, + GLenum dest_internal_format, GLsizei width, GLsizei height, bool flip_y, bool premultiply_alpha, bool unpremultiply_alpha) { + bool premultiply_alpha_change = premultiply_alpha ^ unpremultiply_alpha; + // GL_INVALID_OPERATION is generated if the currently bound framebuffer's + // format does not contain a superset of the components required by the base + // format of internalformat. + // https://www.khronos.org/opengles/sdk/docs/man/xhtml/glCopyTexImage2D.xml + bool source_format_contain_superset_of_dest_format = + source_internal_format == dest_internal_format || + (source_internal_format == GL_RGBA && dest_internal_format == GL_RGB); + // GL_TEXTURE_RECTANGLE_ARB on FBO is supported by OpenGL, not GLES2, + // so restrict this to GL_TEXTURE_2D. + if (source_target == GL_TEXTURE_2D && !flip_y && !premultiply_alpha_change && + source_format_contain_superset_of_dest_format) { + DoCopyTexImage2D(decoder, + source_target, + source_id, + dest_id, + dest_level, + dest_internal_format, + width, + height, + framebuffer_); + return; + } + // Use default transform matrix if no transform passed in. const static GLfloat default_matrix[16] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}; - DoCopyTextureWithTransform(decoder, source_target, dest_target, source_id, - dest_id, level, width, height, flip_y, premultiply_alpha, - unpremultiply_alpha, default_matrix); + DoCopyTextureWithTransform(decoder, + source_target, + source_id, + dest_id, + dest_level, + width, + height, + flip_y, + premultiply_alpha, + unpremultiply_alpha, + default_matrix); } void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform( const gles2::GLES2Decoder* decoder, GLenum source_target, - GLenum dest_target, GLuint source_id, GLuint dest_id, - GLint level, + GLint dest_level, GLsizei width, GLsizei height, bool flip_y, @@ -286,27 +388,27 @@ void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform( VertexShaderId vertex_shader_id = GetVertexShaderId(flip_y); DCHECK_LT(static_cast(vertex_shader_id), vertex_shaders_.size()); - GLuint* vertex_shader = &vertex_shaders_[vertex_shader_id]; - if (!*vertex_shader) { - *vertex_shader = glCreateShader(GL_VERTEX_SHADER); - CompileShader(*vertex_shader, vertex_shader_source[vertex_shader_id]); - } - FragmentShaderId fragment_shader_id = GetFragmentShaderId( premultiply_alpha, unpremultiply_alpha, source_target); DCHECK_LT(static_cast(fragment_shader_id), fragment_shaders_.size()); - GLuint* fragment_shader = &fragment_shaders_[fragment_shader_id]; - if (!*fragment_shader) { - *fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); - CompileShader(*fragment_shader, fragment_shader_source[fragment_shader_id]); - } ProgramMapKey key(vertex_shader_id, fragment_shader_id); ProgramInfo* info = &programs_[key]; // Create program if necessary. if (!info->program) { info->program = glCreateProgram(); + GLuint* vertex_shader = &vertex_shaders_[vertex_shader_id]; + if (!*vertex_shader) { + *vertex_shader = glCreateShader(GL_VERTEX_SHADER); + CompileShader(*vertex_shader, vertex_shader_source[vertex_shader_id]); + } glAttachShader(info->program, *vertex_shader); + GLuint* fragment_shader = &fragment_shaders_[fragment_shader_id]; + if (!*fragment_shader) { + *fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + CompileShader(*fragment_shader, + fragment_shader_source[fragment_shader_id]); + } glAttachShader(info->program, *fragment_shader); glBindAttribLocation(info->program, kVertexPositionAttrib, "a_position"); glLinkProgram(info->program); @@ -337,25 +439,9 @@ void CopyTextureCHROMIUMResourceManager::DoCopyTextureWithTransform( glUniform2f(info->half_size_handle, width / 2.0f, height / 2.0f); else glUniform2f(info->half_size_handle, 0.5f, 0.5f); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, dest_id); - // NVidia drivers require texture settings to be a certain way - // or they won't report FRAMEBUFFER_COMPLETE. - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer_); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, dest_target, - dest_id, level); -#ifndef NDEBUG - GLenum fb_status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER); - if (GL_FRAMEBUFFER_COMPLETE != fb_status) { - DLOG(ERROR) << "CopyTextureCHROMIUM: Incomplete framebuffer."; - } else -#endif - { + if (BindFramebufferTexture2D( + GL_TEXTURE_2D, dest_id, dest_level, framebuffer_)) { decoder->ClearAllAttributes(); glEnableVertexAttribArray(kVertexPositionAttrib); diff --git a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h index 17290f8ed22f3..083fc4c857813 100644 --- a/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h +++ b/gpu/command_buffer/service/gles2_cmd_copy_texture_chromium.h @@ -20,7 +20,8 @@ class GLES2Decoder; // This class encapsulates the resources required to implement the // GL_CHROMIUM_copy_texture extension. The copy operation is performed -// via a blit to a framebuffer object. +// via glCopyTexImage2D() or a blit to a framebuffer object. +// The target of |dest_id| texture must be GL_TEXTURE_2D. class GPU_EXPORT CopyTextureCHROMIUMResourceManager { public: CopyTextureCHROMIUMResourceManager(); @@ -29,18 +30,29 @@ class GPU_EXPORT CopyTextureCHROMIUMResourceManager { void Initialize(const gles2::GLES2Decoder* decoder); void Destroy(); - void DoCopyTexture(const gles2::GLES2Decoder* decoder, GLenum source_target, - GLenum dest_target, GLuint source_id, GLuint dest_id, - GLint level, GLsizei width, GLsizei height, - bool flip_y, bool premultiply_alpha, + void DoCopyTexture(const gles2::GLES2Decoder* decoder, + GLenum source_target, + GLuint source_id, + GLenum source_internal_format, + GLuint dest_id, + GLint dest_level, + GLenum dest_internal_format, + GLsizei width, + GLsizei height, + bool flip_y, + bool premultiply_alpha, bool unpremultiply_alpha); // This will apply a transform on the source texture before copying to // destination texture. void DoCopyTextureWithTransform(const gles2::GLES2Decoder* decoder, - GLenum source_target, GLenum dest_target, - GLuint source_id, GLuint dest_id, GLint level, - GLsizei width, GLsizei height, bool flip_y, + GLenum source_target, + GLuint source_id, + GLuint dest_id, + GLint dest_level, + GLsizei width, + GLsizei height, + bool flip_y, bool premultiply_alpha, bool unpremultiply_alpha, const GLfloat transform_matrix[16]); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index 7e5f94223651d..10d5439022b5b 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc @@ -19,6 +19,7 @@ #include "base/command_line.h" #include "base/debug/trace_event.h" #include "base/debug/trace_event_synthetic_delay.h" +#include "base/float_util.h" #include "base/memory/scoped_ptr.h" #include "base/numerics/safe_math.h" #include "base/strings/string_number_conversions.h" @@ -2019,8 +2020,8 @@ void BackTexture::Create() { Destroy(); glGenTextures(1, &id_); ScopedTextureBinder binder(state_, id_, GL_TEXTURE_2D); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -2374,11 +2375,11 @@ bool GLES2DecoderImpl::Initialize( // Save the loseContextWhenOutOfMemory context creation attribute. lose_context_when_out_of_memory_ = - attrib_parser.lose_context_when_out_of_memory_; + attrib_parser.lose_context_when_out_of_memory; // If the failIfMajorPerformanceCaveat context creation attribute was true // and we are using a software renderer, fail. - if (attrib_parser.fail_if_major_perf_caveat_ && + if (attrib_parser.fail_if_major_perf_caveat && feature_info_->feature_flags().is_swiftshader) { group_ = NULL; // Must not destroy ContextGroup if it is not initialized. Destroy(true); @@ -2461,19 +2462,19 @@ bool GLES2DecoderImpl::Initialize( CHECK_GL_ERROR(); if (offscreen) { - if (attrib_parser.samples_ > 0 && attrib_parser.sample_buffers_ > 0 && + if (attrib_parser.samples > 0 && attrib_parser.sample_buffers > 0 && features().chromium_framebuffer_multisample) { // Per ext_framebuffer_multisample spec, need max bound on sample count. // max_sample_count must be initialized to a sane value. If // glGetIntegerv() throws a GL error, it leaves its argument unchanged. GLint max_sample_count = 1; glGetIntegerv(GL_MAX_SAMPLES_EXT, &max_sample_count); - offscreen_target_samples_ = std::min(attrib_parser.samples_, + offscreen_target_samples_ = std::min(attrib_parser.samples, max_sample_count); } else { offscreen_target_samples_ = 1; } - offscreen_target_buffer_preserved_ = attrib_parser.buffer_preserved_; + offscreen_target_buffer_preserved_ = attrib_parser.buffer_preserved; if (gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) { const bool rgb8_supported = @@ -2482,11 +2483,11 @@ bool GLES2DecoderImpl::Initialize( // little precision. Don't enable multisampling unless 8-bit render // buffer formats are available--instead fall back to 8-bit textures. if (rgb8_supported && offscreen_target_samples_ > 1) { - offscreen_target_color_format_ = attrib_parser.alpha_size_ > 0 ? + offscreen_target_color_format_ = attrib_parser.alpha_size > 0 ? GL_RGBA8 : GL_RGB8; } else { offscreen_target_samples_ = 1; - offscreen_target_color_format_ = attrib_parser.alpha_size_ > 0 ? + offscreen_target_color_format_ = attrib_parser.alpha_size > 0 ? GL_RGBA : GL_RGB; } @@ -2496,20 +2497,20 @@ bool GLES2DecoderImpl::Initialize( feature_info_->feature_flags().packed_depth24_stencil8; VLOG(1) << "GL_OES_packed_depth_stencil " << (depth24_stencil8_supported ? "" : "not ") << "supported."; - if ((attrib_parser.depth_size_ > 0 || attrib_parser.stencil_size_ > 0) && + if ((attrib_parser.depth_size > 0 || attrib_parser.stencil_size > 0) && depth24_stencil8_supported) { offscreen_target_depth_format_ = GL_DEPTH24_STENCIL8; offscreen_target_stencil_format_ = 0; } else { // It may be the case that this depth/stencil combination is not // supported, but this will be checked later by CheckFramebufferStatus. - offscreen_target_depth_format_ = attrib_parser.depth_size_ > 0 ? + offscreen_target_depth_format_ = attrib_parser.depth_size > 0 ? GL_DEPTH_COMPONENT16 : 0; - offscreen_target_stencil_format_ = attrib_parser.stencil_size_ > 0 ? + offscreen_target_stencil_format_ = attrib_parser.stencil_size > 0 ? GL_STENCIL_INDEX8 : 0; } } else { - offscreen_target_color_format_ = attrib_parser.alpha_size_ > 0 ? + offscreen_target_color_format_ = attrib_parser.alpha_size > 0 ? GL_RGBA : GL_RGB; // If depth is requested at all, use the packed depth stencil format if @@ -2520,19 +2521,19 @@ bool GLES2DecoderImpl::Initialize( VLOG(1) << "GL_EXT_packed_depth_stencil " << (depth24_stencil8_supported ? "" : "not ") << "supported."; - if ((attrib_parser.depth_size_ > 0 || attrib_parser.stencil_size_ > 0) && + if ((attrib_parser.depth_size > 0 || attrib_parser.stencil_size > 0) && depth24_stencil8_supported) { offscreen_target_depth_format_ = GL_DEPTH24_STENCIL8; offscreen_target_stencil_format_ = 0; } else { - offscreen_target_depth_format_ = attrib_parser.depth_size_ > 0 ? + offscreen_target_depth_format_ = attrib_parser.depth_size > 0 ? GL_DEPTH_COMPONENT : 0; - offscreen_target_stencil_format_ = attrib_parser.stencil_size_ > 0 ? + offscreen_target_stencil_format_ = attrib_parser.stencil_size > 0 ? GL_STENCIL_INDEX : 0; } } - offscreen_saved_color_format_ = attrib_parser.alpha_size_ > 0 ? + offscreen_saved_color_format_ = attrib_parser.alpha_size > 0 ? GL_RGBA : GL_RGB; // Create the target frame buffer. This is the one that the client renders @@ -2608,11 +2609,11 @@ bool GLES2DecoderImpl::Initialize( // user requested RGB then RGB. If the user did not specify a preference // than use whatever we were given. Same for DEPTH and STENCIL. back_buffer_color_format_ = - (attrib_parser.alpha_size_ != 0 && v > 0) ? GL_RGBA : GL_RGB; + (attrib_parser.alpha_size != 0 && v > 0) ? GL_RGBA : GL_RGB; glGetIntegerv(GL_DEPTH_BITS, &v); - back_buffer_has_depth_ = attrib_parser.depth_size_ != 0 && v > 0; + back_buffer_has_depth_ = attrib_parser.depth_size != 0 && v > 0; glGetIntegerv(GL_STENCIL_BITS, &v); - back_buffer_has_stencil_ = attrib_parser.stencil_size_ != 0 && v > 0; + back_buffer_has_stencil_ = attrib_parser.stencil_size != 0 && v > 0; } // OpenGL ES 2.0 implicitly enables the desktop GL capability @@ -3293,13 +3294,13 @@ void GLES2DecoderImpl::UpdateParentTextureInfo() { GetErrorState(), offscreen_saved_color_texture_info_.get(), GL_TEXTURE_MAG_FILTER, - GL_NEAREST); + GL_LINEAR); texture_manager()->SetParameteri( "UpdateParentTextureInfo", GetErrorState(), offscreen_saved_color_texture_info_.get(), GL_TEXTURE_MIN_FILTER, - GL_NEAREST); + GL_LINEAR); texture_manager()->SetParameteri( "UpdateParentTextureInfo", GetErrorState(), @@ -5085,7 +5086,8 @@ void GLES2DecoderImpl::ClearUnclearedAttachments( 1.0f); state_.SetDeviceColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); clear_bits |= GL_COLOR_BUFFER_BIT; - framebuffer->PrepareDrawBuffersForClear(); + if (feature_info_->feature_flags().ext_draw_buffers) + framebuffer->PrepareDrawBuffersForClear(); } if (framebuffer->HasUnclearedAttachment(GL_STENCIL_ATTACHMENT) || @@ -5106,7 +5108,8 @@ void GLES2DecoderImpl::ClearUnclearedAttachments( state_.SetDeviceCapabilityState(GL_SCISSOR_TEST, false); glClear(clear_bits); - if ((clear_bits | GL_COLOR_BUFFER_BIT) != 0) + if ((clear_bits | GL_COLOR_BUFFER_BIT) != 0 && + feature_info_->feature_flags().ext_draw_buffers) framebuffer->RestoreDrawBuffersAfterClear(); framebuffer_manager()->MarkAttachmentsAsCleared( @@ -10053,6 +10056,11 @@ void GLES2DecoderImpl::DoCopyTextureCHROMIUM( return; } + GLenum source_type = 0; + GLenum source_internal_format = 0; + source_texture->GetLevelType( + source_texture->target(), 0, &source_type, &source_internal_format); + GLenum dest_type_previous = dest_type; GLenum dest_internal_format = internal_format; bool dest_level_defined = dest_texture->GetLevelSize( @@ -10063,6 +10071,20 @@ void GLES2DecoderImpl::DoCopyTextureCHROMIUM( &dest_internal_format); } + // The destination format should be GL_ALPHA, GL_RGB, GL_RGBA, GL_LUMINANCE, + // or GL_LUMINANCE_ALPHA. + bool valid_dest_format = dest_internal_format >= GL_ALPHA && + dest_internal_format <= GL_LUMINANCE_ALPHA; + // The source format can be GL_BGRA_EXT. + bool valid_source_format = (source_internal_format >= GL_ALPHA && + source_internal_format <= GL_LUMINANCE_ALPHA) || + source_internal_format == GL_BGRA_EXT; + if (!valid_source_format || !valid_dest_format) { + LOCAL_SET_GL_ERROR( + GL_INVALID_VALUE, "glCopyTextureCHROMIUM", "invalid internal format"); + return; + } + // Resize the destination texture to the dimensions of the source texture. if (!dest_level_defined || dest_width != source_width || dest_height != source_height || @@ -10103,25 +10125,28 @@ void GLES2DecoderImpl::DoCopyTextureCHROMIUM( copy_texture_CHROMIUM_->DoCopyTextureWithTransform( this, source_texture->target(), - dest_texture->target(), source_texture->service_id(), - dest_texture->service_id(), level, - source_width, source_height, + dest_texture->service_id(), + level, + source_width, + source_height, unpack_flip_y_, unpack_premultiply_alpha_, unpack_unpremultiply_alpha_, default_matrix); } else { - copy_texture_CHROMIUM_->DoCopyTexture( - this, - source_texture->target(), - dest_texture->target(), - source_texture->service_id(), - dest_texture->service_id(), level, - source_width, source_height, - unpack_flip_y_, - unpack_premultiply_alpha_, - unpack_unpremultiply_alpha_); + copy_texture_CHROMIUM_->DoCopyTexture(this, + source_texture->target(), + source_texture->service_id(), + source_internal_format, + dest_texture->service_id(), + level, + internal_format, + source_width, + source_height, + unpack_flip_y_, + unpack_premultiply_alpha_, + unpack_unpremultiply_alpha_); } DoDidUseTexImageIfNeeded(source_texture, source_texture->target()); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h index 8011ab19990ac..b35899cfbc279 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_autogen.h @@ -1360,7 +1360,7 @@ error::Error GLES2DecoderImpl::HandleLineWidth( uint32_t immediate_data_size, const gles2::cmds::LineWidth& c) { GLfloat width = static_cast(c.width); - if (width <= 0.0f) { + if (width <= 0.0f || base::IsNaN(width)) { LOCAL_SET_GL_ERROR(GL_INVALID_VALUE, "LineWidth", "width out of range"); return error::kNoError; } diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h index 32e20d82720ba..b60bd3e4aa206 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_1_autogen.h @@ -1887,6 +1887,14 @@ TEST_P(GLES2DecoderTest1, LineWidthInvalidValue0_0) { EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); } +TEST_P(GLES2DecoderTest1, LineWidthNaNValue0) { + SpecializedSetup(false); + cmds::LineWidth cmd; + cmd.Init(nanf("")); + EXPECT_EQ(error::kNoError, ExecuteCmd(cmd)); + EXPECT_EQ(GL_INVALID_VALUE, GetGLError()); +} + TEST_P(GLES2DecoderTest1, LinkProgramValidArgs) { EXPECT_CALL(*gl_, LinkProgram(kServiceProgramId)); SpecializedSetup(true); diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc index 491749cce2e3f..ab7d6c0f02e78 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.cc @@ -353,7 +353,7 @@ void GLES2DecoderTestBase::InitDecoderWithCommandLine( shared_memory_id_ = kSharedMemoryId; shared_memory_base_ = buffer->memory(); - static const int32 kLoseContextWhenOutOfMemory = 0x10003; + static const int32 kLoseContextWhenOutOfMemory = 0x10002; int32 attributes[] = { EGL_ALPHA_SIZE, diff --git a/gpu/command_buffer/service/in_process_command_buffer.cc b/gpu/command_buffer/service/in_process_command_buffer.cc index 4a3def924a887..4c9ed26efed8a 100644 --- a/gpu/command_buffer/service/in_process_command_buffer.cc +++ b/gpu/command_buffer/service/in_process_command_buffer.cc @@ -206,6 +206,7 @@ InProcessCommandBuffer::GetDefaultService() { InProcessCommandBuffer::InProcessCommandBuffer( const scoped_refptr& service) : context_lost_(false), + idle_work_pending_(false), last_put_offset_(-1), flush_event_(false, false), service_(service.get() ? service : GetDefaultService()), @@ -505,23 +506,30 @@ void InProcessCommandBuffer::FlushOnGpuThread(int32 put_offset) { // pump idle work until the query is passed. if (put_offset == state_after_last_flush_.get_offset && gpu_scheduler_->HasMoreWork()) { - service_->ScheduleIdleWork( - base::Bind(&InProcessCommandBuffer::ScheduleMoreIdleWork, - gpu_thread_weak_ptr_)); + ScheduleIdleWorkOnGpuThread(); } } -void InProcessCommandBuffer::ScheduleMoreIdleWork() { +void InProcessCommandBuffer::PerformIdleWork() { CheckSequencedThread(); + idle_work_pending_ = false; base::AutoLock lock(command_buffer_lock_); - if (gpu_scheduler_->HasMoreWork()) { + if (MakeCurrent() && gpu_scheduler_->HasMoreWork()) { gpu_scheduler_->PerformIdleWork(); - service_->ScheduleIdleWork( - base::Bind(&InProcessCommandBuffer::ScheduleMoreIdleWork, - gpu_thread_weak_ptr_)); + ScheduleIdleWorkOnGpuThread(); } } +void InProcessCommandBuffer::ScheduleIdleWorkOnGpuThread() { + CheckSequencedThread(); + if (idle_work_pending_) + return; + idle_work_pending_ = true; + service_->ScheduleIdleWork( + base::Bind(&InProcessCommandBuffer::PerformIdleWork, + gpu_thread_weak_ptr_)); +} + void InProcessCommandBuffer::Flush(int32 put_offset) { CheckSequencedThread(); if (last_state_.error != gpu::error::kNoError) diff --git a/gpu/command_buffer/service/in_process_command_buffer.h b/gpu/command_buffer/service/in_process_command_buffer.h index 7fece0eccbbed..531d0ef6882c0 100644 --- a/gpu/command_buffer/service/in_process_command_buffer.h +++ b/gpu/command_buffer/service/in_process_command_buffer.h @@ -189,6 +189,7 @@ class GPU_EXPORT InProcessCommandBuffer : public CommandBuffer, bool InitializeOnGpuThread(const InitializeOnGpuThreadParams& params); bool DestroyOnGpuThread(); void FlushOnGpuThread(int32 put_offset); + void ScheduleIdleWorkOnGpuThread(); uint32 CreateStreamTextureOnGpuThread(uint32 client_texture_id); bool MakeCurrent(); base::Closure WrapCallback(const base::Closure& callback); @@ -213,7 +214,7 @@ class GPU_EXPORT InProcessCommandBuffer : public CommandBuffer, void OnResizeView(gfx::Size size, float scale_factor); bool GetBufferChanged(int32 transfer_buffer_id); void PumpCommands(); - void ScheduleMoreIdleWork(); + void PerformIdleWork(); static scoped_refptr GetDefaultService(); @@ -226,6 +227,7 @@ class GPU_EXPORT InProcessCommandBuffer : public CommandBuffer, scoped_refptr context_; scoped_refptr surface_; base::Closure context_lost_callback_; + bool idle_work_pending_; // Used to throttle PerformIdleWork. // Members accessed on the client thread: State last_state_; diff --git a/gpu/command_buffer/tests/gl_manager.cc b/gpu/command_buffer/tests/gl_manager.cc index 6b43e5b2e168a..c143db8efd662 100644 --- a/gpu/command_buffer/tests/gl_manager.cc +++ b/gpu/command_buffer/tests/gl_manager.cc @@ -158,11 +158,11 @@ void GLManager::Initialize(const GLManager::Options& options) { gfx::GpuPreference gpu_preference(gfx::PreferDiscreteGpu); std::vector attribs; gles2::ContextCreationAttribHelper attrib_helper; - attrib_helper.red_size_ = 8; - attrib_helper.green_size_ = 8; - attrib_helper.blue_size_ = 8; - attrib_helper.alpha_size_ = 8; - attrib_helper.depth_size_ = 16; + attrib_helper.red_size = 8; + attrib_helper.green_size = 8; + attrib_helper.blue_size = 8; + attrib_helper.alpha_size = 8; + attrib_helper.depth_size = 16; attrib_helper.Serialize(&attribs); if (!context_group) { diff --git a/gpu/config/BUILD.gn b/gpu/config/BUILD.gn index 30984e308a38a..ef64887cc2696 100644 --- a/gpu/config/BUILD.gn +++ b/gpu/config/BUILD.gn @@ -59,7 +59,7 @@ source_set("config") { ] } } - if (is_linux) { + if (is_linux && (use_x11 || use_ozone)) { deps += [ "//build/config/linux:libpci", ] diff --git a/gpu/config/gpu_control_list.cc b/gpu/config/gpu_control_list.cc index 444c186b39bfc..f5ccd61e291d0 100644 --- a/gpu/config/gpu_control_list.cc +++ b/gpu/config/gpu_control_list.cc @@ -14,6 +14,7 @@ #include "base/sys_info.h" #include "gpu/config/gpu_info.h" #include "gpu/config/gpu_util.h" +#include "third_party/re2/re2/re2.h" namespace gpu { namespace { @@ -89,6 +90,13 @@ int CompareLexicalNumberStrings( return 0; } +// A mismatch is identified only if both |input| and |pattern| are not empty. +bool StringMismatch(const std::string& input, const std::string& pattern) { + if (input.empty() || pattern.empty()) + return false; + return !RE2::FullMatch(input, pattern); +} + const char kMultiGpuStyleStringAMDSwitchable[] = "amd_switchable"; const char kMultiGpuStyleStringAMDSwitchableDiscrete[] = "amd_switchable_discrete"; @@ -261,45 +269,6 @@ GpuControlList::OsType GpuControlList::OsInfo::StringToOsType( return kOsUnknown; } -GpuControlList::StringInfo::StringInfo(const std::string& string_op, - const std::string& string_value) { - op_ = StringToOp(string_op); - value_ = base::StringToLowerASCII(string_value); -} - -bool GpuControlList::StringInfo::Contains(const std::string& value) const { - std::string my_value = base::StringToLowerASCII(value); - switch (op_) { - case kContains: - return strstr(my_value.c_str(), value_.c_str()) != NULL; - case kBeginWith: - return StartsWithASCII(my_value, value_, false); - case kEndWith: - return EndsWith(my_value, value_, false); - case kEQ: - return value_ == my_value; - default: - return false; - } -} - -bool GpuControlList::StringInfo::IsValid() const { - return op_ != kUnknown; -} - -GpuControlList::StringInfo::Op GpuControlList::StringInfo::StringToOp( - const std::string& string_op) { - if (string_op == "=") - return kEQ; - else if (string_op == "contains") - return kContains; - else if (string_op == "beginwith") - return kBeginWith; - else if (string_op == "endwith") - return kEndWith; - return kUnknown; -} - GpuControlList::FloatInfo::FloatInfo(const std::string& float_op, const std::string& float_value, const std::string& float_value2) @@ -520,13 +489,9 @@ GpuControlList::GpuControlListEntry::GetEntryFromValue( dictionary_entry_count++; } - const base::DictionaryValue* driver_vendor_value = NULL; - if (value->GetDictionary("driver_vendor", &driver_vendor_value)) { - std::string vendor_op; - std::string vendor_value; - driver_vendor_value->GetString(kOp, &vendor_op); - driver_vendor_value->GetString("value", &vendor_value); - if (!entry->SetDriverVendorInfo(vendor_op, vendor_value)) { + std::string driver_vendor_value; + if (value->GetString("driver_vendor", &driver_vendor_value)) { + if (!entry->SetDriverVendorInfo(driver_vendor_value)) { LOG(WARNING) << "Malformed driver_vendor entry " << entry->id(); return NULL; } @@ -594,39 +559,27 @@ GpuControlList::GpuControlListEntry::GetEntryFromValue( dictionary_entry_count++; } - const base::DictionaryValue* gl_vendor_value = NULL; - if (value->GetDictionary("gl_vendor", &gl_vendor_value)) { - std::string vendor_op; - std::string vendor_value; - gl_vendor_value->GetString(kOp, &vendor_op); - gl_vendor_value->GetString("value", &vendor_value); - if (!entry->SetGLVendorInfo(vendor_op, vendor_value)) { + std::string gl_vendor_value; + if (value->GetString("gl_vendor", &gl_vendor_value)) { + if (!entry->SetGLVendorInfo(gl_vendor_value)) { LOG(WARNING) << "Malformed gl_vendor entry " << entry->id(); return NULL; } dictionary_entry_count++; } - const base::DictionaryValue* gl_renderer_value = NULL; - if (value->GetDictionary("gl_renderer", &gl_renderer_value)) { - std::string renderer_op; - std::string renderer_value; - gl_renderer_value->GetString(kOp, &renderer_op); - gl_renderer_value->GetString("value", &renderer_value); - if (!entry->SetGLRendererInfo(renderer_op, renderer_value)) { + std::string gl_renderer_value; + if (value->GetString("gl_renderer", &gl_renderer_value)) { + if (!entry->SetGLRendererInfo(gl_renderer_value)) { LOG(WARNING) << "Malformed gl_renderer entry " << entry->id(); return NULL; } dictionary_entry_count++; } - const base::DictionaryValue* gl_extensions_value = NULL; - if (value->GetDictionary("gl_extensions", &gl_extensions_value)) { - std::string extensions_op; - std::string extensions_value; - gl_extensions_value->GetString(kOp, &extensions_op); - gl_extensions_value->GetString("value", &extensions_value); - if (!entry->SetGLExtensionsInfo(extensions_op, extensions_value)) { + std::string gl_extensions_value; + if (value->GetString("gl_extensions", &gl_extensions_value)) { + if (!entry->SetGLExtensionsInfo(gl_extensions_value)) { LOG(WARNING) << "Malformed gl_extensions entry " << entry->id(); return NULL; } @@ -651,13 +604,9 @@ GpuControlList::GpuControlListEntry::GetEntryFromValue( dictionary_entry_count++; } - const base::DictionaryValue* cpu_brand_value = NULL; - if (value->GetDictionary("cpu_info", &cpu_brand_value)) { - std::string cpu_op; - std::string cpu_value; - cpu_brand_value->GetString(kOp, &cpu_op); - cpu_brand_value->GetString("value", &cpu_value); - if (!entry->SetCpuBrand(cpu_op, cpu_value)) { + std::string cpu_brand_value; + if (value->GetString("cpu_info", &cpu_brand_value)) { + if (!entry->SetCpuBrand(cpu_brand_value)) { LOG(WARNING) << "Malformed cpu_brand entry " << entry->id(); return NULL; } @@ -898,10 +847,9 @@ bool GpuControlList::GpuControlListEntry::SetGLType( } bool GpuControlList::GpuControlListEntry::SetDriverVendorInfo( - const std::string& vendor_op, const std::string& vendor_value) { - driver_vendor_info_.reset(new StringInfo(vendor_op, vendor_value)); - return driver_vendor_info_->IsValid(); + driver_vendor_info_ = vendor_value; + return !driver_vendor_info_.empty(); } bool GpuControlList::GpuControlListEntry::SetDriverVersionInfo( @@ -933,24 +881,21 @@ bool GpuControlList::GpuControlListEntry::SetGLVersionInfo( } bool GpuControlList::GpuControlListEntry::SetGLVendorInfo( - const std::string& vendor_op, const std::string& vendor_value) { - gl_vendor_info_.reset(new StringInfo(vendor_op, vendor_value)); - return gl_vendor_info_->IsValid(); + gl_vendor_info_ = vendor_value; + return !gl_vendor_info_.empty(); } bool GpuControlList::GpuControlListEntry::SetGLRendererInfo( - const std::string& renderer_op, const std::string& renderer_value) { - gl_renderer_info_.reset(new StringInfo(renderer_op, renderer_value)); - return gl_renderer_info_->IsValid(); + gl_renderer_info_ = renderer_value; + return !gl_renderer_info_.empty(); } bool GpuControlList::GpuControlListEntry::SetGLExtensionsInfo( - const std::string& extensions_op, const std::string& extensions_value) { - gl_extensions_info_.reset(new StringInfo(extensions_op, extensions_value)); - return gl_extensions_info_->IsValid(); + gl_extensions_info_ = extensions_value; + return !gl_extensions_info_.empty(); } bool GpuControlList::GpuControlListEntry::SetGLResetNotificationStrategyInfo( @@ -963,10 +908,9 @@ bool GpuControlList::GpuControlListEntry::SetGLResetNotificationStrategyInfo( } bool GpuControlList::GpuControlListEntry::SetCpuBrand( - const std::string& cpu_op, const std::string& cpu_value) { - cpu_brand_.reset(new StringInfo(cpu_op, cpu_value)); - return cpu_brand_->IsValid(); + cpu_brand_ = cpu_value; + return !cpu_brand_.empty(); } bool GpuControlList::GpuControlListEntry::SetPerfGraphicsInfo( @@ -1236,8 +1180,7 @@ bool GpuControlList::GpuControlListEntry::Contains( case kMultiGpuStyleNone: break; } - if (driver_vendor_info_.get() != NULL && !gpu_info.driver_vendor.empty() && - !driver_vendor_info_->Contains(gpu_info.driver_vendor)) + if (StringMismatch(gpu_info.driver_vendor, driver_vendor_info_)) return false; if (driver_version_info_.get() != NULL && !gpu_info.driver_version.empty()) { if (!driver_version_info_->Contains(gpu_info.driver_version)) @@ -1249,14 +1192,11 @@ bool GpuControlList::GpuControlListEntry::Contains( } if (GLVersionInfoMismatch(gpu_info.gl_version)) return false; - if (gl_vendor_info_.get() != NULL && !gpu_info.gl_vendor.empty() && - !gl_vendor_info_->Contains(gpu_info.gl_vendor)) + if (StringMismatch(gpu_info.gl_vendor, gl_vendor_info_)) return false; - if (gl_renderer_info_.get() != NULL && !gpu_info.gl_renderer.empty() && - !gl_renderer_info_->Contains(gpu_info.gl_renderer)) + if (StringMismatch(gpu_info.gl_renderer, gl_renderer_info_)) return false; - if (gl_extensions_info_.get() != NULL && !gpu_info.gl_extensions.empty() && - !gl_extensions_info_->Contains(gpu_info.gl_extensions)) + if (StringMismatch(gpu_info.gl_extensions, gl_extensions_info_)) return false; if (gl_reset_notification_strategy_info_.get() != NULL && !gl_reset_notification_strategy_info_->Contains( @@ -1279,7 +1219,8 @@ bool GpuControlList::GpuControlListEntry::Contains( return false; bool found_match = false; for (size_t ii = 0; ii < machine_model_name_list_.size(); ++ii) { - if (machine_model_name_list_[ii] == gpu_info.machine_model_name) { + if (RE2::FullMatch(gpu_info.machine_model_name, + machine_model_name_list_[ii])) { found_match = true; break; } @@ -1297,9 +1238,9 @@ bool GpuControlList::GpuControlListEntry::Contains( if (direct_rendering_info_.get() != NULL && !direct_rendering_info_->Contains(gpu_info.direct_rendering)) return false; - if (cpu_brand_.get() != NULL) { + if (!cpu_brand_.empty()) { base::CPU cpu_info; - if (!cpu_brand_->Contains(cpu_info.cpu_brand())) + if (StringMismatch(cpu_info.cpu_brand(), cpu_brand_)) return false; } @@ -1317,13 +1258,13 @@ bool GpuControlList::GpuControlListEntry::NeedsMoreInfo( // If certain info is missing due to some error, say, we fail to collect // vendor_id/device_id, then even if we launch GPU process and create a gl // context, we won't gather such missing info, so we still return false. - if (driver_vendor_info_.get() && gpu_info.driver_vendor.empty()) + if (!driver_vendor_info_.empty() && gpu_info.driver_vendor.empty()) return true; if (driver_version_info_.get() && gpu_info.driver_version.empty()) return true; - if (gl_vendor_info_.get() && gpu_info.gl_vendor.empty()) + if (!gl_vendor_info_.empty() && gpu_info.gl_vendor.empty()) return true; - if (gl_renderer_info_.get() && gpu_info.gl_renderer.empty()) + if (!gl_renderer_info_.empty() && gpu_info.gl_renderer.empty()) return true; for (size_t i = 0; i < exceptions_.size(); ++i) { if (exceptions_[i]->NeedsMoreInfo(gpu_info)) diff --git a/gpu/config/gpu_control_list.h b/gpu/config/gpu_control_list.h index b2daa47eb38d9..a5abf5236347b 100644 --- a/gpu/config/gpu_control_list.h +++ b/gpu/config/gpu_control_list.h @@ -192,32 +192,6 @@ class GPU_EXPORT GpuControlList { scoped_ptr version_info_; }; - class GPU_EXPORT StringInfo { - public: - StringInfo(const std::string& string_op, const std::string& string_value); - - // Determines if a given string is included in the StringInfo. - bool Contains(const std::string& value) const; - - // Determines if the StringInfo contains valid information. - bool IsValid() const; - - private: - enum Op { - kContains, - kBeginWith, - kEndWith, - kEQ, // = - kUnknown // Indicates StringInfo data is invalid. - }; - - // Maps string to Op; returns kUnknown if it's not a valid Op. - static Op StringToOp(const std::string& string_op); - - Op op_; - std::string value_; - }; - class GPU_EXPORT FloatInfo { public: FloatInfo(const std::string& float_op, @@ -370,8 +344,7 @@ class GPU_EXPORT GpuControlList { bool SetGLType(const std::string& gl_type_string); - bool SetDriverVendorInfo(const std::string& vendor_op, - const std::string& vendor_value); + bool SetDriverVendorInfo(const std::string& vendor_value); bool SetDriverVersionInfo(const std::string& version_op, const std::string& version_style, @@ -386,21 +359,17 @@ class GPU_EXPORT GpuControlList { const std::string& version_string, const std::string& version_string2); - bool SetGLVendorInfo(const std::string& vendor_op, - const std::string& vendor_value); + bool SetGLVendorInfo(const std::string& vendor_value); - bool SetGLRendererInfo(const std::string& renderer_op, - const std::string& renderer_value); + bool SetGLRendererInfo(const std::string& renderer_value); - bool SetGLExtensionsInfo(const std::string& extensions_op, - const std::string& extensions_value); + bool SetGLExtensionsInfo(const std::string& extensions_value); bool SetGLResetNotificationStrategyInfo(const std::string& op, const std::string& int_string, const std::string& int_string2); - bool SetCpuBrand(const std::string& cpu_op, - const std::string& cpu_value); + bool SetCpuBrand(const std::string& cpu_value); bool SetPerfGraphicsInfo(const std::string& op, const std::string& float_string, @@ -464,15 +433,15 @@ class GPU_EXPORT GpuControlList { MultiGpuStyle multi_gpu_style_; MultiGpuCategory multi_gpu_category_; GLType gl_type_; - scoped_ptr driver_vendor_info_; + std::string driver_vendor_info_; scoped_ptr driver_version_info_; scoped_ptr driver_date_info_; scoped_ptr gl_version_info_; - scoped_ptr gl_vendor_info_; - scoped_ptr gl_renderer_info_; - scoped_ptr gl_extensions_info_; + std::string gl_vendor_info_; + std::string gl_renderer_info_; + std::string gl_extensions_info_; scoped_ptr gl_reset_notification_strategy_info_; - scoped_ptr cpu_brand_; + std::string cpu_brand_; scoped_ptr perf_graphics_info_; scoped_ptr perf_gaming_info_; scoped_ptr perf_overall_info_; diff --git a/gpu/config/gpu_control_list_entry_unittest.cc b/gpu/config/gpu_control_list_entry_unittest.cc index 6ae866b7747d1..d693d8511504a 100644 --- a/gpu/config/gpu_control_list_entry_unittest.cc +++ b/gpu/config/gpu_control_list_entry_unittest.cc @@ -470,14 +470,11 @@ TEST_F(GpuControlListEntryTest, GlVersionGLEntry) { EXPECT_FALSE(entry->Contains(GpuControlList::kOsWin, "6.1", gpu_info)); } -TEST_F(GpuControlListEntryTest, GlVendorEntry) { +TEST_F(GpuControlListEntryTest, GlVendorEqual) { const std::string json = LONG_STRING_CONST( { "id": 1, - "gl_vendor": { - "op": "beginwith", - "value": "NVIDIA" - }, + "gl_vendor": "NVIDIA", "features": [ "test_feature_0" ] @@ -486,25 +483,26 @@ TEST_F(GpuControlListEntryTest, GlVendorEntry) { ScopedEntry entry(GetEntryFromString(json)); EXPECT_TRUE(entry.get() != NULL); - const GpuControlList::OsType os_type[] = { - GpuControlList::kOsMacosx, - GpuControlList::kOsWin, - GpuControlList::kOsLinux, - GpuControlList::kOsChromeOS, - GpuControlList::kOsAndroid - }; - for (size_t i = 0; i < arraysize(os_type); ++i) - EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info())); + GPUInfo gpu_info; + gpu_info.gl_vendor = "NVIDIA"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + // Case sensitive. + gpu_info.gl_vendor = "NVidia"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.gl_vendor = "NVIDIA-x"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); } -TEST_F(GpuControlListEntryTest, GlRendererEntry) { +TEST_F(GpuControlListEntryTest, GlVendorWithDot) { const std::string json = LONG_STRING_CONST( { "id": 1, - "gl_renderer": { - "op": "contains", - "value": "GeForce" - }, + "gl_vendor": "X\\.Org.*", "features": [ "test_feature_0" ] @@ -513,15 +511,100 @@ TEST_F(GpuControlListEntryTest, GlRendererEntry) { ScopedEntry entry(GetEntryFromString(json)); EXPECT_TRUE(entry.get() != NULL); - const GpuControlList::OsType os_type[] = { - GpuControlList::kOsMacosx, - GpuControlList::kOsWin, - GpuControlList::kOsLinux, - GpuControlList::kOsChromeOS, - GpuControlList::kOsAndroid - }; - for (size_t i = 0; i < arraysize(os_type); ++i) - EXPECT_TRUE(entry->Contains(os_type[i], "10.6", gpu_info())); + GPUInfo gpu_info; + gpu_info.gl_vendor = "X.Org R300 Project"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsLinux, "", gpu_info)); + + gpu_info.gl_vendor = "X.Org"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsLinux, "", gpu_info)); +} + +TEST_F(GpuControlListEntryTest, GlRendererContains) { + const std::string json = LONG_STRING_CONST( + { + "id": 1, + "gl_renderer": ".*GeForce.*", + "features": [ + "test_feature_0" + ] + } + ); + ScopedEntry entry(GetEntryFromString(json)); + EXPECT_TRUE(entry.get() != NULL); + + GPUInfo gpu_info; + gpu_info.gl_renderer = "NVIDIA GeForce GT 120 OpenGL Engine"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + // Case sensitive. + gpu_info.gl_renderer = "NVIDIA GEFORCE GT 120 OpenGL Engine"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.gl_renderer = "GeForce GT 120 OpenGL Engine"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.gl_renderer = "NVIDIA GeForce"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.gl_renderer = "NVIDIA Ge Force"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); +} + +TEST_F(GpuControlListEntryTest, GlRendererCaseInsensitive) { + const std::string json = LONG_STRING_CONST( + { + "id": 1, + "gl_renderer": "(?i).*software.*", + "features": [ + "test_feature_0" + ] + } + ); + ScopedEntry entry(GetEntryFromString(json)); + EXPECT_TRUE(entry.get() != NULL); + + GPUInfo gpu_info; + gpu_info.gl_renderer = "software rasterizer"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.gl_renderer = "Software Rasterizer"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); +} + +TEST_F(GpuControlListEntryTest, GlExtensionsEndWith) { + const std::string json = LONG_STRING_CONST( + { + "id": 1, + "gl_extensions": ".*GL_SUN_slice_accum", + "features": [ + "test_feature_0" + ] + } + ); + ScopedEntry entry(GetEntryFromString(json)); + EXPECT_TRUE(entry.get() != NULL); + + GPUInfo gpu_info; + gpu_info.gl_extensions = "GL_SGIS_generate_mipmap " + "GL_SGIX_shadow " + "GL_SUN_slice_accum"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.gl_extensions = "GL_SGIS_generate_mipmap " + "GL_SUN_slice_accum " + "GL_SGIX_shadow"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); } TEST_F(GpuControlListEntryTest, PerfGraphicsEntry) { @@ -640,6 +723,38 @@ TEST_F(GpuControlListEntryTest, AMDSwitchableEntry) { GpuControlList::kOsMacosx, "10.6", gpu_info)); } +TEST_F(GpuControlListEntryTest, DriverVendorBeginWith) { + const std::string json = LONG_STRING_CONST( + { + "id": 1, + "driver_vendor": "NVIDIA.*", + "features": [ + "test_feature_0" + ] + } + ); + ScopedEntry entry(GetEntryFromString(json)); + EXPECT_TRUE(entry.get() != NULL); + + GPUInfo gpu_info; + gpu_info.driver_vendor = "NVIDIA Corporation"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + // Case sensitive. + gpu_info.driver_vendor = "NVidia Corporation"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.driver_vendor = "NVIDIA"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); + + gpu_info.driver_vendor = "USA NVIDIA"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsMacosx, "10.9", gpu_info)); +} + TEST_F(GpuControlListEntryTest, LexicalDriverVersionEntry) { const std::string json = LONG_STRING_CONST( { @@ -710,10 +825,7 @@ TEST_F(GpuControlListEntryTest, NeedsMoreInfoForExceptionsEntry) { "vendor_id": "0x8086", "exceptions": [ { - "gl_renderer": { - "op": "contains", - "value": "mesa" - } + "gl_renderer": ".*mesa.*" } ], "features": [ @@ -807,7 +919,9 @@ TEST_F(GpuControlListEntryTest, MachineModelName) { "os": { "type": "android" }, - "machine_model_name": ["Nexus 4", "XT1032"], + "machine_model_name": [ + "Nexus 4", "XT1032", "GT-.*", "SCH-.*" + ], "features": [ "test_feature_0" ] @@ -841,6 +955,18 @@ TEST_F(GpuControlListEntryTest, MachineModelName) { gpu_info.machine_model_name = ""; EXPECT_FALSE(entry->Contains( GpuControlList::kOsAndroid, "4.1", gpu_info)); + + gpu_info.machine_model_name = "GT-N7100"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsAndroid, "4.1", gpu_info)); + + gpu_info.machine_model_name = "GT-I9300"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsAndroid, "4.1", gpu_info)); + + gpu_info.machine_model_name = "SCH-I545"; + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsAndroid, "4.1", gpu_info)); } TEST_F(GpuControlListEntryTest, MachineModelNameException) { @@ -852,7 +978,7 @@ TEST_F(GpuControlListEntryTest, MachineModelNameException) { "os": { "type": "android" }, - "machine_model_name": ["Nexus 4"] + "machine_model_name": ["Nexus.*"] } ], "features": [ @@ -871,6 +997,12 @@ TEST_F(GpuControlListEntryTest, MachineModelNameException) { EXPECT_TRUE(entry->Contains( GpuControlList::kOsLinux, "4.1", gpu_info)); + gpu_info.machine_model_name = "Nexus 7"; + EXPECT_FALSE(entry->Contains( + GpuControlList::kOsAndroid, "4.1", gpu_info)); + EXPECT_TRUE(entry->Contains( + GpuControlList::kOsLinux, "4.1", gpu_info)); + gpu_info.machine_model_name = ""; EXPECT_TRUE(entry->Contains( GpuControlList::kOsAndroid, "4.1", gpu_info)); diff --git a/gpu/config/gpu_control_list_format.txt b/gpu/config/gpu_control_list_format.txt index 71c569c8397eb..c897cb6fd37c5 100644 --- a/gpu/config/gpu_control_list_format.txt +++ b/gpu/config/gpu_control_list_format.txt @@ -32,7 +32,7 @@ // 6. "multi_gpu_category" is a string, valid values include "any", "primary", // "secondary", and "active". If unspecified, the default value is "primary". // See gpu_control_list.h for more details on the meanings of the strings. -// 7. "driver_vendor" is a STRING structure (defined below). +// 7. "driver_vendor" is a string pattern. // 8. "driver_version" is a VERSION structure (defined below). // 9. "driver_date" is a VERSION structure (defined below). // The version is interpreted as "year.month.day". @@ -41,17 +41,16 @@ // The default value on Android is "gles", on Windows is "angle", on other // platforms is "gl". // 11. "gl_version" is a VERSION structure (defined below). -// 12. "gl_vendor" is a STRING structure (defined below). -// 13. "gl_renderer" is a STRING structure (defined below). -// 14. "gl_extensions" is a STRING structure (defined below). +// 12. "gl_vendor" is a string pattern. +// 13. "gl_renderer" is a string pattern. +// 14. "gl_extensions" is a string pattern. // 15. "perf_graphics" is a FLOAT structure (defined below). // 16. "perf_gaming" is a FLOAT structure (defined below). // 17. "perf_overall" is a FLOAT structure (defined below). -// 18. "machine_model_name" is an array of strings. The strings can contain -// any characters. +// 18. "machine_model_name" is an array of string patterns. // 19. "machine_model_version" is a VERSION structure (defined below). // 20. "gpu_count" is a INT structure (defined below). -// 21 "cpu_info" is a STRING structure (defined below). +// 21 "cpu_info" is a string pattern. // 22. "exceptions" is a list of entries. // 23. "features" is a list of gpu control list options, which can be // configured by a specific list. See its *_json.cc file for a list of @@ -74,12 +73,11 @@ // Only "driver_version" supports lexical style if the format is major.minor; // in that case, major is still numerical, but minor is lexical. // -// STRING includes "op" and "value". "op" can be any of the following values: -// "contains", "beginwith", "endwith", "=". "value" is a string. -// // FLOAT includes "op" "value", and "value2". "op" can be any of the // following values: "=", "<", "<=", ">", ">=", "any", "between". "value2" is // only used if "op" is "between". "value" is used for all "op" values except // "any". "value" and "value2" are valid float numbers. // INT is very much like FLOAT, except that the values need to be integers. - +// +// String pattern syntax can be found at +// https://code.google.com/p/re2/wiki/Syntax. diff --git a/gpu/config/gpu_control_list_string_info_unittest.cc b/gpu/config/gpu_control_list_string_info_unittest.cc deleted file mode 100644 index 39e2f5800ceaa..0000000000000 --- a/gpu/config/gpu_control_list_string_info_unittest.cc +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "gpu/config/gpu_control_list.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace gpu { - -class StringInfoTest : public testing::Test { - public: - StringInfoTest() { } - virtual ~StringInfoTest() { } - - typedef GpuControlList::StringInfo StringInfo; -}; - -TEST_F(StringInfoTest, ValidStringInfo) { - const std::string op[] = { - "contains", - "beginwith", - "endwith", - "=" - }; - for (size_t i = 0; i < arraysize(op); ++i) { - { - StringInfo info(op[i], std::string()); - EXPECT_TRUE(info.IsValid()); - } - { - StringInfo info(op[i], "hello"); - EXPECT_TRUE(info.IsValid()); - } - } -} - -TEST_F(StringInfoTest, InvalidStringInfo) { - const std::string op[] = { - "Contains", - "BeginWith", - "EndWith", - " =", - "= " - }; - for (size_t i = 0; i < arraysize(op); ++i) { - StringInfo info(op[i], "hello"); - EXPECT_FALSE(info.IsValid()); - } -} - -TEST_F(StringInfoTest, StringComparison) { - { - StringInfo info("contains", "happy"); - EXPECT_TRUE(info.Contains("unhappy")); - EXPECT_TRUE(info.Contains("happy1")); - EXPECT_TRUE(info.Contains("happy")); - EXPECT_TRUE(info.Contains("a happy dog")); - EXPECT_TRUE(info.Contains("Happy")); - EXPECT_TRUE(info.Contains("HAPPY")); - EXPECT_FALSE(info.Contains("ha-ppy")); - } - { - StringInfo info("beginwith", "happy"); - EXPECT_FALSE(info.Contains("unhappy")); - EXPECT_TRUE(info.Contains("happy1")); - EXPECT_TRUE(info.Contains("happy")); - EXPECT_FALSE(info.Contains("a happy dog")); - EXPECT_TRUE(info.Contains("Happy")); - EXPECT_TRUE(info.Contains("HAPPY")); - EXPECT_FALSE(info.Contains("ha-ppy")); - } - { - StringInfo info("endwith", "happy"); - EXPECT_TRUE(info.Contains("unhappy")); - EXPECT_FALSE(info.Contains("happy1")); - EXPECT_TRUE(info.Contains("happy")); - EXPECT_FALSE(info.Contains("a happy dog")); - EXPECT_TRUE(info.Contains("Happy")); - EXPECT_TRUE(info.Contains("HAPPY")); - EXPECT_FALSE(info.Contains("ha-ppy")); - } - { - StringInfo info("=", "happy"); - EXPECT_FALSE(info.Contains("unhappy")); - EXPECT_FALSE(info.Contains("happy1")); - EXPECT_TRUE(info.Contains("happy")); - EXPECT_FALSE(info.Contains("a happy dog")); - EXPECT_TRUE(info.Contains("Happy")); - EXPECT_TRUE(info.Contains("HAPPY")); - EXPECT_FALSE(info.Contains("ha-ppy")); - EXPECT_FALSE(info.Contains("ha ppy")); - EXPECT_FALSE(info.Contains(" happy")); - EXPECT_FALSE(info.Contains("happy ")); - } -} - -} // namespace gpu - diff --git a/gpu/config/gpu_control_list_unittest.cc b/gpu/config/gpu_control_list_unittest.cc index 03a7d0e249146..9ba06b897aa47 100644 --- a/gpu/config/gpu_control_list_unittest.cc +++ b/gpu/config/gpu_control_list_unittest.cc @@ -303,10 +303,7 @@ TEST_F(GpuControlListTest, NeedsMoreInfoForExceptions) { "vendor_id": "0x8086", "exceptions": [ { - "gl_renderer": { - "op": "contains", - "value": "mesa" - } + "gl_renderer": ".*mesa.*" } ], "features": [ diff --git a/gpu/config/gpu_driver_bug_list_json.cc b/gpu/config/gpu_driver_bug_list_json.cc index 30ddda861a1b5..5477c593b2cec 100644 --- a/gpu/config/gpu_driver_bug_list_json.cc +++ b/gpu/config/gpu_driver_bug_list_json.cc @@ -19,7 +19,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( { "name": "gpu driver bug list", // Please update the version number whenever you change this file. - "version": "6.9", + "version": "7.2", "entries": [ { "id": 1, @@ -27,10 +27,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Imagination" - }, + "gl_vendor": "Imagination.*", "features": [ "use_client_side_arrays_for_stream_buffers" ] @@ -41,10 +38,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "ARM" - }, + "gl_vendor": "ARM.*", "features": [ "use_client_side_arrays_for_stream_buffers" ] @@ -112,10 +106,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "value": "4.3" } }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "restore_scissor_on_fbo_change" ] @@ -256,10 +247,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "disable_depth_texture" ] @@ -284,10 +272,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_extensions": { - "op": "contains", - "value": "GL_VIV_shader_binary" - }, + "gl_extensions": ".*GL_VIV_shader_binary.*", "features": [ "unbind_fbo_on_context_switch" ] @@ -299,10 +284,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Imagination" - }, + "gl_vendor": "Imagination.*", "features": [ "unbind_fbo_on_context_switch" ] @@ -327,14 +309,8 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "ARM" - }, - "gl_renderer": { - "op": "contains", - "value": "Mali-400" - }, + "gl_vendor": "ARM.*", + "gl_renderer": ".*Mali-400.*", "features": [ "use_non_zero_size_for_client_side_stream_buffers" ] @@ -397,14 +373,8 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "id": 31, "cr_bugs": [154715, 10068, 269829, 294779, 285292], "description": "The Mali-Txxx driver does not guarantee flush ordering", - "gl_vendor": { - "op": "beginwith", - "value": "ARM" - }, - "gl_renderer": { - "op": "beginwith", - "value": "Mali-T" - }, + "gl_vendor": "ARM.*", + "gl_renderer": "Mali-T.*", "features": [ "use_virtualized_gl_contexts" ] @@ -416,10 +386,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Broadcom" - }, + "gl_vendor": "Broadcom.*", "features": [ "use_virtualized_gl_contexts" ] @@ -430,10 +397,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Imagination" - }, + "gl_vendor": "Imagination.*", "features": [ "use_virtualized_gl_contexts" ] @@ -445,10 +409,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_extensions": { - "op": "contains", - "value": "GL_VIV_shader_binary" - }, + "gl_extensions": ".*GL_VIV_shader_binary.*", "features": [ "use_virtualized_gl_contexts" ] @@ -464,10 +425,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "value": "4.3" } }, - "gl_vendor": { - "op": "beginwith", - "value": "NVIDIA" - }, + "gl_vendor": "NVIDIA.*", "features": [ "use_virtualized_gl_contexts" ] @@ -483,10 +441,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "value": "4.3" } }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "use_virtualized_gl_contexts" ] @@ -510,10 +465,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "use_virtualized_gl_contexts" ] @@ -540,10 +492,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "value": "4.4" } }, - "gl_vendor": { - "op": "beginwith", - "value": "ARM" - }, + "gl_vendor": "ARM.*", "features": [ "disable_ext_discard_framebuffer" ] @@ -555,14 +504,8 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Imagination" - }, - "gl_renderer": { - "op": "=", - "value": "PowerVR SGX 540" - }, + "gl_vendor": "Imagination.*", + "gl_renderer": "PowerVR SGX 540", "features": [ "disable_ext_discard_framebuffer" ] @@ -574,10 +517,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_extensions": { - "op": "contains", - "value": "GL_VIV_shader_binary" - }, + "gl_extensions": ".*GL_VIV_shader_binary.*", "features": [ "disable_ext_discard_framebuffer" ] @@ -635,10 +575,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "wake_up_gpu_before_drawing" ] @@ -654,10 +591,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "op": "<", "value": "3.1" }, - "gl_vendor": { - "op": "beginwith", - "value": "NVIDIA" - }, + "gl_vendor": "NVIDIA.*", "features": [ "release_image_after_use" ] @@ -668,10 +602,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "win" }, - "gl_renderer": { - "op": "beginwith", - "value": "ANGLE" - }, + "gl_renderer": "ANGLE.*", "features": [ "texsubimage2d_faster_than_teximage2d" ] @@ -682,10 +613,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "disable_multisampling" ] @@ -716,10 +644,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "features": [ "count_all_in_varyings_packing" ] @@ -731,10 +656,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "chromeos" }, - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "features": [ "count_all_in_varyings_packing" ] @@ -865,10 +787,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "id": 69, "description": "Some shaders in Skia need more than the min available vertex and fragment shader uniform vectors in case of OSMesa", "cr_bugs": [174845], - "driver_vendor": { - "op": "=", - "value": "osmesa" - }, + "driver_vendor": "osmesa", "features": [ "max_fragment_uniform_vectors_32", "max_varying_vectors_16", @@ -898,10 +817,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_extensions": { - "op": "contains", - "value": "GL_VIV_shader_binary" - }, + "gl_extensions": ".*GL_VIV_shader_binary.*", "features": [ "disable_oes_standard_derivatives" ] @@ -918,10 +834,7 @@ const char kGpuDriverBugListJson[] = LONG_STRING_CONST( "op": "=", "value": "3.1" }, - "gl_vendor": { - "op": "beginwith", - "value": "NVidia" - }, + "gl_vendor": "NVIDIA.*", "features": [ "use_virtualized_gl_contexts" ] @@ -952,10 +865,7 @@ LONG_STRING_CONST( "value": "4.4.4" } }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "disable_egl_khr_fence_sync" ] @@ -971,14 +881,8 @@ LONG_STRING_CONST( "value": "4.3" } }, - "gl_vendor": { - "op": "beginwith", - "value": "ARM" - }, - "gl_renderer": { - "op": "contains", - "value": "Mali-400" - }, + "gl_vendor": "ARM.*", + "gl_renderer": ".*Mali-400.*", "features": [ "disable_multisampling" ] @@ -994,18 +898,15 @@ LONG_STRING_CONST( "value": "4.4.4" } }, - "gl_vendor": { - "op": "beginwith", - "value": "Imagination Technologies" - }, + "gl_vendor": "Imagination Technologies.*", "features": [ "disable_egl_khr_fence_sync" ] }, { "id": 77, - "cr_bugs": [378691, 373360, 371530], - "description": "Testing fences was broken on Mali-400 MP drivers", + "cr_bugs": [378691, 373360, 371530, 398964], + "description": "Testing fences was broken on Mali ES2 drivers", "os": { "type": "android", "version": { @@ -1013,13 +914,12 @@ LONG_STRING_CONST( "value": "4.4.4" } }, - "gl_vendor": { - "op": "beginwith", - "value": "ARM" - }, - "gl_renderer": { - "op": "beginwith", - "value": "Mali-400 MP" + "gl_vendor": "ARM.*", + "gl_renderer": "Mali.*", + "gl_type": "gles", + "gl_version": { + "op": "<", + "value": "3.0" }, "features": [ "disable_egl_khr_fence_sync" @@ -1036,10 +936,7 @@ LONG_STRING_CONST( "value": "4.4.4" } }, - "gl_vendor": { - "op": "beginwith", - "value": "Broadcom" - }, + "gl_vendor": "Broadcom.*", "features": [ "disable_egl_khr_fence_sync" ] @@ -1066,10 +963,7 @@ LONG_STRING_CONST( "value": "4.3" } }, - "gl_vendor": { - "op": "beginwith", - "value": "Qualcomm" - }, + "gl_vendor": "Qualcomm.*", "features": [ "disable_async_readpixels" ] @@ -1085,6 +979,22 @@ LONG_STRING_CONST( "features": [ "disable_d3d11" ] + }, + { + "id": 87, + "description": "Disable use of Direct3D 11 on older AMD drivers", + "cr_bugs": [402134], + "os": { + "type": "win" + }, + "vendor_id": "0x1002", + "driver_date": { + "op": "<", + "value": "2011.1" + }, + "features": [ + "disable_d3d11" + ] } ] } diff --git a/gpu/config/software_rendering_list_json.cc b/gpu/config/software_rendering_list_json.cc index e0eab81a52cdb..6a12707992208 100644 --- a/gpu/config/software_rendering_list_json.cc +++ b/gpu/config/software_rendering_list_json.cc @@ -18,7 +18,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( { "name": "software rendering list", // Please update the version number whenever you change this file. - "version": "8.10", + "version": "9.7", "entries": [ { "id": 1, @@ -42,10 +42,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "gl_renderer": { - "op": "contains", - "value": "software" - }, + "gl_renderer": "(?i).*software.*", "features": [ "all" ] @@ -76,10 +73,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "vendor_id": "0x1002", "exceptions": [ { - "driver_vendor": { - "op": "contains", - "value": "AMD" - }, + "driver_vendor": ".*AMD.*", "driver_version": { "op": ">=", "style": "lexical", @@ -87,10 +81,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( } }, { - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": ">=", "value": "10.0.4" @@ -150,10 +141,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( } }, { - "driver_vendor": { - "op": "=", - "value": "osmesa" - } + "driver_vendor": "osmesa" }, { "vendor_id": "0x1414", @@ -172,10 +160,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "type": "linux" }, "vendor_id": "0x8086", - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": "<", "value": "10.1" @@ -240,20 +225,14 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": "<", "value": "7.11" }, "exceptions": [ { - "driver_vendor": { - "op": "=", - "value": "osmesa" - } + "driver_vendor": "osmesa" } ], "features": [ @@ -277,16 +256,10 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "gl_vendor": { - "op": "beginwith", - "value": "ATI" - }, + "gl_vendor": "ATI.*", "exceptions": [ { - "driver_vendor": { - "op": "contains", - "value": "AMD" - }, + "driver_vendor": ".*AMD.*", "driver_version": { "op": ">=", "style": "lexical", @@ -294,10 +267,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( } }, { - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": ">=", "value": "10.0.4" @@ -315,20 +285,11 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "gl_vendor": { - "op": "beginwith", - "value": "X.Org" - }, - "gl_renderer": { - "op": "contains", - "value": "AMD" - }, + "gl_vendor": "X\\.Org.*", + "gl_renderer": ".*AMD.*", "exceptions": [ { - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": ">=", "value": "10.0.4" @@ -346,20 +307,11 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "gl_vendor": { - "op": "beginwith", - "value": "X.Org" - }, - "gl_renderer": { - "op": "contains", - "value": "ATI" - }, + "gl_vendor": "X\\.Org.*", + "gl_renderer": ".*ATI.*", "exceptions": [ { - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": ">=", "value": "10.0.4" @@ -378,10 +330,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "type": "linux" }, "vendor_id": "0x10de", - "gl_vendor": { - "op": "beginwith", - "value": "nouveau" - }, + "gl_vendor": "(?i)nouveau.*", "features": [ "all" ] @@ -405,10 +354,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( } }, { - "cpu_info": { - "op": "contains", - "value": "Atom" - } + "cpu_info": "(?i).*Atom.*" } ], "features": [ @@ -449,18 +395,12 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "multi_gpu_style": "optimus", "exceptions": [ { - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": ">=", "value": "10.1" }, - "gl_vendor": { - "op": "beginwith", - "value": "Intel" - } + "gl_vendor": "Intel.*" } ], "features": [ @@ -557,10 +497,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "type": "linux" }, "vendor_id": "0x10de", - "driver_vendor": { - "op": "=", - "value": "NVIDIA" - }, + "driver_vendor": "NVIDIA", "driver_version": { "op": "<", "value": "295" @@ -621,24 +558,15 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "linux" }, - "gl_vendor": { - "op": "beginwith", - "value": "VMware" - }, + "gl_vendor": "VMware.*", "exceptions": [ { - "driver_vendor": { - "op": "=", - "value": "Mesa" - }, + "driver_vendor": "Mesa", "driver_version": { "op": ">=", "value": "9.2.1" }, - "gl_renderer": { - "op": "contains", - "value": "SVGA3D" - } + "gl_renderer": ".*SVGA3D.*" } ], "features": [ @@ -663,10 +591,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "type": "linux" }, "vendor_id": "0x10de", - "driver_vendor": { - "op": "=", - "value": "NVIDIA" - }, + "driver_vendor": "NVIDIA", "features": [ "accelerated_video_decode", "flash_3d", @@ -716,10 +641,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "android" }, - "gl_renderer": { - "op": "contains", - "value": "Adreno" - }, + "gl_renderer": ".*Adreno.*", "driver_version": { "op": "<", "value": "4.1" @@ -848,10 +770,7 @@ const char kSoftwareRenderingListJson[] = LONG_STRING_CONST( "os": { "type": "win" }, - "driver_vendor": { - "op": "=", - "value": "Microsoft" - }, + "driver_vendor": "Microsoft", "exceptions": [ { "vendor_id": "0x1414", @@ -942,28 +861,24 @@ LONG_STRING_CONST( }, { "id": 83, - "description": "Samsung Gaxlaxy NOTE II is too buggy to use for video decoding", + "description": "Samsung Galaxy NOTE is too buggy to use for video decoding", "cr_bugs": [308721], "os": { - "type": "android", - "version": { - "op": "<=", - "value": "4.1.2" - } + "type": "android" }, - "machine_model_name": ["GT-N7100"], + "machine_model_name": ["GT-.*"], "features": [ "accelerated_video_decode" ] }, { "id": 85, - "description": "Samsung Gaxlaxy S4 is too buggy to use for video decoding", + "description": "Samsung Galaxy S4 is too buggy to use for video decoding", "cr_bugs": [329072], "os": { "type": "android" }, - "machine_model_name": ["SCH-I545"], + "machine_model_name": ["SCH-.*"], "features": [ "accelerated_video_decode" ] @@ -1112,10 +1027,7 @@ LONG_STRING_CONST( "type": "linux" }, "vendor_id": "0x1002", - "driver_vendor": { - "op": "contains", - "value": "AMD" - }, + "driver_vendor": ".*AMD.*", "driver_version": { "op": "=", "value": "13.101" @@ -1126,13 +1038,13 @@ LONG_STRING_CONST( }, { "id": 96, - "description": "GPU rasterization is whitelisted on N4, N5, N7 and Moto X", + "description": "GPU rasterization is whitelisted on N4, N5, N7 and Moto X, and on Qualcomm GPUs on Android 4.4", "cr_bugs": [362779], + "os": { + "type": "android" + }, "exceptions": [ { - "os": { - "type": "android" - }, "machine_model_name": ["Nexus 4", "Nexus 5", "Nexus 7", "XT1049", "XT1050", "XT1052", "XT1053", "XT1055", "XT1056", "XT1058", "XT1060"] @@ -1145,6 +1057,20 @@ LONG_STRING_CONST( "value": "4.4.99" } } + }, + { + "os": { + "type": "android", + "version": { + "op": ">=", + "value": "4.4" + } + }, + "gl_type": "gles", + "gl_version": { + "op": ">=", + "value": "3.0" + } } ], "features": [ @@ -1162,8 +1088,8 @@ LONG_STRING_CONST( }, "machine_model_name": ["HTC One", "C5303", "C6603", "C6903", - "GT-I8262", "GT-I8552", "GT-I9195", "GT-I9300", - "GT-I9500", "GT-I9505", "GT-N7100", + "GT-I8262", "GT-I8552", "GT-I9195", + "GT-I9500", "GT-I9505", "SAMSUNG-SCH-I337", "SCH-I545", "SGH-M919", "SM-N900", "SM-N9005", "SPH-L720", "XT907", "XT1032", "XT1033", "XT1080"] @@ -1191,6 +1117,42 @@ LONG_STRING_CONST( "features": [ "gpu_rasterization_expanded_heuristics" ] + }, + { + "id": 99, + "description": "GPU rasterization is blacklisted on non-Android", + "cr_bugs": [362779], + "exceptions": [ + { + "os": { + "type": "android" + } + } + ], + "features": [ + "gpu_rasterization" + ] + }, + { + "id": 100, + "description": "GPU rasterization is blacklisted on Nexus 10", + "cr_bugs": [407144], + "gl_renderer": ".*Mali-T604.*", + "features": [ + "gpu_rasterization" + ] + }, + { + "id": 101, + "description": "Samsung Galaxy Tab is too buggy to use for video decoding", + "cr_bugs": [408353], + "os": { + "type": "android" + }, + "machine_model_name": ["SM-.*"], + "features": [ + "accelerated_video_decode" + ] } ] } diff --git a/gpu/gpu.gyp b/gpu/gpu.gyp index aa2a8c1bd6582..294f436e68220 100644 --- a/gpu/gpu.gyp +++ b/gpu/gpu.gyp @@ -37,6 +37,7 @@ 'target_name': 'gl_in_process_context', 'type': '<(component)', 'dependencies': [ + 'command_buffer/command_buffer.gyp:gles2_utils', 'gles2_implementation', 'gpu', '../base/base.gyp:base', @@ -276,7 +277,6 @@ 'config/gpu_control_list_entry_unittest.cc', 'config/gpu_control_list_number_info_unittest.cc', 'config/gpu_control_list_os_info_unittest.cc', - 'config/gpu_control_list_string_info_unittest.cc', 'config/gpu_control_list_unittest.cc', 'config/gpu_control_list_version_info_unittest.cc', 'config/gpu_driver_bug_list_unittest.cc', diff --git a/gpu/gpu_config.gypi b/gpu/gpu_config.gypi index fc9306f89c1b7..b90143f502e97 100644 --- a/gpu/gpu_config.gypi +++ b/gpu/gpu_config.gypi @@ -65,7 +65,7 @@ '../third_party/amd/amd_videocard_info_win.cc', ], }], - ['OS=="linux"', { + ['OS=="linux" and (use_x11==1 or use_ozone==1)', { 'dependencies': [ '../build/linux/system.gyp:libpci', ], diff --git a/ios/web/public/user_agent.mm b/ios/web/public/user_agent.mm index 879667e754035..24fb64612c078 100644 --- a/ios/web/public/user_agent.mm +++ b/ios/web/public/user_agent.mm @@ -33,6 +33,7 @@ // Safari version can't be, so a lookup table is used instead (for both, since // the reported versions should stay in sync). static const OSVersionMap version_map[] = { + { 8, 0, { "600.1.4", "600.1.4" } }, { 7, 1, { "9537.53", "537.51.2" } }, { 7, 0, { "9537.53", "537.51.1" } }, // 6.1 has the same values as 6.0. diff --git a/ipc/ipc_channel_win.cc b/ipc/ipc_channel_win.cc index eabf85e3e0fe0..cff6cd4335686 100644 --- a/ipc/ipc_channel_win.cc +++ b/ipc/ipc_channel_win.cc @@ -66,6 +66,9 @@ ChannelWin::ChannelWin(const IPC::ChannelHandle &channel_handle, validate_client_(false), writing_(false), debug_flags_(0), + write_error_(0), + last_write_error_(0), + write_size_(0), client_secret_(0), weak_factory_(this) { CreatePipe(channel_handle, mode); @@ -439,14 +442,16 @@ bool ChannelWin::ProcessOutgoingMessages( debug_flags_ |= WRITE_MSG; CHECK(!writing_); writing_ = true; + write_size_ = static_cast(m->size()); + write_error_ = 0; BOOL ok = WriteFile(pipe_, m->data(), - static_cast(m->size()), - &bytes_written, + write_size_, + NULL, &output_state_.context.overlapped); if (!ok) { - DWORD err = GetLastError(); - if (err == ERROR_IO_PENDING) { + write_error_ = GetLastError(); + if (write_error_ == ERROR_IO_PENDING) { output_state_.is_pending = true; DVLOG(2) << "sent pending message @" << m << " on channel @" << this @@ -455,7 +460,8 @@ bool ChannelWin::ProcessOutgoingMessages( return true; } writing_ = false; - LOG(ERROR) << "pipe error: " << err; + last_write_error_ = write_error_; + LOG(ERROR) << "pipe error: " << write_error_; return false; } diff --git a/ipc/ipc_channel_win.h b/ipc/ipc_channel_win.h index 2bbb294083407..a38f9766ee13e 100644 --- a/ipc/ipc_channel_win.h +++ b/ipc/ipc_channel_win.h @@ -98,7 +98,16 @@ class ChannelWin : public Channel, bool writing_; // Tracks the lifetime of this object, for debugging purposes. - int32 debug_flags_; + uint32 debug_flags_; + + // OS result for the current write. TODO(rvargas): remove this. + uint32 write_error_; + + // OS result for a previous failed write. TODO(rvargas): remove this. + uint32 last_write_error_; + + // Size of the current write. TODO(rvargas): remove this. + uint32 write_size_; // This is a unique per-channel value used to authenticate the client end of // a connection. If the value is non-zero, the client passes it in the hello diff --git a/ipc/mojo/BUILD.gn b/ipc/mojo/BUILD.gn index accc89d584124..d059b48bd1e89 100644 --- a/ipc/mojo/BUILD.gn +++ b/ipc/mojo/BUILD.gn @@ -10,13 +10,15 @@ component("mojo") { "ipc_message_pipe_reader.h", ] + defines = [ "IPC_MOJO_IMPLEMENTATION" ] + deps = [ "//base", + "//base/third_party/dynamic_annotations", "//ipc", + "//mojo/environment:chromium", "//mojo/public/cpp/bindings", "//mojo/system", - # TODO(viettrungluu): Needed for base/lazy_instance.h, which is suspect. - "//base/third_party/dynamic_annotations", ] } @@ -28,7 +30,6 @@ test("ipc_mojo_unittests") { deps = [ "//base", - # TODO(viettrungluu): Needed for base/lazy_instance.h, which is suspect. "//base/test:test_support", "//base/third_party/dynamic_annotations", "//ipc", diff --git a/ipc/mojo/OWNERS b/ipc/mojo/OWNERS new file mode 100644 index 0000000000000..584a1f689413e --- /dev/null +++ b/ipc/mojo/OWNERS @@ -0,0 +1,2 @@ +morrita@chromium.org +viettrungluu@chromium.org \ No newline at end of file diff --git a/ipc/mojo/ipc_channel_mojo.cc b/ipc/mojo/ipc_channel_mojo.cc index 27d35b7e30cd9..8e205ac4e233d 100644 --- a/ipc/mojo/ipc_channel_mojo.cc +++ b/ipc/mojo/ipc_channel_mojo.cc @@ -224,18 +224,29 @@ bool ChannelMojo::MessageReader::Send(scoped_ptr message) { message->TraceMessageBegin(); std::vector handles; #if defined(OS_POSIX) && !defined(OS_NACL) + // We dup() the handles in IPC::Message to transmit. + // IPC::FileDescriptorSet has intricate lifecycle semantics + // of FDs, so just to dup()-and-own them is the safest option. if (message->HasFileDescriptors()) { FileDescriptorSet* fdset = message->file_descriptor_set(); for (size_t i = 0; i < fdset->size(); ++i) { + int fd_to_send = dup(fdset->GetDescriptorAt(i)); + if (-1 == fd_to_send) { + DPLOG(WARNING) << "Failed to dup FD to transmit."; + std::for_each(handles.begin(), handles.end(), &MojoClose); + CloseWithError(MOJO_RESULT_UNKNOWN); + return false; + } + MojoHandle wrapped_handle; MojoResult wrap_result = CreatePlatformHandleWrapper( mojo::embedder::ScopedPlatformHandle( - mojo::embedder::PlatformHandle( - fdset->GetDescriptorAt(i))), + mojo::embedder::PlatformHandle(fd_to_send)), &wrapped_handle); if (MOJO_RESULT_OK != wrap_result) { DLOG(WARNING) << "Pipe failed to wrap handles. Closing: " << wrap_result; + std::for_each(handles.begin(), handles.end(), &MojoClose); CloseWithError(wrap_result); return false; } @@ -251,6 +262,7 @@ bool ChannelMojo::MessageReader::Send(scoped_ptr message) { static_cast(handles.size()), MOJO_WRITE_MESSAGE_FLAG_NONE); if (MOJO_RESULT_OK != write_result) { + std::for_each(handles.begin(), handles.end(), &MojoClose); CloseWithError(write_result); return false; } @@ -260,7 +272,7 @@ bool ChannelMojo::MessageReader::Send(scoped_ptr message) { //------------------------------------------------------------------------------ -// MessagePipeReader implemenation for control messages. +// MessagePipeReader implementation for control messages. // Actual message handling is implemented by sublcasses. class ChannelMojo::ControlReader : public internal::MessagePipeReader { public: @@ -476,26 +488,12 @@ ChannelMojo::ChannelMojo( bootstrap_(bootstrap.Pass()), mode_(mode), listener_(listener), peer_pid_(base::kNullProcessId) { - DCHECK(mode_ == MODE_SERVER || mode_ == MODE_CLIENT); - mojo::ScopedMessagePipeHandle control_pipe - = mojo::embedder::CreateChannel( - mojo::embedder::ScopedPlatformHandle( - ToPlatformHandle(bootstrap_->TakePipeHandle())), - io_thread_task_runner, - base::Bind(&ChannelMojo::DidCreateChannel, base::Unretained(this)), - io_thread_task_runner); - - // MessagePipeReader, that is crated in InitOnIOThread(), should live only in - // IO thread, but IPC::Channel can be instantiated outside of it. - // So we move the creation to the appropriate thread. if (base::MessageLoopProxy::current() == io_thread_task_runner) { - InitOnIOThread(control_pipe.Pass()); + InitOnIOThread(); } else { - io_thread_task_runner->PostTask( - FROM_HERE, - base::Bind(&ChannelMojo::InitOnIOThread, - weak_factory_.GetWeakPtr(), - base::Passed(control_pipe.Pass()))); + io_thread_task_runner->PostTask(FROM_HERE, + base::Bind(&ChannelMojo::InitOnIOThread, + weak_factory_.GetWeakPtr())); } } @@ -503,20 +501,26 @@ ChannelMojo::~ChannelMojo() { Close(); } -void ChannelMojo::InitOnIOThread(mojo::ScopedMessagePipeHandle control_pipe) { - control_reader_ = CreateControlReader(control_pipe.Pass()); -} - -scoped_ptr ChannelMojo::CreateControlReader( - mojo::ScopedMessagePipeHandle pipe) { - if (MODE_SERVER == mode_) { - return make_scoped_ptr( - new ServerControlReader(pipe.Pass(), this)).PassAs(); +void ChannelMojo::InitOnIOThread() { + mojo::embedder::ChannelInfo* channel_info; + mojo::ScopedMessagePipeHandle control_pipe = + mojo::embedder::CreateChannelOnIOThread( + mojo::embedder::ScopedPlatformHandle( + ToPlatformHandle(bootstrap_->TakePipeHandle())), + &channel_info); + channel_info_.reset(channel_info); + + switch (mode_) { + case MODE_SERVER: + control_reader_.reset(new ServerControlReader(control_pipe.Pass(), this)); + break; + case MODE_CLIENT: + control_reader_.reset(new ClientControlReader(control_pipe.Pass(), this)); + break; + default: + NOTREACHED(); + break; } - - DCHECK(mode_ == MODE_CLIENT); - return make_scoped_ptr( - new ClientControlReader(pipe.Pass(), this)).PassAs(); } bool ChannelMojo::Connect() { @@ -573,10 +577,6 @@ ChannelHandle ChannelMojo::TakePipeHandle() { return bootstrap_->TakePipeHandle(); } -void ChannelMojo::DidCreateChannel(mojo::embedder::ChannelInfo* info) { - channel_info_.reset(info); -} - void ChannelMojo::OnMessageReceived(Message& message) { listener_->OnMessageReceived(message); if (message.dispatch_error()) diff --git a/ipc/mojo/ipc_channel_mojo.h b/ipc/mojo/ipc_channel_mojo.h index b00abc954489b..a2fa587988524 100644 --- a/ipc/mojo/ipc_channel_mojo.h +++ b/ipc/mojo/ipc_channel_mojo.h @@ -106,10 +106,7 @@ class IPC_MOJO_EXPORT ChannelMojo : public Channel { ChannelMojo(scoped_ptr bootstrap, Mode mode, Listener* listener, scoped_refptr io_thread_task_runner); - void InitOnIOThread(mojo::ScopedMessagePipeHandle control_pipe); - scoped_ptr CreateControlReader( - mojo::ScopedMessagePipeHandle pipe); - void DidCreateChannel(mojo::embedder::ChannelInfo*); + void InitOnIOThread(); base::WeakPtrFactory weak_factory_; scoped_ptr bootstrap_; diff --git a/ipc/mojo/ipc_channel_mojo_unittest.cc b/ipc/mojo/ipc_channel_mojo_unittest.cc index 915029df56ffb..3157837039903 100644 --- a/ipc/mojo/ipc_channel_mojo_unittest.cc +++ b/ipc/mojo/ipc_channel_mojo_unittest.cc @@ -22,7 +22,8 @@ namespace { class ListenerThatExpectsOK : public IPC::Listener { public: - ListenerThatExpectsOK() {} + ListenerThatExpectsOK() + : received_ok_(false) {} virtual ~ListenerThatExpectsOK() {} @@ -31,12 +32,16 @@ class ListenerThatExpectsOK : public IPC::Listener { std::string should_be_ok; EXPECT_TRUE(iter.ReadString(&should_be_ok)); EXPECT_EQ(should_be_ok, "OK"); + received_ok_ = true; base::MessageLoop::current()->Quit(); return true; } virtual void OnChannelError() OVERRIDE { - NOTREACHED(); + // The connection should be healthy while the listener is waiting + // message. An error can occur after that because the peer + // process dies. + DCHECK(received_ok_); } static void SendOK(IPC::Sender* sender) { @@ -45,6 +50,9 @@ class ListenerThatExpectsOK : public IPC::Listener { message->WriteString(std::string("OK")); ASSERT_TRUE(sender->Send(message)); } + + private: + bool received_ok_; }; class ListenerThatShouldBeNeverCalled : public IPC::Listener { diff --git a/ipc/mojo/ipc_message_pipe_reader.cc b/ipc/mojo/ipc_message_pipe_reader.cc index 91022ac7f7be7..b0df9976b3c2c 100644 --- a/ipc/mojo/ipc_message_pipe_reader.cc +++ b/ipc/mojo/ipc_message_pipe_reader.cc @@ -67,12 +67,12 @@ void MessagePipeReader::PipeIsReady(MojoResult wait_result) { pipe_wait_id_ = 0; if (wait_result != MOJO_RESULT_OK) { - // FAILED_PRECONDITION happens when the pipe is - // closed before the waiter is scheduled in a backend thread. - if (wait_result != MOJO_RESULT_ABORTED && - wait_result != MOJO_RESULT_FAILED_PRECONDITION) { - DLOG(WARNING) << "Pipe got error from the waiter. Closing: " - << wait_result; + if (wait_result != MOJO_RESULT_ABORTED) { + // FAILED_PRECONDITION happens every time the peer is dead so + // it isn't worth polluting the log message. + DLOG_IF(WARNING, wait_result != MOJO_RESULT_FAILED_PRECONDITION) + << "Pipe got error from the waiter. Closing: " + << wait_result; OnPipeError(wait_result); } diff --git a/jingle/BUILD.gn b/jingle/BUILD.gn index 5b8e96cc569de..f64a764a17ad3 100644 --- a/jingle/BUILD.gn +++ b/jingle/BUILD.gn @@ -4,12 +4,12 @@ import("//build/config/features.gni") -jingle_includes = exec_script("//build/gypi_to_gn.py", - [ rebase_path("jingle.gypi") ], - "scope", - [ "jingle.gypi" ]) - if (enable_webrtc || !is_android) { + jingle_includes = exec_script("//build/gypi_to_gn.py", + [ rebase_path("jingle.gypi") ], + "scope", + [ "jingle.gypi" ]) + # GYP version: jingle/jingle.gyp:jingle_glue static_library("jingle_glue") { sources = jingle_includes.jingle_glue_sources diff --git a/jingle/PRESUBMIT.py b/jingle/PRESUBMIT.py index b356edc6deaa9..9e62e23dfb9a6 100644 --- a/jingle/PRESUBMIT.py +++ b/jingle/PRESUBMIT.py @@ -17,6 +17,6 @@ def GetPreferredTryMasters(project, change): 'mac_chromium_rel_swarming': set(['defaulttests']), }, 'tryserver.chromium.win': { - 'win_chromium_rel': set(['defaulttests']), + 'win_chromium_rel_swarming': set(['defaulttests']), } } diff --git a/jingle/glue/proxy_resolving_client_socket.cc b/jingle/glue/proxy_resolving_client_socket.cc index d7fcb13a990e8..91eb5ae7d9892 100644 --- a/jingle/glue/proxy_resolving_client_socket.cc +++ b/jingle/glue/proxy_resolving_client_socket.cc @@ -12,7 +12,9 @@ #include "net/base/io_buffer.h" #include "net/base/load_flags.h" #include "net/base/net_errors.h" +#include "net/http/http_auth_controller.h" #include "net/http/http_network_session.h" +#include "net/http/proxy_client_socket.h" #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_manager.h" #include "net/url_request/url_request_context.h" @@ -270,6 +272,15 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) { // "address unreachable" error, and will report both of these failures as // ERR_ADDRESS_UNREACHABLE. return net::ERR_ADDRESS_UNREACHABLE; + case net::ERR_PROXY_AUTH_REQUESTED: { + net::ProxyClientSocket* proxy_socket = + static_cast(transport_->socket()); + + if (proxy_socket->GetAuthController()->HaveAuth()) + return proxy_socket->RestartWithAuth(connect_callback_); + + return error; + } default: return error; } @@ -309,7 +320,7 @@ int ProxyResolvingClientSocket::ReconsiderProxyAfterError(int error) { } void ProxyResolvingClientSocket::ReportSuccessfulProxyConnection() { - network_session_->proxy_service()->ReportSuccess(proxy_info_); + network_session_->proxy_service()->ReportSuccess(proxy_info_, NULL); } void ProxyResolvingClientSocket::Disconnect() { diff --git a/media/BUILD.gn b/media/BUILD.gn index 36ad0938fe128..8a5a9c5e0bf61 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -17,12 +17,12 @@ config("media_config") { } if (use_pulseaudio) { defines += [ "USE_PULSEAUDIO" ] - if (linux_link_pulseaudio) { + if (!linux_link_pulseaudio) { defines += [ "DLOPEN_PULSEAUDIO" ] } } if (use_cras) { - defines = [ "USE_CRAS" ] + defines += [ "USE_CRAS" ] } if (enable_mpeg2ts_stream_parser) { defines += [ "ENABLE_MPEG2TS_STREAM_PARSER" ] @@ -66,150 +66,6 @@ if (use_ozone) { component("media") { sources = [ - "base/audio_block_fifo.cc", - "base/audio_block_fifo.h", - "base/audio_buffer.cc", - "base/audio_buffer.h", - "base/audio_buffer_queue.cc", - "base/audio_buffer_queue.h", - "base/audio_capturer_source.h", - "base/audio_buffer_converter.cc", - "base/audio_buffer_converter.h", - "base/audio_converter.cc", - "base/audio_converter.h", - "base/audio_decoder.cc", - "base/audio_decoder.h", - "base/audio_decoder_config.cc", - "base/audio_decoder_config.h", - "base/audio_discard_helper.cc", - "base/audio_discard_helper.h", - "base/audio_fifo.cc", - "base/audio_fifo.h", - "base/audio_hardware_config.cc", - "base/audio_hardware_config.h", - "base/audio_hash.cc", - "base/audio_hash.h", - "base/audio_pull_fifo.cc", - "base/audio_pull_fifo.h", - "base/audio_renderer.cc", - "base/audio_renderer.h", - "base/audio_renderer_mixer.cc", - "base/audio_renderer_mixer.h", - "base/audio_renderer_mixer_input.cc", - "base/audio_renderer_mixer_input.h", - "base/audio_renderer_sink.h", - "base/audio_splicer.cc", - "base/audio_splicer.h", - "base/audio_timestamp_helper.cc", - "base/audio_timestamp_helper.h", - "base/bind_to_current_loop.h", - "base/bit_reader.cc", - "base/bit_reader.h", - "base/bit_reader_core.cc", - "base/bit_reader_core.h", - "base/bitstream_buffer.h", - "base/buffering_state.h", - "base/buffers.h", - "base/byte_queue.cc", - "base/byte_queue.h", - "base/cdm_promise.cc", - "base/cdm_promise.h", - "base/channel_mixer.cc", - "base/channel_mixer.h", - "base/clock.h", - "base/data_buffer.cc", - "base/data_buffer.h", - "base/data_source.cc", - "base/data_source.h", - "base/decoder_buffer.cc", - "base/decoder_buffer.h", - "base/decoder_buffer_queue.cc", - "base/decoder_buffer_queue.h", - "base/decrypt_config.cc", - "base/decrypt_config.h", - "base/decryptor.cc", - "base/decryptor.h", - "base/demuxer.cc", - "base/demuxer.h", - "base/demuxer_stream.cc", - "base/demuxer_stream.h", - "base/djb2.cc", - "base/djb2.h", - "base/filter_collection.cc", - "base/filter_collection.h", - "base/media.cc", - "base/media.h", - "base/media_keys.cc", - "base/media_keys.h", - "base/media_log.cc", - "base/media_log.h", - "base/media_log_event.h", - "base/media_switches.cc", - "base/media_switches.h", - "base/media_win.cc", - "base/multi_channel_resampler.cc", - "base/multi_channel_resampler.h", - "base/pipeline.cc", - "base/pipeline.h", - "base/pipeline_status.h", - "base/player_tracker.cc", - "base/player_tracker.h", - "base/ranges.cc", - "base/ranges.h", - "base/sample_format.cc", - "base/sample_format.h", - "base/scoped_histogram_timer.h", - "base/seekable_buffer.cc", - "base/seekable_buffer.h", - "base/serial_runner.cc", - "base/serial_runner.h", - "base/simd/convert_rgb_to_yuv.h", - "base/simd/convert_rgb_to_yuv_c.cc", - "base/simd/convert_yuv_to_rgb.h", - "base/simd/convert_yuv_to_rgb_c.cc", - "base/simd/filter_yuv.h", - "base/simd/filter_yuv_c.cc", - "base/simd/yuv_to_rgb_table.cc", - "base/simd/yuv_to_rgb_table.h", - "base/sinc_resampler.cc", - "base/sinc_resampler.h", - "base/stream_parser.cc", - "base/stream_parser.h", - "base/stream_parser_buffer.cc", - "base/stream_parser_buffer.h", - "base/text_cue.cc", - "base/text_cue.h", - "base/text_ranges.cc", - "base/text_ranges.h", - "base/text_renderer.cc", - "base/text_renderer.h", - "base/text_track.h", - "base/text_track_config.cc", - "base/text_track_config.h", - "base/time_delta_interpolator.cc", - "base/time_delta_interpolator.h", - "base/time_source.h", - "base/user_input_monitor.cc", - "base/user_input_monitor.h", - "base/user_input_monitor_mac.cc", - "base/user_input_monitor_win.cc", - "base/video_decoder.cc", - "base/video_decoder.h", - "base/video_decoder_config.cc", - "base/video_decoder_config.h", - "base/video_frame.cc", - "base/video_frame.h", - "base/video_frame_pool.cc", - "base/video_frame_pool.h", - "base/video_renderer.cc", - "base/video_renderer.h", - "base/video_rotation.h", - "base/video_util.cc", - "base/video_util.h", - "base/wall_clock_time_source.cc", - "base/wall_clock_time_source.h", - "base/yuv_convert.cc", - "base/yuv_convert.h", "cdm/aes_decryptor.cc", "cdm/aes_decryptor.h", "cdm/json_web_key.cc", @@ -387,13 +243,6 @@ component("media") { if (media_use_ffmpeg) { deps += [ "//third_party/ffmpeg" ] sources += [ - "base/audio_video_metadata_extractor.cc", - "base/audio_video_metadata_extractor.h", - "base/container_names.cc", - "base/container_names.h", - "base/media_file_checker.cc", - "base/media_file_checker.h", - "base/media_posix.cc", "ffmpeg/ffmpeg_common.cc", "ffmpeg/ffmpeg_common.h", "filters/audio_file_reader.cc", @@ -431,14 +280,6 @@ component("media") { deps += [ "//third_party/libvpx" ] } - if (enable_browser_cdms) { - sources += [ - "base/browser_cdm.cc", - "base/browser_cdm.h", - "base/browser_cdm_factory.h", - ] - } - if (!is_android) { sources += [ "filters/opus_audio_decoder.cc", @@ -446,13 +287,6 @@ component("media") { ] } else { sources += [ - "base/android/demuxer_android.h", - "base/android/demuxer_stream_player_params.cc", - "base/android/demuxer_stream_player_params.h", - "base/android/media_player_manager.h", - "base/android/media_resource_getter.cc", - "base/android/media_resource_getter.h", - "base/media_stub.cc", "midi/midi_manager_android.cc", "midi/usb_midi_device_android.cc", "midi/usb_midi_device_android.h", @@ -463,21 +297,16 @@ component("media") { "video/capture/android/video_capture_device_factory_android.cc", "video/capture/android/video_capture_device_factory_android.h", ] - defines += [ "DISABLE_USER_INPUT_MONITOR" ] deps += [ - ":media_android_jni_headers", - ":player_android", - ":video_capture_android_jni_headers", + "//media/base/android", + "//media/base/android:media_jni_headers", + "//media/base/android:video_capture_jni_headers", ] if (!is_android_webview_build) { deps += [ ":media_java" ] } } - if (!is_ios) { - deps += [ "//third_party/libyuv" ] - } - if (use_alsa) { libs += [ "asound" ] defines += [ "USE_ALSA" ] @@ -488,23 +317,6 @@ component("media") { } # A simple WebM encoder for animated avatars on ChromeOS. - if (is_linux) { - if (use_x11) { - configs += [ - "//build/config/linux:x11", - "//build/config/linux:xext", -# TODO(ajwong): Why does xent get a separate thing in //build/config/linux:BUILD.gn -# "//build/config/linux:xdamage", -# "//build/config/linux:xfixes", -# "//build/config/linux:xtst", - ] - sources += [ - "base/user_input_monitor_linux.cc" - ] - } else { - defines += [ "DISABLE_USER_INPUT_MONITOR" ] - } - } if (use_ozone) { # Used for the generated listing header (ui/ozone/platform_list.h) @@ -595,22 +407,6 @@ component("media") { ] } - if (cpu_arch == "x86" || cpu_arch == "x64") { - sources += [ "base/simd/convert_yuv_to_rgb_x86.cc" ] - deps += [ - ":media_yasm", - ":media_mmx", - ":media_sse2", - ] - } - - if (is_linux || is_win) { - sources += [ - "base/keyboard_event_counter.cc", - "base/keyboard_event_counter.h", - ] - } - deps += [ ":shared_memory_support", "//base", @@ -620,7 +416,9 @@ component("media") { "//crypto:platform", # TODO(ajwong): This used to be provided by crypto.gyp via export_dependent_settings "//gpu/command_buffer/common", "//media/audio", + "//media/base", "//skia", + "//third_party/libyuv", "//third_party/opus", "//ui/events:events_base", "//ui/gfx", @@ -631,53 +429,6 @@ component("media") { test("media_unittests") { sources = [ - "base/android/media_codec_bridge_unittest.cc", - "base/android/media_drm_bridge_unittest.cc", - "base/android/media_source_player_unittest.cc", - "base/audio_block_fifo_unittest.cc", - "base/audio_buffer_converter_unittest.cc", - "base/audio_buffer_unittest.cc", - "base/audio_buffer_queue_unittest.cc", - "base/audio_bus_unittest.cc", - "base/audio_converter_unittest.cc", - "base/audio_discard_helper_unittest.cc", - "base/audio_fifo_unittest.cc", - "base/audio_hardware_config_unittest.cc", - "base/audio_hash_unittest.cc", - "base/audio_pull_fifo_unittest.cc", - "base/audio_renderer_mixer_input_unittest.cc", - "base/audio_renderer_mixer_unittest.cc", - "base/audio_splicer_unittest.cc", - "base/audio_timestamp_helper_unittest.cc", - "base/bind_to_current_loop_unittest.cc", - "base/bit_reader_unittest.cc", - "base/callback_holder.h", - "base/callback_holder_unittest.cc", - "base/channel_mixer_unittest.cc", - "base/data_buffer_unittest.cc", - "base/decoder_buffer_queue_unittest.cc", - "base/decoder_buffer_unittest.cc", - "base/djb2_unittest.cc", - "base/gmock_callback_support_unittest.cc", - "base/multi_channel_resampler_unittest.cc", - "base/pipeline_unittest.cc", - "base/ranges_unittest.cc", - "base/run_all_unittests.cc", - "base/scoped_histogram_timer_unittest.cc", - "base/serial_runner_unittest.cc", - "base/seekable_buffer_unittest.cc", - "base/sinc_resampler_unittest.cc", - "base/stream_parser_unittest.cc", - "base/text_ranges_unittest.cc", - "base/text_renderer_unittest.cc", - "base/user_input_monitor_unittest.cc", - "base/vector_math_testing.h", - "base/vector_math_unittest.cc", - "base/video_frame_unittest.cc", - "base/video_frame_pool_unittest.cc", - "base/video_util_unittest.cc", - "base/wall_clock_time_source_unittest.cc", - "base/yuv_convert_unittest.cc", "cdm/aes_decryptor_unittest.cc", "cdm/json_web_key_unittest.cc", "filters/audio_clock_unittest.cc", @@ -726,17 +477,8 @@ test("media_unittests") { "formats/webm/webm_webvtt_parser_unittest.cc", ] - if (media_use_ffmpeg) { - sources += [ - "base/audio_video_metadata_extractor_unittest.cc", - "base/media_file_checker_unittest.cc", - ] - } - if (!is_android) { sources += [ - - "base/container_names_unittest.cc", "ffmpeg/ffmpeg_common_unittest.cc", "filters/audio_decoder_unittest.cc", "filters/audio_file_reader_unittest.cc", @@ -760,10 +502,6 @@ test("media_unittests") { sources += [ "filters/h264_bitstream_buffer_unittest.cc" ] } - if (cpu_arch == "x86" || cpu_arch == "x64") { - sources += [ "base/simd/convert_rgb_to_yuv_unittest.cc" ] - } - if (proprietary_codecs) { sources += [ "filters/ffmpeg_h264_to_annex_b_bitstream_converter_unittest.cc", @@ -771,7 +509,10 @@ test("media_unittests") { "formats/common/stream_parser_test_base.cc", "formats/common/stream_parser_test_base.h", "formats/mp2t/es_adapter_video_unittest.cc", + "formats/mp2t/es_parser_adts_unittest.cc", "formats/mp2t/es_parser_h264_unittest.cc", + "formats/mp2t/es_parser_test_base.cc", + "formats/mp2t/es_parser_test_base.h", "formats/mp2t/mp2t_stream_parser_unittest.cc", "formats/mp4/aac_unittest.cc", "formats/mp4/avc_unittest.cc", @@ -821,12 +562,15 @@ test("media_unittests") { "//base/test:test_support", "//media/audio:unittests", "//media/audio:test_support", + "//media/base:unittests", + "//media/base:test_support", "//skia", # Direct dependency required to inherit config. "//testing/gmock", "//testing/gtest", # TODO(dalecurtis): Port the rest of Widevine stuff. "//third_party/widevine/cdm:version_h", "//ui/gfx:test_support", + "//url", ] if (media_use_ffmpeg) { deps += [ @@ -836,17 +580,9 @@ test("media_unittests") { } test("media_perftests") { - sources = [ - "base/audio_bus_perftest.cc", - "base/audio_converter_perftest.cc", - "base/run_all_perftests.cc", - "base/sinc_resampler_perftest.cc", - "base/vector_math_perftest.cc", - "base/yuv_convert_perftest.cc", - ] + sources = [] if (media_use_ffmpeg) { sources += [ - "base/demuxer_perftest.cc", "filters/pipeline_integration_perftest.cc", "filters/pipeline_integration_test_base.cc", ] @@ -858,6 +594,8 @@ test("media_perftests") { ":test_support", "//base/test:test_support", "//media/audio:test_support", + "//media/base:perftests", + "//media/base:test_support", "//testing/gmock", "//testing/gtest", "//testing/perf", @@ -874,102 +612,8 @@ test("media_perftests") { } } -if (cpu_arch == "x86" || cpu_arch == "x64") { - source_set("media_mmx") { - sources = [ "base/simd/filter_yuv_mmx.cc" ] - configs += [ ":media_config" ] - cflags = [ "-mmmx" ] - } - - source_set("media_sse2") { - sources = [ - "base/simd/convert_rgb_to_yuv_sse2.cc", - "base/simd/convert_rgb_to_yuv_ssse3.cc", - "base/simd/filter_yuv_sse2.cc", - ] - configs += [ ":media_config" ] - cflags = [ "-msse2" ] - } - - import("//third_party/yasm/yasm_assemble.gni") - yasm_assemble("media_yasm") { - sources = [ - "base/simd/convert_rgb_to_yuv_ssse3.asm", - "base/simd/convert_yuv_to_rgb_mmx.asm", - "base/simd/convert_yuv_to_rgb_sse.asm", - "base/simd/convert_yuva_to_argb_mmx.asm", - "base/simd/empty_register_state_mmx.asm", - "base/simd/linear_scale_yuv_to_rgb_mmx.asm", - "base/simd/linear_scale_yuv_to_rgb_sse.asm", - "base/simd/scale_yuv_to_rgb_mmx.asm", - "base/simd/scale_yuv_to_rgb_sse.asm", - ] - - # TODO(ajwong): Only export if shared_library build... - yasm_flags = [ - "-DCHROMIUM", - "-DEXPORT_SYMBOLS", - # In addition to the same path as source asm, let yasm %include - # search path be relative to src/ per Chromium policy. - "-I", rebase_path("..", root_build_dir), - ] - - inputs = [ - "//third_party/x86inc/x86inc.asm", - "base/simd/convert_rgb_to_yuv_ssse3.inc", - "base/simd/convert_yuv_to_rgb_mmx.inc", - "base/simd/convert_yuva_to_argb_mmx.inc", - "base/simd/linear_scale_yuv_to_rgb_mmx.inc", - "base/simd/media_export.asm", - "base/simd/scale_yuv_to_rgb_mmx.inc", - ] - - if (cpu_arch == "x86") { - yasm_flags += [ "-DARCH_X86_32" ] - } else if (cpu_arch == "x64") { - yasm_flags += [ "-DARCH_X86_64" ] - sources += [ - "base/simd/linear_scale_yuv_to_rgb_mmx_x64.asm", - "base/simd/scale_yuv_to_rgb_sse2_x64.asm", - ] - } - - if (is_mac) { - yasm_flags += [ - "-DPREFIX", - "-DMACHO", - ] - } else { - if (is_posix) { - yasm_flags += [ "-DELF" ] - if (cpu_arch == "x64") { - # TODO(ajwong): Why isn't this true in mac? - yasm_flags += [ "-DPIC" ] - } - } - } - } -} - source_set("test_support") { sources = [ - "base/fake_audio_render_callback.cc", - "base/fake_audio_render_callback.h", - "base/fake_audio_renderer_sink.cc", - "base/fake_audio_renderer_sink.h", - "base/fake_text_track_stream.cc", - "base/fake_text_track_stream.h", - "base/gmock_callback_support.h", - "base/mock_audio_renderer_sink.cc", - "base/mock_audio_renderer_sink.h", - "base/mock_demuxer_host.cc", - "base/mock_demuxer_host.h", - "base/mock_filters.cc", - "base/mock_filters.h", - "base/test_data_util.cc", - "base/test_data_util.h", - "base/test_helpers.cc", - "base/test_helpers.h", "filters/clockless_video_frame_scheduler.cc", "filters/clockless_video_frame_scheduler.h", "filters/mock_gpu_video_accelerator_factories.cc", @@ -1035,6 +679,7 @@ if (media_use_ffmpeg) { ":test_support", "//base/test:test_support", "//media/audio:test_support", + "//media/base:test_support", "//testing/gmock", "//testing/gtest", "//third_party/ffmpeg", @@ -1085,67 +730,6 @@ if (use_x11) { if (is_android) { import("//build/config/android/rules.gni") - source_set("player_android") { - configs += [ ":media_config" ] - sources = [ - "base/android/audio_decoder_job.cc", - "base/android/audio_decoder_job.h", - "base/android/browser_cdm_factory_android.cc", - "base/android/media_codec_bridge.cc", - "base/android/media_codec_bridge.h", - "base/android/media_decoder_job.cc", - "base/android/media_decoder_job.h", - "base/android/media_drm_bridge.cc", - "base/android/media_drm_bridge.h", - "base/android/media_jni_registrar.cc", - "base/android/media_jni_registrar.h", - "base/android/media_player_android.cc", - "base/android/media_player_android.h", - "base/android/media_player_bridge.cc", - "base/android/media_player_bridge.h", - "base/android/media_player_listener.cc", - "base/android/media_player_listener.h", - "base/android/media_source_player.cc", - "base/android/media_source_player.h", - "base/android/video_decoder_job.cc", - "base/android/video_decoder_job.h", - "base/android/webaudio_media_codec_bridge.cc", - "base/android/webaudio_media_codec_bridge.h", - "base/android/webaudio_media_codec_info.h", - ] - - deps = [ - ":media_android_jni_headers", - "//base", - "//third_party/widevine/cdm:version_h", - "//ui/gl", - "//url" - ] - } - - generate_jni("media_android_jni_headers") { - sources = [ - "base/android/java/src/org/chromium/media/AudioManagerAndroid.java", - "base/android/java/src/org/chromium/media/AudioRecordInput.java", - "base/android/java/src/org/chromium/media/MediaCodecBridge.java", - "base/android/java/src/org/chromium/media/MediaDrmBridge.java", - "base/android/java/src/org/chromium/media/MediaPlayerBridge.java", - "base/android/java/src/org/chromium/media/MediaPlayerListener.java", - "base/android/java/src/org/chromium/media/UsbMidiDeviceAndroid.java", - "base/android/java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java", - "base/android/java/src/org/chromium/media/WebAudioMediaCodecBridge.java", - ] - jni_package = "media" - } - - generate_jni("video_capture_android_jni_headers") { - sources = [ - "base/android/java/src/org/chromium/media/VideoCapture.java", - "base/android/java/src/org/chromium/media/VideoCaptureFactory.java", - ] - jni_package = "media" - } - android_library("media_java") { srcjar_deps = [ ":media_android_imageformat_list", @@ -1162,6 +746,4 @@ if (is_android) { ] package_name = "org/chromium/media" } - - # TODO(dalecurtis): Finish media_unittests_apk and media_perftests_apk. } diff --git a/media/audio/BUILD.gn b/media/audio/BUILD.gn index 835c1357d5bc1..e6ac9577f6e2d 100644 --- a/media/audio/BUILD.gn +++ b/media/audio/BUILD.gn @@ -164,7 +164,7 @@ source_set("audio") { "android/opensles_wrapper.cc", ] deps += [ - "//media:media_android_jni_headers", + "//media/base/android:media_jni_headers", ] } diff --git a/media/audio/android/audio_android_unittest.cc b/media/audio/android/audio_android_unittest.cc index a356d9c25deb6..177953d88f1c7 100644 --- a/media/audio/android/audio_android_unittest.cc +++ b/media/audio/android/audio_android_unittest.cc @@ -518,7 +518,7 @@ class AudioAndroidOutputTest : public testing::Test { EXPECT_GE(average_time_between_callbacks_ms, 0.70 * expected_time_between_callbacks_ms); EXPECT_LE(average_time_between_callbacks_ms, - 1.35 * expected_time_between_callbacks_ms); + 1.50 * expected_time_between_callbacks_ms); } void GetDefaultOutputStreamParameters() { @@ -825,7 +825,7 @@ TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacks) { // select a 10ms buffer size instead of the default size and to open up the // device in mono. // TODO(henrika): possibly add support for more variations. -TEST_F(AudioAndroidOutputTest, DISABLED_StartOutputStreamCallbacksNonDefaultParameters) { +TEST_F(AudioAndroidOutputTest, StartOutputStreamCallbacksNonDefaultParameters) { GetDefaultOutputStreamParametersOnAudioThread(); AudioParameters params(audio_output_parameters().format(), CHANNEL_LAYOUT_MONO, diff --git a/media/audio/android/audio_manager_android.cc b/media/audio/android/audio_manager_android.cc index 48f203ab74e8b..8981a753c94da 100644 --- a/media/audio/android/audio_manager_android.cc +++ b/media/audio/android/audio_manager_android.cc @@ -4,6 +4,9 @@ #include "media/audio/android/audio_manager_android.h" +#include +#include + #include "base/android/build_info.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" @@ -246,8 +249,19 @@ AudioInputStream* AudioManagerAndroid::MakeLowLatencyInputStream( DVLOG(1) << "Creating AudioRecordInputStream"; return new AudioRecordInputStream(this, params); } + + // TODO(xingnan): Crash will happen in the openSL ES library on some IA + // devices like ZTE Geek V975, Use AudioRecordInputStream path instead as + // a workaround. Will fall back after the fix of the bug. +#if defined(ARCH_CPU_X86) + if (base::android::BuildInfo::GetInstance()->sdk_int() < 16) + return NULL; + DVLOG(1) << "Creating AudioRecordInputStream"; + return new AudioRecordInputStream(this, params); +#else DVLOG(1) << "Creating OpenSLESInputStream"; return new OpenSLESInputStream(this, params); +#endif } // static diff --git a/media/audio/audio_input_controller.cc b/media/audio/audio_input_controller.cc index 490c62b3c16df..72fb83b49ae26 100644 --- a/media/audio/audio_input_controller.cc +++ b/media/audio/audio_input_controller.cc @@ -8,6 +8,7 @@ #include "base/strings/stringprintf.h" #include "base/threading/thread_restrictions.h" #include "base/time/time.h" +#include "media/audio/audio_parameters.h" #include "media/base/limits.h" #include "media/base/scoped_histogram_timer.h" #include "media/base/user_input_monitor.h" @@ -47,6 +48,27 @@ const int kPowerMonitorLogIntervalSeconds = 5; #endif } +// Used to log the result of capture startup. +// This was previously logged as a boolean with only the no callback and OK +// options. The enum order is kept to ensure backwards compatibility. +// Elements in this enum should not be deleted or rearranged; the only +// permitted operation is to add new elements before CAPTURE_STARTUP_RESULT_MAX +// and update CAPTURE_STARTUP_RESULT_MAX. +enum CaptureStartupResult { + CAPTURE_STARTUP_NO_DATA_CALLBACK = 0, + CAPTURE_STARTUP_OK = 1, + CAPTURE_STARTUP_CREATE_STREAM_FAILED = 2, + CAPTURE_STARTUP_OPEN_STREAM_FAILED = 3, + CAPTURE_STARTUP_RESULT_MAX = CAPTURE_STARTUP_OPEN_STREAM_FAILED +}; + +void LogCaptureStartupResult(CaptureStartupResult result) { + UMA_HISTOGRAM_ENUMERATION("Media.AudioInputControllerCaptureStartupSuccess", + result, + CAPTURE_STARTUP_RESULT_MAX + 1); + +} + namespace media { // static @@ -63,6 +85,10 @@ AudioInputController::AudioInputController(EventHandler* handler, sync_writer_(sync_writer), max_volume_(0.0), user_input_monitor_(user_input_monitor), +#if defined(AUDIO_POWER_MONITORING) + log_silence_state_(false), + silence_state_(SILENCE_STATE_NO_MEASUREMENT), +#endif prev_key_down_count_(0) { DCHECK(creator_task_runner_.get()); } @@ -94,9 +120,13 @@ scoped_refptr AudioInputController::Create( // Create and open a new audio input stream from the existing // audio-device thread. - if (!controller->task_runner_->PostTask(FROM_HERE, - base::Bind(&AudioInputController::DoCreate, controller, - base::Unretained(audio_manager), params, device_id))) { + if (!controller->task_runner_->PostTask( + FROM_HERE, + base::Bind(&AudioInputController::DoCreate, + controller, + base::Unretained(audio_manager), + params, + device_id))) { controller = NULL; } @@ -125,9 +155,13 @@ scoped_refptr AudioInputController::CreateLowLatency( // Create and open a new audio input stream from the existing // audio-device thread. Use the provided audio-input device. - if (!controller->task_runner_->PostTask(FROM_HERE, - base::Bind(&AudioInputController::DoCreate, controller, - base::Unretained(audio_manager), params, device_id))) { + if (!controller->task_runner_->PostTask( + FROM_HERE, + base::Bind(&AudioInputController::DoCreateForLowLatency, + controller, + base::Unretained(audio_manager), + params, + device_id))) { controller = NULL; } @@ -157,8 +191,9 @@ scoped_refptr AudioInputController::CreateForStream( // mirroring use case only. if (!controller->task_runner_->PostTask( FROM_HERE, - base::Bind(&AudioInputController::DoCreateForStream, controller, - stream, false))) { + base::Bind(&AudioInputController::DoCreateForStream, + controller, + stream))) { controller = NULL; } @@ -193,28 +228,46 @@ void AudioInputController::DoCreate(AudioManager* audio_manager, const std::string& device_id) { DCHECK(task_runner_->BelongsToCurrentThread()); SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioInputController.CreateTime"); + if (handler_) + handler_->OnLog(this, "AIC::DoCreate"); #if defined(AUDIO_POWER_MONITORING) // Create the audio (power) level meter given the provided audio parameters. // An AudioBus is also needed to wrap the raw data buffer from the native // layer to match AudioPowerMonitor::Scan(). // TODO(henrika): Remove use of extra AudioBus. See http://crbug.com/375155. + last_audio_level_log_time_ = base::TimeTicks::Now(); audio_level_.reset(new media::AudioPowerMonitor( params.sample_rate(), TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMilliseconds))); audio_params_ = params; + silence_state_ = SILENCE_STATE_NO_MEASUREMENT; #endif // TODO(miu): See TODO at top of file. Until that's resolved, assume all // platform audio input requires the |no_data_timer_| be used to auto-detect // errors. In reality, probably only Windows needs to be treated as // unreliable here. - DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id), - true); + DoCreateForStream(audio_manager->MakeAudioInputStream(params, device_id)); +} + +void AudioInputController::DoCreateForLowLatency(AudioManager* audio_manager, + const AudioParameters& params, + const std::string& device_id) { + DCHECK(task_runner_->BelongsToCurrentThread()); + +#if defined(AUDIO_POWER_MONITORING) + // We only log silence state UMA stats for low latency mode and if we use a + // real device. + if (params.format() != AudioParameters::AUDIO_FAKE) + log_silence_state_ = true; +#endif + + DoCreate(audio_manager, params, device_id); } void AudioInputController::DoCreateForStream( - AudioInputStream* stream_to_control, bool enable_nodata_timer) { + AudioInputStream* stream_to_control) { DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(!stream_); @@ -223,6 +276,7 @@ void AudioInputController::DoCreateForStream( if (!stream_) { if (handler_) handler_->OnError(this, STREAM_CREATE_ERROR); + LogCaptureStartupResult(CAPTURE_STARTUP_CREATE_STREAM_FAILED); return; } @@ -231,29 +285,24 @@ void AudioInputController::DoCreateForStream( stream_ = NULL; if (handler_) handler_->OnError(this, STREAM_OPEN_ERROR); + LogCaptureStartupResult(CAPTURE_STARTUP_OPEN_STREAM_FAILED); return; } DCHECK(!no_data_timer_.get()); + // Create the data timer which will call FirstCheckForNoData(). The timer + // is started in DoRecord() and restarted in each DoCheckForNoData() + // callback. // The timer is enabled for logging purposes. The NO_DATA_ERROR triggered // from the timer must be ignored by the EventHandler. // TODO(henrika): remove usage of timer when it has been verified on Canary // that we are safe doing so. Goal is to get rid of |no_data_timer_| and // everything that is tied to it. crbug.com/357569. - enable_nodata_timer = true; - - if (enable_nodata_timer) { - // Create the data timer which will call FirstCheckForNoData(). The timer - // is started in DoRecord() and restarted in each DoCheckForNoData() - // callback. - no_data_timer_.reset(new base::Timer( - FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds), - base::Bind(&AudioInputController::FirstCheckForNoData, - base::Unretained(this)), false)); - } else { - DVLOG(1) << "Disabled: timer check for no data."; - } + no_data_timer_.reset(new base::Timer( + FROM_HERE, base::TimeDelta::FromSeconds(kTimerInitialIntervalSeconds), + base::Bind(&AudioInputController::FirstCheckForNoData, + base::Unretained(this)), false)); state_ = CREATED; if (handler_) @@ -277,6 +326,9 @@ void AudioInputController::DoRecord() { state_ = RECORDING; } + if (handler_) + handler_->OnLog(this, "AIC::DoRecord"); + if (no_data_timer_) { // Start the data timer. Once |kTimerResetIntervalSeconds| have passed, // a callback to FirstCheckForNoData() is made. @@ -295,6 +347,9 @@ void AudioInputController::DoClose() { if (state_ == CLOSED) return; + if (handler_) + handler_->OnLog(this, "AIC::DoClose"); + // Delete the timer on the same thread that created it. no_data_timer_.reset(); @@ -307,6 +362,13 @@ void AudioInputController::DoClose() { if (user_input_monitor_) user_input_monitor_->DisableKeyPressMonitoring(); +#if defined(AUDIO_POWER_MONITORING) + // Send UMA stats if enabled. + if (log_silence_state_) + LogSilenceState(silence_state_); + log_silence_state_ = false; +#endif + state_ = CLOSED; } @@ -352,8 +414,14 @@ void AudioInputController::DoSetAutomaticGainControl(bool enabled) { void AudioInputController::FirstCheckForNoData() { DCHECK(task_runner_->BelongsToCurrentThread()); - UMA_HISTOGRAM_BOOLEAN("Media.AudioInputControllerCaptureStartupSuccess", - GetDataIsActive()); + LogCaptureStartupResult(GetDataIsActive() ? + CAPTURE_STARTUP_OK : + CAPTURE_STARTUP_NO_DATA_CALLBACK); + if (handler_) { + handler_->OnLog(this, GetDataIsActive() ? + "AIC::FirstCheckForNoData => data is active" : + "AIC::FirstCheckForNoData => data is NOT active"); + } DoCheckForNoData(); } @@ -476,8 +544,9 @@ void AudioInputController::DoLogAudioLevel(float level_dbfs) { static const float kSilenceThresholdDBFS = -72.24719896f; if (level_dbfs < kSilenceThresholdDBFS) log_string += " <=> no audio input!"; - handler_->OnLog(this, log_string); + + UpdateSilenceState(level_dbfs < kSilenceThresholdDBFS); #endif } @@ -509,4 +578,34 @@ bool AudioInputController::GetDataIsActive() { return (base::subtle::Acquire_Load(&data_is_active_) != false); } +#if defined(AUDIO_POWER_MONITORING) +void AudioInputController::UpdateSilenceState(bool silence) { + if (silence) { + if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) { + silence_state_ = SILENCE_STATE_ONLY_SILENCE; + } else if (silence_state_ == SILENCE_STATE_ONLY_AUDIO) { + silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE; + } else { + DCHECK(silence_state_ == SILENCE_STATE_ONLY_SILENCE || + silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE); + } + } else { + if (silence_state_ == SILENCE_STATE_NO_MEASUREMENT) { + silence_state_ = SILENCE_STATE_ONLY_AUDIO; + } else if (silence_state_ == SILENCE_STATE_ONLY_SILENCE) { + silence_state_ = SILENCE_STATE_AUDIO_AND_SILENCE; + } else { + DCHECK(silence_state_ == SILENCE_STATE_ONLY_AUDIO || + silence_state_ == SILENCE_STATE_AUDIO_AND_SILENCE); + } + } +} + +void AudioInputController::LogSilenceState(SilenceState value) { + UMA_HISTOGRAM_ENUMERATION("Media.AudioInputControllerSessionSilenceReport", + value, + SILENCE_STATE_MAX + 1); +} +#endif + } // namespace media diff --git a/media/audio/audio_input_controller.h b/media/audio/audio_input_controller.h index f2771c7e9c2bd..691f1afb05bb7 100644 --- a/media/audio/audio_input_controller.h +++ b/media/audio/audio_input_controller.h @@ -191,11 +191,11 @@ class MEDIA_EXPORT AudioInputController SyncWriter* sync_writer, UserInputMonitor* user_input_monitor); - // Factory method for creating an AudioInputController for low-latency mode, - // taking ownership of |stream|. The stream will be opened on the audio - // thread, and when that is done, the event handler will receive an - // OnCreated() call from that same thread. |user_input_monitor| is used for - // typing detection and can be NULL. + // Factory method for creating an AudioInputController with an existing + // |stream| for low-latency mode, taking ownership of |stream|. The stream + // will be opened on the audio thread, and when that is done, the event + // handler will receive an OnCreated() call from that same thread. + // |user_input_monitor| is used for typing detection and can be NULL. static scoped_refptr CreateForStream( const scoped_refptr& task_runner, EventHandler* event_handler, @@ -246,16 +246,39 @@ class MEDIA_EXPORT AudioInputController CLOSED }; +#if defined(AUDIO_POWER_MONITORING) + // Used to log a silence report (see OnData). + // Elements in this enum should not be deleted or rearranged; the only + // permitted operation is to add new elements before SILENCE_STATE_MAX and + // update SILENCE_STATE_MAX. + // Possible silence state transitions: + // SILENCE_STATE_AUDIO_AND_SILENCE + // ^ ^ + // SILENCE_STATE_ONLY_AUDIO SILENCE_STATE_ONLY_SILENCE + // ^ ^ + // SILENCE_STATE_NO_MEASUREMENT + enum SilenceState { + SILENCE_STATE_NO_MEASUREMENT = 0, + SILENCE_STATE_ONLY_AUDIO = 1, + SILENCE_STATE_ONLY_SILENCE = 2, + SILENCE_STATE_AUDIO_AND_SILENCE = 3, + SILENCE_STATE_MAX = SILENCE_STATE_AUDIO_AND_SILENCE + }; +#endif + AudioInputController(EventHandler* handler, SyncWriter* sync_writer, UserInputMonitor* user_input_monitor); virtual ~AudioInputController(); // Methods called on the audio thread (owned by the AudioManager). - void DoCreate(AudioManager* audio_manager, const AudioParameters& params, + void DoCreate(AudioManager* audio_manager, + const AudioParameters& params, const std::string& device_id); - void DoCreateForStream(AudioInputStream* stream_to_control, - bool enable_nodata_timer); + void DoCreateForLowLatency(AudioManager* audio_manager, + const AudioParameters& params, + const std::string& device_id); + void DoCreateForStream(AudioInputStream* stream_to_control); void DoRecord(); void DoClose(); void DoReportError(); @@ -278,6 +301,15 @@ class MEDIA_EXPORT AudioInputController void SetDataIsActive(bool enabled); bool GetDataIsActive(); +#if defined(AUDIO_POWER_MONITORING) + // Updates the silence state, see enum SilenceState above for state + // transitions. + void UpdateSilenceState(bool silence); + + // Logs the silence state as UMA stat. + void LogSilenceState(SilenceState value); +#endif + // Gives access to the task runner of the creating thread. scoped_refptr creator_task_runner_; @@ -327,6 +359,12 @@ class MEDIA_EXPORT AudioInputController // We need these to be able to feed data to the AudioPowerMonitor. media::AudioParameters audio_params_; base::TimeTicks last_audio_level_log_time_; + + // Whether the silence state should sent as UMA stat. + bool log_silence_state_; + + // The silence report sent as UMA stat at the end of a session. + SilenceState silence_state_; #endif size_t prev_key_down_count_; diff --git a/media/audio/mac/audio_low_latency_input_mac.cc b/media/audio/mac/audio_low_latency_input_mac.cc index f1dbdf786fb29..3d95f90c8ab11 100644 --- a/media/audio/mac/audio_low_latency_input_mac.cc +++ b/media/audio/mac/audio_low_latency_input_mac.cc @@ -165,23 +165,6 @@ bool AUAudioInputStream::Open() { return false; } - // Register the input procedure for the AUHAL. - // This procedure will be called when the AUHAL has received new data - // from the input device. - AURenderCallbackStruct callback; - callback.inputProc = InputProc; - callback.inputProcRefCon = this; - result = AudioUnitSetProperty(audio_unit_, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Global, - 0, - &callback, - sizeof(callback)); - if (result) { - HandleError(result); - return false; - } - // Set up the the desired (output) format. // For obtaining input from a device, the device format is always expressed // on the output scope of the AUHAL's Element 1. @@ -229,6 +212,23 @@ bool AUAudioInputStream::Open() { } } + // Register the input procedure for the AUHAL. + // This procedure will be called when the AUHAL has received new data + // from the input device. + AURenderCallbackStruct callback; + callback.inputProc = InputProc; + callback.inputProcRefCon = this; + result = AudioUnitSetProperty(audio_unit_, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + 0, + &callback, + sizeof(callback)); + if (result) { + HandleError(result); + return false; + } + // Finally, initialize the audio unit and ensure that it is ready to render. // Allocates memory according to the maximum number of audio frames // it can produce in response to a single render call. @@ -299,10 +299,14 @@ void AUAudioInputStream::Close() { } if (audio_unit_) { // Deallocate the audio unit’s resources. - AudioUnitUninitialize(audio_unit_); + OSStatus result = AudioUnitUninitialize(audio_unit_); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioUnitUninitialize() failed."; + + result = AudioComponentInstanceDispose(audio_unit_); + OSSTATUS_DLOG_IF(ERROR, result != noErr, result) + << "AudioComponentInstanceDispose() failed."; - // Terminates our connection to the AUHAL component. - CloseComponent(audio_unit_); audio_unit_ = 0; } diff --git a/media/audio/pulse/pulse_input.cc b/media/audio/pulse/pulse_input.cc index 3509d273bcfe6..4976b5610eda3 100644 --- a/media/audio/pulse/pulse_input.cc +++ b/media/audio/pulse/pulse_input.cc @@ -281,9 +281,6 @@ void PulseAudioInputStream::ReadData() { hardware_delay += fifo_.GetAvailableFrames() * params_.GetBytesPerFrame(); callback_->OnData(this, audio_bus, hardware_delay, normalized_volume); - // TODO(xians): Remove once PPAPI is using circular buffers. - DVLOG(1) << "OnData is being called consecutively, sleep 5ms to " - << "wait until render consumes the data"; base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(5)); } diff --git a/media/audio/sample_rates.cc b/media/audio/sample_rates.cc index 7fa62a79ed152..3757f8317ac3c 100644 --- a/media/audio/sample_rates.cc +++ b/media/audio/sample_rates.cc @@ -44,6 +44,9 @@ bool ToAudioSampleRate(int sample_rate, AudioSampleRate* asr) { case 192000: *asr = k192000Hz; return true; + case 24000: + *asr = k24000Hz; + return true; } return false; } diff --git a/media/audio/sample_rates.h b/media/audio/sample_rates.h index 482ec0fdc8b2f..d2834e74f26cc 100644 --- a/media/audio/sample_rates.h +++ b/media/audio/sample_rates.h @@ -23,8 +23,9 @@ enum AudioSampleRate { k88200Hz = 8, k176400Hz = 9, k192000Hz = 10, + k24000Hz = 11, // Must always equal the largest value ever reported: - kAudioSampleRateMax = k192000Hz, + kAudioSampleRateMax = k24000Hz, }; // Helper method to convert integral values to their respective enum values, diff --git a/media/audio/win/audio_device_listener_win.cc b/media/audio/win/audio_device_listener_win.cc index d7e017cbd0311..ee2b903583f4b 100644 --- a/media/audio/win/audio_device_listener_win.cc +++ b/media/audio/win/audio_device_listener_win.cc @@ -125,9 +125,6 @@ STDMETHODIMP AudioDeviceListenerWin::OnDeviceRemoved(LPCWSTR device_id) { STDMETHODIMP AudioDeviceListenerWin::OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) { - if (new_state != DEVICE_STATE_ACTIVE && new_state != DEVICE_STATE_NOTPRESENT) - return S_OK; - base::SystemMonitor* monitor = base::SystemMonitor::Get(); if (monitor) monitor->ProcessDevicesChanged(base::SystemMonitor::DEVTYPE_AUDIO_CAPTURE); diff --git a/media/base/BUILD.gn b/media/base/BUILD.gn new file mode 100644 index 0000000000000..954b9a360c571 --- /dev/null +++ b/media/base/BUILD.gn @@ -0,0 +1,430 @@ +# 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/config/android/config.gni") +import("//build/config/arm.gni") +import("//build/config/ui.gni") +import("//build/config/linux/pkg_config.gni") +import("//media/media_options.gni") + +source_set("base") { + sources = [ + "audio_block_fifo.cc", + "audio_block_fifo.h", + "audio_buffer.cc", + "audio_buffer.h", + "audio_buffer_queue.cc", + "audio_buffer_queue.h", + "audio_capturer_source.h", + "audio_buffer_converter.cc", + "audio_buffer_converter.h", + "audio_converter.cc", + "audio_converter.h", + "audio_decoder.cc", + "audio_decoder.h", + "audio_decoder_config.cc", + "audio_decoder_config.h", + "audio_discard_helper.cc", + "audio_discard_helper.h", + "audio_fifo.cc", + "audio_fifo.h", + "audio_hardware_config.cc", + "audio_hardware_config.h", + "audio_hash.cc", + "audio_hash.h", + "audio_pull_fifo.cc", + "audio_pull_fifo.h", + "audio_renderer.cc", + "audio_renderer.h", + "audio_renderer_mixer.cc", + "audio_renderer_mixer.h", + "audio_renderer_mixer_input.cc", + "audio_renderer_mixer_input.h", + "audio_renderer_sink.h", + "audio_splicer.cc", + "audio_splicer.h", + "audio_timestamp_helper.cc", + "audio_timestamp_helper.h", + "bind_to_current_loop.h", + "bit_reader.cc", + "bit_reader.h", + "bit_reader_core.cc", + "bit_reader_core.h", + "bitstream_buffer.h", + "buffering_state.h", + "buffers.h", + "byte_queue.cc", + "byte_queue.h", + "cdm_promise.cc", + "cdm_promise.h", + "channel_mixer.cc", + "channel_mixer.h", + "clock.h", + "data_buffer.cc", + "data_buffer.h", + "data_source.cc", + "data_source.h", + "decoder_buffer.cc", + "decoder_buffer.h", + "decoder_buffer_queue.cc", + "decoder_buffer_queue.h", + "decrypt_config.cc", + "decrypt_config.h", + "decryptor.cc", + "decryptor.h", + "demuxer.cc", + "demuxer.h", + "demuxer_stream.cc", + "demuxer_stream.h", + "djb2.cc", + "djb2.h", + "filter_collection.cc", + "filter_collection.h", + "media.cc", + "media.h", + "media_keys.cc", + "media_keys.h", + "media_log.cc", + "media_log.h", + "media_log_event.h", + "media_switches.cc", + "media_switches.h", + "multi_channel_resampler.cc", + "multi_channel_resampler.h", + "pipeline.cc", + "pipeline.h", + "pipeline_status.h", + "player_tracker.cc", + "player_tracker.h", + "ranges.cc", + "ranges.h", + "sample_format.cc", + "sample_format.h", + "scoped_histogram_timer.h", + "seekable_buffer.cc", + "seekable_buffer.h", + "serial_runner.cc", + "serial_runner.h", + "simd/convert_rgb_to_yuv.h", + "simd/convert_rgb_to_yuv_c.cc", + "simd/convert_yuv_to_rgb.h", + "simd/convert_yuv_to_rgb_c.cc", + "simd/filter_yuv.h", + "simd/filter_yuv_c.cc", + "simd/yuv_to_rgb_table.cc", + "simd/yuv_to_rgb_table.h", + "sinc_resampler.cc", + "sinc_resampler.h", + "stream_parser.cc", + "stream_parser.h", + "stream_parser_buffer.cc", + "stream_parser_buffer.h", + "text_cue.cc", + "text_cue.h", + "text_ranges.cc", + "text_ranges.h", + "text_renderer.cc", + "text_renderer.h", + "text_track.h", + "text_track_config.cc", + "text_track_config.h", + "time_delta_interpolator.cc", + "time_delta_interpolator.h", + "time_source.h", + "user_input_monitor.cc", + "user_input_monitor.h", + "video_decoder.cc", + "video_decoder.h", + "video_decoder_config.cc", + "video_decoder_config.h", + "video_frame.cc", + "video_frame.h", + "video_frame_pool.cc", + "video_frame_pool.h", + "video_renderer.cc", + "video_renderer.h", + "video_rotation.h", + "video_util.cc", + "video_util.h", + "wall_clock_time_source.cc", + "wall_clock_time_source.h", + "yuv_convert.cc", + "yuv_convert.h", + ] + defines = [] + deps = [ "//skia" ] + libs = [] + configs += [ "//media:media_config" ] + + if (media_use_ffmpeg) { + sources += [ + "audio_video_metadata_extractor.cc", + "audio_video_metadata_extractor.h", + "container_names.cc", + "container_names.h", + "media_file_checker.cc", + "media_file_checker.h", + ] + deps += [ "//third_party/ffmpeg" ] + } + + if (enable_browser_cdms) { + sources += [ + "browser_cdm.cc", + "browser_cdm.h", + "browser_cdm_factory.h", + ] + } + + if (is_android) { + sources += [ "media_stub.cc" ] + } else if (is_win) { + sources += [ "media_win.cc" ] + } else if (is_posix) { + sources += [ "media_posix.cc" ] + } + + if (is_linux && use_x11) { + configs += [ + "//build/config/linux:x11", + "//build/config/linux:xext", + # TODO(ajwong): Why does xent get a separate thing in //build/config/linux:BUILD.gn + # "//build/config/linux:xdamage", + # "//build/config/linux:xfixes", + # "//build/config/linux:xtst", + ] + sources += [ "user_input_monitor_linux.cc" ] + } else if (is_mac) { + sources += [ "user_input_monitor_mac.cc" ] + } else if (is_win) { + sources += [ "user_input_monitor_win.cc" ] + } else { + defines += [ "DISABLE_USER_INPUT_MONITOR" ] + } + + if (cpu_arch == "x86" || cpu_arch == "x64") { + sources += [ "simd/convert_yuv_to_rgb_x86.cc" ] + deps += [ + ":media_yasm", + ":media_mmx", + ":media_sse2", + ] + } + + if (is_linux || is_win) { + sources += [ + "keyboard_event_counter.cc", + "keyboard_event_counter.h", + ] + } +} + +source_set("test_support") { + sources = [ + "fake_audio_render_callback.cc", + "fake_audio_render_callback.h", + "fake_audio_renderer_sink.cc", + "fake_audio_renderer_sink.h", + "fake_text_track_stream.cc", + "fake_text_track_stream.h", + "gmock_callback_support.h", + "mock_audio_renderer_sink.cc", + "mock_audio_renderer_sink.h", + "mock_demuxer_host.cc", + "mock_demuxer_host.h", + "mock_filters.cc", + "mock_filters.h", + "test_data_util.cc", + "test_data_util.h", + "test_helpers.cc", + "test_helpers.h", + ] + configs += [ "//media:media_config" ] + deps = [ "//testing/gmock" ] +} + +source_set("unittests") { + sources = [ + "audio_block_fifo_unittest.cc", + "audio_buffer_converter_unittest.cc", + "audio_buffer_unittest.cc", + "audio_buffer_queue_unittest.cc", + "audio_bus_unittest.cc", + "audio_converter_unittest.cc", + "audio_discard_helper_unittest.cc", + "audio_fifo_unittest.cc", + "audio_hardware_config_unittest.cc", + "audio_hash_unittest.cc", + "audio_pull_fifo_unittest.cc", + "audio_renderer_mixer_input_unittest.cc", + "audio_renderer_mixer_unittest.cc", + "audio_splicer_unittest.cc", + "audio_timestamp_helper_unittest.cc", + "bind_to_current_loop_unittest.cc", + "bit_reader_unittest.cc", + "callback_holder.h", + "callback_holder_unittest.cc", + "channel_mixer_unittest.cc", + "data_buffer_unittest.cc", + "decoder_buffer_queue_unittest.cc", + "decoder_buffer_unittest.cc", + "djb2_unittest.cc", + "gmock_callback_support_unittest.cc", + "multi_channel_resampler_unittest.cc", + "pipeline_unittest.cc", + "ranges_unittest.cc", + "run_all_unittests.cc", + "scoped_histogram_timer_unittest.cc", + "serial_runner_unittest.cc", + "seekable_buffer_unittest.cc", + "sinc_resampler_unittest.cc", + "stream_parser_unittest.cc", + "text_ranges_unittest.cc", + "text_renderer_unittest.cc", + "user_input_monitor_unittest.cc", + "vector_math_testing.h", + "vector_math_unittest.cc", + "video_frame_unittest.cc", + "video_frame_pool_unittest.cc", + "video_util_unittest.cc", + "wall_clock_time_source_unittest.cc", + "yuv_convert_unittest.cc", + ] + configs += [ "//media:media_config" ] + deps = [ + ":base", + ":test_support", + "//skia", + "//testing/gmock", + "//testing/gtest", + ] + + if (media_use_ffmpeg) { + sources += [ + "audio_video_metadata_extractor_unittest.cc", + "media_file_checker_unittest.cc", + ] + } + + if (!is_android) { + sources += [ + "container_names_unittest.cc", + ] + } else { + deps += [ "//ui/gl" ] + } + + if (cpu_arch == "x86" || cpu_arch == "x64") { + sources += [ "simd/convert_rgb_to_yuv_unittest.cc" ] + } +} + +source_set("perftests") { + sources = [ + "audio_bus_perftest.cc", + "audio_converter_perftest.cc", + "run_all_perftests.cc", + "sinc_resampler_perftest.cc", + "vector_math_perftest.cc", + "yuv_convert_perftest.cc", + ] + configs += [ "//media:media_config" ] + deps = [ + ":base", + ":test_support", + "//testing/gmock", + "//testing/gtest", + ] + + if (media_use_ffmpeg) { + sources += [ + "demuxer_perftest.cc", + ] + } + + if (is_android) { + deps += [ "//ui/gl"] + } +} + +if (cpu_arch == "x86" || cpu_arch == "x64") { + source_set("media_mmx") { + sources = [ "simd/filter_yuv_mmx.cc" ] + configs += [ "//media:media_config" ] + if (!is_win) { + cflags = [ "-mmmx" ] + } + } + + source_set("media_sse2") { + sources = [ + "simd/convert_rgb_to_yuv_sse2.cc", + "simd/convert_rgb_to_yuv_ssse3.cc", + "simd/filter_yuv_sse2.cc", + ] + configs += [ "//media:media_config" ] + if (!is_win) { + cflags = [ "-msse2" ] + } + } + + import("//third_party/yasm/yasm_assemble.gni") + yasm_assemble("media_yasm") { + sources = [ + "simd/convert_rgb_to_yuv_ssse3.asm", + "simd/convert_yuv_to_rgb_mmx.asm", + "simd/convert_yuv_to_rgb_sse.asm", + "simd/convert_yuva_to_argb_mmx.asm", + "simd/empty_register_state_mmx.asm", + "simd/linear_scale_yuv_to_rgb_mmx.asm", + "simd/linear_scale_yuv_to_rgb_sse.asm", + "simd/scale_yuv_to_rgb_mmx.asm", + "simd/scale_yuv_to_rgb_sse.asm", + ] + + # TODO(ajwong): Only export if shared_library build... + yasm_flags = [ + "-DCHROMIUM", + "-DEXPORT_SYMBOLS", + # In addition to the same path as source asm, let yasm %include + # search path be relative to src/ per Chromium policy. + "-I", rebase_path("..", root_build_dir), + ] + + inputs = [ + "//third_party/x86inc/x86inc.asm", + "simd/convert_rgb_to_yuv_ssse3.inc", + "simd/convert_yuv_to_rgb_mmx.inc", + "simd/convert_yuva_to_argb_mmx.inc", + "simd/linear_scale_yuv_to_rgb_mmx.inc", + "simd/media_export.asm", + "simd/scale_yuv_to_rgb_mmx.inc", + ] + + if (cpu_arch == "x86") { + yasm_flags += [ "-DARCH_X86_32" ] + } else if (cpu_arch == "x64") { + yasm_flags += [ "-DARCH_X86_64" ] + sources += [ + "simd/linear_scale_yuv_to_rgb_mmx_x64.asm", + "simd/scale_yuv_to_rgb_sse2_x64.asm", + ] + } + + if (is_mac || is_ios) { + yasm_flags += [ + "-DPREFIX", + "-DMACHO", + ] + } else { + if (is_posix) { + yasm_flags += [ "-DELF" ] + if (cpu_arch == "x64") { + # TODO(ajwong): Why isn't this true in mac? + yasm_flags += [ "-DPIC" ] + } + } + } + } +} diff --git a/media/base/android/BUILD.gn b/media/base/android/BUILD.gn new file mode 100644 index 0000000000000..a1b999935cc86 --- /dev/null +++ b/media/base/android/BUILD.gn @@ -0,0 +1,87 @@ +# Copyright 2014 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("//build/config/android/config.gni") +import("//build/config/android/rules.gni") +import("//build/config/arm.gni") +import("//build/config/ui.gni") +import("//media/media_options.gni") + +assert(is_android) + +source_set("android") { + sources = [ + "audio_decoder_job.cc", + "audio_decoder_job.h", + "browser_cdm_factory_android.cc", + "demuxer_android.h", + "demuxer_stream_player_params.cc", + "demuxer_stream_player_params.h", + "media_codec_bridge.cc", + "media_codec_bridge.h", + "media_decoder_job.cc", + "media_decoder_job.h", + "media_drm_bridge.cc", + "media_drm_bridge.h", + "media_jni_registrar.cc", + "media_jni_registrar.h", + "media_player_android.cc", + "media_player_android.h", + "media_player_bridge.cc", + "media_player_bridge.h", + "media_player_listener.cc", + "media_player_listener.h", + "media_player_manager.h", + "media_resource_getter.cc", + "media_resource_getter.h", + "media_source_player.cc", + "media_source_player.h", + "media_url_interceptor.h", + "video_decoder_job.cc", + "video_decoder_job.h", + "webaudio_media_codec_bridge.cc", + "webaudio_media_codec_bridge.h", + "webaudio_media_codec_info.h", + ] + configs += [ "//media:media_config" ] + deps = [ ":media_jni_headers" ] +} + +source_set("unittests") { + sources = [ + "media_codec_bridge_unittest.cc", + "media_drm_bridge_unittest.cc", + "media_source_player_unittest.cc", + ] + deps = [ + ":android", + "//media/base:test_support", + "//testing/gmock", + "//testing/gtest", + ] + configs += [ "//media:media_config" ] +} + +generate_jni("media_jni_headers") { + sources = [ + "java/src/org/chromium/media/AudioManagerAndroid.java", + "java/src/org/chromium/media/AudioRecordInput.java", + "java/src/org/chromium/media/MediaCodecBridge.java", + "java/src/org/chromium/media/MediaDrmBridge.java", + "java/src/org/chromium/media/MediaPlayerBridge.java", + "java/src/org/chromium/media/MediaPlayerListener.java", + "java/src/org/chromium/media/UsbMidiDeviceAndroid.java", + "java/src/org/chromium/media/UsbMidiDeviceFactoryAndroid.java", + "java/src/org/chromium/media/WebAudioMediaCodecBridge.java", + ] + jni_package = "media" +} + +generate_jni("video_capture_jni_headers") { + sources = [ + "java/src/org/chromium/media/VideoCapture.java", + "java/src/org/chromium/media/VideoCaptureFactory.java", + ] + jni_package = "media" +} diff --git a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java index 308a089998106..d575c2772bf29 100644 --- a/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java +++ b/media/base/android/java/src/org/chromium/media/MediaPlayerBridge.java @@ -8,6 +8,7 @@ import android.media.MediaPlayer; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.ParcelFileDescriptor; import android.text.TextUtils; import android.util.Base64; @@ -34,6 +35,19 @@ @JNINamespace("media") public class MediaPlayerBridge { + public static class ResourceLoadingFilter { + public boolean shouldOverrideResourceLoading( + MediaPlayer mediaPlayer, Context context, Uri uri) { + return false; + } + } + + private static ResourceLoadingFilter sResourceLoadFilter = null; + + public static void setResourceLoadingFilter(ResourceLoadingFilter filter) { + sResourceLoadFilter = filter; + } + private static final String TAG = "MediaPlayerBridge"; // Local player to forward this to. We don't initialize it here since the subclass might not @@ -144,7 +158,18 @@ protected boolean setDataSource( if (hideUrlLog) headersMap.put("x-hide-urls-from-log", "true"); if (!TextUtils.isEmpty(cookies)) headersMap.put("Cookie", cookies); if (!TextUtils.isEmpty(userAgent)) headersMap.put("User-Agent", userAgent); + // The security origin check is enforced for devices above K. For devices below K, + // only anonymous media HTTP request (no cookies) may be considered same-origin. + // Note that if the server rejects the request we must not consider it same-origin. + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { + headersMap.put("allow-cross-domain-redirect", "false"); + } try { + if (sResourceLoadFilter != null && + sResourceLoadFilter.shouldOverrideResourceLoading( + getLocalPlayer(), context, uri)) { + return true; + } getLocalPlayer().setDataSource(context, uri, headersMap); return true; } catch (Exception e) { diff --git a/media/base/android/java/src/org/chromium/media/VideoCapture.java b/media/base/android/java/src/org/chromium/media/VideoCapture.java old mode 100644 new mode 100755 index fe1863f225b42..4e00f66ce2063 --- a/media/base/android/java/src/org/chromium/media/VideoCapture.java +++ b/media/base/android/java/src/org/chromium/media/VideoCapture.java @@ -126,7 +126,10 @@ boolean allocate(int width, int height, int frameRate) { int frameRateInMs = frameRate * 1000; // Use the first range as default. int[] fpsMinMax = listFpsRange.get(0); - int newFrameRate = (fpsMinMax[0] + 999) / 1000; + int frameRateInMSNearest = Math.abs(frameRateInMs - fpsMinMax[0]) < + Math.abs(frameRateInMs - fpsMinMax[1]) ? + fpsMinMax[0] : fpsMinMax[1]; + int newFrameRate = (frameRateInMSNearest + 999) / 1000; for (int[] fpsRange : listFpsRange) { if (fpsRange[0] <= frameRateInMs && frameRateInMs <= fpsRange[1]) { fpsMinMax = fpsRange; diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java index 0bcd2aa8e4b5d..d0ca092e9db17 100644 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java +++ b/media/base/android/java/src/org/chromium/media/VideoCaptureAndroid.java @@ -40,18 +40,18 @@ private static class IdAndSizes { public final int mMinHeight; } - private static final IdAndSizes s_CAPTURESIZE_BUGGY_DEVICE_LIST[] = { + private static final IdAndSizes CAPTURESIZE_BUGGY_DEVICE_LIST[] = { new IdAndSizes("Nexus 7", "flo", 640, 480) }; - private static final String[] s_COLORSPACE_BUGGY_DEVICE_LIST = { + private static final String[] COLORSPACE_BUGGY_DEVICE_LIST = { "SAMSUNG-SGH-I747", "ODROID-U2", }; static void applyMinDimensions(CaptureFormat format) { // NOTE: this can discard requested aspect ratio considerations. - for (IdAndSizes buggyDevice : s_CAPTURESIZE_BUGGY_DEVICE_LIST) { + for (IdAndSizes buggyDevice : CAPTURESIZE_BUGGY_DEVICE_LIST) { if (buggyDevice.mModel.contentEquals(android.os.Build.MODEL) && buggyDevice.mDevice.contentEquals(android.os.Build.DEVICE)) { format.mWidth = (buggyDevice.mMinWidth > format.mWidth) @@ -67,7 +67,7 @@ static int getImageFormat() { return ImageFormat.NV21; } - for (String buggyDevice : s_COLORSPACE_BUGGY_DEVICE_LIST) { + for (String buggyDevice : COLORSPACE_BUGGY_DEVICE_LIST) { if (buggyDevice.contentEquals(android.os.Build.MODEL)) { return ImageFormat.NV21; } diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java index 25c7cbae3965e..12e10a304d0c1 100644 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java +++ b/media/base/android/java/src/org/chromium/media/VideoCaptureFactory.java @@ -48,7 +48,7 @@ static class ChromiumCameraInfo { // Special devices have more cameras than usual. Those devices are // identified by model & device. Currently only the Tango is supported. // Note that these devices have no Camera.CameraInfo. - private static final String[][] s_SPECIAL_DEVICE_LIST = { + private static final String[][] SPECIAL_DEVICE_LIST = { {"Peanut", "peanut"}, }; private static final String TAG = "ChromiumCameraInfo"; @@ -56,7 +56,7 @@ static class ChromiumCameraInfo { private static int sNumberOfSystemCameras = -1; private static boolean isSpecialDevice() { - for (String[] device : s_SPECIAL_DEVICE_LIST) { + for (String[] device : SPECIAL_DEVICE_LIST) { if (device[0].contentEquals(android.os.Build.MODEL) && device[1].contentEquals(android.os.Build.DEVICE)) { return true; diff --git a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java index e3e263d94ee53..b8f94e209dcbc 100644 --- a/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java +++ b/media/base/android/java/src/org/chromium/media/VideoCaptureTango.java @@ -27,7 +27,7 @@ public class VideoCaptureTango extends VideoCapture { private static final int DEPTH_CAMERA_ID = 0; private static final int FISHEYE_CAMERA_ID = 1; private static final int FOURMP_CAMERA_ID = 2; - private static final VideoCaptureFactory.CamParams s_CAM_PARAMS[] = { + private static final VideoCaptureFactory.CamParams CAM_PARAMS[] = { new VideoCaptureFactory.CamParams(DEPTH_CAMERA_ID, "depth", 320, 240), new VideoCaptureFactory.CamParams(FISHEYE_CAMERA_ID, "fisheye", 640, 480), new VideoCaptureFactory.CamParams(FOURMP_CAMERA_ID, "4MP", 1280, 720)}; @@ -49,12 +49,12 @@ public class VideoCaptureTango extends VideoCapture { private static final String TAG = "VideoCaptureTango"; static int numberOfCameras() { - return s_CAM_PARAMS.length; + return CAM_PARAMS.length; } static VideoCaptureFactory.CamParams getCamParams(int index) { - if (index >= s_CAM_PARAMS.length) return null; - return s_CAM_PARAMS[index]; + if (index >= CAM_PARAMS.length) return null; + return CAM_PARAMS[index]; } static CaptureFormat[] getDeviceSupportedFormats(int id) { @@ -84,8 +84,8 @@ protected void setCaptureParameters( int height, int frameRate, Camera.Parameters cameraParameters) { - mCaptureFormat = new CaptureFormat(s_CAM_PARAMS[mTangoCameraId].mWidth, - s_CAM_PARAMS[mTangoCameraId].mHeight, + mCaptureFormat = new CaptureFormat(CAM_PARAMS[mTangoCameraId].mWidth, + CAM_PARAMS[mTangoCameraId].mHeight, frameRate, ImageFormat.YV12); // Connect Tango SuperFrame mode. Available sf modes are "all", diff --git a/media/base/android/media_player_bridge.cc b/media/base/android/media_player_bridge.cc index 469409d373cfe..6b3ef1b02ad73 100644 --- a/media/base/android/media_player_bridge.cc +++ b/media/base/android/media_player_bridge.cc @@ -35,7 +35,8 @@ MediaPlayerBridge::MediaPlayerBridge( MediaPlayerManager* manager, const RequestMediaResourcesCB& request_media_resources_cb, const ReleaseMediaResourcesCB& release_media_resources_cb, - const GURL& frame_url) + const GURL& frame_url, + bool allow_credentials) : MediaPlayerAndroid(player_id, manager, request_media_resources_cb, @@ -54,6 +55,7 @@ MediaPlayerBridge::MediaPlayerBridge( can_seek_backward_(true), is_surface_in_use_(false), volume_(-1.0), + allow_credentials_(allow_credentials), weak_factory_(this) { listener_.reset(new MediaPlayerListener(base::MessageLoopProxy::current(), weak_factory_.GetWeakPtr())); @@ -70,7 +72,7 @@ MediaPlayerBridge::~MediaPlayerBridge() { void MediaPlayerBridge::Initialize() { cookies_.clear(); - if (url_.SchemeIsFile()) { + if (url_.SchemeIsFile() || url_.SchemeIs("app")) { ExtractMediaMetadata(url_.spec()); return; } @@ -85,6 +87,13 @@ void MediaPlayerBridge::Initialize() { return; } + // Start extracting the metadata immediately if the request is anonymous. + // Otherwise, wait for user credentials to be retrieved first. + if (!allow_credentials_) { + ExtractMediaMetadata(url_.spec()); + return; + } + resource_getter->GetCookies(url_, first_party_for_cookies_, base::Bind(&MediaPlayerBridge::OnCookiesRetrieved, diff --git a/media/base/android/media_player_bridge.h b/media/base/android/media_player_bridge.h index 7d03854b81615..ab486d64cc24b 100644 --- a/media/base/android/media_player_bridge.h +++ b/media/base/android/media_player_bridge.h @@ -51,7 +51,8 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid { MediaPlayerManager* manager, const RequestMediaResourcesCB& request_media_resources_cb, const ReleaseMediaResourcesCB& release_media_resources_cb, - const GURL& frame_url); + const GURL& frame_url, + bool allow_credentials); virtual ~MediaPlayerBridge(); // Initialize this object and extract the metadata from the media. @@ -191,6 +192,9 @@ class MEDIA_EXPORT MediaPlayerBridge : public MediaPlayerAndroid { // Volume of playback. double volume_; + // Whether user credentials are allowed to be passed. + bool allow_credentials_; + // Weak pointer passed to |listener_| for callbacks. // NOTE: Weak pointers must be invalidated before all other member variables. base::WeakPtrFactory weak_factory_; diff --git a/media/base/audio_splicer.cc b/media/base/audio_splicer.cc index 7fafc8bbbaca9..0c8ac343324ef 100644 --- a/media/base/audio_splicer.cc +++ b/media/base/audio_splicer.cc @@ -302,8 +302,10 @@ bool AudioSplicer::AddInput(const scoped_refptr& input) { // If a splice frame was incorrectly marked due to poor demuxed timestamps, we // may not actually have a splice. Here we check if any frames exist before // the splice. In this case, just transfer all data to the output sanitizer. - if (pre_splice_sanitizer_->GetFrameCount() <= - output_ts_helper.GetFramesToTarget(splice_timestamp_)) { + const int frames_before_splice = + output_ts_helper.GetFramesToTarget(splice_timestamp_); + if (frames_before_splice < 0 || + pre_splice_sanitizer_->GetFrameCount() <= frames_before_splice) { CHECK(pre_splice_sanitizer_->DrainInto(output_sanitizer_.get())); // If the file contains incorrectly muxed timestamps, there may be huge gaps diff --git a/media/base/audio_splicer_unittest.cc b/media/base/audio_splicer_unittest.cc index e6de2c62ed330..86131bf487474 100644 --- a/media/base/audio_splicer_unittest.cc +++ b/media/base/audio_splicer_unittest.cc @@ -720,4 +720,48 @@ TEST_F(AudioSplicerTest, IncorrectlyMarkedSpliceWithBadGap) { EXPECT_FALSE(AddInput(second_buffer)); } +// Ensure we don't crash when a splice frame is incorrectly marked such that the +// splice timestamp has already passed when SetSpliceTimestamp() is called. +// This can happen if the encoded timestamps are too far behind the decoded +// timestamps. +TEST_F(AudioSplicerTest, IncorrectlyMarkedPastSplice) { + const int kBufferSize = 200; + + scoped_refptr first_buffer = + GetNextInputBuffer(1.0f, kBufferSize); + EXPECT_TRUE(AddInput(first_buffer)); + VerifyNextBuffer(first_buffer); + + // Start the splice at a timestamp which has already occurred. + splicer_.SetSpliceTimestamp(base::TimeDelta()); + + scoped_refptr second_buffer = + GetNextInputBuffer(0.5f, kBufferSize); + EXPECT_TRUE(AddInput(second_buffer)); + EXPECT_FALSE(splicer_.HasNextBuffer()); + + // |third_buffer| will complete the supposed splice. The buffer size is set + // such that unchecked the splicer would try to trim off a negative number of + // frames. + splicer_.SetSpliceTimestamp(kNoTimestamp()); + scoped_refptr third_buffer = + GetNextInputBuffer(0.0f, kBufferSize * 10); + third_buffer->set_timestamp(base::TimeDelta()); + EXPECT_TRUE(AddInput(third_buffer)); + + // The second buffer should come through unmodified. + VerifyNextBuffer(second_buffer); + + // The third buffer should be partially dropped since it overlaps the second. + ASSERT_TRUE(splicer_.HasNextBuffer()); + const base::TimeDelta second_buffer_end_ts = + second_buffer->timestamp() + second_buffer->duration(); + scoped_refptr output = splicer_.GetNextBuffer(); + EXPECT_EQ(second_buffer_end_ts, output->timestamp()); + EXPECT_EQ(third_buffer->duration() - + (second_buffer_end_ts - third_buffer->timestamp()), + output->duration()); + EXPECT_TRUE(VerifyData(output, GetValue(third_buffer))); +} + } // namespace media diff --git a/media/base/cdm_promise.cc b/media/base/cdm_promise.cc index ec5e913dbb8aa..74ff8bca68b1e 100644 --- a/media/base/cdm_promise.cc +++ b/media/base/cdm_promise.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/logging.h" +#include "base/metrics/histogram.h" namespace media { @@ -17,58 +18,159 @@ CdmPromise::CdmPromise(PromiseRejectedCB reject_cb) DCHECK(!reject_cb_.is_null()); } +CdmPromise::CdmPromise(PromiseRejectedCB reject_cb, const std::string& uma_name) + : reject_cb_(reject_cb), is_pending_(true), uma_name_(uma_name) { + DCHECK(!reject_cb_.is_null()); +} + CdmPromise::~CdmPromise() { DCHECK(!is_pending_); } +static CdmPromise::ResultCodeForUMA ConvertExceptionToUMAResult( + MediaKeys::Exception exception_code) { + switch (exception_code) { + case MediaKeys::NOT_SUPPORTED_ERROR: + return CdmPromise::NOT_SUPPORTED_ERROR; + case MediaKeys::INVALID_STATE_ERROR: + return CdmPromise::INVALID_STATE_ERROR; + case MediaKeys::INVALID_ACCESS_ERROR: + return CdmPromise::INVALID_ACCESS_ERROR; + case MediaKeys::QUOTA_EXCEEDED_ERROR: + return CdmPromise::QUOTA_EXCEEDED_ERROR; + case MediaKeys::UNKNOWN_ERROR: + return CdmPromise::UNKNOWN_ERROR; + case MediaKeys::CLIENT_ERROR: + return CdmPromise::CLIENT_ERROR; + case MediaKeys::OUTPUT_ERROR: + return CdmPromise::OUTPUT_ERROR; + } + NOTREACHED(); + return CdmPromise::UNKNOWN_ERROR; +} + void CdmPromise::reject(MediaKeys::Exception exception_code, uint32 system_code, const std::string& error_message) { DCHECK(is_pending_); is_pending_ = false; + if (!uma_name_.empty()) { + ResultCodeForUMA result_code = ConvertExceptionToUMAResult(exception_code); + base::LinearHistogram::FactoryGet( + uma_name_, 1, NUM_RESULT_CODES, NUM_RESULT_CODES + 1, + base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result_code); + } reject_cb_.Run(exception_code, system_code, error_message); } -template -CdmPromiseTemplate::CdmPromiseTemplate( - base::Callback resolve_cb, +CdmPromiseTemplate::CdmPromiseTemplate(base::Callback resolve_cb, + PromiseRejectedCB reject_cb) + : CdmPromise(reject_cb), resolve_cb_(resolve_cb) { + DCHECK(!resolve_cb_.is_null()); +} + +CdmPromiseTemplate::CdmPromiseTemplate(base::Callback resolve_cb, + PromiseRejectedCB reject_cb, + const std::string& uma_name) + : CdmPromise(reject_cb, uma_name), resolve_cb_(resolve_cb) { + DCHECK(!resolve_cb_.is_null()); + DCHECK(!uma_name_.empty()); +} + +CdmPromiseTemplate::CdmPromiseTemplate() { +} + +CdmPromiseTemplate::~CdmPromiseTemplate() { + DCHECK(!is_pending_); +} + +void CdmPromiseTemplate::resolve() { + DCHECK(is_pending_); + is_pending_ = false; + if (!uma_name_.empty()) { + base::LinearHistogram::FactoryGet( + uma_name_, 1, NUM_RESULT_CODES, NUM_RESULT_CODES + 1, + base::HistogramBase::kUmaTargetedHistogramFlag)->Add(SUCCESS); + } + resolve_cb_.Run(); +} + +CdmPromise::ResolveParameterType +CdmPromiseTemplate::GetResolveParameterType() const { + return VOID_TYPE; +} + +CdmPromiseTemplate::CdmPromiseTemplate( + base::Callback resolve_cb, PromiseRejectedCB reject_cb) : CdmPromise(reject_cb), resolve_cb_(resolve_cb) { DCHECK(!resolve_cb_.is_null()); } -template -CdmPromiseTemplate::~CdmPromiseTemplate() { +CdmPromiseTemplate::CdmPromiseTemplate( + base::Callback resolve_cb, + PromiseRejectedCB reject_cb, + const std::string& uma_name) + : CdmPromise(reject_cb, uma_name), resolve_cb_(resolve_cb) { + DCHECK(!resolve_cb_.is_null()); +} + +CdmPromiseTemplate::CdmPromiseTemplate() { +} + +CdmPromiseTemplate::~CdmPromiseTemplate() { DCHECK(!is_pending_); } -template -void CdmPromiseTemplate::resolve(const T& result) { +void CdmPromiseTemplate::resolve(const std::string& result) { DCHECK(is_pending_); is_pending_ = false; + if (!uma_name_.empty()) { + base::LinearHistogram::FactoryGet( + uma_name_, 1, NUM_RESULT_CODES, NUM_RESULT_CODES + 1, + base::HistogramBase::kUmaTargetedHistogramFlag)->Add(SUCCESS); + } resolve_cb_.Run(result); } -CdmPromiseTemplate::CdmPromiseTemplate(base::Callback resolve_cb, - PromiseRejectedCB reject_cb) +CdmPromise::ResolveParameterType +CdmPromiseTemplate::GetResolveParameterType() const { + return STRING_TYPE; +} + +CdmPromiseTemplate::CdmPromiseTemplate( + base::Callback resolve_cb, + PromiseRejectedCB reject_cb) : CdmPromise(reject_cb), resolve_cb_(resolve_cb) { DCHECK(!resolve_cb_.is_null()); } -CdmPromiseTemplate::CdmPromiseTemplate() { +CdmPromiseTemplate::CdmPromiseTemplate( + base::Callback resolve_cb, + PromiseRejectedCB reject_cb, + const std::string& uma_name) + : CdmPromise(reject_cb, uma_name), resolve_cb_(resolve_cb) { + DCHECK(!resolve_cb_.is_null()); } -CdmPromiseTemplate::~CdmPromiseTemplate() { +CdmPromiseTemplate::~CdmPromiseTemplate() { DCHECK(!is_pending_); } -void CdmPromiseTemplate::resolve() { +void CdmPromiseTemplate::resolve(const KeyIdsVector& result) { DCHECK(is_pending_); is_pending_ = false; - resolve_cb_.Run(); + if (!uma_name_.empty()) { + base::LinearHistogram::FactoryGet( + uma_name_, 1, NUM_RESULT_CODES, NUM_RESULT_CODES + 1, + base::HistogramBase::kUmaTargetedHistogramFlag)->Add(SUCCESS); + } + resolve_cb_.Run(result); } -// Explicit template instantiation for the Promises needed. -template class MEDIA_EXPORT CdmPromiseTemplate; +CdmPromise::ResolveParameterType +CdmPromiseTemplate::GetResolveParameterType() const { + return KEY_IDS_VECTOR_TYPE; +} } // namespace media diff --git a/media/base/cdm_promise.h b/media/base/cdm_promise.h index ad1d196ad6e00..33ec042510c84 100644 --- a/media/base/cdm_promise.h +++ b/media/base/cdm_promise.h @@ -22,6 +22,25 @@ namespace media { // This is only the base class, as parameter to resolve() varies. class MEDIA_EXPORT CdmPromise { public: + // A superset of media::MediaKeys::Exception for UMA reporting. + enum ResultCodeForUMA { + SUCCESS, + NOT_SUPPORTED_ERROR, + INVALID_STATE_ERROR, + INVALID_ACCESS_ERROR, + QUOTA_EXCEEDED_ERROR, + UNKNOWN_ERROR, + CLIENT_ERROR, + OUTPUT_ERROR, + NUM_RESULT_CODES + }; + + enum ResolveParameterType { + VOID_TYPE, + STRING_TYPE, + KEY_IDS_VECTOR_TYPE + }; + typedef base::Callback @@ -37,47 +56,89 @@ class MEDIA_EXPORT CdmPromise { uint32 system_code, const std::string& error_message); + virtual ResolveParameterType GetResolveParameterType() const = 0; + protected: CdmPromise(); CdmPromise(PromiseRejectedCB reject_cb); + // If constructed with a |uma_name| (which must be the name of a + // CdmPromiseResult UMA), CdmPromise will report the promise result (success + // or rejection code). + CdmPromise(PromiseRejectedCB reject_cb, const std::string& uma_name); + PromiseRejectedCB reject_cb_; // Keep track of whether the promise hasn't been resolved or rejected yet. bool is_pending_; + // UMA to report result to. + std::string uma_name_; + DISALLOW_COPY_AND_ASSIGN(CdmPromise); }; -template -class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise { +// Specialization for no parameter to resolve(). +template <> +class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise { public: - CdmPromiseTemplate(base::Callback resolve_cb, + CdmPromiseTemplate(base::Callback resolve_cb, PromiseRejectedCB rejected_cb); + CdmPromiseTemplate(base::Callback resolve_cb, + PromiseRejectedCB rejected_cb, + const std::string& uma_name); virtual ~CdmPromiseTemplate(); - virtual void resolve(const T& result); + virtual void resolve(); + virtual ResolveParameterType GetResolveParameterType() const OVERRIDE; + + protected: + // Allow subclasses to completely override the implementation. + CdmPromiseTemplate(); private: - base::Callback resolve_cb_; + base::Callback resolve_cb_; DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate); }; -// Specialization for no parameter to resolve(). template <> -class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise { +class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise { public: - CdmPromiseTemplate(base::Callback resolve_cb, + CdmPromiseTemplate(base::Callback resolve_cb, PromiseRejectedCB rejected_cb); + CdmPromiseTemplate(base::Callback resolve_cb, + PromiseRejectedCB rejected_cb, + const std::string& uma_name); virtual ~CdmPromiseTemplate(); - virtual void resolve(); + virtual void resolve(const std::string& result); + virtual ResolveParameterType GetResolveParameterType() const OVERRIDE; protected: // Allow subclasses to completely override the implementation. + // TODO(jrummell): Remove when derived class SessionLoadedPromise + // (in ppapi_decryptor.cc) is no longer needed. CdmPromiseTemplate(); private: - base::Callback resolve_cb_; + base::Callback resolve_cb_; + + DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate); +}; + +template <> +class MEDIA_EXPORT CdmPromiseTemplate : public CdmPromise { + public: + CdmPromiseTemplate(base::Callback resolve_cb, + PromiseRejectedCB rejected_cb); + CdmPromiseTemplate(base::Callback resolve_cb, + PromiseRejectedCB rejected_cb, + const std::string& uma_name); + virtual ~CdmPromiseTemplate(); + virtual void resolve(const KeyIdsVector& result); + virtual ResolveParameterType GetResolveParameterType() const OVERRIDE; + + private: + base::Callback resolve_cb_; DISALLOW_COPY_AND_ASSIGN(CdmPromiseTemplate); }; diff --git a/media/base/decoder_buffer.h b/media/base/decoder_buffer.h index c17aa213ab121..092b2130c5025 100644 --- a/media/base/decoder_buffer.h +++ b/media/base/decoder_buffer.h @@ -33,7 +33,7 @@ class MEDIA_EXPORT DecoderBuffer : public base::RefCountedThreadSafe { public: enum { - kPaddingSize = 16, + kPaddingSize = 32, #if defined(ARCH_CPU_ARM_FAMILY) kAlignmentSize = 16 #else diff --git a/media/base/decryptor.h b/media/base/decryptor.h index 2c2cf793639f0..292f2b34694a0 100644 --- a/media/base/decryptor.h +++ b/media/base/decryptor.h @@ -161,8 +161,14 @@ class MEDIA_EXPORT Decryptor { DISALLOW_COPY_AND_ASSIGN(Decryptor); }; -// Callback to notify that a decryptor is ready. -typedef base::Callback DecryptorReadyCB; +// Callback to notify that the decryptor has been completely attached into the +// pipeline. Parameter indicates whether the operation succeeded. +typedef base::Callback DecryptorAttachedCB; + +// Callback to notify that a decryptor is ready. DecryptorAttachedCB is called +// when the decryptor has been completely inserted into the pipeline. +typedef base::Callback + DecryptorReadyCB; // Callback to set/cancel a DecryptorReadyCB. // Calling this callback with a non-null callback registers decryptor ready diff --git a/media/base/media_keys.h b/media/base/media_keys.h index d581ae4e8bd9c..063e54b1058d4 100644 --- a/media/base/media_keys.h +++ b/media/base/media_keys.h @@ -23,6 +23,8 @@ class CdmPromiseTemplate; typedef CdmPromiseTemplate NewSessionCdmPromise; typedef CdmPromiseTemplate SimpleCdmPromise; +typedef std::vector > KeyIdsVector; +typedef CdmPromiseTemplate KeyIdsPromise; // Performs media key operations. // diff --git a/media/base/stream_parser.cc b/media/base/stream_parser.cc index 59c3ed60796c8..942afbf8ec93a 100644 --- a/media/base/stream_parser.cc +++ b/media/base/stream_parser.cc @@ -48,7 +48,7 @@ static bool MergeBufferQueuesInternal( // buffers must not be less than. If |merged_buffers| already has buffers, // initialize |last_decode_timestamp| to the decode timestamp of the last // buffer in it. - base::TimeDelta last_decode_timestamp = kNoTimestamp(); + DecodeTimestamp last_decode_timestamp = kNoDecodeTimestamp(); if (!merged_buffers->empty()) last_decode_timestamp = merged_buffers->back()->GetDecodeTimestamp(); @@ -70,7 +70,7 @@ static bool MergeBufferQueuesInternal( // Tracks which queue's iterator is pointing to the candidate buffer to // append next, or -1 if no candidate buffers found. This indexes |itrs|. int index_of_queue_with_next_decode_timestamp = -1; - base::TimeDelta next_decode_timestamp = kNoTimestamp(); + DecodeTimestamp next_decode_timestamp = kNoDecodeTimestamp(); // Scan each of the iterators for |buffer_queues| to find the candidate // buffer, if any, that has the lowest decode timestamp. @@ -79,14 +79,14 @@ static bool MergeBufferQueuesInternal( continue; // Extract the candidate buffer's decode timestamp. - base::TimeDelta ts = (*itrs[i])->GetDecodeTimestamp(); + DecodeTimestamp ts = (*itrs[i])->GetDecodeTimestamp(); - if (last_decode_timestamp != kNoTimestamp() && + if (last_decode_timestamp != kNoDecodeTimestamp() && ts < last_decode_timestamp) return false; if (ts < next_decode_timestamp || - next_decode_timestamp == kNoTimestamp()) { + next_decode_timestamp == kNoDecodeTimestamp()) { // Remember the decode timestamp and queue iterator index for this // potentially winning candidate buffer. next_decode_timestamp = ts; diff --git a/media/base/stream_parser_buffer.cc b/media/base/stream_parser_buffer.cc index ae826594d5e09..a9a12e397823c 100644 --- a/media/base/stream_parser_buffer.cc +++ b/media/base/stream_parser_buffer.cc @@ -61,13 +61,13 @@ scoped_refptr StreamParserBuffer::CopyFrom( is_keyframe, type, track_id)); } -base::TimeDelta StreamParserBuffer::GetDecodeTimestamp() const { - if (decode_timestamp_ == kNoTimestamp()) - return timestamp(); +DecodeTimestamp StreamParserBuffer::GetDecodeTimestamp() const { + if (decode_timestamp_ == kNoDecodeTimestamp()) + return DecodeTimestamp::FromPresentationTime(timestamp()); return decode_timestamp_; } -void StreamParserBuffer::SetDecodeTimestamp(base::TimeDelta timestamp) { +void StreamParserBuffer::SetDecodeTimestamp(DecodeTimestamp timestamp) { decode_timestamp_ = timestamp; if (preroll_buffer_) preroll_buffer_->SetDecodeTimestamp(timestamp); @@ -79,7 +79,7 @@ StreamParserBuffer::StreamParserBuffer(const uint8* data, int data_size, Type type, TrackId track_id) : DecoderBuffer(data, data_size, side_data, side_data_size), is_keyframe_(is_keyframe), - decode_timestamp_(kNoTimestamp()), + decode_timestamp_(kNoDecodeTimestamp()), config_id_(kInvalidConfigId), type_(type), track_id_(track_id) { diff --git a/media/base/stream_parser_buffer.h b/media/base/stream_parser_buffer.h index 24abe1a9cd873..a61cf856ee7da 100644 --- a/media/base/stream_parser_buffer.h +++ b/media/base/stream_parser_buffer.h @@ -14,6 +14,91 @@ namespace media { +// Simple wrapper around base::TimeDelta that represents a decode timestamp. +// Making DecodeTimestamp a different type makes it easier to determine whether +// code is operating on presentation or decode timestamps and makes conversions +// between the two types explicit and easy to spot. +class DecodeTimestamp { + public: + DecodeTimestamp() {} + DecodeTimestamp(const DecodeTimestamp& rhs) : ts_(rhs.ts_) { } + DecodeTimestamp& operator=(const DecodeTimestamp& rhs) { + if (&rhs != this) + ts_ = rhs.ts_; + return *this; + } + + // Only operators that are actually used by the code have been defined. + // Reviewers should pay close attention to the addition of new operators. + bool operator<(const DecodeTimestamp& rhs) const { return ts_ < rhs.ts_; } + bool operator>(const DecodeTimestamp& rhs) const { return ts_ > rhs.ts_; } + bool operator==(const DecodeTimestamp& rhs) const { return ts_ == rhs.ts_; } + bool operator!=(const DecodeTimestamp& rhs) const { return ts_ != rhs.ts_; } + bool operator>=(const DecodeTimestamp& rhs) const { return ts_ >= rhs.ts_; } + bool operator<=(const DecodeTimestamp& rhs) const { return ts_ <= rhs.ts_; } + + base::TimeDelta operator-(const DecodeTimestamp& rhs) const { + return ts_ - rhs.ts_; + } + + DecodeTimestamp& operator+=(const base::TimeDelta& rhs) { + ts_ += rhs; + return *this; + } + + DecodeTimestamp& operator-=(const base::TimeDelta& rhs) { + ts_ -= rhs; + return *this; + } + + DecodeTimestamp operator+(const base::TimeDelta& rhs) const { + return DecodeTimestamp(ts_ + rhs); + } + + DecodeTimestamp operator-(const base::TimeDelta& rhs) const { + return DecodeTimestamp(ts_ - rhs); + } + + int64 operator/(const base::TimeDelta& rhs) const { + return ts_ / rhs; + } + + static DecodeTimestamp FromSecondsD(double seconds) { + return DecodeTimestamp(base::TimeDelta::FromSecondsD(seconds)); + } + + static DecodeTimestamp FromMilliseconds(int64 milliseconds) { + return DecodeTimestamp(base::TimeDelta::FromMilliseconds(milliseconds)); + } + + static DecodeTimestamp FromMicroseconds(int64 microseconds) { + return DecodeTimestamp(base::TimeDelta::FromMicroseconds(microseconds)); + } + + // This method is used to explicitly call out when presentation timestamps + // are being converted to a decode timestamp. + static DecodeTimestamp FromPresentationTime(base::TimeDelta timestamp) { + return DecodeTimestamp(timestamp); + } + + double InSecondsF() const { return ts_.InSecondsF(); } + int64 InMilliseconds() const { return ts_.InMilliseconds(); } + int64 InMicroseconds() const { return ts_.InMicroseconds(); } + + // TODO(acolwell): Remove once all the hacks are gone. This method is called + // by hacks where a decode time is being used as a presentation time. + base::TimeDelta ToPresentationTime() const { return ts_; } + + private: + explicit DecodeTimestamp(base::TimeDelta timestamp) : ts_(timestamp) { } + + base::TimeDelta ts_; +}; + +MEDIA_EXPORT extern inline DecodeTimestamp kNoDecodeTimestamp() { + return DecodeTimestamp::FromPresentationTime(kNoTimestamp()); +} + class MEDIA_EXPORT StreamParserBuffer : public DecoderBuffer { public: // Value used to signal an invalid decoder config ID. @@ -35,8 +120,8 @@ class MEDIA_EXPORT StreamParserBuffer : public DecoderBuffer { // Decode timestamp. If not explicitly set, or set to kNoTimestamp(), the // value will be taken from the normal timestamp. - base::TimeDelta GetDecodeTimestamp() const; - void SetDecodeTimestamp(base::TimeDelta timestamp); + DecodeTimestamp GetDecodeTimestamp() const; + void SetDecodeTimestamp(DecodeTimestamp timestamp); // Gets/sets the ID of the decoder config associated with this buffer. int GetConfigId() const; @@ -89,7 +174,7 @@ class MEDIA_EXPORT StreamParserBuffer : public DecoderBuffer { virtual ~StreamParserBuffer(); bool is_keyframe_; - base::TimeDelta decode_timestamp_; + DecodeTimestamp decode_timestamp_; int config_id_; Type type_; TrackId track_id_; diff --git a/media/base/stream_parser_unittest.cc b/media/base/stream_parser_unittest.cc index 793260ca72230..708747c81a16c 100644 --- a/media/base/stream_parser_unittest.cc +++ b/media/base/stream_parser_unittest.cc @@ -53,7 +53,7 @@ static void GenerateBuffers(const int* decode_timestamps, StreamParserBuffer::CopyFrom(kFakeData, sizeof(kFakeData), true, type, track_id); buffer->SetDecodeTimestamp( - base::TimeDelta::FromMicroseconds(decode_timestamps[i])); + DecodeTimestamp::FromMicroseconds(decode_timestamps[i])); queue->push_back(buffer); } } @@ -379,4 +379,3 @@ TEST_F(StreamParserTest, MergeBufferQueues_InvalidAppendToExistingMerge) { } } // namespace media - diff --git a/media/base/test_helpers.cc b/media/base/test_helpers.cc index 929b2f3c725c7..b37dc0e8f8ab4 100644 --- a/media/base/test_helpers.cc +++ b/media/base/test_helpers.cc @@ -242,4 +242,21 @@ bool VerifyFakeVideoBufferForTest( height == config.coded_size().height()); } +CallbackPairChecker::CallbackPairChecker() : expecting_b_(false) { +} + +CallbackPairChecker::~CallbackPairChecker() { + EXPECT_FALSE(expecting_b_); +} + +void CallbackPairChecker::RecordACalled() { + EXPECT_FALSE(expecting_b_); + expecting_b_ = true; +} + +void CallbackPairChecker::RecordBCalled() { + EXPECT_TRUE(expecting_b_); + expecting_b_ = false; +} + } // namespace media diff --git a/media/base/test_helpers.h b/media/base/test_helpers.h index 8dc38958f5d2a..810e0866ecd08 100644 --- a/media/base/test_helpers.h +++ b/media/base/test_helpers.h @@ -125,6 +125,19 @@ scoped_refptr CreateFakeVideoBufferForTest( bool VerifyFakeVideoBufferForTest(const scoped_refptr& buffer, const VideoDecoderConfig& config); +// Used to verify that the each call to A() is followed by a call to B(), +// before the next call to A(). There may be any number of pairs (including 0). +class CallbackPairChecker { + public: + CallbackPairChecker(); + ~CallbackPairChecker(); + void RecordACalled(); + void RecordBCalled(); + + private: + bool expecting_b_; +}; + } // namespace media #endif // MEDIA_BASE_TEST_HELPERS_H_ diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h index 3cc33d927158a..0954e05fd47a4 100644 --- a/media/base/video_decoder_config.h +++ b/media/base/video_decoder_config.h @@ -58,11 +58,11 @@ enum VideoCodecProfile { H264PROFILE_MULTIVIEWHIGH = 10, H264PROFILE_MAX = H264PROFILE_MULTIVIEWHIGH, VP8PROFILE_MIN = 11, - VP8PROFILE_MAIN = VP8PROFILE_MIN, - VP8PROFILE_MAX = VP8PROFILE_MAIN, + VP8PROFILE_ANY = VP8PROFILE_MIN, + VP8PROFILE_MAX = VP8PROFILE_ANY, VP9PROFILE_MIN = 12, - VP9PROFILE_MAIN = VP9PROFILE_MIN, - VP9PROFILE_MAX = VP9PROFILE_MAIN, + VP9PROFILE_ANY = VP9PROFILE_MIN, + VP9PROFILE_MAX = VP9PROFILE_ANY, VIDEO_CODEC_PROFILE_MAX = VP9PROFILE_MAX, }; diff --git a/media/cast/BUILD.gn b/media/cast/BUILD.gn index 95d0077504ffb..8d38089dae807 100644 --- a/media/cast/BUILD.gn +++ b/media/cast/BUILD.gn @@ -220,6 +220,7 @@ source_set("test_support") { "//third_party/libyuv", "//third_party/mt19937ar", "//ui/gfx", + "//ui/gfx/geometry", ] } diff --git a/media/cast/cast_defines.h b/media/cast/cast_defines.h index 07be3b3f9ef25..c02fa2bfa72cf 100644 --- a/media/cast/cast_defines.h +++ b/media/cast/cast_defines.h @@ -27,7 +27,7 @@ const uint32 kStartFrameId = UINT32_C(0xffffffff); // This is an important system-wide constant. This limits how much history the // implementation must retain in order to process the acknowledgements of past // frames. -const int kMaxUnackedFrames = 255; +const int kMaxUnackedFrames = 60; const int kStartRttMs = 20; const int64 kCastMessageUpdateIntervalMs = 33; diff --git a/media/cast/cast_testing.gypi b/media/cast/cast_testing.gypi index 7cfc50c0499c6..21585c81abf4d 100644 --- a/media/cast/cast_testing.gypi +++ b/media/cast/cast_testing.gypi @@ -82,6 +82,7 @@ 'logging/simple_event_subscriber_unittest.cc', 'logging/stats_event_subscriber_unittest.cc', 'net/cast_transport_sender_impl_unittest.cc', + 'net/frame_id_wrap_helper_test.cc', 'net/pacing/mock_paced_packet_sender.cc', 'net/pacing/mock_paced_packet_sender.h', 'net/pacing/paced_sender_unittest.cc', @@ -318,6 +319,29 @@ 'sources': [ 'test/utility/udp_proxy_main.cc', ], - } + }, + ], # targets + + 'conditions': [ + ['OS=="linux"', + { 'targets': [ + { + 'target_name': 'tap_proxy', + 'type': 'executable', + 'include_dirs': [ + '<(DEPTH)/', + ], + 'dependencies': [ + 'cast_test_utility', + '<(DEPTH)/base/base.gyp:base', + '<(DEPTH)/media/media.gyp:media', + ], + 'sources': [ + 'test/utility/tap_proxy.cc', + ], + } + ] + } + ] ], # targets } diff --git a/media/cast/logging/stats_event_subscriber.cc b/media/cast/logging/stats_event_subscriber.cc index 9e3226a21616d..03c669cfcbcf4 100644 --- a/media/cast/logging/stats_event_subscriber.cc +++ b/media/cast/logging/stats_event_subscriber.cc @@ -19,7 +19,6 @@ namespace { using media::cast::CastLoggingEvent; using media::cast::EventMediaType; -const size_t kMaxFrameEventTimeMapSize = 100; const size_t kMaxPacketEventTimeMapSize = 1000; bool IsReceiverEvent(CastLoggingEvent event) { @@ -39,7 +38,9 @@ StatsEventSubscriber::StatsEventSubscriber( clock_(clock), offset_estimator_(offset_estimator), network_latency_datapoints_(0), - e2e_latency_datapoints_(0) { + e2e_latency_datapoints_(0), + num_frames_dropped_by_encoder_(0), + num_frames_late_(0) { DCHECK(event_media_type == AUDIO_EVENT || event_media_type == VIDEO_EVENT); base::TimeTicks now = clock_->NowTicks(); start_time_ = now; @@ -71,9 +72,13 @@ void StatsEventSubscriber::OnReceiveFrameEvent(const FrameEvent& frame_event) { } if (type == FRAME_CAPTURE_BEGIN) { - RecordFrameCapturedTime(frame_event); + RecordFrameCaptureTime(frame_event); + } else if (type == FRAME_ENCODED) { + MarkAsEncoded(frame_event.rtp_timestamp); } else if (type == FRAME_PLAYOUT) { RecordE2ELatency(frame_event); + if (frame_event.delay_delta <= base::TimeDelta()) + num_frames_late_++; } if (IsReceiverEvent(type)) @@ -138,7 +143,9 @@ void StatsEventSubscriber::Reset() { network_latency_datapoints_ = 0; total_e2e_latency_ = base::TimeDelta(); e2e_latency_datapoints_ = 0; - frame_captured_times_.clear(); + num_frames_dropped_by_encoder_ = 0; + num_frames_late_ = 0; + recent_captured_frames_.clear(); packet_sent_times_.clear(); start_time_ = clock_->NowTicks(); last_response_received_time_ = base::TimeTicks(); @@ -159,6 +166,12 @@ const char* StatsEventSubscriber::CastStatToString(CastStat stat) { STAT_ENUM_TO_STRING(RETRANSMISSION_KBPS); STAT_ENUM_TO_STRING(PACKET_LOSS_FRACTION); STAT_ENUM_TO_STRING(MS_SINCE_LAST_RECEIVER_RESPONSE); + STAT_ENUM_TO_STRING(NUM_FRAMES_CAPTURED); + STAT_ENUM_TO_STRING(NUM_FRAMES_DROPPED_BY_ENCODER); + STAT_ENUM_TO_STRING(NUM_FRAMES_LATE); + STAT_ENUM_TO_STRING(NUM_PACKETS_SENT); + STAT_ENUM_TO_STRING(NUM_PACKETS_RETRANSMITTED); + STAT_ENUM_TO_STRING(NUM_PACKETS_RTX_REJECTED); } NOTREACHED(); return ""; @@ -188,6 +201,12 @@ void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const { RETRANSMISSION_KBPS, stats_map); PopulatePacketLossPercentageStat(stats_map); + PopulateFrameCountStat(FRAME_CAPTURE_END, NUM_FRAMES_CAPTURED, stats_map); + PopulatePacketCountStat(PACKET_SENT_TO_NETWORK, NUM_PACKETS_SENT, stats_map); + PopulatePacketCountStat( + PACKET_RETRANSMITTED, NUM_PACKETS_RETRANSMITTED, stats_map); + PopulatePacketCountStat( + PACKET_RTX_REJECTED, NUM_PACKETS_RTX_REJECTED, stats_map); if (network_latency_datapoints_ > 0) { double avg_network_latency_ms = @@ -208,6 +227,10 @@ void StatsEventSubscriber::GetStatsInternal(StatsMap* stats_map) const { std::make_pair(MS_SINCE_LAST_RECEIVER_RESPONSE, (end_time - last_response_received_time_).InMillisecondsF())); } + + stats_map->insert(std::make_pair(NUM_FRAMES_DROPPED_BY_ENCODER, + num_frames_dropped_by_encoder_)); + stats_map->insert(std::make_pair(NUM_FRAMES_LATE, num_frames_late_)); } bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) { @@ -222,12 +245,22 @@ bool StatsEventSubscriber::GetReceiverOffset(base::TimeDelta* offset) { return true; } -void StatsEventSubscriber::RecordFrameCapturedTime( +void StatsEventSubscriber::RecordFrameCaptureTime( const FrameEvent& frame_event) { - frame_captured_times_.insert( - std::make_pair(frame_event.rtp_timestamp, frame_event.timestamp)); - if (frame_captured_times_.size() > kMaxFrameEventTimeMapSize) - frame_captured_times_.erase(frame_captured_times_.begin()); + recent_captured_frames_.insert(std::make_pair( + frame_event.rtp_timestamp, FrameInfo(frame_event.timestamp))); + if (recent_captured_frames_.size() > kMaxFrameInfoMapSize) { + FrameInfoMap::iterator erase_it = recent_captured_frames_.begin(); + if (!erase_it->second.encoded) + num_frames_dropped_by_encoder_++; + recent_captured_frames_.erase(erase_it); + } +} + +void StatsEventSubscriber::MarkAsEncoded(RtpTimestamp rtp_timestamp) { + FrameInfoMap::iterator it = recent_captured_frames_.find(rtp_timestamp); + if (it != recent_captured_frames_.end()) + it->second.encoded = true; } void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) { @@ -235,15 +268,15 @@ void StatsEventSubscriber::RecordE2ELatency(const FrameEvent& frame_event) { if (!GetReceiverOffset(&receiver_offset)) return; - FrameEventTimeMap::iterator it = - frame_captured_times_.find(frame_event.rtp_timestamp); - if (it == frame_captured_times_.end()) + FrameInfoMap::iterator it = + recent_captured_frames_.find(frame_event.rtp_timestamp); + if (it == recent_captured_frames_.end()) return; // Playout time is event time + playout delay. base::TimeTicks playout_time = frame_event.timestamp + frame_event.delay_delta - receiver_offset; - total_e2e_latency_ += playout_time - it->second; + total_e2e_latency_ += playout_time - it->second.capture_time; e2e_latency_datapoints_++; } @@ -323,6 +356,24 @@ void StatsEventSubscriber::PopulateFpsStat(base::TimeTicks end_time, } } +void StatsEventSubscriber::PopulateFrameCountStat(CastLoggingEvent event, + CastStat stat, + StatsMap* stats_map) const { + FrameStatsMap::const_iterator it = frame_stats_.find(event); + if (it != frame_stats_.end()) { + stats_map->insert(std::make_pair(stat, it->second.event_counter)); + } +} + +void StatsEventSubscriber::PopulatePacketCountStat(CastLoggingEvent event, + CastStat stat, + StatsMap* stats_map) const { + PacketStatsMap::const_iterator it = packet_stats_.find(event); + if (it != packet_stats_.end()) { + stats_map->insert(std::make_pair(stat, it->second.event_counter)); + } +} + void StatsEventSubscriber::PopulatePlayoutDelayStat(StatsMap* stats_map) const { FrameStatsMap::const_iterator it = frame_stats_.find(FRAME_PLAYOUT); if (it != frame_stats_.end()) { @@ -396,5 +447,11 @@ StatsEventSubscriber::PacketLogStats::PacketLogStats() : event_counter(0), sum_size(0) {} StatsEventSubscriber::PacketLogStats::~PacketLogStats() {} +StatsEventSubscriber::FrameInfo::FrameInfo(base::TimeTicks capture_time) + : capture_time(capture_time), encoded(false) { +} +StatsEventSubscriber::FrameInfo::~FrameInfo() { +} + } // namespace cast } // namespace media diff --git a/media/cast/logging/stats_event_subscriber.h b/media/cast/logging/stats_event_subscriber.h index 173378ab0b28c..06ceaca9ed2f1 100644 --- a/media/cast/logging/stats_event_subscriber.h +++ b/media/cast/logging/stats_event_subscriber.h @@ -49,13 +49,15 @@ class StatsEventSubscriber : public RawEventSubscriber { private: friend class StatsEventSubscriberTest; FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, EmptyStats); - FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Capture); + FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, CaptureEncode); FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Encode); FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Decode); FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, PlayoutDelay); FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, E2ELatency); FRIEND_TEST_ALL_PREFIXES(StatsEventSubscriberTest, Packets); + static const size_t kMaxFrameInfoMapSize = 100; + // Generic statistics given the raw data. More specific data (e.g. frame rate // and bit rate) can be computed given the basic metrics. // Some of the metrics will only be set when applicable, e.g. delay and size. @@ -85,8 +87,7 @@ class StatsEventSubscriber : public RawEventSubscriber { // TODO(imcheng): This stat is not populated yet because we do not have // the time when encode started. Record it in FRAME_ENCODED event. AVG_ENCODE_TIME_MS, - // Average playout delay in milliseconds, with target delay already - // accounted for. Ideally, every frame should have a playout delay of 0. + // Average playout delay in milliseconds. AVG_PLAYOUT_DELAY_MS, // Duration from when a packet is transmitted to when it is received. // This measures latency from sender to receiver. @@ -102,11 +103,31 @@ class StatsEventSubscriber : public RawEventSubscriber { // Fraction of packet loss. PACKET_LOSS_FRACTION, // Duration in milliseconds since last receiver response. - MS_SINCE_LAST_RECEIVER_RESPONSE + MS_SINCE_LAST_RECEIVER_RESPONSE, + // Number of frames captured. + NUM_FRAMES_CAPTURED, + // Number of frames dropped by encoder. + NUM_FRAMES_DROPPED_BY_ENCODER, + // Number of late frames. + NUM_FRAMES_LATE, + // Number of packets that were sent (not retransmitted). + NUM_PACKETS_SENT, + // Number of packets that were retransmitted. + NUM_PACKETS_RETRANSMITTED, + // Number of packets that had their retransmission cancelled. + NUM_PACKETS_RTX_REJECTED, + }; + + struct FrameInfo { + explicit FrameInfo(base::TimeTicks capture_time); + ~FrameInfo(); + + base::TimeTicks capture_time; + bool encoded; }; typedef std::map StatsMap; - typedef std::map FrameEventTimeMap; + typedef std::map FrameInfoMap; typedef std::map< std::pair, std::pair > @@ -120,7 +141,8 @@ class StatsEventSubscriber : public RawEventSubscriber { void GetStatsInternal(StatsMap* stats_map) const; bool GetReceiverOffset(base::TimeDelta* offset); - void RecordFrameCapturedTime(const FrameEvent& frame_event); + void RecordFrameCaptureTime(const FrameEvent& frame_event); + void MarkAsEncoded(RtpTimestamp rtp_timestamp); void RecordE2ELatency(const FrameEvent& frame_event); void RecordPacketSentTime(const PacketEvent& packet_event); void ErasePacketSentTime(const PacketEvent& packet_event); @@ -131,6 +153,12 @@ class StatsEventSubscriber : public RawEventSubscriber { CastLoggingEvent event, CastStat stat, StatsMap* stats_map) const; + void PopulateFrameCountStat(CastLoggingEvent event, + CastStat stat, + StatsMap* stats_map) const; + void PopulatePacketCountStat(CastLoggingEvent event, + CastStat stat, + StatsMap* stats_map) const; void PopulatePlayoutDelayStat(StatsMap* stats_map) const; void PopulateFrameBitrateStat(base::TimeTicks now, StatsMap* stats_map) const; void PopulatePacketBitrateStat(base::TimeTicks now, @@ -157,8 +185,11 @@ class StatsEventSubscriber : public RawEventSubscriber { base::TimeTicks last_response_received_time_; - // Fixed size map to record when recent frames were captured. - FrameEventTimeMap frame_captured_times_; + int num_frames_dropped_by_encoder_; + int num_frames_late_; + + // Fixed size map to record when recent frames were captured and other info. + FrameInfoMap recent_captured_frames_; // Fixed size map to record when recent packets were sent. PacketEventTimeMap packet_sent_times_; diff --git a/media/cast/logging/stats_event_subscriber_unittest.cc b/media/cast/logging/stats_event_subscriber_unittest.cc index 09c418b4258dc..fe03bc6247354 100644 --- a/media/cast/logging/stats_event_subscriber_unittest.cc +++ b/media/cast/logging/stats_event_subscriber_unittest.cc @@ -65,20 +65,44 @@ class StatsEventSubscriberTest : public ::testing::Test { scoped_ptr subscriber_; }; -TEST_F(StatsEventSubscriberTest, Capture) { +TEST_F(StatsEventSubscriberTest, CaptureEncode) { Init(VIDEO_EVENT); uint32 rtp_timestamp = 0; uint32 frame_id = 0; - int num_frames = 10; + int extra_frames = 50; + // Only the first |extra_frames| frames logged will be taken into account + // when computing dropped frames. + int num_frames = StatsEventSubscriber::kMaxFrameInfoMapSize + 50; + int dropped_frames = 0; base::TimeTicks start_time = sender_clock_->NowTicks(); + // Drop half the frames during the encode step. for (int i = 0; i < num_frames; i++) { cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(), FRAME_CAPTURE_BEGIN, VIDEO_EVENT, rtp_timestamp, frame_id); - + AdvanceClocks(base::TimeDelta::FromMicroseconds(10)); + cast_environment_->Logging()->InsertFrameEvent(sender_clock_->NowTicks(), + FRAME_CAPTURE_END, + VIDEO_EVENT, + rtp_timestamp, + frame_id); + if (i % 2 == 0) { + AdvanceClocks(base::TimeDelta::FromMicroseconds(10)); + cast_environment_->Logging()->InsertEncodedFrameEvent( + sender_clock_->NowTicks(), + FRAME_ENCODED, + VIDEO_EVENT, + rtp_timestamp, + frame_id, + 1024, + true, + 5678); + } else if (i < extra_frames) { + dropped_frames++; + } AdvanceClocks(base::TimeDelta::FromMicroseconds(34567)); rtp_timestamp += 90; frame_id++; @@ -97,6 +121,16 @@ TEST_F(StatsEventSubscriberTest, Capture) { EXPECT_DOUBLE_EQ( it->second, static_cast(num_frames) / duration.InMillisecondsF() * 1000); + + it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_CAPTURED); + ASSERT_TRUE(it != stats_map.end()); + + EXPECT_DOUBLE_EQ(it->second, static_cast(num_frames)); + + it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_DROPPED_BY_ENCODER); + ASSERT_TRUE(it != stats_map.end()); + + EXPECT_DOUBLE_EQ(it->second, static_cast(dropped_frames)); } TEST_F(StatsEventSubscriberTest, Encode) { @@ -185,10 +219,12 @@ TEST_F(StatsEventSubscriberTest, PlayoutDelay) { uint32 frame_id = 0; int num_frames = 10; int total_delay_ms = 0; - for (int i = 0; i < num_frames; i++) { - int delay_ms = base::RandInt(-50, 50); + int late_frames = 0; + for (int i = 0, delay_ms = -50; i < num_frames; i++, delay_ms += 10) { base::TimeDelta delay = base::TimeDelta::FromMilliseconds(delay_ms); total_delay_ms += delay_ms; + if (delay_ms <= 0) + late_frames++; cast_environment_->Logging()->InsertFrameEventWithDelay( receiver_clock_.NowTicks(), FRAME_PLAYOUT, @@ -211,6 +247,11 @@ TEST_F(StatsEventSubscriberTest, PlayoutDelay) { EXPECT_DOUBLE_EQ( it->second, static_cast(total_delay_ms) / num_frames); + + it = stats_map.find(StatsEventSubscriber::NUM_FRAMES_LATE); + ASSERT_TRUE(it != stats_map.end()); + + EXPECT_DOUBLE_EQ(it->second, late_frames); } TEST_F(StatsEventSubscriberTest, E2ELatency) { @@ -268,11 +309,12 @@ TEST_F(StatsEventSubscriberTest, Packets) { int total_size = 0; int retransmit_total_size = 0; base::TimeDelta total_latency; - int num_packets_sent = 0; + int num_packets_transmitted = 0; int num_packets_retransmitted = 0; + int num_packets_rtx_rejected = 0; // Every 2nd packet will be retransmitted once. // Every 4th packet will be retransmitted twice. - // Every 8th packet will be retransmitted 3 times. + // Every 8th packet will be retransmitted 3 times + 1 rejected retransmission. for (int i = 0; i < num_packets; i++) { int size = 1000 + base::RandInt(-100, 100); total_size += size; @@ -285,7 +327,7 @@ TEST_F(StatsEventSubscriberTest, Packets) { i, num_packets - 1, size); - num_packets_sent++; + num_packets_transmitted++; int latency_micros = 20000 + base::RandInt(-10000, 10000); base::TimeDelta latency = base::TimeDelta::FromMicroseconds(latency_micros); @@ -312,7 +354,7 @@ TEST_F(StatsEventSubscriberTest, Packets) { num_packets - 1, size); retransmit_total_size += size; - num_packets_sent++; + num_packets_transmitted++; num_packets_retransmitted++; } @@ -329,7 +371,7 @@ TEST_F(StatsEventSubscriberTest, Packets) { num_packets - 1, size); retransmit_total_size += size; - num_packets_sent++; + num_packets_transmitted++; num_packets_retransmitted++; } @@ -345,9 +387,19 @@ TEST_F(StatsEventSubscriberTest, Packets) { i, num_packets - 1, size); + cast_environment_->Logging()->InsertPacketEvent( + receiver_clock_.NowTicks(), + PACKET_RTX_REJECTED, + VIDEO_EVENT, + rtp_timestamp, + 0, + i, + num_packets - 1, + size); retransmit_total_size += size; - num_packets_sent++; + num_packets_transmitted++; num_packets_retransmitted++; + num_packets_rtx_rejected++; } cast_environment_->Logging()->InsertPacketEvent(received_time, @@ -394,7 +446,22 @@ TEST_F(StatsEventSubscriberTest, Packets) { EXPECT_DOUBLE_EQ( it->second, - static_cast(num_packets_retransmitted) / num_packets_sent); + static_cast(num_packets_retransmitted) / num_packets_transmitted); + + it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_SENT); + ASSERT_TRUE(it != stats_map.end()); + + EXPECT_DOUBLE_EQ(it->second, static_cast(num_packets)); + + it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RETRANSMITTED); + ASSERT_TRUE(it != stats_map.end()); + + EXPECT_DOUBLE_EQ(it->second, static_cast(num_packets_retransmitted)); + + it = stats_map.find(StatsEventSubscriber::NUM_PACKETS_RTX_REJECTED); + ASSERT_TRUE(it != stats_map.end()); + + EXPECT_DOUBLE_EQ(it->second, static_cast(num_packets_rtx_rejected)); } } // namespace cast diff --git a/media/cast/net/cast_transport_defines.h b/media/cast/net/cast_transport_defines.h index fe329a2f44019..63407aa7ba617 100644 --- a/media/cast/net/cast_transport_defines.h +++ b/media/cast/net/cast_transport_defines.h @@ -61,64 +61,34 @@ typedef std::set PacketIdSet; // Each uint8 represents one cast frame. typedef std::map MissingFramesAndPacketsMap; +class FrameIdWrapHelperTest; + // TODO(miu): UGLY IN-LINE DEFINITION IN HEADER FILE! Move to appropriate // location, separated into .h and .cc files. class FrameIdWrapHelper { public: FrameIdWrapHelper() - : first_(true), frame_id_wrap_count_(0), range_(kLowRange) {} + : largest_frame_id_seen_(kStartFrameId) {} uint32 MapTo32bitsFrameId(const uint8 over_the_wire_frame_id) { - if (first_) { - first_ = false; - if (over_the_wire_frame_id == 0xff) { - // Special case for startup. - return kStartFrameId; - } + uint32 ret = (largest_frame_id_seen_ & ~0xff) | over_the_wire_frame_id; + // Add 1000 to both sides to avoid underflows. + if (1000 + ret - largest_frame_id_seen_ > 1000 + 127) { + ret -= 0x100; + } else if (1000 + ret - largest_frame_id_seen_ < 1000 - 128) { + ret += 0x100; } - - uint32 wrap_count = frame_id_wrap_count_; - switch (range_) { - case kLowRange: - if (over_the_wire_frame_id > kLowRangeThreshold && - over_the_wire_frame_id < kHighRangeThreshold) { - range_ = kMiddleRange; - } - if (over_the_wire_frame_id >= kHighRangeThreshold) { - // Wrap count was incremented in High->Low transition, but this frame - // is 'old', actually from before the wrap count got incremented. - --wrap_count; - } - break; - case kMiddleRange: - if (over_the_wire_frame_id >= kHighRangeThreshold) { - range_ = kHighRange; - } - break; - case kHighRange: - if (over_the_wire_frame_id <= kLowRangeThreshold) { - // Wrap-around detected. - range_ = kLowRange; - ++frame_id_wrap_count_; - // Frame triggering wrap-around so wrap count should be incremented as - // as well to match |frame_id_wrap_count_|. - ++wrap_count; - } - break; + if (1000 + ret - largest_frame_id_seen_ > 1000) { + largest_frame_id_seen_ = ret; } - return (wrap_count << 8) + over_the_wire_frame_id; + return ret; } private: - enum Range { kLowRange, kMiddleRange, kHighRange, }; - - static const uint8 kLowRangeThreshold = 63; - static const uint8 kHighRangeThreshold = 192; + friend class FrameIdWrapHelperTest; static const uint32 kStartFrameId = UINT32_C(0xffffffff); - bool first_; - uint32 frame_id_wrap_count_; - Range range_; + uint32 largest_frame_id_seen_; DISALLOW_COPY_AND_ASSIGN(FrameIdWrapHelper); }; diff --git a/media/cast/net/frame_id_wrap_helper_test.cc b/media/cast/net/frame_id_wrap_helper_test.cc index 92a8443533491..c40979d9283af 100644 --- a/media/cast/net/frame_id_wrap_helper_test.cc +++ b/media/cast/net/frame_id_wrap_helper_test.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include +#include "media/cast/cast_defines.h" #include "media/cast/net/cast_transport_defines.h" namespace media { @@ -13,6 +14,25 @@ class FrameIdWrapHelperTest : public ::testing::Test { FrameIdWrapHelperTest() {} virtual ~FrameIdWrapHelperTest() {} + void RunOneTest(uint32 starting_point, int iterations) { + const int window_size = 127; + uint32 window_base = starting_point; + frame_id_wrap_helper_.largest_frame_id_seen_ = starting_point; + for (int i = 0; i < iterations; i++) { + uint32 largest_frame_id_seen = + frame_id_wrap_helper_.largest_frame_id_seen_; + int offset = rand() % window_size; + uint32 frame_id = window_base + offset; + uint32 mapped_frame_id = + frame_id_wrap_helper_.MapTo32bitsFrameId(frame_id & 0xff); + EXPECT_EQ(frame_id, mapped_frame_id) + << " Largest ID seen: " << largest_frame_id_seen + << " Window base: " << window_base + << " Offset: " << offset; + window_base = frame_id; + } + } + FrameIdWrapHelper frame_id_wrap_helper_; DISALLOW_COPY_AND_ASSIGN(FrameIdWrapHelperTest); @@ -46,5 +66,15 @@ TEST_F(FrameIdWrapHelperTest, OutOfOrder) { EXPECT_EQ(257u, new_frame_id); } +TEST_F(FrameIdWrapHelperTest, Windowed) { + srand(0); + for (int i = 0; i < 50000 && !HasFailure(); i++) { + RunOneTest(i * 4711, 20); + // Test wrap-around scenarios. + RunOneTest(0x7fffff00ul, 20); + RunOneTest(0xffffff00ul, 20); + } +} + } // namespace cast } // namespace media diff --git a/media/cast/net/udp_transport.cc b/media/cast/net/udp_transport.cc index 64bcb8fd202d6..00bd822a4fd78 100644 --- a/media/cast/net/udp_transport.cc +++ b/media/cast/net/udp_transport.cc @@ -172,8 +172,11 @@ bool UdpTransport::SendPacket(PacketRef packet, const base::Closure& cb) { VLOG(1) << "Unable to set DSCP: " << next_dscp_value_ << " to socket; Error: " << result; } - // Don't change DSCP in next send. - next_dscp_value_ = net::DSCP_NO_CHANGE; + + if (result != net::ERR_SOCKET_NOT_CONNECTED) { + // Don't change DSCP in next send. + next_dscp_value_ = net::DSCP_NO_CHANGE; + } } scoped_refptr buf = diff --git a/media/cast/sender/audio_sender.cc b/media/cast/sender/audio_sender.cc index 154710dbcefd9..704621da3822a 100644 --- a/media/cast/sender/audio_sender.cc +++ b/media/cast/sender/audio_sender.cc @@ -30,7 +30,7 @@ int GetMaxUnackedFrames(base::TimeDelta target_delay) { // receiver has the ability to drop any one of the packets. // We send up to three times of the target delay of audio frames. int frames = - 1 + 3 * target_delay * kAudioFrameRate / base::TimeDelta::FromSeconds(1); + 1 + 2 * target_delay * kAudioFrameRate / base::TimeDelta::FromSeconds(1); return std::min(kMaxUnackedFrames, frames); } } // namespace diff --git a/media/cast/sender/congestion_control.cc b/media/cast/sender/congestion_control.cc index 70fcfe9dc99ba..9efe50adbf823 100644 --- a/media/cast/sender/congestion_control.cc +++ b/media/cast/sender/congestion_control.cc @@ -113,9 +113,16 @@ void CongestionControl::AckFrame(uint32 frame_id, base::TimeTicks when) { FrameStats* frame_stats = GetFrameStats(last_acked_frame_); while (IsNewerFrameId(frame_id, last_acked_frame_)) { FrameStats* last_frame_stats = frame_stats; - last_acked_frame_++; - frame_stats = GetFrameStats(last_acked_frame_); + frame_stats = GetFrameStats(last_acked_frame_ + 1); DCHECK(frame_stats); + if (frame_stats->sent_time.is_null()) { + // Can't ack a frame that hasn't been sent yet. + return; + } + last_acked_frame_++; + if (when < frame_stats->sent_time) + when = frame_stats->sent_time; + frame_stats->ack_time = when; acked_bits_in_history_ += frame_stats->frame_size; dead_time_in_history_ += DeadTime(*last_frame_stats, *frame_stats); diff --git a/media/cast/sender/external_video_encoder.cc b/media/cast/sender/external_video_encoder.cc index e3abecd4074cb..fbc24d82cb20c 100644 --- a/media/cast/sender/external_video_encoder.cc +++ b/media/cast/sender/external_video_encoder.cc @@ -9,6 +9,7 @@ #include "base/memory/scoped_vector.h" #include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" +#include "base/metrics/histogram.h" #include "media/base/video_frame.h" #include "media/base/video_util.h" #include "media/cast/cast_defines.h" @@ -34,25 +35,6 @@ void LogFrameEncodedEvent( event_time, media::cast::FRAME_ENCODED, media::cast::VIDEO_EVENT, rtp_timestamp, frame_id); } - -// Proxy this call to ExternalVideoEncoder on the cast main thread. -void ProxyCreateVideoEncodeAccelerator( - const scoped_refptr& cast_environment, - const base::WeakPtr& weak_ptr, - const media::cast::CreateVideoEncodeMemoryCallback& - create_video_encode_mem_cb, - scoped_refptr encoder_task_runner, - scoped_ptr vea) { - cast_environment->PostTask( - media::cast::CastEnvironment::MAIN, - FROM_HERE, - base::Bind( - &media::cast::ExternalVideoEncoder::OnCreateVideoEncodeAccelerator, - weak_ptr, - create_video_encode_mem_cb, - encoder_task_runner, - base::Passed(&vea))); -} } // namespace namespace media { @@ -76,20 +58,28 @@ class LocalVideoEncodeAcceleratorClient : public VideoEncodeAccelerator::Client, public base::RefCountedThreadSafe { public: - LocalVideoEncodeAcceleratorClient( + // Create an instance of this class and post a task to create + // video_encode_accelerator_. A ref to |this| will be kept, awaiting reply + // via ProxyCreateVideoEncodeAccelerator, which will provide us with the + // encoder task runner and vea instance. We cannot be destroyed until we + // receive the reply, otherwise the VEA object created may leak. + static scoped_refptr Create( scoped_refptr cast_environment, - scoped_refptr encoder_task_runner, - scoped_ptr vea, + const CreateVideoEncodeAcceleratorCallback& create_vea_cb, const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, - const base::WeakPtr& weak_owner) - : cast_environment_(cast_environment), - encoder_task_runner_(encoder_task_runner), - video_encode_accelerator_(vea.Pass()), - create_video_encode_memory_cb_(create_video_encode_mem_cb), - weak_owner_(weak_owner), - last_encoded_frame_id_(kStartFrameId), - key_frame_encountered_(false) { - DCHECK(encoder_task_runner_); + const base::WeakPtr& weak_owner) { + scoped_refptr client( + new LocalVideoEncodeAcceleratorClient( + cast_environment, create_video_encode_mem_cb, weak_owner)); + + // This will keep a ref to |client|, if weak_owner is destroyed before + // ProxyCreateVideoEncodeAccelerator is called, we will stay alive until + // we can properly destroy the VEA. + create_vea_cb.Run(base::Bind( + &LocalVideoEncodeAcceleratorClient::OnCreateVideoEncodeAcceleratorProxy, + client)); + + return client; } // Initialize the real HW encoder. @@ -100,7 +90,7 @@ class LocalVideoEncodeAcceleratorClient VideoCodecProfile output_profile = media::VIDEO_CODEC_PROFILE_UNKNOWN; switch (video_config.codec) { case CODEC_VIDEO_VP8: - output_profile = media::VP8PROFILE_MAIN; + output_profile = media::VP8PROFILE_ANY; break; case CODEC_VIDEO_H264: output_profile = media::H264PROFILE_MAIN; @@ -114,12 +104,16 @@ class LocalVideoEncodeAcceleratorClient } max_frame_rate_ = video_config.max_frame_rate; - if (!video_encode_accelerator_->Initialize( - media::VideoFrame::I420, - gfx::Size(video_config.width, video_config.height), - output_profile, - video_config.start_bitrate, - this)) { + bool result = video_encode_accelerator_->Initialize( + media::VideoFrame::I420, + gfx::Size(video_config.width, video_config.height), + output_profile, + video_config.start_bitrate, + this); + + UMA_HISTOGRAM_BOOLEAN("Cast.Sender.VideoEncodeAcceleratorInitializeSuccess", + result); + if (!result) { NotifyError(VideoEncodeAccelerator::kInvalidArgumentError); return; } @@ -128,12 +122,22 @@ class LocalVideoEncodeAcceleratorClient // initialized. } - // Free the HW. + // Destroy the VEA on the correct thread. void Destroy() { DCHECK(encoder_task_runner_); - DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); + if (!video_encode_accelerator_) + return; - video_encode_accelerator_.reset(); + if (encoder_task_runner_->RunsTasksOnCurrentThread()) { + video_encode_accelerator_.reset(); + } else { + // We do this instead of just reposting to encoder_task_runner_, because + // we are called from the destructor. + encoder_task_runner_->PostTask( + FROM_HERE, + base::Bind(&DestroyVideoEncodeAcceleratorOnEncoderThread, + base::Passed(&video_encode_accelerator_))); + } } void SetBitRate(uint32 bit_rate) { @@ -165,7 +169,6 @@ class LocalVideoEncodeAcceleratorClient DCHECK(encoder_task_runner_->RunsTasksOnCurrentThread()); VLOG(1) << "ExternalVideoEncoder NotifyError: " << error; - video_encode_accelerator_.reset(); cast_environment_->PostTask( CastEnvironment::MAIN, FROM_HERE, @@ -269,6 +272,46 @@ class LocalVideoEncodeAcceleratorClient } private: + LocalVideoEncodeAcceleratorClient( + scoped_refptr cast_environment, + const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, + const base::WeakPtr& weak_owner) + : cast_environment_(cast_environment), + create_video_encode_memory_cb_(create_video_encode_mem_cb), + weak_owner_(weak_owner), + last_encoded_frame_id_(kStartFrameId), + key_frame_encountered_(false) {} + + // Trampoline VEA creation callback to OnCreateVideoEncodeAccelerator() + // on encoder_task_runner. Normally we would just repost the same method to + // it, and would not need a separate proxy method, but we can't + // ThreadTaskRunnerHandle::Get() in unittests just yet. + void OnCreateVideoEncodeAcceleratorProxy( + scoped_refptr encoder_task_runner, + scoped_ptr vea) { + encoder_task_runner->PostTask( + FROM_HERE, + base::Bind(&media::cast::LocalVideoEncodeAcceleratorClient:: + OnCreateVideoEncodeAccelerator, + this, + encoder_task_runner, + base::Passed(&vea))); + } + + void OnCreateVideoEncodeAccelerator( + scoped_refptr encoder_task_runner, + scoped_ptr vea) { + encoder_task_runner_ = encoder_task_runner; + video_encode_accelerator_.reset(vea.release()); + + cast_environment_->PostTask( + CastEnvironment::MAIN, + FROM_HERE, + base::Bind(&ExternalVideoEncoder::OnCreateVideoEncodeAccelerator, + weak_owner_, + encoder_task_runner_)); + } + // Note: This method can be called on any thread. void OnCreateSharedMemory(scoped_ptr memory) { encoder_task_runner_->PostTask( @@ -302,9 +345,17 @@ class LocalVideoEncodeAcceleratorClient base::Bind(&ExternalVideoEncoder::EncoderInitialized, weak_owner_)); } + static void DestroyVideoEncodeAcceleratorOnEncoderThread( + scoped_ptr vea) { + // VEA::~VEA specialization takes care of calling Destroy() on the VEA impl. + } + friend class base::RefCountedThreadSafe; - virtual ~LocalVideoEncodeAcceleratorClient() {} + virtual ~LocalVideoEncodeAcceleratorClient() { + Destroy(); + DCHECK(!video_encode_accelerator_); + } const scoped_refptr cast_environment_; scoped_refptr encoder_task_runner_; @@ -337,17 +388,15 @@ ExternalVideoEncoder::ExternalVideoEncoder( weak_factory_(this) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); - create_vea_cb.Run(base::Bind(&ProxyCreateVideoEncodeAccelerator, - cast_environment, - weak_factory_.GetWeakPtr(), - create_video_encode_mem_cb)); + video_accelerator_client_ = + LocalVideoEncodeAcceleratorClient::Create(cast_environment_, + create_vea_cb, + create_video_encode_mem_cb, + weak_factory_.GetWeakPtr()); + DCHECK(video_accelerator_client_); } ExternalVideoEncoder::~ExternalVideoEncoder() { - encoder_task_runner_->PostTask( - FROM_HERE, - base::Bind(&LocalVideoEncodeAcceleratorClient::Destroy, - video_accelerator_client_)); } void ExternalVideoEncoder::EncoderInitialized() { @@ -361,18 +410,10 @@ void ExternalVideoEncoder::EncoderError() { } void ExternalVideoEncoder::OnCreateVideoEncodeAccelerator( - const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, - scoped_refptr encoder_task_runner, - scoped_ptr vea) { + scoped_refptr encoder_task_runner) { DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); encoder_task_runner_ = encoder_task_runner; - video_accelerator_client_ = - new LocalVideoEncodeAcceleratorClient(cast_environment_, - encoder_task_runner, - vea.Pass(), - create_video_encode_mem_cb, - weak_factory_.GetWeakPtr()); encoder_task_runner_->PostTask( FROM_HERE, base::Bind(&LocalVideoEncodeAcceleratorClient::Initialize, @@ -429,6 +470,5 @@ void ExternalVideoEncoder::GenerateKeyFrame() { void ExternalVideoEncoder::LatestFrameIdToReference(uint32 /*frame_id*/) { // Do nothing not supported. } - } // namespace cast } // namespace media diff --git a/media/cast/sender/external_video_encoder.h b/media/cast/sender/external_video_encoder.h index 84de7f08f4c26..269fb3e7c88a5 100644 --- a/media/cast/sender/external_video_encoder.h +++ b/media/cast/sender/external_video_encoder.h @@ -50,11 +50,10 @@ class ExternalVideoEncoder : public VideoEncoder { virtual void GenerateKeyFrame() OVERRIDE; virtual void LatestFrameIdToReference(uint32 frame_id) OVERRIDE; - // Called when a VEA is created. + // Called when video_accelerator_client_ has finished creating the VEA and + // is ready for use. void OnCreateVideoEncodeAccelerator( - const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, - scoped_refptr encoder_task_runner, - scoped_ptr vea); + scoped_refptr encoder_task_runner); protected: void EncoderInitialized(); diff --git a/media/cast/sender/external_video_encoder_unittest.cc b/media/cast/sender/external_video_encoder_unittest.cc index 9461a11f268a7..385b121695e99 100644 --- a/media/cast/sender/external_video_encoder_unittest.cc +++ b/media/cast/sender/external_video_encoder_unittest.cc @@ -23,12 +23,26 @@ using testing::_; namespace { -void CreateVideoEncodeAccelerator( - const scoped_refptr& task_runner, - scoped_ptr fake_vea, - const ReceiveVideoEncodeAcceleratorCallback& callback) { - callback.Run(task_runner, fake_vea.Pass()); -} +class VEAFactory { + public: + VEAFactory(const scoped_refptr& task_runner, + scoped_ptr vea) + : task_runner_(task_runner), vea_(vea.Pass()) {} + + void CreateVideoEncodeAccelerator( + const ReceiveVideoEncodeAcceleratorCallback& callback) { + create_cb_ = callback; + } + + void FinishCreatingVideoEncodeAccelerator() { + create_cb_.Run(task_runner_, vea_.Pass()); + } + + private: + const scoped_refptr task_runner_; + scoped_ptr vea_; + ReceiveVideoEncodeAcceleratorCallback create_cb_; +}; void CreateSharedMemory( size_t size, const ReceiveVideoEncodeMemoryCallback& callback) { @@ -116,13 +130,14 @@ class ExternalVideoEncoderTest : public ::testing::Test { fake_vea_ = new test::FakeVideoEncodeAccelerator(task_runner_, &stored_bitrates_); scoped_ptr fake_vea(fake_vea_); - video_encoder_.reset( - new ExternalVideoEncoder(cast_environment_, - video_config_, - base::Bind(&CreateVideoEncodeAccelerator, - task_runner_, - base::Passed(&fake_vea)), - base::Bind(&CreateSharedMemory))); + VEAFactory vea_factory(task_runner_, fake_vea.Pass()); + video_encoder_.reset(new ExternalVideoEncoder( + cast_environment_, + video_config_, + base::Bind(&VEAFactory::CreateVideoEncodeAccelerator, + base::Unretained(&vea_factory)), + base::Bind(&CreateSharedMemory))); + vea_factory.FinishCreatingVideoEncodeAccelerator(); } virtual ~ExternalVideoEncoderTest() {} @@ -193,5 +208,35 @@ TEST_F(ExternalVideoEncoderTest, StreamHeader) { task_runner_->RunTasks(); } +// Verify that everything goes well even if ExternalVideoEncoder is destroyed +// before it has a chance to receive the VEA creation callback. +TEST(ExternalVideoEncoderEarlyDestroyTest, DestroyBeforeVEACreatedCallback) { + VideoSenderConfig video_config; + base::SimpleTestTickClock* testing_clock = new base::SimpleTestTickClock(); + scoped_refptr task_runner( + new test::FakeSingleThreadTaskRunner(testing_clock)); + scoped_refptr cast_environment( + new CastEnvironment(scoped_ptr(testing_clock).Pass(), + task_runner, + task_runner, + task_runner)); + + std::vector stored_bitrates; + scoped_ptr fake_vea( + new test::FakeVideoEncodeAccelerator(task_runner, &stored_bitrates)); + VEAFactory vea_factory(task_runner, fake_vea.Pass()); + + scoped_ptr video_encoder(new ExternalVideoEncoder( + cast_environment, + video_config, + base::Bind(&VEAFactory::CreateVideoEncodeAccelerator, + base::Unretained(&vea_factory)), + base::Bind(&CreateSharedMemory))); + + video_encoder.reset(); + vea_factory.FinishCreatingVideoEncodeAccelerator(); + task_runner->RunTasks(); +} + } // namespace cast } // namespace media diff --git a/media/cast/test/fake_video_encode_accelerator.cc b/media/cast/test/fake_video_encode_accelerator.cc index a391a9a81c608..0442c0c9283e7 100644 --- a/media/cast/test/fake_video_encode_accelerator.cc +++ b/media/cast/test/fake_video_encode_accelerator.cc @@ -38,7 +38,7 @@ bool FakeVideoEncodeAccelerator::Initialize( uint32 initial_bitrate, Client* client) { client_ = client; - if (output_profile != media::VP8PROFILE_MAIN && + if (output_profile != media::VP8PROFILE_ANY && output_profile != media::H264PROFILE_MAIN) { return false; } diff --git a/media/cast/test/simulator.cc b/media/cast/test/simulator.cc index 157a18fcc346e..b3360c8722b0b 100644 --- a/media/cast/test/simulator.cc +++ b/media/cast/test/simulator.cc @@ -283,10 +283,12 @@ void RunSimulation(const base::FilePath& source_path, // Connect sender to receiver. This initializes the pipe. receiver_to_sender.Initialize( - ipp.NewBuffer(128 * 1024), transport_sender->PacketReceiverForTesting(), + ipp.NewBuffer(128 * 1024).Pass(), + transport_sender->PacketReceiverForTesting(), task_runner, &testing_clock); sender_to_receiver.Initialize( - ipp.NewBuffer(128 * 1024), cast_receiver->packet_receiver(), task_runner, + ipp.NewBuffer(128 * 1024).Pass(), + cast_receiver->packet_receiver(), task_runner, &testing_clock); // Start receiver. diff --git a/media/cast/test/utility/netload.py b/media/cast/test/utility/netload.py new file mode 100755 index 0000000000000..248eca2bbefc7 --- /dev/null +++ b/media/cast/test/utility/netload.py @@ -0,0 +1,100 @@ +#!/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. +# +# Simple client/server script for generating an unlimited TCP stream. +# see shadow.sh for how it's intended to be used. + +import socket +import sys +import thread +import time + +sent = 0 +received = 0 + +def Sink(socket): + global received + while True: + tmp = socket.recv(4096) + received += len(tmp) + if not tmp: + break; + +def Spew(socket): + global sent + data = " " * 4096 + while True: + tmp = socket.send(data) + if tmp <= 0: + break + sent += tmp; + +def PrintStats(): + global sent + global received + last_report = time.time() + last_sent = 0 + last_received = 0 + while True: + time.sleep(5) + now = time.time(); + sent_now = sent + received_now = received + delta = now - last_report + sent_mbps = ((sent_now - last_sent) * 8.0 / 1000000) / delta + received_mbps = ((received_now - last_received) * 8.0 / 1000000) / delta + print "Sent: %5.2f mbps Received: %5.2f mbps" % (sent_mbps, received_mbps) + last_report = now + last_sent = sent_now + last_received = received_now + +def Serve(socket, upload=True, download=True): + while True: + (s, addr) = socket.accept() + if upload: + thread.start_new_thread(Spew, (s,)) + if download: + thread.start_new_thread(Sink, (s,)) + +def Receiver(port, upload=True, download=True): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind(('', port)) + s.listen(5) + thread.start_new_thread(Serve, (s, upload, download)) + + +def Connect(to_hostport, upload=True, download=False): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.connect(to_hostport) + if upload: + thread.start_new_thread(Spew, (s,)) + if download: + thread.start_new_thread(Sink, (s,)) + + +def Usage(): + print "One of:" + print "%s listen " % sys.arv[0] + print "%s upload " % sys.arv[0] + print "%s download " % sys.arv[0] + print "%s updown " % sys.arv[0] + sys.exit(1) + +if len(sys.argv) < 2: + Usage() +if sys.argv[1] == "listen": + Receiver(int(sys.argv[2])) +elif sys.argv[1] == "download": + Connect( (sys.argv[2], int(sys.argv[3])), upload=False, download=True) +elif sys.argv[1] == "upload": + Connect( (sys.argv[2], int(sys.argv[3])), upload=True, download=False) +elif sys.argv[1] == "updown": + Connect( (sys.argv[2], int(sys.argv[3])), upload=True, download=True) +else: + Usage() + +PrintStats() diff --git a/media/cast/test/utility/shadow.sh b/media/cast/test/utility/shadow.sh new file mode 100755 index 0000000000000..4163fd0859cba --- /dev/null +++ b/media/cast/test/utility/shadow.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# 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. +# +# The purpose of this script is to set up all the neccessary magic to +# pipe network traffic through a user-space process. That user-space +# process can then delay, reorder and drop packets as it pleases to +# emulate various network environments. +# +# The script currently assumes that you communicate with your cast streaming +# receiver through eth1. After running "shadow.sh start", your network will +# look something like this: +# +# +--------------------------------------------------+ +# | Your linux machine | +# | +---------------+ | +# cast | |shadowbr bridge| +-------------+ | +# streaming <--+-+---> eth1 | |routing table| | +# receiver | | tap2 <---+-> tap_proxy <-+-> tap1 | | +# | | +->veth | | eth0 <----+--+->internet +# | +--+------------+ | lo | | +# | | +-------------+ | +# | | +------------------+ ^ | +# | | |shadow container | | | +# | +------+-->veth | chrome | +# | | netload.py server| netload.py client| +# | +------------------+ | +# +--------------------------------------------------+ +# +# The result should be that all traffic to/from the cast streaming receiver +# will go through tap_proxy. All traffic to/from the shadow container +# will also go through the tap_proxy. (A container is kind of like a +# virtual machine, but more lightweight.) Running "shadow.sh start" does +# not start the tap_proxy, so you'll have to start it manually with +# the command "tap_proxy tap1 tap2 " where +# is one of "perfect", "good", "wifi", "bad" or "evil". +# +# While testing mirroring, we can now generate TCP traffic through +# the tap proxy by talking to the netload server inside the "shadow" +# container by using the following command: +# +# $ netload.py upload IP PORT +# +# The IP and PORT are printed out by this script when you run +# "shadow.sh start", but will generally be the *.*.*.253 address +# of the eth1 network, so hopefully that's not already taken... + +set -x + +DEV=eth1 +TAP1=tap1 +TAP2=tap2 + +IP="$(ifconfig $DEV | sed -n 's@.*inet addr:\([^ ]*\).*@\1@gp')" +MASK="$(ifconfig $DEV | sed -n 's@.*Mask:\([^ ]*\).*@\1@gp')" +BCAST="$(ifconfig $DEV | sed -n 's@.*Bcast:\([^ ]*\).*@\1@gp')" +NET=$(route -n | grep $DEV | head -1 | awk '{print $1}') +DIR=$(dirname "$0") + +case "$MASK" in + 255.255.255.0) MASK_BITS=24 ;; + 255.255.0.0) MASK_BITS=16 ;; + 255.0.0.0) MASK_BITS=8 ;; + *) + echo "Unknown network mask" + exit 1 + ;; +esac + +SHADOWIP="$(echo $IP | sed 's@[^.]*$@@g')253" +SHADOWCONF="/tmp/shadowconf.$$" +cat <$SHADOWCONF +lxc.utsname = shadow +lxc.network.type = veth +lxc.network.link = shadowbr +lxc.network.flags = up +lxc.network.ipv4 = $SHADOWIP/$MASK_BITS +lxc.network.ipv4.gateway = $IP +lxc.kmsg = 0 +EOF + +trap "rm $SHADOWCONF" SIGINT SIGTERM EXIT +LXC_COMMON="-n shadow -f $SHADOWCONF" + +case "$1" in + start) + openvpn --mktun --dev $TAP1 + openvpn --mktun --dev $TAP2 + ifconfig $TAP1 $IP netmask $MASK broadcast $BCAST up + ifconfig $TAP2 up + route add -net $NET netmask $MASK $TAP1 + brctl addbr shadowbr + brctl addif shadowbr $TAP2 $DEV + ifconfig shadowbr up + lxc-create $LXC_COMMON + lxc-execute $LXC_COMMON -- \ + "$DIRNAME/netload.py listen 9999" >/dev/null &1 & + echo "Now run: tap_proxy $TAP1 $TAP2 wifi" + echo "Data sink/source is available on $SHADOWIP 9999" + ;; + + stop) + lxc-kill -n shadow + sleep 1 + lxc-destroy $LXC_COMMON + ifconfig $TAP1 down + ifconfig $TAP2 down + ifconfig shadowbr down + brctl delbr shadowbr + openvpn --rmtun --dev $TAP1 + openvpn --rmtun --dev $TAP2 + ;; + + *) + echo "$0 start/stop" + echo "Read $0 for more information." + ;; +esac + + diff --git a/media/cast/test/utility/tap_proxy.cc b/media/cast/test/utility/tap_proxy.cc new file mode 100644 index 0000000000000..7827bf976ff51 --- /dev/null +++ b/media/cast/test/utility/tap_proxy.cc @@ -0,0 +1,318 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/thread.h" +#include "base/time/default_tick_clock.h" +#include "media/cast/test/utility/udp_proxy.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/udp/udp_socket.h" + +namespace media { +namespace cast { +namespace test { + +const size_t kMaxPacketSize = 4096; + +class SendToFDPipe : public PacketPipe { + public: + explicit SendToFDPipe(int fd) : fd_(fd) { + } + virtual void Send(scoped_ptr packet) OVERRIDE { + while (1) { + int written = write( + fd_, + reinterpret_cast(&packet->front()), + packet->size()); + if (written < 0) { + if (errno == EINTR) continue; + perror("write"); + exit(1); + } + if (written != static_cast(packet->size())) { + fprintf(stderr, "Truncated write!\n"); + exit(1); + } + break; + } + } + private: + int fd_; +}; + +class QueueManager : public base::MessageLoopForIO::Watcher { + public: + QueueManager(int input_fd, + int output_fd, + scoped_ptr pipe) : + input_fd_(input_fd), + packet_pipe_(pipe.Pass()) { + + CHECK(base::MessageLoopForIO::current()->WatchFileDescriptor( + input_fd_, true, base::MessageLoopForIO::WATCH_READ, + &read_socket_watcher_, this)); + + scoped_ptr tmp(new SendToFDPipe(output_fd)); + if (packet_pipe_) { + packet_pipe_->AppendToPipe(tmp.Pass()); + } else { + packet_pipe_ = tmp.Pass(); + } + packet_pipe_->InitOnIOThread(base::MessageLoopProxy::current(), + &tick_clock_); + } + + virtual ~QueueManager() { + } + + // MessageLoopForIO::Watcher methods + virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE { + scoped_ptr packet(new Packet(kMaxPacketSize)); + int nread = read(input_fd_, + reinterpret_cast(&packet->front()), + kMaxPacketSize); + if (nread < 0) { + if (errno == EINTR) return; + perror("read"); + exit(1); + } + if (nread == 0) return; + packet->resize(nread); + packet_pipe_->Send(packet.Pass()); + } + virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE { + NOTREACHED(); + } + + private: + int input_fd_; + scoped_ptr packet_pipe_; + base::MessageLoopForIO::FileDescriptorWatcher read_socket_watcher_; + base::DefaultTickClock tick_clock_; +}; + +} // namespace test +} // namespace cast +} // namespace media + +base::TimeTicks last_printout; + +class ByteCounter { + public: + ByteCounter() : bytes_(0), packets_(0) { + push(base::TimeTicks::Now()); + } + + base::TimeDelta time_range() { + return time_data_.back() - time_data_.front(); + } + + void push(base::TimeTicks now) { + byte_data_.push_back(bytes_); + packet_data_.push_back(packets_); + time_data_.push_back(now); + while (time_range().InSeconds() > 10) { + byte_data_.pop_front(); + packet_data_.pop_front(); + time_data_.pop_front(); + } + } + + double megabits_per_second() { + double megabits = (byte_data_.back() - byte_data_.front()) * 8 / 1E6; + return megabits / time_range().InSecondsF(); + } + + double packets_per_second() { + double packets = packet_data_.back()- packet_data_.front(); + return packets / time_range().InSecondsF(); + } + + void Increment(uint64 x) { + bytes_ += x; + packets_ ++; + } + + private: + uint64 bytes_; + uint64 packets_; + std::deque byte_data_; + std::deque packet_data_; + std::deque time_data_; +}; + +ByteCounter in_pipe_input_counter; +ByteCounter in_pipe_output_counter; +ByteCounter out_pipe_input_counter; +ByteCounter out_pipe_output_counter; + +class ByteCounterPipe : public media::cast::test::PacketPipe { + public: + ByteCounterPipe(ByteCounter* counter) : counter_(counter) {} + virtual void Send(scoped_ptr packet) + OVERRIDE { + counter_->Increment(packet->size()); + pipe_->Send(packet.Pass()); + } + private: + ByteCounter* counter_; +}; + +void SetupByteCounters(scoped_ptr* pipe, + ByteCounter* pipe_input_counter, + ByteCounter* pipe_output_counter) { + media::cast::test::PacketPipe* new_pipe = + new ByteCounterPipe(pipe_input_counter); + new_pipe->AppendToPipe(pipe->Pass()); + new_pipe->AppendToPipe( + scoped_ptr( + new ByteCounterPipe(pipe_output_counter)).Pass()); + pipe->reset(new_pipe); +} + +void CheckByteCounters() { + base::TimeTicks now = base::TimeTicks::Now(); + in_pipe_input_counter.push(now); + in_pipe_output_counter.push(now); + out_pipe_input_counter.push(now); + out_pipe_output_counter.push(now); + if ((now - last_printout).InSeconds() >= 5) { + fprintf(stderr, "Sending : %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n", + in_pipe_output_counter.megabits_per_second(), + in_pipe_input_counter.megabits_per_second(), + in_pipe_output_counter.packets_per_second(), + in_pipe_input_counter.packets_per_second()); + fprintf(stderr, "Receiving: %5.2f / %5.2f mbps %6.2f / %6.2f packets / s\n", + out_pipe_output_counter.megabits_per_second(), + out_pipe_input_counter.megabits_per_second(), + out_pipe_output_counter.packets_per_second(), + out_pipe_input_counter.packets_per_second()); + + last_printout = now; + } + base::MessageLoopProxy::current()->PostDelayedTask( + FROM_HERE, + base::Bind(&CheckByteCounters), + base::TimeDelta::FromMilliseconds(100)); +} + +int tun_alloc(char *dev, int flags) { + struct ifreq ifr; + int fd, err; + const char *clonedev = "/dev/net/tun"; + + /* Arguments taken by the function: + * + * char *dev: the name of an interface (or '\0'). MUST have enough + * space to hold the interface name if '\0' is passed + * int flags: interface flags (eg, IFF_TUN etc.) + */ + + /* open the clone device */ + if( (fd = open(clonedev, O_RDWR)) < 0 ) { + return fd; + } + + /* preparation of the struct ifr, of type "struct ifreq" */ + memset(&ifr, 0, sizeof(ifr)); + + ifr.ifr_flags = flags; /* IFF_TUN or IFF_TAP, plus maybe IFF_NO_PI */ + + if (*dev) { + /* if a device name was specified, put it in the structure; otherwise, + * the kernel will try to allocate the "next" device of the + * specified type */ + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + } + + /* try to create the device */ + if( (err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0 ) { + close(fd); + return err; + } + + if (!*dev) { + /* if the operation was successful, write back the name of the + * interface to the variable "dev", so the caller can know + * it. Note that the caller MUST reserve space in *dev (see calling + * code below) */ + strcpy(dev, ifr.ifr_name); + } + + /* this is the special file descriptor that the caller will use to talk + * with the virtual interface */ + return fd; +} + + +int main(int argc, char **argv) { + base::AtExitManager exit_manager; + CommandLine::Init(argc, argv); + InitLogging(logging::LoggingSettings()); + + if (argc < 4) { + fprintf(stderr, "Usage: tap_proxy tap1 tap2 type\n"); + fprintf(stderr, + "Where 'type' is one of perfect, good, wifi, bad or evil\n"); + exit(1); + } + + scoped_ptr in_pipe, out_pipe; + std::string network_type = argv[3]; + if (network_type == "perfect") { + // No action needed. + } else if (network_type == "good") { + in_pipe = media::cast::test::GoodNetwork().Pass(); + out_pipe = media::cast::test::GoodNetwork().Pass(); + } else if (network_type == "wifi") { + in_pipe = media::cast::test::WifiNetwork().Pass(); + out_pipe = media::cast::test::WifiNetwork().Pass(); + } else if (network_type == "bad") { + in_pipe = media::cast::test::BadNetwork().Pass(); + out_pipe = media::cast::test::BadNetwork().Pass(); + } else if (network_type == "evil") { + in_pipe = media::cast::test::EvilNetwork().Pass(); + out_pipe = media::cast::test::EvilNetwork().Pass(); + } else { + fprintf(stderr, "Unknown network type.\n"); + exit(1); + } + + SetupByteCounters(&in_pipe, &in_pipe_input_counter, &in_pipe_output_counter); + SetupByteCounters( + &out_pipe, &out_pipe_input_counter, &out_pipe_output_counter); + + int fd1 = tun_alloc(argv[1], IFF_TAP); + int fd2 = tun_alloc(argv[2], IFF_TAP); + + base::MessageLoopForIO message_loop; + last_printout = base::TimeTicks::Now(); + media::cast::test::QueueManager qm1(fd1, fd2, in_pipe.Pass()); + media::cast::test::QueueManager qm2(fd2, fd1, out_pipe.Pass()); + CheckByteCounters(); + printf("Press Ctrl-C when done.\n"); + message_loop.Run(); +} diff --git a/media/cast/test/utility/udp_proxy.cc b/media/cast/test/utility/udp_proxy.cc index 4714b7ed675d8..e71678c482ed8 100644 --- a/media/cast/test/utility/udp_proxy.cc +++ b/media/cast/test/utility/udp_proxy.cc @@ -68,6 +68,7 @@ class Buffer : public PacketPipe { private: void Schedule() { + last_schedule_ = clock_->NowTicks(); double megabits = buffer_.front()->size() * 8 / 1000000.0; double seconds = megabits / max_megabits_per_second_; int64 microseconds = static_cast(seconds * 1E6); @@ -78,17 +79,28 @@ class Buffer : public PacketPipe { } void ProcessBuffer() { - CHECK(!buffer_.empty()); - scoped_ptr packet(buffer_.front().release()); - buffer_size_ -= packet->size(); - buffer_.pop_front(); - pipe_->Send(packet.Pass()); + int64 bytes_to_send = static_cast( + (clock_->NowTicks() - last_schedule_).InSecondsF() * + max_megabits_per_second_ * 1E6 / 8); + if (bytes_to_send < static_cast(buffer_.front()->size())) { + bytes_to_send = buffer_.front()->size(); + } + while (!buffer_.empty() && + static_cast(buffer_.front()->size()) <= bytes_to_send) { + CHECK(!buffer_.empty()); + scoped_ptr packet(buffer_.front().release()); + bytes_to_send -= packet->size(); + buffer_size_ -= packet->size(); + buffer_.pop_front(); + pipe_->Send(packet.Pass()); + } if (!buffer_.empty()) { Schedule(); } } std::deque > buffer_; + base::TimeTicks last_schedule_; size_t buffer_size_; size_t max_buffer_size_; double max_megabits_per_second_; // megabits per second @@ -188,7 +200,11 @@ class RandomSortedDelay : public PacketPipe { virtual void Send(scoped_ptr packet) OVERRIDE { buffer_.push_back(linked_ptr(packet.release())); if (buffer_.size() == 1) { - Schedule(); + next_send_ = std::max( + clock_->NowTicks() + + base::TimeDelta::FromSecondsD(base::RandDouble() * random_delay_), + next_send_); + ProcessBuffer(); } } virtual void InitOnIOThread( @@ -212,38 +228,34 @@ class RandomSortedDelay : public PacketPipe { } void CauseExtraDelay() { - block_until_ = clock_->NowTicks() + + next_send_ = std::max( + clock_->NowTicks() + base::TimeDelta::FromMicroseconds( - static_cast(extra_delay_ * 1E6)); + static_cast(extra_delay_ * 1E6)), + next_send_); // An extra delay just happened, wait up to seconds_between_extra_delay_*2 // before scheduling another one to make the average equal to // seconds_between_extra_delay_. ScheduleExtraDelay(2.0); } - void Schedule() { - double seconds = base::RandDouble() * random_delay_; - base::TimeDelta block_time = block_until_ - base::TimeTicks::Now(); - base::TimeDelta delay_time = - base::TimeDelta::FromMicroseconds( - static_cast(seconds * 1E6)); - if (block_time > delay_time) { - block_time = delay_time; - } + void ProcessBuffer() { + base::TimeTicks now = clock_->NowTicks(); + while (!buffer_.empty() && next_send_ <= now) { + scoped_ptr packet(buffer_.front().release()); + pipe_->Send(packet.Pass()); + buffer_.pop_front(); - task_runner_->PostDelayedTask(FROM_HERE, - base::Bind(&RandomSortedDelay::ProcessBuffer, - weak_factory_.GetWeakPtr()), - delay_time); - } + next_send_ += base::TimeDelta::FromSecondsD( + base::RandDouble() * random_delay_); + } - void ProcessBuffer() { - CHECK(!buffer_.empty()); - scoped_ptr packet(buffer_.front().release()); - pipe_->Send(packet.Pass()); - buffer_.pop_front(); if (!buffer_.empty()) { - Schedule(); + task_runner_->PostDelayedTask( + FROM_HERE, + base::Bind(&RandomSortedDelay::ProcessBuffer, + weak_factory_.GetWeakPtr()), + next_send_ - now); } } @@ -253,6 +265,7 @@ class RandomSortedDelay : public PacketPipe { double extra_delay_; double seconds_between_extra_delay_; base::WeakPtrFactory weak_factory_; + base::TimeTicks next_send_; }; scoped_ptr NewRandomSortedDelay( @@ -536,6 +549,17 @@ void BuildPipe(scoped_ptr* pipe, PacketPipe* next) { } } // namespace +scoped_ptr GoodNetwork() { + // This represents the buffer on the sender. + scoped_ptr pipe; + BuildPipe(&pipe, new Buffer(2 << 20, 50)); + BuildPipe(&pipe, new ConstantDelay(1E-3)); + BuildPipe(&pipe, new RandomSortedDelay(1E-3, 2E-3, 3)); + // This represents the buffer on the receiving device. + BuildPipe(&pipe, new Buffer(2 << 20, 50)); + return pipe.Pass(); +} + scoped_ptr WifiNetwork() { // This represents the buffer on the sender. scoped_ptr pipe; diff --git a/media/cast/test/utility/udp_proxy.h b/media/cast/test/utility/udp_proxy.h index ea50a2c86a090..6c72fb6576668 100644 --- a/media/cast/test/utility/udp_proxy.h +++ b/media/cast/test/utility/udp_proxy.h @@ -155,6 +155,10 @@ scoped_ptr NewRandomSortedDelay(double random_delay, scoped_ptr NewNetworkGlitchPipe(double average_work_time, double average_outage_time); +// This method builds a stack of PacketPipes to emulate a reasonably +// good network. ~50mbit, ~3ms latency, no packet loss unless saturated. +scoped_ptr GoodNetwork(); + // This method builds a stack of PacketPipes to emulate a reasonably // good wifi network. ~20mbit, 1% packet loss, ~3ms latency. scoped_ptr WifiNetwork(); diff --git a/media/cdm/aes_decryptor.cc b/media/cdm/aes_decryptor.cc index 3530c3e9de01d..daafb1e88d191 100644 --- a/media/cdm/aes_decryptor.cc +++ b/media/cdm/aes_decryptor.cc @@ -53,6 +53,10 @@ class AesDecryptor::SessionIdDecryptionKeyMap { return key_list_.begin()->second; } + bool Contains(const std::string& web_session_id) { + return Find(web_session_id) != key_list_.end(); + } + private: // Searches the list for an element with |web_session_id|. KeyList::iterator Find(const std::string& web_session_id); @@ -315,6 +319,23 @@ void AesDecryptor::UpdateSession(const std::string& web_session_id, promise->resolve(); } +void AesDecryptor::GetUsableKeyIds(const std::string& web_session_id, + scoped_ptr promise) { + // Since |web_session_id| is not provided by the user, this should never + // happen. + DCHECK(valid_sessions_.find(web_session_id) != valid_sessions_.end()); + + KeyIdsVector keyids; + base::AutoLock auto_lock(key_map_lock_); + for (KeyIdToSessionKeysMap::iterator it = key_map_.begin(); + it != key_map_.end(); + ++it) { + if (it->second->Contains(web_session_id)) + keyids.push_back(std::vector(it->first.begin(), it->first.end())); + } + promise->resolve(keyids); +} + void AesDecryptor::ReleaseSession(const std::string& web_session_id, scoped_ptr promise) { // Validate that this is a reference to an active session and then forget it. diff --git a/media/cdm/aes_decryptor.h b/media/cdm/aes_decryptor.h index 3a177701960b3..dc08bb32b01dc 100644 --- a/media/cdm/aes_decryptor.h +++ b/media/cdm/aes_decryptor.h @@ -67,6 +67,9 @@ class MEDIA_EXPORT AesDecryptor : public MediaKeys, public Decryptor { virtual void ResetDecoder(StreamType stream_type) OVERRIDE; virtual void DeinitializeDecoder(StreamType stream_type) OVERRIDE; + void GetUsableKeyIds(const std::string& web_session_id, + scoped_ptr promise); + private: // TODO(fgalligan): Remove this and change KeyMap to use crypto::SymmetricKey // as there are no decryptors that are performing an integrity check. diff --git a/media/cdm/aes_decryptor_unittest.cc b/media/cdm/aes_decryptor_unittest.cc index d2d7ee0c8e584..91cd6ac365d39 100644 --- a/media/cdm/aes_decryptor_unittest.cc +++ b/media/cdm/aes_decryptor_unittest.cc @@ -220,41 +220,66 @@ class AesDecryptorTest : public testing::Test { } protected: - void OnResolveWithSession(PromiseResult expected, + void OnResolveWithSession(PromiseResult expected_result, const std::string& web_session_id) { - EXPECT_EQ(expected, RESOLVED); + EXPECT_EQ(expected_result, RESOLVED) << "Unexpectedly resolved."; EXPECT_GT(web_session_id.length(), 0ul); web_session_id_ = web_session_id; } - void OnResolve(PromiseResult expected) { - EXPECT_EQ(expected, RESOLVED); + void OnResolve(PromiseResult expected_result) { + EXPECT_EQ(expected_result, RESOLVED) << "Unexpectedly resolved."; } - void OnReject(PromiseResult expected, + void OnResolveWithUsableKeyIds(PromiseResult expected_result, + uint32 expected_count, + const KeyIdsVector& useable_key_ids) { + EXPECT_EQ(expected_result, RESOLVED) << "Unexpectedly resolved."; + EXPECT_EQ(expected_count, useable_key_ids.size()); + useable_key_ids_ = useable_key_ids; + } + + void OnReject(PromiseResult expected_result, MediaKeys::Exception exception_code, uint32 system_code, const std::string& error_message) { - EXPECT_EQ(expected, REJECTED); + EXPECT_EQ(expected_result, REJECTED) << "Unexpectedly rejected."; } - scoped_ptr CreatePromise(PromiseResult expected) { - scoped_ptr promise(new SimpleCdmPromise( - base::Bind( - &AesDecryptorTest::OnResolve, base::Unretained(this), expected), - base::Bind( - &AesDecryptorTest::OnReject, base::Unretained(this), expected))); + scoped_ptr CreatePromise(PromiseResult expected_result) { + scoped_ptr promise( + new SimpleCdmPromise(base::Bind(&AesDecryptorTest::OnResolve, + base::Unretained(this), + expected_result), + base::Bind(&AesDecryptorTest::OnReject, + base::Unretained(this), + expected_result))); return promise.Pass(); } scoped_ptr CreateSessionPromise( - PromiseResult expected) { + PromiseResult expected_result) { scoped_ptr promise(new NewSessionCdmPromise( base::Bind(&AesDecryptorTest::OnResolveWithSession, base::Unretained(this), - expected), - base::Bind( - &AesDecryptorTest::OnReject, base::Unretained(this), expected))); + expected_result), + base::Bind(&AesDecryptorTest::OnReject, + base::Unretained(this), + expected_result))); + return promise.Pass(); + } + + scoped_ptr CreateUsableKeyIdsPromise( + PromiseResult expected_result, + uint32 expected_count) { + scoped_ptr promise(new KeyIdsPromise( + base::Bind(&AesDecryptorTest::OnResolveWithUsableKeyIds, + base::Unretained(this), + expected_result, + expected_count), + base::Bind(&AesDecryptorTest::OnReject, + base::Unretained(this), + expected_result))); return promise.Pass(); } @@ -292,6 +317,23 @@ class AesDecryptorTest : public testing::Test { CreatePromise(result)); } + void GetUsableKeyIdsAndExpect(const std::string& session_id, + PromiseResult expected_result, + uint32 expected_count) { + decryptor_.GetUsableKeyIds( + session_id, CreateUsableKeyIdsPromise(expected_result, expected_count)); + } + + bool UsableKeyIdsContains(std::vector expected) { + for (KeyIdsVector::iterator it = useable_key_ids_.begin(); + it != useable_key_ids_.end(); + ++it) { + if (*it == expected) + return true; + } + return false; + } + MOCK_METHOD2(BufferDecrypted, void(Decryptor::Status, const scoped_refptr&)); @@ -361,6 +403,10 @@ class AesDecryptorTest : public testing::Test { AesDecryptor::DecryptCB decrypt_cb_; std::string web_session_id_; + // Copy of the vector from the last successful call to + // OnResolveWithUsableKeyIds(). + KeyIdsVector useable_key_ids_; + // Constants for testing. const std::vector original_data_; const std::vector encrypted_data_; @@ -789,4 +835,26 @@ TEST_F(AesDecryptorTest, JWKKey) { ReleaseSession(session_id); } +TEST_F(AesDecryptorTest, GetKeyIds) { + std::vector key_id1(kKeyId, kKeyId + arraysize(kKeyId)); + std::vector key_id2(kKeyId2, kKeyId2 + arraysize(kKeyId2)); + + std::string session_id = CreateSession(key_id_); + GetUsableKeyIdsAndExpect(session_id, RESOLVED, 0); + EXPECT_FALSE(UsableKeyIdsContains(key_id1)); + EXPECT_FALSE(UsableKeyIdsContains(key_id2)); + + // Add 1 key, verify ID is returned. + UpdateSessionAndExpect(session_id, kKeyAsJWK, RESOLVED); + GetUsableKeyIdsAndExpect(session_id, RESOLVED, 1); + EXPECT_TRUE(UsableKeyIdsContains(key_id1)); + EXPECT_FALSE(UsableKeyIdsContains(key_id2)); + + // Add second key, verify both IDs returned. + UpdateSessionAndExpect(session_id, kKey2AsJWK, RESOLVED); + GetUsableKeyIdsAndExpect(session_id, RESOLVED, 2); + EXPECT_TRUE(UsableKeyIdsContains(key_id1)); + EXPECT_TRUE(UsableKeyIdsContains(key_id2)); +} + } // namespace media diff --git a/media/cdm/ppapi/cdm_adapter.cc b/media/cdm/ppapi/cdm_adapter.cc index 14aa23120b27b..2452be87b3de7 100644 --- a/media/cdm/ppapi/cdm_adapter.cc +++ b/media/cdm/ppapi/cdm_adapter.cc @@ -383,12 +383,30 @@ void CdmAdapter::UpdateSession(uint32_t promise_id, response_size); } +void CdmAdapter::CloseSession(uint32_t promise_id, + const std::string& web_session_id) { + if (!cdm_->CloseSession( + promise_id, web_session_id.data(), web_session_id.length())) { + // CDM_4 and CDM_5 don't support this method, so reject the promise. + RejectPromise(promise_id, cdm::kNotSupportedError, 0, "Not implemented."); + } +} + void CdmAdapter::ReleaseSession(uint32_t promise_id, const std::string& web_session_id) { - cdm_->ReleaseSession( + cdm_->RemoveSession( promise_id, web_session_id.data(), web_session_id.length()); } +void CdmAdapter::GetUsableKeyIds(uint32_t promise_id, + const std::string& web_session_id) { + if (!cdm_->GetUsableKeyIds( + promise_id, web_session_id.data(), web_session_id.length())) { + // CDM_4 and CDM_5 don't support this method, so reject the promise. + RejectPromise(promise_id, cdm::kNotSupportedError, 0, "Not implemented."); + } +} + // Note: In the following decryption/decoding related functions, errors are NOT // reported via KeyError, but are reported via corresponding PPB calls. @@ -667,15 +685,25 @@ void CdmAdapter::OnSessionError(uint32_t session_id, } } -// cdm::Host_5 methods +// cdm::Host_5 and cdm::Host_6 methods cdm::Time CdmAdapter::GetCurrentTime() { + return GetCurrentWallTime(); +} + +cdm::Time CdmAdapter::GetCurrentWallTime() { return pp::Module::Get()->core()->GetTime(); } void CdmAdapter::OnResolvePromise(uint32_t promise_id) { PostOnMain(callback_factory_.NewCallback( &CdmAdapter::SendPromiseResolvedInternal, promise_id)); + + // CDM_5 doesn't support OnSessionKeysChange(), so simulate one if requested. + // Passing "true" which may result in false positives for retrying. + std::string session_id; + if (cdm_->SessionUsableKeysEventNeeded(promise_id, &session_id)) + OnSessionKeysChange(session_id.data(), session_id.length(), true); } void CdmAdapter::OnResolveNewSessionPromise(uint32_t promise_id, @@ -685,6 +713,27 @@ void CdmAdapter::OnResolveNewSessionPromise(uint32_t promise_id, &CdmAdapter::SendPromiseResolvedWithSessionInternal, promise_id, std::string(web_session_id, web_session_id_length))); + + // CDM_5 doesn't support OnSessionKeysChange(), so simulate one if requested. + // Passing "true" which may result in false positives for retrying. + std::string session_id; + if (cdm_->SessionUsableKeysEventNeeded(promise_id, &session_id)) + OnSessionKeysChange(web_session_id, web_session_id_length, true); +} + +void CdmAdapter::OnResolveKeyIdsPromise(uint32_t promise_id, + const cdm::BinaryData* usable_key_ids, + uint32_t usable_key_ids_length) { + std::vector > key_ids; + for (uint32_t i = 0; i < usable_key_ids_length; ++i) { + key_ids.push_back( + std::vector(usable_key_ids[i].data, + usable_key_ids[i].data + usable_key_ids[i].length)); + } + PostOnMain(callback_factory_.NewCallback( + &CdmAdapter::SendPromiseResolvedWithUsableKeyIdsInternal, + promise_id, + key_ids)); } void CdmAdapter::OnRejectPromise(uint32_t promise_id, @@ -724,17 +773,26 @@ void CdmAdapter::OnSessionMessage(const char* web_session_id, void CdmAdapter::OnSessionKeysChange(const char* web_session_id, uint32_t web_session_id_length, bool has_additional_usable_key) { - // TODO(jrummell): Implement this event in subsequent CL - // (http://crbug.com/370251). - PP_NOTREACHED(); + OnSessionUsableKeysChange( + web_session_id, web_session_id_length, has_additional_usable_key); +} + +void CdmAdapter::OnSessionUsableKeysChange(const char* web_session_id, + uint32_t web_session_id_length, + bool has_additional_usable_key) { + PostOnMain(callback_factory_.NewCallback( + &CdmAdapter::SendSessionUsableKeysChangeInternal, + std::string(web_session_id, web_session_id_length), + has_additional_usable_key)); } void CdmAdapter::OnExpirationChange(const char* web_session_id, uint32_t web_session_id_length, cdm::Time new_expiry_time) { - // TODO(jrummell): Implement this event in subsequent CL - // (http://crbug.com/370251). - PP_NOTREACHED(); + PostOnMain(callback_factory_.NewCallback( + &CdmAdapter::SendExpirationChangeInternal, + std::string(web_session_id, web_session_id_length), + new_expiry_time)); } void CdmAdapter::OnSessionReady(const char* web_session_id, @@ -782,6 +840,15 @@ void CdmAdapter::SendPromiseResolvedWithSessionInternal( web_session_id); } +void CdmAdapter::SendPromiseResolvedWithUsableKeyIdsInternal( + int32_t result, + uint32_t promise_id, + std::vector > key_ids) { + PP_DCHECK(result == PP_OK); + // TODO(jrummell): Implement this event in subsequent CL. + // (http://crbug.com/358271). +} + void CdmAdapter::SendPromiseRejectedInternal(int32_t result, uint32_t promise_id, const SessionError& error) { @@ -832,6 +899,23 @@ void CdmAdapter::SendSessionErrorInternal(int32_t result, error.error_description); } +void CdmAdapter::SendSessionUsableKeysChangeInternal( + int32_t result, + const std::string& web_session_id, + bool has_additional_usable_key) { + PP_DCHECK(result == PP_OK); + // TODO(jrummell): Implement this event in subsequent CL. + // (http://crbug.com/358271). +} + +void CdmAdapter::SendExpirationChangeInternal(int32_t result, + const std::string& web_session_id, + cdm::Time new_expiry_time) { + PP_DCHECK(result == PP_OK); + // TODO(jrummell): Implement this event in subsequent CL. + // (http://crbug.com/358271). +} + void CdmAdapter::DeliverBlock(int32_t result, const cdm::Status& status, const LinkedDecryptedBlock& decrypted_block, @@ -1214,7 +1298,7 @@ void* GetCdmHost(int host_interface_version, void* user_data) { return NULL; COMPILE_ASSERT( - cdm::ContentDecryptionModule::Host::kVersion == cdm::Host_5::kVersion, + cdm::ContentDecryptionModule::Host::kVersion == cdm::Host_6::kVersion, update_code_below); // Ensure IsSupportedCdmHostVersion matches implementation of this function. @@ -1224,10 +1308,11 @@ void* GetCdmHost(int host_interface_version, void* user_data) { PP_DCHECK( // Future version is not supported. - !IsSupportedCdmHostVersion(cdm::Host_5::kVersion + 1) && + !IsSupportedCdmHostVersion(cdm::Host_6::kVersion + 1) && // Current version is supported. - IsSupportedCdmHostVersion(cdm::Host_5::kVersion) && + IsSupportedCdmHostVersion(cdm::Host_6::kVersion) && // Include all previous supported versions (if any) here. + IsSupportedCdmHostVersion(cdm::Host_5::kVersion) && IsSupportedCdmHostVersion(cdm::Host_4::kVersion) && // One older than the oldest supported version is not supported. !IsSupportedCdmHostVersion(cdm::Host_4::kVersion - 1)); @@ -1240,6 +1325,8 @@ void* GetCdmHost(int host_interface_version, void* user_data) { return static_cast(cdm_adapter); case cdm::Host_5::kVersion: return static_cast(cdm_adapter); + case cdm::Host_6::kVersion: + return static_cast(cdm_adapter); default: PP_NOTREACHED(); return NULL; diff --git a/media/cdm/ppapi/cdm_adapter.h b/media/cdm/ppapi/cdm_adapter.h index 0189c1fc353cd..cd4738b856bdb 100644 --- a/media/cdm/ppapi/cdm_adapter.h +++ b/media/cdm/ppapi/cdm_adapter.h @@ -43,7 +43,8 @@ void* GetCdmHost(int host_interface_version, void* user_data); class CdmAdapter : public pp::Instance, public pp::ContentDecryptor_Private, public cdm::Host_4, - public cdm::Host_5 { + public cdm::Host_5, + public cdm::Host_6 { public: CdmAdapter(PP_Instance instance, pp::Module* module); virtual ~CdmAdapter(); @@ -66,8 +67,15 @@ class CdmAdapter : public pp::Instance, virtual void UpdateSession(uint32_t promise_id, const std::string& web_session_id, pp::VarArrayBuffer response) OVERRIDE; + // TODO(jrummell): Pass this function through Pepper and add OVERRIDE. + virtual void CloseSession(uint32_t promise_id, + const std::string& web_session_id); + // TODO(jrummell): Rename to RemoveSession(). virtual void ReleaseSession(uint32_t promise_id, const std::string& web_session_id) OVERRIDE; + // TODO(jrummell): Pass this function through Pepper and add OVERRIDE. + virtual void GetUsableKeyIds(uint32_t promise_id, + const std::string& web_session_id); virtual void Decrypt( pp::Buffer_Dev encrypted_buffer, const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE; @@ -86,7 +94,7 @@ class CdmAdapter : public pp::Instance, pp::Buffer_Dev encrypted_buffer, const PP_EncryptedBlockInfo& encrypted_block_info) OVERRIDE; - // cdm::Host_4 and cdm::Host_5 implementation. + // cdm::Host_4, cdm::Host_5 and cdm::Host_6 implementation. virtual cdm::Buffer* Allocate(uint32_t capacity) OVERRIDE; virtual void SetTimer(int64_t delay_ms, void* context) OVERRIDE; @@ -126,10 +134,10 @@ class CdmAdapter : public pp::Instance, uint32_t destination_url_length) OVERRIDE; virtual void OnSessionKeysChange(const char* web_session_id, uint32_t web_session_id_length, - bool has_additional_usable_key); + bool has_additional_usable_key) OVERRIDE; virtual void OnExpirationChange(const char* web_session_id, uint32_t web_session_id_length, - cdm::Time new_expiry_time); + cdm::Time new_expiry_time) OVERRIDE; virtual void OnSessionReady(const char* web_session_id, uint32_t web_session_id_length) OVERRIDE; virtual void OnSessionClosed(const char* web_session_id, @@ -141,7 +149,17 @@ class CdmAdapter : public pp::Instance, const char* error_message, uint32_t error_message_length) OVERRIDE; - // cdm::Host_4 and cdm::Host_5 implementation. + // cdm::Host_6 implementation. + virtual cdm::Time GetCurrentWallTime() OVERRIDE; + virtual void OnResolveKeyIdsPromise(uint32_t promise_id, + const cdm::BinaryData* usable_key_ids, + uint32_t usable_key_ids_length) OVERRIDE; + virtual void OnSessionUsableKeysChange( + const char* web_session_id, + uint32_t web_session_id_length, + bool has_additional_usable_key) OVERRIDE; + + // cdm::Host_4, cdm::Host_5 and cdm::Host_6 implementation. virtual void SendPlatformChallenge(const char* service_id, uint32_t service_id_length, const char* challenge, @@ -186,6 +204,10 @@ class CdmAdapter : public pp::Instance, int32_t result, uint32_t promise_id, const std::string& web_session_id); + void SendPromiseResolvedWithUsableKeyIdsInternal( + int32_t result, + uint32_t promise_id, + std::vector > key_ids); void SendPromiseRejectedInternal(int32_t result, uint32_t promise_id, const SessionError& error); @@ -200,6 +222,12 @@ class CdmAdapter : public pp::Instance, void SendSessionErrorInternal(int32_t result, const std::string& web_session_id, const SessionError& error); + void SendSessionUsableKeysChangeInternal(int32_t result, + const std::string& web_session_id, + bool has_additional_usable_key); + void SendExpirationChangeInternal(int32_t result, + const std::string& web_session_id, + cdm::Time new_expiry_time); void RejectPromise(uint32_t promise_id, cdm::Error error, uint32_t system_code, diff --git a/media/cdm/ppapi/cdm_wrapper.h b/media/cdm/ppapi/cdm_wrapper.h index 665b6b68dadf7..f11672cadc33b 100644 --- a/media/cdm/ppapi/cdm_wrapper.h +++ b/media/cdm/ppapi/cdm_wrapper.h @@ -56,9 +56,17 @@ class CdmWrapper { uint32_t web_session_id_size, const uint8_t* response, uint32_t response_size) = 0; - virtual void ReleaseSession(uint32_t promise_id, - const char* web_session_id, - uint32_t web_session_id_size) = 0; + // TODO(jrummell): Remove return value when CDM4/5 are removed. + virtual bool CloseSession(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) = 0; + virtual void RemoveSession(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) = 0; + // TODO(jrummell): Remove return value when CDM4/5 are removed. + virtual bool GetUsableKeyIds(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) = 0; virtual void TimerExpired(void* context) = 0; virtual cdm::Status Decrypt(const cdm::InputBuffer& encrypted_buffer, cdm::DecryptedBlock* decrypted_buffer) = 0; @@ -95,6 +103,38 @@ class CdmWrapper { virtual std::string LookupWebSessionId(uint32_t session_id) = 0; virtual void DropWebSessionId(std::string web_session_id) = 0; + // Helper functions for the cdm::Host_4 and cdm::Host_5 methods. + // CDMs using cdm::Host_6 will call OnSessionUsableKeys() as necessary when + // resolving LoadSession() and UpdateSession(). This needs to be simulated + // for the older CDMs. These must not be called for cdm::Host_6 and later. + // TODO(jrummell): Remove these once Host_4 and Host_5 interfaces are removed. + + // Query whether a SessionUsableKeys event is necessary for the specified + // |promise_id|. Returns true if needed and |web_session_id| is updated, + // otherwise returns false. + virtual bool SessionUsableKeysEventNeeded(uint32_t promise_id, + std::string* web_session_id) = 0; + + // Used to indicate that a SessionUsableKeys event is required for the + // specified |promise_id| and associated |web_session_id|. + virtual void SetSessionUsableKeysEventNeeded( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) = 0; + + // cdm::Host_6 introduces InputBuffer_2 (aka InputBuffer). cdm::Host_4 and + // cdm::Host_5 methods still use InputBuffer_1, so this helper function + // converts InputBuffer_2 to InputBuffer_1. + // TODO(jrummell): Remove these once Host_4 and Host_5 interfaces are removed. + virtual void ConvertInputBuffer(const cdm::InputBuffer& v2, + cdm::InputBuffer_1* v1) = 0; + + // Prior to CDM_6, |init_data_type| was a content type. This helper convererts + // an |init_data_type| to a content type. + // TODO(sandersd): Remove once Host_4 and Host_5 interfaces are removed. + virtual std::string ConvertInitDataTypeToContentType( + const std::string& init_data_type) const = 0; + protected: CdmWrapper() {} @@ -126,12 +166,35 @@ class CdmWrapperImpl : public CdmWrapper { cdm_->Destroy(); } + // Returns true if |data| is prefixed with |header| and has data after the + // |header|. + bool HasHeader(const uint8* data, + int data_length, + const std::string& header) { + return static_cast(data_length) > header.length() && + std::equal(data, data + header.length(), header.begin()); + } + virtual void CreateSession(uint32_t promise_id, const char* init_data_type, uint32_t init_data_type_size, const uint8_t* init_data, uint32_t init_data_size, cdm::SessionType session_type) OVERRIDE { + // TODO(jrummell): Remove this code once |session_type| is passed through + // Pepper. When removing, add the header back in for CDM4. + PP_DCHECK(session_type == cdm::kTemporary); + const char kPersistentSessionHeader[] = "PERSISTENT|"; + if (HasHeader(init_data, init_data_size, kPersistentSessionHeader)) { + cdm_->CreateSession(promise_id, + init_data_type, + init_data_type_size, + init_data + strlen(kPersistentSessionHeader), + init_data_size - strlen(kPersistentSessionHeader), + cdm::kPersistent); + return; + } + cdm_->CreateSession(promise_id, init_data_type, init_data_type_size, @@ -158,10 +221,24 @@ class CdmWrapperImpl : public CdmWrapper { response_size); } - virtual void ReleaseSession(uint32_t promise_id, - const char* web_session_id, - uint32_t web_session_id_size) OVERRIDE { - cdm_->ReleaseSession(promise_id, web_session_id, web_session_id_size); + virtual bool GetUsableKeyIds(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) OVERRIDE { + cdm_->GetUsableKeyIds(promise_id, web_session_id, web_session_id_size); + return true; + } + + virtual bool CloseSession(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) OVERRIDE { + cdm_->CloseSession(promise_id, web_session_id, web_session_id_size); + return true; + } + + virtual void RemoveSession(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) OVERRIDE { + cdm_->RemoveSession(promise_id, web_session_id, web_session_id_size); } virtual void TimerExpired(void* context) OVERRIDE { @@ -261,6 +338,47 @@ class CdmWrapperImpl : public CdmWrapper { web_session_to_session_id_map_.erase(web_session_id); } + virtual bool SessionUsableKeysEventNeeded(uint32_t promise_id, + std::string* web_session_id) { + std::map::iterator it = + promises_needing_usable_keys_event_.find(promise_id); + if (it == promises_needing_usable_keys_event_.end()) + return false; + web_session_id->swap(it->second); + promises_needing_usable_keys_event_.erase(it); + return true; + } + + virtual void SetSessionUsableKeysEventNeeded(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + promises_needing_usable_keys_event_.insert(std::make_pair( + promise_id, std::string(web_session_id, web_session_id_size))); + } + + virtual void ConvertInputBuffer(const cdm::InputBuffer& v2, + cdm::InputBuffer_1* v1) { + v1->data = v2.data; + v1->data_size = v2.data_size; + v1->data_offset = 0; + v1->key_id = v2.key_id; + v1->key_id_size = v2.key_id_size; + v1->iv = v2.iv; + v1->iv_size = v2.iv_size; + v1->subsamples = v2.subsamples; + v1->num_subsamples = v2.num_subsamples; + v1->timestamp = v2.timestamp; + } + + virtual std::string ConvertInitDataTypeToContentType( + const std::string& init_data_type) const { + if (init_data_type == "cenc") + return "video/mp4"; + if (init_data_type == "webm") + return "video/webm"; + return init_data_type; + } + private: CdmWrapperImpl(CdmInterface* cdm) : cdm_(cdm), next_session_id_(100) { PP_DCHECK(cdm_); @@ -272,6 +390,8 @@ class CdmWrapperImpl : public CdmWrapper { uint32_t next_session_id_; std::map web_session_to_session_id_map_; + std::map promises_needing_usable_keys_event_; + DISALLOW_COPY_AND_ASSIGN(CdmWrapperImpl); }; @@ -281,7 +401,8 @@ class CdmWrapperImpl : public CdmWrapper { // create a new session_id to pass to the CDM. For update and release, we need // to look up |web_session_id| and convert it into the existing |session_id|. // Since the callbacks don't come through this interface, cdm_adapter needs to -// create the mapping (and delete it on release). +// create the mapping (and delete it on release). Finally, for create, we need +// to translate |init_data_type| to a MIME type. // TODO(jrummell): Remove these once Host_4 interface is removed. template <> @@ -294,9 +415,11 @@ void CdmWrapperImpl::CreateSession( cdm::SessionType session_type) { uint32_t session_id = CreateSessionId(); RegisterPromise(session_id, promise_id); + std::string converted_init_data_type = ConvertInitDataTypeToContentType( + std::string(init_data_type, init_data_type_size)); cdm_->CreateSession(session_id, - init_data_type, - init_data_type_size, + converted_init_data_type.data(), + converted_init_data_type.length(), init_data, init_data_size); } @@ -308,6 +431,10 @@ void CdmWrapperImpl::LoadSession( uint32_t web_session_id_size) { uint32_t session_id = CreateSessionId(); RegisterPromise(session_id, promise_id); + // As CDM_4 doesn't support OnSessionUsableKeysChange(), make sure to generate + // one when the promise is resolved. This may be overly aggressive. + SetSessionUsableKeysEventNeeded( + promise_id, web_session_id, web_session_id_size); cdm_->LoadSession(session_id, web_session_id, web_session_id_size); } @@ -321,11 +448,23 @@ void CdmWrapperImpl::UpdateSession( std::string web_session_str(web_session_id, web_session_id_size); uint32_t session_id = LookupSessionId(web_session_str); RegisterPromise(session_id, promise_id); + // As CDM_4 doesn't support OnSessionUsableKeysChange(), make sure to generate + // one when the promise is resolved. This may be overly aggressive. + SetSessionUsableKeysEventNeeded( + promise_id, web_session_id, web_session_id_size); cdm_->UpdateSession(session_id, response, response_size); } template <> -void CdmWrapperImpl::ReleaseSession( +bool CdmWrapperImpl::CloseSession( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + return false; +} + +template <> +void CdmWrapperImpl::RemoveSession( uint32_t promise_id, const char* web_session_id, uint32_t web_session_id_size) { @@ -335,12 +474,164 @@ void CdmWrapperImpl::ReleaseSession( cdm_->ReleaseSession(session_id); } +template <> +bool CdmWrapperImpl::GetUsableKeyIds( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + return false; +} + +template <> +cdm::Status CdmWrapperImpl::Decrypt( + const cdm::InputBuffer& encrypted_buffer, + cdm::DecryptedBlock* decrypted_buffer) { + cdm::InputBuffer_1 buffer; + ConvertInputBuffer(encrypted_buffer, &buffer); + return cdm_->Decrypt(buffer, decrypted_buffer); +} + +template <> +cdm::Status +CdmWrapperImpl::DecryptAndDecodeFrame( + const cdm::InputBuffer& encrypted_buffer, + cdm::VideoFrame* video_frame) { + cdm::InputBuffer_1 buffer; + ConvertInputBuffer(encrypted_buffer, &buffer); + return cdm_->DecryptAndDecodeFrame(buffer, video_frame); +} + +template <> +cdm::Status +CdmWrapperImpl::DecryptAndDecodeSamples( + const cdm::InputBuffer& encrypted_buffer, + cdm::AudioFrames* audio_frames) { + cdm::InputBuffer_1 buffer; + ConvertInputBuffer(encrypted_buffer, &buffer); + return cdm_->DecryptAndDecodeSamples(buffer, audio_frames); +} + +// Overrides for the cdm::Host_5 methods. +// TODO(jrummell): Remove these once Host_5 interface is removed. + +template <> +void CdmWrapperImpl::CreateSession( + uint32_t promise_id, + const char* init_data_type, + uint32_t init_data_type_size, + const uint8_t* init_data, + uint32_t init_data_size, + cdm::SessionType session_type) { + std::string converted_init_data_type = ConvertInitDataTypeToContentType( + std::string(init_data_type, init_data_type_size)); + // TODO(jrummell): Remove this code once |session_type| is passed through + // Pepper. When removing, add the header back in for CDM4. + PP_DCHECK(session_type == cdm::kTemporary); + const char kPersistentSessionHeader[] = "PERSISTENT|"; + if (HasHeader(init_data, init_data_size, kPersistentSessionHeader)) { + cdm_->CreateSession(promise_id, + converted_init_data_type.data(), + converted_init_data_type.length(), + init_data + strlen(kPersistentSessionHeader), + init_data_size - strlen(kPersistentSessionHeader), + cdm::kPersistent); + return; + } + + cdm_->CreateSession(promise_id, + converted_init_data_type.data(), + converted_init_data_type.length(), + init_data, + init_data_size, + session_type); +} + +template <> +void CdmWrapperImpl::LoadSession( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + // As CDM_5 doesn't support OnSessionUsableKeysChange(), make sure to generate + // one when the promise is resolved. This may be overly aggressive. + SetSessionUsableKeysEventNeeded( + promise_id, web_session_id, web_session_id_size); + cdm_->LoadSession(promise_id, web_session_id, web_session_id_size); +} + +template <> +void CdmWrapperImpl::UpdateSession( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size, + const uint8_t* response, + uint32_t response_size) { + // As CDM_5 doesn't support OnSessionUsableKeysChange(), make sure to generate + // one when the promise is resolved. This may be overly aggressive. + SetSessionUsableKeysEventNeeded( + promise_id, web_session_id, web_session_id_size); + cdm_->UpdateSession( + promise_id, web_session_id, web_session_id_size, response, response_size); +} + +template <> +bool CdmWrapperImpl::CloseSession( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + return false; +} + +template <> +void CdmWrapperImpl::RemoveSession( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + cdm_->ReleaseSession(promise_id, web_session_id, web_session_id_size); +} + +template <> +bool CdmWrapperImpl::GetUsableKeyIds( + uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_size) { + return false; +} + +template <> +cdm::Status CdmWrapperImpl::Decrypt( + const cdm::InputBuffer& encrypted_buffer, + cdm::DecryptedBlock* decrypted_buffer) { + cdm::InputBuffer_1 buffer; + ConvertInputBuffer(encrypted_buffer, &buffer); + return cdm_->Decrypt(buffer, decrypted_buffer); +} + +template <> +cdm::Status +CdmWrapperImpl::DecryptAndDecodeFrame( + const cdm::InputBuffer& encrypted_buffer, + cdm::VideoFrame* video_frame) { + cdm::InputBuffer_1 buffer; + ConvertInputBuffer(encrypted_buffer, &buffer); + return cdm_->DecryptAndDecodeFrame(buffer, video_frame); +} + +template <> +cdm::Status +CdmWrapperImpl::DecryptAndDecodeSamples( + const cdm::InputBuffer& encrypted_buffer, + cdm::AudioFrames* audio_frames) { + cdm::InputBuffer_1 buffer; + ConvertInputBuffer(encrypted_buffer, &buffer); + return cdm_->DecryptAndDecodeSamples(buffer, audio_frames); +} + CdmWrapper* CdmWrapper::Create(const char* key_system, uint32_t key_system_size, GetCdmHostFunc get_cdm_host_func, void* user_data) { COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == - cdm::ContentDecryptionModule_5::kVersion, + cdm::ContentDecryptionModule_6::kVersion, update_code_below); // Ensure IsSupportedCdmInterfaceVersion() matches this implementation. @@ -365,6 +656,11 @@ CdmWrapper* CdmWrapper::Create(const char* key_system, // If |cdm_wrapper| is NULL, try to create the CDM using older supported // versions of the CDM interface. + cdm_wrapper = CdmWrapperImpl::Create( + key_system, key_system_size, get_cdm_host_func, user_data); + if (cdm_wrapper) + return cdm_wrapper; + cdm_wrapper = CdmWrapperImpl::Create( key_system, key_system_size, get_cdm_host_func, user_data); return cdm_wrapper; @@ -375,7 +671,7 @@ CdmWrapper* CdmWrapper::Create(const char* key_system, // does not have. // Also update supported_cdm_versions.h. COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == - cdm::ContentDecryptionModule_5::kVersion, + cdm::ContentDecryptionModule_6::kVersion, ensure_cdm_wrapper_templates_have_old_version_support); } // namespace media diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc index 6f0809f89edb9..e06f41b55bc97 100644 --- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc +++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.cc @@ -113,7 +113,6 @@ static scoped_refptr CopyDecoderBufferFrom( subsamples.push_back(subsample); } - DCHECK_EQ(input_buffer.data_offset, 0u); scoped_ptr decrypt_config(new media::DecryptConfig( std::string(reinterpret_cast(input_buffer.key_id), input_buffer.key_id_size), @@ -273,6 +272,7 @@ void ClearKeyCdm::LoadSession(uint32 promise_id, if (std::string(kLoadableWebSessionId) != std::string(web_session_id, web_session_id_length)) { + // TODO(jrummell): This should be resolved with undefined, not rejected. std::string message("Incorrect session id specified for LoadSession()."); host_->OnRejectPromise(promise_id, cdm::kInvalidAccessError, @@ -298,11 +298,11 @@ void ClearKeyCdm::LoadSession(uint32 promise_id, void ClearKeyCdm::UpdateSession(uint32 promise_id, const char* web_session_id, - uint32_t web_session_id_size, + uint32_t web_session_id_length, const uint8* response, uint32 response_size) { DVLOG(1) << __FUNCTION__; - std::string web_session_str(web_session_id, web_session_id_size); + std::string web_session_str(web_session_id, web_session_id_length); scoped_ptr promise(new media::SimpleCdmPromise( base::Bind(&ClearKeyCdm::OnSessionUpdated, @@ -320,11 +320,11 @@ void ClearKeyCdm::UpdateSession(uint32 promise_id, } } -void ClearKeyCdm::ReleaseSession(uint32 promise_id, - const char* web_session_id, - uint32_t web_session_id_size) { +void ClearKeyCdm::CloseSession(uint32 promise_id, + const char* web_session_id, + uint32_t web_session_id_length) { DVLOG(1) << __FUNCTION__; - std::string web_session_str(web_session_id, web_session_id_size); + std::string web_session_str(web_session_id, web_session_id_length); scoped_ptr promise(new media::SimpleCdmPromise( base::Bind(&ClearKeyCdm::OnSessionReleased, @@ -336,6 +336,26 @@ void ClearKeyCdm::ReleaseSession(uint32 promise_id, decryptor_.ReleaseSession(web_session_str, promise.Pass()); } +void ClearKeyCdm::RemoveSession(uint32 promise_id, + const char* web_session_id, + uint32_t web_session_id_length) { + DVLOG(1) << __FUNCTION__; + // RemoveSession only allowed for persistent sessions. + bool is_persistent_session = + std::string(kLoadableWebSessionId) == + std::string(web_session_id, web_session_id_length); + if (is_persistent_session) { + host_->OnResolvePromise(promise_id); + } else { + std::string message("Not supported for non-persistent sessions."); + host_->OnRejectPromise(promise_id, + cdm::kInvalidAccessError, + 0, + message.data(), + message.length()); + } +} + void ClearKeyCdm::SetServerCertificate(uint32 promise_id, const uint8_t* server_certificate_data, uint32_t server_certificate_data_size) { @@ -343,6 +363,19 @@ void ClearKeyCdm::SetServerCertificate(uint32 promise_id, host_->OnResolvePromise(promise_id); } +void ClearKeyCdm::GetUsableKeyIds(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_length) { + std::string web_session_str(web_session_id, web_session_id_length); + scoped_ptr promise(new media::KeyIdsPromise( + base::Bind(&ClearKeyCdm::OnUsableKeyIdsObtained, + base::Unretained(this), + promise_id), + base::Bind( + &ClearKeyCdm::OnPromiseFailed, base::Unretained(this), promise_id))); + decryptor_.GetUsableKeyIds(web_session_str, promise.Pass()); +} + void ClearKeyCdm::TimerExpired(void* context) { if (context == &session_id_for_emulated_loadsession_) { LoadLoadableSession(); @@ -381,9 +414,8 @@ static void CopyDecryptResults( *buffer_copy = buffer; } -cdm::Status ClearKeyCdm::Decrypt( - const cdm::InputBuffer& encrypted_buffer, - cdm::DecryptedBlock* decrypted_block) { +cdm::Status ClearKeyCdm::Decrypt(const cdm::InputBuffer& encrypted_buffer, + cdm::DecryptedBlock* decrypted_block) { DVLOG(1) << "Decrypt()"; DCHECK(encrypted_buffer.data); @@ -558,7 +590,7 @@ void ClearKeyCdm::ScheduleNextHeartBeat() { // Prepare the next heartbeat message and set timer. std::ostringstream msg_stream; msg_stream << kHeartBeatHeader << " from ClearKey CDM set at time " - << host_->GetCurrentTime() << "."; + << host_->GetCurrentWallTime() << "."; next_heartbeat_message_ = msg_stream.str(); host_->SetTimer(timer_delay_ms_, &next_heartbeat_message_[0]); @@ -688,15 +720,13 @@ void ClearKeyCdm::OnSessionLoaded(uint32 promise_id, void ClearKeyCdm::OnSessionUpdated(uint32 promise_id, const std::string& web_session_id) { - // OnSessionReady() only called as success for UpdateSession(). However, - // UpdateSession() also called to finish loading sessions, so handle + // UpdateSession() may be called to finish loading sessions, so handle // appropriately. if (web_session_id == session_id_for_emulated_loadsession_) { session_id_for_emulated_loadsession_ = std::string(); // |promise_id| is the LoadSession() promise, so resolve appropriately. host_->OnResolveNewSessionPromise( promise_id, kLoadableWebSessionId, strlen(kLoadableWebSessionId)); - host_->OnSessionReady(kLoadableWebSessionId, strlen(kLoadableWebSessionId)); return; } @@ -708,6 +738,16 @@ void ClearKeyCdm::OnSessionReleased(uint32 promise_id, host_->OnResolvePromise(promise_id); } +void ClearKeyCdm::OnUsableKeyIdsObtained(uint32 promise_id, + const KeyIdsVector& key_ids) { + scoped_ptr result(new cdm::BinaryData[key_ids.size()]); + for (uint32 i = 0; i < key_ids.size(); ++i) { + result[i].data = key_ids[i].data(); + result[i].length = key_ids[i].size(); + } + host_->OnResolveKeyIdsPromise(promise_id, result.get(), key_ids.size()); +} + void ClearKeyCdm::OnPromiseFailed(uint32 promise_id, MediaKeys::Exception exception_code, uint32 system_code, diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm.h b/media/cdm/ppapi/external_clear_key/clear_key_cdm.h index 5903642a5827c..c491cd71009d5 100644 --- a/media/cdm/ppapi/external_clear_key/clear_key_cdm.h +++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm.h @@ -49,9 +49,15 @@ class ClearKeyCdm : public ClearKeyCdmInterface { uint32_t web_session_id_length, const uint8* response, uint32 response_size) OVERRIDE; - virtual void ReleaseSession(uint32 promise_id, - const char* web_session_id, - uint32_t web_session_id_length) OVERRIDE; + virtual void CloseSession(uint32 promise_id, + const char* web_session_id, + uint32_t web_session_id_length) OVERRIDE; + virtual void RemoveSession(uint32 promise_id, + const char* web_session_id, + uint32_t web_session_id_length) OVERRIDE; + virtual void GetUsableKeyIds(uint32_t promise_id, + const char* web_session_id, + uint32_t web_session_id_length) OVERRIDE; virtual void SetServerCertificate( uint32 promise_id, const uint8_t* server_certificate_data, @@ -94,6 +100,7 @@ class ClearKeyCdm : public ClearKeyCdmInterface { void OnSessionLoaded(uint32 promise_id, const std::string& web_session_id); void OnSessionUpdated(uint32 promise_id, const std::string& web_session_id); void OnSessionReleased(uint32 promise_id, const std::string& web_session_id); + void OnUsableKeyIdsObtained(uint32 promise_id, const KeyIdsVector& key_ids); void OnPromiseFailed(uint32 promise_id, MediaKeys::Exception exception_code, uint32 system_code, @@ -145,6 +152,16 @@ class ClearKeyCdm : public ClearKeyCdmInterface { std::string last_session_id_; std::string next_heartbeat_message_; + // In order to simulate LoadSession(), CreateSession() and then + // UpdateSession() will be called to create a session with known keys. + // |session_id_for_emulated_loadsession_| is used to keep track of the + // session_id allocated by aes_decryptor, as the session_id will be returned + // as |kLoadableWebSessionId|. Future requests for this simulated session + // need to use |session_id_for_emulated_loadsession_| for all calls + // to aes_decryptor. + // |promise_id_for_emulated_loadsession_| is used to keep track of the + // original LoadSession() promise, as it is not resolved until the + // UpdateSession() call succeeds. // TODO(xhwang): Extract testing code from main implementation. // See http://crbug.com/341751 std::string session_id_for_emulated_loadsession_; diff --git a/media/cdm/ppapi/external_clear_key/clear_key_cdm_common.h b/media/cdm/ppapi/external_clear_key/clear_key_cdm_common.h index 2bbc5b1c3241e..dfd1fe2a784c5 100644 --- a/media/cdm/ppapi/external_clear_key/clear_key_cdm_common.h +++ b/media/cdm/ppapi/external_clear_key/clear_key_cdm_common.h @@ -10,7 +10,7 @@ namespace media { // Aliases for the version of the interfaces that this CDM implements. -typedef cdm::ContentDecryptionModule_5 ClearKeyCdmInterface; +typedef cdm::ContentDecryptionModule_6 ClearKeyCdmInterface; typedef ClearKeyCdmInterface::Host ClearKeyCdmHost; } // namespace media diff --git a/media/cdm/ppapi/supported_cdm_versions.h b/media/cdm/ppapi/supported_cdm_versions.h index 8de7a8c2acb55..fc05345be40ea 100644 --- a/media/cdm/ppapi/supported_cdm_versions.h +++ b/media/cdm/ppapi/supported_cdm_versions.h @@ -21,10 +21,11 @@ bool IsSupportedCdmModuleVersion(int version) { bool IsSupportedCdmInterfaceVersion(int version) { COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == - cdm::ContentDecryptionModule_5::kVersion, + cdm::ContentDecryptionModule_6::kVersion, update_code_below); switch(version) { // Supported versions in decreasing order. + case cdm::ContentDecryptionModule_6::kVersion: case cdm::ContentDecryptionModule_5::kVersion: case cdm::ContentDecryptionModule_4::kVersion: return true; @@ -35,10 +36,11 @@ bool IsSupportedCdmInterfaceVersion(int version) { bool IsSupportedCdmHostVersion(int version) { COMPILE_ASSERT(cdm::ContentDecryptionModule::Host::kVersion == - cdm::ContentDecryptionModule_5::Host::kVersion, + cdm::ContentDecryptionModule_6::Host::kVersion, update_code_below); switch(version) { // Supported versions in decreasing order. + case cdm::Host_6::kVersion: case cdm::Host_5::kVersion: case cdm::Host_4::kVersion: return true; diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc index 5f52fe95bad8f..c468d9574c644 100644 --- a/media/ffmpeg/ffmpeg_common.cc +++ b/media/ffmpeg/ffmpeg_common.cc @@ -61,7 +61,7 @@ int64 ConvertToTimeBase(const AVRational& time_base, } // Converts an FFmpeg audio codec ID into its corresponding supported codec id. -static AudioCodec CodecIDToAudioCodec(AVCodecID codec_id) { +AudioCodec CodecIDToAudioCodec(AVCodecID codec_id) { switch (codec_id) { case AV_CODEC_ID_AAC: return kCodecAAC; @@ -146,7 +146,7 @@ static AVCodecID AudioCodecToCodecID(AudioCodec audio_codec, } // Converts an FFmpeg video codec ID into its corresponding supported codec id. -static VideoCodec CodecIDToVideoCodec(AVCodecID codec_id) { +VideoCodec CodecIDToVideoCodec(AVCodecID codec_id) { switch (codec_id) { case AV_CODEC_ID_H264: return kCodecH264; @@ -382,9 +382,9 @@ void AVStreamToVideoDecoderConfig( VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN; if (codec == kCodecVP8) - profile = VP8PROFILE_MAIN; + profile = VP8PROFILE_ANY; else if (codec == kCodecVP9) - profile = VP9PROFILE_MAIN; + profile = VP9PROFILE_ANY; else profile = ProfileIDToVideoCodecProfile(stream->codec->profile); diff --git a/media/ffmpeg/ffmpeg_common.h b/media/ffmpeg/ffmpeg_common.h index d4e85c80e6214..fba61498508e2 100644 --- a/media/ffmpeg/ffmpeg_common.h +++ b/media/ffmpeg/ffmpeg_common.h @@ -62,9 +62,7 @@ inline void ScopedPtrAVFreePacket::operator()(void* x) const { inline void ScopedPtrAVFreeContext::operator()(void* x) const { AVCodecContext* codec_context = static_cast(x); - av_free(codec_context->extradata); - avcodec_close(codec_context); - av_free(codec_context); + avcodec_free_context(&codec_context); } inline void ScopedPtrAVFreeFrame::operator()(void* x) const { @@ -130,6 +128,10 @@ PixelFormat VideoFormatToPixelFormat(VideoFrame::Format video_format); // date string. Otherwise returns fals and timeline_offset is unmodified. MEDIA_EXPORT bool FFmpegUTCDateToTime(const char* date_utc, base::Time* out); +MEDIA_EXPORT AudioCodec CodecIDToAudioCodec(AVCodecID codec_id); + +MEDIA_EXPORT VideoCodec CodecIDToVideoCodec(AVCodecID codec_id); + } // namespace media #endif // MEDIA_FFMPEG_FFMPEG_COMMON_H_ diff --git a/media/filters/audio_clock.cc b/media/filters/audio_clock.cc index e315fa31e2d70..eae807ece581b 100644 --- a/media/filters/audio_clock.cc +++ b/media/filters/audio_clock.cc @@ -4,144 +4,137 @@ #include "media/filters/audio_clock.h" +#include + #include "base/logging.h" #include "media/base/buffers.h" namespace media { -AudioClock::AudioClock(int sample_rate) - : sample_rate_(sample_rate), last_endpoint_timestamp_(kNoTimestamp()) { +AudioClock::AudioClock(base::TimeDelta start_timestamp, int sample_rate) + : start_timestamp_(start_timestamp), + sample_rate_(sample_rate), + microseconds_per_frame_( + static_cast(base::Time::kMicrosecondsPerSecond) / + sample_rate), + total_buffered_frames_(0), + current_media_timestamp_(start_timestamp), + audio_data_buffered_(0) { } AudioClock::~AudioClock() { } -void AudioClock::WroteAudio(int frames, +void AudioClock::WroteAudio(int frames_written, + int frames_requested, int delay_frames, - float playback_rate, - base::TimeDelta timestamp) { - CHECK_GT(playback_rate, 0); - CHECK(timestamp != kNoTimestamp()); - DCHECK_GE(frames, 0); + float playback_rate) { + DCHECK_GE(frames_written, 0); + DCHECK_LE(frames_written, frames_requested); DCHECK_GE(delay_frames, 0); + DCHECK_GE(playback_rate, 0); + + // First write: initialize buffer with silence. + if (start_timestamp_ == current_media_timestamp_ && buffered_.empty()) + PushBufferedAudioData(delay_frames, 0.0f); + + // Move frames from |buffered_| into the computed timestamp based on + // |delay_frames|. + // + // The ordering of compute -> push -> pop eliminates unnecessary memory + // reallocations in cases where |buffered_| gets emptied. + int64_t frames_played = + std::max(INT64_C(0), total_buffered_frames_ - delay_frames); + current_media_timestamp_ += ComputeBufferedMediaTime(frames_played); + PushBufferedAudioData(frames_written, playback_rate); + PushBufferedAudioData(frames_requested - frames_written, 0.0f); + PopBufferedAudioData(frames_played); + + // Update cached values. + double scaled_frames = 0; + double scaled_frames_at_same_rate = 0; + bool found_silence = false; + audio_data_buffered_ = false; + for (size_t i = 0; i < buffered_.size(); ++i) { + if (buffered_[i].playback_rate == 0) { + found_silence = true; + continue; + } - if (last_endpoint_timestamp_ == kNoTimestamp()) - PushBufferedAudio(delay_frames, 0, kNoTimestamp()); - - TrimBufferedAudioToMatchDelay(delay_frames); - PushBufferedAudio(frames, playback_rate, timestamp); + audio_data_buffered_ = true; - last_endpoint_timestamp_ = timestamp; -} + // Any buffered silence breaks our contiguous stretch of audio data. + if (found_silence) + break; -void AudioClock::WroteSilence(int frames, int delay_frames) { - DCHECK_GE(frames, 0); - DCHECK_GE(delay_frames, 0); + scaled_frames += (buffered_[i].frames * buffered_[i].playback_rate); - if (last_endpoint_timestamp_ == kNoTimestamp()) - PushBufferedAudio(delay_frames, 0, kNoTimestamp()); + if (i == 0) + scaled_frames_at_same_rate = scaled_frames; + } - TrimBufferedAudioToMatchDelay(delay_frames); - PushBufferedAudio(frames, 0, kNoTimestamp()); + contiguous_audio_data_buffered_ = base::TimeDelta::FromMicroseconds( + scaled_frames * microseconds_per_frame_); + contiguous_audio_data_buffered_at_same_rate_ = + base::TimeDelta::FromMicroseconds(scaled_frames_at_same_rate * + microseconds_per_frame_); } -base::TimeDelta AudioClock::CurrentMediaTimestamp( +base::TimeDelta AudioClock::CurrentMediaTimestampSinceWriting( base::TimeDelta time_since_writing) const { - int frames_to_skip = - static_cast(time_since_writing.InSecondsF() * sample_rate_); - int silence_frames = 0; - for (size_t i = 0; i < buffered_audio_.size(); ++i) { - int frames = buffered_audio_[i].frames; - if (frames_to_skip > 0) { - if (frames <= frames_to_skip) { - frames_to_skip -= frames; - continue; - } - frames -= frames_to_skip; - frames_to_skip = 0; - } - - // Account for silence ahead of the buffer closest to being played. - if (buffered_audio_[i].playback_rate == 0) { - silence_frames += frames; - continue; - } - - // Multiply by playback rate as frames represent time-scaled audio. - return buffered_audio_[i].endpoint_timestamp - - base::TimeDelta::FromMicroseconds( - ((frames * buffered_audio_[i].playback_rate) + silence_frames) / - sample_rate_ * base::Time::kMicrosecondsPerSecond); - } + int64_t frames_played_since_writing = std::min( + total_buffered_frames_, + static_cast(time_since_writing.InSecondsF() * sample_rate_)); + return current_media_timestamp_ + + ComputeBufferedMediaTime(frames_played_since_writing); +} - // Either: - // 1) AudioClock is uninitialziated and we'll return kNoTimestamp() - // 2) All previously buffered audio has been replaced by silence, - // meaning media time is now at the last endpoint - return last_endpoint_timestamp_; +AudioClock::AudioData::AudioData(int64_t frames, float playback_rate) + : frames(frames), playback_rate(playback_rate) { } -void AudioClock::TrimBufferedAudioToMatchDelay(int delay_frames) { - if (buffered_audio_.empty()) +void AudioClock::PushBufferedAudioData(int64_t frames, float playback_rate) { + if (frames == 0) return; - size_t i = buffered_audio_.size() - 1; - while (true) { - if (buffered_audio_[i].frames <= delay_frames) { - // Reached the end before accounting for all of |delay_frames|. This - // means we haven't written enough audio data yet to account for hardware - // delay. In this case, do nothing. - if (i == 0) - return; - - // Keep accounting for |delay_frames|. - delay_frames -= buffered_audio_[i].frames; - --i; - continue; - } + total_buffered_frames_ += frames; - // All of |delay_frames| has been accounted for: adjust amount of frames - // left in current buffer. All preceeding elements with index < |i| should - // be considered played out and hence discarded. - buffered_audio_[i].frames = delay_frames; - break; + // Avoid creating extra elements where possible. + if (!buffered_.empty() && buffered_.back().playback_rate == playback_rate) { + buffered_.back().frames += frames; + return; } - // At this point |i| points at what will be the new head of |buffered_audio_| - // however if it contains no audio it should be removed as well. - if (buffered_audio_[i].frames == 0) - ++i; - - buffered_audio_.erase(buffered_audio_.begin(), buffered_audio_.begin() + i); + buffered_.push_back(AudioData(frames, playback_rate)); } -void AudioClock::PushBufferedAudio(int frames, - float playback_rate, - base::TimeDelta endpoint_timestamp) { - if (playback_rate == 0) - DCHECK(endpoint_timestamp == kNoTimestamp()); +void AudioClock::PopBufferedAudioData(int64_t frames) { + DCHECK_LE(frames, total_buffered_frames_); - if (frames == 0) - return; + total_buffered_frames_ -= frames; - // Avoid creating extra elements where possible. - if (!buffered_audio_.empty() && - buffered_audio_.back().playback_rate == playback_rate) { - buffered_audio_.back().frames += frames; - buffered_audio_.back().endpoint_timestamp = endpoint_timestamp; - return; - } + while (frames > 0) { + int64_t frames_to_pop = std::min(buffered_.front().frames, frames); + buffered_.front().frames -= frames_to_pop; + if (buffered_.front().frames == 0) + buffered_.pop_front(); - buffered_audio_.push_back( - BufferedAudio(frames, playback_rate, endpoint_timestamp)); + frames -= frames_to_pop; + } } -AudioClock::BufferedAudio::BufferedAudio(int frames, - float playback_rate, - base::TimeDelta endpoint_timestamp) - : frames(frames), - playback_rate(playback_rate), - endpoint_timestamp(endpoint_timestamp) { +base::TimeDelta AudioClock::ComputeBufferedMediaTime(int64_t frames) const { + DCHECK_LE(frames, total_buffered_frames_); + + double scaled_frames = 0; + for (size_t i = 0; i < buffered_.size() && frames > 0; ++i) { + int64_t min_frames = std::min(buffered_[i].frames, frames); + scaled_frames += min_frames * buffered_[i].playback_rate; + frames -= min_frames; + } + + return base::TimeDelta::FromMicroseconds(scaled_frames * + microseconds_per_frame_); } } // namespace media diff --git a/media/filters/audio_clock.h b/media/filters/audio_clock.h index 625da7d183ee0..1a27dee00092d 100644 --- a/media/filters/audio_clock.h +++ b/media/filters/audio_clock.h @@ -18,59 +18,75 @@ namespace media { // a playback pipeline with large delay. class MEDIA_EXPORT AudioClock { public: - explicit AudioClock(int sample_rate); + AudioClock(base::TimeDelta start_timestamp, int sample_rate); ~AudioClock(); - // |frames| amount of audio data scaled to |playback_rate| was written. + // |frames_written| amount of audio data scaled to |playback_rate| written. + // |frames_requested| amount of audio data requested by hardware. // |delay_frames| is the current amount of hardware delay. - // |timestamp| is the endpoint media timestamp of the audio data written. - void WroteAudio(int frames, + void WroteAudio(int frames_written, + int frames_requested, int delay_frames, - float playback_rate, - base::TimeDelta timestamp); - - // |frames| amount of silence was written. - // |delay_frames| is the current amount of hardware delay. - void WroteSilence(int frames, int delay_frames); + float playback_rate); // Calculates the current media timestamp taking silence and changes in // playback rate into account. - // + base::TimeDelta current_media_timestamp() const { + return current_media_timestamp_; + } + // Clients can provide |time_since_writing| to simulate the passage of time // since last writing audio to get a more accurate current media timestamp. - base::TimeDelta CurrentMediaTimestamp( + base::TimeDelta CurrentMediaTimestampSinceWriting( base::TimeDelta time_since_writing) const; - // Returns the last endpoint timestamp provided to WroteAudio(). - base::TimeDelta last_endpoint_timestamp() const { - return last_endpoint_timestamp_; + // Returns the amount of contiguous media time buffered at the head of the + // audio hardware buffer. Silence introduced into the audio hardware buffer is + // treated as a break in media time. + base::TimeDelta contiguous_audio_data_buffered() const { + return contiguous_audio_data_buffered_; } - private: - void TrimBufferedAudioToMatchDelay(int delay_frames); - void PushBufferedAudio(int frames, - float playback_rate, - base::TimeDelta endpoint_timestamp); - - const int sample_rate_; + // Same as above, but also treats changes in playback rate as a break in media + // time. + base::TimeDelta contiguous_audio_data_buffered_at_same_rate() const { + return contiguous_audio_data_buffered_at_same_rate_; + } - // Initially set to kNoTimestamp(), otherwise is the last endpoint timestamp - // delivered to WroteAudio(). A copy is kept outside of |buffered_audio_| to - // handle the case where all of |buffered_audio_| has been replaced with - // silence. - base::TimeDelta last_endpoint_timestamp_; + // Returns true if there is any audio data buffered by the audio hardware, + // even if there is silence mixed in. + bool audio_data_buffered() const { return audio_data_buffered_; } - struct BufferedAudio { - BufferedAudio(int frames, - float playback_rate, - base::TimeDelta endpoint_timestamp); + private: + // Even with a ridiculously high sample rate of 256kHz, using 64 bits will + // permit tracking up to 416999965 days worth of time (that's 1141 millenia). + // + // 32 bits on the other hand would top out at measly 2 hours and 20 minutes. + struct AudioData { + AudioData(int64_t frames, float playback_rate); - int frames; + int64_t frames; float playback_rate; - base::TimeDelta endpoint_timestamp; }; - std::deque buffered_audio_; + // Helpers for operating on |buffered_|. + void PushBufferedAudioData(int64_t frames, float playback_rate); + void PopBufferedAudioData(int64_t frames); + base::TimeDelta ComputeBufferedMediaTime(int64_t frames) const; + + const base::TimeDelta start_timestamp_; + const int sample_rate_; + const double microseconds_per_frame_; + + std::deque buffered_; + int64_t total_buffered_frames_; + + base::TimeDelta current_media_timestamp_; + + // Cached results of last call to WroteAudio(). + bool audio_data_buffered_; + base::TimeDelta contiguous_audio_data_buffered_; + base::TimeDelta contiguous_audio_data_buffered_at_same_rate_; DISALLOW_COPY_AND_ASSIGN(AudioClock); }; diff --git a/media/filters/audio_clock_unittest.cc b/media/filters/audio_clock_unittest.cc index 00179f9094fda..5f69fd8efe10d 100644 --- a/media/filters/audio_clock_unittest.cc +++ b/media/filters/audio_clock_unittest.cc @@ -12,171 +12,242 @@ namespace media { class AudioClockTest : public testing::Test { public: AudioClockTest() - : sample_rate_(10), - timestamp_helper_(sample_rate_), - clock_(sample_rate_) { - timestamp_helper_.SetBaseTimestamp(base::TimeDelta()); - } + : sample_rate_(10), clock_(base::TimeDelta(), sample_rate_) {} virtual ~AudioClockTest() {} - void WroteAudio(int frames, int delay_frames, float playback_rate) { - timestamp_helper_.AddFrames(static_cast(frames * playback_rate)); + void WroteAudio(int frames_written, + int frames_requested, + int delay_frames, + float playback_rate) { clock_.WroteAudio( - frames, delay_frames, playback_rate, timestamp_helper_.GetTimestamp()); + frames_written, frames_requested, delay_frames, playback_rate); } - void WroteSilence(int frames, int delay_frames) { - clock_.WroteSilence(frames, delay_frames); + int CurrentMediaTimestampInDays() { + return clock_.current_media_timestamp().InDays(); } int CurrentMediaTimestampInMilliseconds() { - return CurrentMediaTimestampSinceLastWritingInMilliseconds(0); + return clock_.current_media_timestamp().InMilliseconds(); } int CurrentMediaTimestampSinceLastWritingInMilliseconds(int milliseconds) { - return clock_.CurrentMediaTimestamp(base::TimeDelta::FromMilliseconds( - milliseconds)).InMilliseconds(); + return clock_.CurrentMediaTimestampSinceWriting( + base::TimeDelta::FromMilliseconds(milliseconds)) + .InMilliseconds(); + } + + int ContiguousAudioDataBufferedInDays() { + return clock_.contiguous_audio_data_buffered().InDays(); + } + + int ContiguousAudioDataBufferedInMilliseconds() { + return clock_.contiguous_audio_data_buffered().InMilliseconds(); } - int LastEndpointTimestampInMilliseconds() { - return clock_.last_endpoint_timestamp().InMilliseconds(); + int ContiguousAudioDataBufferedAtSameRateInMilliseconds() { + return clock_.contiguous_audio_data_buffered_at_same_rate() + .InMilliseconds(); } const int sample_rate_; - AudioTimestampHelper timestamp_helper_; AudioClock clock_; private: DISALLOW_COPY_AND_ASSIGN(AudioClockTest); }; -TEST_F(AudioClockTest, TimestampsStartAtNoTimestamp) { - EXPECT_EQ(kNoTimestamp(), clock_.CurrentMediaTimestamp(base::TimeDelta())); - EXPECT_EQ(kNoTimestamp(), clock_.last_endpoint_timestamp()); +TEST_F(AudioClockTest, CurrentMediaTimestampStartsAtStartTimestamp) { + base::TimeDelta expected = base::TimeDelta::FromSeconds(123); + AudioClock clock(expected, sample_rate_); + + EXPECT_EQ(expected, clock.current_media_timestamp()); +} + +TEST_F(AudioClockTest, + CurrentMediaTimestampSinceWritingStartsAtStartTimestamp) { + base::TimeDelta expected = base::TimeDelta::FromSeconds(123); + AudioClock clock(expected, sample_rate_); + + base::TimeDelta time_since_writing = base::TimeDelta::FromSeconds(456); + EXPECT_EQ(expected, + clock.CurrentMediaTimestampSinceWriting(time_since_writing)); +} + +TEST_F(AudioClockTest, ContiguousAudioDataBufferedStartsAtZero) { + EXPECT_EQ(base::TimeDelta(), clock_.contiguous_audio_data_buffered()); +} + +TEST_F(AudioClockTest, ContiguousAudioDataBufferedAtSameRateStartsAtZero) { + EXPECT_EQ(base::TimeDelta(), + clock_.contiguous_audio_data_buffered_at_same_rate()); +} + +TEST_F(AudioClockTest, AudioDataBufferedStartsAtFalse) { + EXPECT_FALSE(clock_.audio_data_buffered()); } TEST_F(AudioClockTest, Playback) { - // The first time we write data we should expect a negative time matching the - // current delay. - WroteAudio(10, 20, 1.0); - EXPECT_EQ(-2000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(1000, LastEndpointTimestampInMilliseconds()); - - // The media time should keep advancing as we write data. - WroteAudio(10, 20, 1.0); - EXPECT_EQ(-1000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(2000, LastEndpointTimestampInMilliseconds()); - - WroteAudio(10, 20, 1.0); + // The first time we write data we should still expect our start timestamp + // due to delay. + WroteAudio(10, 10, 20, 1.0); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + EXPECT_TRUE(clock_.audio_data_buffered()); - WroteAudio(10, 20, 1.0); + // The media time should remain at start timestamp as we write data. + WroteAudio(10, 10, 20, 1.0); + EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + WroteAudio(10, 10, 20, 1.0); + EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + // The media time should now start advanced now that delay has been covered. + WroteAudio(10, 10, 20, 1.0); EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(4000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - // Introduce a rate change to slow down time. Current time will keep advancing - // by one second until it hits the slowed down audio. - WroteAudio(10, 20, 0.5); + WroteAudio(10, 10, 20, 1.0); EXPECT_EQ(2000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(4500, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - WroteAudio(10, 20, 0.5); + // Introduce a rate change to slow down time: + // - Current time will advance by one second until it hits rate change + // - Contiguous audio data will start shrinking immediately + WroteAudio(10, 10, 20, 0.5); EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(5000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(2500, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(2000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - WroteAudio(10, 20, 0.5); + WroteAudio(10, 10, 20, 0.5); EXPECT_EQ(4000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(5500, LastEndpointTimestampInMilliseconds()); - - WroteAudio(10, 20, 0.5); - EXPECT_EQ(4500, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(6000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(2000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - // Introduce a rate change to speed up time. Current time will keep advancing - // by half a second until it hits the the sped up audio. - WroteAudio(10, 20, 2); + WroteAudio(10, 10, 20, 0.5); EXPECT_EQ(5000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(8000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(1500, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(1500, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - WroteAudio(10, 20, 2); + WroteAudio(10, 10, 20, 0.5); EXPECT_EQ(5500, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(10000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(1500, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(1500, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); - WroteAudio(10, 20, 2); + // Introduce a rate change to speed up time: + // - Current time will advance by half a second until it hits rate change + // - Contiguous audio data will start growing immediately + WroteAudio(10, 10, 20, 2); EXPECT_EQ(6000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(12000, LastEndpointTimestampInMilliseconds()); - - WroteAudio(10, 20, 2); - EXPECT_EQ(8000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); - - // Write silence to simulate reaching end of stream. - WroteSilence(10, 20); - EXPECT_EQ(10000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); - - WroteSilence(10, 20); - EXPECT_EQ(12000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); - - WroteSilence(10, 20); - EXPECT_EQ(14000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(3000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + WroteAudio(10, 10, 20, 2); + EXPECT_EQ(6500, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(4500, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(500, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + WroteAudio(10, 10, 20, 2); + EXPECT_EQ(7000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(6000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(6000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + WroteAudio(10, 10, 20, 2); + EXPECT_EQ(9000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(6000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(6000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + // Write silence to simulate reaching end of stream: + // - Current time will advance by half a second until it hits silence + // - Contiguous audio data will start shrinking towards zero + WroteAudio(0, 10, 20, 2); + EXPECT_EQ(11000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(4000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(4000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + + WroteAudio(0, 10, 20, 2); + EXPECT_EQ(13000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(2000, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(2000, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + EXPECT_TRUE(clock_.audio_data_buffered()); // Still audio data buffered. + + WroteAudio(0, 10, 20, 2); + EXPECT_EQ(15000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + EXPECT_FALSE(clock_.audio_data_buffered()); // No more audio data buffered. // At this point media time should stop increasing. - WroteSilence(10, 20); - EXPECT_EQ(14000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(14000, LastEndpointTimestampInMilliseconds()); + WroteAudio(0, 10, 20, 2); + EXPECT_EQ(15000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedAtSameRateInMilliseconds()); + EXPECT_FALSE(clock_.audio_data_buffered()); } TEST_F(AudioClockTest, AlternatingAudioAndSilence) { // Buffer #1: [0, 1000) - WroteAudio(10, 20, 1.0); - EXPECT_EQ(-2000, CurrentMediaTimestampInMilliseconds()); + WroteAudio(10, 10, 20, 1.0); + EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); // Buffer #2: 1000ms of silence - WroteSilence(10, 20); - EXPECT_EQ(-1000, CurrentMediaTimestampInMilliseconds()); - - // Buffer #3: [1000, 2000), buffer #1 is at front - WroteAudio(10, 20, 1.0); + WroteAudio(0, 10, 20, 1.0); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - // Buffer #4: 1000ms of silence, time shouldn't advance - WroteSilence(10, 20); + // Buffer #3: [1000, 2000): + // - Buffer #1 is at front with 1000ms of contiguous audio data + WroteAudio(10, 10, 20, 1.0); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); + + // Buffer #4: 1000ms of silence + // - Buffer #1 has been played out + // - Buffer #2 of silence leaves us with 0ms of contiguous audio data + WroteAudio(0, 10, 20, 1.0); + EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - // Buffer #5: [2000, 3000), buffer #3 is at front - WroteAudio(10, 20, 1.0); + // Buffer #5: [2000, 3000): + // - Buffer #3 is at front with 1000ms of contiguous audio data + WroteAudio(10, 10, 20, 1.0); EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); } TEST_F(AudioClockTest, ZeroDelay) { // The first time we write data we should expect the first timestamp // immediately. - WroteAudio(10, 0, 1.0); + WroteAudio(10, 10, 0, 1.0); EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(1000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); // Ditto for all subsequent buffers. - WroteAudio(10, 0, 1.0); + WroteAudio(10, 10, 0, 1.0); EXPECT_EQ(1000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(2000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); - WroteAudio(10, 0, 1.0); + WroteAudio(10, 10, 0, 1.0); EXPECT_EQ(2000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(1000, ContiguousAudioDataBufferedInMilliseconds()); // Ditto for silence. - WroteSilence(10, 0); + WroteAudio(0, 10, 0, 1.0); EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); - WroteSilence(10, 0); + WroteAudio(0, 10, 0, 1.0); EXPECT_EQ(3000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(3000, LastEndpointTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); } TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) { @@ -187,20 +258,20 @@ TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) { // +-------------------+----------------+------------------+----------------+ // Media timestamp: 0 1000 1500 3500 // Wall clock time: 2000 3000 4000 5000 - WroteAudio(10, 40, 1.0); - WroteAudio(10, 40, 0.5); - WroteAudio(10, 40, 2.0); - EXPECT_EQ(-2000, CurrentMediaTimestampInMilliseconds()); - EXPECT_EQ(3500, LastEndpointTimestampInMilliseconds()); + WroteAudio(10, 10, 40, 1.0); + WroteAudio(10, 10, 40, 0.5); + WroteAudio(10, 10, 40, 2.0); + EXPECT_EQ(0, CurrentMediaTimestampInMilliseconds()); + EXPECT_EQ(0, ContiguousAudioDataBufferedInMilliseconds()); // Simulate passing 2000ms of initial delay in the audio hardware. - EXPECT_EQ(-2000, CurrentMediaTimestampSinceLastWritingInMilliseconds(0)); - EXPECT_EQ(-1500, CurrentMediaTimestampSinceLastWritingInMilliseconds(500)); - EXPECT_EQ(-1000, CurrentMediaTimestampSinceLastWritingInMilliseconds(1000)); - EXPECT_EQ(-500, CurrentMediaTimestampSinceLastWritingInMilliseconds(1500)); + EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(0)); + EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(500)); + EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(1000)); + EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(1500)); EXPECT_EQ(0, CurrentMediaTimestampSinceLastWritingInMilliseconds(2000)); - // New we should see the 1.0x buffer. + // Now we should see the 1.0x buffer. EXPECT_EQ(500, CurrentMediaTimestampSinceLastWritingInMilliseconds(2500)); EXPECT_EQ(1000, CurrentMediaTimestampSinceLastWritingInMilliseconds(3000)); @@ -213,11 +284,40 @@ TEST_F(AudioClockTest, CurrentMediaTimestampSinceLastWriting) { EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(5000)); // Times beyond the known length of the audio clock should return the last - // value we know of. - EXPECT_EQ(LastEndpointTimestampInMilliseconds(), - CurrentMediaTimestampSinceLastWritingInMilliseconds(5001)); - EXPECT_EQ(LastEndpointTimestampInMilliseconds(), - CurrentMediaTimestampSinceLastWritingInMilliseconds(6000)); + // media timestamp we know of. + EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(5001)); + EXPECT_EQ(3500, CurrentMediaTimestampSinceLastWritingInMilliseconds(6000)); +} + +TEST_F(AudioClockTest, SupportsYearsWorthOfAudioData) { + // Use number of frames that would be likely to overflow 32-bit integer math. + const int huge_amount_of_frames = std::numeric_limits::max(); + const base::TimeDelta huge = + base::TimeDelta::FromSeconds(huge_amount_of_frames / sample_rate_); + EXPECT_EQ(2485, huge.InDays()); // Just to give some context on how big... + + // Use zero delay to test calculation of current timestamp. + WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); + EXPECT_EQ(0, CurrentMediaTimestampInDays()); + EXPECT_EQ(2485, ContiguousAudioDataBufferedInDays()); + + WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); + EXPECT_EQ(huge.InDays(), CurrentMediaTimestampInDays()); + EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays()); + + WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); + EXPECT_EQ((huge * 2).InDays(), CurrentMediaTimestampInDays()); + EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays()); + + WroteAudio(huge_amount_of_frames, huge_amount_of_frames, 0, 1.0); + EXPECT_EQ((huge * 3).InDays(), CurrentMediaTimestampInDays()); + EXPECT_EQ(huge.InDays(), ContiguousAudioDataBufferedInDays()); + + // Use huge delay to test calculation of buffered data. + WroteAudio( + huge_amount_of_frames, huge_amount_of_frames, huge_amount_of_frames, 1.0); + EXPECT_EQ((huge * 3).InDays(), CurrentMediaTimestampInDays()); + EXPECT_EQ((huge * 2).InDays(), ContiguousAudioDataBufferedInDays()); } } // namespace media diff --git a/media/filters/audio_decoder_selector_unittest.cc b/media/filters/audio_decoder_selector_unittest.cc index eb0e8dbd3c460..3b55925a803fc 100644 --- a/media/filters/audio_decoder_selector_unittest.cc +++ b/media/filters/audio_decoder_selector_unittest.cc @@ -14,12 +14,29 @@ #include "testing/gtest/include/gtest/gtest.h" using ::testing::_; +using ::testing::InvokeWithoutArgs; using ::testing::IsNull; using ::testing::NiceMock; using ::testing::NotNull; using ::testing::Return; using ::testing::StrictMock; +// Use anonymous namespace here to prevent the actions to be defined multiple +// times across multiple test files. Sadly we can't use static for them. +namespace { + +ACTION_P3(ExecuteCallbackWithVerifier, decryptor, done_cb, verifier) { + // verifier must be called first since |done_cb| call will invoke it as well. + verifier->RecordACalled(); + arg0.Run(decryptor, done_cb); +} + +ACTION_P(ReportCallback, verifier) { + verifier->RecordBCalled(); +} + +} // namespace + namespace media { class AudioDecoderSelectorTest : public ::testing::Test { @@ -51,6 +68,7 @@ class AudioDecoderSelectorTest : public ::testing::Test { MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); MOCK_METHOD2(OnDecoderSelected, void(AudioDecoder*, DecryptingDemuxerStream*)); + MOCK_METHOD1(DecryptorSet, void(bool)); void MockOnDecoderSelected(scoped_ptr decoder, scoped_ptr stream) { @@ -83,9 +101,14 @@ class AudioDecoderSelectorTest : public ::testing::Test { if (decryptor_capability == kDecryptOnly || decryptor_capability == kDecryptAndDecode) { - EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) - .WillRepeatedly(RunCallback<0>(decryptor_.get())); + .WillRepeatedly(ExecuteCallbackWithVerifier( + decryptor_.get(), + base::Bind(&AudioDecoderSelectorTest::DecryptorSet, + base::Unretained(this)), + &verifier_)); + EXPECT_CALL(*this, DecryptorSet(true)) + .WillRepeatedly(ReportCallback(&verifier_)); if (decryptor_capability == kDecryptOnly) { EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) @@ -149,6 +172,8 @@ class AudioDecoderSelectorTest : public ::testing::Test { base::MessageLoop message_loop_; + CallbackPairChecker verifier_; + private: DISALLOW_COPY_AND_ASSIGN(AudioDecoderSelectorTest); }; diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc index b4a4b4125aeb1..fbf4c163d30f8 100644 --- a/media/filters/audio_renderer_impl.cc +++ b/media/filters/audio_renderer_impl.cc @@ -61,6 +61,7 @@ AudioRendererImpl::AudioRendererImpl( pending_read_(false), received_end_of_stream_(false), rendered_end_of_stream_(false), + last_timestamp_update_(kNoTimestamp()), weak_factory_(this) { audio_buffer_stream_->set_splice_observer(base::Bind( &AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr())); @@ -147,6 +148,7 @@ void AudioRendererImpl::SetMediaTime(base::TimeDelta time) { DCHECK_EQ(state_, kFlushed); start_timestamp_ = time; + audio_clock_.reset(new AudioClock(time, audio_parameters_.sample_rate())); } base::TimeDelta AudioRendererImpl::CurrentMediaTime() { @@ -201,9 +203,10 @@ void AudioRendererImpl::ResetDecoderDone() { DCHECK_EQ(state_, kFlushed); DCHECK(!flush_cb_.is_null()); - audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate())); + audio_clock_.reset(); received_end_of_stream_ = false; rendered_end_of_stream_ = false; + last_timestamp_update_ = kNoTimestamp(); // Flush() may have been called while underflowed/not fully buffered. if (buffering_state_ != BUFFERING_HAVE_NOTHING) @@ -294,7 +297,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, hardware_config_->GetHighLatencyBufferSize()); } - audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate())); + audio_clock_.reset( + new AudioClock(base::TimeDelta(), audio_parameters_.sample_rate())); audio_buffer_stream_->Initialize( stream, @@ -549,18 +553,21 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, // Ensure Stop() hasn't destroyed our |algorithm_| on the pipeline thread. if (!algorithm_) { - audio_clock_->WroteSilence(requested_frames, delay_frames); + audio_clock_->WroteAudio( + 0, requested_frames, delay_frames, playback_rate_); return 0; } if (playback_rate_ == 0) { - audio_clock_->WroteSilence(requested_frames, delay_frames); + audio_clock_->WroteAudio( + 0, requested_frames, delay_frames, playback_rate_); return 0; } // Mute audio by returning 0 when not playing. if (state_ != kPlaying) { - audio_clock_->WroteSilence(requested_frames, delay_frames); + audio_clock_->WroteAudio( + 0, requested_frames, delay_frames, playback_rate_); return 0; } @@ -576,20 +583,16 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, // 3) We are in the kPlaying state // // Otherwise the buffer has data we can send to the device. - const base::TimeDelta media_timestamp_before_filling = - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()); if (algorithm_->frames_buffered() > 0) { frames_written = algorithm_->FillBuffer(audio_bus, requested_frames, playback_rate_); - audio_clock_->WroteAudio( - frames_written, delay_frames, playback_rate_, algorithm_->GetTime()); } - audio_clock_->WroteSilence(requested_frames - frames_written, delay_frames); + audio_clock_->WroteAudio( + frames_written, requested_frames, delay_frames, playback_rate_); if (frames_written == 0) { if (received_end_of_stream_ && !rendered_end_of_stream_ && - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) == - audio_clock_->last_endpoint_timestamp()) { + !audio_clock_->audio_data_buffered()) { rendered_end_of_stream_ = true; task_runner_->PostTask(FROM_HERE, ended_cb_); } else if (!received_end_of_stream_ && state_ == kPlaying) { @@ -606,15 +609,18 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, weak_factory_.GetWeakPtr())); } - // We only want to execute |time_cb_| if time has progressed and we haven't - // signaled end of stream yet. - if (media_timestamp_before_filling != - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()) && - !rendered_end_of_stream_) { - time_cb = - base::Bind(time_cb_, - audio_clock_->CurrentMediaTimestamp(base::TimeDelta()), - audio_clock_->last_endpoint_timestamp()); + // Firing |ended_cb_| means we no longer need to run |time_cb_|. + if (!rendered_end_of_stream_ && + last_timestamp_update_ != audio_clock_->current_media_timestamp()) { + // Since |max_time| uses linear interpolation, only provide an upper bound + // that is for audio data at the same playback rate. Failing to do so can + // make time jump backwards when the linear interpolated time advances + // past buffered regions of audio at different rates. + last_timestamp_update_ = audio_clock_->current_media_timestamp(); + base::TimeDelta max_time = + last_timestamp_update_ + + audio_clock_->contiguous_audio_data_buffered_at_same_rate(); + time_cb = base::Bind(time_cb_, last_timestamp_update_, max_time); } } diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h index 423063b08ed71..99d6200cabcad 100644 --- a/media/filters/audio_renderer_impl.h +++ b/media/filters/audio_renderer_impl.h @@ -249,6 +249,7 @@ class MEDIA_EXPORT AudioRendererImpl scoped_ptr audio_clock_; base::TimeDelta start_timestamp_; + base::TimeDelta last_timestamp_update_; // End variables which must be accessed under |lock_|. ---------------------- diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc index 748120f90cf05..98d5e7fa3d544 100644 --- a/media/filters/audio_renderer_impl_unittest.cc +++ b/media/filters/audio_renderer_impl_unittest.cc @@ -62,6 +62,7 @@ class AudioRendererImplTest : public ::testing::Test { demuxer_stream_(DemuxerStream::AUDIO), decoder_(new MockAudioDecoder()), last_time_update_(kNoTimestamp()), + last_max_time_(kNoTimestamp()), ended_(false) { AudioDecoderConfig audio_config(kCodec, kSampleFormat, @@ -117,6 +118,7 @@ class AudioRendererImplTest : public ::testing::Test { void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) { CHECK(current_time <= max_time); last_time_update_ = current_time; + last_max_time_ = max_time; } void InitializeRenderer(const PipelineStatusCB& pipeline_status_cb) { @@ -334,6 +336,8 @@ class AudioRendererImplTest : public ::testing::Test { return last_time_update_; } + base::TimeDelta last_max_time() const { return last_max_time_; } + bool ended() const { return ended_; } // Fixture members. @@ -404,6 +408,7 @@ class AudioRendererImplTest : public ::testing::Test { PipelineStatusCB init_decoder_cb_; base::TimeDelta last_time_update_; + base::TimeDelta last_max_time_; bool ended_; DISALLOW_COPY_AND_ASSIGN(AudioRendererImplTest); @@ -628,6 +633,7 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) { AudioTimestampHelper timestamp_helper(kOutputSamplesPerSecond); EXPECT_EQ(kNoTimestamp(), last_time_update()); + EXPECT_EQ(kNoTimestamp(), last_max_time()); // Preroll() should be buffered some data, consume half of it now. OutputFrames frames_to_consume(frames_buffered().value / 2); @@ -639,17 +645,21 @@ TEST_F(AudioRendererImplTest, TimeUpdatesOnFirstBuffer) { // a time update that's equal to |kFramesToConsume| from above. timestamp_helper.SetBaseTimestamp(base::TimeDelta()); timestamp_helper.AddFrames(frames_to_consume.value); - EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); + EXPECT_EQ(base::TimeDelta(), last_time_update()); + EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time()); // The next time update should match the remaining frames_buffered(), but only // after running the message loop. frames_to_consume = frames_buffered(); EXPECT_TRUE(ConsumeBufferedData(frames_to_consume)); - EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); + EXPECT_EQ(base::TimeDelta(), last_time_update()); + EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time()); + // Now the times should be updated. base::RunLoop().RunUntilIdle(); - timestamp_helper.AddFrames(frames_to_consume.value); EXPECT_EQ(timestamp_helper.GetTimestamp(), last_time_update()); + timestamp_helper.AddFrames(frames_to_consume.value); + EXPECT_EQ(timestamp_helper.GetTimestamp(), last_max_time()); } TEST_F(AudioRendererImplTest, ImmediateEndOfStream) { diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc index c80ee9ea2d1e1..ff4337d157bca 100644 --- a/media/filters/chunk_demuxer.cc +++ b/media/filters/chunk_demuxer.cc @@ -854,7 +854,7 @@ TimeDelta ChunkDemuxerStream::GetBufferedDuration() const { return stream_->GetBufferedDuration(); } -void ChunkDemuxerStream::OnNewMediaSegment(TimeDelta start_timestamp) { +void ChunkDemuxerStream::OnNewMediaSegment(DecodeTimestamp start_timestamp) { DVLOG(2) << "ChunkDemuxerStream::OnNewMediaSegment(" << start_timestamp.InSecondsF() << ")"; base::AutoLock auto_lock(lock_); diff --git a/media/filters/chunk_demuxer.h b/media/filters/chunk_demuxer.h index 2abeeeaef59d7..476780bff2df8 100644 --- a/media/filters/chunk_demuxer.h +++ b/media/filters/chunk_demuxer.h @@ -67,7 +67,7 @@ class MEDIA_EXPORT ChunkDemuxerStream : public DemuxerStream { // Signal to the stream that buffers handed in through subsequent calls to // Append() belong to a media segment that starts at |start_timestamp|. - void OnNewMediaSegment(base::TimeDelta start_timestamp); + void OnNewMediaSegment(DecodeTimestamp start_timestamp); // Called when midstream config updates occur. // Returns true if the new config is accepted. diff --git a/media/filters/decrypting_audio_decoder.cc b/media/filters/decrypting_audio_decoder.cc index 79d82093302c3..ee50e2edac8ff 100644 --- a/media/filters/decrypting_audio_decoder.cc +++ b/media/filters/decrypting_audio_decoder.cc @@ -166,7 +166,9 @@ DecryptingAudioDecoder::~DecryptingAudioDecoder() { base::ResetAndReturn(&reset_cb_).Run(); } -void DecryptingAudioDecoder::SetDecryptor(Decryptor* decryptor) { +void DecryptingAudioDecoder::SetDecryptor( + Decryptor* decryptor, + const DecryptorAttachedCB& decryptor_attached_cb) { DVLOG(2) << "SetDecryptor()"; DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kDecryptorRequested) << state_; @@ -178,12 +180,14 @@ void DecryptingAudioDecoder::SetDecryptor(Decryptor* decryptor) { if (!decryptor) { base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); state_ = kError; + decryptor_attached_cb.Run(false); return; } decryptor_ = decryptor; InitializeDecoder(); + decryptor_attached_cb.Run(true); } void DecryptingAudioDecoder::InitializeDecoder() { diff --git a/media/filters/decrypting_audio_decoder.h b/media/filters/decrypting_audio_decoder.h index 70cbc5632268c..9ea211a1ab9f7 100644 --- a/media/filters/decrypting_audio_decoder.h +++ b/media/filters/decrypting_audio_decoder.h @@ -66,8 +66,10 @@ class MEDIA_EXPORT DecryptingAudioDecoder : public AudioDecoder { kError }; - // Callback for DecryptorHost::RequestDecryptor(). - void SetDecryptor(Decryptor* decryptor); + // Callback for DecryptorHost::RequestDecryptor(). |decryptor_attached_cb| is + // called when the decryptor has been completely attached to the pipeline. + void SetDecryptor(Decryptor* decryptor, + const DecryptorAttachedCB& decryptor_attached_cb); // Initializes the audio decoder on the |decryptor_| with |config_|. void InitializeDecoder(); diff --git a/media/filters/decrypting_audio_decoder_unittest.cc b/media/filters/decrypting_audio_decoder_unittest.cc index 83d7f5f36bdc9..f6acc1bb607c4 100644 --- a/media/filters/decrypting_audio_decoder_unittest.cc +++ b/media/filters/decrypting_audio_decoder_unittest.cc @@ -54,11 +54,6 @@ ACTION_P(ReturnBuffer, buffer) { return buffer; } -ACTION_P(RunCallbackIfNotNull, param) { - if (!arg0.is_null()) - arg0.Run(param); -} - } // namespace class DecryptingAudioDecoderTest : public testing::Test { @@ -103,12 +98,19 @@ class DecryptingAudioDecoderTest : public testing::Test { message_loop_.RunUntilIdle(); } + void ExpectDecryptorNotification(Decryptor* decryptor, bool expected_result) { + EXPECT_CALL(*this, RequestDecryptorNotification(_)).WillOnce( + RunCallback<0>(decryptor, + base::Bind(&DecryptingAudioDecoderTest::DecryptorSet, + base::Unretained(this)))); + EXPECT_CALL(*this, DecryptorSet(expected_result)); + } + void Initialize() { EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) .Times(AtMost(1)) .WillOnce(RunCallback<1>(true)); - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillOnce(RunCallbackIfNotNull(decryptor_.get())); + ExpectDecryptorNotification(decryptor_.get(), true); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) .WillOnce(SaveArg<1>(&key_added_cb_)); @@ -248,6 +250,8 @@ class DecryptingAudioDecoderTest : public testing::Test { MOCK_METHOD1(FrameReady, void(const scoped_refptr&)); MOCK_METHOD1(DecodeDone, void(AudioDecoder::Status)); + MOCK_METHOD1(DecryptorSet, void(bool)); + base::MessageLoop message_loop_; scoped_ptr decoder_; scoped_ptr > decryptor_; @@ -294,8 +298,7 @@ TEST_F(DecryptingAudioDecoderTest, Initialize_InvalidAudioConfig) { TEST_F(DecryptingAudioDecoderTest, Initialize_UnsupportedAudioConfig) { EXPECT_CALL(*decryptor_, InitializeAudioDecoder(_, _)) .WillOnce(RunCallback<1>(false)); - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillOnce(RunCallbackIfNotNull(decryptor_.get())); + ExpectDecryptorNotification(decryptor_.get(), true); AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, kSampleRate, NULL, 0, true); @@ -303,9 +306,7 @@ TEST_F(DecryptingAudioDecoderTest, Initialize_UnsupportedAudioConfig) { } TEST_F(DecryptingAudioDecoderTest, Initialize_NullDecryptor) { - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillRepeatedly(RunCallbackIfNotNull(static_cast(NULL))); - + ExpectDecryptorNotification(NULL, false); AudioDecoderConfig config(kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, kSampleRate, NULL, 0, true); InitializeAndExpectStatus(config, DECODER_ERROR_NOT_SUPPORTED); diff --git a/media/filters/decrypting_demuxer_stream.cc b/media/filters/decrypting_demuxer_stream.cc index 0f0930eb723ff..c4e6b8478380d 100644 --- a/media/filters/decrypting_demuxer_stream.cc +++ b/media/filters/decrypting_demuxer_stream.cc @@ -160,7 +160,9 @@ DecryptingDemuxerStream::~DecryptingDemuxerStream() { pending_buffer_to_decrypt_ = NULL; } -void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) { +void DecryptingDemuxerStream::SetDecryptor( + Decryptor* decryptor, + const DecryptorAttachedCB& decryptor_attached_cb) { DVLOG(2) << __FUNCTION__; DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kDecryptorRequested) << state_; @@ -172,6 +174,7 @@ void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) { if (!decryptor) { state_ = kUninitialized; base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); + decryptor_attached_cb.Run(false); return; } @@ -184,6 +187,7 @@ void DecryptingDemuxerStream::SetDecryptor(Decryptor* decryptor) { state_ = kIdle; base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); + decryptor_attached_cb.Run(true); } void DecryptingDemuxerStream::DecryptBuffer( diff --git a/media/filters/decrypting_demuxer_stream.h b/media/filters/decrypting_demuxer_stream.h index c65df176fa003..b98727751f36c 100644 --- a/media/filters/decrypting_demuxer_stream.h +++ b/media/filters/decrypting_demuxer_stream.h @@ -67,8 +67,10 @@ class MEDIA_EXPORT DecryptingDemuxerStream : public DemuxerStream { kWaitingForKey }; - // Callback for DecryptorHost::RequestDecryptor(). - void SetDecryptor(Decryptor* decryptor); + // Callback for DecryptorHost::RequestDecryptor(). |decryptor_attached_cb| is + // called when the decryptor has been completely attached to the pipeline. + void SetDecryptor(Decryptor* decryptor, + const DecryptorAttachedCB& decryptor_attached_cb); // Callback for DemuxerStream::Read(). void DecryptBuffer(DemuxerStream::Status status, diff --git a/media/filters/decrypting_demuxer_stream_unittest.cc b/media/filters/decrypting_demuxer_stream_unittest.cc index fec56248e0bc9..fa2a36cf947a6 100644 --- a/media/filters/decrypting_demuxer_stream_unittest.cc +++ b/media/filters/decrypting_demuxer_stream_unittest.cc @@ -53,9 +53,9 @@ ACTION_P(ReturnBuffer, buffer) { // Sets the |decryptor| if the DecryptorReadyCB (arg0) is not null. Sets // |is_decryptor_set| to true if a non-NULL |decryptor| has been set through the // callback. -ACTION_P2(SetDecryptorIfNotNull, decryptor, is_decryptor_set) { +ACTION_P3(SetDecryptorIfNotNull, decryptor, done_cb, is_decryptor_set) { if (!arg0.is_null()) - arg0.Run(decryptor); + arg0.Run(decryptor, done_cb); *is_decryptor_set = !arg0.is_null() && decryptor; } @@ -112,14 +112,23 @@ class DecryptingDemuxerStreamTest : public testing::Test { message_loop_.RunUntilIdle(); } + void ExpectDecryptorNotification(Decryptor* decryptor, bool expected_result) { + EXPECT_CALL(*this, RequestDecryptorNotification(_)) + .WillOnce(SetDecryptorIfNotNull( + decryptor, + base::Bind(&DecryptingDemuxerStreamTest::DecryptorSet, + base::Unretained(this)), + &is_decryptor_set_)); + EXPECT_CALL(*this, DecryptorSet(expected_result)); + } + // The following functions are used to test stream-type-neutral logic in // DecryptingDemuxerStream. Therefore, we don't specify audio or video in the // function names. But for testing purpose, they all use an audio input // demuxer stream. void Initialize() { - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillOnce(SetDecryptorIfNotNull(decryptor_.get(), &is_decryptor_set_)); + ExpectDecryptorNotification(decryptor_.get(), true); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kAudio, _)) .WillOnce(SaveArg<1>(&key_added_cb_)); @@ -249,6 +258,8 @@ class DecryptingDemuxerStreamTest : public testing::Test { MOCK_METHOD2(BufferReady, void(DemuxerStream::Status, const scoped_refptr&)); + MOCK_METHOD1(DecryptorSet, void(bool)); + base::MessageLoop message_loop_; scoped_ptr demuxer_stream_; scoped_ptr > decryptor_; @@ -276,8 +287,7 @@ TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalAudio) { } TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalVideo) { - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillOnce(SetDecryptorIfNotNull(decryptor_.get(), &is_decryptor_set_)); + ExpectDecryptorNotification(decryptor_.get(), true); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) .WillOnce(SaveArg<1>(&key_added_cb_)); @@ -303,10 +313,7 @@ TEST_F(DecryptingDemuxerStreamTest, Initialize_NormalVideo) { } TEST_F(DecryptingDemuxerStreamTest, Initialize_NullDecryptor) { - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillRepeatedly(SetDecryptorIfNotNull(static_cast(NULL), - &is_decryptor_set_)); - + ExpectDecryptorNotification(NULL, false); AudioDecoderConfig input_config(kCodecVorbis, kSampleFormatPlanarF32, CHANNEL_LAYOUT_STEREO, 44100, NULL, 0, true); InitializeAudioAndExpectStatus(input_config, DECODER_ERROR_NOT_SUPPORTED); diff --git a/media/filters/decrypting_video_decoder.cc b/media/filters/decrypting_video_decoder.cc index 2651c5da74040..421ec07fa89f8 100644 --- a/media/filters/decrypting_video_decoder.cc +++ b/media/filters/decrypting_video_decoder.cc @@ -146,7 +146,9 @@ DecryptingVideoDecoder::~DecryptingVideoDecoder() { base::ResetAndReturn(&reset_cb_).Run(); } -void DecryptingVideoDecoder::SetDecryptor(Decryptor* decryptor) { +void DecryptingVideoDecoder::SetDecryptor( + Decryptor* decryptor, + const DecryptorAttachedCB& decryptor_attached_cb) { DVLOG(2) << "SetDecryptor()"; DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK_EQ(state_, kDecryptorRequested) << state_; @@ -157,6 +159,7 @@ void DecryptingVideoDecoder::SetDecryptor(Decryptor* decryptor) { if (!decryptor) { base::ResetAndReturn(&init_cb_).Run(DECODER_ERROR_NOT_SUPPORTED); state_ = kError; + decryptor_attached_cb.Run(false); return; } @@ -167,6 +170,7 @@ void DecryptingVideoDecoder::SetDecryptor(Decryptor* decryptor) { config_, BindToCurrentLoop(base::Bind( &DecryptingVideoDecoder::FinishInitialization, weak_this_))); + decryptor_attached_cb.Run(true); } void DecryptingVideoDecoder::FinishInitialization(bool success) { diff --git a/media/filters/decrypting_video_decoder.h b/media/filters/decrypting_video_decoder.h index a1f43edfeac27..6550a2986b232 100644 --- a/media/filters/decrypting_video_decoder.h +++ b/media/filters/decrypting_video_decoder.h @@ -55,8 +55,10 @@ class MEDIA_EXPORT DecryptingVideoDecoder : public VideoDecoder { kError }; - // Callback for DecryptorHost::RequestDecryptor(). - void SetDecryptor(Decryptor* decryptor); + // Callback for DecryptorHost::RequestDecryptor(). |decryptor_attached_cb| is + // called when the decryptor has been completely attached to the pipeline. + void SetDecryptor(Decryptor* decryptor, + const DecryptorAttachedCB& decryptor_attached_cb); // Callback for Decryptor::InitializeVideoDecoder() during initialization. void FinishInitialization(bool success); diff --git a/media/filters/decrypting_video_decoder_unittest.cc b/media/filters/decrypting_video_decoder_unittest.cc index ed956ea1d51e0..11dc52ae7a0be 100644 --- a/media/filters/decrypting_video_decoder_unittest.cc +++ b/media/filters/decrypting_video_decoder_unittest.cc @@ -44,13 +44,8 @@ static scoped_refptr CreateFakeEncryptedBuffer() { // times across multiple test files. Sadly we can't use static for them. namespace { -ACTION_P(RunCallbackIfNotNull, param) { - if (!arg0.is_null()) - arg0.Run(param); -} - -ACTION_P2(ResetAndRunCallback, callback, param) { - base::ResetAndReturn(callback).Run(param); +ACTION_P3(ResetAndRunCallback, callback, p1, p2) { + base::ResetAndReturn(callback).Run(p1, p2); } } // namespace @@ -70,14 +65,20 @@ class DecryptingVideoDecoderTest : public testing::Test { decoded_video_frame_(VideoFrame::CreateBlackFrame( TestVideoConfig::NormalCodedSize())), null_video_frame_(scoped_refptr()) { - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillRepeatedly(RunCallbackIfNotNull(decryptor_.get())); } virtual ~DecryptingVideoDecoderTest() { Destroy(); } + void ExpectDecryptorNotification(Decryptor* decryptor, bool expected_result) { + EXPECT_CALL(*this, RequestDecryptorNotification(_)).WillOnce( + RunCallback<0>(decryptor, + base::Bind(&DecryptingVideoDecoderTest::DecryptorSet, + base::Unretained(this)))); + EXPECT_CALL(*this, DecryptorSet(expected_result)); + } + // Initializes the |decoder_| and expects |status|. Note the initialization // can succeed or fail. void InitializeAndExpectStatus(const VideoDecoderConfig& config, @@ -90,6 +91,7 @@ class DecryptingVideoDecoderTest : public testing::Test { // Initialize the |decoder_| and expects it to succeed. void Initialize() { + ExpectDecryptorNotification(decryptor_.get(), true); EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) .WillOnce(RunCallback<1>(true)); EXPECT_CALL(*decryptor_, RegisterNewKeyCB(Decryptor::kVideo, _)) @@ -223,6 +225,8 @@ class DecryptingVideoDecoderTest : public testing::Test { MOCK_METHOD1(FrameReady, void(const scoped_refptr&)); MOCK_METHOD1(DecodeDone, void(VideoDecoder::Status)); + MOCK_METHOD1(DecryptorSet, void(bool)); + base::MessageLoop message_loop_; scoped_ptr decoder_; scoped_ptr > decryptor_; @@ -249,8 +253,7 @@ TEST_F(DecryptingVideoDecoderTest, Initialize_Normal) { } TEST_F(DecryptingVideoDecoderTest, Initialize_NullDecryptor) { - EXPECT_CALL(*this, RequestDecryptorNotification(_)) - .WillRepeatedly(RunCallbackIfNotNull(static_cast(NULL))); + ExpectDecryptorNotification(NULL, false); InitializeAndExpectStatus(TestVideoConfig::NormalEncrypted(), DECODER_ERROR_NOT_SUPPORTED); } @@ -415,14 +418,18 @@ TEST_F(DecryptingVideoDecoderTest, Destroy_DuringDecryptorRequested) { // During destruction, RequestDecryptorNotification() should be called with a // NULL callback to cancel the |decryptor_ready_cb|. - EXPECT_CALL(*this, RequestDecryptorNotification(IsNullCallback())) - .WillOnce(ResetAndRunCallback(&decryptor_ready_cb, - reinterpret_cast(NULL))); + EXPECT_CALL(*this, RequestDecryptorNotification(IsNullCallback())).WillOnce( + ResetAndRunCallback(&decryptor_ready_cb, + reinterpret_cast(NULL), + base::Bind(&DecryptingVideoDecoderTest::DecryptorSet, + base::Unretained(this)))); + EXPECT_CALL(*this, DecryptorSet(_)).Times(0); Destroy(); } // Test destruction when the decoder is in kPendingDecoderInit state. TEST_F(DecryptingVideoDecoderTest, Destroy_DuringPendingDecoderInit) { + ExpectDecryptorNotification(decryptor_.get(), true); EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) .WillOnce(SaveArg<1>(&pending_init_cb_)); diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index 15b6a51fb17b2..d5152300d055f 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -305,6 +305,12 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) { start_time = base::TimeDelta(); } + // Don't rebase timestamps for positive start times, the HTML Media Spec + // details this in section "4.8.10.6 Offsets into the media resource." We + // will still need to rebase timestamps before seeking with FFmpeg though. + if (start_time > base::TimeDelta()) + start_time = base::TimeDelta(); + buffer->set_timestamp(stream_timestamp - start_time); // If enabled, mark audio packets with negative timestamps for post-decode @@ -557,8 +563,12 @@ FFmpegDemuxer::~FFmpegDemuxer() {} void FFmpegDemuxer::Stop(const base::Closure& callback) { DCHECK(task_runner_->BelongsToCurrentThread()); - url_protocol_->Abort(); + + // The order of Stop() and Abort() is important here. If Abort() is called + // first, control may pass into FFmpeg where it can destruct buffers that are + // in the process of being fulfilled by the DataSource. data_source_->Stop(); + url_protocol_->Abort(); // This will block until all tasks complete. Note that after this returns it's // possible for reply tasks (e.g., OnReadFrameDone()) to be queued on this @@ -585,7 +595,15 @@ void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { // we know we're going to drop it on the floor. // FFmpeg requires seeks to be adjusted according to the lowest starting time. - const base::TimeDelta seek_time = time + start_time_; + // Since EnqueuePacket() rebased negative timestamps by the start time, we + // must correct the shift here. + // + // Additionally, to workaround limitations in how we expose seekable ranges to + // Blink (http://crbug.com/137275), we also want to clamp seeks before the + // start time to the start time. + const base::TimeDelta seek_time = + start_time_ < base::TimeDelta() ? time + start_time_ + : time < start_time_ ? start_time_ : time; // Choose the seeking stream based on whether it contains the seek time, if no // match can be found prefer the preferred stream. diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index 76ccba2259a4f..dedb3ae00a337 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -441,17 +441,9 @@ TEST_F(FFmpegDemuxerTest, Read_VideoPositiveStartTime) { // Run the test twice with a seek in between. for (int i = 0; i < 2; ++i) { - // Check first buffer in video stream. It should have been adjusted such - // that it starts 400ms after the first audio buffer. - video->Read( - NewReadCB(FROM_HERE, - 5636, - (video_start_time - audio_start_time).InMicroseconds())); + video->Read(NewReadCB(FROM_HERE, 5636, video_start_time.InMicroseconds())); message_loop_.Run(); - - // Since the audio buffer has a lower first timestamp, it should become - // zero. - audio->Read(NewReadCB(FROM_HERE, 165, 0)); + audio->Read(NewReadCB(FROM_HERE, 165, audio_start_time.InMicroseconds())); message_loop_.Run(); // Verify that the start time is equal to the lowest timestamp (ie the diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc index c3fde2700bdfb..18013bd3cc789 100644 --- a/media/filters/ffmpeg_video_decoder_unittest.cc +++ b/media/filters/ffmpeg_video_decoder_unittest.cc @@ -242,37 +242,57 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) { TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorZero) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 0, 1); - VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN, + VideoDecoderConfig config(kCodecVP8, + VP8PROFILE_ANY, kVideoFormat, - kCodedSize, kVisibleRect, natural_size, - NULL, 0, false); + kCodedSize, + kVisibleRect, + natural_size, + NULL, + 0, + false); InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED); } TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorZero) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, 0); - VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN, + VideoDecoderConfig config(kCodecVP8, + VP8PROFILE_ANY, kVideoFormat, - kCodedSize, kVisibleRect, natural_size, - NULL, 0, false); + kCodedSize, + kVisibleRect, + natural_size, + NULL, + 0, + false); InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED); } TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorNegative) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), -1, 1); - VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN, + VideoDecoderConfig config(kCodecVP8, + VP8PROFILE_ANY, kVideoFormat, - kCodedSize, kVisibleRect, natural_size, - NULL, 0, false); + kCodedSize, + kVisibleRect, + natural_size, + NULL, + 0, + false); InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED); } TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorNegative) { gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, -1); - VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN, + VideoDecoderConfig config(kCodecVP8, + VP8PROFILE_ANY, kVideoFormat, - kCodedSize, kVisibleRect, natural_size, - NULL, 0, false); + kCodedSize, + kVisibleRect, + natural_size, + NULL, + 0, + false); InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED); } @@ -280,20 +300,30 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioNumeratorTooLarge) { int width = kVisibleRect.size().width(); int num = ceil(static_cast(limits::kMaxDimension + 1) / width); gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), num, 1); - VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN, + VideoDecoderConfig config(kCodecVP8, + VP8PROFILE_ANY, kVideoFormat, - kCodedSize, kVisibleRect, natural_size, - NULL, 0, false); + kCodedSize, + kVisibleRect, + natural_size, + NULL, + 0, + false); InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED); } TEST_F(FFmpegVideoDecoderTest, Initialize_AspectRatioDenominatorTooLarge) { int den = kVisibleRect.size().width() + 1; gfx::Size natural_size = GetNaturalSize(kVisibleRect.size(), 1, den); - VideoDecoderConfig config(kCodecVP8, VP8PROFILE_MAIN, + VideoDecoderConfig config(kCodecVP8, + VP8PROFILE_ANY, kVideoFormat, - kCodedSize, kVisibleRect, natural_size, - NULL, 0, false); + kCodedSize, + kVisibleRect, + natural_size, + NULL, + 0, + false); InitializeWithConfigAndStatus(config, DECODER_ERROR_NOT_SUPPORTED); } diff --git a/media/filters/frame_processor.cc b/media/filters/frame_processor.cc index 6b6c8c3451926..6d98e8b13abc8 100644 --- a/media/filters/frame_processor.cc +++ b/media/filters/frame_processor.cc @@ -23,10 +23,10 @@ class MseTrackBuffer { ~MseTrackBuffer(); // Get/set |last_decode_timestamp_|. - base::TimeDelta last_decode_timestamp() const { + DecodeTimestamp last_decode_timestamp() const { return last_decode_timestamp_; } - void set_last_decode_timestamp(base::TimeDelta timestamp) { + void set_last_decode_timestamp(DecodeTimestamp timestamp) { last_decode_timestamp_ = timestamp; } @@ -78,7 +78,7 @@ class MseTrackBuffer { private: // The decode timestamp of the last coded frame appended in the current coded // frame group. Initially kNoTimestamp(), meaning "unset". - base::TimeDelta last_decode_timestamp_; + DecodeTimestamp last_decode_timestamp_; // The coded frame duration of the last coded frame appended in the current // coded frame group. Initially kNoTimestamp(), meaning "unset". @@ -108,7 +108,7 @@ class MseTrackBuffer { }; MseTrackBuffer::MseTrackBuffer(ChunkDemuxerStream* stream) - : last_decode_timestamp_(kNoTimestamp()), + : last_decode_timestamp_(kNoDecodeTimestamp()), last_frame_duration_(kNoTimestamp()), highest_presentation_timestamp_(kNoTimestamp()), needs_random_access_point_(true), @@ -123,7 +123,7 @@ MseTrackBuffer::~MseTrackBuffer() { void MseTrackBuffer::Reset() { DVLOG(2) << __FUNCTION__ << "()"; - last_decode_timestamp_ = kNoTimestamp(); + last_decode_timestamp_ = kNoDecodeTimestamp(); last_frame_duration_ = kNoTimestamp(); highest_presentation_timestamp_ = kNoTimestamp(); needs_random_access_point_ = true; @@ -302,7 +302,7 @@ MseTrackBuffer* FrameProcessor::FindTrack(StreamParser::TrackId id) { } void FrameProcessor::NotifyNewMediaSegmentStarting( - base::TimeDelta segment_timestamp) { + DecodeTimestamp segment_timestamp) { DVLOG(2) << __FUNCTION__ << "(" << segment_timestamp.InSecondsF() << ")"; for (TrackBufferMap::iterator itr = track_buffers_.begin(); @@ -390,7 +390,8 @@ bool FrameProcessor::HandlePartialAppendWindowTrimming( // Adjust the timestamp of this buffer forward to |append_window_start| and // decrease the duration to compensate. buffer->set_timestamp(append_window_start); - buffer->SetDecodeTimestamp(append_window_start); + buffer->SetDecodeTimestamp( + DecodeTimestamp::FromPresentationTime(append_window_start)); buffer->set_duration(frame_end_timestamp - append_window_start); processed_buffer = true; } @@ -434,9 +435,9 @@ bool FrameProcessor::ProcessFrame( // representation of the coded frame's decode timestamp in seconds. // 3. Let frame duration be a double precision floating point representation // of the coded frame's duration in seconds. - // We use base::TimeDelta instead of double. + // We use base::TimeDelta and DecodeTimestamp instead of double. base::TimeDelta presentation_timestamp = frame->timestamp(); - base::TimeDelta decode_timestamp = frame->GetDecodeTimestamp(); + DecodeTimestamp decode_timestamp = frame->GetDecodeTimestamp(); base::TimeDelta frame_duration = frame->duration(); DVLOG(3) << __FUNCTION__ << ": Processing frame " @@ -452,11 +453,11 @@ bool FrameProcessor::ProcessFrame( DVLOG(2) << __FUNCTION__ << ": Unknown frame PTS"; return false; } - if (decode_timestamp == kNoTimestamp()) { + if (decode_timestamp == kNoDecodeTimestamp()) { DVLOG(2) << __FUNCTION__ << ": Unknown frame DTS"; return false; } - if (decode_timestamp > presentation_timestamp) { + if (decode_timestamp.ToPresentationTime() > presentation_timestamp) { // TODO(wolenetz): Determine whether DTS>PTS should really be allowed. See // http://crbug.com/354518. DVLOG(2) << __FUNCTION__ << ": WARNING: Frame DTS(" @@ -544,9 +545,9 @@ bool FrameProcessor::ProcessFrame( // If last decode timestamp for track buffer is set and the difference // between decode timestamp and last decode timestamp is greater than 2 // times last frame duration: - base::TimeDelta last_decode_timestamp = + DecodeTimestamp last_decode_timestamp = track_buffer->last_decode_timestamp(); - if (last_decode_timestamp != kNoTimestamp()) { + if (last_decode_timestamp != kNoDecodeTimestamp()) { base::TimeDelta dts_delta = decode_timestamp - last_decode_timestamp; if (dts_delta < base::TimeDelta() || dts_delta > 2 * track_buffer->last_frame_duration()) { @@ -626,7 +627,7 @@ bool FrameProcessor::ProcessFrame( // presentation start time, then run the end of stream algorithm with the // error parameter set to "decode", and abort these steps. DCHECK(presentation_timestamp >= base::TimeDelta()); - if (decode_timestamp < base::TimeDelta()) { + if (decode_timestamp < DecodeTimestamp()) { // B-frames may still result in negative DTS here after being shifted by // |timestamp_offset_|. DVLOG(2) << __FUNCTION__ @@ -663,6 +664,9 @@ bool FrameProcessor::ProcessFrame( return false; *new_media_segment = false; + + // TODO(acolwell/wolenetz): This should be changed to a presentation + // timestamp. See http://crbug.com/402502 NotifyNewMediaSegmentStarting(decode_timestamp); } diff --git a/media/filters/frame_processor.h b/media/filters/frame_processor.h index 0cd24f1b06cc5..0afcf0ae17fc4 100644 --- a/media/filters/frame_processor.h +++ b/media/filters/frame_processor.h @@ -98,8 +98,8 @@ class MEDIA_EXPORT FrameProcessor { MseTrackBuffer* FindTrack(StreamParser::TrackId id); // Signals all track buffers' streams that a new media segment is starting - // with timestamp |segment_timestamp|. - void NotifyNewMediaSegmentStarting(base::TimeDelta segment_timestamp); + // with decode timestamp |segment_timestamp|. + void NotifyNewMediaSegmentStarting(DecodeTimestamp segment_timestamp); // Helper that signals each track buffer to append any processed, but not yet // appended, frames to its stream. Returns true on success, or false if one or diff --git a/media/filters/frame_processor_unittest.cc b/media/filters/frame_processor_unittest.cc index d6cabf0446b72..4daf5d0940354 100644 --- a/media/filters/frame_processor_unittest.cc +++ b/media/filters/frame_processor_unittest.cc @@ -124,7 +124,6 @@ class FrameProcessorTest : public testing::TestWithParam { base::TimeDelta timestamp = base::TimeDelta::FromSecondsD( time_in_ms / base::Time::kMillisecondsPerSecond); buffer->set_timestamp(timestamp); - buffer->SetDecodeTimestamp(timestamp); buffer->set_duration(frame_duration_); buffers.push_back(buffer); } diff --git a/media/filters/gpu_video_decoder.cc b/media/filters/gpu_video_decoder.cc index 16a33eae54c6f..3954e09406fc8 100644 --- a/media/filters/gpu_video_decoder.cc +++ b/media/filters/gpu_video_decoder.cc @@ -422,8 +422,22 @@ void GpuVideoDecoder::PictureReady(const media::Picture& picture) { } const PictureBuffer& pb = it->second; + // Validate picture rectangle from GPU. This is for sanity/security check + // even the rectangle is not used in this class. + if (picture.visible_rect().IsEmpty() || + !gfx::Rect(pb.size()).Contains(picture.visible_rect())) { + NOTREACHED() << "Invalid picture size from VDA: " + << picture.visible_rect().ToString() << " should fit in " + << pb.size().ToString(); + NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); + return; + } + // Update frame's timestamp. base::TimeDelta timestamp; + // Some of the VDAs don't support and thus don't provide us with visible + // size in picture.size, passing coded size instead, so always drop it and + // use config information instead. gfx::Rect visible_rect; gfx::Size natural_size; GetBufferData(picture.bitstream_buffer_id(), ×tamp, &visible_rect, diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc index 54e7f58aea6ea..a6352868faa6c 100644 --- a/media/filters/pipeline_integration_test_base.cc +++ b/media/filters/pipeline_integration_test_base.cc @@ -307,7 +307,11 @@ PipelineIntegrationTestBase::CreateFilterCollection( void PipelineIntegrationTestBase::SetDecryptor( Decryptor* decryptor, const DecryptorReadyCB& decryptor_ready_cb) { - decryptor_ready_cb.Run(decryptor); + decryptor_ready_cb.Run( + decryptor, + base::Bind(&PipelineIntegrationTestBase::DecryptorAttached, + base::Unretained(this))); + EXPECT_CALL(*this, DecryptorAttached(true)); } void PipelineIntegrationTestBase::OnVideoRendererPaint( diff --git a/media/filters/pipeline_integration_test_base.h b/media/filters/pipeline_integration_test_base.h index 37f744c0e465d..f6b8d244d8b92 100644 --- a/media/filters/pipeline_integration_test_base.h +++ b/media/filters/pipeline_integration_test_base.h @@ -135,6 +135,7 @@ class PipelineIntegrationTestBase { MOCK_METHOD1(OnMetadata, void(PipelineMetadata)); MOCK_METHOD1(OnBufferingStateChanged, void(BufferingState)); + MOCK_METHOD1(DecryptorAttached, void(bool)); }; } // namespace media diff --git a/media/filters/skcanvas_video_renderer.cc b/media/filters/skcanvas_video_renderer.cc index 062e317b71da0..003c1951a9b40 100644 --- a/media/filters/skcanvas_video_renderer.cc +++ b/media/filters/skcanvas_video_renderer.cc @@ -9,6 +9,7 @@ #include "media/base/yuv_convert.h" #include "third_party/libyuv/include/libyuv.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "ui/gfx/skbitmap_operations.h" // Skia internal format depends on a platform. On Android it is ABGR, on others // it is ARGB. @@ -188,7 +189,9 @@ SkCanvasVideoRenderer::~SkCanvasVideoRenderer() {} void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame, SkCanvas* canvas, const gfx::RectF& dest_rect, - uint8 alpha) { + uint8 alpha, + SkXfermode::Mode mode, + VideoRotation video_rotation) { if (alpha == 0) { return; } @@ -210,16 +213,42 @@ void SkCanvasVideoRenderer::Paint(media::VideoFrame* video_frame, if (last_frame_.isNull() || video_frame->timestamp() != last_frame_timestamp_) { ConvertVideoFrameToBitmap(video_frame, &last_frame_); + + switch (video_rotation) { + case VIDEO_ROTATION_0: + break; + case VIDEO_ROTATION_90: + last_frame_ = SkBitmapOperations::Rotate( + last_frame_, SkBitmapOperations::ROTATION_90_CW); + break; + case VIDEO_ROTATION_180: + last_frame_ = SkBitmapOperations::Rotate( + last_frame_, SkBitmapOperations::ROTATION_180_CW); + break; + case VIDEO_ROTATION_270: + last_frame_ = SkBitmapOperations::Rotate( + last_frame_, SkBitmapOperations::ROTATION_270_CW); + break; + } + last_frame_timestamp_ = video_frame->timestamp(); } - // Use SRC mode so we completely overwrite the buffer (in case we have alpha) - // this means we don't need the extra cost of clearing the buffer first. - paint.setXfermode(SkXfermode::Create(SkXfermode::kSrc_Mode)); + paint.setXfermodeMode(mode); // Paint using |last_frame_|. paint.setFilterLevel(SkPaint::kLow_FilterLevel); canvas->drawBitmapRect(last_frame_, NULL, dest, &paint); } +void SkCanvasVideoRenderer::Copy(media::VideoFrame* video_frame, + SkCanvas* canvas) { + Paint(video_frame, + canvas, + video_frame->visible_rect(), + 0xff, + SkXfermode::kSrc_Mode, + media::VIDEO_ROTATION_0); +} + } // namespace media diff --git a/media/filters/skcanvas_video_renderer.h b/media/filters/skcanvas_video_renderer.h index 9e3622d79c531..3d1f4da56f76e 100644 --- a/media/filters/skcanvas_video_renderer.h +++ b/media/filters/skcanvas_video_renderer.h @@ -7,7 +7,9 @@ #include "base/time/time.h" #include "media/base/media_export.h" +#include "media/base/video_rotation.h" #include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkXfermode.h" #include "ui/gfx/rect.h" class SkCanvas; @@ -30,7 +32,12 @@ class MEDIA_EXPORT SkCanvasVideoRenderer { void Paint(media::VideoFrame* video_frame, SkCanvas* canvas, const gfx::RectF& dest_rect, - uint8 alpha); + uint8 alpha, + SkXfermode::Mode mode, + VideoRotation video_rotation); + + // Copy |video_frame| on |canvas|. + void Copy(media::VideoFrame* video_frame, SkCanvas* canvas); private: // An RGB bitmap and corresponding timestamp of the previously converted diff --git a/media/filters/skcanvas_video_renderer_unittest.cc b/media/filters/skcanvas_video_renderer_unittest.cc index 5db3a34d6cfa2..358ce0ab1e031 100644 --- a/media/filters/skcanvas_video_renderer_unittest.cc +++ b/media/filters/skcanvas_video_renderer_unittest.cc @@ -53,6 +53,13 @@ class SkCanvasVideoRendererTest : public testing::Test { // Paints the |video_frame| to the |canvas| using |renderer_|, setting the // color of |video_frame| to |color| first. void Paint(VideoFrame* video_frame, SkCanvas* canvas, Color color); + void PaintRotated(VideoFrame* video_frame, + SkCanvas* canvas, + Color color, + SkXfermode::Mode mode, + VideoRotation video_rotation); + + void Copy(VideoFrame* video_frame, SkCanvas* canvas); // Getters for various frame sizes. VideoFrame* natural_frame() { return natural_frame_.get(); } @@ -185,12 +192,26 @@ SkCanvasVideoRendererTest::SkCanvasVideoRendererTest() SkCanvasVideoRendererTest::~SkCanvasVideoRendererTest() {} void SkCanvasVideoRendererTest::PaintWithoutFrame(SkCanvas* canvas) { - renderer_.Paint(NULL, canvas, kNaturalRect, 0xFF); + renderer_.Paint(NULL, + canvas, + kNaturalRect, + 0xFF, + SkXfermode::kSrcOver_Mode, + VIDEO_ROTATION_0); } void SkCanvasVideoRendererTest::Paint(VideoFrame* video_frame, SkCanvas* canvas, Color color) { + PaintRotated( + video_frame, canvas, color, SkXfermode::kSrcOver_Mode, VIDEO_ROTATION_0); +} + +void SkCanvasVideoRendererTest::PaintRotated(VideoFrame* video_frame, + SkCanvas* canvas, + Color color, + SkXfermode::Mode mode, + VideoRotation video_rotation) { switch (color) { case kNone: break; @@ -204,7 +225,13 @@ void SkCanvasVideoRendererTest::Paint(VideoFrame* video_frame, media::FillYUV(video_frame, 29, 255, 107); break; } - renderer_.Paint(video_frame, canvas, kNaturalRect, 0xFF); + renderer_.Paint( + video_frame, canvas, kNaturalRect, 0xFF, mode, video_rotation); +} + +void SkCanvasVideoRendererTest::Copy(VideoFrame* video_frame, + SkCanvas* canvas) { + renderer_.Copy(video_frame, canvas); } TEST_F(SkCanvasVideoRendererTest, NoFrame) { @@ -215,11 +242,31 @@ TEST_F(SkCanvasVideoRendererTest, NoFrame) { } TEST_F(SkCanvasVideoRendererTest, TransparentFrame) { - // Test that we don't blend with existing canvas contents. FillCanvas(target_canvas(), SK_ColorRED); - Paint(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)), - target_canvas(), - kNone); + PaintRotated(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)), + target_canvas(), + kNone, + SkXfermode::kSrcOver_Mode, + VIDEO_ROTATION_0); + EXPECT_EQ(static_cast(SK_ColorRED), GetColor(target_canvas())); +} + +TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) { + FillCanvas(target_canvas(), SK_ColorRED); + // SRC mode completely overwrites the buffer. + PaintRotated(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)), + target_canvas(), + kNone, + SkXfermode::kSrc_Mode, + VIDEO_ROTATION_0); + EXPECT_EQ(static_cast(SK_ColorTRANSPARENT), + GetColor(target_canvas())); +} + +TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) { + FillCanvas(target_canvas(), SK_ColorRED); + Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)), + target_canvas()); EXPECT_EQ(static_cast(SK_ColorTRANSPARENT), GetColor(target_canvas())); } @@ -311,4 +358,49 @@ TEST_F(SkCanvasVideoRendererTest, CroppedFrame_NoScaling) { offset_y + crop_rect.height() - 1)); } +TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) { + SkCanvas canvas(AllocBitmap(kWidth, kHeight)); + const gfx::Rect crop_rect = cropped_frame()->visible_rect(); + PaintRotated(cropped_frame(), + &canvas, + kNone, + SkXfermode::kSrcOver_Mode, + VIDEO_ROTATION_90); + // Check the corners. + EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0)); + EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0)); + EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1)); + EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, kHeight - 1)); +} + +TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) { + SkCanvas canvas(AllocBitmap(kWidth, kHeight)); + const gfx::Rect crop_rect = cropped_frame()->visible_rect(); + PaintRotated(cropped_frame(), + &canvas, + kNone, + SkXfermode::kSrcOver_Mode, + VIDEO_ROTATION_180); + // Check the corners. + EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0)); + EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0)); + EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1)); + EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, kHeight - 1)); +} + +TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) { + SkCanvas canvas(AllocBitmap(kWidth, kHeight)); + const gfx::Rect crop_rect = cropped_frame()->visible_rect(); + PaintRotated(cropped_frame(), + &canvas, + kNone, + SkXfermode::kSrcOver_Mode, + VIDEO_ROTATION_270); + // Check the corners. + EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0)); + EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0)); + EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1)); + EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, 0, kHeight - 1)); +} + } // namespace media diff --git a/media/filters/source_buffer_stream.cc b/media/filters/source_buffer_stream.cc index 8bc65854c0d23..a0381d2ccf810 100644 --- a/media/filters/source_buffer_stream.cc +++ b/media/filters/source_buffer_stream.cc @@ -57,7 +57,7 @@ class SourceBufferRange { // segment to which these buffers belong. SourceBufferRange(SourceBufferStream::Type type, const BufferQueue& new_buffers, - base::TimeDelta media_segment_start_time, + DecodeTimestamp media_segment_start_time, const InterbufferDistanceCB& interbuffer_distance_cb); // Appends |buffers| to the end of the range and updates |keyframe_map_| as @@ -77,15 +77,15 @@ class SourceBufferRange { // Updates |next_buffer_index_| to point to the Buffer containing |timestamp|. // Assumes |timestamp| is valid and in this range. - void Seek(base::TimeDelta timestamp); + void Seek(DecodeTimestamp timestamp); // Updates |next_buffer_index_| to point to next keyframe after or equal to // |timestamp|. - void SeekAheadTo(base::TimeDelta timestamp); + void SeekAheadTo(DecodeTimestamp timestamp); // Updates |next_buffer_index_| to point to next keyframe strictly after // |timestamp|. - void SeekAheadPast(base::TimeDelta timestamp); + void SeekAheadPast(DecodeTimestamp timestamp); // Seeks to the beginning of the range. void SeekToStart(); @@ -96,7 +96,7 @@ class SourceBufferRange { // The buffers in the new SourceBufferRange are moved out of this range. If // there is no keyframe after |timestamp|, SplitRange() returns null and this // range is unmodified. - SourceBufferRange* SplitRange(base::TimeDelta timestamp, bool is_exclusive); + SourceBufferRange* SplitRange(DecodeTimestamp timestamp, bool is_exclusive); // Deletes the buffers from this range starting at |timestamp|, exclusive if // |is_exclusive| is true, inclusive otherwise. @@ -107,7 +107,7 @@ class SourceBufferRange { // starting at the buffer that had been at |next_buffer_index_|. // Returns true if everything in the range was deleted. Otherwise // returns false. - bool TruncateAt(base::TimeDelta timestamp, + bool TruncateAt(DecodeTimestamp timestamp, BufferQueue* deleted_buffers, bool is_exclusive); // Deletes all buffers in range. void DeleteAll(BufferQueue* deleted_buffers); @@ -124,8 +124,8 @@ class SourceBufferRange { // [|start_timestamp|, |end_removal_timestamp|) is removed. // Will not update |end_removal_timestamp| if the returned size is 0. int GetRemovalGOP( - base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, - int bytes_to_free, base::TimeDelta* end_removal_timestamp); + DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp, + int bytes_to_free, DecodeTimestamp* end_removal_timestamp); // Indicates whether the GOP at the beginning or end of the range contains the // next buffer position. @@ -154,39 +154,39 @@ class SourceBufferRange { // Returns the timestamp of the next buffer that will be returned from // GetNextBuffer(), or kNoTimestamp() if the timestamp is unknown. - base::TimeDelta GetNextTimestamp() const; + DecodeTimestamp GetNextTimestamp() const; // Returns the start timestamp of the range. - base::TimeDelta GetStartTimestamp() const; + DecodeTimestamp GetStartTimestamp() const; // Returns the timestamp of the last buffer in the range. - base::TimeDelta GetEndTimestamp() const; + DecodeTimestamp GetEndTimestamp() const; // Returns the timestamp for the end of the buffered region in this range. // This is an approximation if the duration for the last buffer in the range // is unset. - base::TimeDelta GetBufferedEndTimestamp() const; + DecodeTimestamp GetBufferedEndTimestamp() const; // Gets the timestamp for the keyframe that is after |timestamp|. If // there isn't a keyframe in the range after |timestamp| then kNoTimestamp() // is returned. If |timestamp| is in the "gap" between the value returned by // GetStartTimestamp() and the timestamp on the first buffer in |buffers_|, // then |timestamp| is returned. - base::TimeDelta NextKeyframeTimestamp(base::TimeDelta timestamp); + DecodeTimestamp NextKeyframeTimestamp(DecodeTimestamp timestamp); // Gets the timestamp for the closest keyframe that is <= |timestamp|. If // there isn't a keyframe before |timestamp| or |timestamp| is outside // this range, then kNoTimestamp() is returned. - base::TimeDelta KeyframeBeforeTimestamp(base::TimeDelta timestamp); + DecodeTimestamp KeyframeBeforeTimestamp(DecodeTimestamp timestamp); // Returns whether a buffer with a starting timestamp of |timestamp| would // belong in this range. This includes a buffer that would be appended to // the end of the range. - bool BelongsToRange(base::TimeDelta timestamp) const; + bool BelongsToRange(DecodeTimestamp timestamp) const; // Returns true if the range has enough data to seek to the specified // |timestamp|, false otherwise. - bool CanSeekTo(base::TimeDelta timestamp) const; + bool CanSeekTo(DecodeTimestamp timestamp) const; // Returns true if this range's buffered timespan completely overlaps the // buffered timespan of |range|. @@ -198,38 +198,38 @@ class SourceBufferRange { // Returns true if |timestamp| is the timestamp of the next buffer in // sequence after |buffers_.back()|, false otherwise. - bool IsNextInSequence(base::TimeDelta timestamp, bool is_keyframe) const; + bool IsNextInSequence(DecodeTimestamp timestamp, bool is_keyframe) const; // Adds all buffers which overlap [start, end) to the end of |buffers|. If // no buffers exist in the range returns false, true otherwise. - bool GetBuffersInRange(base::TimeDelta start, base::TimeDelta end, + bool GetBuffersInRange(DecodeTimestamp start, DecodeTimestamp end, BufferQueue* buffers); int size_in_bytes() const { return size_in_bytes_; } private: - typedef std::map KeyframeMap; + typedef std::map KeyframeMap; // Seeks the range to the next keyframe after |timestamp|. If // |skip_given_timestamp| is true, the seek will go to a keyframe with a // timestamp strictly greater than |timestamp|. - void SeekAhead(base::TimeDelta timestamp, bool skip_given_timestamp); + void SeekAhead(DecodeTimestamp timestamp, bool skip_given_timestamp); // Returns an iterator in |buffers_| pointing to the buffer at |timestamp|. // If |skip_given_timestamp| is true, this returns the first buffer with // timestamp greater than |timestamp|. BufferQueue::iterator GetBufferItrAt( - base::TimeDelta timestamp, bool skip_given_timestamp); + DecodeTimestamp timestamp, bool skip_given_timestamp); // Returns an iterator in |keyframe_map_| pointing to the next keyframe after // |timestamp|. If |skip_given_timestamp| is true, this returns the first // keyframe with a timestamp strictly greater than |timestamp|. KeyframeMap::iterator GetFirstKeyframeAt( - base::TimeDelta timestamp, bool skip_given_timestamp); + DecodeTimestamp timestamp, bool skip_given_timestamp); // Returns an iterator in |keyframe_map_| pointing to the first keyframe // before or at |timestamp|. - KeyframeMap::iterator GetFirstKeyframeBefore(base::TimeDelta timestamp); + KeyframeMap::iterator GetFirstKeyframeBefore(DecodeTimestamp timestamp); // Helper method to delete buffers in |buffers_| starting at // |starting_point|, an iterator in |buffers_|. @@ -276,7 +276,7 @@ class SourceBufferRange { // garbage collection or after an end overlap that results in a split range // (we don't have a way of knowing the media segment timestamp for the new // range). - base::TimeDelta media_segment_start_time_; + DecodeTimestamp media_segment_start_time_; // Called to get the largest interbuffer distance seen so far in the stream. InterbufferDistanceCB interbuffer_distance_cb_; @@ -287,16 +287,14 @@ class SourceBufferRange { DISALLOW_COPY_AND_ASSIGN(SourceBufferRange); }; -} // namespace media - // Helper method that returns true if |ranges| is sorted in increasing order, // false otherwise. static bool IsRangeListSorted( const std::list& ranges) { - base::TimeDelta prev = media::kNoTimestamp(); - for (std::list::const_iterator itr = + DecodeTimestamp prev = kNoDecodeTimestamp(); + for (std::list::const_iterator itr = ranges.begin(); itr != ranges.end(); ++itr) { - if (prev != media::kNoTimestamp() && prev >= (*itr)->GetStartTimestamp()) + if (prev != kNoDecodeTimestamp() && prev >= (*itr)->GetStartTimestamp()) return false; prev = (*itr)->GetEndTimestamp(); } @@ -305,13 +303,13 @@ static bool IsRangeListSorted( // Comparison operators for std::upper_bound() and std::lower_bound(). static bool CompareTimeDeltaToStreamParserBuffer( - const base::TimeDelta& decode_timestamp, - const scoped_refptr& buffer) { + const DecodeTimestamp& decode_timestamp, + const scoped_refptr& buffer) { return decode_timestamp < buffer->GetDecodeTimestamp(); } static bool CompareStreamParserBufferToTimeDelta( - const scoped_refptr& buffer, - const base::TimeDelta& decode_timestamp) { + const scoped_refptr& buffer, + const DecodeTimestamp& decode_timestamp) { return buffer->GetDecodeTimestamp() < decode_timestamp; } @@ -335,8 +333,6 @@ static base::TimeDelta kSeekToStartFudgeRoom() { return base::TimeDelta::FromMilliseconds(1000); } -namespace media { - SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config, const LogCB& log_cb, bool splice_frames_enabled) @@ -347,12 +343,12 @@ SourceBufferStream::SourceBufferStream(const AudioDecoderConfig& audio_config, end_of_stream_(false), seek_buffer_timestamp_(kNoTimestamp()), selected_range_(NULL), - media_segment_start_time_(kNoTimestamp()), + media_segment_start_time_(kNoDecodeTimestamp()), range_for_next_append_(ranges_.end()), new_media_segment_(false), - last_appended_buffer_timestamp_(kNoTimestamp()), + last_appended_buffer_timestamp_(kNoDecodeTimestamp()), last_appended_buffer_is_keyframe_(false), - last_output_buffer_timestamp_(kNoTimestamp()), + last_output_buffer_timestamp_(kNoDecodeTimestamp()), max_interbuffer_distance_(kNoTimestamp()), memory_limit_(kSourceBufferAudioMemoryLimit), config_change_pending_(false), @@ -373,12 +369,12 @@ SourceBufferStream::SourceBufferStream(const VideoDecoderConfig& video_config, end_of_stream_(false), seek_buffer_timestamp_(kNoTimestamp()), selected_range_(NULL), - media_segment_start_time_(kNoTimestamp()), + media_segment_start_time_(kNoDecodeTimestamp()), range_for_next_append_(ranges_.end()), new_media_segment_(false), - last_appended_buffer_timestamp_(kNoTimestamp()), + last_appended_buffer_timestamp_(kNoDecodeTimestamp()), last_appended_buffer_is_keyframe_(false), - last_output_buffer_timestamp_(kNoTimestamp()), + last_output_buffer_timestamp_(kNoDecodeTimestamp()), max_interbuffer_distance_(kNoTimestamp()), memory_limit_(kSourceBufferVideoMemoryLimit), config_change_pending_(false), @@ -400,12 +396,12 @@ SourceBufferStream::SourceBufferStream(const TextTrackConfig& text_config, end_of_stream_(false), seek_buffer_timestamp_(kNoTimestamp()), selected_range_(NULL), - media_segment_start_time_(kNoTimestamp()), + media_segment_start_time_(kNoDecodeTimestamp()), range_for_next_append_(ranges_.end()), new_media_segment_(false), - last_appended_buffer_timestamp_(kNoTimestamp()), + last_appended_buffer_timestamp_(kNoDecodeTimestamp()), last_appended_buffer_is_keyframe_(false), - last_output_buffer_timestamp_(kNoTimestamp()), + last_output_buffer_timestamp_(kNoDecodeTimestamp()), max_interbuffer_distance_(kNoTimestamp()), memory_limit_(kSourceBufferAudioMemoryLimit), config_change_pending_(false), @@ -421,7 +417,7 @@ SourceBufferStream::~SourceBufferStream() { } void SourceBufferStream::OnNewMediaSegment( - base::TimeDelta media_segment_start_time) { + DecodeTimestamp media_segment_start_time) { DCHECK(!end_of_stream_); media_segment_start_time_ = media_segment_start_time; new_media_segment_ = true; @@ -434,7 +430,7 @@ void SourceBufferStream::OnNewMediaSegment( if (range_for_next_append_ == ranges_.end() || !AreAdjacentInSequence(last_appended_buffer_timestamp_, media_segment_start_time)) { - last_appended_buffer_timestamp_ = kNoTimestamp(); + last_appended_buffer_timestamp_ = kNoDecodeTimestamp(); last_appended_buffer_is_keyframe_ = false; } else if (last_range != ranges_.end()) { DCHECK(last_range == range_for_next_append_); @@ -447,7 +443,7 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { "buffers to append", buffers.size()); DCHECK(!buffers.empty()); - DCHECK(media_segment_start_time_ != kNoTimestamp()); + DCHECK(media_segment_start_time_ != kNoDecodeTimestamp()); DCHECK(media_segment_start_time_ <= buffers.front()->GetDecodeTimestamp()); DCHECK(!end_of_stream_); @@ -461,8 +457,8 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { if (!IsMonotonicallyIncreasing(buffers)) return false; - if (media_segment_start_time_ < base::TimeDelta() || - buffers.front()->GetDecodeTimestamp() < base::TimeDelta()) { + if (media_segment_start_time_ < DecodeTimestamp() || + buffers.front()->GetDecodeTimestamp() < DecodeTimestamp()) { MEDIA_LOG(log_cb_) << "Cannot append a media segment with negative timestamps."; return false; @@ -480,7 +476,7 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { SetConfigIds(buffers); // Save a snapshot of stream state before range modifications are made. - base::TimeDelta next_buffer_timestamp = GetNextBufferTimestamp(); + DecodeTimestamp next_buffer_timestamp = GetNextBufferTimestamp(); BufferQueue deleted_buffers; PrepareRangesForNextAppend(buffers, &deleted_buffers); @@ -492,7 +488,7 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { last_appended_buffer_timestamp_ = buffers.back()->GetDecodeTimestamp(); last_appended_buffer_is_keyframe_ = buffers.back()->IsKeyframe(); } else { - base::TimeDelta new_range_start_time = std::min( + DecodeTimestamp new_range_start_time = std::min( media_segment_start_time_, buffers.front()->GetDecodeTimestamp()); const BufferQueue* buffers_for_new_range = &buffers; BufferQueue trimmed_buffers; @@ -549,7 +545,7 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { } if (!deleted_buffers.empty()) { - base::TimeDelta start_of_deleted = + DecodeTimestamp start_of_deleted = deleted_buffers.front()->GetDecodeTimestamp(); DCHECK(track_buffer_.empty() || @@ -565,9 +561,9 @@ bool SourceBufferStream::Append(const BufferQueue& buffers) { // Prune any extra buffers in |track_buffer_| if new keyframes // are appended to the range covered by |track_buffer_|. if (!track_buffer_.empty()) { - base::TimeDelta keyframe_timestamp = + DecodeTimestamp keyframe_timestamp = FindKeyframeAfterTimestamp(track_buffer_.front()->GetDecodeTimestamp()); - if (keyframe_timestamp != kNoTimestamp()) + if (keyframe_timestamp != kNoDecodeTimestamp()) PruneTrackBuffer(keyframe_timestamp); } @@ -590,29 +586,32 @@ void SourceBufferStream::Remove(base::TimeDelta start, base::TimeDelta end, << " end " << end.InSecondsF(); DCHECK(duration != kNoTimestamp()); - base::TimeDelta remove_end_timestamp = duration; - base::TimeDelta keyframe_timestamp = FindKeyframeAfterTimestamp(end); - if (keyframe_timestamp != kNoTimestamp()) { + DecodeTimestamp start_dts = DecodeTimestamp::FromPresentationTime(start); + DecodeTimestamp end_dts = DecodeTimestamp::FromPresentationTime(end); + DecodeTimestamp remove_end_timestamp = + DecodeTimestamp::FromPresentationTime(duration); + DecodeTimestamp keyframe_timestamp = FindKeyframeAfterTimestamp(end_dts); + if (keyframe_timestamp != kNoDecodeTimestamp()) { remove_end_timestamp = keyframe_timestamp; - } else if (end < remove_end_timestamp) { - remove_end_timestamp = end; + } else if (end_dts < remove_end_timestamp) { + remove_end_timestamp = end_dts; } BufferQueue deleted_buffers; - RemoveInternal(start, remove_end_timestamp, false, &deleted_buffers); + RemoveInternal(start_dts, remove_end_timestamp, false, &deleted_buffers); if (!deleted_buffers.empty()) SetSelectedRangeIfNeeded(deleted_buffers.front()->GetDecodeTimestamp()); } void SourceBufferStream::RemoveInternal( - base::TimeDelta start, base::TimeDelta end, bool is_exclusive, + DecodeTimestamp start, DecodeTimestamp end, bool is_exclusive, BufferQueue* deleted_buffers) { DVLOG(1) << __FUNCTION__ << "(" << start.InSecondsF() << ", " << end.InSecondsF() << ", " << is_exclusive << ")"; - DCHECK(start >= base::TimeDelta()); + DCHECK(start >= DecodeTimestamp()); DCHECK(start < end) << "start " << start.InSecondsF() << " end " << end.InSecondsF(); DCHECK(deleted_buffers); @@ -665,8 +664,8 @@ void SourceBufferStream::RemoveInternal( // to the current range. if (range_for_next_append_ != ranges_.end() && *range_for_next_append_ == range && - last_appended_buffer_timestamp_ != kNoTimestamp()) { - base::TimeDelta potential_next_append_timestamp = + last_appended_buffer_timestamp_ != kNoDecodeTimestamp()) { + DecodeTimestamp potential_next_append_timestamp = last_appended_buffer_timestamp_ + base::TimeDelta::FromInternalValue(1); @@ -691,7 +690,7 @@ void SourceBufferStream::ResetSeekState() { SetSelectedRange(NULL); track_buffer_.clear(); config_change_pending_ = false; - last_output_buffer_timestamp_ = kNoTimestamp(); + last_output_buffer_timestamp_ = kNoDecodeTimestamp(); splice_buffers_index_ = 0; pending_buffer_ = NULL; pending_buffers_complete_ = false; @@ -702,7 +701,7 @@ bool SourceBufferStream::ShouldSeekToStartOfBuffered( if (ranges_.empty()) return false; base::TimeDelta beginning_of_buffered = - ranges_.front()->GetStartTimestamp(); + ranges_.front()->GetStartTimestamp().ToPresentationTime(); return (seek_timestamp <= beginning_of_buffered && beginning_of_buffered < kSeekToStartFudgeRoom()); } @@ -710,20 +709,20 @@ bool SourceBufferStream::ShouldSeekToStartOfBuffered( bool SourceBufferStream::IsMonotonicallyIncreasing( const BufferQueue& buffers) const { DCHECK(!buffers.empty()); - base::TimeDelta prev_timestamp = last_appended_buffer_timestamp_; + DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_; bool prev_is_keyframe = last_appended_buffer_is_keyframe_; for (BufferQueue::const_iterator itr = buffers.begin(); itr != buffers.end(); ++itr) { - base::TimeDelta current_timestamp = (*itr)->GetDecodeTimestamp(); + DecodeTimestamp current_timestamp = (*itr)->GetDecodeTimestamp(); bool current_is_keyframe = (*itr)->IsKeyframe(); - DCHECK(current_timestamp != kNoTimestamp()); + DCHECK(current_timestamp != kNoDecodeTimestamp()); DCHECK((*itr)->duration() >= base::TimeDelta()) << "Packet with invalid duration." << " pts " << (*itr)->timestamp().InSecondsF() << " dts " << (*itr)->GetDecodeTimestamp().InSecondsF() << " dur " << (*itr)->duration().InSecondsF(); - if (prev_timestamp != kNoTimestamp()) { + if (prev_timestamp != kNoDecodeTimestamp()) { if (current_timestamp < prev_timestamp) { MEDIA_LOG(log_cb_) << "Buffers were not monotonically increasing."; return false; @@ -746,7 +745,7 @@ bool SourceBufferStream::IsMonotonicallyIncreasing( } bool SourceBufferStream::IsNextTimestampValid( - base::TimeDelta next_timestamp, bool next_is_keyframe) const { + DecodeTimestamp next_timestamp, bool next_is_keyframe) const { return (last_appended_buffer_timestamp_ != next_timestamp) || new_media_segment_ || AllowSameTimestamp(last_appended_buffer_is_keyframe_, next_is_keyframe, @@ -766,13 +765,13 @@ bool SourceBufferStream::OnlySelectedRangeIsSeeked() const { void SourceBufferStream::UpdateMaxInterbufferDistance( const BufferQueue& buffers) { DCHECK(!buffers.empty()); - base::TimeDelta prev_timestamp = last_appended_buffer_timestamp_; + DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_; for (BufferQueue::const_iterator itr = buffers.begin(); itr != buffers.end(); ++itr) { - base::TimeDelta current_timestamp = (*itr)->GetDecodeTimestamp(); - DCHECK(current_timestamp != kNoTimestamp()); + DecodeTimestamp current_timestamp = (*itr)->GetDecodeTimestamp(); + DCHECK(current_timestamp != kNoDecodeTimestamp()); - if (prev_timestamp != kNoTimestamp()) { + if (prev_timestamp != kNoDecodeTimestamp()) { base::TimeDelta interbuffer_distance = current_timestamp - prev_timestamp; if (max_interbuffer_distance_ == kNoTimestamp()) { max_interbuffer_distance_ = interbuffer_distance; @@ -817,37 +816,41 @@ void SourceBufferStream::GarbageCollectIfNeeded() { } int SourceBufferStream::FreeBuffersAfterLastAppended(int total_bytes_to_free) { - base::TimeDelta next_buffer_timestamp = GetNextBufferTimestamp(); - if (last_appended_buffer_timestamp_ == kNoTimestamp() || - next_buffer_timestamp == kNoTimestamp() || + DecodeTimestamp next_buffer_timestamp = GetNextBufferTimestamp(); + if (last_appended_buffer_timestamp_ == kNoDecodeTimestamp() || + next_buffer_timestamp == kNoDecodeTimestamp() || last_appended_buffer_timestamp_ >= next_buffer_timestamp) { return 0; } - base::TimeDelta remove_range_start = last_appended_buffer_timestamp_; + DecodeTimestamp remove_range_start = last_appended_buffer_timestamp_; if (last_appended_buffer_is_keyframe_) remove_range_start += GetMaxInterbufferDistance(); - base::TimeDelta remove_range_start_keyframe = FindKeyframeAfterTimestamp( + DecodeTimestamp remove_range_start_keyframe = FindKeyframeAfterTimestamp( remove_range_start); - if (remove_range_start_keyframe != kNoTimestamp()) + if (remove_range_start_keyframe != kNoDecodeTimestamp()) remove_range_start = remove_range_start_keyframe; if (remove_range_start >= next_buffer_timestamp) return 0; - base::TimeDelta remove_range_end; + DecodeTimestamp remove_range_end; int bytes_freed = GetRemovalRange( remove_range_start, next_buffer_timestamp, total_bytes_to_free, &remove_range_end); - if (bytes_freed > 0) - Remove(remove_range_start, remove_range_end, next_buffer_timestamp); + if (bytes_freed > 0) { + Remove(remove_range_start.ToPresentationTime(), + remove_range_end.ToPresentationTime(), + next_buffer_timestamp.ToPresentationTime()); + } + return bytes_freed; } int SourceBufferStream::GetRemovalRange( - base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, - int total_bytes_to_free, base::TimeDelta* removal_end_timestamp) { - DCHECK(start_timestamp >= base::TimeDelta()) << start_timestamp.InSecondsF(); + DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp, + int total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) { + DCHECK(start_timestamp >= DecodeTimestamp()) << start_timestamp.InSecondsF(); DCHECK(start_timestamp < end_timestamp) << "start " << start_timestamp.InSecondsF() << ", end " << end_timestamp.InSecondsF(); @@ -907,13 +910,13 @@ int SourceBufferStream::FreeBuffers(int total_bytes_to_free, } // Check to see if we've just deleted the GOP that was last appended. - base::TimeDelta end_timestamp = buffers.back()->GetDecodeTimestamp(); + DecodeTimestamp end_timestamp = buffers.back()->GetDecodeTimestamp(); if (end_timestamp == last_appended_buffer_timestamp_) { - DCHECK(last_appended_buffer_timestamp_ != kNoTimestamp()); + DCHECK(last_appended_buffer_timestamp_ != kNoDecodeTimestamp()); DCHECK(!new_range_for_append); // Create a new range containing these buffers. new_range_for_append = new SourceBufferRange( - GetType(), buffers, kNoTimestamp(), + GetType(), buffers, kNoDecodeTimestamp(), base::Bind(&SourceBufferStream::GetMaxInterbufferDistance, base::Unretained(this))); range_for_next_append_ = ranges_.end(); @@ -957,9 +960,9 @@ void SourceBufferStream::PrepareRangesForNextAppend( bool temporarily_select_range = false; if (!track_buffer_.empty()) { - base::TimeDelta tb_timestamp = track_buffer_.back()->GetDecodeTimestamp(); - base::TimeDelta seek_timestamp = FindKeyframeAfterTimestamp(tb_timestamp); - if (seek_timestamp != kNoTimestamp() && + DecodeTimestamp tb_timestamp = track_buffer_.back()->GetDecodeTimestamp(); + DecodeTimestamp seek_timestamp = FindKeyframeAfterTimestamp(tb_timestamp); + if (seek_timestamp != kNoDecodeTimestamp() && seek_timestamp < new_buffers.front()->GetDecodeTimestamp() && range_for_next_append_ != ranges_.end() && (*range_for_next_append_)->BelongsToRange(seek_timestamp)) { @@ -984,12 +987,13 @@ void SourceBufferStream::PrepareRangesForNextAppend( if (splice_frames_enabled_) GenerateSpliceFrame(new_buffers); - base::TimeDelta prev_timestamp = last_appended_buffer_timestamp_; + DecodeTimestamp prev_timestamp = last_appended_buffer_timestamp_; bool prev_is_keyframe = last_appended_buffer_is_keyframe_; - base::TimeDelta next_timestamp = new_buffers.front()->GetDecodeTimestamp(); + DecodeTimestamp next_timestamp = new_buffers.front()->GetDecodeTimestamp(); bool next_is_keyframe = new_buffers.front()->IsKeyframe(); - if (prev_timestamp != kNoTimestamp() && prev_timestamp != next_timestamp) { + if (prev_timestamp != kNoDecodeTimestamp() && + prev_timestamp != next_timestamp) { // Clean up the old buffers between the last appended buffer and the // beginning of |new_buffers|. RemoveInternal(prev_timestamp, next_timestamp, true, deleted_buffers); @@ -1010,8 +1014,8 @@ void SourceBufferStream::PrepareRangesForNextAppend( AllowSameTimestamp(prev_is_keyframe, next_is_keyframe, GetType()); // Delete the buffers that |new_buffers| overlaps. - base::TimeDelta start = new_buffers.front()->GetDecodeTimestamp(); - base::TimeDelta end = new_buffers.back()->GetDecodeTimestamp(); + DecodeTimestamp start = new_buffers.front()->GetDecodeTimestamp(); + DecodeTimestamp end = new_buffers.back()->GetDecodeTimestamp(); base::TimeDelta duration = new_buffers.back()->duration(); if (duration != kNoTimestamp() && duration > base::TimeDelta()) { @@ -1031,15 +1035,15 @@ void SourceBufferStream::PrepareRangesForNextAppend( } bool SourceBufferStream::AreAdjacentInSequence( - base::TimeDelta first_timestamp, base::TimeDelta second_timestamp) const { + DecodeTimestamp first_timestamp, DecodeTimestamp second_timestamp) const { return first_timestamp < second_timestamp && second_timestamp <= first_timestamp + ComputeFudgeRoom(GetMaxInterbufferDistance()); } -void SourceBufferStream::PruneTrackBuffer(const base::TimeDelta timestamp) { +void SourceBufferStream::PruneTrackBuffer(const DecodeTimestamp timestamp) { // If we don't have the next timestamp, we don't have anything to delete. - if (timestamp == kNoTimestamp()) + if (timestamp == kNoDecodeTimestamp()) return; while (!track_buffer_.empty() && @@ -1089,16 +1093,18 @@ void SourceBufferStream::Seek(base::TimeDelta timestamp) { seek_buffer_timestamp_ = timestamp; seek_pending_ = true; + DecodeTimestamp seek_dts = DecodeTimestamp::FromPresentationTime(timestamp); + RangeList::iterator itr = ranges_.end(); for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { - if ((*itr)->CanSeekTo(timestamp)) + if ((*itr)->CanSeekTo(seek_dts)) break; } if (itr == ranges_.end()) return; - SeekAndSetSelectedRange(*itr, timestamp); + SeekAndSetSelectedRange(*itr, seek_dts); seek_pending_ = false; } @@ -1107,17 +1113,20 @@ bool SourceBufferStream::IsSeekPending() const { } void SourceBufferStream::OnSetDuration(base::TimeDelta duration) { + DecodeTimestamp duration_dts = + DecodeTimestamp::FromPresentationTime(duration); + RangeList::iterator itr = ranges_.end(); for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { - if ((*itr)->GetEndTimestamp() > duration) + if ((*itr)->GetEndTimestamp() > duration_dts) break; } if (itr == ranges_.end()) return; // Need to partially truncate this range. - if ((*itr)->GetStartTimestamp() < duration) { - bool delete_range = (*itr)->TruncateAt(duration, NULL, false); + if ((*itr)->GetStartTimestamp() < duration_dts) { + bool delete_range = (*itr)->TruncateAt(duration_dts, NULL, false); if ((*itr == selected_range_) && !selected_range_->HasNextBufferPosition()) SetSelectedRange(NULL); @@ -1128,10 +1137,10 @@ void SourceBufferStream::OnSetDuration(base::TimeDelta duration) { } } - // Delete all ranges that begin after |duration|. + // Delete all ranges that begin after |duration_dts|. while (itr != ranges_.end()) { // If we're about to delete the selected range, also reset the seek state. - DCHECK((*itr)->GetStartTimestamp() >= duration); + DCHECK((*itr)->GetStartTimestamp() >= duration_dts); if (*itr == selected_range_) ResetSeekState(); DeleteAndRemoveRange(&itr); @@ -1267,25 +1276,19 @@ SourceBufferStream::Status SourceBufferStream::GetNextBufferInternal( return kSuccess; } -base::TimeDelta SourceBufferStream::GetNextBufferTimestamp() { +DecodeTimestamp SourceBufferStream::GetNextBufferTimestamp() { if (!track_buffer_.empty()) return track_buffer_.front()->GetDecodeTimestamp(); if (!selected_range_) - return kNoTimestamp(); + return kNoDecodeTimestamp(); DCHECK(selected_range_->HasNextBufferPosition()); return selected_range_->GetNextTimestamp(); } -base::TimeDelta SourceBufferStream::GetEndBufferTimestamp() { - if (!selected_range_) - return kNoTimestamp(); - return selected_range_->GetEndTimestamp(); -} - SourceBufferStream::RangeList::iterator -SourceBufferStream::FindExistingRangeFor(base::TimeDelta start_timestamp) { +SourceBufferStream::FindExistingRangeFor(DecodeTimestamp start_timestamp) { for (RangeList::iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) { if ((*itr)->BelongsToRange(start_timestamp)) return itr; @@ -1295,7 +1298,7 @@ SourceBufferStream::FindExistingRangeFor(base::TimeDelta start_timestamp) { SourceBufferStream::RangeList::iterator SourceBufferStream::AddToRanges(SourceBufferRange* new_range) { - base::TimeDelta start_timestamp = new_range->GetStartTimestamp(); + DecodeTimestamp start_timestamp = new_range->GetStartTimestamp(); RangeList::iterator itr = ranges_.end(); for (itr = ranges_.begin(); itr != ranges_.end(); ++itr) { if ((*itr)->GetStartTimestamp() > start_timestamp) @@ -1317,7 +1320,7 @@ SourceBufferStream::GetSelectedRangeItr() { } void SourceBufferStream::SeekAndSetSelectedRange( - SourceBufferRange* range, base::TimeDelta seek_timestamp) { + SourceBufferRange* range, DecodeTimestamp seek_timestamp) { if (range) range->Seek(seek_timestamp); SetSelectedRange(range); @@ -1335,7 +1338,8 @@ Ranges SourceBufferStream::GetBufferedTime() const { Ranges ranges; for (RangeList::const_iterator itr = ranges_.begin(); itr != ranges_.end(); ++itr) { - ranges.Add((*itr)->GetStartTimestamp(), (*itr)->GetBufferedEndTimestamp()); + ranges.Add((*itr)->GetStartTimestamp().ToPresentationTime(), + (*itr)->GetBufferedEndTimestamp().ToPresentationTime()); } return ranges; } @@ -1344,7 +1348,7 @@ base::TimeDelta SourceBufferStream::GetBufferedDuration() const { if (ranges_.empty()) return base::TimeDelta(); - return ranges_.back()->GetBufferedEndTimestamp(); + return ranges_.back()->GetBufferedEndTimestamp().ToPresentationTime(); } void SourceBufferStream::MarkEndOfStream() { @@ -1361,8 +1365,11 @@ bool SourceBufferStream::IsEndSelected() const { if (ranges_.empty()) return true; - if (seek_pending_) - return seek_buffer_timestamp_ >= ranges_.back()->GetBufferedEndTimestamp(); + if (seek_pending_) { + base::TimeDelta last_range_end_time = + ranges_.back()->GetBufferedEndTimestamp().ToPresentationTime(); + return seek_buffer_timestamp_ >= last_range_end_time; + } return selected_range_ == ranges_.back(); } @@ -1470,7 +1477,7 @@ void SourceBufferStream::CompleteConfigChange() { } void SourceBufferStream::SetSelectedRangeIfNeeded( - const base::TimeDelta timestamp) { + const DecodeTimestamp timestamp) { DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")"; if (selected_range_) { @@ -1483,23 +1490,23 @@ void SourceBufferStream::SetSelectedRangeIfNeeded( return; } - base::TimeDelta start_timestamp = timestamp; + DecodeTimestamp start_timestamp = timestamp; // If the next buffer timestamp is not known then use a timestamp just after // the timestamp on the last buffer returned by GetNextBuffer(). - if (start_timestamp == kNoTimestamp()) { - if (last_output_buffer_timestamp_ == kNoTimestamp()) + if (start_timestamp == kNoDecodeTimestamp()) { + if (last_output_buffer_timestamp_ == kNoDecodeTimestamp()) return; start_timestamp = last_output_buffer_timestamp_ + base::TimeDelta::FromInternalValue(1); } - base::TimeDelta seek_timestamp = + DecodeTimestamp seek_timestamp = FindNewSelectedRangeSeekTimestamp(start_timestamp); // If we don't have buffered data to seek to, then return. - if (seek_timestamp == kNoTimestamp()) + if (seek_timestamp == kNoDecodeTimestamp()) return; DCHECK(track_buffer_.empty()); @@ -1507,10 +1514,10 @@ void SourceBufferStream::SetSelectedRangeIfNeeded( seek_timestamp); } -base::TimeDelta SourceBufferStream::FindNewSelectedRangeSeekTimestamp( - const base::TimeDelta start_timestamp) { - DCHECK(start_timestamp != kNoTimestamp()); - DCHECK(start_timestamp >= base::TimeDelta()); +DecodeTimestamp SourceBufferStream::FindNewSelectedRangeSeekTimestamp( + const DecodeTimestamp start_timestamp) { + DCHECK(start_timestamp != kNoDecodeTimestamp()); + DCHECK(start_timestamp >= DecodeTimestamp()); RangeList::iterator itr = ranges_.begin(); @@ -1521,31 +1528,31 @@ base::TimeDelta SourceBufferStream::FindNewSelectedRangeSeekTimestamp( } if (itr == ranges_.end()) - return kNoTimestamp(); + return kNoDecodeTimestamp(); // First check for a keyframe timestamp >= |start_timestamp| // in the current range. - base::TimeDelta keyframe_timestamp = + DecodeTimestamp keyframe_timestamp = (*itr)->NextKeyframeTimestamp(start_timestamp); - if (keyframe_timestamp != kNoTimestamp()) + if (keyframe_timestamp != kNoDecodeTimestamp()) return keyframe_timestamp; // If a keyframe was not found then look for a keyframe that is // "close enough" in the current or next range. - base::TimeDelta end_timestamp = + DecodeTimestamp end_timestamp = start_timestamp + ComputeFudgeRoom(GetMaxInterbufferDistance()); DCHECK(start_timestamp < end_timestamp); // Make sure the current range doesn't start beyond |end_timestamp|. if ((*itr)->GetStartTimestamp() >= end_timestamp) - return kNoTimestamp(); + return kNoDecodeTimestamp(); keyframe_timestamp = (*itr)->KeyframeBeforeTimestamp(end_timestamp); // Check to see if the keyframe is within the acceptable range // (|start_timestamp|, |end_timestamp|]. - if (keyframe_timestamp != kNoTimestamp() && + if (keyframe_timestamp != kNoDecodeTimestamp() && start_timestamp < keyframe_timestamp && keyframe_timestamp <= end_timestamp) { return keyframe_timestamp; @@ -1554,36 +1561,36 @@ base::TimeDelta SourceBufferStream::FindNewSelectedRangeSeekTimestamp( // If |end_timestamp| is within this range, then no other checks are // necessary. if (end_timestamp <= (*itr)->GetEndTimestamp()) - return kNoTimestamp(); + return kNoDecodeTimestamp(); // Move on to the next range. ++itr; // Return early if the next range does not contain |end_timestamp|. if (itr == ranges_.end() || (*itr)->GetStartTimestamp() >= end_timestamp) - return kNoTimestamp(); + return kNoDecodeTimestamp(); keyframe_timestamp = (*itr)->KeyframeBeforeTimestamp(end_timestamp); // Check to see if the keyframe is within the acceptable range // (|start_timestamp|, |end_timestamp|]. - if (keyframe_timestamp != kNoTimestamp() && + if (keyframe_timestamp != kNoDecodeTimestamp() && start_timestamp < keyframe_timestamp && keyframe_timestamp <= end_timestamp) { return keyframe_timestamp; } - return kNoTimestamp(); + return kNoDecodeTimestamp(); } -base::TimeDelta SourceBufferStream::FindKeyframeAfterTimestamp( - const base::TimeDelta timestamp) { - DCHECK(timestamp != kNoTimestamp()); +DecodeTimestamp SourceBufferStream::FindKeyframeAfterTimestamp( + const DecodeTimestamp timestamp) { + DCHECK(timestamp != kNoDecodeTimestamp()); RangeList::iterator itr = FindExistingRangeFor(timestamp); if (itr == ranges_.end()) - return kNoTimestamp(); + return kNoDecodeTimestamp(); // First check for a keyframe timestamp >= |timestamp| // in the current range. @@ -1624,7 +1631,7 @@ void SourceBufferStream::DeleteAndRemoveRange(RangeList::iterator* itr) { if (*itr == range_for_next_append_) { DVLOG(1) << __FUNCTION__ << " deleting range_for_next_append_."; range_for_next_append_ = ranges_.end(); - last_appended_buffer_timestamp_ = kNoTimestamp(); + last_appended_buffer_timestamp_ = kNoDecodeTimestamp(); last_appended_buffer_is_keyframe_ = false; } @@ -1641,18 +1648,20 @@ void SourceBufferStream::GenerateSpliceFrame(const BufferQueue& new_buffers) { // Find the overlapped range (if any). const base::TimeDelta splice_timestamp = new_buffers.front()->timestamp(); - RangeList::iterator range_itr = FindExistingRangeFor(splice_timestamp); + const DecodeTimestamp splice_dts = + DecodeTimestamp::FromPresentationTime(splice_timestamp); + RangeList::iterator range_itr = FindExistingRangeFor(splice_dts); if (range_itr == ranges_.end()) return; - const base::TimeDelta max_splice_end_timestamp = - splice_timestamp + base::TimeDelta::FromMilliseconds( - AudioSplicer::kCrossfadeDurationInMilliseconds); + const DecodeTimestamp max_splice_end_dts = + splice_dts + base::TimeDelta::FromMilliseconds( + AudioSplicer::kCrossfadeDurationInMilliseconds); // Find all buffers involved before the splice point. BufferQueue pre_splice_buffers; if (!(*range_itr)->GetBuffersInRange( - splice_timestamp, max_splice_end_timestamp, &pre_splice_buffers)) { + splice_dts, max_splice_end_dts, &pre_splice_buffers)) { return; } @@ -1702,7 +1711,7 @@ void SourceBufferStream::GenerateSpliceFrame(const BufferQueue& new_buffers) { SourceBufferRange::SourceBufferRange( SourceBufferStream::Type type, const BufferQueue& new_buffers, - base::TimeDelta media_segment_start_time, + DecodeTimestamp media_segment_start_time, const InterbufferDistanceCB& interbuffer_distance_cb) : type_(type), keyframe_map_index_base_(0), @@ -1718,13 +1727,13 @@ SourceBufferRange::SourceBufferRange( void SourceBufferRange::AppendBuffersToEnd(const BufferQueue& new_buffers) { DCHECK(buffers_.empty() || CanAppendBuffersToEnd(new_buffers)); - DCHECK(media_segment_start_time_ == kNoTimestamp() || + DCHECK(media_segment_start_time_ == kNoDecodeTimestamp() || media_segment_start_time_ <= new_buffers.front()->GetDecodeTimestamp()); for (BufferQueue::const_iterator itr = new_buffers.begin(); itr != new_buffers.end(); ++itr) { - DCHECK((*itr)->GetDecodeTimestamp() != kNoTimestamp()); + DCHECK((*itr)->GetDecodeTimestamp() != kNoDecodeTimestamp()); buffers_.push_back(*itr); size_in_bytes_ += (*itr)->data_size(); @@ -1736,7 +1745,7 @@ void SourceBufferRange::AppendBuffersToEnd(const BufferQueue& new_buffers) { } } -void SourceBufferRange::Seek(base::TimeDelta timestamp) { +void SourceBufferRange::Seek(DecodeTimestamp timestamp) { DCHECK(CanSeekTo(timestamp)); DCHECK(!keyframe_map_.empty()); @@ -1745,15 +1754,15 @@ void SourceBufferRange::Seek(base::TimeDelta timestamp) { DCHECK_LT(next_buffer_index_, static_cast(buffers_.size())); } -void SourceBufferRange::SeekAheadTo(base::TimeDelta timestamp) { +void SourceBufferRange::SeekAheadTo(DecodeTimestamp timestamp) { SeekAhead(timestamp, false); } -void SourceBufferRange::SeekAheadPast(base::TimeDelta timestamp) { +void SourceBufferRange::SeekAheadPast(DecodeTimestamp timestamp) { SeekAhead(timestamp, true); } -void SourceBufferRange::SeekAhead(base::TimeDelta timestamp, +void SourceBufferRange::SeekAhead(DecodeTimestamp timestamp, bool skip_given_timestamp) { DCHECK(!keyframe_map_.empty()); @@ -1776,7 +1785,7 @@ void SourceBufferRange::SeekToStart() { } SourceBufferRange* SourceBufferRange::SplitRange( - base::TimeDelta timestamp, bool is_exclusive) { + DecodeTimestamp timestamp, bool is_exclusive) { CHECK(!buffers_.empty()); // Find the first keyframe after |timestamp|. If |is_exclusive|, do not @@ -1796,7 +1805,7 @@ SourceBufferRange* SourceBufferRange::SplitRange( BufferQueue::iterator starting_point = buffers_.begin() + keyframe_index; BufferQueue removed_buffers(starting_point, buffers_.end()); - base::TimeDelta new_range_start_timestamp = kNoTimestamp(); + DecodeTimestamp new_range_start_timestamp = kNoDecodeTimestamp(); if (GetStartTimestamp() < buffers_.front()->GetDecodeTimestamp() && timestamp < removed_buffers.front()->GetDecodeTimestamp()) { // The split is in the gap between |media_segment_start_time_| and @@ -1826,7 +1835,7 @@ SourceBufferRange* SourceBufferRange::SplitRange( } BufferQueue::iterator SourceBufferRange::GetBufferItrAt( - base::TimeDelta timestamp, + DecodeTimestamp timestamp, bool skip_given_timestamp) { return skip_given_timestamp ? std::upper_bound(buffers_.begin(), @@ -1840,7 +1849,7 @@ BufferQueue::iterator SourceBufferRange::GetBufferItrAt( } SourceBufferRange::KeyframeMap::iterator -SourceBufferRange::GetFirstKeyframeAt(base::TimeDelta timestamp, +SourceBufferRange::GetFirstKeyframeAt(DecodeTimestamp timestamp, bool skip_given_timestamp) { return skip_given_timestamp ? keyframe_map_.upper_bound(timestamp) : @@ -1848,7 +1857,7 @@ SourceBufferRange::GetFirstKeyframeAt(base::TimeDelta timestamp, } SourceBufferRange::KeyframeMap::iterator -SourceBufferRange::GetFirstKeyframeBefore(base::TimeDelta timestamp) { +SourceBufferRange::GetFirstKeyframeBefore(DecodeTimestamp timestamp) { KeyframeMap::iterator result = keyframe_map_.lower_bound(timestamp); // lower_bound() returns the first element >= |timestamp|, so we want the // previous element if it did not return the element exactly equal to @@ -1865,7 +1874,7 @@ void SourceBufferRange::DeleteAll(BufferQueue* removed_buffers) { } bool SourceBufferRange::TruncateAt( - base::TimeDelta timestamp, BufferQueue* removed_buffers, + DecodeTimestamp timestamp, BufferQueue* removed_buffers, bool is_exclusive) { // Find the place in |buffers_| where we will begin deleting data. BufferQueue::iterator starting_point = @@ -1914,7 +1923,7 @@ int SourceBufferRange::DeleteGOPFromFront(BufferQueue* deleted_buffers) { // Invalidate media segment start time if we've deleted the first buffer of // the range. if (buffers_deleted > 0) - media_segment_start_time_ = kNoTimestamp(); + media_segment_start_time_ = kNoDecodeTimestamp(); return total_bytes_deleted; } @@ -1949,8 +1958,8 @@ int SourceBufferRange::DeleteGOPFromBack(BufferQueue* deleted_buffers) { } int SourceBufferRange::GetRemovalGOP( - base::TimeDelta start_timestamp, base::TimeDelta end_timestamp, - int total_bytes_to_free, base::TimeDelta* removal_end_timestamp) { + DecodeTimestamp start_timestamp, DecodeTimestamp end_timestamp, + int total_bytes_to_free, DecodeTimestamp* removal_end_timestamp) { int bytes_to_free = total_bytes_to_free; int bytes_removed = 0; @@ -2037,8 +2046,8 @@ bool SourceBufferRange::TruncateAt( // Reset the next buffer index if we will be deleting the buffer that's next // in sequence. if (HasNextBufferPosition()) { - base::TimeDelta next_buffer_timestamp = GetNextTimestamp(); - if (next_buffer_timestamp == kNoTimestamp() || + DecodeTimestamp next_buffer_timestamp = GetNextTimestamp(); + if (next_buffer_timestamp == kNoDecodeTimestamp() || next_buffer_timestamp >= (*starting_point)->GetDecodeTimestamp()) { if (HasNextBuffer() && removed_buffers) { int starting_offset = starting_point - buffers_.begin(); @@ -2083,12 +2092,12 @@ int SourceBufferRange::GetNextConfigId() const { return GetConfigId(buffers_[next_buffer_index_], 0); } -base::TimeDelta SourceBufferRange::GetNextTimestamp() const { +DecodeTimestamp SourceBufferRange::GetNextTimestamp() const { DCHECK(!buffers_.empty()); DCHECK(HasNextBufferPosition()); if (next_buffer_index_ >= static_cast(buffers_.size())) { - return kNoTimestamp(); + return kNoDecodeTimestamp(); } return buffers_[next_buffer_index_]->GetDecodeTimestamp(); @@ -2125,16 +2134,16 @@ bool SourceBufferRange::CanAppendBuffersToEnd( buffers.front()->IsKeyframe()); } -bool SourceBufferRange::BelongsToRange(base::TimeDelta timestamp) const { +bool SourceBufferRange::BelongsToRange(DecodeTimestamp timestamp) const { DCHECK(!buffers_.empty()); return (IsNextInSequence(timestamp, false) || (GetStartTimestamp() <= timestamp && timestamp <= GetEndTimestamp())); } -bool SourceBufferRange::CanSeekTo(base::TimeDelta timestamp) const { - base::TimeDelta start_timestamp = - std::max(base::TimeDelta(), GetStartTimestamp() - GetFudgeRoom()); +bool SourceBufferRange::CanSeekTo(DecodeTimestamp timestamp) const { + DecodeTimestamp start_timestamp = + std::max(DecodeTimestamp(), GetStartTimestamp() - GetFudgeRoom()); return !keyframe_map_.empty() && start_timestamp <= timestamp && timestamp < GetBufferedEndTimestamp(); } @@ -2150,20 +2159,20 @@ bool SourceBufferRange::EndOverlaps(const SourceBufferRange& range) const { GetEndTimestamp() < range.GetEndTimestamp(); } -base::TimeDelta SourceBufferRange::GetStartTimestamp() const { +DecodeTimestamp SourceBufferRange::GetStartTimestamp() const { DCHECK(!buffers_.empty()); - base::TimeDelta start_timestamp = media_segment_start_time_; - if (start_timestamp == kNoTimestamp()) + DecodeTimestamp start_timestamp = media_segment_start_time_; + if (start_timestamp == kNoDecodeTimestamp()) start_timestamp = buffers_.front()->GetDecodeTimestamp(); return start_timestamp; } -base::TimeDelta SourceBufferRange::GetEndTimestamp() const { +DecodeTimestamp SourceBufferRange::GetEndTimestamp() const { DCHECK(!buffers_.empty()); return buffers_.back()->GetDecodeTimestamp(); } -base::TimeDelta SourceBufferRange::GetBufferedEndTimestamp() const { +DecodeTimestamp SourceBufferRange::GetBufferedEndTimestamp() const { DCHECK(!buffers_.empty()); base::TimeDelta duration = buffers_.back()->duration(); if (duration == kNoTimestamp() || duration == base::TimeDelta()) @@ -2171,16 +2180,16 @@ base::TimeDelta SourceBufferRange::GetBufferedEndTimestamp() const { return GetEndTimestamp() + duration; } -base::TimeDelta SourceBufferRange::NextKeyframeTimestamp( - base::TimeDelta timestamp) { +DecodeTimestamp SourceBufferRange::NextKeyframeTimestamp( + DecodeTimestamp timestamp) { DCHECK(!keyframe_map_.empty()); if (timestamp < GetStartTimestamp() || timestamp >= GetBufferedEndTimestamp()) - return kNoTimestamp(); + return kNoDecodeTimestamp(); KeyframeMap::iterator itr = GetFirstKeyframeAt(timestamp, false); if (itr == keyframe_map_.end()) - return kNoTimestamp(); + return kNoDecodeTimestamp(); // If the timestamp is inside the gap between the start of the media // segment and the first buffer, then just pretend there is a @@ -2194,19 +2203,19 @@ base::TimeDelta SourceBufferRange::NextKeyframeTimestamp( return itr->first; } -base::TimeDelta SourceBufferRange::KeyframeBeforeTimestamp( - base::TimeDelta timestamp) { +DecodeTimestamp SourceBufferRange::KeyframeBeforeTimestamp( + DecodeTimestamp timestamp) { DCHECK(!keyframe_map_.empty()); if (timestamp < GetStartTimestamp() || timestamp >= GetBufferedEndTimestamp()) - return kNoTimestamp(); + return kNoDecodeTimestamp(); return GetFirstKeyframeBefore(timestamp)->first; } bool SourceBufferRange::IsNextInSequence( - base::TimeDelta timestamp, bool is_keyframe) const { - base::TimeDelta end = buffers_.back()->GetDecodeTimestamp(); + DecodeTimestamp timestamp, bool is_keyframe) const { + DecodeTimestamp end = buffers_.back()->GetDecodeTimestamp(); if (end < timestamp && (type_ == SourceBufferStream::kText || timestamp <= end + GetFudgeRoom())) { @@ -2227,12 +2236,12 @@ base::TimeDelta SourceBufferRange::GetApproximateDuration() const { return max_interbuffer_distance; } -bool SourceBufferRange::GetBuffersInRange(base::TimeDelta start, - base::TimeDelta end, +bool SourceBufferRange::GetBuffersInRange(DecodeTimestamp start, + DecodeTimestamp end, BufferQueue* buffers) { // Find the nearest buffer with a decode timestamp <= start. - const base::TimeDelta first_timestamp = KeyframeBeforeTimestamp(start); - if (first_timestamp == kNoTimestamp()) + const DecodeTimestamp first_timestamp = KeyframeBeforeTimestamp(start); + if (first_timestamp == kNoDecodeTimestamp()) return false; // Find all buffers involved in the range. @@ -2246,9 +2255,12 @@ bool SourceBufferRange::GetBuffersInRange(base::TimeDelta start, buffer->duration() <= base::TimeDelta()) { return false; } - if (buffer->end_of_stream() || buffer->timestamp() >= end) + if (buffer->end_of_stream() || + buffer->timestamp() >= end.ToPresentationTime()) { break; - if (buffer->timestamp() + buffer->duration() <= start) + } + + if (buffer->timestamp() + buffer->duration() <= start.ToPresentationTime()) continue; buffers->push_back(buffer); } diff --git a/media/filters/source_buffer_stream.h b/media/filters/source_buffer_stream.h index 95b2e0970b16e..efbe31f8aeeac 100644 --- a/media/filters/source_buffer_stream.h +++ b/media/filters/source_buffer_stream.h @@ -66,7 +66,9 @@ class MEDIA_EXPORT SourceBufferStream { // Signals that the next buffers appended are part of a new media segment // starting at |media_segment_start_time|. - void OnNewMediaSegment(base::TimeDelta media_segment_start_time); + // TODO(acolwell/wolenetz): This should be changed to a presentation + // timestamp. See http://crbug.com/402502 + void OnNewMediaSegment(DecodeTimestamp media_segment_start_time); // Add the |buffers| to the SourceBufferStream. Buffers within the queue are // expected to be in order, but multiple calls to Append() may add buffers out @@ -167,9 +169,9 @@ class MEDIA_EXPORT SourceBufferStream { // Returns the size of buffers to secure if future // Remove(|start_timestamp|, |removal_end_timestamp|, duration) is called. // Will not update |removal_end_timestamp| if the returned size is 0. - int GetRemovalRange(base::TimeDelta start_timestamp, - base::TimeDelta end_timestamp, int byte_to_free, - base::TimeDelta* removal_end_timestamp); + int GetRemovalRange(DecodeTimestamp start_timestamp, + DecodeTimestamp end_timestamp, int byte_to_free, + DecodeTimestamp* removal_end_timestamp); // Prepares |range_for_next_append_| so |new_buffers| can be appended. // This involves removing buffers between the end of the previous append @@ -181,7 +183,7 @@ class MEDIA_EXPORT SourceBufferStream { BufferQueue* deleted_buffers); // Removes buffers, from the |track_buffer_|, that come after |timestamp|. - void PruneTrackBuffer(const base::TimeDelta timestamp); + void PruneTrackBuffer(const DecodeTimestamp timestamp); // Checks to see if |range_with_new_buffers_itr| can be merged with the range // next to it, and merges them if so. @@ -191,21 +193,17 @@ class MEDIA_EXPORT SourceBufferStream { // Returns true if |second_timestamp| is the timestamp of the next buffer in // sequence after |first_timestamp|, false otherwise. bool AreAdjacentInSequence( - base::TimeDelta first_timestamp, base::TimeDelta second_timestamp) const; + DecodeTimestamp first_timestamp, DecodeTimestamp second_timestamp) const; // Helper method that returns the timestamp for the next buffer that // |selected_range_| will return from GetNextBuffer() call, or kNoTimestamp() // if in between seeking (i.e. |selected_range_| is null). - base::TimeDelta GetNextBufferTimestamp(); - - // Returns the timestamp of the last buffer in the |selected_range_| or - // kNoTimestamp() if |selected_range_| is null. - base::TimeDelta GetEndBufferTimestamp(); + DecodeTimestamp GetNextBufferTimestamp(); // Finds the range that should contain a media segment that begins with // |start_timestamp| and returns the iterator pointing to it. Returns // |ranges_.end()| if there's no such existing range. - RangeList::iterator FindExistingRangeFor(base::TimeDelta start_timestamp); + RangeList::iterator FindExistingRangeFor(DecodeTimestamp start_timestamp); // Inserts |new_range| into |ranges_| preserving sorted order. Returns an // iterator in |ranges_| that points to |new_range|. @@ -222,7 +220,7 @@ class MEDIA_EXPORT SourceBufferStream { // Seeks |range| to |seek_timestamp| and then calls SetSelectedRange() with // |range|. void SeekAndSetSelectedRange(SourceBufferRange* range, - base::TimeDelta seek_timestamp); + DecodeTimestamp seek_timestamp); // Resets this stream back to an unseeked state. void ResetSeekState(); @@ -237,7 +235,7 @@ class MEDIA_EXPORT SourceBufferStream { // Returns true if |next_timestamp| and |next_is_keyframe| are valid for // the first buffer after the previous append. - bool IsNextTimestampValid(base::TimeDelta next_timestamp, + bool IsNextTimestampValid(DecodeTimestamp next_timestamp, bool next_is_keyframe) const; // Returns true if |selected_range_| is the only range in |ranges_| that @@ -260,19 +258,19 @@ class MEDIA_EXPORT SourceBufferStream { // |timestamp| if necessary and possible. This method only attempts to // set |selected_range_| if |seleted_range_| is null and |track_buffer_| // is empty. - void SetSelectedRangeIfNeeded(const base::TimeDelta timestamp); + void SetSelectedRangeIfNeeded(const DecodeTimestamp timestamp); // Find a keyframe timestamp that is >= |start_timestamp| and can be used to // find a new selected range. // Returns kNoTimestamp() if an appropriate keyframe timestamp could not be // found. - base::TimeDelta FindNewSelectedRangeSeekTimestamp( - const base::TimeDelta start_timestamp); + DecodeTimestamp FindNewSelectedRangeSeekTimestamp( + const DecodeTimestamp start_timestamp); // Searches |ranges_| for the first keyframe timestamp that is >= |timestamp|. // If |ranges_| doesn't contain a GOP that covers |timestamp| or doesn't // have a keyframe after |timestamp| then kNoTimestamp() is returned. - base::TimeDelta FindKeyframeAfterTimestamp(const base::TimeDelta timestamp); + DecodeTimestamp FindKeyframeAfterTimestamp(const DecodeTimestamp timestamp); // Returns "VIDEO" for a video SourceBufferStream, "AUDIO" for an audio // stream, and "TEXT" for a text stream. @@ -297,7 +295,7 @@ class MEDIA_EXPORT SourceBufferStream { // if the removal range included the current playback position. These buffers // can be used as candidates for placing in the |track_buffer_|. void RemoveInternal( - base::TimeDelta start, base::TimeDelta end, bool is_exclusive, + DecodeTimestamp start, DecodeTimestamp end, bool is_exclusive, BufferQueue* deleted_buffers); Type GetType() const; @@ -373,7 +371,7 @@ class MEDIA_EXPORT SourceBufferStream { BufferQueue track_buffer_; // The start time of the current media segment being appended. - base::TimeDelta media_segment_start_time_; + DecodeTimestamp media_segment_start_time_; // Points to the range containing the current media segment being appended. RangeList::iterator range_for_next_append_; @@ -382,14 +380,14 @@ class MEDIA_EXPORT SourceBufferStream { bool new_media_segment_; // The timestamp of the last buffer appended to the media segment, set to - // kNoTimestamp() if the beginning of the segment. - base::TimeDelta last_appended_buffer_timestamp_; + // kNoDecodeTimestamp() if the beginning of the segment. + DecodeTimestamp last_appended_buffer_timestamp_; bool last_appended_buffer_is_keyframe_; // The decode timestamp on the last buffer returned by the most recent // GetNextBuffer() call. Set to kNoTimestamp() if GetNextBuffer() hasn't been // called yet or a seek has happened since the last GetNextBuffer() call. - base::TimeDelta last_output_buffer_timestamp_; + DecodeTimestamp last_output_buffer_timestamp_; // Stores the largest distance between two adjacent buffers in this stream. base::TimeDelta max_interbuffer_distance_; diff --git a/media/filters/source_buffer_stream_unittest.cc b/media/filters/source_buffer_stream_unittest.cc index 01e4fd2f921ea..a1df540e695c4 100644 --- a/media/filters/source_buffer_stream_unittest.cc +++ b/media/filters/source_buffer_stream_unittest.cc @@ -157,11 +157,11 @@ class SourceBufferStreamTest : public testing::Test { int GetRemovalRangeInMs(int start, int end, int bytes_to_free, int* removal_end) { - base::TimeDelta removal_end_timestamp = - base::TimeDelta::FromMilliseconds(*removal_end); + DecodeTimestamp removal_end_timestamp = + DecodeTimestamp::FromMilliseconds(*removal_end); int bytes_removed = stream_->GetRemovalRange( - base::TimeDelta::FromMilliseconds(start), - base::TimeDelta::FromMilliseconds(end), bytes_to_free, + DecodeTimestamp::FromMilliseconds(start), + DecodeTimestamp::FromMilliseconds(end), bytes_to_free, &removal_end_timestamp); *removal_end = removal_end_timestamp.InMilliseconds(); return bytes_removed; @@ -288,8 +288,10 @@ class SourceBufferStreamTest : public testing::Test { ss << buffer->timestamp().InMilliseconds(); - if (buffer->GetDecodeTimestamp() != buffer->timestamp()) + if (buffer->GetDecodeTimestamp() != + DecodeTimestamp::FromPresentationTime(buffer->timestamp())) { ss << "|" << buffer->GetDecodeTimestamp().InMilliseconds(); + } // Handle preroll buffers. if (EndsWith(timestamps[i], "P", true)) { @@ -375,7 +377,8 @@ class SourceBufferStreamTest : public testing::Test { const uint8* data, int size) { if (begin_media_segment) - stream_->OnNewMediaSegment(starting_position * frame_duration_); + stream_->OnNewMediaSegment(DecodeTimestamp::FromPresentationTime( + starting_position * frame_duration_)); int keyframe_interval = frames_per_second_ / keyframes_per_second_; @@ -391,7 +394,8 @@ class SourceBufferStreamTest : public testing::Test { if (i == 0) timestamp += first_buffer_offset; - buffer->SetDecodeTimestamp(timestamp); + buffer->SetDecodeTimestamp( + DecodeTimestamp::FromPresentationTime(timestamp)); // Simulate an IBB...BBP pattern in which all B-frames reference both // the I- and P-frames. For a GOP with playback order 12345, this would @@ -415,12 +419,12 @@ class SourceBufferStreamTest : public testing::Test { EXPECT_EQ(expect_success, stream_->Append(queue)); } - void UpdateLastBufferDuration(base::TimeDelta current_dts, + void UpdateLastBufferDuration(DecodeTimestamp current_dts, BufferQueue* buffers) { if (buffers->empty() || buffers->back()->duration() > base::TimeDelta()) return; - base::TimeDelta last_dts = buffers->back()->GetDecodeTimestamp(); + DecodeTimestamp last_dts = buffers->back()->GetDecodeTimestamp(); DCHECK(current_dts >= last_dts); buffers->back()->set_duration(current_dts - last_dts); } @@ -533,7 +537,7 @@ class SourceBufferStreamTest : public testing::Test { if (dts_in_ms != pts_in_ms) { buffer->SetDecodeTimestamp( - base::TimeDelta::FromMilliseconds(dts_in_ms)); + DecodeTimestamp::FromMilliseconds(dts_in_ms)); } if (duration_in_ms) @@ -603,11 +607,12 @@ class SourceBufferStreamTest : public testing::Test { if (start_new_segment) { base::TimeDelta start_timestamp = segment_start_timestamp; if (start_timestamp == kNoTimestamp()) - start_timestamp = buffers[0]->GetDecodeTimestamp(); + start_timestamp = buffers[0]->timestamp(); - ASSERT_TRUE(start_timestamp <= buffers[0]->GetDecodeTimestamp()); + ASSERT_TRUE(start_timestamp <= buffers[0]->timestamp()); - stream_->OnNewMediaSegment(start_timestamp); + stream_->OnNewMediaSegment( + DecodeTimestamp::FromPresentationTime(start_timestamp)); } if (!one_by_one) { @@ -1921,7 +1926,8 @@ TEST_F(SourceBufferStreamTest, Seek_StartOfSegment) { // GetNextBuffer() should return the next buffer at position (5 + |bump|). EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess); - EXPECT_EQ(buffer->GetDecodeTimestamp(), 5 * frame_duration() + bump); + EXPECT_EQ(buffer->GetDecodeTimestamp(), + DecodeTimestamp::FromPresentationTime(5 * frame_duration() + bump)); // Check rest of buffers. CheckExpectedBuffers(6, 9); @@ -1935,7 +1941,8 @@ TEST_F(SourceBufferStreamTest, Seek_StartOfSegment) { // GetNextBuffer() should return the next buffer at position (15 + |bump|). EXPECT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess); - EXPECT_EQ(buffer->GetDecodeTimestamp(), 15 * frame_duration() + bump); + EXPECT_EQ(buffer->GetDecodeTimestamp(), DecodeTimestamp::FromPresentationTime( + 15 * frame_duration() + bump)); // Check rest of buffers. CheckExpectedBuffers(16, 19); @@ -2295,7 +2302,8 @@ TEST_F(SourceBufferStreamTest, PresentationTimestampIndependence) { ASSERT_EQ(stream_->GetNextBuffer(&buffer), SourceBufferStream::kSuccess); if (buffer->IsKeyframe()) { - EXPECT_EQ(buffer->timestamp(), buffer->GetDecodeTimestamp()); + EXPECT_EQ(DecodeTimestamp::FromPresentationTime(buffer->timestamp()), + buffer->GetDecodeTimestamp()); last_keyframe_idx = i; last_keyframe_presentation_timestamp = buffer->timestamp(); } else if (i == last_keyframe_idx + 1) { @@ -2306,7 +2314,8 @@ TEST_F(SourceBufferStreamTest, PresentationTimestampIndependence) { } else { EXPECT_GT(buffer->timestamp(), last_keyframe_presentation_timestamp); EXPECT_LT(buffer->timestamp(), last_p_frame_presentation_timestamp); - EXPECT_LT(buffer->timestamp(), buffer->GetDecodeTimestamp()); + EXPECT_LT(DecodeTimestamp::FromPresentationTime(buffer->timestamp()), + buffer->GetDecodeTimestamp()); } } } diff --git a/media/filters/video_decoder_selector_unittest.cc b/media/filters/video_decoder_selector_unittest.cc index 57760b5e06543..fdbcaaa0bcaa6 100644 --- a/media/filters/video_decoder_selector_unittest.cc +++ b/media/filters/video_decoder_selector_unittest.cc @@ -20,6 +20,22 @@ using ::testing::NotNull; using ::testing::Return; using ::testing::StrictMock; +// Use anonymous namespace here to prevent the actions to be defined multiple +// times across multiple test files. Sadly we can't use static for them. +namespace { + +ACTION_P3(ExecuteCallbackWithVerifier, decryptor, done_cb, verifier) { + // verifier must be called first since |done_cb| call will invoke it as well. + verifier->RecordACalled(); + arg0.Run(decryptor, done_cb); +} + +ACTION_P(ReportCallback, verifier) { + verifier->RecordBCalled(); +} + +} // namespace + namespace media { class VideoDecoderSelectorTest : public ::testing::Test { @@ -51,6 +67,7 @@ class VideoDecoderSelectorTest : public ::testing::Test { MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); MOCK_METHOD2(OnDecoderSelected, void(VideoDecoder*, DecryptingDemuxerStream*)); + MOCK_METHOD1(DecryptorSet, void(bool)); void MockOnDecoderSelected( scoped_ptr decoder, @@ -80,7 +97,13 @@ class VideoDecoderSelectorTest : public ::testing::Test { if (decryptor_capability == kDecryptOnly || decryptor_capability == kDecryptAndDecode) { EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) - .WillRepeatedly(RunCallback<0>(decryptor_.get())); + .WillRepeatedly(ExecuteCallbackWithVerifier( + decryptor_.get(), + base::Bind(&VideoDecoderSelectorTest::DecryptorSet, + base::Unretained(this)), + &verifier_)); + EXPECT_CALL(*this, DecryptorSet(true)) + .WillRepeatedly(ReportCallback(&verifier_)); if (decryptor_capability == kDecryptOnly) { EXPECT_CALL(*decryptor_, InitializeVideoDecoder(_, _)) @@ -145,6 +168,8 @@ class VideoDecoderSelectorTest : public ::testing::Test { base::MessageLoop message_loop_; + CallbackPairChecker verifier_; + private: DISALLOW_COPY_AND_ASSIGN(VideoDecoderSelectorTest); }; diff --git a/media/filters/video_frame_stream_unittest.cc b/media/filters/video_frame_stream_unittest.cc index 492e7cf9ffbb5..96848e42cd5f5 100644 --- a/media/filters/video_frame_stream_unittest.cc +++ b/media/filters/video_frame_stream_unittest.cc @@ -17,6 +17,7 @@ using ::testing::_; using ::testing::AnyNumber; using ::testing::Assign; using ::testing::Invoke; +using ::testing::InvokeWithoutArgs; using ::testing::NiceMock; using ::testing::Return; using ::testing::SaveArg; @@ -24,6 +25,22 @@ using ::testing::SaveArg; static const int kNumConfigs = 3; static const int kNumBuffersInOneConfig = 5; +// Use anonymous namespace here to prevent the actions to be defined multiple +// times across multiple test files. Sadly we can't use static for them. +namespace { + +ACTION_P3(ExecuteCallbackWithVerifier, decryptor, done_cb, verifier) { + // verifier must be called first since |done_cb| call will invoke it as well. + verifier->RecordACalled(); + arg0.Run(decryptor, done_cb); +} + +ACTION_P(ReportCallback, verifier) { + verifier->RecordBCalled(); +} + +} // namespace + namespace media { struct VideoFrameStreamTestParams { @@ -93,6 +110,7 @@ class VideoFrameStreamTest MOCK_METHOD1(OnNewSpliceBuffer, void(base::TimeDelta)); MOCK_METHOD1(SetDecryptorReadyCallback, void(const media::DecryptorReadyCB&)); + MOCK_METHOD1(DecryptorSet, void(bool)); void OnStatistics(const PipelineStatistics& statistics) { total_bytes_decoded_ += statistics.video_bytes_decoded; @@ -197,6 +215,17 @@ class VideoFrameStreamTest DECODER_RESET }; + void ExpectDecryptorNotification() { + EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) + .WillRepeatedly(ExecuteCallbackWithVerifier( + decryptor_.get(), + base::Bind(&VideoFrameStreamTest::DecryptorSet, + base::Unretained(this)), + &verifier_)); + EXPECT_CALL(*this, DecryptorSet(true)) + .WillRepeatedly(ReportCallback(&verifier_)); + } + void EnterPendingState(PendingState state) { DCHECK_NE(state, NOT_PENDING); switch (state) { @@ -219,15 +248,13 @@ class VideoFrameStreamTest break; case DECRYPTOR_NO_KEY: - EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) - .WillRepeatedly(RunCallback<0>(decryptor_.get())); + ExpectDecryptorNotification(); has_no_key_ = true; ReadOneFrame(); break; case DECODER_INIT: - EXPECT_CALL(*this, SetDecryptorReadyCallback(_)) - .WillRepeatedly(RunCallback<0>(decryptor_.get())); + ExpectDecryptorNotification(); decoder_->HoldNextInit(); InitializeVideoFrameStream(); break; @@ -332,6 +359,8 @@ class VideoFrameStreamTest // Decryptor has no key to decrypt a frame. bool has_no_key_; + CallbackPairChecker verifier_; + private: DISALLOW_COPY_AND_ASSIGN(VideoFrameStreamTest); }; diff --git a/media/filters/video_renderer_impl.cc b/media/filters/video_renderer_impl.cc index 73419c677dff2..25099ca0f84a7 100644 --- a/media/filters/video_renderer_impl.cc +++ b/media/filters/video_renderer_impl.cc @@ -39,6 +39,7 @@ VideoRendererImpl::VideoRendererImpl( buffering_state_(BUFFERING_HAVE_NOTHING), paint_cb_(paint_cb), last_timestamp_(kNoTimestamp()), + last_painted_timestamp_(kNoTimestamp()), frames_decoded_(0), frames_dropped_(0), is_shutting_down_(false), @@ -184,6 +185,11 @@ void VideoRendererImpl::ThreadMain() { const base::TimeDelta kIdleTimeDelta = base::TimeDelta::FromMilliseconds(10); + // If we have no frames and haven't painted any frame for certain amount of + // time, declare BUFFERING_HAVE_NOTHING. + const base::TimeDelta kTimeToDeclareHaveNothing = + base::TimeDelta::FromSeconds(3); + for (;;) { base::AutoLock auto_lock(lock_); @@ -197,6 +203,8 @@ void VideoRendererImpl::ThreadMain() { continue; } + base::TimeDelta now = get_time_cb_.Run(); + // Remain idle until we have the next frame ready for rendering. if (ready_frames_.empty()) { if (received_end_of_stream_) { @@ -204,7 +212,8 @@ void VideoRendererImpl::ThreadMain() { rendered_end_of_stream_ = true; task_runner_->PostTask(FROM_HERE, ended_cb_); } - } else { + } else if (last_painted_timestamp_ != kNoTimestamp() && + now - last_painted_timestamp_ >= kTimeToDeclareHaveNothing) { buffering_state_ = BUFFERING_HAVE_NOTHING; task_runner_->PostTask( FROM_HERE, base::Bind(buffering_state_cb_, BUFFERING_HAVE_NOTHING)); @@ -214,7 +223,6 @@ void VideoRendererImpl::ThreadMain() { continue; } - base::TimeDelta now = get_time_cb_.Run(); base::TimeDelta target_paint_timestamp = ready_frames_.front()->timestamp(); base::TimeDelta latest_paint_timestamp; @@ -258,6 +266,7 @@ void VideoRendererImpl::PaintNextReadyFrame_Locked() { frames_decoded_++; last_timestamp_ = next_frame->timestamp(); + last_painted_timestamp_ = next_frame->timestamp(); paint_cb_.Run(next_frame); @@ -443,6 +452,7 @@ void VideoRendererImpl::OnVideoFrameStreamResetDone() { state_ = kFlushed; last_timestamp_ = kNoTimestamp(); + last_painted_timestamp_ = kNoTimestamp(); base::ResetAndReturn(&flush_cb_).Run(); } diff --git a/media/filters/video_renderer_impl.h b/media/filters/video_renderer_impl.h index eef3d0b194fb0..181beb57d503c 100644 --- a/media/filters/video_renderer_impl.h +++ b/media/filters/video_renderer_impl.h @@ -198,6 +198,10 @@ class MEDIA_EXPORT VideoRendererImpl // during flushing. base::TimeDelta last_timestamp_; + // The timestamp of the last successfully painted frame. Set to kNoTimestamp() + // during flushing. + base::TimeDelta last_painted_timestamp_; + // Keeps track of the number of frames decoded and dropped since the // last call to |statistics_cb_|. These must be accessed under lock. int frames_decoded_; diff --git a/media/filters/video_renderer_impl_unittest.cc b/media/filters/video_renderer_impl_unittest.cc index 27a2bfe9552e9..4f44939640ad7 100644 --- a/media/filters/video_renderer_impl_unittest.cc +++ b/media/filters/video_renderer_impl_unittest.cc @@ -43,8 +43,9 @@ MATCHER_P(HasTimestamp, ms, "") { return arg->timestamp().InMilliseconds() == ms; } -// Arbitrary value. Has to be larger to cover any timestamp value used in tests. -static const int kVideoDurationInMs = 1000; +// Arbitrary value. Has to be larger to cover any timestamp value used in tests +// and kTimeToDeclareHaveNothing. +static const int kVideoDurationInMs = 10000; class VideoRendererImplTest : public ::testing::Test { public: @@ -530,13 +531,17 @@ TEST_F(VideoRendererImplTest, Underflow) { EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_ENOUGH)); StartPlaying(); - // Frames should be dropped and we should signal having nothing. + // Advance time slightly. Frames should be dropped and we should NOT signal + // having nothing. + AdvanceTimeInMs(100); + + // Advance time more. Now we should signal having nothing. { SCOPED_TRACE("Waiting for BUFFERING_HAVE_NOTHING"); WaitableMessageLoopEvent event; EXPECT_CALL(mock_cb_, BufferingStateChange(BUFFERING_HAVE_NOTHING)) .WillOnce(RunClosure(event.GetClosure())); - AdvanceTimeInMs(100); + AdvanceTimeInMs(3000); // Must match kTimeToDeclareHaveNothing. event.RunAndWait(); } diff --git a/media/formats/mp2t/es_adapter_video.h b/media/formats/mp2t/es_adapter_video.h index 0739fc3c27690..2127235918637 100644 --- a/media/formats/mp2t/es_adapter_video.h +++ b/media/formats/mp2t/es_adapter_video.h @@ -13,9 +13,10 @@ #include "base/memory/ref_counted.h" #include "base/time/time.h" #include "media/base/media_export.h" +#include "media/base/stream_parser_buffer.h" namespace media { -class StreamParserBuffer; + class VideoDecoderConfig; namespace mp2t { @@ -87,7 +88,7 @@ class MEDIA_EXPORT EsAdapterVideo { // - Minimum PTS of discarded frames. // - DTS of discarded frames. base::TimeDelta discarded_frames_min_pts_; - std::list discarded_frames_dts_; + std::list discarded_frames_dts_; DISALLOW_COPY_AND_ASSIGN(EsAdapterVideo); }; diff --git a/media/formats/mp2t/es_parser.h b/media/formats/mp2t/es_parser.h index 5297d32133253..96785eafb73be 100644 --- a/media/formats/mp2t/es_parser.h +++ b/media/formats/mp2t/es_parser.h @@ -9,6 +9,7 @@ #include "base/callback.h" #include "base/memory/ref_counted.h" #include "base/time/time.h" +#include "media/base/stream_parser_buffer.h" namespace media { @@ -27,7 +28,7 @@ class EsParser { // Should use kNoTimestamp when a timestamp is not valid. virtual bool Parse(const uint8* buf, int size, base::TimeDelta pts, - base::TimeDelta dts) = 0; + DecodeTimestamp dts) = 0; // Flush any pending buffer. virtual void Flush() = 0; diff --git a/media/formats/mp2t/es_parser_adts.cc b/media/formats/mp2t/es_parser_adts.cc index 433baabe5e674..f2a284a200458 100644 --- a/media/formats/mp2t/es_parser_adts.cc +++ b/media/formats/mp2t/es_parser_adts.cc @@ -127,7 +127,7 @@ EsParserAdts::~EsParserAdts() { bool EsParserAdts::Parse(const uint8* buf, int size, base::TimeDelta pts, - base::TimeDelta dts) { + DecodeTimestamp dts) { // The incoming PTS applies to the access unit that comes just after // the beginning of |buf|. if (pts != kNoTimestamp()) @@ -151,6 +151,10 @@ bool EsParserAdts::Parse(const uint8* buf, int size, pts_list_.pop_front(); } + if (audio_timestamp_helper_->base_timestamp() == kNoTimestamp()) { + DVLOG(1) << "Audio frame with unknown timestamp"; + return false; + } base::TimeDelta current_pts = audio_timestamp_helper_->GetTimestamp(); base::TimeDelta frame_duration = audio_timestamp_helper_->GetFrameDuration(kSamplesPerAACFrame); @@ -166,7 +170,6 @@ bool EsParserAdts::Parse(const uint8* buf, int size, adts_frame.size, is_key_frame, DemuxerStream::AUDIO, 0); - stream_parser_buffer->SetDecodeTimestamp(current_pts); stream_parser_buffer->set_timestamp(current_pts); stream_parser_buffer->set_duration(frame_duration); emit_buffer_cb_.Run(stream_parser_buffer); @@ -247,7 +250,8 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) { DVLOG(1) << "Channel config: " << channel_configuration; DVLOG(1) << "Adts profile: " << adts_profile; // Reset the timestamp helper to use a new time scale. - if (audio_timestamp_helper_) { + if (audio_timestamp_helper_ && + audio_timestamp_helper_->base_timestamp() != kNoTimestamp()) { base::TimeDelta base_timestamp = audio_timestamp_helper_->GetTimestamp(); audio_timestamp_helper_.reset( new AudioTimestampHelper(samples_per_second)); @@ -266,4 +270,3 @@ bool EsParserAdts::UpdateAudioConfiguration(const uint8* adts_header) { } // namespace mp2t } // namespace media - diff --git a/media/formats/mp2t/es_parser_adts.h b/media/formats/mp2t/es_parser_adts.h index 03c90110e7670..320180460f00b 100644 --- a/media/formats/mp2t/es_parser_adts.h +++ b/media/formats/mp2t/es_parser_adts.h @@ -13,6 +13,7 @@ #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "media/base/audio_decoder_config.h" +#include "media/base/media_export.h" #include "media/formats/mp2t/es_parser.h" namespace media { @@ -25,7 +26,7 @@ class StreamParserBuffer; namespace media { namespace mp2t { -class EsParserAdts : public EsParser { +class MEDIA_EXPORT EsParserAdts : public EsParser { public: typedef base::Callback NewAudioConfigCB; @@ -37,7 +38,7 @@ class EsParserAdts : public EsParser { // EsParser implementation. virtual bool Parse(const uint8* buf, int size, base::TimeDelta pts, - base::TimeDelta dts) OVERRIDE; + DecodeTimestamp dts) OVERRIDE; virtual void Flush() OVERRIDE; virtual void Reset() OVERRIDE; @@ -92,4 +93,3 @@ class EsParserAdts : public EsParser { } // namespace media #endif - diff --git a/media/formats/mp2t/es_parser_adts_unittest.cc b/media/formats/mp2t/es_parser_adts_unittest.cc new file mode 100644 index 0000000000000..c9dd47c54b486 --- /dev/null +++ b/media/formats/mp2t/es_parser_adts_unittest.cc @@ -0,0 +1,86 @@ +// 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 + +#include "base/bind.h" +#include "base/logging.h" +#include "base/time/time.h" +#include "media/base/buffers.h" +#include "media/base/stream_parser_buffer.h" +#include "media/formats/mp2t/es_parser_adts.h" +#include "media/formats/mp2t/es_parser_test_base.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { +class AudioDecoderConfig; + +namespace mp2t { + +class EsParserAdtsTest : public EsParserTestBase, + public testing::Test { + public: + EsParserAdtsTest(); + virtual ~EsParserAdtsTest() {} + + protected: + bool Process(const std::vector& pes_packets, bool force_timing); + + std::vector GenerateFixedSizePesPacket(size_t pes_size); + + private: + DISALLOW_COPY_AND_ASSIGN(EsParserAdtsTest); +}; + +EsParserAdtsTest::EsParserAdtsTest() { +} + +bool EsParserAdtsTest::Process( + const std::vector& pes_packets, + bool force_timing) { + EsParserAdts es_parser( + base::Bind(&EsParserAdtsTest::NewAudioConfig, base::Unretained(this)), + base::Bind(&EsParserAdtsTest::EmitBuffer, base::Unretained(this)), + false); + return ProcessPesPackets(&es_parser, pes_packets, force_timing); +} + +std::vector +EsParserAdtsTest::GenerateFixedSizePesPacket(size_t pes_size) { + DCHECK_GT(stream_.size(), 0u); + std::vector pes_packets; + + Packet cur_pes_packet; + cur_pes_packet.offset = 0; + cur_pes_packet.pts = kNoTimestamp(); + while (cur_pes_packet.offset < stream_.size()) { + pes_packets.push_back(cur_pes_packet); + cur_pes_packet.offset += pes_size; + } + ComputePacketSize(&pes_packets); + + return pes_packets; +} + +TEST_F(EsParserAdtsTest, NoInitialPts) { + LoadStream("bear.adts"); + std::vector pes_packets = GenerateFixedSizePesPacket(512); + EXPECT_FALSE(Process(pes_packets, false)); + EXPECT_EQ(0u, buffer_count_); +} + +TEST_F(EsParserAdtsTest, SinglePts) { + LoadStream("bear.adts"); + + std::vector pes_packets = GenerateFixedSizePesPacket(512); + pes_packets.front().pts = base::TimeDelta::FromSeconds(10); + + EXPECT_TRUE(Process(pes_packets, false)); + EXPECT_EQ(1u, config_count_); + EXPECT_EQ(45u, buffer_count_); +} + +} // namespace mp2t +} // namespace media + diff --git a/media/formats/mp2t/es_parser_h264.cc b/media/formats/mp2t/es_parser_h264.cc index f2c166cf8cc6e..f5b87e6284b49 100644 --- a/media/formats/mp2t/es_parser_h264.cc +++ b/media/formats/mp2t/es_parser_h264.cc @@ -39,7 +39,7 @@ EsParserH264::~EsParserH264() { bool EsParserH264::Parse(const uint8* buf, int size, base::TimeDelta pts, - base::TimeDelta dts) { + DecodeTimestamp dts) { // Note: Parse is invoked each time a PES packet has been reassembled. // Unfortunately, a PES packet does not necessarily map // to an h264 access unit, although the HLS recommendation is to use one PES @@ -53,7 +53,8 @@ bool EsParserH264::Parse(const uint8* buf, int size, if (pts != kNoTimestamp()) { TimingDesc timing_desc; timing_desc.pts = pts; - timing_desc.dts = (dts != kNoTimestamp()) ? dts : pts; + timing_desc.dts = (dts != kNoDecodeTimestamp()) ? dts : + DecodeTimestamp::FromPresentationTime(pts); // Link the end of the byte queue with the incoming timing descriptor. timing_desc_list_.push_back( @@ -231,7 +232,7 @@ bool EsParserH264::ParseInternal() { bool EsParserH264::EmitFrame(int64 access_unit_pos, int access_unit_size, bool is_key_frame, int pps_id) { // Get the access unit timing info. - TimingDesc current_timing_desc = {kNoTimestamp(), kNoTimestamp()}; + TimingDesc current_timing_desc = {kNoDecodeTimestamp(), kNoTimestamp()}; while (!timing_desc_list_.empty() && timing_desc_list_.front().first <= access_unit_pos) { current_timing_desc = timing_desc_list_.front().second; @@ -332,4 +333,3 @@ bool EsParserH264::UpdateVideoDecoderConfig(const H264SPS* sps) { } // namespace mp2t } // namespace media - diff --git a/media/formats/mp2t/es_parser_h264.h b/media/formats/mp2t/es_parser_h264.h index 674b2c650a9c5..82499f5ec530e 100644 --- a/media/formats/mp2t/es_parser_h264.h +++ b/media/formats/mp2t/es_parser_h264.h @@ -43,13 +43,13 @@ class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) { // EsParser implementation. virtual bool Parse(const uint8* buf, int size, base::TimeDelta pts, - base::TimeDelta dts) OVERRIDE; + DecodeTimestamp dts) OVERRIDE; virtual void Flush() OVERRIDE; virtual void Reset() OVERRIDE; private: struct TimingDesc { - base::TimeDelta dts; + DecodeTimestamp dts; base::TimeDelta pts; }; @@ -96,4 +96,3 @@ class MEDIA_EXPORT EsParserH264 : NON_EXPORTED_BASE(public EsParser) { } // namespace media #endif - diff --git a/media/formats/mp2t/es_parser_h264_unittest.cc b/media/formats/mp2t/es_parser_h264_unittest.cc index 2c13df0d853fb..5228b6e8a1e58 100644 --- a/media/formats/mp2t/es_parser_h264_unittest.cc +++ b/media/formats/mp2t/es_parser_h264_unittest.cc @@ -2,19 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include +#include +#include #include #include "base/bind.h" -#include "base/command_line.h" -#include "base/files/memory_mapped_file.h" #include "base/logging.h" -#include "base/path_service.h" +#include "base/strings/string_util.h" #include "base/time/time.h" #include "media/base/stream_parser_buffer.h" -#include "media/base/test_data_util.h" #include "media/filters/h264_parser.h" #include "media/formats/mp2t/es_parser_h264.h" +#include "media/formats/mp2t/es_parser_test_base.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { @@ -22,36 +21,51 @@ class VideoDecoderConfig; namespace mp2t { -namespace { +class EsParserH264Test : public EsParserTestBase, + public testing::Test { + public: + EsParserH264Test() {} + virtual ~EsParserH264Test() {} + + protected: + void LoadH264Stream(const char* filename); + void GetPesTimestamps(std::vector* pes_packets); + bool Process(const std::vector& pes_packets, bool force_timing); + void CheckAccessUnits(); + + // Access units of the stream with AUD NALUs. + std::vector access_units_; -struct Packet { - // Offset in the stream. - size_t offset; + private: + // Get the offset of the start of each access unit of |stream_|. + // This function assumes there is only one slice per access unit. + // This is a very simplified access unit segmenter that is good + // enough for unit tests. + void GetAccessUnits(); - // Size of the packet. - size_t size; + // Insert an AUD before each access unit. + // Update |stream_| and |access_units_| accordingly. + void InsertAUD(); - // Timestamp of the packet. - base::TimeDelta pts; + DISALLOW_COPY_AND_ASSIGN(EsParserH264Test); }; -// Compute the size of each packet assuming packets are given in stream order -// and the last packet covers the end of the stream. -void ComputePacketSize(std::vector& packets, size_t stream_size) { - for (size_t k = 0; k < packets.size() - 1; k++) { - DCHECK_GE(packets[k + 1].offset, packets[k].offset); - packets[k].size = packets[k + 1].offset - packets[k].offset; - } - packets[packets.size() - 1].size = - stream_size - packets[packets.size() - 1].offset; +void EsParserH264Test::LoadH264Stream(const char* filename) { + // Load the input H264 file and segment it into access units. + LoadStream(filename); + GetAccessUnits(); + ASSERT_GT(access_units_.size(), 0u); + + // Insert AUDs into the stream. + InsertAUD(); + + // Generate some timestamps based on a 25fps stream. + for (size_t k = 0; k < access_units_.size(); k++) + access_units_[k].pts = base::TimeDelta::FromMilliseconds(k * 40u); } -// Get the offset of the start of each access unit. -// This function assumes there is only one slice per access unit. -// This is a very simplified access unit segmenter that is good -// enough for unit tests. -std::vector GetAccessUnits(const uint8* stream, size_t stream_size) { - std::vector access_units; +void EsParserH264Test::GetAccessUnits() { + access_units_.resize(0); bool start_access_unit = true; // In a first pass, retrieve the offsets of all access units. @@ -61,7 +75,7 @@ std::vector GetAccessUnits(const uint8* stream, size_t stream_size) { off_t relative_offset = 0; off_t start_code_size = 0; bool success = H264Parser::FindStartCode( - &stream[offset], stream_size - offset, + &stream_[offset], stream_.size() - offset, &relative_offset, &start_code_size); if (!success) break; @@ -70,15 +84,15 @@ std::vector GetAccessUnits(const uint8* stream, size_t stream_size) { if (start_access_unit) { Packet cur_access_unit; cur_access_unit.offset = offset; - access_units.push_back(cur_access_unit); + access_units_.push_back(cur_access_unit); start_access_unit = false; } // Get the NALU type. offset += start_code_size; - if (offset >= stream_size) + if (offset >= stream_.size()) break; - int nal_unit_type = stream[offset] & 0x1f; + int nal_unit_type = stream_[offset] & 0x1f; // We assume there is only one slice per access unit. if (nal_unit_type == H264NALU::kIDRSlice || @@ -87,95 +101,46 @@ std::vector GetAccessUnits(const uint8* stream, size_t stream_size) { } } - ComputePacketSize(access_units, stream_size); - return access_units; + ComputePacketSize(&access_units_); } -// Append an AUD NALU at the beginning of each access unit -// needed for streams which do not already have AUD NALUs. -void AppendAUD( - const uint8* stream, size_t stream_size, - const std::vector& access_units, - std::vector& stream_with_aud, - std::vector& access_units_with_aud) { +void EsParserH264Test::InsertAUD() { uint8 aud[] = { 0x00, 0x00, 0x01, 0x09 }; - stream_with_aud.resize(stream_size + access_units.size() * sizeof(aud)); - access_units_with_aud.resize(access_units.size()); + + std::vector stream_with_aud( + stream_.size() + access_units_.size() * sizeof(aud)); + std::vector access_units_with_aud( + access_units_.size()); size_t offset = 0; - for (size_t k = 0; k < access_units.size(); k++) { + for (size_t k = 0; k < access_units_.size(); k++) { access_units_with_aud[k].offset = offset; - access_units_with_aud[k].size = access_units[k].size + sizeof(aud); + access_units_with_aud[k].size = access_units_[k].size + sizeof(aud); memcpy(&stream_with_aud[offset], aud, sizeof(aud)); offset += sizeof(aud); memcpy(&stream_with_aud[offset], - &stream[access_units[k].offset], access_units[k].size); - offset += access_units[k].size; - } -} - -} // namespace - -class EsParserH264Test : public testing::Test { - public: - EsParserH264Test() : buffer_count_(0) { - } - virtual ~EsParserH264Test() {} - - protected: - void LoadStream(const char* filename); - void GetPesTimestamps(std::vector& pes_packets); - void ProcessPesPackets(const std::vector& pes_packets, - bool force_timing); - - // Stream with AUD NALUs. - std::vector stream_; - - // Access units of the stream with AUD NALUs. - std::vector access_units_; - - // Number of buffers generated while parsing the H264 stream. - size_t buffer_count_; - - private: - void EmitBuffer(scoped_refptr buffer); - - void NewVideoConfig(const VideoDecoderConfig& config) { + &stream_[access_units_[k].offset], access_units_[k].size); + offset += access_units_[k].size; } - DISALLOW_COPY_AND_ASSIGN(EsParserH264Test); -}; - -void EsParserH264Test::LoadStream(const char* filename) { - base::FilePath file_path = GetTestDataFilePath(filename); - - base::MemoryMappedFile stream_without_aud; - ASSERT_TRUE(stream_without_aud.Initialize(file_path)) - << "Couldn't open stream file: " << file_path.MaybeAsASCII(); - - // The input file does not have AUDs. - std::vector access_units_without_aud = GetAccessUnits( - stream_without_aud.data(), stream_without_aud.length()); - ASSERT_GT(access_units_without_aud.size(), 0u); - AppendAUD(stream_without_aud.data(), stream_without_aud.length(), - access_units_without_aud, - stream_, access_units_); - - // Generate some timestamps based on a 25fps stream. - for (size_t k = 0; k < access_units_.size(); k++) - access_units_[k].pts = base::TimeDelta::FromMilliseconds(k * 40u); + // Update the stream and access units used for the test. + stream_ = stream_with_aud; + access_units_ = access_units_with_aud; } -void EsParserH264Test::GetPesTimestamps(std::vector& pes_packets) { +void EsParserH264Test::GetPesTimestamps(std::vector* pes_packets_ptr) { + DCHECK(pes_packets_ptr); + const std::vector& pes_packets = *pes_packets_ptr; + // Default: set to a negative timestamp to be able to differentiate from // real timestamps. // Note: we don't use kNoTimestamp() here since this one has already // a special meaning in EsParserH264. The negative timestamps should be // ultimately discarded by the H264 parser since not relevant. for (size_t k = 0; k < pes_packets.size(); k++) { - pes_packets[k].pts = base::TimeDelta::FromMilliseconds(-1); + (*pes_packets_ptr)[k].pts = base::TimeDelta::FromMilliseconds(-1); } // Set a valid timestamp for PES packets which include the start @@ -187,55 +152,51 @@ void EsParserH264Test::GetPesTimestamps(std::vector& pes_packets) { size_t pes_end = pes_packets[pes_idx].offset + pes_packets[pes_idx].size; if (pes_start <= access_units_[k].offset && pes_end > access_units_[k].offset) { - pes_packets[pes_idx].pts = access_units_[k].pts; + (*pes_packets_ptr)[pes_idx].pts = access_units_[k].pts; break; } } } } -void EsParserH264Test::ProcessPesPackets( +bool EsParserH264Test::Process( const std::vector& pes_packets, bool force_timing) { EsParserH264 es_parser( base::Bind(&EsParserH264Test::NewVideoConfig, base::Unretained(this)), base::Bind(&EsParserH264Test::EmitBuffer, base::Unretained(this))); + return ProcessPesPackets(&es_parser, pes_packets, force_timing); +} - for (size_t k = 0; k < pes_packets.size(); k++) { - size_t cur_pes_offset = pes_packets[k].offset; - size_t cur_pes_size = pes_packets[k].size; - - base::TimeDelta pts = kNoTimestamp(); - base::TimeDelta dts = kNoTimestamp(); - if (pes_packets[k].pts >= base::TimeDelta() || force_timing) - pts = pes_packets[k].pts; +void EsParserH264Test::CheckAccessUnits() { + EXPECT_EQ(buffer_count_, access_units_.size()); - ASSERT_TRUE( - es_parser.Parse(&stream_[cur_pes_offset], cur_pes_size, pts, dts)); + std::stringstream buffer_timestamps_stream; + for (size_t k = 0; k < access_units_.size(); k++) { + buffer_timestamps_stream << "(" + << access_units_[k].pts.InMilliseconds() + << ") "; } - es_parser.Flush(); -} - -void EsParserH264Test::EmitBuffer(scoped_refptr buffer) { - ASSERT_LT(buffer_count_, access_units_.size()); - EXPECT_EQ(buffer->timestamp(), access_units_[buffer_count_].pts); - buffer_count_++; + std::string buffer_timestamps = buffer_timestamps_stream.str(); + base::TrimWhitespaceASCII( + buffer_timestamps, base::TRIM_ALL, &buffer_timestamps); + EXPECT_EQ(buffer_timestamps_, buffer_timestamps); } TEST_F(EsParserH264Test, OneAccessUnitPerPes) { - LoadStream("bear.h264"); + LoadH264Stream("bear.h264"); // One to one equivalence between PES packets and access units. std::vector pes_packets(access_units_); - GetPesTimestamps(pes_packets); + GetPesTimestamps(&pes_packets); // Process each PES packet. - ProcessPesPackets(pes_packets, false); - EXPECT_EQ(buffer_count_, access_units_.size()); + EXPECT_TRUE(Process(pes_packets, false)); + CheckAccessUnits(); } TEST_F(EsParserH264Test, NonAlignedPesPacket) { - LoadStream("bear.h264"); + LoadH264Stream("bear.h264"); // Generate the PES packets. std::vector pes_packets; @@ -251,16 +212,16 @@ TEST_F(EsParserH264Test, NonAlignedPesPacket) { cur_pes_packet.offset = access_units_[k].offset + std::min(487u, access_units_[k].size); } - ComputePacketSize(pes_packets, stream_.size()); - GetPesTimestamps(pes_packets); + ComputePacketSize(&pes_packets); + GetPesTimestamps(&pes_packets); // Process each PES packet. - ProcessPesPackets(pes_packets, false); - EXPECT_EQ(buffer_count_, access_units_.size()); + EXPECT_TRUE(Process(pes_packets, false)); + CheckAccessUnits(); } TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) { - LoadStream("bear.h264"); + LoadH264Stream("bear.h264"); // Get the minimum size of an access unit. size_t min_access_unit_size = stream_.size(); @@ -282,19 +243,17 @@ TEST_F(EsParserH264Test, SeveralPesPerAccessUnit) { pes_packets.push_back(cur_pes_packet); cur_pes_packet.offset += pes_size; } - ComputePacketSize(pes_packets, stream_.size()); - GetPesTimestamps(pes_packets); + ComputePacketSize(&pes_packets); + GetPesTimestamps(&pes_packets); // Process each PES packet. - ProcessPesPackets(pes_packets, false); - EXPECT_EQ(buffer_count_, access_units_.size()); + EXPECT_TRUE(Process(pes_packets, false)); + CheckAccessUnits(); // Process PES packets forcing timings for each PES packet. - buffer_count_ = 0; - ProcessPesPackets(pes_packets, true); - EXPECT_EQ(buffer_count_, access_units_.size()); + EXPECT_TRUE(Process(pes_packets, true)); + CheckAccessUnits(); } } // namespace mp2t } // namespace media - diff --git a/media/formats/mp2t/es_parser_test_base.cc b/media/formats/mp2t/es_parser_test_base.cc new file mode 100644 index 0000000000000..49c0fe6a95593 --- /dev/null +++ b/media/formats/mp2t/es_parser_test_base.cc @@ -0,0 +1,108 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "media/formats/mp2t/es_parser_test_base.h" + +#include "base/files/memory_mapped_file.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/strings/string_util.h" +#include "media/base/buffers.h" +#include "media/base/stream_parser_buffer.h" +#include "media/base/test_data_util.h" +#include "media/formats/mp2t/es_parser.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { +namespace mp2t { + +EsParserTestBase::Packet::Packet() + : offset(0u), + size(0u), + pts(kNoTimestamp()) { +} + +EsParserTestBase::EsParserTestBase() + : config_count_(0u), + buffer_count_(0u) { +} + +EsParserTestBase::~EsParserTestBase() { +} + +void EsParserTestBase::LoadStream(const char* filename) { + base::FilePath file_path = GetTestDataFilePath(filename); + + base::MemoryMappedFile stream; + ASSERT_TRUE(stream.Initialize(file_path)) + << "Couldn't open stream file: " << file_path.MaybeAsASCII(); + + stream_.resize(stream.length()); + memcpy(&stream_[0], stream.data(), stream_.size()); +} + +void EsParserTestBase::NewAudioConfig(const AudioDecoderConfig& config) { + config_count_++; +} + +void EsParserTestBase::NewVideoConfig(const VideoDecoderConfig& config) { + config_count_++; +} + +void EsParserTestBase::EmitBuffer(scoped_refptr buffer) { + buffer_timestamps_stream_ << "(" + << buffer->timestamp().InMilliseconds() + << ") "; + buffer_count_++; +} + +bool EsParserTestBase::ProcessPesPackets( + EsParser* es_parser, + const std::vector& pes_packets, + bool force_timing) { + DCHECK(es_parser); + + buffer_count_ = 0; + config_count_ = 0; + buffer_timestamps_stream_.str(std::string()); + + for (size_t k = 0; k < pes_packets.size(); k++) { + size_t cur_pes_offset = pes_packets[k].offset; + size_t cur_pes_size = pes_packets[k].size; + + base::TimeDelta pts = kNoTimestamp(); + DecodeTimestamp dts = kNoDecodeTimestamp(); + if (pes_packets[k].pts >= base::TimeDelta() || force_timing) + pts = pes_packets[k].pts; + + DCHECK_LT(cur_pes_offset, stream_.size()); + if (!es_parser->Parse(&stream_[cur_pes_offset], cur_pes_size, pts, dts)) + return false; + } + es_parser->Flush(); + + buffer_timestamps_ = buffer_timestamps_stream_.str(); + base::TrimWhitespaceASCII( + buffer_timestamps_, base::TRIM_ALL, &buffer_timestamps_); + return true; +} + +void EsParserTestBase::ComputePacketSize(std::vector* packets) { + DCHECK(packets); + if (packets->size() == 0u) + return; + + Packet* cur = &(*packets)[0]; + for (size_t k = 0; k < packets->size() - 1; k++) { + Packet* next = &(*packets)[k + 1]; + DCHECK_GE(next->offset, cur->offset); + cur->size = next->offset - cur->offset; + cur = next; + } + DCHECK_GE(stream_.size(), cur->offset); + cur->size = stream_.size() - cur->offset; +} + +} // namespace mp2t +} // namespace media diff --git a/media/formats/mp2t/es_parser_test_base.h b/media/formats/mp2t/es_parser_test_base.h new file mode 100644 index 0000000000000..4b18efac6f95a --- /dev/null +++ b/media/formats/mp2t/es_parser_test_base.h @@ -0,0 +1,85 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_FORMATS_MP2T_ES_PARSER_TEST_BASE_H_ +#define MEDIA_FORMATS_MP2T_ES_PARSER_TEST_BASE_H_ + +#include +#include +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/time/time.h" + +namespace media { +class AudioDecoderConfig; +class StreamParserBuffer; +class VideoDecoderConfig; + +namespace mp2t { +class EsParser; + +class EsParserTestBase { + public: + struct Packet { + Packet(); + + // Offset in the stream. + size_t offset; + + // Size of the packet. + size_t size; + + // Timestamp of the packet. + base::TimeDelta pts; + }; + + EsParserTestBase(); + virtual ~EsParserTestBase(); + + protected: + void LoadStream(const char* filename); + + // ES parser callbacks. + void NewAudioConfig(const AudioDecoderConfig& config); + void NewVideoConfig(const VideoDecoderConfig& config); + void EmitBuffer(scoped_refptr buffer); + + // Process the PES packets using the given ES parser. + // When |force_timing| is true, even the invalid negative timestamps will be + // given to the ES parser. + // Return true if successful, false otherwise. + bool ProcessPesPackets(EsParser* es_parser, + const std::vector& pes_packets, + bool force_timing); + + // Assume the offsets are known, compute the size of each packet. + // The last packet is assumed to cover the end of the stream. + // Packets are assumed to be in stream order. + void ComputePacketSize(std::vector* packets); + + // ES stream. + std::vector stream_; + + // Number of decoder configs received from the ES parser. + size_t config_count_; + + // Number of buffers generated while parsing the ES stream. + size_t buffer_count_; + + // Timestamps of buffers generated while parsing the ES stream. + std::string buffer_timestamps_; + + private: + // Timestamps of buffers generated while parsing the ES stream. + std::stringstream buffer_timestamps_stream_; + + DISALLOW_COPY_AND_ASSIGN(EsParserTestBase); +}; + +} // namespace mp2t +} // namespace media + +#endif // MEDIA_FORMATS_MP2T_ES_PARSER_TEST_BASE_H_ diff --git a/media/formats/mp2t/mp2t_stream_parser_unittest.cc b/media/formats/mp2t/mp2t_stream_parser_unittest.cc index f74baa6dcdd26..041ae49ece160 100644 --- a/media/formats/mp2t/mp2t_stream_parser_unittest.cc +++ b/media/formats/mp2t/mp2t_stream_parser_unittest.cc @@ -37,7 +37,7 @@ bool IsMonotonic(const StreamParser::BufferQueue& buffers) { return true; } -bool IsAlmostEqual(base::TimeDelta t0, base::TimeDelta t1) { +bool IsAlmostEqual(DecodeTimestamp t0, DecodeTimestamp t1) { base::TimeDelta kMaxDeviation = base::TimeDelta::FromMilliseconds(5); base::TimeDelta diff = t1 - t0; return (diff >= -kMaxDeviation && diff <= kMaxDeviation); @@ -52,10 +52,10 @@ class Mp2tStreamParserTest : public testing::Test { config_count_(0), audio_frame_count_(0), video_frame_count_(0), - audio_min_dts_(kNoTimestamp()), - audio_max_dts_(kNoTimestamp()), - video_min_dts_(kNoTimestamp()), - video_max_dts_(kNoTimestamp()) { + audio_min_dts_(kNoDecodeTimestamp()), + audio_max_dts_(kNoDecodeTimestamp()), + video_min_dts_(kNoDecodeTimestamp()), + video_max_dts_(kNoDecodeTimestamp()) { bool has_sbr = false; parser_.reset(new Mp2tStreamParser(has_sbr)); } @@ -66,20 +66,20 @@ class Mp2tStreamParserTest : public testing::Test { int config_count_; int audio_frame_count_; int video_frame_count_; - base::TimeDelta audio_min_dts_; - base::TimeDelta audio_max_dts_; - base::TimeDelta video_min_dts_; - base::TimeDelta video_max_dts_; + DecodeTimestamp audio_min_dts_; + DecodeTimestamp audio_max_dts_; + DecodeTimestamp video_min_dts_; + DecodeTimestamp video_max_dts_; void ResetStats() { segment_count_ = 0; config_count_ = 0; audio_frame_count_ = 0; video_frame_count_ = 0; - audio_min_dts_ = kNoTimestamp(); - audio_max_dts_ = kNoTimestamp(); - video_min_dts_ = kNoTimestamp(); - video_max_dts_ = kNoTimestamp(); + audio_min_dts_ = kNoDecodeTimestamp(); + audio_max_dts_ = kNoDecodeTimestamp(); + video_min_dts_ = kNoDecodeTimestamp(); + video_max_dts_ = kNoDecodeTimestamp(); } bool AppendData(const uint8* data, size_t length) { @@ -149,20 +149,20 @@ class Mp2tStreamParserTest : public testing::Test { return false; if (!video_buffers.empty()) { - base::TimeDelta first_dts = video_buffers.front()->GetDecodeTimestamp(); - base::TimeDelta last_dts = video_buffers.back()->GetDecodeTimestamp(); - if (video_max_dts_ != kNoTimestamp() && first_dts < video_max_dts_) + DecodeTimestamp first_dts = video_buffers.front()->GetDecodeTimestamp(); + DecodeTimestamp last_dts = video_buffers.back()->GetDecodeTimestamp(); + if (video_max_dts_ != kNoDecodeTimestamp() && first_dts < video_max_dts_) return false; - if (video_min_dts_ == kNoTimestamp()) + if (video_min_dts_ == kNoDecodeTimestamp()) video_min_dts_ = first_dts; video_max_dts_ = last_dts; } if (!audio_buffers.empty()) { - base::TimeDelta first_dts = audio_buffers.front()->GetDecodeTimestamp(); - base::TimeDelta last_dts = audio_buffers.back()->GetDecodeTimestamp(); - if (audio_max_dts_ != kNoTimestamp() && first_dts < audio_max_dts_) + DecodeTimestamp first_dts = audio_buffers.front()->GetDecodeTimestamp(); + DecodeTimestamp last_dts = audio_buffers.back()->GetDecodeTimestamp(); + if (audio_max_dts_ != kNoDecodeTimestamp() && first_dts < audio_max_dts_) return false; - if (audio_min_dts_ == kNoTimestamp()) + if (audio_min_dts_ == kNoDecodeTimestamp()) audio_min_dts_ = first_dts; audio_max_dts_ = last_dts; } @@ -262,9 +262,9 @@ TEST_F(Mp2tStreamParserTest, TimestampWrapAround) { EXPECT_EQ(video_frame_count_, 82); EXPECT_TRUE(IsAlmostEqual(video_min_dts_, - base::TimeDelta::FromSecondsD(95443.376))); + DecodeTimestamp::FromSecondsD(95443.376))); EXPECT_TRUE(IsAlmostEqual(video_max_dts_, - base::TimeDelta::FromSecondsD(95446.079))); + DecodeTimestamp::FromSecondsD(95446.079))); // Note: for audio, AdtsStreamParser considers only the PTS (which is then // used as the DTS). @@ -278,9 +278,9 @@ TEST_F(Mp2tStreamParserTest, TimestampWrapAround) { // So the PTS of the last AAC frame is: // 95445.931 + 8 * (1024 / 44100) = 95446.117 EXPECT_TRUE(IsAlmostEqual(audio_min_dts_, - base::TimeDelta::FromSecondsD(95443.400))); + DecodeTimestamp::FromSecondsD(95443.400))); EXPECT_TRUE(IsAlmostEqual(audio_max_dts_, - base::TimeDelta::FromSecondsD(95446.117))); + DecodeTimestamp::FromSecondsD(95446.117))); } } // namespace mp2t diff --git a/media/formats/mp2t/ts_section_pes.cc b/media/formats/mp2t/ts_section_pes.cc index de69a32e63b33..ff9090945b3b6 100644 --- a/media/formats/mp2t/ts_section_pes.cc +++ b/media/formats/mp2t/ts_section_pes.cc @@ -267,7 +267,7 @@ bool TsSectionPes::ParseInternal(const uint8* raw_pes, int raw_pes_size) { // Convert and unroll the timestamps. base::TimeDelta media_pts(kNoTimestamp()); - base::TimeDelta media_dts(kNoTimestamp()); + DecodeTimestamp media_dts(kNoDecodeTimestamp()); if (is_pts_valid) { int64 pts = ConvertTimestampSectionToTimestamp(pts_section); if (previous_pts_valid_) @@ -282,7 +282,7 @@ bool TsSectionPes::ParseInternal(const uint8* raw_pes, int raw_pes_size) { dts = UnrollTimestamp(previous_dts_, dts); previous_dts_ = dts; previous_dts_valid_ = true; - media_dts = base::TimeDelta::FromMicroseconds((1000 * dts) / 90); + media_dts = DecodeTimestamp::FromMicroseconds((1000 * dts) / 90); } // Discard the rest of the PES packet header. @@ -309,4 +309,3 @@ void TsSectionPes::ResetPesState() { } // namespace mp2t } // namespace media - diff --git a/media/formats/mp4/mp4_stream_parser_unittest.cc b/media/formats/mp4/mp4_stream_parser_unittest.cc index 8805c05c3a75d..c44e0ce96ccb2 100644 --- a/media/formats/mp4/mp4_stream_parser_unittest.cc +++ b/media/formats/mp4/mp4_stream_parser_unittest.cc @@ -32,7 +32,8 @@ class MP4StreamParserTest : public testing::Test { public: MP4StreamParserTest() : configs_received_(false), - lower_bound_(base::TimeDelta::Max()) { + lower_bound_( + DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max())) { std::set audio_object_types; audio_object_types.insert(kISO_14496_3); parser_.reset(new MP4StreamParser(audio_object_types, false)); @@ -41,7 +42,7 @@ class MP4StreamParserTest : public testing::Test { protected: scoped_ptr parser_; bool configs_received_; - base::TimeDelta lower_bound_; + DecodeTimestamp lower_bound_; bool AppendData(const uint8* data, size_t length) { return parser_->Parse(data, length); @@ -99,17 +100,17 @@ class MP4StreamParserTest : public testing::Test { // Find the second highest timestamp so that we know what the // timestamps on the next set of buffers must be >= than. - base::TimeDelta audio = !audio_buffers.empty() ? - audio_buffers.back()->GetDecodeTimestamp() : kNoTimestamp(); - base::TimeDelta video = !video_buffers.empty() ? - video_buffers.back()->GetDecodeTimestamp() : kNoTimestamp(); - base::TimeDelta second_highest_timestamp = - (audio == kNoTimestamp() || - (video != kNoTimestamp() && audio > video)) ? video : audio; + DecodeTimestamp audio = !audio_buffers.empty() ? + audio_buffers.back()->GetDecodeTimestamp() : kNoDecodeTimestamp(); + DecodeTimestamp video = !video_buffers.empty() ? + video_buffers.back()->GetDecodeTimestamp() : kNoDecodeTimestamp(); + DecodeTimestamp second_highest_timestamp = + (audio == kNoDecodeTimestamp() || + (video != kNoDecodeTimestamp() && audio > video)) ? video : audio; - DCHECK(second_highest_timestamp != kNoTimestamp()); + DCHECK(second_highest_timestamp != kNoDecodeTimestamp()); - if (lower_bound_ != kNoTimestamp() && + if (lower_bound_ != kNoDecodeTimestamp() && second_highest_timestamp < lower_bound_) { return false; } @@ -127,12 +128,13 @@ class MP4StreamParserTest : public testing::Test { void NewSegmentF() { DVLOG(1) << "NewSegmentF"; - lower_bound_ = kNoTimestamp(); + lower_bound_ = kNoDecodeTimestamp(); } void EndOfSegmentF() { DVLOG(1) << "EndOfSegmentF()"; - lower_bound_ = base::TimeDelta::Max(); + lower_bound_ = + DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max()); } void InitializeParser() { diff --git a/media/formats/mp4/track_run_iterator.cc b/media/formats/mp4/track_run_iterator.cc index f3dc83010123e..52959f1c73855 100644 --- a/media/formats/mp4/track_run_iterator.cc +++ b/media/formats/mp4/track_run_iterator.cc @@ -7,7 +7,6 @@ #include #include "media/base/buffers.h" -#include "media/base/stream_parser_buffer.h" #include "media/formats/mp4/rcheck.h" #include "media/formats/mp4/sample_to_group_iterator.h" @@ -57,7 +56,7 @@ TrackRunInfo::TrackRunInfo() } TrackRunInfo::~TrackRunInfo() {} -TimeDelta TimeDeltaFromRational(int64 numer, int64 denom) { +base::TimeDelta TimeDeltaFromRational(int64 numer, int64 denom) { // To avoid overflow, split the following calculation: // (numer * base::Time::kMicrosecondsPerSecond) / denom // into: @@ -73,7 +72,12 @@ TimeDelta TimeDeltaFromRational(int64 numer, int64 denom) { DCHECK((timeb_in_us < 0) || (timea_in_us <= kint64max - timeb_in_us)); DCHECK((timeb_in_us > 0) || (timea_in_us >= kint64min - timeb_in_us)); - return TimeDelta::FromMicroseconds(timea_in_us + timeb_in_us); + return base::TimeDelta::FromMicroseconds(timea_in_us + timeb_in_us); +} + +DecodeTimestamp DecodeTimestampFromRational(int64 numer, int64 denom) { + return DecodeTimestamp::FromPresentationTime( + TimeDeltaFromRational(numer, denom)); } TrackRunIterator::TrackRunIterator(const Movie* moov, @@ -469,18 +473,18 @@ int TrackRunIterator::sample_size() const { return sample_itr_->size; } -TimeDelta TrackRunIterator::dts() const { +DecodeTimestamp TrackRunIterator::dts() const { DCHECK(IsSampleValid()); - return TimeDeltaFromRational(sample_dts_, run_itr_->timescale); + return DecodeTimestampFromRational(sample_dts_, run_itr_->timescale); } -TimeDelta TrackRunIterator::cts() const { +base::TimeDelta TrackRunIterator::cts() const { DCHECK(IsSampleValid()); return TimeDeltaFromRational(sample_dts_ + sample_itr_->cts_offset, run_itr_->timescale); } -TimeDelta TrackRunIterator::duration() const { +base::TimeDelta TrackRunIterator::duration() const { DCHECK(IsSampleValid()); return TimeDeltaFromRational(sample_itr_->duration, run_itr_->timescale); } diff --git a/media/formats/mp4/track_run_iterator.h b/media/formats/mp4/track_run_iterator.h index fb53927c9ceae..b5009678b8bdc 100644 --- a/media/formats/mp4/track_run_iterator.h +++ b/media/formats/mp4/track_run_iterator.h @@ -11,6 +11,7 @@ #include "base/time/time.h" #include "media/base/media_export.h" #include "media/base/media_log.h" +#include "media/base/stream_parser_buffer.h" #include "media/formats/mp4/box_definitions.h" #include "media/formats/mp4/cenc.h" @@ -20,8 +21,9 @@ class DecryptConfig; namespace mp4 { -using base::TimeDelta; base::TimeDelta MEDIA_EXPORT TimeDeltaFromRational(int64 numer, int64 denom); +DecodeTimestamp MEDIA_EXPORT DecodeTimestampFromRational(int64 numer, + int64 denom); struct SampleInfo; struct TrackRunInfo; @@ -74,9 +76,9 @@ class MEDIA_EXPORT TrackRunIterator { // Properties of the current sample. Only valid if IsSampleValid(). int64 sample_offset() const; int sample_size() const; - TimeDelta dts() const; - TimeDelta cts() const; - TimeDelta duration() const; + DecodeTimestamp dts() const; + base::TimeDelta cts() const; + base::TimeDelta duration() const; bool is_keyframe() const; bool is_random_access_point() const; diff --git a/media/formats/mp4/track_run_iterator_unittest.cc b/media/formats/mp4/track_run_iterator_unittest.cc index baaa010de72f6..9caba620d3960 100644 --- a/media/formats/mp4/track_run_iterator_unittest.cc +++ b/media/formats/mp4/track_run_iterator_unittest.cc @@ -291,7 +291,7 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) { EXPECT_EQ(iter_->track_id(), 1u); EXPECT_EQ(iter_->sample_offset(), 100); EXPECT_EQ(iter_->sample_size(), 1); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kAudioScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(0, kAudioScale)); EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kAudioScale)); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale)); EXPECT_TRUE(iter_->is_keyframe()); @@ -301,7 +301,7 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) { EXPECT_EQ(iter_->track_id(), 1u); EXPECT_EQ(iter_->sample_offset(), 100 + kSumAscending1); EXPECT_EQ(iter_->sample_size(), 10); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 9, kAudioScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(1024 * 9, kAudioScale)); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1024, kAudioScale)); EXPECT_TRUE(iter_->is_keyframe()); @@ -317,14 +317,14 @@ TEST_F(TrackRunIteratorTest, BasicOperationTest) { EXPECT_EQ(iter_->sample_offset(), 200 + kSumAscending1); EXPECT_EQ(iter_->sample_size(), 10); int64 base_dts = kSumAscending1 + moof.tracks[1].decode_time.decode_time; - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(base_dts, kVideoScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(base_dts, kVideoScale)); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(10, kVideoScale)); EXPECT_FALSE(iter_->is_keyframe()); // Test final run iter_->AdvanceRun(); EXPECT_EQ(iter_->track_id(), 1u); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1024 * 10, kAudioScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(1024 * 10, kAudioScale)); iter_->AdvanceSample(); EXPECT_EQ(moof.tracks[0].runs[1].data_offset + moof.tracks[0].header.default_sample_size, @@ -349,7 +349,7 @@ TEST_F(TrackRunIteratorTest, TrackExtendsDefaultsTest) { EXPECT_EQ(iter_->sample_size(), 3); EXPECT_EQ(iter_->sample_offset(), moof.tracks[0].runs[0].data_offset + 3); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(50, kAudioScale)); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(50, kAudioScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(50, kAudioScale)); } TEST_F(TrackRunIteratorTest, FirstSampleFlagTest) { @@ -408,15 +408,15 @@ TEST_F(TrackRunIteratorTest, ReorderingTest) { ASSERT_TRUE(iter_->Init(moof)); iter_->AdvanceRun(); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(0, kVideoScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(0, kVideoScale)); EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(0, kVideoScale)); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(1, kVideoScale)); iter_->AdvanceSample(); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(1, kVideoScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(1, kVideoScale)); EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(4, kVideoScale)); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(2, kVideoScale)); iter_->AdvanceSample(); - EXPECT_EQ(iter_->dts(), TimeDeltaFromRational(3, kVideoScale)); + EXPECT_EQ(iter_->dts(), DecodeTimestampFromRational(3, kVideoScale)); EXPECT_EQ(iter_->cts(), TimeDeltaFromRational(1, kVideoScale)); EXPECT_EQ(iter_->duration(), TimeDeltaFromRational(3, kVideoScale)); } diff --git a/media/formats/webm/webm_cluster_parser.cc b/media/formats/webm/webm_cluster_parser.cc index 172eafafcf203..e563a8ef5053e 100644 --- a/media/formats/webm/webm_cluster_parser.cc +++ b/media/formats/webm/webm_cluster_parser.cc @@ -44,7 +44,7 @@ WebMClusterParser::WebMClusterParser( cluster_ended_(false), audio_(audio_track_num, false, audio_default_duration, log_cb), video_(video_track_num, true, video_default_duration, log_cb), - ready_buffer_upper_bound_(kNoTimestamp()), + ready_buffer_upper_bound_(kNoDecodeTimestamp()), log_cb_(log_cb) { for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin(); it != text_tracks.end(); @@ -65,14 +65,14 @@ void WebMClusterParser::Reset() { audio_.Reset(); video_.Reset(); ResetTextTracks(); - ready_buffer_upper_bound_ = kNoTimestamp(); + ready_buffer_upper_bound_ = kNoDecodeTimestamp(); } int WebMClusterParser::Parse(const uint8* buf, int size) { audio_.ClearReadyBuffers(); video_.ClearReadyBuffers(); ClearTextTrackReadyBuffers(); - ready_buffer_upper_bound_ = kNoTimestamp(); + ready_buffer_upper_bound_ = kNoDecodeTimestamp(); int result = parser_.Parse(buf, size); @@ -108,14 +108,14 @@ int WebMClusterParser::Parse(const uint8* buf, int size) { } const WebMClusterParser::BufferQueue& WebMClusterParser::GetAudioBuffers() { - if (ready_buffer_upper_bound_ == kNoTimestamp()) + if (ready_buffer_upper_bound_ == kNoDecodeTimestamp()) UpdateReadyBuffers(); return audio_.ready_buffers(); } const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { - if (ready_buffer_upper_bound_ == kNoTimestamp()) + if (ready_buffer_upper_bound_ == kNoDecodeTimestamp()) UpdateReadyBuffers(); return video_.ready_buffers(); @@ -123,7 +123,7 @@ const WebMClusterParser::BufferQueue& WebMClusterParser::GetVideoBuffers() { const WebMClusterParser::TextBufferQueueMap& WebMClusterParser::GetTextBuffers() { - if (ready_buffer_upper_bound_ == kNoTimestamp()) + if (ready_buffer_upper_bound_ == kNoDecodeTimestamp()) UpdateReadyBuffers(); // Translate our |text_track_map_| into |text_buffers_map_|, inserting rows in @@ -439,19 +439,19 @@ WebMClusterParser::Track::Track(int track_num, WebMClusterParser::Track::~Track() {} -base::TimeDelta WebMClusterParser::Track::GetReadyUpperBound() { +DecodeTimestamp WebMClusterParser::Track::GetReadyUpperBound() { DCHECK(ready_buffers_.empty()); if (last_added_buffer_missing_duration_) return last_added_buffer_missing_duration_->GetDecodeTimestamp(); - return kInfiniteDuration(); + return DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max()); } void WebMClusterParser::Track::ExtractReadyBuffers( - const base::TimeDelta before_timestamp) { + const DecodeTimestamp before_timestamp) { DCHECK(ready_buffers_.empty()); - DCHECK(base::TimeDelta() <= before_timestamp); - DCHECK(kNoTimestamp() != before_timestamp); + DCHECK(DecodeTimestamp() <= before_timestamp); + DCHECK(kNoDecodeTimestamp() != before_timestamp); if (buffers_.empty()) return; @@ -579,8 +579,8 @@ bool WebMClusterParser::Track::QueueBuffer( // WebMClusterParser::OnBlock() gives MEDIA_LOG and parse error on decreasing // block timecode detection within a cluster. Therefore, we should not see // those here. - base::TimeDelta previous_buffers_timestamp = buffers_.empty() ? - base::TimeDelta() : buffers_.back()->GetDecodeTimestamp(); + DecodeTimestamp previous_buffers_timestamp = buffers_.empty() ? + DecodeTimestamp() : buffers_.back()->GetDecodeTimestamp(); CHECK(previous_buffers_timestamp <= buffer->GetDecodeTimestamp()); base::TimeDelta duration = buffer->duration(); @@ -644,7 +644,7 @@ void WebMClusterParser::ResetTextTracks() { } void WebMClusterParser::UpdateReadyBuffers() { - DCHECK(ready_buffer_upper_bound_ == kNoTimestamp()); + DCHECK(ready_buffer_upper_bound_ == kNoDecodeTimestamp()); DCHECK(text_buffers_map_.empty()); if (cluster_ended_) { @@ -653,14 +653,15 @@ void WebMClusterParser::UpdateReadyBuffers() { // Per OnBlock(), all text buffers should already have valid durations, so // there is no need to call ApplyDurationEstimateIfNeeded() on text tracks // here. - ready_buffer_upper_bound_ = kInfiniteDuration(); + ready_buffer_upper_bound_ = + DecodeTimestamp::FromPresentationTime(base::TimeDelta::Max()); DCHECK(ready_buffer_upper_bound_ == audio_.GetReadyUpperBound()); DCHECK(ready_buffer_upper_bound_ == video_.GetReadyUpperBound()); } else { ready_buffer_upper_bound_ = std::min(audio_.GetReadyUpperBound(), video_.GetReadyUpperBound()); - DCHECK(base::TimeDelta() <= ready_buffer_upper_bound_); - DCHECK(kNoTimestamp() != ready_buffer_upper_bound_); + DCHECK(DecodeTimestamp() <= ready_buffer_upper_bound_); + DCHECK(kNoDecodeTimestamp() != ready_buffer_upper_bound_); } // Prepare each track's ready buffers for retrieval. diff --git a/media/formats/webm/webm_cluster_parser.h b/media/formats/webm/webm_cluster_parser.h index ab1d4a11fb115..1f18205055060 100644 --- a/media/formats/webm/webm_cluster_parser.h +++ b/media/formats/webm/webm_cluster_parser.h @@ -49,12 +49,13 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { // If a buffer is currently held aside pending duration calculation, returns // its decode timestamp. Otherwise, returns kInfiniteDuration(). - base::TimeDelta GetReadyUpperBound(); + DecodeTimestamp GetReadyUpperBound(); // Prepares |ready_buffers_| for retrieval. Prior to calling, - // |ready_buffers_| must be empty. Moves all |buffers_| with timestamp - // before |before_timestamp| to |ready_buffers_|, preserving their order. - void ExtractReadyBuffers(const base::TimeDelta before_timestamp); + // |ready_buffers_| must be empty. Moves all |buffers_| with decode + // timestamp before |before_timestamp| to |ready_buffers_|, preserving their + // order. + void ExtractReadyBuffers(const DecodeTimestamp before_timestamp); const BufferQueue& ready_buffers() const { return ready_buffers_; } @@ -219,7 +220,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { // bound.) // Parse() or Reset() must be called between calls to UpdateReadyBuffers() to // clear each track's ready buffers and to reset |ready_buffer_upper_bound_| - // to kNoTimestamp(). + // to kNoDecodeTimestamp(). void UpdateReadyBuffers(); // Search for the indicated track_num among the text tracks. Returns NULL @@ -258,12 +259,12 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient { TextBufferQueueMap text_buffers_map_; // Limits the range of buffers returned by Get{Audio,Video,Text}Buffers() to - // this exclusive upper bound. Set to kNoTimestamp(), meaning not yet - // calculated, by Reset() and Parse(). If kNoTimestamp(), then + // this exclusive upper bound. Set to kNoDecodeTimestamp(), meaning not yet + // calculated, by Reset() and Parse(). If kNoDecodeTimestamp(), then // Get{Audio,Video,Text}Buffers() will calculate it to be the minimum (decode) // timestamp across all tracks' |last_buffer_missing_duration_|, or // kInfiniteDuration() if no buffers are currently missing duration. - base::TimeDelta ready_buffer_upper_bound_; + DecodeTimestamp ready_buffer_upper_bound_; LogCB log_cb_; diff --git a/media/formats/webm/webm_video_client.cc b/media/formats/webm/webm_video_client.cc index bda78efafac22..592475f092c99 100644 --- a/media/formats/webm/webm_video_client.cc +++ b/media/formats/webm/webm_video_client.cc @@ -39,10 +39,10 @@ bool WebMVideoClient::InitializeConfig( VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN; if (codec_id == "V_VP8") { video_codec = kCodecVP8; - profile = VP8PROFILE_MAIN; + profile = VP8PROFILE_ANY; } else if (codec_id == "V_VP9") { video_codec = kCodecVP9; - profile = VP9PROFILE_MAIN; + profile = VP9PROFILE_ANY; } else { MEDIA_LOG(log_cb_) << "Unsupported video codec_id " << codec_id; return false; diff --git a/media/media.gyp b/media/media.gyp index 0aa1719884152..027cb55f0959c 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -12,12 +12,11 @@ # detection of ABI mismatches and prevents silent errors. 'linux_link_pulseaudio%': 0, 'conditions': [ - ['OS=="android"', { - # Android doesn't use ffmpeg. + ['OS=="android" or OS=="ios"', { + # Android and iOS don't use ffmpeg or libvpx. 'media_use_ffmpeg%': 0, - # Android doesn't use libvpx. 'media_use_libvpx%': 0, - }, { # 'OS!="android"' + }, { # 'OS!="android" and OS!="ios"' 'media_use_ffmpeg%': 1, 'media_use_libvpx%': 1, }], @@ -57,6 +56,7 @@ '../crypto/crypto.gyp:crypto', '../gpu/gpu.gyp:command_buffer_common', '../skia/skia.gyp:skia', + '../third_party/libyuv/libyuv.gyp:libyuv', '../third_party/opus/opus.gyp:opus', '../ui/events/events.gyp:events_base', '../ui/gfx/gfx.gyp:gfx', @@ -657,11 +657,6 @@ 'filters/h264_bitstream_buffer.h', ], }], - ['OS!="ios"', { - 'dependencies': [ - '../third_party/libyuv/libyuv.gyp:libyuv', - ], - }], ['use_alsa==1', { 'link_settings': { 'libraries': [ @@ -1253,7 +1248,10 @@ 'formats/common/stream_parser_test_base.cc', 'formats/common/stream_parser_test_base.h', 'formats/mp2t/es_adapter_video_unittest.cc', + 'formats/mp2t/es_parser_adts_unittest.cc', 'formats/mp2t/es_parser_h264_unittest.cc', + 'formats/mp2t/es_parser_test_base.cc', + 'formats/mp2t/es_parser_test_base.h', 'formats/mp2t/mp2t_stream_parser_unittest.cc', 'formats/mp4/aac_unittest.cc', 'formats/mp4/avc_unittest.cc', @@ -1442,7 +1440,7 @@ 'yasm_flags': ['-DARCH_X86_64'], }, }], - ['OS=="mac"', { + ['OS=="mac" or OS=="ios"', { 'variables': { 'yasm_flags': [ '-DPREFIX', diff --git a/media/media_options.gni b/media/media_options.gni index 23decabcf905e..33bcba02d81ef 100644 --- a/media/media_options.gni +++ b/media/media_options.gni @@ -17,8 +17,8 @@ linux_link_pulseaudio = false # TODO(ajwong): Enable libvpx once that's converted. media_use_ffmpeg = true media_use_libvpx = false -if (is_android) { - # Android doesn't use ffmpeg or libvpx. +if (is_android || is_ios) { + # Android and iOS don't use ffmpeg or libvpx. media_use_ffmpeg = false media_use_libvpx = false } diff --git a/media/video/capture/mac/avfoundation_glue.mm b/media/video/capture/mac/avfoundation_glue.mm index 551f759df5967..f3496d1cf9d39 100644 --- a/media/video/capture/mac/avfoundation_glue.mm +++ b/media/video/capture/mac/avfoundation_glue.mm @@ -120,9 +120,6 @@ return false; // Next in precedence is the enable-avfoundation flag. - // TODO(mcasas,vrk): There should be 3 states of AVFoundation: user forced on, - // user forced off (i.e. force QTKit), and default (respect field trial). - // crbug.com/396764 // TODO(vrk): Does this really need to be static? static bool should_enable_avfoundation = command_line->HasSwitch(switches::kEnableAVFoundation) || diff --git a/media/video/capture/mac/platform_video_capturing_mac.h b/media/video/capture/mac/platform_video_capturing_mac.h index 466ae1bc8fd39..29bc49715c56c 100644 --- a/media/video/capture/mac/platform_video_capturing_mac.h +++ b/media/video/capture/mac/platform_video_capturing_mac.h @@ -36,7 +36,9 @@ class VideoCaptureDeviceMac; - (BOOL)setCaptureDevice:(NSString*)deviceId; // Configures the capture properties. -- (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate; +- (BOOL)setCaptureHeight:(int)height + width:(int)width + frameRate:(float)frameRate; // Start video capturing, register observers. Returns YES on sucess, NO // otherwise. diff --git a/media/video/capture/mac/video_capture_device_avfoundation_mac.h b/media/video/capture/mac/video_capture_device_avfoundation_mac.h index 1c607b1918b83..d2262aeead175 100644 --- a/media/video/capture/mac/video_capture_device_avfoundation_mac.h +++ b/media/video/capture/mac/video_capture_device_avfoundation_mac.h @@ -62,7 +62,7 @@ class VideoCaptureDeviceMac; // The following attributes are set via -setCaptureHeight:width:frameRate:. int frameWidth_; int frameHeight_; - int frameRate_; + float frameRate_; base::Lock lock_; // Protects concurrent setting and using of frameReceiver_. media::VideoCaptureDeviceMac* frameReceiver_; // weak. @@ -105,7 +105,9 @@ class VideoCaptureDeviceMac; // Configures the capture properties for the capture session and the video data // output; this means it MUST be called after setCaptureDevice:. Return YES on // success, else NO. -- (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate; +- (BOOL)setCaptureHeight:(int)height + width:(int)width + frameRate:(float)frameRate; // Starts video capturing and register the notification listeners. Must be // called after setCaptureDevice:, and, eventually, also after diff --git a/media/video/capture/mac/video_capture_device_avfoundation_mac.mm b/media/video/capture/mac/video_capture_device_avfoundation_mac.mm index 399ad7d9387ba..c700ca20a5676 100644 --- a/media/video/capture/mac/video_capture_device_avfoundation_mac.mm +++ b/media/video/capture/mac/video_capture_device_avfoundation_mac.mm @@ -171,7 +171,9 @@ - (BOOL)setCaptureDevice:(NSString*)deviceId { return YES; } -- (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { +- (BOOL)setCaptureHeight:(int)height + width:(int)width + frameRate:(float)frameRate { // Check if either of VideoCaptureDeviceMac::AllocateAndStart() or // VideoCaptureDeviceMac::ReceiveFrame() is calling here, depending on the // running state. VCDM::ReceiveFrame() calls here to change aspect ratio. @@ -208,14 +210,14 @@ - (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { [captureConnection isVideoMinFrameDurationSupported]) { [captureConnection setVideoMinFrameDuration: CoreMediaGlue::CMTimeMake(media::kFrameRatePrecision, - frameRate * media::kFrameRatePrecision)]; + (int)(frameRate * media::kFrameRatePrecision))]; } if ([captureConnection respondsToSelector:@selector(isVideoMaxFrameDurationSupported)] && [captureConnection isVideoMaxFrameDurationSupported]) { [captureConnection setVideoMaxFrameDuration: CoreMediaGlue::CMTimeMake(media::kFrameRatePrecision, - frameRate * media::kFrameRatePrecision)]; + (int)(frameRate * media::kFrameRatePrecision))]; } return YES; } diff --git a/media/video/capture/mac/video_capture_device_factory_mac.mm b/media/video/capture/mac/video_capture_device_factory_mac.mm index af97fd21e03cf..f0efe6b760b53 100644 --- a/media/video/capture/mac/video_capture_device_factory_mac.mm +++ b/media/video/capture/mac/video_capture_device_factory_mac.mm @@ -20,10 +20,16 @@ // Some devices are not correctly supported in AVFoundation, f.i. Blackmagic, // see http://crbug.com/347371. The devices are identified by a characteristic // trailing substring of uniqueId and by (part of) the vendor's name. +// Blackmagic cameras are known to crash if VGA is requested , see +// http://crbug.com/396812; for them HD is the only supported resolution. const struct NameAndVid { const char* unique_id_signature; const char* name; -} kBlacklistedCameras[] = { { "-01FDA82C8A9C", "Blackmagic" } }; + const int capture_width; + const int capture_height; + const float capture_frame_rate; +} kBlacklistedCameras[] = { + { "-01FDA82C8A9C", "Blackmagic", 1280, 720, 60.0f } }; static scoped_ptr EnumerateDevicesUsingQTKit() { @@ -36,6 +42,13 @@ VideoCaptureDevice::Name name( [[[capture_devices valueForKey:key] deviceName] UTF8String], [key UTF8String], VideoCaptureDevice::Name::QTKIT); + for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) { + if (name.id().find(kBlacklistedCameras[i].name) != std::string::npos) { + DVLOG(2) << "Found blacklisted camera: " << name.id(); + name.set_is_blacklisted(true); + break; + } + } device_names->push_back(name); } return device_names.Pass(); @@ -175,7 +188,21 @@ static void RunDevicesEnumeratedCallback( [VideoCaptureDeviceAVFoundation getDevice:device supportedFormats:supported_formats]; } else { - NOTIMPLEMENTED(); + // Blacklisted cameras provide their own supported format(s), otherwise no + // such information is provided for QTKit. + if (device.is_blacklisted()) { + for (size_t i = 0; i < arraysize(kBlacklistedCameras); ++i) { + if (device.id().find(kBlacklistedCameras[i].name) != + std::string::npos) { + supported_formats->push_back(media::VideoCaptureFormat( + gfx::Size(kBlacklistedCameras[i].capture_width, + kBlacklistedCameras[i].capture_height), + kBlacklistedCameras[i].capture_frame_rate, + media::PIXEL_FORMAT_UYVY)); + break; + } + } + } } } diff --git a/media/video/capture/mac/video_capture_device_mac.mm b/media/video/capture/mac/video_capture_device_mac.mm index b13496c8058bb..2c1943b0b9a6b 100644 --- a/media/video/capture/mac/video_capture_device_mac.mm +++ b/media/video/capture/mac/video_capture_device_mac.mm @@ -43,8 +43,9 @@ - (int32_t)transportType { namespace media { -const int kMinFrameRate = 1; -const int kMaxFrameRate = 30; +// Mac specific limits for minimum and maximum frame rate. +const float kMinFrameRate = 1.0f; +const float kMaxFrameRate = 30.0f; // In device identifiers, the USB VID and PID are stored in 4 bytes each. const size_t kVidPidSize = 4; @@ -350,7 +351,9 @@ static void SetAntiFlickerInUsbDevice(const int vendor_id, state_(kNotInitialized), capture_device_(nil), weak_factory_(this) { - final_resolution_selected_ = AVFoundationGlue::IsAVFoundationSupported(); + // Avoid reconfiguring AVFoundation or blacklisted devices. + final_resolution_selected_ = AVFoundationGlue::IsAVFoundationSupported() || + device_name.is_blacklisted(); } VideoCaptureDeviceMac::~VideoCaptureDeviceMac() { @@ -569,13 +572,13 @@ static void SetAntiFlickerInUsbDevice(const int vendor_id, } bool VideoCaptureDeviceMac::UpdateCaptureResolution() { - if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height() - width:capture_format_.frame_size.width() - frameRate:capture_format_.frame_rate]) { - ReceiveError("Could not configure capture device."); - return false; - } - return true; + if (![capture_device_ setCaptureHeight:capture_format_.frame_size.height() + width:capture_format_.frame_size.width() + frameRate:capture_format_.frame_rate]) { + ReceiveError("Could not configure capture device."); + return false; + } + return true; } } // namespace media diff --git a/media/video/capture/mac/video_capture_device_qtkit_mac.h b/media/video/capture/mac/video_capture_device_qtkit_mac.h index 1ed511b54cece..c1af697848818 100644 --- a/media/video/capture/mac/video_capture_device_qtkit_mac.h +++ b/media/video/capture/mac/video_capture_device_qtkit_mac.h @@ -24,7 +24,7 @@ class VideoCaptureDeviceMac; @interface VideoCaptureDeviceQTKit : NSObject { @private // Settings. - int frameRate_; + float frameRate_; NSLock *lock_; media::VideoCaptureDeviceMac *frameReceiver_; @@ -58,7 +58,9 @@ class VideoCaptureDeviceMac; - (BOOL)setCaptureDevice:(NSString*)deviceId; // Configures the capture properties. -- (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate; +- (BOOL)setCaptureHeight:(int)height + width:(int)width + frameRate:(float)frameRate; // Start video capturing. Returns YES on sucess, NO otherwise. - (BOOL)startCapture; diff --git a/media/video/capture/mac/video_capture_device_qtkit_mac.mm b/media/video/capture/mac/video_capture_device_qtkit_mac.mm index 599cfac59eefb..7f4a853c63a9b 100644 --- a/media/video/capture/mac/video_capture_device_qtkit_mac.mm +++ b/media/video/capture/mac/video_capture_device_qtkit_mac.mm @@ -174,7 +174,9 @@ - (BOOL)setCaptureDevice:(NSString*)deviceId { } } -- (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { +- (BOOL)setCaptureHeight:(int)height + width:(int)width + frameRate:(float)frameRate { if (!captureDeviceInput_) { [self sendErrorString:[NSString stringWithUTF8String:"No video capture device set."]]; @@ -185,7 +187,7 @@ - (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { stringWithUTF8String:"Video capture capabilities already set."]]; return NO; } - if (frameRate <= 0) { + if (frameRate <= 0.0f) { [self sendErrorString:[NSString stringWithUTF8String: "Wrong frame rate."]]; return NO; } @@ -205,7 +207,7 @@ - (BOOL)setCaptureHeight:(int)height width:(int)width frameRate:(int)frameRate { }; [output setPixelBufferAttributes:videoSettingsDictionary]; - [output setMinimumVideoFrameInterval:(NSTimeInterval)1/(float)frameRate]; + [output setMinimumVideoFrameInterval:(NSTimeInterval)1/frameRate]; return YES; } diff --git a/media/video/capture/video_capture_device.cc b/media/video/capture/video_capture_device.cc index 2d8420791fc86..3aa18269f013b 100644 --- a/media/video/capture/video_capture_device.cc +++ b/media/video/capture/video_capture_device.cc @@ -38,7 +38,8 @@ VideoCaptureDevice::Name::Name(const std::string& name, : device_name_(name), unique_id_(id), capture_api_class_(api_type), - transport_type_(OTHER_TRANSPORT) {} + transport_type_(OTHER_TRANSPORT), + is_blacklisted_(false) {} VideoCaptureDevice::Name::Name(const std::string& name, const std::string& id, @@ -47,7 +48,8 @@ VideoCaptureDevice::Name::Name(const std::string& name, : device_name_(name), unique_id_(id), capture_api_class_(api_type), - transport_type_(transport_type) {} + transport_type_(transport_type), + is_blacklisted_(false) {} #endif VideoCaptureDevice::Name::~Name() {} diff --git a/media/video/capture/video_capture_device.h b/media/video/capture/video_capture_device.h index 3f953f8896282..1edecc39c12c7 100644 --- a/media/video/capture/video_capture_device.h +++ b/media/video/capture/video_capture_device.h @@ -109,6 +109,12 @@ class MEDIA_EXPORT VideoCaptureDevice { TransportType transport_type() const { return transport_type_; } + bool is_blacklisted() const { + return is_blacklisted_; + } + void set_is_blacklisted(bool is_blacklisted) { + is_blacklisted_ = is_blacklisted; + } #endif // if defined(OS_WIN) private: @@ -134,6 +140,8 @@ class MEDIA_EXPORT VideoCaptureDevice { #endif #if defined(OS_MACOSX) TransportType transport_type_; + // Flag used to mark blacklisted devices for QTKit Api. + bool is_blacklisted_; #endif // Allow generated copy constructor and assignment. }; diff --git a/media/video/capture/win/video_capture_device_win.cc b/media/video/capture/win/video_capture_device_win.cc index e47c5cbf05fb1..29ccf5afcdb8e 100644 --- a/media/video/capture/win/video_capture_device_win.cc +++ b/media/video/capture/win/video_capture_device_win.cc @@ -304,7 +304,7 @@ void VideoCaptureDeviceWin::AllocateAndStart( int count = 0, size = 0; hr = stream_config->GetNumberOfCapabilities(&count, &size); if (FAILED(hr)) { - DVLOG(2) << "Failed to GetNumberOfCapabilities"; + SetErrorState("Failed to GetNumberOfCapabilities"); return; } diff --git a/media/video/picture.cc b/media/video/picture.cc index 7b32563824e31..f0510131ea6bf 100644 --- a/media/video/picture.cc +++ b/media/video/picture.cc @@ -22,9 +22,12 @@ PictureBuffer::PictureBuffer(int32 id, texture_mailbox_(texture_mailbox) { } -Picture::Picture(int32 picture_buffer_id, int32 bitstream_buffer_id) +Picture::Picture(int32 picture_buffer_id, + int32 bitstream_buffer_id, + const gfx::Rect& visible_rect) : picture_buffer_id_(picture_buffer_id), - bitstream_buffer_id_(bitstream_buffer_id) { + bitstream_buffer_id_(bitstream_buffer_id), + visible_rect_(visible_rect) { } } // namespace media diff --git a/media/video/picture.h b/media/video/picture.h index d5be2276f2be7..844e629ef310f 100644 --- a/media/video/picture.h +++ b/media/video/picture.h @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "gpu/command_buffer/common/mailbox.h" #include "media/base/media_export.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/size.h" namespace media { @@ -54,7 +55,9 @@ class MEDIA_EXPORT PictureBuffer { // This is the media-namespace equivalent of PP_Picture_Dev. class MEDIA_EXPORT Picture { public: - Picture(int32 picture_buffer_id, int32 bitstream_buffer_id); + Picture(int32 picture_buffer_id, + int32 bitstream_buffer_id, + const gfx::Rect& visible_rect); // Returns the id of the picture buffer where this picture is contained. int32 picture_buffer_id() const { @@ -70,9 +73,15 @@ class MEDIA_EXPORT Picture { bitstream_buffer_id_ = bitstream_buffer_id; } + // Returns the visible rectangle of the picture. Its size may be smaller + // than the size of the PictureBuffer, as it is the only visible part of the + // Picture contained in the PictureBuffer. + gfx::Rect visible_rect() const { return visible_rect_; } + private: int32 picture_buffer_id_; int32 bitstream_buffer_id_; + gfx::Rect visible_rect_; }; } // namespace media diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn index 62c6ba82b7713..8813c1e7b4e35 100644 --- a/mojo/BUILD.gn +++ b/mojo/BUILD.gn @@ -8,6 +8,7 @@ group("mojo") { "//mojo/public", "//mojo/services", "//mojo/shell:mojo_shell", + "//mojo/shell:mojo_shell_tests", ] } @@ -18,7 +19,7 @@ if (is_android) { sources = [ "android/javatests/src/org/chromium/mojo/MojoTestCase.java", "android/system/src/org/chromium/mojo/system/impl/CoreImpl.java", - "services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java", + "services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java", ] jni_package = "mojo" diff --git a/mojo/application_manager/BUILD.gn b/mojo/application_manager/BUILD.gn new file mode 100644 index 0000000000000..267fa6824fa3b --- /dev/null +++ b/mojo/application_manager/BUILD.gn @@ -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. + +# GYP version: mojo.gyp:mojo_application_manager +component("application_manager") { + output_name = "mojo_application_manager" + sources = [ + "application_loader.cc", + "application_loader.h", + "application_manager.cc", + "application_manager.h", + "application_manager_export.h", + "background_shell_application_loader.cc", + "background_shell_application_loader.h", + ] + + defines = [ + "MOJO_APPLICATION_MANAGER_IMPLEMENTATION", + ] + + deps = [ + "//base", + "//base/third_party/dynamic_annotations", + "//net", + "//url", + "//mojo/common", + "//mojo/environment:chromium", + "//mojo/public/interfaces/application:application", + "//mojo/services/public/interfaces/content_handler:content_handler", + "//mojo/services/public/interfaces/network:network", + "//mojo/system", + ] + + forward_dependent_configs_from = [ + "//mojo/public/interfaces/application:application", + ] +} diff --git a/mojo/application_manager/application_loader.cc b/mojo/application_manager/application_loader.cc new file mode 100644 index 0000000000000..0c78c3275010c --- /dev/null +++ b/mojo/application_manager/application_loader.cc @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/application_manager/application_loader.h" + +#include "base/logging.h" + +namespace mojo { + +ApplicationLoader::SimpleLoadCallbacks::SimpleLoadCallbacks( + ScopedMessagePipeHandle shell_handle) + : shell_handle_(shell_handle.Pass()) { +} + +ApplicationLoader::SimpleLoadCallbacks::~SimpleLoadCallbacks() { +} + +ScopedMessagePipeHandle +ApplicationLoader::SimpleLoadCallbacks::RegisterApplication() { + return shell_handle_.Pass(); +} + +void ApplicationLoader::SimpleLoadCallbacks::LoadWithContentHandler( + const GURL& content_handle_url, + URLResponsePtr content) { + NOTREACHED(); +} + +} // namespace mojo diff --git a/mojo/application_manager/application_loader.h b/mojo/application_manager/application_loader.h new file mode 100644 index 0000000000000..42f49794d9fc0 --- /dev/null +++ b/mojo/application_manager/application_loader.h @@ -0,0 +1,86 @@ +// 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_APPLICATION_MANAGER_APPLICATION_LOADER_H_ +#define MOJO_APPLICATION_MANAGER_APPLICATION_LOADER_H_ + +#include "base/memory/ref_counted.h" +#include "mojo/application_manager/application_manager_export.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/services/public/interfaces/network/url_loader.mojom.h" +#include "url/gurl.h" + +namespace mojo { + +class ApplicationManager; + +// Interface to allowing loading behavior to be established for schemes, +// specific urls or as the default. +// A ApplicationLoader is responsible to using whatever mechanism is appropriate +// to load the application at url. +// The handle to the shell is passed to that application so it can bind it to +// a Shell instance. This will give the Application a way to connect to other +// apps and services. +class MOJO_APPLICATION_MANAGER_EXPORT ApplicationLoader { + public: + class MOJO_APPLICATION_MANAGER_EXPORT LoadCallbacks + : public base::RefCounted { + public: + // Register the requested application with ApplicationManager. If the + // returned handle is valid, it should be used to implement the + // mojo::Application interface. + virtual ScopedMessagePipeHandle RegisterApplication() = 0; + + // Load the requested application with a content handler. + virtual void LoadWithContentHandler(const GURL& content_handler_url, + URLResponsePtr response) = 0; + + protected: + friend base::RefCounted; + virtual ~LoadCallbacks() {} + }; + + // Implements RegisterApplication() by returning a handle that was specified + // at construction time. LoadWithContentHandler() is not supported. + class MOJO_APPLICATION_MANAGER_EXPORT SimpleLoadCallbacks + : public LoadCallbacks { + public: + SimpleLoadCallbacks(ScopedMessagePipeHandle shell_handle); + virtual ScopedMessagePipeHandle RegisterApplication() OVERRIDE; + virtual void LoadWithContentHandler(const GURL& content_handler_url, + URLResponsePtr response) OVERRIDE; + + private: + ScopedMessagePipeHandle shell_handle_; + virtual ~SimpleLoadCallbacks(); + }; + + virtual ~ApplicationLoader() {} + + // Load the application named |url|. Applications can be loaded two ways: + // + // 1. |url| can refer directly to a Mojo application. In this case, call + // callbacks->RegisterApplication(). The returned handle should be used to + // implement the mojo.Application interface. Note that the returned handle + // can be invalid in the case where the application has already been + // loaded. + // + // 2. |url| can refer to some content that can be handled by some other Mojo + // application. In this case, call callbacks->LoadWithContentHandler() and + // specify the URL of the application that should handle the content. + // The specified application must implement the mojo.ContentHandler + // interface. + virtual void Load(ApplicationManager* application_manager, + const GURL& url, + scoped_refptr callbacks) = 0; + + virtual void OnServiceError(ApplicationManager* manager, const GURL& url) = 0; + + protected: + ApplicationLoader() {} +}; + +} // namespace mojo + +#endif // MOJO_APPLICATION_MANAGER_APPLICATION_LOADER_H_ diff --git a/mojo/application_manager/application_manager.cc b/mojo/application_manager/application_manager.cc new file mode 100644 index 0000000000000..1a75caebba29b --- /dev/null +++ b/mojo/application_manager/application_manager.cc @@ -0,0 +1,302 @@ +// 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/application_manager/application_manager.h" + +#include + +#include "base/bind.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/stl_util.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/common/common_type_converters.h" +#include "mojo/public/cpp/application/connect.h" +#include "mojo/public/interfaces/application/application.mojom.h" +#include "mojo/public/interfaces/application/shell.mojom.h" +#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h" + +namespace mojo { + +namespace { +// Used by TestAPI. +bool has_created_instance = false; + +class StubServiceProvider : public InterfaceImpl { + public: + ServiceProvider* GetRemoteServiceProvider() { return client(); } + + private: + virtual void ConnectToService(const String& service_name, + ScopedMessagePipeHandle client_handle) + MOJO_OVERRIDE {} +}; + +} // namespace + +class ApplicationManager::LoadCallbacksImpl + : public ApplicationLoader::LoadCallbacks { + public: + LoadCallbacksImpl(base::WeakPtr manager, + const GURL& requested_url, + const GURL& requestor_url, + ServiceProviderPtr service_provider) + : manager_(manager), + requested_url_(requested_url), + requestor_url_(requestor_url), + service_provider_(service_provider.Pass()) {} + + private: + virtual ~LoadCallbacksImpl() {} + + // LoadCallbacks implementation + virtual ScopedMessagePipeHandle RegisterApplication() OVERRIDE { + ScopedMessagePipeHandle shell_handle; + if (manager_) { + manager_->RegisterLoadedApplication(requested_url_, + requestor_url_, + service_provider_.Pass(), + &shell_handle); + } + return shell_handle.Pass(); + } + + virtual void LoadWithContentHandler(const GURL& content_handler_url, + URLResponsePtr content) OVERRIDE { + if (manager_) { + manager_->LoadWithContentHandler(requested_url_, + requestor_url_, + content_handler_url, + content.Pass(), + service_provider_.Pass()); + } + } + + base::WeakPtr manager_; + GURL requested_url_; + GURL requestor_url_; + ServiceProviderPtr service_provider_; +}; + +class ApplicationManager::ShellImpl : public InterfaceImpl { + public: + ShellImpl(ApplicationManager* manager, const GURL& url) + : manager_(manager), url_(url) {} + + virtual ~ShellImpl() {} + + void ConnectToClient(const GURL& requestor_url, + ServiceProviderPtr service_provider) { + client()->AcceptConnection(String::From(requestor_url), + service_provider.Pass()); + } + + // ServiceProvider implementation: + virtual void ConnectToApplication( + const String& app_url, + InterfaceRequest in_service_provider) OVERRIDE { + ServiceProviderPtr out_service_provider; + out_service_provider.Bind(in_service_provider.PassMessagePipe()); + manager_->ConnectToApplication( + app_url.To(), url_, out_service_provider.Pass()); + } + + const GURL& url() const { return url_; } + + private: + virtual void OnConnectionError() OVERRIDE { + manager_->OnShellImplError(this); + } + + ApplicationManager* const manager_; + const GURL url_; + + DISALLOW_COPY_AND_ASSIGN(ShellImpl); +}; + +struct ApplicationManager::ContentHandlerConnection { + ContentHandlerConnection(ApplicationManager* manager, + const GURL& content_handler_url) { + ServiceProviderPtr service_provider; + BindToProxy(&service_provider_impl, &service_provider); + manager->ConnectToApplication( + content_handler_url, GURL(), service_provider.Pass()); + mojo::ConnectToService(service_provider_impl.client(), &content_handler); + } + + StubServiceProvider service_provider_impl; + ContentHandlerPtr content_handler; +}; + +// static +ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager) + : manager_(manager) { +} + +ApplicationManager::TestAPI::~TestAPI() { +} + +bool ApplicationManager::TestAPI::HasCreatedInstance() { + return has_created_instance; +} + +bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const { + return manager_->url_to_shell_impl_.find(url) != + manager_->url_to_shell_impl_.end(); +} + +ApplicationManager::ApplicationManager() + : interceptor_(NULL), weak_ptr_factory_(this) { +} + +ApplicationManager::~ApplicationManager() { + STLDeleteValues(&url_to_content_handler_); + TerminateShellConnections(); + STLDeleteValues(&url_to_loader_); + STLDeleteValues(&scheme_to_loader_); +} + +void ApplicationManager::TerminateShellConnections() { + STLDeleteValues(&url_to_shell_impl_); +} + +// static +ApplicationManager* ApplicationManager::GetInstance() { + static base::LazyInstance instance = + LAZY_INSTANCE_INITIALIZER; + has_created_instance = true; + return &instance.Get(); +} + +void ApplicationManager::ConnectToApplication( + const GURL& url, + const GURL& requestor_url, + ServiceProviderPtr service_provider) { + URLToShellImplMap::const_iterator shell_it = url_to_shell_impl_.find(url); + if (shell_it != url_to_shell_impl_.end()) { + ConnectToClient( + shell_it->second, url, requestor_url, service_provider.Pass()); + return; + } + + scoped_refptr callbacks( + new LoadCallbacksImpl(weak_ptr_factory_.GetWeakPtr(), + url, + requestor_url, + service_provider.Pass())); + GetLoaderForURL(url)->Load(this, url, callbacks); +} + +void ApplicationManager::ConnectToClient(ShellImpl* shell_impl, + const GURL& url, + const GURL& requestor_url, + ServiceProviderPtr service_provider) { + if (interceptor_) { + shell_impl->ConnectToClient( + requestor_url, + interceptor_->OnConnectToClient(url, service_provider.Pass())); + } else { + shell_impl->ConnectToClient(requestor_url, service_provider.Pass()); + } +} + +void ApplicationManager::RegisterLoadedApplication( + const GURL& url, + const GURL& requestor_url, + ServiceProviderPtr service_provider, + ScopedMessagePipeHandle* shell_handle) { + ShellImpl* shell_impl = NULL; + URLToShellImplMap::iterator iter = url_to_shell_impl_.find(url); + if (iter != url_to_shell_impl_.end()) { + // This can happen because services are loaded asynchronously. So if we get + // two requests for the same service close to each other, we might get here + // and find that we already have it. + shell_impl = iter->second; + } else { + MessagePipe pipe; + shell_impl = WeakBindToPipe(new ShellImpl(this, url), pipe.handle1.Pass()); + url_to_shell_impl_[url] = shell_impl; + *shell_handle = pipe.handle0.Pass(); + } + + ConnectToClient(shell_impl, url, requestor_url, service_provider.Pass()); +} + +void ApplicationManager::LoadWithContentHandler( + const GURL& content_url, + const GURL& requestor_url, + const GURL& content_handler_url, + URLResponsePtr content, + ServiceProviderPtr service_provider) { + ContentHandlerConnection* connection = NULL; + URLToContentHandlerMap::iterator iter = + url_to_content_handler_.find(content_handler_url); + if (iter != url_to_content_handler_.end()) { + connection = iter->second; + } else { + connection = new ContentHandlerConnection(this, content_handler_url); + url_to_content_handler_[content_handler_url] = connection; + } + connection->content_handler->OnConnect( + content_url.spec(), content.Pass(), service_provider.Pass()); +} + +void ApplicationManager::SetLoaderForURL(scoped_ptr loader, + const GURL& url) { + URLToLoaderMap::iterator it = url_to_loader_.find(url); + if (it != url_to_loader_.end()) + delete it->second; + url_to_loader_[url] = loader.release(); +} + +void ApplicationManager::SetLoaderForScheme( + scoped_ptr loader, + const std::string& scheme) { + SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme); + if (it != scheme_to_loader_.end()) + delete it->second; + scheme_to_loader_[scheme] = loader.release(); +} + +void ApplicationManager::SetInterceptor(Interceptor* interceptor) { + interceptor_ = interceptor; +} + +ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) { + URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url); + if (url_it != url_to_loader_.end()) + return url_it->second; + SchemeToLoaderMap::const_iterator scheme_it = + scheme_to_loader_.find(url.scheme()); + if (scheme_it != scheme_to_loader_.end()) + return scheme_it->second; + return default_loader_.get(); +} + +void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) { + // Called from ~ShellImpl, so we do not need to call Destroy here. + const GURL url = shell_impl->url(); + URLToShellImplMap::iterator it = url_to_shell_impl_.find(url); + DCHECK(it != url_to_shell_impl_.end()); + delete it->second; + url_to_shell_impl_.erase(it); + ApplicationLoader* loader = GetLoaderForURL(url); + if (loader) + loader->OnServiceError(this, url); +} + +ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName( + const GURL& application_url, + const std::string& interface_name) { + StubServiceProvider* stub_sp = new StubServiceProvider; + ServiceProviderPtr spp; + BindToProxy(stub_sp, &spp); + ConnectToApplication(application_url, GURL(), spp.Pass()); + MessagePipe pipe; + stub_sp->GetRemoteServiceProvider()->ConnectToService(interface_name, + pipe.handle1.Pass()); + return pipe.handle0.Pass(); +} +} // namespace mojo diff --git a/mojo/application_manager/application_manager.h b/mojo/application_manager/application_manager.h new file mode 100644 index 0000000000000..bb58c1a8553d7 --- /dev/null +++ b/mojo/application_manager/application_manager.h @@ -0,0 +1,141 @@ +// 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_APPLICATION_MANAGER_APPLICATION_MANAGER_H_ +#define MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_H_ + +#include + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/application_manager/application_manager_export.h" +#include "mojo/public/interfaces/application/service_provider.mojom.h" +#include "url/gurl.h" + +namespace mojo { + +class MOJO_APPLICATION_MANAGER_EXPORT ApplicationManager { + public: + // API for testing. + class MOJO_APPLICATION_MANAGER_EXPORT TestAPI { + public: + explicit TestAPI(ApplicationManager* manager); + ~TestAPI(); + + // Returns true if the shared instance has been created. + static bool HasCreatedInstance(); + // Returns true if there is a ShellImpl for this URL. + bool HasFactoryForURL(const GURL& url) const; + + private: + ApplicationManager* manager_; + + DISALLOW_COPY_AND_ASSIGN(TestAPI); + }; + + // Interface class for debugging only. + class Interceptor { + public: + virtual ~Interceptor() {} + // Called when ApplicationManager::Connect is called. + virtual ServiceProviderPtr OnConnectToClient( + const GURL& url, + ServiceProviderPtr service_provider) = 0; + }; + + ApplicationManager(); + ~ApplicationManager(); + + // Returns a shared instance, creating it if necessary. + static ApplicationManager* GetInstance(); + + // Loads a service if necessary and establishes a new client connection. + void ConnectToApplication(const GURL& application_url, + const GURL& requestor_url, + ServiceProviderPtr service_provider); + + template + inline void ConnectToService(const GURL& application_url, + InterfacePtr* ptr) { + ScopedMessagePipeHandle service_handle = + ConnectToServiceByName(application_url, Interface::Name_); + ptr->Bind(service_handle.Pass()); + } + + ScopedMessagePipeHandle ConnectToServiceByName( + const GURL& application_url, + const std::string& interface_name); + + // Sets the default Loader to be used if not overridden by SetLoaderForURL() + // or SetLoaderForScheme(). + void set_default_loader(scoped_ptr loader) { + default_loader_ = loader.Pass(); + } + // Sets a Loader to be used for a specific url. + void SetLoaderForURL(scoped_ptr loader, const GURL& url); + // Sets a Loader to be used for a specific url scheme. + void SetLoaderForScheme(scoped_ptr loader, + const std::string& scheme); + // Allows to interpose a debugger to service connections. + void SetInterceptor(Interceptor* interceptor); + + // Destroys all Shell-ends of connections established with Applications. + // Applications connected by this ApplicationManager will observe pipe errors + // and have a chance to shutdown. + void TerminateShellConnections(); + + private: + struct ContentHandlerConnection; + class LoadCallbacksImpl; + class ShellImpl; + + typedef std::map SchemeToLoaderMap; + typedef std::map URLToLoaderMap; + typedef std::map URLToShellImplMap; + typedef std::map URLToContentHandlerMap; + + void ConnectToClient(ShellImpl* shell_impl, + const GURL& url, + const GURL& requestor_url, + ServiceProviderPtr service_provider); + + void RegisterLoadedApplication(const GURL& service_url, + const GURL& requestor_url, + ServiceProviderPtr service_provider, + ScopedMessagePipeHandle* shell_handle); + + void LoadWithContentHandler(const GURL& content_url, + const GURL& requestor_url, + const GURL& content_handler_url, + URLResponsePtr content, + ServiceProviderPtr service_provider); + + // Returns the Loader to use for a url (using default if not overridden.) + // The preference is to use a loader that's been specified for an url first, + // then one that's been specified for a scheme, then the default. + ApplicationLoader* GetLoaderForURL(const GURL& url); + + // Removes a ShellImpl when it encounters an error. + void OnShellImplError(ShellImpl* shell_impl); + + // Loader management. + URLToLoaderMap url_to_loader_; + SchemeToLoaderMap scheme_to_loader_; + scoped_ptr default_loader_; + Interceptor* interceptor_; + + URLToShellImplMap url_to_shell_impl_; + URLToContentHandlerMap url_to_content_handler_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ApplicationManager); +}; + +} // namespace mojo + +#endif // MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_H_ diff --git a/mojo/application_manager/application_manager_export.h b/mojo/application_manager/application_manager_export.h new file mode 100644 index 0000000000000..9af43cfffeb36 --- /dev/null +++ b/mojo/application_manager/application_manager_export.h @@ -0,0 +1,32 @@ +// 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_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_ +#define MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_ + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(MOJO_APPLICATION_MANAGER_IMPLEMENTATION) +#define MOJO_APPLICATION_MANAGER_EXPORT __declspec(dllexport) +#else +#define MOJO_APPLICATION_MANAGER_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(MOJO_APPLICATION_MANAGER_IMPLEMENTATION) +#define MOJO_APPLICATION_MANAGER_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_APPLICATION_MANAGER_EXPORT +#endif + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) +#define MOJO_APPLICATION_MANAGER_EXPORT +#endif + +#endif // MOJO_APPLICATION_MANAGER_APPLICATION_MANAGER_EXPORT_H_ diff --git a/mojo/application_manager/application_manager_unittest.cc b/mojo/application_manager/application_manager_unittest.cc new file mode 100644 index 0000000000000..6b00f6092aab6 --- /dev/null +++ b/mojo/application_manager/application_manager_unittest.cc @@ -0,0 +1,643 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/bind.h" +#include "base/message_loop/message_loop.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/application_manager/application_manager.h" +#include "mojo/application_manager/background_shell_application_loader.h" +#include "mojo/application_manager/test.mojom.h" +#include "mojo/public/cpp/application/application_connection.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/application_impl.h" +#include "mojo/public/cpp/application/interface_factory.h" +#include "mojo/public/interfaces/application/service_provider.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace { + +const char kTestURLString[] = "test:testService"; +const char kTestAURLString[] = "test:TestA"; +const char kTestBURLString[] = "test:TestB"; + +struct TestContext { + TestContext() : num_impls(0), num_loader_deletes(0) {} + std::string last_test_string; + int num_impls; + int num_loader_deletes; +}; + +class QuitMessageLoopErrorHandler : public ErrorHandler { + public: + QuitMessageLoopErrorHandler() {} + virtual ~QuitMessageLoopErrorHandler() {} + + // |ErrorHandler| implementation: + virtual void OnConnectionError() OVERRIDE { + base::MessageLoop::current()->QuitWhenIdle(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler); +}; + +class TestServiceImpl : public InterfaceImpl { + public: + explicit TestServiceImpl(TestContext* context) : context_(context) { + ++context_->num_impls; + } + + virtual ~TestServiceImpl() { --context_->num_impls; } + + virtual void OnConnectionError() OVERRIDE { + if (!base::MessageLoop::current()->is_running()) + return; + base::MessageLoop::current()->Quit(); + } + + // TestService implementation: + virtual void Test(const String& test_string) OVERRIDE { + context_->last_test_string = test_string; + client()->AckTest(); + } + + private: + TestContext* context_; +}; + +class TestClientImpl : public TestClient { + public: + explicit TestClientImpl(TestServicePtr service) + : service_(service.Pass()), quit_after_ack_(false) { + service_.set_client(this); + } + + virtual ~TestClientImpl() { service_.reset(); } + + virtual void AckTest() OVERRIDE { + if (quit_after_ack_) + base::MessageLoop::current()->Quit(); + } + + void Test(std::string test_string) { + quit_after_ack_ = true; + service_->Test(test_string); + } + + private: + TestServicePtr service_; + bool quit_after_ack_; + DISALLOW_COPY_AND_ASSIGN(TestClientImpl); +}; + +class TestApplicationLoader : public ApplicationLoader, + public ApplicationDelegate, + public InterfaceFactory { + public: + TestApplicationLoader() : context_(NULL), num_loads_(0) {} + + virtual ~TestApplicationLoader() { + if (context_) + ++context_->num_loader_deletes; + test_app_.reset(NULL); + } + + void set_context(TestContext* context) { context_ = context; } + int num_loads() const { return num_loads_; } + + private: + // ApplicationLoader implementation. + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE { + ++num_loads_; + test_app_.reset( + new ApplicationImpl(this, callbacks->RegisterApplication().Pass())); + } + + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE {} + + // ApplicationDelegate implementation. + virtual bool ConfigureIncomingConnection( + ApplicationConnection* connection) OVERRIDE { + connection->AddService(this); + return true; + } + + // InterfaceFactory implementation. + virtual void Create(ApplicationConnection* connection, + InterfaceRequest request) OVERRIDE { + BindToRequest(new TestServiceImpl(context_), &request); + } + + scoped_ptr test_app_; + TestContext* context_; + int num_loads_; + DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader); +}; + +class TesterContext { + public: + explicit TesterContext(base::MessageLoop* loop) + : num_b_calls_(0), + num_c_calls_(0), + num_a_deletes_(0), + num_b_deletes_(0), + num_c_deletes_(0), + tester_called_quit_(false), + a_called_quit_(false), + loop_(loop) {} + + void IncrementNumBCalls() { + base::AutoLock lock(lock_); + num_b_calls_++; + } + + void IncrementNumCCalls() { + base::AutoLock lock(lock_); + num_c_calls_++; + } + + void IncrementNumADeletes() { + base::AutoLock lock(lock_); + num_a_deletes_++; + } + + void IncrementNumBDeletes() { + base::AutoLock lock(lock_); + num_b_deletes_++; + } + + void IncrementNumCDeletes() { + base::AutoLock lock(lock_); + num_c_deletes_++; + } + + void set_tester_called_quit() { + base::AutoLock lock(lock_); + tester_called_quit_ = true; + } + + void set_a_called_quit() { + base::AutoLock lock(lock_); + a_called_quit_ = true; + } + + int num_b_calls() { + base::AutoLock lock(lock_); + return num_b_calls_; + } + int num_c_calls() { + base::AutoLock lock(lock_); + return num_c_calls_; + } + int num_a_deletes() { + base::AutoLock lock(lock_); + return num_a_deletes_; + } + int num_b_deletes() { + base::AutoLock lock(lock_); + return num_b_deletes_; + } + int num_c_deletes() { + base::AutoLock lock(lock_); + return num_c_deletes_; + } + bool tester_called_quit() { + base::AutoLock lock(lock_); + return tester_called_quit_; + } + bool a_called_quit() { + base::AutoLock lock(lock_); + return a_called_quit_; + } + + void QuitSoon() { + loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); + } + + private: + // lock_ protects all members except for loop_ which must be unchanged for the + // lifetime of this class. + base::Lock lock_; + int num_b_calls_; + int num_c_calls_; + int num_a_deletes_; + int num_b_deletes_; + int num_c_deletes_; + bool tester_called_quit_; + bool a_called_quit_; + + base::MessageLoop* loop_; +}; + +// Used to test that the requestor url will be correctly passed. +class TestAImpl : public InterfaceImpl { + public: + TestAImpl(ApplicationConnection* connection, TesterContext* test_context) + : test_context_(test_context) { + connection->ConnectToApplication(kTestBURLString)->ConnectToService(&b_); + } + virtual ~TestAImpl() { + test_context_->IncrementNumADeletes(); + if (base::MessageLoop::current()->is_running()) + Quit(); + } + + private: + virtual void CallB() OVERRIDE { + b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this))); + } + + virtual void CallCFromB() OVERRIDE { + b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this))); + } + + void Quit() { + base::MessageLoop::current()->Quit(); + test_context_->set_a_called_quit(); + test_context_->QuitSoon(); + } + + TesterContext* test_context_; + TestBPtr b_; +}; + +class TestBImpl : public InterfaceImpl { + public: + TestBImpl(ApplicationConnection* connection, TesterContext* test_context) + : test_context_(test_context) { + connection->ConnectToService(&c_); + } + + virtual ~TestBImpl() { + test_context_->IncrementNumBDeletes(); + if (base::MessageLoop::current()->is_running()) + base::MessageLoop::current()->Quit(); + test_context_->QuitSoon(); + } + + private: + virtual void B(const mojo::Callback& callback) OVERRIDE { + test_context_->IncrementNumBCalls(); + callback.Run(); + } + + virtual void CallC(const mojo::Callback& callback) OVERRIDE { + test_context_->IncrementNumBCalls(); + c_->C(callback); + } + + TesterContext* test_context_; + TestCPtr c_; +}; + +class TestCImpl : public InterfaceImpl { + public: + TestCImpl(ApplicationConnection* connection, TesterContext* test_context) + : test_context_(test_context) {} + + virtual ~TestCImpl() { test_context_->IncrementNumCDeletes(); } + + private: + virtual void C(const mojo::Callback& callback) OVERRIDE { + test_context_->IncrementNumCCalls(); + callback.Run(); + } + TesterContext* test_context_; +}; + +class Tester : public ApplicationDelegate, + public ApplicationLoader, + public InterfaceFactory, + public InterfaceFactory, + public InterfaceFactory { + public: + Tester(TesterContext* context, const std::string& requestor_url) + : context_(context), requestor_url_(requestor_url) {} + virtual ~Tester() {} + + private: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE { + app_.reset( + new ApplicationImpl(this, callbacks->RegisterApplication().Pass())); + } + + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE {} + + virtual bool ConfigureIncomingConnection( + ApplicationConnection* connection) OVERRIDE { + if (!requestor_url_.empty() && + requestor_url_ != connection->GetRemoteApplicationURL()) { + context_->set_tester_called_quit(); + context_->QuitSoon(); + base::MessageLoop::current()->Quit(); + return false; + } + // If we're coming from A, then add B, otherwise A. + if (connection->GetRemoteApplicationURL() == kTestAURLString) + connection->AddService(this); + else + connection->AddService(this); + return true; + } + + virtual bool ConfigureOutgoingConnection( + ApplicationConnection* connection) OVERRIDE { + // If we're connecting to B, then add C. + if (connection->GetRemoteApplicationURL() == kTestBURLString) + connection->AddService(this); + return true; + } + + virtual void Create(ApplicationConnection* connection, + InterfaceRequest request) OVERRIDE { + BindToRequest(new TestAImpl(connection, context_), &request); + } + + virtual void Create(ApplicationConnection* connection, + InterfaceRequest request) OVERRIDE { + BindToRequest(new TestBImpl(connection, context_), &request); + } + + virtual void Create(ApplicationConnection* connection, + InterfaceRequest request) OVERRIDE { + BindToRequest(new TestCImpl(connection, context_), &request); + } + + TesterContext* context_; + scoped_ptr app_; + std::string requestor_url_; +}; + +class TestServiceInterceptor : public ApplicationManager::Interceptor { + public: + TestServiceInterceptor() : call_count_(0) {} + + virtual ServiceProviderPtr OnConnectToClient( + const GURL& url, + ServiceProviderPtr service_provider) OVERRIDE { + ++call_count_; + url_ = url; + return service_provider.Pass(); + } + + std::string url_spec() const { + if (!url_.is_valid()) + return "invalid url"; + return url_.spec(); + } + + int call_count() const { return call_count_; } + + private: + int call_count_; + GURL url_; + DISALLOW_COPY_AND_ASSIGN(TestServiceInterceptor); +}; + +} // namespace + +class ApplicationManagerTest : public testing::Test { + public: + ApplicationManagerTest() : tester_context_(&loop_) {} + + virtual ~ApplicationManagerTest() {} + + virtual void SetUp() OVERRIDE { + application_manager_.reset(new ApplicationManager); + TestApplicationLoader* default_loader = new TestApplicationLoader; + default_loader->set_context(&context_); + application_manager_->set_default_loader( + scoped_ptr(default_loader)); + + TestServicePtr service_proxy; + application_manager_->ConnectToService(GURL(kTestURLString), + &service_proxy); + test_client_.reset(new TestClientImpl(service_proxy.Pass())); + } + + virtual void TearDown() OVERRIDE { + test_client_.reset(NULL); + application_manager_.reset(NULL); + } + + scoped_ptr MakeLoader( + const std::string& requestor_url) { + scoped_ptr real_loader( + new Tester(&tester_context_, requestor_url)); + scoped_ptr loader( + new BackgroundShellApplicationLoader(real_loader.Pass(), + std::string(), + base::MessageLoop::TYPE_DEFAULT)); + return loader.Pass(); + } + + void AddLoaderForURL(const GURL& url, const std::string& requestor_url) { + application_manager_->SetLoaderForURL( + MakeLoader(requestor_url).PassAs(), url); + } + + bool HasFactoryForTestURL() { + ApplicationManager::TestAPI manager_test_api(application_manager_.get()); + return manager_test_api.HasFactoryForURL(GURL(kTestURLString)); + } + + protected: + base::ShadowingAtExitManager at_exit_; + TesterContext tester_context_; + TestContext context_; + base::MessageLoop loop_; + scoped_ptr test_client_; + scoped_ptr application_manager_; + DISALLOW_COPY_AND_ASSIGN(ApplicationManagerTest); +}; + +TEST_F(ApplicationManagerTest, Basic) { + test_client_->Test("test"); + loop_.Run(); + EXPECT_EQ(std::string("test"), context_.last_test_string); +} + +TEST_F(ApplicationManagerTest, ClientError) { + test_client_->Test("test"); + EXPECT_TRUE(HasFactoryForTestURL()); + loop_.Run(); + EXPECT_EQ(1, context_.num_impls); + test_client_.reset(NULL); + loop_.Run(); + EXPECT_EQ(0, context_.num_impls); + EXPECT_TRUE(HasFactoryForTestURL()); +} + +TEST_F(ApplicationManagerTest, Deletes) { + { + ApplicationManager sm; + TestApplicationLoader* default_loader = new TestApplicationLoader; + default_loader->set_context(&context_); + TestApplicationLoader* url_loader1 = new TestApplicationLoader; + TestApplicationLoader* url_loader2 = new TestApplicationLoader; + url_loader1->set_context(&context_); + url_loader2->set_context(&context_); + TestApplicationLoader* scheme_loader1 = new TestApplicationLoader; + TestApplicationLoader* scheme_loader2 = new TestApplicationLoader; + scheme_loader1->set_context(&context_); + scheme_loader2->set_context(&context_); + sm.set_default_loader(scoped_ptr(default_loader)); + sm.SetLoaderForURL(scoped_ptr(url_loader1), + GURL("test:test1")); + sm.SetLoaderForURL(scoped_ptr(url_loader2), + GURL("test:test1")); + sm.SetLoaderForScheme(scoped_ptr(scheme_loader1), + "test"); + sm.SetLoaderForScheme(scoped_ptr(scheme_loader2), + "test"); + } + EXPECT_EQ(5, context_.num_loader_deletes); +} + +// Confirm that both urls and schemes can have their loaders explicitly set. +TEST_F(ApplicationManagerTest, SetLoaders) { + ApplicationManager sm; + TestApplicationLoader* default_loader = new TestApplicationLoader; + TestApplicationLoader* url_loader = new TestApplicationLoader; + TestApplicationLoader* scheme_loader = new TestApplicationLoader; + application_manager_->set_default_loader( + scoped_ptr(default_loader)); + application_manager_->SetLoaderForURL( + scoped_ptr(url_loader), GURL("test:test1")); + application_manager_->SetLoaderForScheme( + scoped_ptr(scheme_loader), "test"); + + // test::test1 should go to url_loader. + TestServicePtr test_service; + application_manager_->ConnectToService(GURL("test:test1"), &test_service); + EXPECT_EQ(1, url_loader->num_loads()); + EXPECT_EQ(0, scheme_loader->num_loads()); + EXPECT_EQ(0, default_loader->num_loads()); + + // test::test2 should go to scheme loader. + application_manager_->ConnectToService(GURL("test:test2"), &test_service); + EXPECT_EQ(1, url_loader->num_loads()); + EXPECT_EQ(1, scheme_loader->num_loads()); + EXPECT_EQ(0, default_loader->num_loads()); + + // http::test1 should go to default loader. + application_manager_->ConnectToService(GURL("http:test1"), &test_service); + EXPECT_EQ(1, url_loader->num_loads()); + EXPECT_EQ(1, scheme_loader->num_loads()); + EXPECT_EQ(1, default_loader->num_loads()); +} + +// Confirm that the url of a service is correctly passed to another service that +// it loads. +TEST_F(ApplicationManagerTest, ACallB) { + // Any url can load a. + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // Only a can load b. + AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); + + TestAPtr a; + application_manager_->ConnectToService(GURL(kTestAURLString), &a); + a->CallB(); + loop_.Run(); + EXPECT_EQ(1, tester_context_.num_b_calls()); + EXPECT_TRUE(tester_context_.a_called_quit()); +} + +// A calls B which calls C. +TEST_F(ApplicationManagerTest, BCallC) { + // Any url can load a. + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // Only a can load b. + AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); + + TestAPtr a; + application_manager_->ConnectToService(GURL(kTestAURLString), &a); + a->CallCFromB(); + loop_.Run(); + + EXPECT_EQ(1, tester_context_.num_b_calls()); + EXPECT_EQ(1, tester_context_.num_c_calls()); + EXPECT_TRUE(tester_context_.a_called_quit()); +} + +// Confirm that a service impl will be deleted if the app that connected to +// it goes away. +TEST_F(ApplicationManagerTest, BDeleted) { + AddLoaderForURL(GURL(kTestAURLString), std::string()); + AddLoaderForURL(GURL(kTestBURLString), std::string()); + + TestAPtr a; + application_manager_->ConnectToService(GURL(kTestAURLString), &a); + + a->CallB(); + loop_.Run(); + + // Kills the a app. + application_manager_->SetLoaderForURL(scoped_ptr(), + GURL(kTestAURLString)); + loop_.Run(); + + EXPECT_EQ(1, tester_context_.num_b_deletes()); +} + +// Confirm that the url of a service is correctly passed to another service that +// it loads, and that it can be rejected. +TEST_F(ApplicationManagerTest, ANoLoadB) { + // Any url can load a. + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // Only c can load b, so this will fail. + AddLoaderForURL(GURL(kTestBURLString), "test:TestC"); + + TestAPtr a; + application_manager_->ConnectToService(GURL(kTestAURLString), &a); + a->CallB(); + loop_.Run(); + EXPECT_EQ(0, tester_context_.num_b_calls()); + + EXPECT_FALSE(tester_context_.a_called_quit()); + EXPECT_TRUE(tester_context_.tester_called_quit()); +} + +TEST_F(ApplicationManagerTest, NoServiceNoLoad) { + AddLoaderForURL(GURL(kTestAURLString), std::string()); + + // There is no TestC service implementation registered with + // ApplicationManager, so this cannot succeed (but also shouldn't crash). + TestCPtr c; + application_manager_->ConnectToService(GURL(kTestAURLString), &c); + QuitMessageLoopErrorHandler quitter; + c.set_error_handler(&quitter); + + loop_.Run(); + EXPECT_TRUE(c.encountered_error()); +} + +TEST_F(ApplicationManagerTest, Interceptor) { + TestServiceInterceptor interceptor; + TestApplicationLoader* default_loader = new TestApplicationLoader; + application_manager_->set_default_loader( + scoped_ptr(default_loader)); + application_manager_->SetInterceptor(&interceptor); + + std::string url("test:test3"); + TestServicePtr test_service; + application_manager_->ConnectToService(GURL(url), &test_service); + + EXPECT_EQ(1, interceptor.call_count()); + EXPECT_EQ(url, interceptor.url_spec()); + EXPECT_EQ(1, default_loader->num_loads()); +} + +} // namespace mojo diff --git a/mojo/application_manager/background_shell_application_loader.cc b/mojo/application_manager/background_shell_application_loader.cc new file mode 100644 index 0000000000000..33b7e698c6352 --- /dev/null +++ b/mojo/application_manager/background_shell_application_loader.cc @@ -0,0 +1,128 @@ +// 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/application_manager/background_shell_application_loader.h" + +#include "base/bind.h" +#include "base/run_loop.h" +#include "mojo/application_manager/application_manager.h" + +namespace mojo { + +class BackgroundShellApplicationLoader::BackgroundLoader { + public: + explicit BackgroundLoader(ApplicationLoader* loader) : loader_(loader) {} + ~BackgroundLoader() {} + + void Load(ApplicationManager* manager, + const GURL& url, + ScopedMessagePipeHandle shell_handle) { + scoped_refptr callbacks( + new ApplicationLoader::SimpleLoadCallbacks(shell_handle.Pass())); + loader_->Load(manager, url, callbacks); + } + + void OnServiceError(ApplicationManager* manager, const GURL& url) { + loader_->OnServiceError(manager, url); + } + + private: + ApplicationLoader* loader_; // Owned by BackgroundShellApplicationLoader + + DISALLOW_COPY_AND_ASSIGN(BackgroundLoader); +}; + +BackgroundShellApplicationLoader::BackgroundShellApplicationLoader( + scoped_ptr real_loader, + const std::string& thread_name, + base::MessageLoop::Type message_loop_type) + : loader_(real_loader.Pass()), + message_loop_type_(message_loop_type), + thread_name_(thread_name), + message_loop_created_(true, false), + background_loader_(NULL) { +} + +BackgroundShellApplicationLoader::~BackgroundShellApplicationLoader() { + if (thread_) + thread_->Join(); +} + +void BackgroundShellApplicationLoader::Load( + ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; + + if (!thread_) { + // TODO(tim): It'd be nice if we could just have each Load call + // result in a new thread like DynamicService{Loader, Runner}. But some + // loaders are creating multiple ApplicationImpls (NetworkApplicationLoader) + // sharing a delegate (etc). So we have to keep it single threaded, wait + // for the thread to initialize, and post to the TaskRunner for subsequent + // Load calls for now. + thread_.reset(new base::DelegateSimpleThread(this, thread_name_)); + thread_->Start(); + message_loop_created_.Wait(); + DCHECK(task_runner_); + } + + task_runner_->PostTask( + FROM_HERE, + base::Bind( + &BackgroundShellApplicationLoader::LoadOnBackgroundThread, + base::Unretained(this), + manager, + url, + base::Owned(new ScopedMessagePipeHandle(shell_handle.Pass())))); +} + +void BackgroundShellApplicationLoader::OnServiceError( + ApplicationManager* manager, + const GURL& url) { + task_runner_->PostTask( + FROM_HERE, + base::Bind( + &BackgroundShellApplicationLoader::OnServiceErrorOnBackgroundThread, + base::Unretained(this), + manager, + url)); +} + +void BackgroundShellApplicationLoader::Run() { + base::MessageLoop message_loop(message_loop_type_); + base::RunLoop loop; + task_runner_ = message_loop.task_runner(); + quit_closure_ = loop.QuitClosure(); + message_loop_created_.Signal(); + loop.Run(); + + delete background_loader_; + background_loader_ = NULL; + // Destroy |loader_| on the thread it's actually used on. + loader_.reset(); +} + +void BackgroundShellApplicationLoader::LoadOnBackgroundThread( + ApplicationManager* manager, + const GURL& url, + ScopedMessagePipeHandle* shell_handle) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (!background_loader_) + background_loader_ = new BackgroundLoader(loader_.get()); + background_loader_->Load(manager, url, shell_handle->Pass()); +} + +void BackgroundShellApplicationLoader::OnServiceErrorOnBackgroundThread( + ApplicationManager* manager, + const GURL& url) { + DCHECK(task_runner_->RunsTasksOnCurrentThread()); + if (!background_loader_) + background_loader_ = new BackgroundLoader(loader_.get()); + background_loader_->OnServiceError(manager, url); +} + +} // namespace mojo diff --git a/mojo/application_manager/background_shell_application_loader.h b/mojo/application_manager/background_shell_application_loader.h new file mode 100644 index 0000000000000..ee34ad89bcce7 --- /dev/null +++ b/mojo/application_manager/background_shell_application_loader.h @@ -0,0 +1,76 @@ +// 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_APPLICATION_MANAGER_BACKGROUND_SHELL_APPLICATION_LOADER_H_ +#define MOJO_APPLICATION_MANAGER_BACKGROUND_SHELL_APPLICATION_LOADER_H_ + +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/synchronization/waitable_event.h" +#include "base/threading/simple_thread.h" +#include "mojo/application_manager/application_loader.h" + +namespace mojo { + +// TODO(tim): Eventually this should be Android-only to support services +// that we need to bundle with the shell (such as NetworkService). Perhaps +// we should move it to shell/ as well. +class MOJO_APPLICATION_MANAGER_EXPORT BackgroundShellApplicationLoader + : public ApplicationLoader, + public base::DelegateSimpleThread::Delegate { + public: + BackgroundShellApplicationLoader(scoped_ptr real_loader, + const std::string& thread_name, + base::MessageLoop::Type message_loop_type); + virtual ~BackgroundShellApplicationLoader(); + + // ApplicationLoader overrides: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE; + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE; + + private: + class BackgroundLoader; + + // |base::DelegateSimpleThread::Delegate| method: + virtual void Run() OVERRIDE; + + // These functions are exected on the background thread. They call through + // to |background_loader_| to do the actual loading. + // TODO: having this code take a |manager| is fragile (as ApplicationManager + // isn't thread safe). + void LoadOnBackgroundThread(ApplicationManager* manager, + const GURL& url, + ScopedMessagePipeHandle* shell_handle); + void OnServiceErrorOnBackgroundThread(ApplicationManager* manager, + const GURL& url); + bool quit_on_shutdown_; + scoped_ptr loader_; + + const base::MessageLoop::Type message_loop_type_; + const std::string thread_name_; + + // Created on |thread_| during construction of |this|. Protected against + // uninitialized use by |message_loop_created_|, and protected against + // use-after-free by holding a reference to the thread-safe object. Note + // that holding a reference won't hold |thread_| from exiting. + scoped_refptr task_runner_; + base::WaitableEvent message_loop_created_; + + // Lives on |thread_|. + base::Closure quit_closure_; + + scoped_ptr thread_; + + // Lives on |thread_|. Trivial interface that calls through to |loader_|. + BackgroundLoader* background_loader_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundShellApplicationLoader); +}; + +} // namespace mojo + +#endif // MOJO_APPLICATION_MANAGER_BACKGROUND_SHELL_APPLICATION_LOADER_H_ diff --git a/mojo/application_manager/background_shell_application_loader_unittest.cc b/mojo/application_manager/background_shell_application_loader_unittest.cc new file mode 100644 index 0000000000000..4de4b85c512ab --- /dev/null +++ b/mojo/application_manager/background_shell_application_loader_unittest.cc @@ -0,0 +1,56 @@ +// 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/application_manager/background_shell_application_loader.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { + +namespace { + +class DummyLoader : public ApplicationLoader { + public: + DummyLoader() : simulate_app_quit_(true) {} + virtual ~DummyLoader() {} + + // ApplicationLoader overrides: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE { + if (simulate_app_quit_) + base::MessageLoop::current()->Quit(); + } + + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE {} + + void DontSimulateAppQuit() { simulate_app_quit_ = false; } + + private: + bool simulate_app_quit_; +}; + +} // namespace + +// Tests that the loader can start and stop gracefully. +TEST(BackgroundShellApplicationLoaderTest, StartStop) { + scoped_ptr real_loader(new DummyLoader()); + BackgroundShellApplicationLoader loader( + real_loader.Pass(), "test", base::MessageLoop::TYPE_DEFAULT); +} + +// Tests that the loader can load a service that is well behaved (quits +// itself). +TEST(BackgroundShellApplicationLoaderTest, Load) { + scoped_ptr real_loader(new DummyLoader()); + BackgroundShellApplicationLoader loader( + real_loader.Pass(), "test", base::MessageLoop::TYPE_DEFAULT); + MessagePipe dummy; + scoped_refptr callbacks( + new ApplicationLoader::SimpleLoadCallbacks(dummy.handle0.Pass())); + loader.Load(NULL, GURL(), callbacks); +} + +} // namespace mojo diff --git a/mojo/service_manager/test.mojom b/mojo/application_manager/test.mojom similarity index 85% rename from mojo/service_manager/test.mojom rename to mojo/application_manager/test.mojom index 868a4d305ff9b..0beaba5a70456 100644 --- a/mojo/service_manager/test.mojom +++ b/mojo/application_manager/test.mojom @@ -1,4 +1,4 @@ -// Copyright 2013 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. diff --git a/mojo/apps/js/bindings/gl/context.cc b/mojo/apps/js/bindings/gl/context.cc index 250c6f8a01c11..4754d4efda146 100644 --- a/mojo/apps/js/bindings/gl/context.cc +++ b/mojo/apps/js/bindings/gl/context.cc @@ -11,6 +11,7 @@ #include "gin/object_template_builder.h" #include "gin/per_context_data.h" #include "mojo/public/c/gles2/gles2.h" +#include "mojo/public/cpp/environment/environment.h" namespace gin { template<> @@ -154,10 +155,10 @@ Context::Context(v8::Isolate* isolate, v8::Handle context = isolate->GetCurrentContext(); runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); context_lost_callback_.Reset(isolate, context_lost_callback); - context_ = MojoGLES2CreateContext( - handle.value(), - &ContextLostThunk, - this); + context_ = MojoGLES2CreateContext(handle.value(), + &ContextLostThunk, + this, + Environment::GetDefaultAsyncWaiter()); MojoGLES2MakeCurrent(context_); } diff --git a/mojo/apps/js/main.cc b/mojo/apps/js/main.cc index 84231e5971e39..b5d4486736d60 100644 --- a/mojo/apps/js/main.cc +++ b/mojo/apps/js/main.cc @@ -5,7 +5,6 @@ #include "base/message_loop/message_loop.h" #include "gin/public/isolate_holder.h" #include "mojo/apps/js/mojo_runner_delegate.h" -#include "mojo/public/cpp/gles2/gles2.h" #include "mojo/public/cpp/system/core.h" #include "mojo/public/cpp/system/macros.h" @@ -37,7 +36,6 @@ void Start(MojoHandle pipe, const std::string& module) { } // namespace mojo extern "C" MOJO_APPS_JS_EXPORT MojoResult CDECL MojoMain(MojoHandle pipe) { - mojo::GLES2Initializer gles2; mojo::apps::Start(pipe, "mojo/apps/js/main"); return MOJO_RESULT_OK; } diff --git a/mojo/aura/window_tree_host_mojo.cc b/mojo/aura/window_tree_host_mojo.cc index d92e3556c77a3..8ef87c6c673cc 100644 --- a/mojo/aura/window_tree_host_mojo.cc +++ b/mojo/aura/window_tree_host_mojo.cc @@ -61,19 +61,19 @@ class TreeHosts : public base::SupportsUserData::Data { //////////////////////////////////////////////////////////////////////////////// // WindowTreeHostMojo, public: -WindowTreeHostMojo::WindowTreeHostMojo(Node* node, +WindowTreeHostMojo::WindowTreeHostMojo(View* view, WindowTreeHostMojoDelegate* delegate) - : node_(node), - bounds_(node->bounds()), + : view_(view), + bounds_(view->bounds()), delegate_(delegate) { - node_->AddObserver(this); + view_->AddObserver(this); CreateCompositor(GetAcceleratedWidget()); TreeHosts::Get()->Add(this); } WindowTreeHostMojo::~WindowTreeHostMojo() { - node_->RemoveObserver(this); + view_->RemoveObserver(this); TreeHosts::Get()->Remove(this); DestroyCompositor(); DestroyDispatcher(); @@ -157,10 +157,10 @@ ui::EventProcessor* WindowTreeHostMojo::GetEventProcessor() { } //////////////////////////////////////////////////////////////////////////////// -// WindowTreeHostMojo, NodeObserver implementation: +// WindowTreeHostMojo, ViewObserver implementation: -void WindowTreeHostMojo::OnNodeBoundsChanged( - Node* node, +void WindowTreeHostMojo::OnViewBoundsChanged( + View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { bounds_ = new_bounds; diff --git a/mojo/aura/window_tree_host_mojo.h b/mojo/aura/window_tree_host_mojo.h index 026253a071732..75ae0706e23db 100644 --- a/mojo/aura/window_tree_host_mojo.h +++ b/mojo/aura/window_tree_host_mojo.h @@ -5,7 +5,7 @@ #ifndef MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_ #define MOJO_EXAMPLES_AURA_DEMO_WINDOW_TREE_HOST_VIEW_MANAGER_H_ -#include "mojo/services/public/cpp/view_manager/node_observer.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "ui/aura/window_tree_host.h" #include "ui/events/event_source.h" #include "ui/gfx/geometry/rect.h" @@ -22,9 +22,9 @@ class WindowTreeHostMojoDelegate; class WindowTreeHostMojo : public aura::WindowTreeHost, public ui::EventSource, - public NodeObserver { + public ViewObserver { public: - WindowTreeHostMojo(Node* node, WindowTreeHostMojoDelegate* delegate); + WindowTreeHostMojo(View* view, WindowTreeHostMojoDelegate* delegate); virtual ~WindowTreeHostMojo(); // Returns the WindowTreeHostMojo for the specified compositor. @@ -59,13 +59,13 @@ class WindowTreeHostMojo : public aura::WindowTreeHost, // ui::EventSource: virtual ui::EventProcessor* GetEventProcessor() OVERRIDE; - // NodeObserver: - virtual void OnNodeBoundsChanged( - Node* node, + // ViewObserver: + virtual void OnViewBoundsChanged( + View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE; - Node* node_; + View* view_; gfx::Rect bounds_; diff --git a/mojo/cc/context_provider_mojo.cc b/mojo/cc/context_provider_mojo.cc index 1dd62d8a08bd8..16c6f8c6ac2bc 100644 --- a/mojo/cc/context_provider_mojo.cc +++ b/mojo/cc/context_provider_mojo.cc @@ -5,6 +5,7 @@ #include "mojo/cc/context_provider_mojo.h" #include "base/logging.h" +#include "mojo/public/cpp/environment/environment.h" namespace mojo { @@ -16,10 +17,10 @@ ContextProviderMojo::ContextProviderMojo( bool ContextProviderMojo::BindToCurrentThread() { DCHECK(command_buffer_handle_.is_valid()); - context_ = MojoGLES2CreateContext( - command_buffer_handle_.release().value(), - &ContextLostThunk, - this); + context_ = MojoGLES2CreateContext(command_buffer_handle_.release().value(), + &ContextLostThunk, + this, + Environment::GetDefaultAsyncWaiter()); return !!context_; } diff --git a/mojo/common/data_pipe_utils.cc b/mojo/common/data_pipe_utils.cc index cf9dc78c30dd3..2a6b21e6d4824 100644 --- a/mojo/common/data_pipe_utils.cc +++ b/mojo/common/data_pipe_utils.cc @@ -11,7 +11,6 @@ #include "base/files/scoped_file.h" #include "base/message_loop/message_loop.h" #include "base/task_runner_util.h" -#include "mojo/common/handle_watcher.h" namespace mojo { namespace common { diff --git a/mojo/common/message_pump_mojo.cc b/mojo/common/message_pump_mojo.cc index c7903f2831be0..a227592587614 100644 --- a/mojo/common/message_pump_mojo.cc +++ b/mojo/common/message_pump_mojo.cc @@ -15,6 +15,20 @@ namespace mojo { namespace common { +namespace { + +MojoDeadline TimeTicksToMojoDeadline(base::TimeTicks time_ticks, + base::TimeTicks now) { + // The is_null() check matches that of HandleWatcher as well as how + // |delayed_work_time| is used. + if (time_ticks.is_null()) + return MOJO_DEADLINE_INDEFINITE; + const int64_t delta = (time_ticks - now).InMicroseconds(); + return delta < 0 ? static_cast(0) : + static_cast(delta); +} + +} // namespace // State needed for one iteration of WaitMany. The first handle and flags // corresponds to that of the control pipe. @@ -52,10 +66,10 @@ void MessagePumpMojo::AddHandler(MessagePumpMojoHandler* handler, const Handle& handle, MojoHandleSignals wait_signals, base::TimeTicks deadline) { - DCHECK(handler); + CHECK(handler); DCHECK(handle.is_valid()); // Assume it's an error if someone tries to reregister an existing handle. - DCHECK_EQ(0u, handlers_.count(handle)); + CHECK_EQ(0u, handlers_.count(handle)); Handler handler_data; handler_data.handler = handler; handler_data.wait_signals = wait_signals; @@ -104,7 +118,6 @@ void MessagePumpMojo::ScheduleDelayedWork( if (!run_state_) return; run_state_->delayed_work_time = delayed_work_time; - SignalControlPipe(*run_state_); } void MessagePumpMojo::DoRunLoop(RunState* run_state, Delegate* delegate) { @@ -186,7 +199,7 @@ void MessagePumpMojo::DoInternalWork(const RunState& run_state, bool block) { void MessagePumpMojo::RemoveFirstInvalidHandle(const WaitState& wait_state) { // TODO(sky): deal with control pipe going bad. - for (size_t i = 1; i < wait_state.handles.size(); ++i) { + for (size_t i = 0; i < wait_state.handles.size(); ++i) { const MojoResult result = Wait(wait_state.handles[i], wait_state.wait_signals[i], 0); if (result == MOJO_RESULT_INVALID_ARGUMENT) { @@ -196,9 +209,11 @@ void MessagePumpMojo::RemoveFirstInvalidHandle(const WaitState& wait_state) { CHECK(false); } else if (result == MOJO_RESULT_FAILED_PRECONDITION || result == MOJO_RESULT_CANCELLED) { + CHECK_NE(i, 0u); // Indicates the control pipe went bad. + // Remove the handle first, this way if OnHandleError() tries to remove // the handle our iterator isn't invalidated. - DCHECK(handlers_.find(wait_state.handles[i]) != handlers_.end()); + CHECK(handlers_.find(wait_state.handles[i]) != handlers_.end()); MessagePumpMojoHandler* handler = handlers_[wait_state.handles[i]].handler; handlers_.erase(wait_state.handles[i]); @@ -209,9 +224,12 @@ void MessagePumpMojo::RemoveFirstInvalidHandle(const WaitState& wait_state) { } void MessagePumpMojo::SignalControlPipe(const RunState& run_state) { - // TODO(sky): deal with error? - WriteMessageRaw(run_state.write_handle.get(), NULL, 0, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE); + const MojoResult result = + WriteMessageRaw(run_state.write_handle.get(), NULL, 0, NULL, 0, + MOJO_WRITE_MESSAGE_FLAG_NONE); + // If we can't write we likely won't wake up the thread and there is a strong + // chance we'll deadlock. + CHECK_EQ(MOJO_RESULT_OK, result); } MessagePumpMojo::WaitState MessagePumpMojo::GetWaitState( @@ -230,16 +248,15 @@ MessagePumpMojo::WaitState MessagePumpMojo::GetWaitState( MojoDeadline MessagePumpMojo::GetDeadlineForWait( const RunState& run_state) const { - base::TimeTicks min_time = run_state.delayed_work_time; + const base::TimeTicks now(internal::NowTicks()); + MojoDeadline deadline = TimeTicksToMojoDeadline(run_state.delayed_work_time, + now); for (HandleToHandler::const_iterator i = handlers_.begin(); i != handlers_.end(); ++i) { - if (min_time.is_null() && i->second.deadline < min_time) - min_time = i->second.deadline; + deadline = std::min( + TimeTicksToMojoDeadline(i->second.deadline, now), deadline); } - return min_time.is_null() ? MOJO_DEADLINE_INDEFINITE : - std::max(static_cast(0), - static_cast( - (min_time - internal::NowTicks()).InMicroseconds())); + return deadline; } } // namespace common diff --git a/mojo/common/message_pump_mojo.h b/mojo/common/message_pump_mojo.h index 751311850ebb4..7b2c1703162db 100644 --- a/mojo/common/message_pump_mojo.h +++ b/mojo/common/message_pump_mojo.h @@ -31,6 +31,7 @@ class MOJO_COMMON_EXPORT MessagePumpMojo : public base::MessagePump { // Registers a MessagePumpMojoHandler for the specified handle. Only one // handler can be registered for a specified handle. + // NOTE: a value of 0 for |deadline| indicates an indefinite timeout. void AddHandler(MessagePumpMojoHandler* handler, const Handle& handle, MojoHandleSignals wait_signals, diff --git a/mojo/embedder/channel_init.cc b/mojo/embedder/channel_init.cc index 8b79fc8b3a09a..31cb50090e472 100644 --- a/mojo/embedder/channel_init.cc +++ b/mojo/embedder/channel_init.cc @@ -15,43 +15,41 @@ ChannelInit::ChannelInit() : channel_info_(NULL), weak_factory_(this) { } ChannelInit::~ChannelInit() { - if (channel_info_) { - io_thread_task_runner_->PostTask( - FROM_HERE, - base::Bind(&mojo::embedder::DestroyChannelOnIOThread, channel_info_)); - } + if (channel_info_) + DestroyChannel(channel_info_); } -mojo::ScopedMessagePipeHandle ChannelInit::Init( +ScopedMessagePipeHandle ChannelInit::Init( base::PlatformFile file, scoped_refptr io_thread_task_runner) { DCHECK(!io_thread_task_runner_.get()); // Should only init once. io_thread_task_runner_ = io_thread_task_runner; - mojo::ScopedMessagePipeHandle message_pipe = - mojo::embedder::CreateChannel( - mojo::embedder::ScopedPlatformHandle( - mojo::embedder::PlatformHandle(file)), - io_thread_task_runner, - base::Bind(&ChannelInit::OnCreatedChannel, - weak_factory_.GetWeakPtr(), - io_thread_task_runner), - base::MessageLoop::current()->message_loop_proxy()).Pass(); + ScopedMessagePipeHandle message_pipe = + CreateChannel(ScopedPlatformHandle(PlatformHandle(file)), + io_thread_task_runner, + base::Bind(&ChannelInit::OnCreatedChannel, + weak_factory_.GetWeakPtr(), + io_thread_task_runner), + base::MessageLoop::current()->message_loop_proxy()).Pass(); return message_pipe.Pass(); } +void ChannelInit::WillDestroySoon() { + if (channel_info_) + WillDestroyChannelSoon(channel_info_); +} + // static -void ChannelInit::OnCreatedChannel(base::WeakPtr host, +void ChannelInit::OnCreatedChannel(base::WeakPtr self, scoped_refptr io_thread, - embedder::ChannelInfo* channel) { - // By the time we get here |host| may have been destroyed. If so, shutdown the - // channel. - if (!host.get()) { - io_thread->PostTask( - FROM_HERE, - base::Bind(&mojo::embedder::DestroyChannelOnIOThread, channel)); + ChannelInfo* channel) { + // If |self| was already destroyed, shut the channel down. + if (!self) { + DestroyChannel(channel); return; } - host->channel_info_ = channel; + + self->channel_info_ = channel; } } // namespace embedder diff --git a/mojo/embedder/channel_init.h b/mojo/embedder/channel_init.h index d06332f1db68f..26a645288f977 100644 --- a/mojo/embedder/channel_init.h +++ b/mojo/embedder/channel_init.h @@ -23,8 +23,8 @@ struct ChannelInfo; namespace embedder { -// ChannelInit handle creation (and destruction) of the mojo channel. It is -// expected that this class is created and destroyed on the main thread. +// |ChannelInit| handles creation (and destruction) of the Mojo channel. It is +// not thread-safe, but may be used on any single thread (with a |MessageLoop|). class MOJO_SYSTEM_IMPL_EXPORT ChannelInit { public: ChannelInit(); @@ -36,16 +36,21 @@ class MOJO_SYSTEM_IMPL_EXPORT ChannelInit { base::PlatformFile file, scoped_refptr io_thread_task_runner); + // Notifies the channel that we (hence it) will soon be destroyed. + void WillDestroySoon(); + private: - // Invoked on the main thread once the channel has been established. - static void OnCreatedChannel(base::WeakPtr host, + // Invoked on the thread on which this object lives once the channel has been + // established. (This is a static method that takes a weak pointer to self, + // since we want to destroy the channel even if we're destroyed.) + static void OnCreatedChannel(base::WeakPtr self, scoped_refptr io_thread, - embedder::ChannelInfo* channel); + ChannelInfo* channel); scoped_refptr io_thread_task_runner_; // If non-null the channel has been established. - embedder::ChannelInfo* channel_info_; + ChannelInfo* channel_info_; base::WeakPtrFactory weak_factory_; diff --git a/mojo/embedder/embedder.cc b/mojo/embedder/embedder.cc index 703b8e44c0f8c..a9f70c5ef09cc 100644 --- a/mojo/embedder/embedder.cc +++ b/mojo/embedder/embedder.cc @@ -24,17 +24,19 @@ namespace embedder { // outside world. But we need to define it before our (internal-only) functions // that use it. struct ChannelInfo { - explicit ChannelInfo(scoped_refptr channel) - : channel(channel) {} + ChannelInfo() {} ~ChannelInfo() {} scoped_refptr channel; + + // May be null, in which case |DestroyChannelOnIOThread()| must be used (from + // the IO thread), instead of |DestroyChannel()|. + scoped_refptr io_thread_task_runner; }; namespace { -// Helper for |CreateChannelOnIOThread()|. (Note: May return null for some -// failures.) +// Helper for |CreateChannel...()|. (Note: May return null for some failures.) scoped_refptr MakeChannel( ScopedPlatformHandle platform_handle, scoped_refptr message_pipe) { @@ -73,13 +75,13 @@ scoped_refptr MakeChannel( return channel; } -void CreateChannelOnIOThread( +void CreateChannelHelper( ScopedPlatformHandle platform_handle, + scoped_ptr channel_info, scoped_refptr message_pipe, DidCreateChannelCallback callback, scoped_refptr callback_thread_task_runner) { - scoped_ptr channel_info( - new ChannelInfo(MakeChannel(platform_handle.Pass(), message_pipe))); + channel_info->channel = MakeChannel(platform_handle.Pass(), message_pipe); // Hand the channel back to the embedder. if (callback_thread_task_runner) { @@ -96,6 +98,29 @@ void Init() { system::entrypoints::SetCore(new system::Core()); } +// TODO(vtl): Write tests for this. +ScopedMessagePipeHandle CreateChannelOnIOThread( + ScopedPlatformHandle platform_handle, + ChannelInfo** channel_info) { + DCHECK(platform_handle.is_valid()); + DCHECK(channel_info); + + std::pair, + scoped_refptr > remote_message_pipe = + system::MessagePipeDispatcher::CreateRemoteMessagePipe(); + + system::Core* core = system::entrypoints::GetCore(); + DCHECK(core); + ScopedMessagePipeHandle rv( + MessagePipeHandle(core->AddDispatcher(remote_message_pipe.first))); + + *channel_info = new ChannelInfo(); + (*channel_info)->channel = + MakeChannel(platform_handle.Pass(), remote_message_pipe.second); + + return rv.Pass(); +} + ScopedMessagePipeHandle CreateChannel( ScopedPlatformHandle platform_handle, scoped_refptr io_thread_task_runner, @@ -111,15 +136,24 @@ ScopedMessagePipeHandle CreateChannel( DCHECK(core); ScopedMessagePipeHandle rv( MessagePipeHandle(core->AddDispatcher(remote_message_pipe.first))); - // TODO(vtl): Do we properly handle the failure case here? + + scoped_ptr channel_info(new ChannelInfo()); + channel_info->io_thread_task_runner = io_thread_task_runner; + if (rv.is_valid()) { io_thread_task_runner->PostTask(FROM_HERE, - base::Bind(&CreateChannelOnIOThread, + base::Bind(&CreateChannelHelper, base::Passed(&platform_handle), + base::Passed(&channel_info), remote_message_pipe.second, callback, callback_thread_task_runner)); + } else { + (callback_thread_task_runner ? callback_thread_task_runner + : io_thread_task_runner) + ->PostTask(FROM_HERE, base::Bind(callback, channel_info.release())); } + return rv.Pass(); } @@ -134,6 +168,26 @@ void DestroyChannelOnIOThread(ChannelInfo* channel_info) { delete channel_info; } +// TODO(vtl): Write tests for this. +void DestroyChannel(ChannelInfo* channel_info) { + DCHECK(channel_info); + DCHECK(channel_info->io_thread_task_runner); + + if (!channel_info->channel) { + // Presumably, |Init()| on the channel failed. + return; + } + + channel_info->channel->WillShutdownSoon(); + channel_info->io_thread_task_runner->PostTask( + FROM_HERE, base::Bind(&DestroyChannelOnIOThread, channel_info)); +} + +void WillDestroyChannelSoon(ChannelInfo* channel_info) { + DCHECK(channel_info); + channel_info->channel->WillShutdownSoon(); +} + MojoResult CreatePlatformHandleWrapper( ScopedPlatformHandle platform_handle, MojoHandle* platform_handle_wrapper_handle) { diff --git a/mojo/embedder/embedder.h b/mojo/embedder/embedder.h index 57da5a0964d91..c6638e5e1b33b 100644 --- a/mojo/embedder/embedder.h +++ b/mojo/embedder/embedder.h @@ -18,44 +18,89 @@ namespace embedder { // Must be called first to initialize the (global, singleton) system. MOJO_SYSTEM_IMPL_EXPORT void Init(); -// Creates a new "channel", returning a handle to the bootstrap message pipe on -// that channel. |platform_handle| should be an OS-dependent handle to one side -// of a suitable bidirectional OS "pipe" (e.g., a file descriptor to a socket on -// POSIX, a handle to a named pipe on Windows); this "pipe" should be connected -// and ready for operation (e.g., to be written to or read from). -// |io_thread_task_runner| should be a |TaskRunner| for the thread on which the -// "channel" will run (read data and demultiplex). +// A "channel" is a connection on top of an OS "pipe", on top of which Mojo +// message pipes (etc.) can be multiplexed. It must "live" on some I/O thread. // -// On completion, it will run |callback| with a pointer to a |ChannelInfo| -// (which is meant to be opaque to the embedder). If -// |callback_thread_task_runner| is non-null, it the callback will be posted to -// that task runner. Otherwise, it will be run on the I/O thread directly. +// There are two "channel" creation/destruction APIs: the synchronous +// |CreateChannelOnIOThread()|/|DestroyChannelOnIOThread()|, which must be +// called from the I/O thread, and the asynchronous +// |CreateChannel()|/|DestroyChannel()|, which may be called from any thread. // -// Returns an invalid |MOJO_HANDLE_INVALID| on error. Note that this will happen -// only if, e.g., the handle table is full (operation of the channel begins -// asynchronously and if, e.g., the other end of the "pipe" is closed, this will -// report an error to the returned handle in the usual way). +// Both creation functions have a |platform_handle| argument, which should be an +// OS-dependent handle to one side of a suitable bidirectional OS "pipe" (e.g., +// a file descriptor to a socket on POSIX, a handle to a named pipe on Windows); +// this "pipe" should be connected and ready for operation (e.g., to be written +// to or read from). // -// Notes: The handle returned is ready for use immediately, with messages -// written to it queued. E.g., it would be perfectly valid for a message to be -// immediately written to the returned handle and the handle closed, all before -// the channel has begun operation on the IO thread. In this case, the channel -// is expected to connect as usual, send the queued message, and report that the -// handle was closed to the other side. (This message may well contain another -// handle, so there may well still be message pipes "on" this channel.) +// Both (synchronously) return a handle to the bootstrap message pipe on the +// channel that was (or is to be) created, or |MOJO_HANDLE_INVALID| on error +// (but note that this will happen only if, e.g., the handle table is full). +// This message pipe may be used immediately, but since channel operation +// actually begins asynchronously, other errors may still occur (e.g., if the +// other end of the "pipe" is closed) and be reported in the usual way to the +// returned handle. +// +// (E.g., a message written immediately to the returned handle will be queued +// and the handle immediately closed, before the channel begins operation. In +// this case, the channel should connect as usual, send the queued message, and +// report that the handle was closed to the other side. The message sent may +// have other handles, so there may still be message pipes "on" this channel.) +// +// Both also produce a |ChannelInfo*| (a pointer to an opaque object) -- the +// first synchronously and second asynchronously. +// +// The destruction functions are similarly synchronous and asynchronous, +// respectively, and take the |ChannelInfo*| produced by the creation function. +// (Note: One may call |DestroyChannelOnIOThread()| with the result of +// |CreateChannel()|, but not |DestroyChannel()| with the result of +// |CreateChannelOnIOThread()|.) // // TODO(vtl): Figure out channel teardown. struct ChannelInfo; + +// Creates a channel; must only be called from the I/O thread. |platform_handle| +// should be a handle to a connected OS "pipe". Eventually (even on failure), +// the "out" value |*channel_info| should be passed to +// |DestroyChannelOnIOThread()| to tear down the channel. Returns a handle to +// the bootstrap message pipe. +MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle + CreateChannelOnIOThread(ScopedPlatformHandle platform_handle, + ChannelInfo** channel_info); + typedef base::Callback DidCreateChannelCallback; +// Creates a channel asynchronously; may be called from any thread. +// |platform_handle| should be a handle to a connected OS "pipe". +// |io_thread_task_runner| should be the |TaskRunner| for the I/O thread. +// |callback| should be the callback to call with the |ChannelInfo*|, which +// should eventually be passed to |DestroyChannel()| (or +// |DestroyChannelOnIOThread()|) to tear down the channel; the callback will be +// called using |callback_thread_task_runner| if that is non-null, or otherwise +// it will be called using |io_thread_task_runner|. Returns a handle to the +// bootstrap message pipe. MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle CreateChannel(ScopedPlatformHandle platform_handle, scoped_refptr io_thread_task_runner, DidCreateChannelCallback callback, scoped_refptr callback_thread_task_runner); +// Destroys a channel that was created using either |CreateChannelOnIOThread()| +// or |CreateChannel()|; must only be called from the I/O thread. |channel_info| +// should be the "out" value from |CreateChannelOnIOThread()| or the value +// provided to the callback to |CreateChannel()|. MOJO_SYSTEM_IMPL_EXPORT void DestroyChannelOnIOThread( ChannelInfo* channel_info); +// Destroys a channel (asynchronously) that was created using |CreateChannel()| +// (note: NOT |CreateChannelOnIOThread()|); may be called from any thread. +// |channel_info| should be the value provided to the callback to +// |CreateChannel()|. +MOJO_SYSTEM_IMPL_EXPORT void DestroyChannel(ChannelInfo* channel_info); + +// Inform the channel that it will soon be destroyed (doing so is optional). +// This may be called from any thread, but the caller must ensure that this is +// called before |DestroyChannel()| or |DestroyChannelOnIOThread()|. +MOJO_SYSTEM_IMPL_EXPORT void WillDestroyChannelSoon(ChannelInfo* channel_info); + // Creates a |MojoHandle| that wraps the given |PlatformHandle| (taking // ownership of it). This |MojoHandle| can then, e.g., be passed through message // pipes. Note: This takes ownership (and thus closes) |platform_handle| even on diff --git a/mojo/embedder/embedder_unittest.cc b/mojo/embedder/embedder_unittest.cc index bc32213b0f53c..afe608e7a78e6 100644 --- a/mojo/embedder/embedder_unittest.cc +++ b/mojo/embedder/embedder_unittest.cc @@ -456,7 +456,7 @@ TEST_F(EmbedderTest, MultiprocessChannels) { } MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) { - embedder::ScopedPlatformHandle client_platform_handle = + ScopedPlatformHandle client_platform_handle = mojo::test::MultiprocessTestHelper::client_platform_handle.Pass(); EXPECT_TRUE(client_platform_handle.is_valid()); diff --git a/mojo/embedder/platform_shared_buffer.h b/mojo/embedder/platform_shared_buffer.h new file mode 100644 index 0000000000000..f8f422ea746c6 --- /dev/null +++ b/mojo/embedder/platform_shared_buffer.h @@ -0,0 +1,102 @@ +// 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_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ +#define MOJO_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/embedder/scoped_platform_handle.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +class PlatformSharedBufferMapping; + +// |PlatformSharedBuffer| is an interface for a thread-safe, ref-counted wrapper +// around OS-specific shared memory. It has the following features: +// - A |PlatformSharedBuffer| simply represents a piece of shared memory that +// *may* be mapped and *may* be shared to another process. +// - A single |PlatformSharedBuffer| may be mapped multiple times. The +// lifetime of the mapping (owned by |PlatformSharedBufferMapping|) is +// separate from the lifetime of the |PlatformSharedBuffer|. +// - Sizes/offsets (of the shared memory and mappings) are arbitrary, and not +// restricted by page size. However, more memory may actually be mapped than +// requested. +// +// It currently does NOT support the following: +// - Sharing read-only. (This will probably eventually be supported.) +// +// TODO(vtl): Rectify this with |base::SharedMemory|. +class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBuffer + : public base::RefCountedThreadSafe { + public: + // Gets the size of shared buffer (in number of bytes). + virtual size_t GetNumBytes() const = 0; + + // Maps (some) of the shared buffer into memory; [|offset|, |offset + length|] + // must be contained in [0, |num_bytes|], and |length| must be at least 1. + // Returns null on failure. + virtual scoped_ptr Map(size_t offset, + size_t length) = 0; + + // Checks if |offset| and |length| are valid arguments. + virtual bool IsValidMap(size_t offset, size_t length) = 0; + + // Like |Map()|, but doesn't check its arguments (which should have been + // preflighted using |IsValidMap()|). + virtual scoped_ptr MapNoCheck(size_t offset, + size_t length) = 0; + + // Duplicates the underlying platform handle and passes it to the caller. + // TODO(vtl): On POSIX, we'll need two FDs to support sharing read-only. + virtual ScopedPlatformHandle DuplicatePlatformHandle() = 0; + + // Passes the underlying platform handle to the caller. This should only be + // called if there's a unique reference to this object (owned by the caller). + // After calling this, this object should no longer be used, but should only + // be disposed of. + virtual ScopedPlatformHandle PassPlatformHandle() = 0; + + protected: + friend class base::RefCountedThreadSafe; + + PlatformSharedBuffer() {} + virtual ~PlatformSharedBuffer() {} + + private: + DISALLOW_COPY_AND_ASSIGN(PlatformSharedBuffer); +}; + +// An interface for a mapping of a |PlatformSharedBuffer| (compararable to a +// "file view" in Windows); see above. Created by (implementations of) +// |PlatformSharedBuffer::Map()|. Automatically unmaps memory on destruction. +// +// Mappings are NOT thread-safe. +// +// Note: This is an entirely separate class (instead of +// |PlatformSharedBuffer::Mapping|) so that it can be forward-declared. +class MOJO_SYSTEM_IMPL_EXPORT PlatformSharedBufferMapping { + public: + // IMPORTANT: Implementations must implement a destructor that unmaps memory. + virtual ~PlatformSharedBufferMapping() {} + + virtual void* GetBase() const = 0; + virtual size_t GetLength() const = 0; + + protected: + PlatformSharedBufferMapping() {} + + private: + DISALLOW_COPY_AND_ASSIGN(PlatformSharedBufferMapping); +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EMBEDDER_PLATFORM_SHARED_BUFFER_H_ diff --git a/mojo/embedder/platform_support.h b/mojo/embedder/platform_support.h new file mode 100644 index 0000000000000..8b52fdc83905c --- /dev/null +++ b/mojo/embedder/platform_support.h @@ -0,0 +1,40 @@ +// 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_EMBEDDER_PLATFORM_SUPPORT_H_ +#define MOJO_EMBEDDER_PLATFORM_SUPPORT_H_ + +#include + +#include "base/macros.h" +#include "mojo/embedder/scoped_platform_handle.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +class PlatformSharedBuffer; + +// This class is provided by the embedder to implement (typically +// platform-dependent) things needed by the Mojo system implementation. +class MOJO_SYSTEM_IMPL_EXPORT PlatformSupport { + public: + virtual ~PlatformSupport() {} + + virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) = 0; + virtual PlatformSharedBuffer* CreateSharedBufferFromHandle( + size_t num_bytes, + ScopedPlatformHandle platform_handle) = 0; + + protected: + PlatformSupport() {} + + private: + DISALLOW_COPY_AND_ASSIGN(PlatformSupport); +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EMBEDDER_PLATFORM_SUPPORT_H_ diff --git a/mojo/embedder/simple_platform_shared_buffer.cc b/mojo/embedder/simple_platform_shared_buffer.cc new file mode 100644 index 0000000000000..278de00069f21 --- /dev/null +++ b/mojo/embedder/simple_platform_shared_buffer.cc @@ -0,0 +1,108 @@ +// 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/embedder/simple_platform_shared_buffer.h" + +#include "base/logging.h" +#include "mojo/embedder/platform_handle_utils.h" + +namespace mojo { +namespace embedder { + +// static +SimplePlatformSharedBuffer* SimplePlatformSharedBuffer::Create( + size_t num_bytes) { + DCHECK_GT(num_bytes, 0u); + + SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes); + if (!rv->Init()) { + // We can't just delete it directly, due to the "in destructor" (debug) + // check. + scoped_refptr deleter(rv); + return NULL; + } + + return rv; +} + +// static +SimplePlatformSharedBuffer* +SimplePlatformSharedBuffer::CreateFromPlatformHandle( + size_t num_bytes, + ScopedPlatformHandle platform_handle) { + DCHECK_GT(num_bytes, 0u); + + SimplePlatformSharedBuffer* rv = new SimplePlatformSharedBuffer(num_bytes); + if (!rv->InitFromPlatformHandle(platform_handle.Pass())) { + // We can't just delete it directly, due to the "in destructor" (debug) + // check. + scoped_refptr deleter(rv); + return NULL; + } + + return rv; +} + +size_t SimplePlatformSharedBuffer::GetNumBytes() const { + return num_bytes_; +} + +scoped_ptr SimplePlatformSharedBuffer::Map( + size_t offset, + size_t length) { + if (!IsValidMap(offset, length)) + return scoped_ptr(); + + return MapNoCheck(offset, length); +} + +bool SimplePlatformSharedBuffer::IsValidMap(size_t offset, size_t length) { + if (offset > num_bytes_ || length == 0) + return false; + + // Note: This is an overflow-safe check of |offset + length > num_bytes_| + // (that |num_bytes >= offset| is verified above). + if (length > num_bytes_ - offset) + return false; + + return true; +} + +scoped_ptr SimplePlatformSharedBuffer::MapNoCheck( + size_t offset, + size_t length) { + DCHECK(IsValidMap(offset, length)); + return MapImpl(offset, length); +} + +ScopedPlatformHandle SimplePlatformSharedBuffer::DuplicatePlatformHandle() { + return mojo::embedder::DuplicatePlatformHandle(handle_.get()); +} + +ScopedPlatformHandle SimplePlatformSharedBuffer::PassPlatformHandle() { + DCHECK(HasOneRef()); + return handle_.Pass(); +} + +SimplePlatformSharedBuffer::SimplePlatformSharedBuffer(size_t num_bytes) + : num_bytes_(num_bytes) { +} + +SimplePlatformSharedBuffer::~SimplePlatformSharedBuffer() { +} + +SimplePlatformSharedBufferMapping::~SimplePlatformSharedBufferMapping() { + Unmap(); +} + +void* SimplePlatformSharedBufferMapping::GetBase() const { + return base_; +} + +size_t SimplePlatformSharedBufferMapping::GetLength() const { + return length_; +} + +} // namespace embedder +} // namespace mojo diff --git a/mojo/embedder/simple_platform_shared_buffer.h b/mojo/embedder/simple_platform_shared_buffer.h new file mode 100644 index 0000000000000..e9f1209713c57 --- /dev/null +++ b/mojo/embedder/simple_platform_shared_buffer.h @@ -0,0 +1,102 @@ +// 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_SYSTEM_SIMPLE_PLATFORM_SHARED_BUFFER_H_ +#define MOJO_SYSTEM_SIMPLE_PLATFORM_SHARED_BUFFER_H_ + +#include + +#include "base/macros.h" +#include "mojo/embedder/platform_shared_buffer.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +// A simple implementation of |PlatformSharedBuffer|. +class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSharedBuffer + : public PlatformSharedBuffer { + public: + // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled). + // |num_bytes| must be nonzero. Returns null on failure. + static SimplePlatformSharedBuffer* Create(size_t num_bytes); + + static SimplePlatformSharedBuffer* CreateFromPlatformHandle( + size_t num_bytes, + ScopedPlatformHandle platform_handle); + + // |PlatformSharedBuffer| implementation: + virtual size_t GetNumBytes() const OVERRIDE; + virtual scoped_ptr Map(size_t offset, + size_t length) OVERRIDE; + virtual bool IsValidMap(size_t offset, size_t length) OVERRIDE; + virtual scoped_ptr MapNoCheck( + size_t offset, + size_t length) OVERRIDE; + virtual ScopedPlatformHandle DuplicatePlatformHandle() OVERRIDE; + virtual ScopedPlatformHandle PassPlatformHandle() OVERRIDE; + + private: + explicit SimplePlatformSharedBuffer(size_t num_bytes); + virtual ~SimplePlatformSharedBuffer(); + + // Implemented in simple_platform_shared_buffer_{posix,win}.cc: + + // This is called by |Create()| before this object is given to anyone. + bool Init(); + + // This is like |Init()|, but for |CreateFromPlatformHandle()|. (Note: It + // should verify that |platform_handle| is an appropriate handle for the + // claimed |num_bytes_|.) + bool InitFromPlatformHandle(ScopedPlatformHandle platform_handle); + + // The platform-dependent part of |Map()|; doesn't check arguments. + scoped_ptr MapImpl(size_t offset, size_t length); + + const size_t num_bytes_; + + // This is set in |Init()|/|InitFromPlatformHandle()| and never modified + // (except by |PassPlatformHandle()|; see the comments above its declaration), + // hence does not need to be protected by a lock. + ScopedPlatformHandle handle_; + + DISALLOW_COPY_AND_ASSIGN(SimplePlatformSharedBuffer); +}; + +// An implementation of |PlatformSharedBufferMapping|, produced by +// |SimplePlatformSharedBuffer|. +class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSharedBufferMapping + : public PlatformSharedBufferMapping { + public: + virtual ~SimplePlatformSharedBufferMapping(); + + virtual void* GetBase() const OVERRIDE; + virtual size_t GetLength() const OVERRIDE; + + private: + friend class SimplePlatformSharedBuffer; + + SimplePlatformSharedBufferMapping(void* base, + size_t length, + void* real_base, + size_t real_length) + : base_(base), + length_(length), + real_base_(real_base), + real_length_(real_length) {} + void Unmap(); + + void* const base_; + const size_t length_; + + void* const real_base_; + const size_t real_length_; + + DISALLOW_COPY_AND_ASSIGN(SimplePlatformSharedBufferMapping); +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_SYSTEM_SIMPLE_PLATFORM_SHARED_BUFFER_H_ diff --git a/mojo/embedder/simple_platform_shared_buffer_posix.cc b/mojo/embedder/simple_platform_shared_buffer_posix.cc new file mode 100644 index 0000000000000..f5da0a882467e --- /dev/null +++ b/mojo/embedder/simple_platform_shared_buffer_posix.cc @@ -0,0 +1,157 @@ +// 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/embedder/simple_platform_shared_buffer.h" + +#include +#include // For |fileno()|. +#include // For |mmap()|/|munmap()|. +#include +#include // For |off_t|. +#include + +#include + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/posix/eintr_wrapper.h" +#include "base/sys_info.h" +#include "base/threading/thread_restrictions.h" +#include "mojo/embedder/platform_handle.h" + +// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a +// |uint64_t|. +COMPILE_ASSERT(sizeof(size_t) <= sizeof(uint64_t), size_t_too_big); +COMPILE_ASSERT(sizeof(off_t) <= sizeof(uint64_t), off_t_too_big); + +namespace mojo { +namespace embedder { + +// SimplePlatformSharedBuffer -------------------------------------------------- + +bool SimplePlatformSharedBuffer::Init() { + DCHECK(!handle_.is_valid()); + + base::ThreadRestrictions::ScopedAllowIO allow_io; + + if (static_cast(num_bytes_) > + static_cast(std::numeric_limits::max())) { + return false; + } + + // TODO(vtl): This is stupid. The implementation of + // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a + // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we + // can own. (base/memory/shared_memory_posix.cc does this too, with more + // |fstat()|s thrown in for good measure.) + base::FilePath shared_buffer_dir; + if (!base::GetShmemTempDir(false, &shared_buffer_dir)) { + LOG(ERROR) << "Failed to get temporary directory for shared memory"; + return false; + } + base::FilePath shared_buffer_file; + base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir( + shared_buffer_dir, &shared_buffer_file)); + if (!fp) { + LOG(ERROR) << "Failed to create/open temporary file for shared memory"; + return false; + } + // Note: |unlink()| is not interruptible. + if (unlink(shared_buffer_file.value().c_str()) != 0) { + PLOG(WARNING) << "unlink"; + // This isn't "fatal" (e.g., someone else may have unlinked the file first), + // so we may as well continue. + } + + // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are). + base::ScopedFD fd(dup(fileno(fp.get()))); + if (!fd.is_valid()) { + PLOG(ERROR) << "dup"; + return false; + } + + if (HANDLE_EINTR(ftruncate(fd.get(), static_cast(num_bytes_))) != 0) { + PLOG(ERROR) << "ftruncate"; + return false; + } + + handle_.reset(PlatformHandle(fd.release())); + return true; +} + +bool SimplePlatformSharedBuffer::InitFromPlatformHandle( + ScopedPlatformHandle platform_handle) { + DCHECK(!handle_.is_valid()); + + if (static_cast(num_bytes_) > + static_cast(std::numeric_limits::max())) { + return false; + } + + struct stat sb = {}; + // Note: |fstat()| isn't interruptible. + if (fstat(platform_handle.get().fd, &sb) != 0) { + PLOG(ERROR) << "fstat"; + return false; + } + + if (!S_ISREG(sb.st_mode)) { + LOG(ERROR) << "Platform handle not to a regular file"; + return false; + } + + if (sb.st_size != static_cast(num_bytes_)) { + LOG(ERROR) << "Shared memory file has the wrong size"; + return false; + } + + // TODO(vtl): More checks? + + handle_ = platform_handle.Pass(); + return true; +} + +scoped_ptr SimplePlatformSharedBuffer::MapImpl( + size_t offset, + size_t length) { + size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity(); + size_t real_offset = offset - offset_rounding; + size_t real_length = length + offset_rounding; + + // This should hold (since we checked |num_bytes| versus the maximum value of + // |off_t| on creation, but it never hurts to be paranoid. + DCHECK_LE(static_cast(real_offset), + static_cast(std::numeric_limits::max())); + + void* real_base = mmap(NULL, + real_length, + PROT_READ | PROT_WRITE, + MAP_SHARED, + handle_.get().fd, + static_cast(real_offset)); + // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't + // return null either. + if (real_base == MAP_FAILED || !real_base) { + PLOG(ERROR) << "mmap"; + return scoped_ptr(); + } + + void* base = static_cast(real_base) + offset_rounding; + return scoped_ptr( + new SimplePlatformSharedBufferMapping( + base, length, real_base, real_length)); +} + +// SimplePlatformSharedBufferMapping ------------------------------------------- + +void SimplePlatformSharedBufferMapping::Unmap() { + int result = munmap(real_base_, real_length_); + PLOG_IF(ERROR, result != 0) << "munmap"; +} + +} // namespace embedder +} // namespace mojo diff --git a/mojo/embedder/simple_platform_shared_buffer_unittest.cc b/mojo/embedder/simple_platform_shared_buffer_unittest.cc new file mode 100644 index 0000000000000..a3a7c6e623a95 --- /dev/null +++ b/mojo/embedder/simple_platform_shared_buffer_unittest.cc @@ -0,0 +1,187 @@ +// 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/embedder/simple_platform_shared_buffer.h" + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace embedder { +namespace { + +TEST(SimplePlatformSharedBufferTest, Basic) { + const size_t kNumInts = 100; + const size_t kNumBytes = kNumInts * sizeof(int); + // A fudge so that we're not just writing zero bytes 75% of the time. + const int kFudge = 1234567890; + + // Make some memory. + scoped_refptr buffer( + SimplePlatformSharedBuffer::Create(kNumBytes)); + ASSERT_TRUE(buffer); + + // Map it all, scribble some stuff, and then unmap it. + { + EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes)); + scoped_ptr mapping(buffer->Map(0, kNumBytes)); + ASSERT_TRUE(mapping); + ASSERT_TRUE(mapping->GetBase()); + int* stuff = static_cast(mapping->GetBase()); + for (size_t i = 0; i < kNumInts; i++) + stuff[i] = static_cast(i) + kFudge; + } + + // Map it all again, check that our scribbling is still there, then do a + // partial mapping and scribble on that, check that everything is coherent, + // unmap the first mapping, scribble on some of the second mapping, and then + // unmap it. + { + ASSERT_TRUE(buffer->IsValidMap(0, kNumBytes)); + // Use |MapNoCheck()| this time. + scoped_ptr mapping1( + buffer->MapNoCheck(0, kNumBytes)); + ASSERT_TRUE(mapping1); + ASSERT_TRUE(mapping1->GetBase()); + int* stuff1 = static_cast(mapping1->GetBase()); + for (size_t i = 0; i < kNumInts; i++) + EXPECT_EQ(static_cast(i) + kFudge, stuff1[i]) << i; + + scoped_ptr mapping2( + buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int))); + ASSERT_TRUE(mapping2); + ASSERT_TRUE(mapping2->GetBase()); + int* stuff2 = static_cast(mapping2->GetBase()); + EXPECT_EQ(static_cast(kNumInts / 2) + kFudge, stuff2[0]); + EXPECT_EQ(static_cast(kNumInts / 2) + 1 + kFudge, stuff2[1]); + + stuff2[0] = 123; + stuff2[1] = 456; + EXPECT_EQ(123, stuff1[kNumInts / 2]); + EXPECT_EQ(456, stuff1[kNumInts / 2 + 1]); + + mapping1.reset(); + + EXPECT_EQ(123, stuff2[0]); + EXPECT_EQ(456, stuff2[1]); + stuff2[1] = 789; + } + + // Do another partial mapping and check that everything is the way we expect + // it to be. + { + EXPECT_TRUE(buffer->IsValidMap(sizeof(int), kNumBytes - sizeof(int))); + scoped_ptr mapping( + buffer->Map(sizeof(int), kNumBytes - sizeof(int))); + ASSERT_TRUE(mapping); + ASSERT_TRUE(mapping->GetBase()); + int* stuff = static_cast(mapping->GetBase()); + + for (size_t j = 0; j < kNumInts - 1; j++) { + int i = static_cast(j) + 1; + if (i == kNumInts / 2) { + EXPECT_EQ(123, stuff[j]); + } else if (i == kNumInts / 2 + 1) { + EXPECT_EQ(789, stuff[j]); + } else { + EXPECT_EQ(i + kFudge, stuff[j]) << i; + } + } + } +} + +// TODO(vtl): Bigger buffers. + +TEST(SimplePlatformSharedBufferTest, InvalidMappings) { + scoped_refptr buffer( + SimplePlatformSharedBuffer::Create(100)); + ASSERT_TRUE(buffer); + + // Zero length not allowed. + EXPECT_FALSE(buffer->Map(0, 0)); + EXPECT_FALSE(buffer->IsValidMap(0, 0)); + + // Okay: + EXPECT_TRUE(buffer->Map(0, 100)); + EXPECT_TRUE(buffer->IsValidMap(0, 100)); + // Offset + length too big. + EXPECT_FALSE(buffer->Map(0, 101)); + EXPECT_FALSE(buffer->IsValidMap(0, 101)); + EXPECT_FALSE(buffer->Map(1, 100)); + EXPECT_FALSE(buffer->IsValidMap(1, 100)); + + // Okay: + EXPECT_TRUE(buffer->Map(50, 50)); + EXPECT_TRUE(buffer->IsValidMap(50, 50)); + // Offset + length too big. + EXPECT_FALSE(buffer->Map(50, 51)); + EXPECT_FALSE(buffer->IsValidMap(50, 51)); + EXPECT_FALSE(buffer->Map(51, 50)); + EXPECT_FALSE(buffer->IsValidMap(51, 50)); +} + +TEST(SimplePlatformSharedBufferTest, TooBig) { + // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds + // (since it only involves creating a 4 GB file). + const size_t kMaxSizeT = std::numeric_limits::max(); + scoped_refptr buffer( + SimplePlatformSharedBuffer::Create(kMaxSizeT)); + // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should + // always fail. + if (buffer) + EXPECT_FALSE(buffer->Map(0, kMaxSizeT)); +} + +// Tests that separate mappings get distinct addresses. +// Note: It's not inconceivable that the OS could ref-count identical mappings +// and reuse the same address, in which case we'd have to be more careful about +// using the address as the key for unmapping. +TEST(SimplePlatformSharedBufferTest, MappingsDistinct) { + scoped_refptr buffer( + SimplePlatformSharedBuffer::Create(100)); + scoped_ptr mapping1(buffer->Map(0, 100)); + scoped_ptr mapping2(buffer->Map(0, 100)); + EXPECT_NE(mapping1->GetBase(), mapping2->GetBase()); +} + +TEST(SimplePlatformSharedBufferTest, BufferZeroInitialized) { + static const size_t kSizes[] = {10, 100, 1000, 10000, 100000}; + for (size_t i = 0; i < arraysize(kSizes); i++) { + scoped_refptr buffer( + SimplePlatformSharedBuffer::Create(kSizes[i])); + scoped_ptr mapping(buffer->Map(0, kSizes[i])); + for (size_t j = 0; j < kSizes[i]; j++) { + // "Assert" instead of "expect" so we don't spam the output with thousands + // of failures if we fail. + ASSERT_EQ('\0', static_cast(mapping->GetBase())[j]) + << "size " << kSizes[i] << ", offset " << j; + } + } +} + +TEST(SimplePlatformSharedBufferTest, MappingsOutliveBuffer) { + scoped_ptr mapping1; + scoped_ptr mapping2; + + { + scoped_refptr buffer( + SimplePlatformSharedBuffer::Create(100)); + mapping1 = buffer->Map(0, 100).Pass(); + mapping2 = buffer->Map(50, 50).Pass(); + static_cast(mapping1->GetBase())[50] = 'x'; + } + + EXPECT_EQ('x', static_cast(mapping2->GetBase())[0]); + + static_cast(mapping2->GetBase())[1] = 'y'; + EXPECT_EQ('y', static_cast(mapping1->GetBase())[51]); +} + +} // namespace +} // namespace embedder +} // namespace mojo diff --git a/mojo/embedder/simple_platform_shared_buffer_win.cc b/mojo/embedder/simple_platform_shared_buffer_win.cc new file mode 100644 index 0000000000000..d2caa2e94dfb8 --- /dev/null +++ b/mojo/embedder/simple_platform_shared_buffer_win.cc @@ -0,0 +1,95 @@ +// 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/embedder/simple_platform_shared_buffer.h" + +#include + +#include + +#include "base/logging.h" +#include "base/sys_info.h" +#include "mojo/embedder/platform_handle.h" +#include "mojo/embedder/scoped_platform_handle.h" + +namespace mojo { +namespace embedder { + +// SimplePlatformSharedBuffer -------------------------------------------------- + +bool SimplePlatformSharedBuffer::Init() { + DCHECK(!handle_.is_valid()); + + // TODO(vtl): Currently, we only support mapping up to 2^32-1 bytes. + if (static_cast(num_bytes_) > + static_cast(std::numeric_limits::max())) { + return false; + } + + // IMPORTANT NOTE: Unnamed objects are NOT SECURABLE. Thus if we ever want to + // share read-only to other processes, we'll have to name our file mapping + // object. + // TODO(vtl): Unlike |base::SharedMemory|, we don't round up the size (to a + // multiple of 64 KB). This may cause problems with NaCl. Cross this bridge + // when we get there. crbug.com/210609 + handle_.reset(PlatformHandle(CreateFileMapping(INVALID_HANDLE_VALUE, + NULL, + PAGE_READWRITE, + 0, + static_cast(num_bytes_), + NULL))); + if (!handle_.is_valid()) { + PLOG(ERROR) << "CreateFileMapping"; + return false; + } + + return true; +} + +bool SimplePlatformSharedBuffer::InitFromPlatformHandle( + ScopedPlatformHandle platform_handle) { + DCHECK(!handle_.is_valid()); + + // TODO(vtl): Implement. + NOTIMPLEMENTED(); + return false; +} + +scoped_ptr SimplePlatformSharedBuffer::MapImpl( + size_t offset, + size_t length) { + size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity(); + size_t real_offset = offset - offset_rounding; + size_t real_length = length + offset_rounding; + + // This should hold (since we checked |num_bytes| versus the maximum value of + // |off_t| on creation, but it never hurts to be paranoid. + DCHECK_LE(static_cast(real_offset), + static_cast(std::numeric_limits::max())); + + void* real_base = MapViewOfFile(handle_.get().handle, + FILE_MAP_READ | FILE_MAP_WRITE, + 0, + static_cast(real_offset), + real_length); + if (!real_base) { + PLOG(ERROR) << "MapViewOfFile"; + return scoped_ptr(); + } + + void* base = static_cast(real_base) + offset_rounding; + return scoped_ptr( + new SimplePlatformSharedBufferMapping( + base, length, real_base, real_length)); +} + +// SimplePlatformSharedBufferMapping ------------------------------------------- + +void SimplePlatformSharedBufferMapping::Unmap() { + BOOL result = UnmapViewOfFile(real_base_); + PLOG_IF(ERROR, !result) << "UnmapViewOfFile"; +} + +} // namespace embedder +} // namespace mojo diff --git a/mojo/embedder/simple_platform_support.cc b/mojo/embedder/simple_platform_support.cc new file mode 100644 index 0000000000000..1c7f82bcb5c4c --- /dev/null +++ b/mojo/embedder/simple_platform_support.cc @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/embedder/simple_platform_support.h" + +#include "mojo/embedder/simple_platform_shared_buffer.h" + +namespace mojo { +namespace embedder { + +PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBuffer( + size_t num_bytes) { + return SimplePlatformSharedBuffer::Create(num_bytes); +} + +PlatformSharedBuffer* SimplePlatformSupport::CreateSharedBufferFromHandle( + size_t num_bytes, + ScopedPlatformHandle platform_handle) { + return SimplePlatformSharedBuffer::CreateFromPlatformHandle( + num_bytes, platform_handle.Pass()); +} + +} // namespace embedder +} // namespace mojo diff --git a/mojo/embedder/simple_platform_support.h b/mojo/embedder/simple_platform_support.h new file mode 100644 index 0000000000000..899c0271bd2cf --- /dev/null +++ b/mojo/embedder/simple_platform_support.h @@ -0,0 +1,37 @@ +// 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_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_ +#define MOJO_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_ + +#include "base/macros.h" +#include "mojo/embedder/platform_support.h" +#include "mojo/system/system_impl_export.h" + +namespace mojo { +namespace embedder { + +// A simple implementation of |PlatformSupport|, when sandboxing and +// multiprocess support are not issues (e.g., in most tests). Note: This class +// has no state, and different instances of |SimplePlatformSupport| are mutually +// compatible (i.e., you don't need to use a single instance of it everywhere -- +// you may simply create one whenever/wherever you need it). +class MOJO_SYSTEM_IMPL_EXPORT SimplePlatformSupport : public PlatformSupport { + public: + SimplePlatformSupport() {} + virtual ~SimplePlatformSupport() {} + + virtual PlatformSharedBuffer* CreateSharedBuffer(size_t num_bytes) OVERRIDE; + virtual PlatformSharedBuffer* CreateSharedBufferFromHandle( + size_t num_bytes, + ScopedPlatformHandle platform_handle) OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(SimplePlatformSupport); +}; + +} // namespace embedder +} // namespace mojo + +#endif // MOJO_EMBEDDER_SIMPLE_PLATFORM_SUPPORT_H_ diff --git a/mojo/environment/BUILD.gn b/mojo/environment/BUILD.gn index 5c9fe752827d4..cba8cac18845c 100644 --- a/mojo/environment/BUILD.gn +++ b/mojo/environment/BUILD.gn @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# GYP version: mojo.gyp:mojo_environment_chromium +# GYP version: mojo_base.gyp:mojo_environment_chromium source_set("chromium") { output_name = "mojo_environment_chromium" @@ -22,7 +22,7 @@ source_set("chromium") { ] } -# GYP version: mojo.gyp:mojo_environment_chromium_impl +# GYP version: mojo_base.gyp:mojo_environment_chromium_impl component("chromium_impl") { output_name = "mojo_environment_impl" visibility = "//mojo/*" diff --git a/mojo/examples/apptest/example_apptest.cc b/mojo/examples/apptest/example_apptest.cc new file mode 100644 index 0000000000000..2324b14417b8a --- /dev/null +++ b/mojo/examples/apptest/example_apptest.cc @@ -0,0 +1,92 @@ +// 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/examples/apptest/example_client_impl.h" +#include "mojo/examples/apptest/example_service.mojom.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/application_impl.h" +#include "mojo/public/cpp/bindings/callback.h" +#include "mojo/public/cpp/environment/environment.h" +#include "mojo/public/cpp/system/macros.h" +#include "mojo/public/cpp/utility/run_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// TODO(msw): Remove this once we can get ApplicationImpl from TLS. +mojo::ApplicationImpl* g_application_impl_hack = NULL; + +} // namespace + +namespace mojo { + +namespace { + +class ExampleServiceTest : public testing::Test { + public: + ExampleServiceTest() { + g_application_impl_hack->ConnectToService("mojo:mojo_example_service", + &example_service_); + example_service_.set_client(&example_client_); + } + + virtual ~ExampleServiceTest() MOJO_OVERRIDE {} + + protected: + ExampleServicePtr example_service_; + ExampleClientImpl example_client_; + + private: + MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleServiceTest); +}; + +TEST_F(ExampleServiceTest, Ping) { + EXPECT_EQ(0, example_client_.last_pong_value()); + example_service_->Ping(1); + RunLoop::current()->Run(); + EXPECT_EQ(1, example_client_.last_pong_value()); +} + +template +struct SetAndQuit : public Callback::Runnable { + SetAndQuit(T* val, T result) : val_(val), result_(result) {} + virtual ~SetAndQuit() {} + virtual void Run() const MOJO_OVERRIDE{ + *val_ = result_; + RunLoop::current()->Quit(); + } + T* val_; + T result_; +}; + +TEST_F(ExampleServiceTest, RunCallback) { + bool was_run = false; + example_service_->RunCallback(SetAndQuit(&was_run, true)); + RunLoop::current()->Run(); + EXPECT_TRUE(was_run); +} + +} // namespace + +} // namespace mojo + +extern "C" APPLICATION_EXPORT MojoResult CDECL MojoMain( + MojoHandle shell_handle) { + mojo::Environment env; + mojo::RunLoop loop; + + mojo::ApplicationDelegate* delegate = mojo::ApplicationDelegate::Create(); + mojo::ApplicationImpl app(delegate); + app.BindShell(shell_handle); + g_application_impl_hack = &app; + + // TODO(msw): Get actual commandline arguments. + int argc = 0; + char** argv = NULL; + testing::InitGoogleTest(&argc, argv); + mojo_ignore_result(RUN_ALL_TESTS()); + + delete delegate; + return MOJO_RESULT_OK; +} diff --git a/mojo/examples/apptest/example_client_application.cc b/mojo/examples/apptest/example_client_application.cc new file mode 100644 index 0000000000000..196d85a7ed381 --- /dev/null +++ b/mojo/examples/apptest/example_client_application.cc @@ -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. + +#include "mojo/examples/apptest/example_client_application.h" + +#include "mojo/examples/apptest/example_client_impl.h" + +namespace mojo { + +ExampleClientApplication::ExampleClientApplication() {} + +ExampleClientApplication::~ExampleClientApplication() {} + +void ExampleClientApplication::Initialize(ApplicationImpl* app) {} + +// static +ApplicationDelegate* ApplicationDelegate::Create() { + return new ExampleClientApplication(); +} + +} // namespace mojo diff --git a/mojo/examples/apptest/example_client_application.h b/mojo/examples/apptest/example_client_application.h new file mode 100644 index 0000000000000..e256d21869e20 --- /dev/null +++ b/mojo/examples/apptest/example_client_application.h @@ -0,0 +1,28 @@ +// 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_EXAMPLES_TEST_EXAMPLE_CLIENT_APPLICATION_H_ +#define MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_APPLICATION_H_ + +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/interface_factory.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class ExampleClientApplication : public ApplicationDelegate { + public: + ExampleClientApplication(); + virtual ~ExampleClientApplication(); + + private: + // ApplicationDelegate implementation. + virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleClientApplication); +}; + +} // namespace mojo + +#endif // MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_APPLICATION_H_ diff --git a/mojo/examples/apptest/example_client_impl.cc b/mojo/examples/apptest/example_client_impl.cc new file mode 100644 index 0000000000000..9d539bea05e99 --- /dev/null +++ b/mojo/examples/apptest/example_client_impl.cc @@ -0,0 +1,18 @@ +// 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/examples/apptest/example_client_impl.h" +#include "mojo/public/cpp/utility/run_loop.h" + +namespace mojo { + +ExampleClientImpl::ExampleClientImpl() : last_pong_value_(0) {} +ExampleClientImpl::~ExampleClientImpl() {} + +void ExampleClientImpl::Pong(uint16_t pong_value) { + last_pong_value_ = pong_value; + RunLoop::current()->Quit(); +} + +} // namespace mojo diff --git a/mojo/examples/apptest/example_client_impl.h b/mojo/examples/apptest/example_client_impl.h new file mode 100644 index 0000000000000..739778c375764 --- /dev/null +++ b/mojo/examples/apptest/example_client_impl.h @@ -0,0 +1,32 @@ +// 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_EXAMPLES_TEST_EXAMPLE_CLIENT_IMPL_H_ +#define MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_IMPL_H_ + +#include "mojo/examples/apptest/example_service.mojom.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class ApplicationConnection; + +class ExampleClientImpl : public InterfaceImpl { + public: + explicit ExampleClientImpl(); + virtual ~ExampleClientImpl(); + + int16_t last_pong_value() const { return last_pong_value_; } + + private: + // InterfaceImpl overrides. + virtual void Pong(uint16_t pong_value) MOJO_OVERRIDE; + + int16_t last_pong_value_; + MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleClientImpl); +}; + +} // namespace mojo + +#endif // MOJO_EXAMPLES_TEST_EXAMPLE_CLIENT_IMPL_H_ diff --git a/mojo/examples/apptest/example_service.mojom b/mojo/examples/apptest/example_service.mojom new file mode 100644 index 0000000000000..a2226fa3bf890 --- /dev/null +++ b/mojo/examples/apptest/example_service.mojom @@ -0,0 +1,21 @@ +// 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. + +module mojo { + +// A simple service used to exemplify application testing within mojo_shell. +[Client=ExampleClient] +interface ExampleService { + // Calls ExampleClient::Pong with |ping_value|. + Ping(uint16 ping_value); + + // Runs the argument callback. + RunCallback() => (); +}; + +interface ExampleClient { + Pong(uint16 pong_value); +}; + +} diff --git a/mojo/examples/apptest/example_service_application.cc b/mojo/examples/apptest/example_service_application.cc new file mode 100644 index 0000000000000..5211df53caf61 --- /dev/null +++ b/mojo/examples/apptest/example_service_application.cc @@ -0,0 +1,26 @@ +// 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/examples/apptest/example_service_application.h" + +#include "mojo/public/cpp/application/application_connection.h" + +namespace mojo { + +ExampleServiceApplication::ExampleServiceApplication() {} + +ExampleServiceApplication::~ExampleServiceApplication() {} + +bool ExampleServiceApplication::ConfigureIncomingConnection( + ApplicationConnection* connection) { + connection->AddService(&example_service_factory_); + return true; +} + +// static +ApplicationDelegate* ApplicationDelegate::Create() { + return new ExampleServiceApplication(); +} + +} // namespace mojo diff --git a/mojo/examples/apptest/example_service_application.h b/mojo/examples/apptest/example_service_application.h new file mode 100644 index 0000000000000..249174825dd4f --- /dev/null +++ b/mojo/examples/apptest/example_service_application.h @@ -0,0 +1,34 @@ +// 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_EXAMPLES_TEST_EXAMPLE_SERVICE_APPLICATION_H_ +#define MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_APPLICATION_H_ + +#include "mojo/examples/apptest/example_service_impl.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/interface_factory_impl.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class ApplicationConnection; + +class ExampleServiceApplication : public ApplicationDelegate { + public: + ExampleServiceApplication(); + virtual ~ExampleServiceApplication(); + + private: + // ApplicationDelegate implementation. + virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) + MOJO_OVERRIDE; + + InterfaceFactoryImpl example_service_factory_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleServiceApplication); +}; + +} // namespace mojo + +#endif // MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_APPLICATION_H_ diff --git a/mojo/examples/apptest/example_service_impl.cc b/mojo/examples/apptest/example_service_impl.cc new file mode 100644 index 0000000000000..b656ce0425861 --- /dev/null +++ b/mojo/examples/apptest/example_service_impl.cc @@ -0,0 +1,25 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/examples/apptest/example_service_impl.h" + +#include "mojo/public/cpp/utility/run_loop.h" + +namespace mojo { + +ExampleServiceImpl::ExampleServiceImpl() {} + +ExampleServiceImpl::~ExampleServiceImpl() {} + +void ExampleServiceImpl::Ping(uint16_t ping_value) { + client()->Pong(ping_value); + RunLoop::current()->Quit(); +} + +void ExampleServiceImpl::RunCallback(const Callback& callback) { + callback.Run(); + RunLoop::current()->Quit(); +} + +} // namespace mojo diff --git a/mojo/examples/apptest/example_service_impl.h b/mojo/examples/apptest/example_service_impl.h new file mode 100644 index 0000000000000..727e30fb8fada --- /dev/null +++ b/mojo/examples/apptest/example_service_impl.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_IMPL_H_ +#define MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_IMPL_H_ + +#include "mojo/examples/apptest/example_service.mojom.h" +#include "mojo/public/cpp/system/macros.h" + +namespace mojo { + +class ApplicationConnection; + +class ExampleServiceImpl : public InterfaceImpl { + public: + ExampleServiceImpl(); + virtual ~ExampleServiceImpl(); + + private: + // InterfaceImpl overrides. + virtual void Ping(uint16_t ping_value) MOJO_OVERRIDE; + virtual void RunCallback(const Callback& callback) MOJO_OVERRIDE; + + MOJO_DISALLOW_COPY_AND_ASSIGN(ExampleServiceImpl); +}; + +} // namespace mojo + +#endif // MOJO_EXAMPLES_TEST_EXAMPLE_SERVICE_IMPL_H_ diff --git a/mojo/examples/aura_demo/aura_demo.cc b/mojo/examples/aura_demo/aura_demo.cc index 0f8bc5726c5be..cbed7f0945124 100644 --- a/mojo/examples/aura_demo/aura_demo.cc +++ b/mojo/examples/aura_demo/aura_demo.cc @@ -13,7 +13,6 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/system/core.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" @@ -112,19 +111,17 @@ class AuraDemo : public ApplicationDelegate, : window1_(NULL), window2_(NULL), window21_(NULL), - view_(NULL), view_manager_client_factory_(this) {} virtual ~AuraDemo() {} private: // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { // TODO(beng): this function could be called multiple times! - view_ = View::Create(view_manager); - root->SetActiveView(view_); + root_ = root; window_tree_host_.reset(new WindowTreeHostMojo(root, this)); window_tree_host_->InitHost(); @@ -162,7 +159,7 @@ class AuraDemo : public ApplicationDelegate, // WindowTreeHostMojoDelegate: virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE { - view_->SetContents(bitmap); + root_->SetContents(bitmap); } virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE { @@ -193,7 +190,7 @@ class AuraDemo : public ApplicationDelegate, aura::Window* window2_; aura::Window* window21_; - View* view_; + View* root_; ViewManagerClientFactory view_manager_client_factory_; diff --git a/mojo/examples/browser/browser.cc b/mojo/examples/browser/browser.cc index 42bc423f6a59d..bd854949ca658 100644 --- a/mojo/examples/browser/browser.cc +++ b/mojo/examples/browser/browser.cc @@ -11,12 +11,10 @@ #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" -#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "mojo/services/public/interfaces/navigation/navigation.mojom.h" #include "mojo/views/native_widget_view_manager.h" #include "mojo/views/views_init.h" @@ -70,10 +68,10 @@ class KeyboardManager public: KeyboardManager(views::Widget* widget, IWindowManager* window_manager, - Node* node) + View* view) : widget_(widget), window_manager_(window_manager), - node_(node), + view_(view), last_view_id_(0), focused_view_(NULL) { widget_->GetFocusManager()->AddFocusChangeListener(this); @@ -96,7 +94,7 @@ class KeyboardManager const gfx::Rect bounds_in_widget = view->ConvertRectToWidget(gfx::Rect(view->bounds().size())); - last_view_id_ = node_->active_view()->id(); + last_view_id_ = view_->id(); window_manager_->ShowKeyboard(last_view_id_, Rect::From(bounds_in_widget)); // TODO(sky): listen for view to be removed. @@ -139,7 +137,7 @@ class KeyboardManager views::Widget* widget_; IWindowManager* window_manager_; - Node* node_; + View* view_; Id last_view_id_; views::View* focused_view_; @@ -151,7 +149,7 @@ class KeyboardManager class Browser : public ApplicationDelegate, public ViewManagerDelegate, public views::TextfieldController, - public NodeObserver { + public ViewObserver { public: Browser() : view_manager_(NULL), @@ -178,7 +176,7 @@ class Browser : public ApplicationDelegate, return true; } - void CreateWidget(Node* node) { + void CreateWidget(View* view) { views::Textfield* textfield = new views::Textfield; textfield->set_controller(this); @@ -192,26 +190,25 @@ class Browser : public ApplicationDelegate, widget_ = new views::Widget; views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.native_widget = new NativeWidgetViewManager(widget_, node); + params.native_widget = new NativeWidgetViewManager(widget_, view); params.delegate = widget_delegate; - params.bounds = gfx::Rect(node->bounds().width(), node->bounds().height()); + params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height()); widget_->Init(params); // KeyboardManager handles deleting itself when the widget is destroyed. - new KeyboardManager(widget_, window_manager_.get(), node); + new KeyboardManager(widget_, window_manager_.get(), view); widget_->Show(); textfield->RequestFocus(); } // ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { // TODO: deal with OnEmbed() being invoked multiple times. view_manager_ = view_manager; root_ = root; root_->AddObserver(this); - root_->SetActiveView(View::Create(view_manager)); root_->SetFocus(); CreateWidget(root_); } @@ -237,9 +234,9 @@ class Browser : public ApplicationDelegate, return false; } - // NodeObserver: - virtual void OnNodeFocusChanged(Node* gained_focus, - Node* lost_focus) OVERRIDE { + // ViewObserver: + virtual void OnViewFocusChanged(View* gained_focus, + View* lost_focus) OVERRIDE { aura::client::FocusClient* focus_client = aura::client::GetFocusClient(widget_->GetNativeView()); if (lost_focus == root_) @@ -247,9 +244,9 @@ class Browser : public ApplicationDelegate, else if (gained_focus == root_) focus_client->FocusWindow(widget_->GetNativeView()); } - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK_EQ(root_, node); - node->RemoveObserver(this); + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK_EQ(root_, view); + view->RemoveObserver(this); root_ = NULL; } @@ -257,7 +254,7 @@ class Browser : public ApplicationDelegate, ViewManager* view_manager_; ViewManagerClientFactory view_manager_client_factory_; - Node* root_; + View* root_; views::Widget* widget_; NavigatorHostPtr navigator_host_; IWindowManagerPtr window_manager_; diff --git a/mojo/examples/compositor_app/compositor_app.cc b/mojo/examples/compositor_app/compositor_app.cc index fd66b90306a1a..513379e6d0fca 100644 --- a/mojo/examples/compositor_app/compositor_app.cc +++ b/mojo/examples/compositor_app/compositor_app.cc @@ -9,7 +9,6 @@ #include "mojo/examples/compositor_app/compositor_host.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" -#include "mojo/public/cpp/gles2/gles2.h" #include "mojo/public/cpp/system/core.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" #include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" @@ -53,7 +52,6 @@ class SampleApp : public ApplicationDelegate, public NativeViewportClient { } private: - mojo::GLES2Initializer gles2; NativeViewportPtr viewport_; scoped_ptr host_; DISALLOW_COPY_AND_ASSIGN(SampleApp); diff --git a/mojo/examples/content_handler_demo/content_handler_demo.cc b/mojo/examples/content_handler_demo/content_handler_demo.cc new file mode 100644 index 0000000000000..21864f75f474e --- /dev/null +++ b/mojo/examples/content_handler_demo/content_handler_demo.cc @@ -0,0 +1,89 @@ +// 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 + +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/application_impl.h" +#include "mojo/public/cpp/application/interface_factory_impl.h" +#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h" + +namespace mojo { +namespace examples { + +class ContentHandlerApp; + +class ContentHandlerImpl : public InterfaceImpl { + public: + explicit ContentHandlerImpl(ContentHandlerApp* content_handler_app) + : content_handler_app_(content_handler_app) { + } + virtual ~ContentHandlerImpl() {} + + private: + virtual void OnConnect(const mojo::String& url, + URLResponsePtr content, + ServiceProviderPtr service_provider) MOJO_OVERRIDE; + + ContentHandlerApp* content_handler_app_; +}; + +class ContentHandlerApp : public ApplicationDelegate { + public: + ContentHandlerApp() : content_handler_factory_(this) { + } + + virtual void Initialize(ApplicationImpl* app) MOJO_OVERRIDE { + } + + virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) + MOJO_OVERRIDE { + connection->AddService(&content_handler_factory_); + return true; + } + + void PrintResponse(ScopedDataPipeConsumerHandle body) const { + for (;;) { + char buf[512]; + uint32_t num_bytes = sizeof(buf); + MojoResult result = ReadDataRaw(body.get(), buf, &num_bytes, + MOJO_READ_DATA_FLAG_NONE); + if (result == MOJO_RESULT_SHOULD_WAIT) { + Wait(body.get(), + MOJO_HANDLE_SIGNAL_READABLE, + MOJO_DEADLINE_INDEFINITE); + } else if (result == MOJO_RESULT_OK) { + if (fwrite(buf, num_bytes, 1, stdout) != 1) { + printf("\nUnexpected error writing to file\n"); + break; + } + } else { + break; + } + + printf("\n>>> EOF <<<\n"); + } + } + + private: + InterfaceFactoryImplWithContext content_handler_factory_; +}; + +void ContentHandlerImpl::OnConnect(const mojo::String& url, + URLResponsePtr content, + ServiceProviderPtr service_provider) { + printf("ContentHandler::OnConnect - url:%s - body follows\n\n", + url.To().c_str()); + content_handler_app_->PrintResponse(content->body.Pass()); +} + +} // namespace examples + +// static +ApplicationDelegate* ApplicationDelegate::Create() { + return new examples::ContentHandlerApp(); +} + +} // namespace mojo diff --git a/mojo/examples/embedded_app/embedded_app.cc b/mojo/examples/embedded_app/embedded_app.cc index f3ce42de683ba..fd5877336a195 100644 --- a/mojo/examples/embedded_app/embedded_app.cc +++ b/mojo/examples/embedded_app/embedded_app.cc @@ -11,8 +11,6 @@ #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/application/interface_factory_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" @@ -33,7 +31,7 @@ class NavigatorImpl : public InterfaceImpl { private: virtual void Navigate( - uint32 node_id, + uint32 view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) OVERRIDE; @@ -44,8 +42,7 @@ class NavigatorImpl : public InterfaceImpl { class EmbeddedApp : public ApplicationDelegate, public ViewManagerDelegate, - public ViewObserver, - public NodeObserver { + public ViewObserver { public: EmbeddedApp() : navigator_factory_(this), @@ -55,9 +52,9 @@ class EmbeddedApp } virtual ~EmbeddedApp() {} - void SetNodeColor(uint32 node_id, SkColor color) { - pending_node_colors_[node_id] = color; - ProcessPendingNodeColor(node_id); + void SetViewColor(uint32 view_id, SkColor color) { + pending_view_colors_[view_id] = color; + ProcessPendingViewColor(view_id); } private: @@ -79,61 +76,45 @@ class EmbeddedApp // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { - View* view = View::Create(view_manager); - view->AddObserver(this); - root->SetActiveView(view); root->AddObserver(this); - roots_[root->id()] = root; - ProcessPendingNodeColor(root->id()); + ProcessPendingViewColor(root->id()); } virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE { base::MessageLoop::current()->Quit(); } // Overridden from ViewObserver: + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK(roots_.find(view->id()) != roots_.end()); + roots_.erase(view->id()); + } virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE { if (event->action == EVENT_TYPE_MOUSE_RELEASED) { if (event->flags & EVENT_FLAGS_LEFT_MOUSE_BUTTON) { NavigationDetailsPtr nav_details(NavigationDetails::New()); nav_details->request->url = "http://www.aaronboodman.com/z_dropbox/test.html"; - navigator_host_->RequestNavigate(view->node()->id(), - TARGET_SOURCE_NODE, + navigator_host_->RequestNavigate(view->id(), TARGET_SOURCE_NODE, nav_details.Pass()); } } } - // Overridden from NodeObserver: - virtual void OnNodeActiveViewChanged(Node* node, - View* old_view, - View* new_view) OVERRIDE { - if (new_view == 0) - views_to_reap_[node] = old_view; - } - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK(roots_.find(node->id()) != roots_.end()); - roots_.erase(node->id()); - std::map::const_iterator it = views_to_reap_.find(node); - if (it != views_to_reap_.end()) - it->second->Destroy(); - } - - void ProcessPendingNodeColor(uint32 node_id) { - RootMap::iterator root = roots_.find(node_id); + void ProcessPendingViewColor(uint32 view_id) { + RootMap::iterator root = roots_.find(view_id); if (root == roots_.end()) return; - PendingNodeColors::iterator color = pending_node_colors_.find(node_id); - if (color == pending_node_colors_.end()) + PendingViewColors::iterator color = pending_view_colors_.find(view_id); + if (color == pending_view_colors_.end()) return; - root->second->active_view()->SetColor(color->second); - pending_node_colors_.erase(color); + root->second->SetColor(color->second); + pending_view_colors_.erase(color); } InterfaceFactoryImplWithContext @@ -141,20 +122,19 @@ class EmbeddedApp ViewManager* view_manager_; NavigatorHostPtr navigator_host_; - std::map views_to_reap_; ViewManagerClientFactory view_manager_client_factory_; - typedef std::map RootMap; + typedef std::map RootMap; RootMap roots_; - // We can receive navigations for nodes we don't have yet. - typedef std::map PendingNodeColors; - PendingNodeColors pending_node_colors_; + // We can receive navigations for views we don't have yet. + typedef std::map PendingViewColors; + PendingViewColors pending_view_colors_; DISALLOW_COPY_AND_ASSIGN(EmbeddedApp); }; -void NavigatorImpl::Navigate(uint32 node_id, +void NavigatorImpl::Navigate(uint32 view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) { GURL url(navigation_details->request->url.To()); @@ -168,7 +148,7 @@ void NavigatorImpl::Navigate(uint32 node_id, LOG(ERROR) << "Invalid URL, path not convertible to integer"; return; } - app_->SetNodeColor(node_id, color); + app_->SetViewColor(view_id, color); } } // namespace examples diff --git a/mojo/examples/keyboard/keyboard.cc b/mojo/examples/keyboard/keyboard.cc index d7599463dc77c..e033627610ef8 100644 --- a/mojo/examples/keyboard/keyboard.cc +++ b/mojo/examples/keyboard/keyboard.cc @@ -11,12 +11,10 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/interface_factory_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" -#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "mojo/services/public/interfaces/navigation/navigation.mojom.h" #include "mojo/views/native_widget_view_manager.h" #include "mojo/views/views_init.h" @@ -37,7 +35,7 @@ class KeyboardServiceImpl : public InterfaceImpl { virtual ~KeyboardServiceImpl() {} // KeyboardService: - virtual void SetTarget(uint32_t node_id) OVERRIDE; + virtual void SetTarget(uint32_t view_id) OVERRIDE; private: Keyboard* keyboard_; @@ -75,7 +73,7 @@ class Keyboard : public ApplicationDelegate, return true; } - void CreateWidget(Node* node) { + void CreateWidget(View* view) { views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView; widget_delegate->GetContentsView()->AddChildView(new KeyboardView(this)); widget_delegate->GetContentsView()->SetLayoutManager(new views::FillLayout); @@ -83,21 +81,20 @@ class Keyboard : public ApplicationDelegate, views::Widget* widget = new views::Widget; views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.native_widget = new NativeWidgetViewManager(widget, node); + params.native_widget = new NativeWidgetViewManager(widget, view); params.delegate = widget_delegate; - params.bounds = gfx::Rect(node->bounds().width(), node->bounds().height()); + params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height()); widget->Init(params); widget->Show(); } // ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { // TODO: deal with OnEmbed() being invoked multiple times. view_manager_ = view_manager; - root->SetActiveView(View::Create(view_manager)); CreateWidget(root); } virtual void OnViewManagerDisconnected( @@ -135,8 +132,8 @@ KeyboardServiceImpl::KeyboardServiceImpl(Keyboard* keyboard) keyboard_->set_keyboard_service(this); } -void KeyboardServiceImpl::SetTarget(uint32_t node_id) { - keyboard_->set_target(node_id); +void KeyboardServiceImpl::SetTarget(uint32_t view_id) { + keyboard_->set_target(view_id); } } // namespace examples diff --git a/mojo/examples/media_viewer/media_viewer.cc b/mojo/examples/media_viewer/media_viewer.cc index 0f84a4091754e..531e14416e5b6 100644 --- a/mojo/examples/media_viewer/media_viewer.cc +++ b/mojo/examples/media_viewer/media_viewer.cc @@ -14,12 +14,11 @@ #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/application/interface_factory_impl.h" #include "mojo/public/cpp/bindings/interface_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "mojo/services/public/interfaces/navigation/navigation.mojom.h" #include "mojo/views/native_widget_view_manager.h" #include "mojo/views/views_init.h" @@ -137,7 +136,7 @@ class ControlPanel : public views::ButtonListener { virtual ~ControlPanel() {} - void Initialize(Node* node) { + void Initialize(View* view) { const char* kNames[] = { "Zoom In", "Actual Size", "Zoom Out" }; views::WidgetDelegateView* widget_delegate = new views::WidgetDelegateView; @@ -159,9 +158,9 @@ class ControlPanel : public views::ButtonListener { views::Widget* widget = new views::Widget; views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.native_widget = new NativeWidgetViewManager(widget, node); + params.native_widget = new NativeWidgetViewManager(widget, view); params.delegate = widget_delegate; - params.bounds = gfx::Rect(node->bounds().width(), node->bounds().height()); + params.bounds = gfx::Rect(view->bounds().width(), view->bounds().height()); params.opacity = views::Widget::InitParams::OPAQUE_WINDOW; widget->Init(params); widget->Show(); @@ -193,7 +192,7 @@ class NavigatorImpl : public InterfaceImpl { private: // Overridden from Navigator: virtual void Navigate( - uint32_t node_id, + uint32_t view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) OVERRIDE; @@ -206,33 +205,33 @@ class MediaViewer : public ApplicationDelegate, public ViewManagerDelegate, public ControlPanel::Delegate, - public NodeObserver { + public ViewObserver { public: MediaViewer() : navigator_factory_(this), view_manager_client_factory_(this), app_(NULL), view_manager_(NULL), - root_node_(NULL), - control_node_(NULL), - content_node_(NULL), + root_view_(NULL), + control_view_(NULL), + content_view_(NULL), control_panel_(this) { handler_map_["image/png"] = "mojo:mojo_png_viewer"; } virtual ~MediaViewer() { - if (root_node_) - root_node_->RemoveObserver(this); + if (root_view_) + root_view_->RemoveObserver(this); } void Navigate( - uint32_t node_id, + uint32_t view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) { // TODO(yzshen): This shouldn't be needed once FIFO is ready. if (!view_manager_) { pending_navigate_request_.reset(new PendingNavigateRequest); - pending_navigate_request_->node_id = node_id; + pending_navigate_request_->view_id = view_id; pending_navigate_request_->navigation_details = navigation_details.Pass(); pending_navigate_request_->response_details = response_details.Pass(); @@ -244,12 +243,12 @@ class MediaViewer if (handler.empty()) return; - content_node_->Embed(handler); + content_view_->Embed(handler); if (navigation_details) { NavigatorPtr navigator; app_->ConnectToService(handler, &navigator); - navigator->Navigate(content_node_->id(), navigation_details.Pass(), + navigator->Navigate(content_view_->id(), navigation_details.Pass(), response_details.Pass()); } @@ -262,7 +261,7 @@ class MediaViewer typedef std::map HandlerMap; struct PendingNavigateRequest { - uint32_t node_id; + uint32_t view_id; NavigationDetailsPtr navigation_details; ResponseDetailsPtr response_details; }; @@ -281,40 +280,39 @@ class MediaViewer return true; } - void LayoutNodes() { - Node* root = content_node_->parent(); + void LayoutViews() { + View* root = content_view_->parent(); gfx::Rect control_bounds(root->bounds().width(), 28); - control_node_->SetBounds(control_bounds); + control_view_->SetBounds(control_bounds); gfx::Rect content_bounds(0, control_bounds.height(), root->bounds().width(), root->bounds().height() - control_bounds.height()); - content_node_->SetBounds(content_bounds); + content_view_->SetBounds(content_bounds); } // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { - root_node_ = root; + root_view_ = root; view_manager_ = view_manager; - control_node_ = Node::Create(view_manager_); - root_node_->AddChild(control_node_); + control_view_ = View::Create(view_manager_); + root_view_->AddChild(control_view_); - content_node_ = Node::Create(view_manager_); - root_node_->AddChild(content_node_); + content_view_ = View::Create(view_manager_); + root_view_->AddChild(content_view_); - control_node_->SetActiveView(View::Create(view_manager_)); - control_panel_.Initialize(control_node_); + control_panel_.Initialize(control_view_); - LayoutNodes(); - root_node_->AddObserver(this); + LayoutViews(); + root_view_->AddObserver(this); if (pending_navigate_request_) { scoped_ptr request( pending_navigate_request_.release()); - Navigate(request->node_id, request->navigation_details.Pass(), + Navigate(request->view_id, request->navigation_details.Pass(), request->response_details.Pass()); } } @@ -342,16 +340,16 @@ class MediaViewer } } - // NodeObserver: - virtual void OnNodeBoundsChanged(Node* node, + // ViewObserver: + virtual void OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE { - LayoutNodes(); + LayoutViews(); } - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK_EQ(node, root_node_); - node->RemoveObserver(this); - root_node_ = NULL; + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK_EQ(view, root_view_); + view->RemoveObserver(this); + root_view_ = NULL; } std::string GetHandlerForContentType(const std::string& content_type) { @@ -366,9 +364,9 @@ class MediaViewer ApplicationImpl* app_; scoped_ptr views_init_; ViewManager* view_manager_; - Node* root_node_; - Node* control_node_; - Node* content_node_; + View* root_view_; + View* control_view_; + View* content_view_; ControlPanel control_panel_; ZoomableMediaPtr zoomable_media_; HandlerMap handler_map_; @@ -378,10 +376,10 @@ class MediaViewer }; void NavigatorImpl::Navigate( - uint32_t node_id, + uint32_t view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) { - viewer_->Navigate(node_id, navigation_details.Pass(), + viewer_->Navigate(view_id, navigation_details.Pass(), response_details.Pass()); } diff --git a/mojo/examples/nesting_app/nesting_app.cc b/mojo/examples/nesting_app/nesting_app.cc index e425df6d25666..e8a168a057723 100644 --- a/mojo/examples/nesting_app/nesting_app.cc +++ b/mojo/examples/nesting_app/nesting_app.cc @@ -10,8 +10,6 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/interface_factory_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" @@ -49,8 +47,7 @@ class NavigatorImpl : public InterfaceImpl { class NestingApp : public ApplicationDelegate, public ViewManagerDelegate, - public ViewObserver, - public NodeObserver { + public ViewObserver { public: NestingApp() : navigator_factory_(this), @@ -88,17 +85,13 @@ class NestingApp // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { root->AddObserver(this); + root->SetColor(SK_ColorCYAN); - View* view = View::Create(view_manager); - root->SetActiveView(view); - view->SetColor(SK_ColorCYAN); - view->AddObserver(this); - - nested_ = Node::Create(view_manager); + nested_ = View::Create(view_manager); root->AddChild(nested_); nested_->SetBounds(gfx::Rect(20, 20, 50, 50)); nested_->Embed(kEmbeddedAppURL); @@ -110,22 +103,20 @@ class NestingApp } // Overridden from ViewObserver: + virtual void OnViewDestroyed(View* view) OVERRIDE { + // TODO(beng): reap views & child Views. + nested_ = NULL; + } virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE { if (event->action == EVENT_TYPE_MOUSE_RELEASED) - window_manager_->CloseWindow(view->node()->id()); - } - - // Overridden from NodeObserver: - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - // TODO(beng): reap views & child nodes. - nested_ = NULL; + window_manager_->CloseWindow(view->id()); } InterfaceFactoryImplWithContext navigator_factory_; ViewManagerClientFactory view_manager_client_factory_; std::string color_; - Node* nested_; + View* nested_; NavigatorPtr navigator_; IWindowManagerPtr window_manager_; diff --git a/mojo/examples/pepper_container_app/graphics_3d_resource.cc b/mojo/examples/pepper_container_app/graphics_3d_resource.cc index 1fa8675638b28..ef6bd76911b63 100644 --- a/mojo/examples/pepper_container_app/graphics_3d_resource.cc +++ b/mojo/examples/pepper_container_app/graphics_3d_resource.cc @@ -8,6 +8,7 @@ #include "mojo/examples/pepper_container_app/mojo_ppapi_globals.h" #include "mojo/examples/pepper_container_app/plugin_instance.h" #include "mojo/public/c/gles2/gles2.h" +#include "mojo/public/cpp/environment/environment.h" #include "ppapi/c/pp_errors.h" namespace mojo { @@ -28,7 +29,8 @@ Graphics3DResource::Graphics3DResource(PP_Instance instance) ScopedMessagePipeHandle pipe = MojoPpapiGlobals::Get()->CreateGLES2Context(); context_ = MojoGLES2CreateContext(pipe.release().value(), &ContextLostThunk, - this); + this, + Environment::GetDefaultAsyncWaiter()); } bool Graphics3DResource::IsBoundGraphics() const { diff --git a/mojo/examples/pepper_container_app/pepper_container_app.cc b/mojo/examples/pepper_container_app/pepper_container_app.cc index 107f6fdf694e1..18df756eba488 100644 --- a/mojo/examples/pepper_container_app/pepper_container_app.cc +++ b/mojo/examples/pepper_container_app/pepper_container_app.cc @@ -13,7 +13,6 @@ #include "mojo/examples/pepper_container_app/type_converters.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" -#include "mojo/public/cpp/gles2/gles2.h" #include "mojo/public/cpp/system/core.h" #include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" #include "ppapi/c/pp_rect.h" diff --git a/mojo/examples/png_viewer/png_viewer.cc b/mojo/examples/png_viewer/png_viewer.cc index aefebb9a8ef6e..2a6c9b8ca2436 100644 --- a/mojo/examples/png_viewer/png_viewer.cc +++ b/mojo/examples/png_viewer/png_viewer.cc @@ -10,13 +10,12 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/interface_factory_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/types.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "mojo/services/public/interfaces/navigation/navigation.mojom.h" #include "skia/ext/platform_canvas.h" #include "skia/ext/refptr.h" @@ -55,7 +54,7 @@ class NavigatorImpl : public InterfaceImpl { private: // Overridden from Navigator: virtual void Navigate( - uint32_t node_id, + uint32_t view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) OVERRIDE { int content_length = GetContentLength(response_details->response->headers); @@ -84,12 +83,12 @@ class NavigatorImpl : public InterfaceImpl { SkBitmap bitmap; gfx::PNGCodec::Decode(static_cast(data), content_length, &bitmap); - UpdateView(node_id, bitmap); + UpdateView(view_id, bitmap); delete[] data; } - void UpdateView(Id node_id, const SkBitmap& bitmap); + void UpdateView(Id view_id, const SkBitmap& bitmap); int GetContentLength(const Array& headers) { for (size_t i = 0; i < headers.size(); ++i) { @@ -114,13 +113,12 @@ class NavigatorImpl : public InterfaceImpl { class PNGViewer : public ApplicationDelegate, public ViewManagerDelegate, - public NodeObserver { + public ViewObserver { public: PNGViewer() : navigator_factory_(this), zoomable_media_factory_(this), view_manager_client_factory_(this), - content_view_(NULL), root_(NULL), zoom_percentage_(kDefaultZoomPercentage) {} virtual ~PNGViewer() { @@ -128,7 +126,7 @@ class PNGViewer root_->RemoveObserver(this); } - void UpdateView(Id node_id, const SkBitmap& bitmap) { + void UpdateView(Id view_id, const SkBitmap& bitmap) { bitmap_ = bitmap; zoom_percentage_ = kDefaultZoomPercentage; DrawBitmap(); @@ -172,14 +170,12 @@ class PNGViewer // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { root_ = root; root_->AddObserver(this); - content_view_ = View::Create(view_manager); - root_->SetActiveView(content_view_); - content_view_->SetColor(SK_ColorGRAY); + root_->SetColor(SK_ColorGRAY); if (!bitmap_.isNull()) DrawBitmap(); } @@ -189,12 +185,12 @@ class PNGViewer } void DrawBitmap() { - if (!content_view_) + if (!root_) return; skia::RefPtr canvas(skia::AdoptRef(skia::CreatePlatformCanvas( - content_view_->node()->bounds().width(), - content_view_->node()->bounds().height(), + root_->bounds().width(), + root_->bounds().height(), true))); canvas->drawColor(SK_ColorGRAY); SkPaint paint; @@ -202,19 +198,19 @@ class PNGViewer SkFloatToScalar(zoom_percentage_ * 1.0f / kDefaultZoomPercentage); canvas->scale(scale, scale); canvas->drawBitmap(bitmap_, 0, 0, &paint); - content_view_->SetContents(skia::GetTopDevice(*canvas)->accessBitmap(true)); + root_->SetContents(skia::GetTopDevice(*canvas)->accessBitmap(true)); } - // NodeObserver: - virtual void OnNodeBoundsChanged(Node* node, + // ViewObserver: + virtual void OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE { - DCHECK_EQ(node, root_); + DCHECK_EQ(view, root_); DrawBitmap(); } - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK_EQ(node, root_); - node->RemoveObserver(this); + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK_EQ(view, root_); + view->RemoveObserver(this); root_ = NULL; } @@ -223,8 +219,7 @@ class PNGViewer zoomable_media_factory_; ViewManagerClientFactory view_manager_client_factory_; - View* content_view_; - Node* root_; + View* root_; SkBitmap bitmap_; uint16_t zoom_percentage_; @@ -243,9 +238,9 @@ void ZoomableMediaImpl::ZoomToActualSize() { viewer_->ZoomToActualSize(); } -void NavigatorImpl::UpdateView(Id node_id, +void NavigatorImpl::UpdateView(Id view_id, const SkBitmap& bitmap) { - viewer_->UpdateView(node_id, bitmap); + viewer_->UpdateView(view_id, bitmap); } } // namespace examples diff --git a/mojo/examples/sample_app/gles2_client_impl.cc b/mojo/examples/sample_app/gles2_client_impl.cc index ad4b93eeaaeec..fde074abba130 100644 --- a/mojo/examples/sample_app/gles2_client_impl.cc +++ b/mojo/examples/sample_app/gles2_client_impl.cc @@ -10,6 +10,7 @@ #include #include "mojo/public/c/gles2/gles2.h" +#include "mojo/public/cpp/environment/environment.h" #include "mojo/public/cpp/utility/run_loop.h" namespace examples { @@ -28,10 +29,11 @@ float GetRandomColor() { GLES2ClientImpl::GLES2ClientImpl(mojo::CommandBufferPtr command_buffer) : last_time_(mojo::GetTimeTicksNow()), waiting_to_draw_(false) { - context_ = MojoGLES2CreateContext( - command_buffer.PassMessagePipe().release().value(), - &ContextLostThunk, - this); + context_ = + MojoGLES2CreateContext(command_buffer.PassMessagePipe().release().value(), + &ContextLostThunk, + this, + mojo::Environment::GetDefaultAsyncWaiter()); MojoGLES2MakeCurrent(context_); } diff --git a/mojo/examples/sample_app/sample_app.cc b/mojo/examples/sample_app/sample_app.cc index c60e873ff1815..222205a3baa76 100644 --- a/mojo/examples/sample_app/sample_app.cc +++ b/mojo/examples/sample_app/sample_app.cc @@ -9,7 +9,6 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" -#include "mojo/public/cpp/gles2/gles2.h" #include "mojo/public/cpp/system/core.h" #include "mojo/public/cpp/system/macros.h" #include "mojo/public/cpp/utility/run_loop.h" @@ -70,7 +69,6 @@ class SampleApp : public mojo::ApplicationDelegate, } private: - mojo::GLES2Initializer gles2; scoped_ptr gles2_client_; mojo::NativeViewportPtr viewport_; diff --git a/mojo/examples/window_manager/DEPS b/mojo/examples/window_manager/DEPS index 2930a789cc8c0..bfc0d9fa05897 100644 --- a/mojo/examples/window_manager/DEPS +++ b/mojo/examples/window_manager/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+ui/aura", "+ui/events", "+ui/gfx", "+ui/views", diff --git a/mojo/examples/window_manager/debug_panel.cc b/mojo/examples/window_manager/debug_panel.cc index 25ac8c4616044..1648d0c53fa23 100644 --- a/mojo/examples/window_manager/debug_panel.cc +++ b/mojo/examples/window_manager/debug_panel.cc @@ -6,7 +6,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "mojo/services/public/cpp/view_manager/node.h" +#include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/views/native_widget_view_manager.h" #include "ui/gfx/text_constants.h" #include "ui/views/background.h" @@ -24,9 +24,9 @@ const int kNavigationTargetGroupId = 1; } // namespace -DebugPanel::DebugPanel(Delegate* delegate, Node* node) +DebugPanel::DebugPanel(Delegate* delegate, View* view) : delegate_(delegate), - node_(node), + view_(view), navigation_target_label_(new views::Label( base::ASCIIToUTF16("Navigation target:"))), navigation_target_new_(new views::RadioButton( @@ -60,9 +60,9 @@ DebugPanel::DebugPanel(Delegate* delegate, Node* node) views::Widget* widget = new views::Widget(); views::Widget::InitParams params( views::Widget::InitParams::TYPE_WINDOW_FRAMELESS); - params.native_widget = new NativeWidgetViewManager(widget, node); + params.native_widget = new NativeWidgetViewManager(widget, view); params.delegate = widget_delegate; - params.bounds = gfx::Rect(node->bounds().size()); + params.bounds = gfx::Rect(view->bounds().size()); widget->Init(params); widget->Show(); } @@ -130,7 +130,7 @@ void DebugPanel::ButtonPressed(views::Button* sender, const ui::Event& event) { void DebugPanel::Navigate(const std::string& url) { NavigationDetailsPtr details(NavigationDetails::New()); details->request->url = url; - delegate_->RequestNavigate(node_->id(), TARGET_NEW_NODE, details.Pass()); + delegate_->RequestNavigate(view_->id(), TARGET_NEW_NODE, details.Pass()); } } // namespace examples diff --git a/mojo/examples/window_manager/debug_panel.h b/mojo/examples/window_manager/debug_panel.h index 843c937126fe6..df22e0bf33a45 100644 --- a/mojo/examples/window_manager/debug_panel.h +++ b/mojo/examples/window_manager/debug_panel.h @@ -19,7 +19,7 @@ class RadioButton; namespace mojo { -class Node; +class View; namespace examples { @@ -38,13 +38,13 @@ class DebugPanel : public views::LayoutManager, public views::ButtonListener { public: virtual void CloseTopWindow() = 0; virtual void RequestNavigate( - uint32 source_node_id, Target target, + uint32 source_view_id, Target target, NavigationDetailsPtr nav_details) = 0; protected: virtual ~Delegate(){} }; - DebugPanel(Delegate* delegate, Node* node); + DebugPanel(Delegate* delegate, View* view); virtual ~DebugPanel(); Target navigation_target() const; @@ -59,7 +59,7 @@ class DebugPanel : public views::LayoutManager, public views::ButtonListener { void Navigate(const std::string& url); Delegate* delegate_; - Node* node_; + View* view_; views::Label* navigation_target_label_; views::RadioButton* navigation_target_new_; diff --git a/mojo/examples/window_manager/window_manager.cc b/mojo/examples/window_manager/window_manager.cc index 841711759b971..f139e150f15fa 100644 --- a/mojo/examples/window_manager/window_manager.cc +++ b/mojo/examples/window_manager/window_manager.cc @@ -13,17 +13,17 @@ #include "mojo/public/cpp/application/interface_factory_impl.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" #include "mojo/services/public/cpp/input_events/input_events_type_converters.h" -#include "mojo/services/public/cpp/view_manager/node.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" -#include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "mojo/services/public/cpp/view_manager/window_manager_delegate.h" #include "mojo/services/public/interfaces/input_events/input_events.mojom.h" #include "mojo/services/public/interfaces/launcher/launcher.mojom.h" #include "mojo/services/public/interfaces/navigation/navigation.mojom.h" +#include "mojo/services/window_manager/window_manager_app.h" #include "mojo/views/views_init.h" +#include "ui/aura/window.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" #include "ui/gfx/geometry/size_conversions.h" @@ -53,7 +53,7 @@ class WindowManagerConnection : public InterfaceImpl { private: // Overridden from IWindowManager: - virtual void CloseWindow(Id node_id) OVERRIDE; + virtual void CloseWindow(Id view_id) OVERRIDE; virtual void ShowKeyboard(Id view_id, RectPtr bounds) OVERRIDE; virtual void HideKeyboard(Id view_id) OVERRIDE; @@ -70,10 +70,10 @@ class NavigatorHostImpl : public InterfaceImpl { } private: - virtual void DidNavigateLocally(uint32 source_node_id, + virtual void DidNavigateLocally(uint32 source_view_id, const mojo::String& url) OVERRIDE; virtual void RequestNavigate( - uint32 source_node_id, + uint32 source_view_id, Target target, NavigationDetailsPtr nav_details) OVERRIDE; WindowManager* window_manager_; @@ -82,26 +82,26 @@ class NavigatorHostImpl : public InterfaceImpl { }; class KeyboardManager : public KeyboardClient, - public NodeObserver { + public ViewObserver { public: - KeyboardManager() : view_manager_(NULL), node_(NULL) { + KeyboardManager() : view_manager_(NULL), view_(NULL) { } virtual ~KeyboardManager() { - if (node_) - node_->parent()->RemoveObserver(this); + if (view_) + view_->parent()->RemoveObserver(this); } - Node* node() { return node_; } + View* view() { return view_; } void Init(ApplicationImpl* application, ViewManager* view_manager, - Node* parent, + View* parent, const gfx::Rect& bounds) { view_manager_ = view_manager; - node_ = Node::Create(view_manager); - node_->SetBounds(bounds); - parent->AddChild(node_); - node_->Embed("mojo:mojo_keyboard"); + view_ = View::Create(view_manager); + view_->SetBounds(bounds); + parent->AddChild(view_); + view_->Embed("mojo:mojo_keyboard"); application->ConnectToService("mojo:mojo_keyboard", &keyboard_service_); keyboard_service_.set_client(this); parent->AddObserver(this); @@ -109,12 +109,12 @@ class KeyboardManager : public KeyboardClient, void Show(Id view_id, const gfx::Rect& bounds) { keyboard_service_->SetTarget(view_id); - node_->SetVisible(true); + view_->SetVisible(true); } void Hide(Id view_id) { keyboard_service_->SetTarget(0); - node_->SetVisible(false); + view_->SetVisible(false); } private: @@ -134,14 +134,14 @@ class KeyboardManager : public KeyboardClient, view_manager_->DispatchEvent( view, Event::From(ui::KeyEvent(ui::ET_KEY_PRESSED, - static_cast(code), - flags))); + static_cast(code), + flags))); } else { view_manager_->DispatchEvent( view, Event::From(ui::KeyEvent(static_cast(code), - static_cast(code), - flags))); + static_cast(code), + flags))); } view_manager_->DispatchEvent( view, @@ -150,99 +150,99 @@ class KeyboardManager : public KeyboardClient, flags))); } - // Overridden from NodeObserver: - virtual void OnNodeBoundsChanged(Node* parent, + // Overridden from ViewObserver: + virtual void OnViewBoundsChanged(View* parent, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE { - gfx::Rect keyboard_bounds(node_->bounds()); + gfx::Rect keyboard_bounds(view_->bounds()); keyboard_bounds.set_y(new_bounds.bottom() - keyboard_bounds.height()); keyboard_bounds.set_width(keyboard_bounds.width() + new_bounds.width() - old_bounds.width()); - node_->SetBounds(keyboard_bounds); + view_->SetBounds(keyboard_bounds); } - virtual void OnNodeDestroyed(Node* parent) OVERRIDE { - DCHECK_EQ(parent, node_->parent()); + virtual void OnViewDestroyed(View* parent) OVERRIDE { + DCHECK_EQ(parent, view_->parent()); parent->RemoveObserver(this); - node_ = NULL; + view_ = NULL; } KeyboardServicePtr keyboard_service_; ViewManager* view_manager_; - // Node the keyboard is attached to. - Node* node_; + // View the keyboard is attached to. + View* view_; DISALLOW_COPY_AND_ASSIGN(KeyboardManager); }; -class RootLayoutManager : public NodeObserver { +class RootLayoutManager : public ViewObserver { public: RootLayoutManager(ViewManager* view_manager, - Node* root, - Id content_node_id, - Id launcher_ui_node_id, - Id control_panel_node_id) + View* root, + Id content_view_id, + Id launcher_ui_view_id, + Id control_panel_view_id) : root_(root), view_manager_(view_manager), - content_node_id_(content_node_id), - launcher_ui_node_id_(launcher_ui_node_id), - control_panel_node_id_(control_panel_node_id) {} + content_view_id_(content_view_id), + launcher_ui_view_id_(launcher_ui_view_id), + control_panel_view_id_(control_panel_view_id) {} virtual ~RootLayoutManager() { if (root_) root_->RemoveObserver(this); } private: - // Overridden from NodeObserver: - virtual void OnNodeBoundsChanged(Node* node, + // Overridden from ViewObserver: + virtual void OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE { - DCHECK_EQ(node, root_); + DCHECK_EQ(view, root_); - Node* content_node = view_manager_->GetNodeById(content_node_id_); - content_node->SetBounds(new_bounds); + View* content_view = view_manager_->GetViewById(content_view_id_); + content_view->SetBounds(new_bounds); // Force the view's bitmap to be recreated - content_node->active_view()->SetColor(SK_ColorBLUE); + content_view->SetColor(SK_ColorBLUE); int delta_width = new_bounds.width() - old_bounds.width(); int delta_height = new_bounds.height() - old_bounds.height(); - Node* launcher_ui_node = - view_manager_->GetNodeById(launcher_ui_node_id_); - gfx::Rect launcher_ui_bounds(launcher_ui_node->bounds()); + View* launcher_ui_view = + view_manager_->GetViewById(launcher_ui_view_id_); + gfx::Rect launcher_ui_bounds(launcher_ui_view->bounds()); launcher_ui_bounds.set_width(launcher_ui_bounds.width() + delta_width); - launcher_ui_node->SetBounds(launcher_ui_bounds); + launcher_ui_view->SetBounds(launcher_ui_bounds); - Node* control_panel_node = - view_manager_->GetNodeById(control_panel_node_id_); - gfx::Rect control_panel_bounds(control_panel_node->bounds()); + View* control_panel_view = + view_manager_->GetViewById(control_panel_view_id_); + gfx::Rect control_panel_bounds(control_panel_view->bounds()); control_panel_bounds.set_x(control_panel_bounds.x() + delta_width); - control_panel_node->SetBounds(control_panel_bounds); - - const Node::Children& content_nodes = content_node->children(); - Node::Children::const_iterator iter = content_nodes.begin(); - for(; iter != content_nodes.end(); ++iter) { - Node* node = *iter; - if (node->id() == control_panel_node->id() || - node->id() == launcher_ui_node->id()) + control_panel_view->SetBounds(control_panel_bounds); + + const View::Children& content_views = content_view->children(); + View::Children::const_iterator iter = content_views.begin(); + for(; iter != content_views.end(); ++iter) { + View* view = *iter; + if (view->id() == control_panel_view->id() || + view->id() == launcher_ui_view->id()) continue; - gfx::Rect node_bounds(node->bounds()); - node_bounds.set_width(node_bounds.width() + delta_width); - node_bounds.set_height(node_bounds.height() + delta_height); - node->SetBounds(node_bounds); + gfx::Rect view_bounds(view->bounds()); + view_bounds.set_width(view_bounds.width() + delta_width); + view_bounds.set_height(view_bounds.height() + delta_height); + view->SetBounds(view_bounds); } } - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK_EQ(node, root_); + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK_EQ(view, root_); root_->RemoveObserver(this); root_ = NULL; } - Node* root_; + View* root_; ViewManager* view_manager_; - const Id content_node_id_; - const Id launcher_ui_node_id_; - const Id control_panel_node_id_; + const Id content_view_id_; + const Id launcher_ui_view_id_; + const Id control_panel_view_id_; DISALLOW_COPY_AND_ASSIGN(RootLayoutManager); }; @@ -251,26 +251,29 @@ class WindowManager : public ApplicationDelegate, public DebugPanel::Delegate, public ViewManagerDelegate, - public WindowManagerDelegate { + public WindowManagerDelegate, + public ui::EventHandler { public: WindowManager() : window_manager_factory_(this), navigator_host_factory_(this), launcher_ui_(NULL), view_manager_(NULL), - view_manager_client_factory_(this), + window_manager_app_(new WindowManagerApp(this, this)), app_(NULL) {} - virtual ~WindowManager() {} + virtual ~WindowManager() { + window_manager_app_->host()->window()->RemovePreTargetHandler(this); + } - void CloseWindow(Id node_id) { - Node* node = view_manager_->GetNodeById(node_id); - DCHECK(node); - std::vector::iterator iter = - std::find(windows_.begin(), windows_.end(), node); + void CloseWindow(Id view_id) { + View* view = view_manager_->GetViewById(view_id); + DCHECK(view); + std::vector::iterator iter = + std::find(windows_.begin(), windows_.end(), view); DCHECK(iter != windows_.end()); windows_.erase(iter); - node->Destroy(); + view->Destroy(); } void ShowKeyboard(Id view_id, const gfx::Rect& bounds) { @@ -280,7 +283,7 @@ class WindowManager // TODO: honor |bounds|. if (!keyboard_manager_) { keyboard_manager_.reset(new KeyboardManager); - Node* parent = view_manager_->GetRoots().back(); + View* parent = view_manager_->GetRoots().back(); int ideal_height = 200; // TODO(sky): 10 is a bit of a hack here. There is a bug that causes // white strips to appear when 0 is used. Figure this out! @@ -298,8 +301,8 @@ class WindowManager keyboard_manager_->Hide(view_id); } - void DidNavigateLocally(uint32 source_node_id, const mojo::String& url) { - LOG(ERROR) << "DidNavigateLocally: source_node_id: " << source_node_id + void DidNavigateLocally(uint32 source_view_id, const mojo::String& url) { + LOG(ERROR) << "DidNavigateLocally: source_view_id: " << source_view_id << " url: " << url.To(); } @@ -310,13 +313,13 @@ class WindowManager } virtual void RequestNavigate( - uint32 source_node_id, + uint32 source_view_id, Target target, NavigationDetailsPtr nav_details) OVERRIDE { launcher_->Launch(nav_details.Pass(), base::Bind(&WindowManager::OnLaunch, base::Unretained(this), - source_node_id, + source_view_id, target)); } @@ -326,43 +329,43 @@ class WindowManager app_ = app; app->ConnectToService("mojo:mojo_launcher", &launcher_); views_init_.reset(new ViewsInit); + window_manager_app_->Initialize(app); } virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) MOJO_OVERRIDE { connection->AddService(&window_manager_factory_); connection->AddService(&navigator_host_factory_); - connection->AddService(&view_manager_client_factory_); + window_manager_app_->ConfigureIncomingConnection(connection); return true; } // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { DCHECK(!view_manager_); view_manager_ = view_manager; - view_manager_->SetWindowManagerDelegate(this); - - Node* node = Node::Create(view_manager_); - root->AddChild(node); - node->SetBounds(gfx::Rect(root->bounds().size())); - content_node_id_ = node->id(); View* view = View::Create(view_manager_); - node->SetActiveView(view); - view->SetColor(SK_ColorBLUE); + root->AddChild(view); + view->SetBounds(gfx::Rect(root->bounds().size())); + content_view_id_ = view->id(); + + root->SetColor(SK_ColorBLUE); Id launcher_ui_id = CreateLauncherUI(); - Id control_panel_id = CreateControlPanel(node); + Id control_panel_id = CreateControlPanel(view); root_layout_manager_.reset( new RootLayoutManager(view_manager, root, - content_node_id_, + content_view_id_, launcher_ui_id, control_panel_id)); root->AddObserver(root_layout_manager_.get()); + + window_manager_app_->host()->window()->AddPreTargetHandler(this); } virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE { DCHECK_EQ(view_manager_, view_manager); @@ -378,17 +381,20 @@ class WindowManager NavigationDetailsPtr().Pass(), ResponseDetailsPtr().Pass()); } - virtual void DispatchEvent(View* target, EventPtr event) OVERRIDE { - // TODO(beng): More sophisticated focus handling than this is required! - if (event->action == EVENT_TYPE_MOUSE_PRESSED && - !IsDescendantOfKeyboard(target)) { - target->node()->SetFocus(); + virtual void DispatchEvent(EventPtr event) MOJO_OVERRIDE {} + + // Overridden from ui::EventHandler: + virtual void OnEvent(ui::Event* event) OVERRIDE { + View* view = WindowManagerApp::GetViewForWindow( + static_cast(event->target())); + if (event->type() == ui::ET_MOUSE_PRESSED && + !IsDescendantOfKeyboard(view)) { + view->SetFocus(); } - view_manager_->DispatchEvent(target, event.Pass()); } void OnLaunch( - uint32 source_node_id, + uint32 source_view_id, Target requested_target, const mojo::String& handler_url, const mojo::String& view_url, @@ -410,19 +416,19 @@ class WindowManager } } - Node* dest_node = NULL; + View* dest_view = NULL; if (target == TARGET_SOURCE_NODE) { - Node* source_node = view_manager_->GetNodeById(source_node_id); + View* source_view = view_manager_->GetViewById(source_view_id); bool app_initiated = std::find(windows_.begin(), windows_.end(), - source_node) != windows_.end(); + source_view) != windows_.end(); if (app_initiated) - dest_node = source_node; + dest_view = source_view; else if (!windows_.empty()) - dest_node = windows_.back(); + dest_view = windows_.back(); } - if (dest_node) - Embed(dest_node, handler_url, nav_details.Pass(), response.Pass()); + if (dest_view) + Embed(dest_view, handler_url, nav_details.Pass(), response.Pass()); else CreateWindow(handler_url, nav_details.Pass(), response.Pass()); } @@ -431,11 +437,11 @@ class WindowManager Id CreateLauncherUI() { NavigationDetailsPtr nav_details; ResponseDetailsPtr response; - Node* node = view_manager_->GetNodeById(content_node_id_); - gfx::Rect bounds = node->bounds(); + View* view = view_manager_->GetViewById(content_view_id_); + gfx::Rect bounds = view->bounds(); bounds.Inset(kBorderInset, kBorderInset); bounds.set_height(kTextfieldHeight); - launcher_ui_ = CreateChild(content_node_id_, "mojo:mojo_browser", bounds, + launcher_ui_ = CreateChild(content_view_id_, "mojo:mojo_browser", bounds, nav_details.Pass(), response.Pass()); return launcher_ui_->id(); } @@ -443,57 +449,55 @@ class WindowManager void CreateWindow(const std::string& handler_url, NavigationDetailsPtr nav_details, ResponseDetailsPtr response) { - Node* node = view_manager_->GetNodeById(content_node_id_); + View* view = view_manager_->GetViewById(content_view_id_); gfx::Rect bounds(kBorderInset, 2 * kBorderInset + kTextfieldHeight, - node->bounds().width() - 3 * kBorderInset - + view->bounds().width() - 3 * kBorderInset - kControlPanelWidth, - node->bounds().height() - + view->bounds().height() - (3 * kBorderInset + kTextfieldHeight)); if (!windows_.empty()) { gfx::Point position = windows_.back()->bounds().origin(); position.Offset(35, 35); bounds.set_origin(position); } - windows_.push_back(CreateChild(content_node_id_, handler_url, bounds, + windows_.push_back(CreateChild(content_view_id_, handler_url, bounds, nav_details.Pass(), response.Pass())); } - Node* CreateChild(Id parent_id, + View* CreateChild(Id parent_id, const std::string& url, const gfx::Rect& bounds, NavigationDetailsPtr nav_details, ResponseDetailsPtr response) { - Node* node = view_manager_->GetNodeById(parent_id); - Node* embedded = Node::Create(view_manager_); - node->AddChild(embedded); + View* view = view_manager_->GetViewById(parent_id); + View* embedded = View::Create(view_manager_); + view->AddChild(embedded); embedded->SetBounds(bounds); Embed(embedded, url, nav_details.Pass(), response.Pass()); embedded->SetFocus(); return embedded; } - void Embed(Node* node, const std::string& app_url, + void Embed(View* view, const std::string& app_url, NavigationDetailsPtr nav_details, ResponseDetailsPtr response) { - node->Embed(app_url); + view->Embed(app_url); if (nav_details) { NavigatorPtr navigator; app_->ConnectToService(app_url, &navigator); - navigator->Navigate(node->id(), nav_details.Pass(), response.Pass()); + navigator->Navigate(view->id(), nav_details.Pass(), response.Pass()); } } bool IsDescendantOfKeyboard(View* target) { return keyboard_manager_.get() && - keyboard_manager_->node()->Contains(target->node()); + keyboard_manager_->view()->Contains(target); } - Id CreateControlPanel(Node* root) { - Node* node = Node::Create(view_manager_); + Id CreateControlPanel(View* root) { View* view = View::Create(view_manager_); - root->AddChild(node); - node->SetActiveView(view); + root->AddChild(view); gfx::Rect bounds(root->bounds().width() - kControlPanelWidth - kBorderInset, @@ -501,10 +505,10 @@ class WindowManager kControlPanelWidth, root->bounds().height() - kBorderInset * 3 - kTextfieldHeight); - node->SetBounds(bounds); + view->SetBounds(bounds); - debug_panel_ = new DebugPanel(this, node); - return node->id(); + debug_panel_ = new DebugPanel(this, view); + return view->id(); } InterfaceFactoryImplWithContext @@ -515,14 +519,15 @@ class WindowManager scoped_ptr views_init_; DebugPanel* debug_panel_; LauncherPtr launcher_; - Node* launcher_ui_; - std::vector windows_; + View* launcher_ui_; + std::vector windows_; ViewManager* view_manager_; - ViewManagerClientFactory view_manager_client_factory_; scoped_ptr root_layout_manager_; - // Id of the node most content is added to. The keyboard is NOT added here. - Id content_node_id_; + scoped_ptr window_manager_app_; + + // Id of the view most content is added to. The keyboard is NOT added here. + Id content_view_id_; scoped_ptr keyboard_manager_; ApplicationImpl* app_; @@ -530,28 +535,28 @@ class WindowManager DISALLOW_COPY_AND_ASSIGN(WindowManager); }; -void WindowManagerConnection::CloseWindow(Id node_id) { - window_manager_->CloseWindow(node_id); +void WindowManagerConnection::CloseWindow(Id view_id) { + window_manager_->CloseWindow(view_id); } void WindowManagerConnection::ShowKeyboard(Id view_id, RectPtr bounds) { window_manager_->ShowKeyboard(view_id, bounds.To()); } -void WindowManagerConnection::HideKeyboard(Id node_id) { - window_manager_->HideKeyboard(node_id); +void WindowManagerConnection::HideKeyboard(Id view_id) { + window_manager_->HideKeyboard(view_id); } -void NavigatorHostImpl::DidNavigateLocally(uint32 source_node_id, +void NavigatorHostImpl::DidNavigateLocally(uint32 source_view_id, const mojo::String& url) { - window_manager_->DidNavigateLocally(source_node_id, url); + window_manager_->DidNavigateLocally(source_view_id, url); } void NavigatorHostImpl::RequestNavigate( - uint32 source_node_id, + uint32 source_view_id, Target target, NavigationDetailsPtr nav_details) { - window_manager_->RequestNavigate(source_node_id, target, nav_details.Pass()); + window_manager_->RequestNavigate(source_view_id, target, nav_details.Pass()); } } // namespace examples diff --git a/mojo/examples/wm_flow/app/app.cc b/mojo/examples/wm_flow/app/app.cc index 23ddd6c9be75d..787aec61f4f38 100644 --- a/mojo/examples/wm_flow/app/app.cc +++ b/mojo/examples/wm_flow/app/app.cc @@ -12,7 +12,6 @@ #include "mojo/public/cpp/application/interface_factory_impl.h" #include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" @@ -44,7 +43,7 @@ class EmbedderImpl : public mojo::InterfaceImpl { // This app starts its life via Connect() rather than by being embed, so it does // not start with a connection to the ViewManager service. It has to obtain a // connection by connecting to the ViewManagerInit service and asking to be -// embed without a node context. +// embed without a view context. class WMFlowApp : public mojo::ApplicationDelegate, public mojo::ViewManagerDelegate { public: @@ -56,8 +55,8 @@ class WMFlowApp : public mojo::ApplicationDelegate, private: // Overridden from Application: virtual void Initialize(mojo::ApplicationImpl* app) MOJO_OVERRIDE { - mojo::ViewManagerInitServicePtr init_svc; mojo::ServiceProviderPtr sp; + mojo::ViewManagerInitServicePtr init_svc; app->ConnectToService("mojo:mojo_view_manager", &init_svc); init_svc->Embed("mojo:mojo_wm_flow_app", sp.Pass(), base::Bind(&ConnectCallback)); @@ -73,15 +72,12 @@ class WMFlowApp : public mojo::ApplicationDelegate, // Overridden from mojo::ViewManagerDelegate: virtual void OnEmbed( mojo::ViewManager* view_manager, - mojo::Node* root, + mojo::View* root, mojo::ServiceProviderImpl* exported_services, scoped_ptr imported_services) MOJO_OVERRIDE { - mojo::View* view = - mojo::View::Create(view_manager); - root->SetActiveView(view); - view->SetColor(kColors[embed_count_++ % arraysize(kColors)]); + root->SetColor(kColors[embed_count_++ % arraysize(kColors)]); - mojo::Node* embed = mojo::Node::Create(view_manager); + mojo::View* embed = mojo::View::Create(view_manager); root->AddChild(embed); gfx::Rect bounds = root->bounds(); bounds.Inset(25, 25); diff --git a/mojo/examples/wm_flow/embedded/embedded.cc b/mojo/examples/wm_flow/embedded/embedded.cc index 00b242c153651..c1a9d0d539caa 100644 --- a/mojo/examples/wm_flow/embedded/embedded.cc +++ b/mojo/examples/wm_flow/embedded/embedded.cc @@ -11,7 +11,6 @@ #include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/application/interface_factory_impl.h" #include "mojo/public/cpp/application/service_provider_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" @@ -58,13 +57,10 @@ class WMFlowEmbedded : public mojo::ApplicationDelegate, // Overridden from mojo::ViewManagerDelegate: virtual void OnEmbed( mojo::ViewManager* view_manager, - mojo::Node* root, + mojo::View* root, mojo::ServiceProviderImpl* exported_services, scoped_ptr imported_services) MOJO_OVERRIDE { - mojo::View* view = - mojo::View::Create(view_manager); - root->SetActiveView(view); - view->SetColor(SK_ColorMAGENTA); + root->SetColor(SK_ColorMAGENTA); exported_services->AddService(&embeddee_factory_); mojo::ConnectToService(imported_services.get(), &embedder_); diff --git a/mojo/examples/wm_flow/wm/wm.cc b/mojo/examples/wm_flow/wm/wm.cc index 5bfa69fcafc92..023c44b4f4003 100644 --- a/mojo/examples/wm_flow/wm/wm.cc +++ b/mojo/examples/wm_flow/wm/wm.cc @@ -16,7 +16,7 @@ class SimpleWM : public mojo::ApplicationDelegate, public mojo::WindowManagerDelegate { public: SimpleWM() - : window_manager_app_(new mojo::WindowManagerApp(this)), + : window_manager_app_(new mojo::WindowManagerApp(this, this)), view_manager_(NULL), root_(NULL), window_container_(NULL), @@ -37,14 +37,13 @@ class SimpleWM : public mojo::ApplicationDelegate, // Overridden from mojo::ViewManagerDelegate: virtual void OnEmbed( mojo::ViewManager* view_manager, - mojo::Node* root, + mojo::View* root, mojo::ServiceProviderImpl* exported_services, scoped_ptr remote_service_provider) MOJO_OVERRIDE { view_manager_ = view_manager; root_ = root; - view_manager_->SetWindowManagerDelegate(this); - window_container_ = mojo::Node::Create(view_manager_); + window_container_ = mojo::View::Create(view_manager_); window_container_->SetBounds(root_->bounds()); root_->AddChild(window_container_); @@ -60,29 +59,25 @@ class SimpleWM : public mojo::ApplicationDelegate, const mojo::String& url, mojo::InterfaceRequest service_provider) MOJO_OVERRIDE { - mojo::Node* embed_node = - mojo::Node::Create(view_manager_); - embed_node->SetBounds(gfx::Rect(next_window_origin_, gfx::Size(400, 400))); - window_container_->AddChild(embed_node); + mojo::View* embed_view = mojo::View::Create(view_manager_); + embed_view->SetBounds(gfx::Rect(next_window_origin_, gfx::Size(400, 400))); + window_container_->AddChild(embed_view); // TODO(beng): We're dropping the |service_provider| passed from the client // on the floor here and passing our own. Seems like we should // be sending both. I'm not yet sure how this sould work for // N levels of proxying. - embed_node->Embed(url, scoped_ptr( + embed_view->Embed(url, scoped_ptr( new mojo::ServiceProviderImpl).Pass()); next_window_origin_.Offset(50, 50); } - virtual void DispatchEvent(mojo::View* target, - mojo::EventPtr event) MOJO_OVERRIDE { - view_manager_->DispatchEvent(target, event.Pass()); - } + virtual void DispatchEvent(mojo::EventPtr event) MOJO_OVERRIDE {} scoped_ptr window_manager_app_; mojo::ViewManager* view_manager_; - mojo::Node* root_; - mojo::Node* window_container_; + mojo::View* root_; + mojo::View* window_container_; gfx::Point next_window_origin_; diff --git a/mojo/gles2/BUILD.gn b/mojo/gles2/BUILD.gn index 089311680f86d..a37647a3d9387 100644 --- a/mojo/gles2/BUILD.gn +++ b/mojo/gles2/BUILD.gn @@ -3,7 +3,17 @@ # found in the LICENSE file. import("//mojo/public/tools/bindings/mojom.gni") +import("//mojo/system.gni") +config("mojo_use_gles2") { + defines = [ "MOJO_USE_GLES2_IMPL" ] +} + +config("gles2_use_mojo") { + defines = [ "USE_MOJO_GLES2" ] +} + +# GYP version: mojo/mojo_base.gyp:mojo_gles2_impl component("gles2") { output_name = "mojo_gles2_impl" @@ -18,19 +28,25 @@ component("gles2") { "//mojo/services/gles2:interfaces", "//mojo/environment:chromium", ] + deps += mojo_system_for_component - if (is_component_build) { - deps += [ "//mojo/system" ] - } + defines = [ + "MOJO_GLES2_IMPL_IMPLEMENTATION", + "MOJO_GLES2_IMPLEMENTATION", + ] - defines = [ "MOJO_GLES2_IMPL_IMPLEMENTATION" ] + configs += [ + ":gles2_use_mojo", + ":mojo_use_gles2", + ] + direct_dependent_configs = [ ":gles2_use_mojo" ] + all_dependent_configs = [ ":mojo_use_gles2" ] sources = [ "command_buffer_client_impl.cc", "command_buffer_client_impl.h", "gles2_impl_export.h", - "gles2_support_impl.cc", - "gles2_support_impl.h", + "gles2_impl.cc", "gles2_context.cc", "gles2_context.h", ] diff --git a/mojo/gles2/DEPS b/mojo/gles2/DEPS index a0e373e85c17e..db90643e10bee 100644 --- a/mojo/gles2/DEPS +++ b/mojo/gles2/DEPS @@ -1,4 +1,5 @@ include_rules = [ "+gpu/command_buffer/client", "+gpu/command_buffer/common", + "+gpu/GLES2", ] diff --git a/mojo/gles2/gles2_impl.cc b/mojo/gles2/gles2_impl.cc new file mode 100644 index 0000000000000..3d46ecd7f6cff --- /dev/null +++ b/mojo/gles2/gles2_impl.cc @@ -0,0 +1,73 @@ +// 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/public/c/gles2/gles2.h" + +#include "base/lazy_instance.h" +#include "base/threading/thread_local.h" +#include "gpu/GLES2/gl2extchromium.h" +#include "gpu/command_buffer/client/gles2_interface.h" +#include "mojo/gles2/gles2_context.h" + +using mojo::gles2::GLES2Context; + +namespace { + +base::LazyInstance >::Leaky + g_gpu_interface; + +} // namespace + +extern "C" { +MojoGLES2Context MojoGLES2CreateContext(MojoHandle handle, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter) { + mojo::MessagePipeHandle mph(handle); + mojo::ScopedMessagePipeHandle scoped_handle(mph); + scoped_ptr client(new GLES2Context( + async_waiter, scoped_handle.Pass(), lost_callback, closure)); + if (!client->Initialize()) + client.reset(); + return client.release(); +} + +void MojoGLES2DestroyContext(MojoGLES2Context context) { + delete static_cast(context); +} + +void MojoGLES2MakeCurrent(MojoGLES2Context context) { + gpu::gles2::GLES2Interface* interface = NULL; + if (context) { + GLES2Context* client = static_cast(context); + interface = client->interface(); + DCHECK(interface); + } + g_gpu_interface.Get().Set(interface); +} + +void MojoGLES2SwapBuffers() { + DCHECK(g_gpu_interface.Get().Get()); + g_gpu_interface.Get().Get()->SwapBuffers(); +} + +void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) { + return static_cast(context)->interface(); +} + +void* MojoGLES2GetContextSupport(MojoGLES2Context context) { + return static_cast(context)->context_support(); +} + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType gl##Function PARAMETERS { \ + DCHECK(g_gpu_interface.Get().Get()); \ + return g_gpu_interface.Get().Get()->Function ARGUMENTS; \ + } +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h" +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h" +#undef VISIT_GL_CALL + +} // extern "C" diff --git a/mojo/gles2/gles2_support_impl.cc b/mojo/gles2/gles2_support_impl.cc deleted file mode 100644 index 99e8bb44dadeb..0000000000000 --- a/mojo/gles2/gles2_support_impl.cc +++ /dev/null @@ -1,106 +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/gles2/gles2_support_impl.h" - -#include "base/lazy_instance.h" -#include "gpu/command_buffer/client/gles2_interface.h" -#include "mojo/gles2/gles2_context.h" -#include "mojo/public/gles2/gles2_interface.h" -#include "mojo/public/gles2/gles2_private.h" - -namespace mojo { -namespace gles2 { - -namespace { - -class GLES2ImplForCommandBuffer : public GLES2Interface { - public: - GLES2ImplForCommandBuffer() : gpu_interface_(NULL) {} - - void set_gpu_interface(gpu::gles2::GLES2Interface* gpu_interface) { - gpu_interface_ = gpu_interface; - } - gpu::gles2::GLES2Interface* gpu_interface() const { return gpu_interface_; } - -#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ - virtual ReturnType Function PARAMETERS OVERRIDE { \ - return gpu_interface_->Function ARGUMENTS; \ - } -#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" -#undef VISIT_GL_CALL - - private: - gpu::gles2::GLES2Interface* gpu_interface_; - DISALLOW_COPY_AND_ASSIGN(GLES2ImplForCommandBuffer); -}; - -base::LazyInstance g_gles2_interface = - LAZY_INSTANCE_INITIALIZER; - -} // anonymous namespace - -GLES2SupportImpl::GLES2SupportImpl() : async_waiter_(NULL) {} -GLES2SupportImpl::~GLES2SupportImpl() {} - -// static -void GLES2SupportImpl::Init() { GLES2Support::Init(new GLES2SupportImpl()); } - -void GLES2SupportImpl::Initialize(const MojoAsyncWaiter* async_waiter) { - DCHECK(!async_waiter_); - DCHECK(async_waiter); - async_waiter_ = async_waiter; -} - -void GLES2SupportImpl::Terminate() { - DCHECK(async_waiter_); - async_waiter_ = NULL; -} - -MojoGLES2Context GLES2SupportImpl::CreateContext( - MessagePipeHandle handle, - MojoGLES2ContextLost lost_callback, - void* closure) { - ScopedMessagePipeHandle scoped_handle(handle); - scoped_ptr client(new GLES2Context(async_waiter_, - scoped_handle.Pass(), - lost_callback, - closure)); - if (!client->Initialize()) - client.reset(); - return client.release(); -} - -void GLES2SupportImpl::DestroyContext(MojoGLES2Context context) { - delete static_cast(context); -} - -void GLES2SupportImpl::MakeCurrent(MojoGLES2Context context) { - gpu::gles2::GLES2Interface* interface = NULL; - if (context) { - GLES2Context* client = static_cast(context); - interface = client->interface(); - DCHECK(interface); - } - g_gles2_interface.Get().set_gpu_interface(interface); -} - -void GLES2SupportImpl::SwapBuffers() { - g_gles2_interface.Get().gpu_interface()->SwapBuffers(); -} - -void* GLES2SupportImpl::GetGLES2Interface(MojoGLES2Context context) { - return static_cast(context)->interface(); -} - -void* GLES2SupportImpl::GetContextSupport(MojoGLES2Context context) { - return static_cast(context)->context_support(); -} - -GLES2Interface* GLES2SupportImpl::GetGLES2InterfaceForCurrentContext() { - return &g_gles2_interface.Get(); -} - -} // namespace gles2 -} // namespace mojo diff --git a/mojo/gles2/gles2_support_impl.h b/mojo/gles2/gles2_support_impl.h deleted file mode 100644 index 5598a09a15f76..0000000000000 --- a/mojo/gles2/gles2_support_impl.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_GLES2_GLES2_SUPPORT_IMPL_H_ -#define MOJO_GLES2_GLES2_SUPPORT_IMPL_H_ - -#include "base/compiler_specific.h" -#include "mojo/gles2/gles2_impl_export.h" -#include "mojo/public/gles2/gles2_private.h" - -namespace mojo { -namespace gles2 { - -class MOJO_GLES2_IMPL_EXPORT GLES2SupportImpl : public GLES2Support { - public: - virtual ~GLES2SupportImpl(); - - static void Init(); - - virtual void Initialize(const MojoAsyncWaiter* async_waiter) OVERRIDE; - virtual void Terminate() OVERRIDE; - virtual MojoGLES2Context CreateContext( - MessagePipeHandle handle, - MojoGLES2ContextLost lost_callback, - void* closure) OVERRIDE; - virtual void DestroyContext(MojoGLES2Context context) OVERRIDE; - virtual void MakeCurrent(MojoGLES2Context context) OVERRIDE; - virtual void SwapBuffers() OVERRIDE; - virtual void* GetGLES2Interface(MojoGLES2Context context) OVERRIDE; - virtual void* GetContextSupport(MojoGLES2Context context) OVERRIDE; - virtual GLES2Interface* GetGLES2InterfaceForCurrentContext() OVERRIDE; - - private: - GLES2SupportImpl(); - - const MojoAsyncWaiter* async_waiter_; -}; - -} // namespace gles2 -} // namespace mojo - -#endif // MOJO_GLES2_GLES2_SUPPORT_IMPL_H_ diff --git a/mojo/mojo.gyp b/mojo/mojo.gyp index 1e92147781cff..69678c5772f6c 100644 --- a/mojo/mojo.gyp +++ b/mojo/mojo.gyp @@ -24,30 +24,33 @@ 'target_name': 'mojo', 'type': 'none', 'dependencies': [ - 'mojo_base.gyp:mojo_base', + 'mojo_application_manager', + 'mojo_application_manager_unittests', 'mojo_apps_js_unittests', + 'mojo_base.gyp:mojo_base', 'mojo_compositor_app', + 'mojo_content_handler_demo', 'mojo_echo_client', 'mojo_echo_service', + 'mojo_example_apptests', + 'mojo_example_service', 'mojo_geometry_lib', 'mojo_html_viewer', 'mojo_js', 'mojo_launcher', - 'mojo_native_viewport_service', + 'mojo_native_viewport_service_lib', 'mojo_network_service', 'mojo_pepper_container_app', 'mojo_png_viewer', 'mojo_sample_app', - 'mojo_service_manager', - 'mojo_service_manager_unittests', 'mojo_shell', 'mojo_shell_lib', 'mojo_shell_tests', 'mojo_surfaces_app', + 'mojo_surfaces_app', 'mojo_surfaces_child_app', 'mojo_surfaces_lib', 'mojo_surfaces_lib_unittests', - 'mojo_surfaces_app', 'mojo_surfaces_service', 'mojo_test_app', 'mojo_test_request_tracker_app', @@ -100,35 +103,6 @@ 'mojo_base.gyp:mojo_cpp_bindings', ], }, - { - # GN version: //mojo/gles2 - 'target_name': 'mojo_gles2_impl', - 'type': '<(component)', - 'dependencies': [ - '../base/base.gyp:base', - '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', - '../gpu/gpu.gyp:command_buffer_client', - '../gpu/gpu.gyp:command_buffer_common', - '../gpu/gpu.gyp:gles2_cmd_helper', - '../gpu/gpu.gyp:gles2_implementation', - 'mojo_base.gyp:mojo_environment_chromium', - 'mojo_gles2', - 'mojo_gles2_bindings', - '<(mojo_system_for_component)', - ], - 'defines': [ - 'MOJO_GLES2_IMPL_IMPLEMENTATION', - ], - 'sources': [ - 'gles2/command_buffer_client_impl.cc', - 'gles2/command_buffer_client_impl.h', - 'gles2/gles2_impl_export.h', - 'gles2/gles2_support_impl.cc', - 'gles2/gles2_support_impl.h', - 'gles2/gles2_context.cc', - 'gles2/gles2_context.h', - ], - }, { 'target_name': 'mojo_spy', 'type': 'static_library', @@ -137,7 +111,7 @@ '../base/base.gyp:base_static', '../net/net.gyp:http_server', '../url/url.gyp:url_lib', - 'mojo_service_manager', + 'mojo_application_manager', ], 'variables': { 'mojom_base_output_dir': 'mojo', @@ -164,15 +138,15 @@ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '../net/net.gyp:net', '../url/url.gyp:url_lib', + 'mojo_application_manager', 'mojo_base.gyp:mojo_application_bindings', 'mojo_base.gyp:mojo_common_lib', + 'mojo_base.gyp:mojo_gles2_impl', 'mojo_base.gyp:mojo_system_impl', 'mojo_base.gyp:mojo_application_chromium', 'mojo_external_service_bindings', - 'mojo_gles2_impl', - 'mojo_native_viewport_service', + 'mojo_native_viewport_service_lib', 'mojo_network_bindings', - 'mojo_service_manager', 'mojo_spy', ], 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], @@ -188,10 +162,10 @@ 'shell/child_process_host.h', 'shell/context.cc', 'shell/context.h', - 'shell/dbus_service_loader_linux.cc', - 'shell/dbus_service_loader_linux.h', - 'shell/dynamic_service_loader.cc', - 'shell/dynamic_service_loader.h', + 'shell/dbus_application_loader_linux.cc', + 'shell/dbus_application_loader_linux.h', + 'shell/dynamic_application_loader.cc', + 'shell/dynamic_application_loader.h', 'shell/dynamic_service_runner.h', 'shell/init.cc', 'shell/init.h', @@ -211,8 +185,8 @@ 'shell/task_runners.h', 'shell/test_child_process.cc', 'shell/test_child_process.h', - 'shell/ui_service_loader_android.cc', - 'shell/ui_service_loader_android.h', + 'shell/ui_application_loader_android.cc', + 'shell/ui_application_loader_android.h', 'shell/view_manager_loader.cc', 'shell/view_manager_loader.h', ], @@ -228,15 +202,14 @@ 'mojo_network_service_lib', ], 'sources': [ - 'shell/network_service_loader.cc', - 'shell/network_service_loader.h', + 'shell/network_application_loader.cc', + 'shell/network_application_loader.h', ], }], ['use_aura==1', { 'dependencies': [ # These are only necessary as long as we hard code use of ViewManager. '../skia/skia.gyp:skia', - 'mojo_gles2', 'mojo_view_manager', 'mojo_view_manager_bindings', ], @@ -255,8 +228,8 @@ '../base/base.gyp:base', '../base/base.gyp:base_static', '../url/url.gyp:url_lib', + 'mojo_application_manager', 'mojo_base.gyp:mojo_system_impl', - 'mojo_service_manager', 'mojo_shell_lib', ], 'sources': [ @@ -272,10 +245,10 @@ '../base/base.gyp:base', '../ui/gl/gl.gyp:gl', '../url/url.gyp:url_lib', + 'mojo_application_manager', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_system_impl', - 'mojo_service_manager', 'mojo_shell_lib', ], 'conditions': [ @@ -290,6 +263,7 @@ ], }, { + # GN version: //mojo/shell:mojo_shell_tests 'target_name': 'mojo_shell_tests', 'type': '<(gtest_target_type)', 'dependencies': [ @@ -301,10 +275,10 @@ # TODO(vtl): We don't currently need this, but I imagine we will soon. # '../ui/gl/gl.gyp:gl', '../url/url.gyp:url_lib', + 'mojo_application_manager', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_system_impl', - 'mojo_service_manager', 'mojo_shell_lib', 'mojo_test_app', 'mojo_test_request_tracker_app', @@ -312,7 +286,7 @@ ], 'sources': [ 'shell/child_process_host_unittest.cc', - 'shell/dynamic_service_loader_unittest.cc', + 'shell/dynamic_application_loader_unittest.cc', 'shell/in_process_dynamic_service_runner_unittest.cc', 'shell/shell_test_base.cc', 'shell/shell_test_base.h', @@ -328,29 +302,32 @@ ], }, { - # GN version: //mojo/service_manager - 'target_name': 'mojo_service_manager', + # GN version: //mojo/application_manager + 'target_name': 'mojo_application_manager', 'type': '<(component)', 'defines': [ - 'MOJO_SERVICE_MANAGER_IMPLEMENTATION', + 'MOJO_APPLICATION_MANAGER_IMPLEMENTATION', ], 'dependencies': [ '../base/base.gyp:base', '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', '../net/net.gyp:net', '../url/url.gyp:url_lib', + 'mojo_content_handler_bindings', + 'mojo_network_bindings', 'mojo_base.gyp:mojo_application_bindings', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', '<(mojo_system_for_component)', ], 'sources': [ - 'service_manager/background_shell_service_loader.cc', - 'service_manager/background_shell_service_loader.h', - 'service_manager/service_loader.h', - 'service_manager/service_manager.cc', - 'service_manager/service_manager.h', - 'service_manager/service_manager_export.h', + 'application_manager/application_loader.cc', + 'application_manager/application_loader.h', + 'application_manager/application_manager.cc', + 'application_manager/application_manager.h', + 'application_manager/application_manager_export.h', + 'application_manager/background_shell_application_loader.cc', + 'application_manager/background_shell_application_loader.h', ], 'export_dependent_settings': [ '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', @@ -358,24 +335,24 @@ ], }, { - 'target_name': 'mojo_service_manager_unittests', + 'target_name': 'mojo_application_manager_unittests', 'type': 'executable', 'dependencies': [ '../base/base.gyp:base', '../testing/gtest.gyp:gtest', '../url/url.gyp:url_lib', + 'mojo_application_manager', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_cpp_bindings', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_run_all_unittests', 'mojo_base.gyp:mojo_application_chromium', - 'mojo_service_manager', ], 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], 'sources': [ - 'service_manager/background_shell_service_loader_unittest.cc', - 'service_manager/service_manager_unittest.cc', - 'service_manager/test.mojom', + 'application_manager/application_manager_unittest.cc', + 'application_manager/background_shell_application_loader_unittest.cc', + 'application_manager/test.mojom', ], }, { @@ -387,7 +364,7 @@ '../cc/cc.gyp:cc', '../skia/skia.gyp:skia', '../gpu/gpu.gyp:gles2_implementation', - 'mojo_gles2', + '<(mojo_gles2_for_loadable_module)', ], 'sources': [ 'cc/context_provider_mojo.cc', @@ -484,8 +461,8 @@ '../ui/gl/gl.gyp:gl', '../webkit/common/gpu/webkit_gpu.gyp:webkit_gpu', 'mojo_cc_support', - 'mojo_gles2', 'mojo_native_viewport_bindings', + '<(mojo_gles2_for_loadable_module)', ], 'sources': [ 'aura/aura_init.cc', diff --git a/mojo/mojo_apps.gypi b/mojo/mojo_apps.gypi index 934964f0bf4e0..fda9084c190a0 100644 --- a/mojo/mojo_apps.gypi +++ b/mojo/mojo_apps.gypi @@ -1,3 +1,7 @@ +# 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. + { 'targets': [ { @@ -10,18 +14,18 @@ '../v8/tools/gyp/v8.gyp:v8', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', + 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_base.gyp:mojo_js_bindings_lib', - 'mojo_gles2', - 'mojo_gles2_bindings', 'mojo_native_viewport_bindings', + '<(mojo_gles2_for_loadable_module)', ], 'export_dependent_settings': [ '../base/base.gyp:base', '../gin/gin.gyp:gin', 'mojo_base.gyp:mojo_common_lib', - 'mojo_gles2', - 'mojo_gles2_bindings', + 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_native_viewport_bindings', + '<(mojo_gles2_for_loadable_module)', ], 'sources': [ 'apps/js/mojo_runner_delegate.cc', diff --git a/mojo/mojo_apps_js_unittests.isolate b/mojo/mojo_apps_js_unittests.isolate index 4fdeb7296cb4b..af1b0aabfe569 100644 --- a/mojo/mojo_apps_js_unittests.isolate +++ b/mojo/mojo_apps_js_unittests.isolate @@ -45,7 +45,6 @@ ['OS=="mac"', { 'variables': { 'isolate_dependency_tracked': [ - '<(PRODUCT_DIR)/libmojo_gles2.dylib', '<(PRODUCT_DIR)/libmojo_test_support.dylib', ], }, diff --git a/mojo/mojo_base.gyp b/mojo/mojo_base.gyp index e08c2680925f9..b06997fdfd96f 100644 --- a/mojo/mojo_base.gyp +++ b/mojo/mojo_base.gyp @@ -108,7 +108,15 @@ 'embedder/platform_handle_utils_posix.cc', 'embedder/platform_handle_utils_win.cc', 'embedder/platform_handle_vector.h', + 'embedder/platform_shared_buffer.h', + 'embedder/platform_support.h', 'embedder/scoped_platform_handle.h', + 'embedder/simple_platform_shared_buffer.cc', + 'embedder/simple_platform_shared_buffer.h', + 'embedder/simple_platform_shared_buffer_posix.cc', + 'embedder/simple_platform_shared_buffer_win.cc', + 'embedder/simple_platform_support.cc', + 'embedder/simple_platform_support.h', 'system/channel.cc', 'system/channel.h', 'system/constants.h', @@ -153,10 +161,6 @@ 'system/raw_channel.h', 'system/raw_channel_posix.cc', 'system/raw_channel_win.cc', - 'system/raw_shared_buffer.cc', - 'system/raw_shared_buffer.h', - 'system/raw_shared_buffer_posix.cc', - 'system/raw_shared_buffer_win.cc', 'system/shared_buffer_dispatcher.cc', 'system/shared_buffer_dispatcher.h', 'system/simple_dispatcher.cc', @@ -191,6 +195,7 @@ 'sources': [ 'embedder/embedder_unittest.cc', 'embedder/platform_channel_pair_posix_unittest.cc', + 'embedder/simple_platform_shared_buffer_unittest.cc', 'system/channel_unittest.cc', 'system/core_unittest.cc', 'system/core_test_base.cc', @@ -205,7 +210,6 @@ 'system/options_validation_unittest.cc', 'system/platform_handle_dispatcher_unittest.cc', 'system/raw_channel_unittest.cc', - 'system/raw_shared_buffer_unittest.cc', 'system/remote_message_pipe_unittest.cc', 'system/run_all_unittests.cc', 'system/shared_buffer_dispatcher_unittest.cc', @@ -341,6 +345,68 @@ '..', ], }, + { + # GN version: //mojo/services/gles2:interfaces (for files generated from + # the mojom file) + # GN version: //mojo/services/gles2:bindings + 'target_name': 'mojo_gles2_bindings', + 'type': 'static_library', + 'sources': [ + 'services/gles2/command_buffer.mojom', + 'services/gles2/command_buffer_type_conversions.cc', + 'services/gles2/command_buffer_type_conversions.h', + 'services/gles2/mojo_buffer_backing.cc', + 'services/gles2/mojo_buffer_backing.h', + ], + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'export_dependent_settings': [ + 'mojo_cpp_bindings', + ], + 'dependencies': [ + 'mojo_cpp_bindings', + '../gpu/gpu.gyp:command_buffer_common', + ], + }, + { + # GN version: //mojo/gles2 + 'target_name': 'mojo_gles2_impl', + 'type': '<(component)', + 'dependencies': [ + '../base/base.gyp:base', + '../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations', + '../gpu/gpu.gyp:command_buffer_client', + '../gpu/gpu.gyp:command_buffer_common', + '../gpu/gpu.gyp:gles2_cmd_helper', + '../gpu/gpu.gyp:gles2_implementation', + 'mojo_environment_chromium', + 'mojo_gles2_bindings', + '<(mojo_system_for_component)', + ], + 'defines': [ + 'GLES2_USE_MOJO', + 'GL_GLEXT_PROTOTYPES', + 'MOJO_GLES2_IMPLEMENTATION', + 'MOJO_GLES2_IMPL_IMPLEMENTATION', + 'MOJO_USE_GLES2_IMPL' + ], + 'direct_dependent_settings': { + 'defines': [ + 'GLES2_USE_MOJO', + ], + }, + 'sources': [ + 'gles2/command_buffer_client_impl.cc', + 'gles2/command_buffer_client_impl.h', + 'gles2/gles2_impl_export.h', + 'gles2/gles2_impl.cc', + 'gles2/gles2_context.cc', + 'gles2/gles2_context.h', + ], + 'all_dependent_settings': { + # Ensures that dependent projects import the core functions on Windows. + 'defines': ['MOJO_USE_GLES2_IMPL'], + } + }, { 'target_name': 'mojo_application_chromium', 'type': 'static_library', @@ -409,7 +475,7 @@ 'sources': [ 'android/javatests/src/org/chromium/mojo/MojoTestCase.java', 'android/system/src/org/chromium/mojo/system/impl/CoreImpl.java', - 'services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java', + 'services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java', 'shell/android/apk/src/org/chromium/mojo_shell_apk/MojoMain.java', ], 'variables': { @@ -518,4 +584,4 @@ ], }], ] -} \ No newline at end of file +} diff --git a/mojo/mojo_examples.gypi b/mojo/mojo_examples.gypi index 029ca211eb898..81b0a4fc4ab29 100644 --- a/mojo/mojo_examples.gypi +++ b/mojo/mojo_examples.gypi @@ -76,8 +76,8 @@ 'mojo_base.gyp:mojo_environment_standalone', 'mojo_base.gyp:mojo_utility', 'mojo_geometry_bindings', - 'mojo_gles2', 'mojo_native_viewport_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ @@ -96,6 +96,66 @@ }, 'includes': [ 'build/package_app.gypi' ], }, + { + 'target_name': 'mojo_example_service_bindings', + 'type': 'static_library', + 'sources': [ + 'examples/apptest/example_service.mojom', + ], + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'export_dependent_settings': [ + 'mojo_base.gyp:mojo_cpp_bindings', + ], + 'dependencies': [ + 'mojo_base.gyp:mojo_cpp_bindings', + ], + }, + { + 'target_name': 'mojo_example_service', + 'type': 'loadable_module', + 'dependencies': [ + 'mojo_base.gyp:mojo_application_standalone', # For ApplicationDelegate. + 'mojo_base.gyp:mojo_cpp_bindings', # For *.mojom.h + 'mojo_base.gyp:mojo_environment_standalone', # For Environment. + 'mojo_example_service_bindings', + 'mojo_base.gyp:mojo_utility', # For RunLoop. + '<(mojo_system_for_loadable_module)', + ], + 'sources': [ + 'examples/apptest/example_service_application.cc', + 'examples/apptest/example_service_application.h', + 'examples/apptest/example_service_impl.cc', + 'examples/apptest/example_service_impl.h', + 'public/cpp/application/lib/mojo_main_standalone.cc', + ], + }, + { + 'target_name': 'mojo_example_apptests', + 'type': 'loadable_module', + 'dependencies': [ + '../testing/gtest.gyp:gtest', + 'mojo_base.gyp:mojo_application_standalone', # For ApplicationDelegate. + 'mojo_base.gyp:mojo_environment_standalone', # For Environment. + 'mojo_example_service', + 'mojo_example_service_bindings', + 'mojo_base.gyp:mojo_utility', # For RunLoop. + '<(mojo_system_for_loadable_module)', + ], + 'sources': [ + 'examples/apptest/example_apptest.cc', + 'examples/apptest/example_client_application.cc', + 'examples/apptest/example_client_application.h', + 'examples/apptest/example_client_impl.cc', + 'examples/apptest/example_client_impl.h', + ], + }, + { + 'target_name': 'package_mojo_example_apptests', + 'variables': { + 'app_name': 'mojo_example_apptests', + }, + 'includes': [ 'build/package_app.gypi' ], + }, { 'target_name': 'mojo_compositor_app', 'type': 'loadable_module', @@ -110,8 +170,8 @@ 'mojo_cc_support', 'mojo_geometry_bindings', 'mojo_geometry_lib', - 'mojo_gles2', 'mojo_native_viewport_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ @@ -144,6 +204,22 @@ 'public/cpp/application/lib/mojo_main_standalone.cc', ], }, + { + 'target_name': 'mojo_content_handler_demo', + 'type': 'loadable_module', + 'dependencies': [ + 'mojo_base.gyp:mojo_application_standalone', + 'mojo_base.gyp:mojo_cpp_bindings', + 'mojo_base.gyp:mojo_environment_standalone', + 'mojo_base.gyp:mojo_utility', + 'mojo_content_handler_bindings', + '<(mojo_system_for_loadable_module)', + ], + 'sources': [ + 'examples/content_handler_demo/content_handler_demo.cc', + 'public/cpp/application/lib/mojo_main_standalone.cc', + ], + }, { 'target_name': 'package_mojo_wget', 'variables': { @@ -201,8 +277,8 @@ 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_geometry_bindings', - 'mojo_gles2', 'mojo_native_viewport_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'defines': [ @@ -296,10 +372,8 @@ 'mojo_base.gyp:mojo_application_chromium', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', - 'mojo_base.gyp:mojo_system_impl', 'mojo_geometry_bindings', 'mojo_geometry_lib', - 'mojo_gles2', 'mojo_native_viewport_bindings', 'mojo_surfaces_bindings', 'mojo_surfaces_app_bindings', @@ -351,7 +425,6 @@ 'mojo_base.gyp:mojo_application_chromium', 'mojo_base.gyp:mojo_common_lib', 'mojo_base.gyp:mojo_environment_chromium', - 'mojo_base.gyp:mojo_system_impl', 'mojo_geometry_bindings', 'mojo_geometry_lib', 'mojo_surfaces_app_bindings', @@ -469,8 +542,8 @@ 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_utility', 'mojo_geometry_bindings', - 'mojo_gles2', 'mojo_view_manager_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ @@ -566,9 +639,9 @@ 'mojo_base.gyp:mojo_utility', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_aura_support', + 'mojo_core_window_manager_lib', 'mojo_geometry_bindings', 'mojo_geometry_lib', - 'mojo_gles2', 'mojo_input_events_lib', 'mojo_keyboard_bindings', 'mojo_launcher_bindings', @@ -576,6 +649,7 @@ 'mojo_view_manager_lib', 'mojo_views_support', 'mojo_window_manager_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ @@ -598,10 +672,10 @@ 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_utility', 'mojo_geometry_bindings', - 'mojo_gles2', 'mojo_navigation_bindings', 'mojo_view_manager_lib', 'mojo_window_manager_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ @@ -622,10 +696,10 @@ 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_utility', 'mojo_geometry_bindings', - 'mojo_gles2', 'mojo_navigation_bindings', 'mojo_view_manager_lib', 'mojo_window_manager_bindings', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ diff --git a/mojo/mojo_public.gypi b/mojo/mojo_public.gypi index 1897f6d731065..0c94c6a498573 100644 --- a/mojo/mojo_public.gypi +++ b/mojo/mojo_public.gypi @@ -43,6 +43,52 @@ 'public/platform/native/system_thunks.h', ], }, + { + # GN version: //mojo/public/gles2 + 'target_name': 'mojo_gles2', + 'type': 'static_library', + 'defines': [ + 'MOJO_GLES2_IMPLEMENTATION', + 'GLES2_USE_MOJO', + ], + 'include_dirs': [ + '..', + ], + 'dependencies': [ + '../third_party/khronos/khronos.gyp:khronos_headers' + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + 'defines': [ + 'GLES2_USE_MOJO', + ], + }, + 'all_dependent_settings': { + 'conditions': [ + # We need to be able to call the MojoSetGLES2Thunks() function in + # gles2_thunks.cc + ['OS=="android"', { + 'ldflags!': [ + '-Wl,--exclude-libs=ALL', + ], + }], + ], + }, + 'sources': [ + 'public/c/gles2/gles2.h', + 'public/c/gles2/gles2_export.h', + 'public/platform/native/gles2_thunks.cc', + 'public/platform/native/gles2_thunks.h', + 'public/platform/native/gles2_impl_thunks.cc', + 'public/platform/native/gles2_impl_thunks.h', + 'public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc', + 'public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h', + 'public/platform/native/gles2_impl_chromium_sync_point_thunks.cc', + 'public/platform/native/gles2_impl_chromium_sync_point_thunks.h', + ], + }, { # GN version: //mojo/public/cpp/bindings 'target_name': 'mojo_cpp_bindings', @@ -211,6 +257,7 @@ ], }, { + # GN version: //mojo/public/cpp/application:standalone" 'target_name': 'mojo_application_standalone', 'type': 'static_library', 'sources': [ diff --git a/mojo/mojo_services.gypi b/mojo/mojo_services.gypi index 3e07f8e034055..c46baf4fb3959 100644 --- a/mojo/mojo_services.gypi +++ b/mojo/mojo_services.gypi @@ -48,6 +48,8 @@ 'services/html_viewer/blink_input_events_type_converters.h', 'services/html_viewer/blink_platform_impl.cc', 'services/html_viewer/blink_platform_impl.h', + 'services/html_viewer/blink_url_request_type_converters.cc', + 'services/html_viewer/blink_url_request_type_converters.h', 'services/html_viewer/html_viewer.cc', 'services/html_viewer/html_document_view.cc', 'services/html_viewer/html_document_view.h', @@ -194,65 +196,6 @@ 'services/public/cpp/surfaces/tests/surface_unittest.cc', ], }, - { - # GN version: //mojo/public/gles2 - 'target_name': 'mojo_gles2', - 'type': 'shared_library', - 'defines': [ - 'MOJO_GLES2_IMPLEMENTATION', - 'GLES2_USE_MOJO', - ], - 'include_dirs': [ - '..', - ], - 'dependencies': [ - '../third_party/khronos/khronos.gyp:khronos_headers' - ], - 'direct_dependent_settings': { - 'include_dirs': [ - '..', - ], - 'defines': [ - 'GLES2_USE_MOJO', - ], - }, - 'sources': [ - 'public/c/gles2/gles2.h', - 'public/c/gles2/gles2_export.h', - 'public/gles2/gles2_private.cc', - 'public/gles2/gles2_private.h', - ], - 'conditions': [ - ['OS=="mac"', { - 'xcode_settings': { - # Make it a run-path dependent library. - 'DYLIB_INSTALL_NAME_BASE': '@loader_path', - }, - }], - ], - }, - { - # GN version: //mojo/services/gles2:interfaces (for files generated from - # the mojom file) - # GN version: //mojo/services/gles2:bindings - 'target_name': 'mojo_gles2_bindings', - 'type': 'static_library', - 'sources': [ - 'services/gles2/command_buffer.mojom', - 'services/gles2/command_buffer_type_conversions.cc', - 'services/gles2/command_buffer_type_conversions.h', - 'services/gles2/mojo_buffer_backing.cc', - 'services/gles2/mojo_buffer_backing.h', - ], - 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], - 'export_dependent_settings': [ - 'mojo_base.gyp:mojo_cpp_bindings', - ], - 'dependencies': [ - 'mojo_base.gyp:mojo_cpp_bindings', - '../gpu/gpu.gyp:command_buffer_common', - ], - }, { # GN version: //mojo/services/gles2 'target_name': 'mojo_gles2_service', @@ -263,10 +206,10 @@ '../ui/gfx/gfx.gyp:gfx', '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gl/gl.gyp:gl', - 'mojo_gles2_bindings', + 'mojo_base.gyp:mojo_gles2_bindings', ], 'export_dependent_settings': [ - 'mojo_gles2_bindings', + 'mojo_base.gyp:mojo_gles2_bindings', ], 'sources': [ 'services/gles2/command_buffer_impl.cc', @@ -286,16 +229,17 @@ ], 'dependencies': [ 'mojo_base.gyp:mojo_cpp_bindings', + 'mojo_base.gyp:mojo_gles2_bindings', 'mojo_geometry_bindings', - 'mojo_gles2_bindings', 'mojo_input_events_bindings', ], }, { # GN version: //mojo/services/native_viewport - 'target_name': 'mojo_native_viewport_service', - # This is linked directly into the embedder, so we make it a component. - 'type': '<(component)', + 'target_name': 'mojo_native_viewport_service_lib', + # This is linked directly into the embedder, so we make it a static_library. + # TODO(davemoore): Make this a true service. + 'type': 'static_library', 'dependencies': [ '../base/base.gyp:base', '../ui/events/events.gyp:events', @@ -309,26 +253,22 @@ 'mojo_gles2_service', 'mojo_input_events_lib', 'mojo_native_viewport_bindings', - '<(mojo_system_for_component)', - ], - 'defines': [ - 'MOJO_NATIVE_VIEWPORT_IMPLEMENTATION', ], 'sources': [ - 'services/native_viewport/native_viewport.h', - 'services/native_viewport/native_viewport_android.cc', - 'services/native_viewport/native_viewport_mac.mm', - 'services/native_viewport/native_viewport_ozone.cc', - 'services/native_viewport/native_viewport_service.cc', - 'services/native_viewport/native_viewport_service.h', - 'services/native_viewport/native_viewport_stub.cc', - 'services/native_viewport/native_viewport_win.cc', - 'services/native_viewport/native_viewport_x11.cc', + 'services/native_viewport/native_viewport_impl.cc', + 'services/native_viewport/native_viewport_impl.h', + 'services/native_viewport/platform_viewport.h', + 'services/native_viewport/platform_viewport_android.cc', + 'services/native_viewport/platform_viewport_mac.mm', + 'services/native_viewport/platform_viewport_ozone.cc', + 'services/native_viewport/platform_viewport_stub.cc', + 'services/native_viewport/platform_viewport_win.cc', + 'services/native_viewport/platform_viewport_x11.cc', ], 'conditions': [ ['OS=="win" or OS=="android" or OS=="linux" or OS=="mac"', { 'sources!': [ - 'services/native_viewport/native_viewport_stub.cc', + 'services/native_viewport/platform_viewport_stub.cc', ], }], ['OS=="android"', { @@ -344,6 +284,12 @@ ['use_x11==1', { 'dependencies': [ '../ui/platform_window/x11/x11_window.gyp:x11_window', + '../ui/events/platform/x11/x11_events_platform.gyp:x11_events_platform', + ], + }], + ['use_ozone==1', { + 'dependencies': [ + '../ui/ozone/ozone.gyp:ozone', ], }], ], @@ -364,6 +310,23 @@ 'mojo_network_bindings', ], }, + { + # GN version: //mojo/services/public/interfaces/content_handler + 'target_name': 'mojo_content_handler_bindings', + 'type': 'static_library', + 'sources': [ + 'services/public/interfaces/content_handler/content_handler.mojom', + ], + 'includes': [ 'public/tools/bindings/mojom_bindings_generator.gypi' ], + 'export_dependent_settings': [ + 'mojo_base.gyp:mojo_cpp_bindings', + ], + 'dependencies': [ + 'mojo_base.gyp:mojo_application_bindings', + 'mojo_base.gyp:mojo_cpp_bindings', + 'mojo_network_bindings', + ], + }, { # GN version: //mojo/services/public/interfaces/network 'target_name': 'mojo_network_bindings', @@ -433,14 +396,13 @@ '../cc/cc.gyp:cc_surfaces', '../ui/gfx/gfx.gyp:gfx_geometry', 'mojo_base.gyp:mojo_environment_chromium', - 'mojo_base.gyp:mojo_system_impl', 'mojo_base.gyp:mojo_application_chromium', 'mojo_cc_support', 'mojo_geometry_bindings', 'mojo_geometry_lib', - 'mojo_gles2', 'mojo_surfaces_bindings', 'mojo_surfaces_lib', + '<(mojo_gles2_for_loadable_module)', '<(mojo_system_for_loadable_module)', ], 'sources': [ @@ -506,6 +468,7 @@ 'mojo_base.gyp:mojo_cpp_bindings', ], 'dependencies': [ + 'mojo_base.gyp:mojo_application_bindings', 'mojo_base.gyp:mojo_cpp_bindings', 'mojo_geometry_bindings', 'mojo_input_events_bindings', @@ -523,24 +486,20 @@ '../ui/gfx/gfx.gyp:gfx_geometry', 'mojo_base.gyp:mojo_application_chromium', 'mojo_base.gyp:mojo_application_bindings', + 'mojo_core_window_manager_bindings', 'mojo_geometry_bindings', 'mojo_geometry_lib', 'mojo_view_manager_bindings', 'mojo_view_manager_common', ], 'sources': [ - 'services/public/cpp/view_manager/lib/node.cc', - 'services/public/cpp/view_manager/lib/node_observer.cc', - 'services/public/cpp/view_manager/lib/node_private.cc', - 'services/public/cpp/view_manager/lib/node_private.h', 'services/public/cpp/view_manager/lib/view.cc', - 'services/public/cpp/view_manager/lib/view_private.cc', - 'services/public/cpp/view_manager/lib/view_private.h', 'services/public/cpp/view_manager/lib/view_manager_client_factory.cc', 'services/public/cpp/view_manager/lib/view_manager_client_impl.cc', 'services/public/cpp/view_manager/lib/view_manager_client_impl.h', - 'services/public/cpp/view_manager/node.h', - 'services/public/cpp/view_manager/node_observer.h', + 'services/public/cpp/view_manager/lib/view_observer.cc', + 'services/public/cpp/view_manager/lib/view_private.cc', + 'services/public/cpp/view_manager/lib/view_private.h', 'services/public/cpp/view_manager/view.h', 'services/public/cpp/view_manager/view_manager.h', 'services/public/cpp/view_manager/view_manager_client_factory.h', @@ -569,7 +528,6 @@ 'mojo_view_manager_lib', ], 'sources': [ - 'services/public/cpp/view_manager/tests/node_unittest.cc', 'services/public/cpp/view_manager/tests/view_unittest.cc', 'services/public/cpp/view_manager/tests/view_manager_unittest.cc', ], @@ -711,12 +669,12 @@ 'mojo_cc_support', 'mojo_geometry_bindings', 'mojo_geometry_lib', - 'mojo_gles2', 'mojo_input_events_bindings', 'mojo_input_events_lib', 'mojo_native_viewport_bindings', 'mojo_view_manager_bindings', 'mojo_view_manager_common', + '<(mojo_gles2_for_component)', '<(mojo_system_for_component)', ], 'sources': [ @@ -739,8 +697,6 @@ 'services/view_manager/root_view_manager_delegate.h', 'services/view_manager/screen_impl.cc', 'services/view_manager/screen_impl.h', - 'services/view_manager/view.cc', - 'services/view_manager/view.h', 'services/view_manager/view_manager_export.h', 'services/view_manager/view_manager_init_service_context.cc', 'services/view_manager/view_manager_init_service_context.h', @@ -774,7 +730,7 @@ ['OS=="linux"', { 'dependencies': [ '../third_party/mesa/mesa.gyp:osmesa', - 'mojo_native_viewport_service', + 'mojo_native_viewport_service_lib', ], }], ['use_x11==1', { @@ -795,6 +751,7 @@ '../ui/aura/aura.gyp:aura', '../ui/gfx/gfx.gyp:gfx_geometry', '../ui/gl/gl.gyp:gl', + 'mojo_application_manager', 'mojo_base.gyp:mojo_system_impl', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_base.gyp:mojo_application_chromium', @@ -802,7 +759,6 @@ 'mojo_geometry_lib', 'mojo_input_events_bindings', 'mojo_input_events_lib', - 'mojo_service_manager', 'mojo_shell_test_support', 'mojo_view_manager_bindings', 'mojo_view_manager_common', @@ -835,6 +791,7 @@ 'mojo_base.gyp:mojo_application_chromium', 'mojo_aura_support', 'mojo_core_window_manager_bindings', + 'mojo_input_events_lib', 'mojo_view_manager_lib', ], 'sources': [ @@ -863,10 +820,10 @@ '../base/base.gyp:test_support_base', '../testing/gtest.gyp:gtest', '../ui/gl/gl.gyp:gl', + 'mojo_application_manager', 'mojo_base.gyp:mojo_system_impl', 'mojo_base.gyp:mojo_environment_chromium', 'mojo_core_window_manager_bindings', - 'mojo_service_manager', 'mojo_shell_test_support', 'mojo_view_manager_bindings', 'mojo_view_manager_lib', @@ -879,7 +836,7 @@ ['OS=="linux"', { 'dependencies': [ '../third_party/mesa/mesa.gyp:osmesa', - 'mojo_native_viewport_service', + 'mojo_native_viewport_service_lib', ], }], ['use_x11==1', { diff --git a/mojo/mojo_variables.gypi b/mojo/mojo_variables.gypi index 8969220727598..ff0d1b186238d 100644 --- a/mojo/mojo_variables.gypi +++ b/mojo/mojo_variables.gypi @@ -34,9 +34,13 @@ ['component=="shared_library"', { 'mojo_system_for_component': "mojo_base.gyp:mojo_system_impl", 'mojo_system_for_loadable_module': "mojo_base.gyp:mojo_system_impl", + 'mojo_gles2_for_component': "mojo_base.gyp:mojo_gles2_impl", + 'mojo_gles2_for_loadable_module': "mojo_base.gyp:mojo_gles2_impl", }, { 'mojo_system_for_component': "mojo_base.gyp:mojo_none", 'mojo_system_for_loadable_module': "mojo_base.gyp:mojo_system", + 'mojo_gles2_for_component': "mojo_base.gyp:mojo_none", + 'mojo_gles2_for_loadable_module': "mojo_base.gyp:mojo_gles2", }], ], }, diff --git a/mojo/public/c/PRESUBMIT.py b/mojo/public/c/PRESUBMIT.py new file mode 100644 index 0000000000000..4cba43385371f --- /dev/null +++ b/mojo/public/c/PRESUBMIT.py @@ -0,0 +1,16 @@ +# 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. + +"""Presubmit script for mojo/public/c. + +See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts +for more details about the presubmit API built into depot_tools. +""" + +def CheckChangeOnUpload(input_api, output_api): + results = [] + results += input_api.canned_checks.CheckChangeHasOnlyOneEol(input_api, + output_api) + results += input_api.canned_checks.CheckPatchFormatted(input_api, output_api) + return results diff --git a/mojo/public/c/environment/logger.h b/mojo/public/c/environment/logger.h index 5e9067fb50964..c492a66397f9d 100644 --- a/mojo/public/c/environment/logger.h +++ b/mojo/public/c/environment/logger.h @@ -19,11 +19,11 @@ const MojoLogLevel MOJO_LOG_LEVEL_WARNING = 1; const MojoLogLevel MOJO_LOG_LEVEL_ERROR = 2; const MojoLogLevel MOJO_LOG_LEVEL_FATAL = 3; #else -#define MOJO_LOG_LEVEL_VERBOSE ((MojoLogLevel) -1) -#define MOJO_LOG_LEVEL_INFO ((MojoLogLevel) 0) -#define MOJO_LOG_LEVEL_WARNING ((MojoLogLevel) 1) -#define MOJO_LOG_LEVEL_ERROR ((MojoLogLevel) 2) -#define MOJO_LOG_LEVEL_FATAL ((MojoLogLevel) 3) +#define MOJO_LOG_LEVEL_VERBOSE ((MojoLogLevel) - 1) +#define MOJO_LOG_LEVEL_INFO ((MojoLogLevel)0) +#define MOJO_LOG_LEVEL_WARNING ((MojoLogLevel)1) +#define MOJO_LOG_LEVEL_ERROR ((MojoLogLevel)2) +#define MOJO_LOG_LEVEL_FATAL ((MojoLogLevel)3) #endif // Structure with basic logging functions (on top of which more friendly logging diff --git a/mojo/public/c/gles2/chromium_sync_point.h b/mojo/public/c/gles2/chromium_sync_point.h new file mode 100644 index 0000000000000..63697125490ed --- /dev/null +++ b/mojo/public/c/gles2/chromium_sync_point.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_ +#define MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_ + +// Note: This header should be compilable as C. + +#include +#include + +#include "mojo/public/c/gles2/gles2_export.h" +#include "mojo/public/c/gles2/gles2_types.h" +#include "mojo/public/c/system/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h" +#undef VISIT_GL_CALL + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GLES2_CHROMIUM_SYNC_POINT_H_ diff --git a/mojo/public/c/gles2/chromium_texture_mailbox.h b/mojo/public/c/gles2/chromium_texture_mailbox.h new file mode 100644 index 0000000000000..177ebbb3d4d8f --- /dev/null +++ b/mojo/public/c/gles2/chromium_texture_mailbox.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_ +#define MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_ + +// Note: This header should be compilable as C. + +#include +#include + +#include "mojo/public/c/gles2/gles2_export.h" +#include "mojo/public/c/gles2/gles2_types.h" +#include "mojo/public/c/system/types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + MOJO_GLES2_EXPORT ReturnType GL_APIENTRY gl##Function PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h" +#undef VISIT_GL_CALL + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // MOJO_PUBLIC_C_GLES2_CHROMIUM_TEXTURE_MAILBOX_H_ diff --git a/mojo/public/c/gles2/gles2.h b/mojo/public/c/gles2/gles2.h index 223b1760aad31..36c6f7fdc96e4 100644 --- a/mojo/public/c/gles2/gles2.h +++ b/mojo/public/c/gles2/gles2.h @@ -19,12 +19,11 @@ extern "C" { #endif -MOJO_GLES2_EXPORT void MojoGLES2Initialize(const MojoAsyncWaiter* async_waiter); -MOJO_GLES2_EXPORT void MojoGLES2Terminate(void); -MOJO_GLES2_EXPORT MojoGLES2Context MojoGLES2CreateContext( - MojoHandle handle, - MojoGLES2ContextLost lost_callback, - void* closure); +MOJO_GLES2_EXPORT MojoGLES2Context + MojoGLES2CreateContext(MojoHandle handle, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter); MOJO_GLES2_EXPORT void MojoGLES2DestroyContext(MojoGLES2Context context); MOJO_GLES2_EXPORT void MojoGLES2MakeCurrent(MojoGLES2Context context); MOJO_GLES2_EXPORT void MojoGLES2SwapBuffers(void); diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h new file mode 100644 index 0000000000000..3c3c4b9cbd74a --- /dev/null +++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h @@ -0,0 +1,12 @@ +// 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. + +// This file is auto-generated from +// gpu/command_buffer/build_gles2_cmd_buffer.py +// It's formatted by clang-format using chromium coding style: +// clang-format -i -style=chromium filename +// DO NOT EDIT! + +VISIT_GL_CALL(InsertSyncPointCHROMIUM, GLuint, (), ()) +VISIT_GL_CALL(WaitSyncPointCHROMIUM, void, (GLuint sync_point), (sync_point)) diff --git a/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h b/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h new file mode 100644 index 0000000000000..184c2f249ef52 --- /dev/null +++ b/mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h @@ -0,0 +1,27 @@ +// 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. + +// This file is auto-generated from +// gpu/command_buffer/build_gles2_cmd_buffer.py +// It's formatted by clang-format using chromium coding style: +// clang-format -i -style=chromium filename +// DO NOT EDIT! + +VISIT_GL_CALL(GenMailboxCHROMIUM, void, (GLbyte * mailbox), (mailbox)) +VISIT_GL_CALL(ProduceTextureCHROMIUM, + void, + (GLenum target, const GLbyte* mailbox), + (target, mailbox)) +VISIT_GL_CALL(ProduceTextureDirectCHROMIUM, + void, + (GLuint texture, GLenum target, const GLbyte* mailbox), + (texture, target, mailbox)) +VISIT_GL_CALL(ConsumeTextureCHROMIUM, + void, + (GLenum target, const GLbyte* mailbox), + (target, mailbox)) +VISIT_GL_CALL(CreateAndConsumeTextureCHROMIUM, + GLuint, + (GLenum target, const GLbyte* mailbox), + (target, mailbox)) diff --git a/mojo/public/c/gles2/gles2_export.h b/mojo/public/c/gles2/gles2_export.h index 4f8796da06908..60667b11cc7cd 100644 --- a/mojo/public/c/gles2/gles2_export.h +++ b/mojo/public/c/gles2/gles2_export.h @@ -5,6 +5,7 @@ #ifndef MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_ #define MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_ +#if defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL) #if defined(WIN32) #if defined(MOJO_GLES2_IMPLEMENTATION) @@ -23,4 +24,10 @@ #endif // defined(WIN32) +#else // !defined(COMPONENT_BUILD) || !defined(MOJO_USE_GLES2_IMPL) + +#define MOJO_GLES2_EXPORT + +#endif // defined(COMPONENT_BUILD) && defined(MOJO_USE_GLES2_IMPL) + #endif // MOJO_PUBLIC_C_GLES2_GLES2_EXPORT_H_ diff --git a/mojo/public/c/system/buffer.h b/mojo/public/c/system/buffer.h index e3b1bfeec3d5c..19e3c52d05209 100644 --- a/mojo/public/c/system/buffer.h +++ b/mojo/public/c/system/buffer.h @@ -35,7 +35,7 @@ const MojoCreateSharedBufferOptionsFlags MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE = 0; #else #define MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE \ - ((MojoCreateSharedBufferOptionsFlags) 0) + ((MojoCreateSharedBufferOptionsFlags)0) #endif MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment); @@ -64,7 +64,7 @@ const MojoDuplicateBufferHandleOptionsFlags MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE = 0; #else #define MOJO_DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE \ - ((MojoDuplicateBufferHandleOptionsFlags) 0) + ((MojoDuplicateBufferHandleOptionsFlags)0) #endif struct MojoDuplicateBufferHandleOptions { @@ -82,7 +82,7 @@ typedef uint32_t MojoMapBufferFlags; #ifdef __cplusplus const MojoMapBufferFlags MOJO_MAP_BUFFER_FLAG_NONE = 0; #else -#define MOJO_MAP_BUFFER_FLAG_NONE ((MojoMapBufferFlags) 0) +#define MOJO_MAP_BUFFER_FLAG_NONE ((MojoMapBufferFlags)0) #endif #ifdef __cplusplus @@ -116,8 +116,8 @@ extern "C" { // |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|. MOJO_SYSTEM_EXPORT MojoResult MojoCreateSharedBuffer( const struct MojoCreateSharedBufferOptions* options, // Optional. - uint64_t num_bytes, // In. - MojoHandle* shared_buffer_handle); // Out. + uint64_t num_bytes, // In. + MojoHandle* shared_buffer_handle); // Out. // Duplicates the handle |buffer_handle| to a buffer. This creates another // handle (returned in |*new_buffer_handle| on success), which can then be sent @@ -138,7 +138,7 @@ MOJO_SYSTEM_EXPORT MojoResult MojoCreateSharedBuffer( MOJO_SYSTEM_EXPORT MojoResult MojoDuplicateBufferHandle( MojoHandle buffer_handle, const struct MojoDuplicateBufferHandleOptions* options, // Optional. - MojoHandle* new_buffer_handle); // Out. + MojoHandle* new_buffer_handle); // Out. // Maps the part (at offset |offset| of length |num_bytes|) of the buffer given // by |buffer_handle| into memory, with options specified by |flags|. |offset + diff --git a/mojo/public/c/system/data_pipe.h b/mojo/public/c/system/data_pipe.h index 2b30504945e88..c8087eac623cf 100644 --- a/mojo/public/c/system/data_pipe.h +++ b/mojo/public/c/system/data_pipe.h @@ -35,15 +35,15 @@ typedef uint32_t MojoCreateDataPipeOptionsFlags; #ifdef __cplusplus -const MojoCreateDataPipeOptionsFlags - MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE = 0; +const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE = + 0; const MojoCreateDataPipeOptionsFlags MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD = 1 << 0; #else #define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE \ - ((MojoCreateDataPipeOptionsFlags) 0) + ((MojoCreateDataPipeOptionsFlags)0) #define MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD \ - ((MojoCreateDataPipeOptionsFlags) 1 << 0) + ((MojoCreateDataPipeOptionsFlags)1 << 0) #endif MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment); @@ -68,8 +68,8 @@ typedef uint32_t MojoWriteDataFlags; const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_NONE = 0; const MojoWriteDataFlags MOJO_WRITE_DATA_FLAG_ALL_OR_NONE = 1 << 0; #else -#define MOJO_WRITE_DATA_FLAG_NONE ((MojoWriteDataFlags) 0) -#define MOJO_WRITE_DATA_FLAG_ALL_OR_NONE ((MojoWriteDataFlags) 1 << 0) +#define MOJO_WRITE_DATA_FLAG_NONE ((MojoWriteDataFlags)0) +#define MOJO_WRITE_DATA_FLAG_ALL_OR_NONE ((MojoWriteDataFlags)1 << 0) #endif // |MojoReadDataFlags|: Used to specify different modes to |MojoReadData()| and @@ -92,10 +92,10 @@ const MojoReadDataFlags MOJO_READ_DATA_FLAG_ALL_OR_NONE = 1 << 0; const MojoReadDataFlags MOJO_READ_DATA_FLAG_DISCARD = 1 << 1; const MojoReadDataFlags MOJO_READ_DATA_FLAG_QUERY = 1 << 2; #else -#define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags) 0) -#define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags) 1 << 0) -#define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags) 1 << 1) -#define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags) 1 << 2) +#define MOJO_READ_DATA_FLAG_NONE ((MojoReadDataFlags)0) +#define MOJO_READ_DATA_FLAG_ALL_OR_NONE ((MojoReadDataFlags)1 << 0) +#define MOJO_READ_DATA_FLAG_DISCARD ((MojoReadDataFlags)1 << 1) +#define MOJO_READ_DATA_FLAG_QUERY ((MojoReadDataFlags)1 << 2) #endif #ifdef __cplusplus @@ -129,8 +129,8 @@ extern "C" { // |MOJO_RESULT_UNIMPLEMENTED| if an unsupported flag was set in |*options|. MOJO_SYSTEM_EXPORT MojoResult MojoCreateDataPipe( const struct MojoCreateDataPipeOptions* options, // Optional. - MojoHandle* data_pipe_producer_handle, // Out. - MojoHandle* data_pipe_consumer_handle); // Out. + MojoHandle* data_pipe_producer_handle, // Out. + MojoHandle* data_pipe_consumer_handle); // Out. // Writes the given data to the data pipe producer given by // |data_pipe_producer_handle|. |elements| points to data of size |*num_bytes|; @@ -168,11 +168,11 @@ MOJO_SYSTEM_EXPORT MojoResult MojoCreateDataPipe( // |MOJO_WRITE_DATA_FLAG_ALL_OR_NONE| set. // // TODO(vtl): Should there be a way of querying how much data can be written? -MOJO_SYSTEM_EXPORT MojoResult MojoWriteData( - MojoHandle data_pipe_producer_handle, - const void* elements, - uint32_t* num_bytes, // In/out. - MojoWriteDataFlags flags); +MOJO_SYSTEM_EXPORT MojoResult + MojoWriteData(MojoHandle data_pipe_producer_handle, + const void* elements, + uint32_t* num_bytes, // In/out. + MojoWriteDataFlags flags); // Begins a two-phase write to the data pipe producer given by // |data_pipe_producer_handle|. On success, |*buffer| will be a pointer to which @@ -213,11 +213,11 @@ MOJO_SYSTEM_EXPORT MojoResult MojoWriteData( // called, but not yet the matching |MojoEndWriteData()|). // |MOJO_RESULT_SHOULD_WAIT| if no data can currently be written (and the // consumer is still open). -MOJO_SYSTEM_EXPORT MojoResult MojoBeginWriteData( - MojoHandle data_pipe_producer_handle, - void** buffer, // Out. - uint32_t* buffer_num_bytes, // In/out. - MojoWriteDataFlags flags); +MOJO_SYSTEM_EXPORT MojoResult + MojoBeginWriteData(MojoHandle data_pipe_producer_handle, + void** buffer, // Out. + uint32_t* buffer_num_bytes, // In/out. + MojoWriteDataFlags flags); // Ends a two-phase write to the data pipe producer given by // |data_pipe_producer_handle| that was begun by a call to @@ -241,9 +241,9 @@ MOJO_SYSTEM_EXPORT MojoResult MojoBeginWriteData( // |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe producer is not in a // two-phase write (e.g., |MojoBeginWriteData()| was not called or // |MojoEndWriteData()| has already been called). -MOJO_SYSTEM_EXPORT MojoResult MojoEndWriteData( - MojoHandle data_pipe_producer_handle, - uint32_t num_bytes_written); +MOJO_SYSTEM_EXPORT MojoResult + MojoEndWriteData(MojoHandle data_pipe_producer_handle, + uint32_t num_bytes_written); // Reads data from the data pipe consumer given by |data_pipe_consumer_handle|. // May also be used to discard data or query the amount of data available. @@ -287,11 +287,10 @@ MOJO_SYSTEM_EXPORT MojoResult MojoEndWriteData( // |MOJO_RESULT_SHOULD_WAIT| if there is no data to be read or discarded (and // the producer is still open) and |flags| does *not* have // |MOJO_READ_DATA_FLAG_ALL_OR_NONE| set. -MOJO_SYSTEM_EXPORT MojoResult MojoReadData( - MojoHandle data_pipe_consumer_handle, - void* elements, // Out. - uint32_t* num_bytes, // In/out. - MojoReadDataFlags flags); +MOJO_SYSTEM_EXPORT MojoResult MojoReadData(MojoHandle data_pipe_consumer_handle, + void* elements, // Out. + uint32_t* num_bytes, // In/out. + MojoReadDataFlags flags); // Begins a two-phase read from the data pipe consumer given by // |data_pipe_consumer_handle|. On success, |*buffer| will be a pointer from @@ -329,11 +328,11 @@ MOJO_SYSTEM_EXPORT MojoResult MojoReadData( // called, but not yet the matching |MojoEndReadData()|). // |MOJO_RESULT_SHOULD_WAIT| if no data can currently be read (and the // producer is still open). -MOJO_SYSTEM_EXPORT MojoResult MojoBeginReadData( - MojoHandle data_pipe_consumer_handle, - const void** buffer, // Out. - uint32_t* buffer_num_bytes, // In/out. - MojoReadDataFlags flags); +MOJO_SYSTEM_EXPORT MojoResult + MojoBeginReadData(MojoHandle data_pipe_consumer_handle, + const void** buffer, // Out. + uint32_t* buffer_num_bytes, // In/out. + MojoReadDataFlags flags); // Ends a two-phase read from the data pipe consumer given by // |data_pipe_consumer_handle| that was begun by a call to |MojoBeginReadData()| @@ -354,9 +353,9 @@ MOJO_SYSTEM_EXPORT MojoResult MojoBeginReadData( // |MOJO_RESULT_FAILED_PRECONDITION| if the data pipe consumer is not in a // two-phase read (e.g., |MojoBeginReadData()| was not called or // |MojoEndReadData()| has already been called). -MOJO_SYSTEM_EXPORT MojoResult MojoEndReadData( - MojoHandle data_pipe_consumer_handle, - uint32_t num_bytes_read); +MOJO_SYSTEM_EXPORT MojoResult + MojoEndReadData(MojoHandle data_pipe_consumer_handle, + uint32_t num_bytes_read); #ifdef __cplusplus } // extern "C" diff --git a/mojo/public/c/system/macros.h b/mojo/public/c/system/macros.h index ae88249ec6411..564ee60ff8917 100644 --- a/mojo/public/c/system/macros.h +++ b/mojo/public/c/system/macros.h @@ -35,7 +35,7 @@ // if (TakeOwnership(my_var.get()) == SUCCESS) // mojo_ignore_result(my_var.release()); // -template +template inline void mojo_ignore_result(const T&) { } #endif @@ -47,9 +47,12 @@ inline void mojo_ignore_result(const T&) { #if __cplusplus >= 201103L #define MOJO_COMPILE_ASSERT(expr, msg) static_assert(expr, #msg) #elif defined(__cplusplus) -namespace mojo { template struct CompileAssert {}; } +namespace mojo { +template +struct CompileAssert {}; +} #define MOJO_COMPILE_ASSERT(expr, msg) \ - typedef ::mojo::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + typedef ::mojo::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] #else #define MOJO_COMPILE_ASSERT(expr, msg) #endif diff --git a/mojo/public/c/system/message_pipe.h b/mojo/public/c/system/message_pipe.h index 39b7859610989..b08ba75d8e9fd 100644 --- a/mojo/public/c/system/message_pipe.h +++ b/mojo/public/c/system/message_pipe.h @@ -28,7 +28,7 @@ const MojoCreateMessagePipeOptionsFlags MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE = 0; #else #define MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE \ - ((MojoCreateMessagePipeOptionsFlags) 0) + ((MojoCreateMessagePipeOptionsFlags)0) #endif MOJO_COMPILE_ASSERT(MOJO_ALIGNOF(int64_t) == 8, int64_t_has_weird_alignment); @@ -48,7 +48,7 @@ typedef uint32_t MojoWriteMessageFlags; #ifdef __cplusplus const MojoWriteMessageFlags MOJO_WRITE_MESSAGE_FLAG_NONE = 0; #else -#define MOJO_WRITE_MESSAGE_FLAG_NONE ((MojoWriteMessageFlags) 0) +#define MOJO_WRITE_MESSAGE_FLAG_NONE ((MojoWriteMessageFlags)0) #endif // |MojoReadMessageFlags|: Used to specify different modes to @@ -64,8 +64,8 @@ typedef uint32_t MojoReadMessageFlags; const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_NONE = 0; const MojoReadMessageFlags MOJO_READ_MESSAGE_FLAG_MAY_DISCARD = 1 << 0; #else -#define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags) 0) -#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags) 1 << 0) +#define MOJO_READ_MESSAGE_FLAG_NONE ((MojoReadMessageFlags)0) +#define MOJO_READ_MESSAGE_FLAG_MAY_DISCARD ((MojoReadMessageFlags)1 << 0) #endif #ifdef __cplusplus @@ -94,8 +94,8 @@ extern "C" { // TODO(vtl): Add an options struct pointer argument. MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe( const struct MojoCreateMessagePipeOptions* options, // Optional. - MojoHandle* message_pipe_handle0, // Out. - MojoHandle* message_pipe_handle1); // Out. + MojoHandle* message_pipe_handle0, // Out. + MojoHandle* message_pipe_handle1); // Out. // Writes a message to the message pipe endpoint given by |message_pipe_handle|, // with message data specified by |bytes| of size |num_bytes| and attached @@ -125,13 +125,13 @@ MOJO_SYSTEM_EXPORT MojoResult MojoCreateMessagePipe( // // TODO(vtl): Add a notion of capacity for message pipes, and return // |MOJO_RESULT_SHOULD_WAIT| if the message pipe is full. -MOJO_SYSTEM_EXPORT MojoResult MojoWriteMessage( - MojoHandle message_pipe_handle, - const void* bytes, // Optional. - uint32_t num_bytes, - const MojoHandle* handles, // Optional. - uint32_t num_handles, - MojoWriteMessageFlags flags); +MOJO_SYSTEM_EXPORT MojoResult + MojoWriteMessage(MojoHandle message_pipe_handle, + const void* bytes, // Optional. + uint32_t num_bytes, + const MojoHandle* handles, // Optional. + uint32_t num_handles, + MojoWriteMessageFlags flags); // Reads a message from the message pipe endpoint given by // |message_pipe_handle|; also usable to query the size of the next message or @@ -164,13 +164,13 @@ MOJO_SYSTEM_EXPORT MojoResult MojoWriteMessage( // error code; should distinguish this from the hitting-system-limits // case.) // |MOJO_RESULT_SHOULD_WAIT| if no message was available to be read. -MOJO_SYSTEM_EXPORT MojoResult MojoReadMessage( - MojoHandle message_pipe_handle, - void* bytes, // Optional out. - uint32_t* num_bytes, // Optional in/out. - MojoHandle* handles, // Optional out. - uint32_t* num_handles, // Optional in/out. - MojoReadMessageFlags flags); +MOJO_SYSTEM_EXPORT MojoResult + MojoReadMessage(MojoHandle message_pipe_handle, + void* bytes, // Optional out. + uint32_t* num_bytes, // Optional in/out. + MojoHandle* handles, // Optional out. + uint32_t* num_handles, // Optional in/out. + MojoReadMessageFlags flags); #ifdef __cplusplus } // extern "C" diff --git a/mojo/public/c/system/tests/core_perftest.cc b/mojo/public/c/system/tests/core_perftest.cc index c81f51ac9fe3d..868457b07da3b 100644 --- a/mojo/public/c/system/tests/core_perftest.cc +++ b/mojo/public/c/system/tests/core_perftest.cc @@ -28,9 +28,7 @@ namespace { class MessagePipeWriterThread : public mojo::Thread { public: MessagePipeWriterThread(MojoHandle handle, uint32_t num_bytes) - : handle_(handle), - num_bytes_(num_bytes), - num_writes_(0) {} + : handle_(handle), num_bytes_(num_bytes), num_writes_(0) {} virtual ~MessagePipeWriterThread() {} virtual void Run() MOJO_OVERRIDE { @@ -39,8 +37,8 @@ class MessagePipeWriterThread : public mojo::Thread { // TODO(vtl): Should I throttle somehow? for (;;) { - MojoResult result = MojoWriteMessage(handle_, buffer, num_bytes_, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE); + MojoResult result = MojoWriteMessage( + handle_, buffer, num_bytes_, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE); if (result == MOJO_RESULT_OK) { num_writes_++; continue; @@ -68,9 +66,7 @@ class MessagePipeWriterThread : public mojo::Thread { class MessagePipeReaderThread : public mojo::Thread { public: explicit MessagePipeReaderThread(MojoHandle handle) - : handle_(handle), - num_reads_(0) { - } + : handle_(handle), num_reads_(0) {} virtual ~MessagePipeReaderThread() {} virtual void Run() MOJO_OVERRIDE { @@ -78,16 +74,16 @@ class MessagePipeReaderThread : public mojo::Thread { for (;;) { uint32_t num_bytes = static_cast(sizeof(buffer)); - MojoResult result = MojoReadMessage(handle_, buffer, &num_bytes, NULL, - NULL, MOJO_READ_MESSAGE_FLAG_NONE); + MojoResult result = MojoReadMessage( + handle_, buffer, &num_bytes, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE); if (result == MOJO_RESULT_OK) { num_reads_++; continue; } if (result == MOJO_RESULT_SHOULD_WAIT) { - result = MojoWait(handle_, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE); + result = MojoWait( + handle_, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE); if (result == MOJO_RESULT_OK) { // Go to the top of the loop to read again. continue; @@ -118,8 +114,7 @@ class CorePerftest : public testing::Test { CorePerftest() : buffer_(NULL), num_bytes_(0) {} virtual ~CorePerftest() {} - static void NoOp(void* /*closure*/) { - } + static void NoOp(void* /*closure*/) {} static void MessagePipe_CreateAndClose(void* closure) { CorePerftest* self = static_cast(closure); @@ -136,14 +131,18 @@ class CorePerftest : public testing::Test { CorePerftest* self = static_cast(closure); MojoResult result MOJO_ALLOW_UNUSED; result = MojoWriteMessage(self->h0_, - self->buffer_, self->num_bytes_, - NULL, 0, + self->buffer_, + self->num_bytes_, + NULL, + 0, MOJO_WRITE_MESSAGE_FLAG_NONE); assert(result == MOJO_RESULT_OK); uint32_t read_bytes = self->num_bytes_; result = MojoReadMessage(self->h1_, - self->buffer_, &read_bytes, - NULL, NULL, + self->buffer_, + &read_bytes, + NULL, + NULL, MOJO_READ_MESSAGE_FLAG_NONE); assert(result == MOJO_RESULT_OK); } @@ -151,10 +150,8 @@ class CorePerftest : public testing::Test { static void MessagePipe_EmptyRead(void* closure) { CorePerftest* self = static_cast(closure); MojoResult result MOJO_ALLOW_UNUSED; - result = MojoReadMessage(self->h0_, - NULL, NULL, - NULL, NULL, - MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); + result = MojoReadMessage( + self->h0_, NULL, NULL, NULL, NULL, MOJO_READ_MESSAGE_FLAG_MAY_DISCARD); assert(result == MOJO_RESULT_SHOULD_WAIT); } @@ -223,18 +220,24 @@ class CorePerftest : public testing::Test { readers.clear(); char test_name[200]; - sprintf(test_name, "MessagePipe_Threaded_Writes_%uw_%ur_%ubytes", - num_writers, num_readers, static_cast(num_bytes)); - mojo::test::LogPerfResult(test_name, - 1000000.0 * static_cast(num_writes) / - (end_time - start_time), - "writes/second"); - sprintf(test_name, "MessagePipe_Threaded_Reads_%uw_%ur_%ubytes", - num_writers, num_readers, static_cast(num_bytes)); - mojo::test::LogPerfResult(test_name, - 1000000.0 * static_cast(num_reads) / - (end_time - start_time), - "reads/second"); + sprintf(test_name, + "MessagePipe_Threaded_Writes_%uw_%ur_%ubytes", + num_writers, + num_readers, + static_cast(num_bytes)); + mojo::test::LogPerfResult( + test_name, + 1000000.0 * static_cast(num_writes) / (end_time - start_time), + "writes/second"); + sprintf(test_name, + "MessagePipe_Threaded_Reads_%uw_%ur_%ubytes", + num_writers, + num_readers, + static_cast(num_bytes)); + mojo::test::LogPerfResult( + test_name, + 1000000.0 * static_cast(num_reads) / (end_time - start_time), + "reads/second"); } #endif // !defined(WIN32) @@ -248,8 +251,8 @@ class CorePerftest : public testing::Test { #if !defined(WIN32) void Sleep(int64_t microseconds) { struct timespec req = { - static_cast(microseconds / 1000000), // Seconds. - static_cast(microseconds % 1000000) * 1000L // Nanoseconds. + static_cast(microseconds / 1000000), // Seconds. + static_cast(microseconds % 1000000) * 1000L // Nanoseconds. }; int rv MOJO_ALLOW_UNUSED; rv = nanosleep(&req, NULL); @@ -275,7 +278,7 @@ TEST_F(CorePerftest, MessagePipe_WriteAndRead) { MojoResult result MOJO_ALLOW_UNUSED; result = MojoCreateMessagePipe(NULL, &h0_, &h1_); assert(result == MOJO_RESULT_OK); - char buffer[10000] = { 0 }; + char buffer[10000] = {0}; buffer_ = buffer; num_bytes_ = 10u; mojo::test::IterateAndReportPerf("MessagePipe_WriteAndRead_10bytes", @@ -303,9 +306,8 @@ TEST_F(CorePerftest, MessagePipe_EmptyRead) { MojoResult result MOJO_ALLOW_UNUSED; result = MojoCreateMessagePipe(NULL, &h0_, &h1_); assert(result == MOJO_RESULT_OK); - mojo::test::IterateAndReportPerf("MessagePipe_EmptyRead", - &CorePerftest::MessagePipe_EmptyRead, - this); + mojo::test::IterateAndReportPerf( + "MessagePipe_EmptyRead", &CorePerftest::MessagePipe_EmptyRead, this); result = MojoClose(h0_); assert(result == MOJO_RESULT_OK); result = MojoClose(h1_); diff --git a/mojo/public/c/system/tests/core_unittest.cc b/mojo/public/c/system/tests/core_unittest.cc index 9c29aea6e7ccb..a2f6785205238 100644 --- a/mojo/public/c/system/tests/core_unittest.cc +++ b/mojo/public/c/system/tests/core_unittest.cc @@ -24,7 +24,7 @@ TEST(CoreTest, GetTimeTicksNow) { TEST(CoreTest, InvalidHandle) { MojoHandle h0, h1; MojoHandleSignals sig; - char buffer[10] = { 0 }; + char buffer[10] = {0}; uint32_t buffer_size; void* write_pointer; const void* read_pointer; @@ -41,13 +41,14 @@ TEST(CoreTest, InvalidHandle) { MojoWaitMany(&h0, &sig, 1, MOJO_DEADLINE_INDEFINITE)); // Message pipe: - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWriteMessage(h0, buffer, 3, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoWriteMessage(h0, buffer, 3, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); buffer_size = static_cast(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoReadMessage(h0, buffer, &buffer_size, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoReadMessage( + h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE)); // Data pipe: buffer_size = static_cast(sizeof(buffer)); @@ -55,16 +56,16 @@ TEST(CoreTest, InvalidHandle) { MojoWriteData(h0, buffer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE)); write_pointer = NULL; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoBeginWriteData(h0, &write_pointer, &buffer_size, - MOJO_WRITE_DATA_FLAG_NONE)); + MojoBeginWriteData( + h0, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndWriteData(h0, 1)); buffer_size = static_cast(sizeof(buffer)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoReadData(h0, buffer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); read_pointer = NULL; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoBeginReadData(h0, &read_pointer, &buffer_size, - MOJO_READ_DATA_FLAG_NONE)); + MojoBeginReadData( + h0, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoEndReadData(h0, 1)); // Shared buffer: @@ -78,7 +79,7 @@ TEST(CoreTest, InvalidHandle) { TEST(CoreTest, BasicMessagePipe) { MojoHandle h0, h1; MojoHandleSignals sig; - char buffer[10] = { 0 }; + char buffer[10] = {0}; uint32_t buffer_size; h0 = MOJO_HANDLE_INVALID; @@ -96,16 +97,18 @@ TEST(CoreTest, BasicMessagePipe) { // Try to read. buffer_size = static_cast(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - MojoReadMessage(h0, buffer, &buffer_size, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ( + MOJO_RESULT_SHOULD_WAIT, + MojoReadMessage( + h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE)); // Write to |h1|. static const char kHello[] = "hello"; buffer_size = static_cast(sizeof(kHello)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(h1, kHello, buffer_size, NULL, 0, - MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ( + MOJO_RESULT_OK, + MojoWriteMessage( + h1, kHello, buffer_size, NULL, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); // |h0| should be readable. sig = MOJO_HANDLE_SIGNAL_READABLE; @@ -114,9 +117,10 @@ TEST(CoreTest, BasicMessagePipe) { // Read from |h0|. buffer_size = static_cast(sizeof(buffer)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(h0, buffer, &buffer_size, NULL, NULL, - MOJO_READ_MESSAGE_FLAG_NONE)); + EXPECT_EQ( + MOJO_RESULT_OK, + MojoReadMessage( + h0, buffer, &buffer_size, NULL, NULL, MOJO_READ_MESSAGE_FLAG_NONE)); EXPECT_EQ(static_cast(sizeof(kHello)), buffer_size); EXPECT_STREQ(kHello, buffer); @@ -128,10 +132,10 @@ TEST(CoreTest, BasicMessagePipe) { EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0)); // |h1| should no longer be readable or writable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - MojoWait(h1, - MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, - 1000)); + EXPECT_EQ( + MOJO_RESULT_FAILED_PRECONDITION, + MojoWait( + h1, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, 1000)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1)); } @@ -139,7 +143,7 @@ TEST(CoreTest, BasicMessagePipe) { TEST(CoreTest, BasicDataPipe) { MojoHandle hp, hc; MojoHandleSignals sig; - char buffer[20] = { 0 }; + char buffer[20] = {0}; uint32_t buffer_size; void* write_pointer; const void* read_pointer; @@ -165,16 +169,16 @@ TEST(CoreTest, BasicDataPipe) { // Try to begin a two-phase read from |hc|. read_pointer = NULL; EXPECT_EQ(MOJO_RESULT_SHOULD_WAIT, - MojoBeginReadData(hc, &read_pointer, &buffer_size, - MOJO_READ_DATA_FLAG_NONE)); + MojoBeginReadData( + hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); // Write to |hp|. static const char kHello[] = "hello "; // Don't include terminating null. buffer_size = static_cast(strlen(kHello)); - EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteData(hp, kHello, &buffer_size, - MOJO_WRITE_MESSAGE_FLAG_NONE)); + EXPECT_EQ( + MOJO_RESULT_OK, + MojoWriteData(hp, kHello, &buffer_size, MOJO_WRITE_MESSAGE_FLAG_NONE)); // |hc| should be(come) readable. sig = MOJO_HANDLE_SIGNAL_READABLE; @@ -183,8 +187,8 @@ TEST(CoreTest, BasicDataPipe) { // Do a two-phase write to |hp|. EXPECT_EQ(MOJO_RESULT_OK, - MojoBeginWriteData(hp, &write_pointer, &buffer_size, - MOJO_WRITE_DATA_FLAG_NONE)); + MojoBeginWriteData( + hp, &write_pointer, &buffer_size, MOJO_WRITE_DATA_FLAG_NONE)); static const char kWorld[] = "world"; ASSERT_GE(buffer_size, sizeof(kWorld)); // Include the terminating null. @@ -207,8 +211,8 @@ TEST(CoreTest, BasicDataPipe) { // Do a two-phase read from |hc|. read_pointer = NULL; EXPECT_EQ(MOJO_RESULT_OK, - MojoBeginReadData(hc, &read_pointer, &buffer_size, - MOJO_READ_DATA_FLAG_NONE)); + MojoBeginReadData( + hc, &read_pointer, &buffer_size, MOJO_READ_DATA_FLAG_NONE)); ASSERT_LE(buffer_size, sizeof(buffer) - 1); memcpy(&buffer[1], read_pointer, buffer_size); EXPECT_EQ(MOJO_RESULT_OK, MojoEndReadData(hc, buffer_size)); diff --git a/mojo/public/c/system/tests/core_unittest_pure_c.c b/mojo/public/c/system/tests/core_unittest_pure_c.c index 6464d8e342f06..33de6883f9654 100644 --- a/mojo/public/c/system/tests/core_unittest_pure_c.c +++ b/mojo/public/c/system/tests/core_unittest_pure_c.c @@ -22,14 +22,14 @@ __FILE__ "(" STRINGIFY2(__LINE__) "): Failure: " message // Poor man's gtest. -#define EXPECT_EQ(a, b) \ - do { \ - if ((a) != (b)) \ +#define EXPECT_EQ(a, b) \ + do { \ + if ((a) != (b)) \ return FAILURE(STRINGIFY(a) " != " STRINGIFY(b) " (expected ==)"); \ } while (0) -#define EXPECT_NE(a, b) \ - do { \ - if ((a) == (b)) \ +#define EXPECT_NE(a, b) \ + do { \ + if ((a) == (b)) \ return FAILURE(STRINGIFY(a) " == " STRINGIFY(b) " (expected !=)"); \ } while (0) @@ -44,7 +44,7 @@ const char* MinimalCTest(void) { MojoHandle handle0, handle1; MojoHandleSignals signals; const char kHello[] = "hello"; - char buffer[200] = { 0 }; + char buffer[200] = {0}; uint32_t num_bytes; ticks = MojoGetTimeTicksNow(); @@ -53,9 +53,9 @@ const char* MinimalCTest(void) { handle0 = MOJO_HANDLE_INVALID; EXPECT_NE(MOJO_RESULT_OK, MojoClose(handle0)); - EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, - MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, - MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ( + MOJO_RESULT_INVALID_ARGUMENT, + MojoWait(handle0, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE)); handle1 = MOJO_HANDLE_INVALID; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &handle0, &handle1)); @@ -65,18 +65,26 @@ const char* MinimalCTest(void) { MojoWaitMany(&handle0, &signals, 1, 1)); EXPECT_EQ(MOJO_RESULT_OK, - MojoWriteMessage(handle0, kHello, (uint32_t) sizeof(kHello), NULL, - 0u, MOJO_WRITE_DATA_FLAG_NONE)); - - EXPECT_EQ(MOJO_RESULT_OK, - MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, - MOJO_DEADLINE_INDEFINITE)); - - num_bytes = (uint32_t) sizeof(buffer); + MojoWriteMessage(handle0, + kHello, + (uint32_t)sizeof(kHello), + NULL, + 0u, + MOJO_WRITE_DATA_FLAG_NONE)); + + EXPECT_EQ( + MOJO_RESULT_OK, + MojoWait(handle1, MOJO_HANDLE_SIGNAL_READABLE, MOJO_DEADLINE_INDEFINITE)); + + num_bytes = (uint32_t)sizeof(buffer); EXPECT_EQ(MOJO_RESULT_OK, - MojoReadMessage(handle1, buffer, &num_bytes, NULL, NULL, + MojoReadMessage(handle1, + buffer, + &num_bytes, + NULL, + NULL, MOJO_READ_MESSAGE_FLAG_NONE)); - EXPECT_EQ((uint32_t) sizeof(kHello), num_bytes); + EXPECT_EQ((uint32_t)sizeof(kHello), num_bytes); EXPECT_EQ(0, memcmp(buffer, kHello, sizeof(kHello))); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handle0)); diff --git a/mojo/public/c/system/tests/macros_unittest.cc b/mojo/public/c/system/tests/macros_unittest.cc index fdc0c87c99c2d..6a694b81b1b1c 100644 --- a/mojo/public/c/system/tests/macros_unittest.cc +++ b/mojo/public/c/system/tests/macros_unittest.cc @@ -58,11 +58,17 @@ TEST(MacrosTest, Alignof) { #if defined(_MSC_VER) #pragma warning(push) // Disable the warning "structure was padded due to __declspec(align())". -#pragma warning(disable:4324) +#pragma warning(disable : 4324) #endif -struct MOJO_ALIGNAS(1) StructAlignas1 { char x; }; -struct MOJO_ALIGNAS(4) StructAlignas4 { char x; }; -struct MOJO_ALIGNAS(8) StructAlignas8 { char x; }; +struct MOJO_ALIGNAS(1) StructAlignas1 { + char x; +}; +struct MOJO_ALIGNAS(4) StructAlignas4 { + char x; +}; +struct MOJO_ALIGNAS(8) StructAlignas8 { + char x; +}; #if defined(_MSC_VER) #pragma warning(pop) #endif diff --git a/mojo/public/c/system/types.h b/mojo/public/c/system/types.h index e992d110e3aa0..5a72d2f2e6833 100644 --- a/mojo/public/c/system/types.h +++ b/mojo/public/c/system/types.h @@ -29,7 +29,7 @@ typedef uint32_t MojoHandle; #ifdef __cplusplus const MojoHandle MOJO_HANDLE_INVALID = 0; #else -#define MOJO_HANDLE_INVALID ((MojoHandle) 0) +#define MOJO_HANDLE_INVALID ((MojoHandle)0) #endif // |MojoResult|: Result codes for Mojo operations. Non-negative values are @@ -110,24 +110,24 @@ const MojoResult MOJO_RESULT_DATA_LOSS = -15; const MojoResult MOJO_RESULT_BUSY = -16; const MojoResult MOJO_RESULT_SHOULD_WAIT = -17; #else -#define MOJO_RESULT_OK ((MojoResult) 0) -#define MOJO_RESULT_CANCELLED ((MojoResult) -1) -#define MOJO_RESULT_UNKNOWN ((MojoResult) -2) -#define MOJO_RESULT_INVALID_ARGUMENT ((MojoResult) -3) -#define MOJO_RESULT_DEADLINE_EXCEEDED ((MojoResult) -4) -#define MOJO_RESULT_NOT_FOUND ((MojoResult) -5) -#define MOJO_RESULT_ALREADY_EXISTS ((MojoResult) -6) -#define MOJO_RESULT_PERMISSION_DENIED ((MojoResult) -7) -#define MOJO_RESULT_RESOURCE_EXHAUSTED ((MojoResult) -8) -#define MOJO_RESULT_FAILED_PRECONDITION ((MojoResult) -9) -#define MOJO_RESULT_ABORTED ((MojoResult) -10) -#define MOJO_RESULT_OUT_OF_RANGE ((MojoResult) -11) -#define MOJO_RESULT_UNIMPLEMENTED ((MojoResult) -12) -#define MOJO_RESULT_INTERNAL ((MojoResult) -13) -#define MOJO_RESULT_UNAVAILABLE ((MojoResult) -14) -#define MOJO_RESULT_DATA_LOSS ((MojoResult) -15) -#define MOJO_RESULT_BUSY ((MojoResult) -16) -#define MOJO_RESULT_SHOULD_WAIT ((MojoResult) -17) +#define MOJO_RESULT_OK ((MojoResult)0) +#define MOJO_RESULT_CANCELLED ((MojoResult) - 1) +#define MOJO_RESULT_UNKNOWN ((MojoResult) - 2) +#define MOJO_RESULT_INVALID_ARGUMENT ((MojoResult) - 3) +#define MOJO_RESULT_DEADLINE_EXCEEDED ((MojoResult) - 4) +#define MOJO_RESULT_NOT_FOUND ((MojoResult) - 5) +#define MOJO_RESULT_ALREADY_EXISTS ((MojoResult) - 6) +#define MOJO_RESULT_PERMISSION_DENIED ((MojoResult) - 7) +#define MOJO_RESULT_RESOURCE_EXHAUSTED ((MojoResult) - 8) +#define MOJO_RESULT_FAILED_PRECONDITION ((MojoResult) - 9) +#define MOJO_RESULT_ABORTED ((MojoResult) - 10) +#define MOJO_RESULT_OUT_OF_RANGE ((MojoResult) - 11) +#define MOJO_RESULT_UNIMPLEMENTED ((MojoResult) - 12) +#define MOJO_RESULT_INTERNAL ((MojoResult) - 13) +#define MOJO_RESULT_UNAVAILABLE ((MojoResult) - 14) +#define MOJO_RESULT_DATA_LOSS ((MojoResult) - 15) +#define MOJO_RESULT_BUSY ((MojoResult) - 16) +#define MOJO_RESULT_SHOULD_WAIT ((MojoResult) - 17) #endif // |MojoDeadline|: Used to specify deadlines (timeouts), in microseconds (except @@ -139,7 +139,7 @@ typedef uint64_t MojoDeadline; #ifdef __cplusplus const MojoDeadline MOJO_DEADLINE_INDEFINITE = static_cast(-1); #else -#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline) -1) +#define MOJO_DEADLINE_INDEFINITE ((MojoDeadline) - 1) #endif // |MojoHandleSignals|: Used to specify signals that can be waited on for a @@ -157,9 +157,9 @@ const MojoHandleSignals MOJO_HANDLE_SIGNAL_NONE = 0; const MojoHandleSignals MOJO_HANDLE_SIGNAL_READABLE = 1 << 0; const MojoHandleSignals MOJO_HANDLE_SIGNAL_WRITABLE = 1 << 1; #else -#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals) 0) -#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals) 1 << 0) -#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals) 1 << 1) +#define MOJO_HANDLE_SIGNAL_NONE ((MojoHandleSignals)0) +#define MOJO_HANDLE_SIGNAL_READABLE ((MojoHandleSignals)1 << 0) +#define MOJO_HANDLE_SIGNAL_WRITABLE ((MojoHandleSignals)1 << 1) #endif // TODO(vtl): Add out parameters with this to MojoWait/MojoWaitMany. diff --git a/mojo/public/cpp/application/lib/service_provider_impl.cc b/mojo/public/cpp/application/lib/service_provider_impl.cc index c9706014d6ad0..1edebd2553252 100644 --- a/mojo/public/cpp/application/lib/service_provider_impl.cc +++ b/mojo/public/cpp/application/lib/service_provider_impl.cc @@ -19,7 +19,7 @@ ServiceProviderImpl::~ServiceProviderImpl() { ServiceProvider* ServiceProviderImpl::CreateRemoteServiceProvider() { // TODO(beng): it sure would be nice if this method could return a scoped_ptr. MOJO_DCHECK(!remote_); - remote_ = new internal::WeakServiceProvider(client()); + remote_ = new internal::WeakServiceProvider(this, client()); return remote_; } @@ -38,10 +38,7 @@ void ServiceProviderImpl::ConnectToService( } void ServiceProviderImpl::OnConnectionError() { - if (remote_) { - remote_->Clear(); - remote_ = NULL; - } + ClearRemote(); } void ServiceProviderImpl::AddServiceConnector( @@ -62,4 +59,11 @@ void ServiceProviderImpl::RemoveServiceConnector( service_connectors_.erase(it); } +void ServiceProviderImpl::ClearRemote() { + if (remote_) { + remote_->Clear(); + remote_ = NULL; + } +} + } // namespace mojo diff --git a/mojo/public/cpp/application/lib/weak_service_provider.cc b/mojo/public/cpp/application/lib/weak_service_provider.cc index 542e62df35aec..7e97876f51457 100644 --- a/mojo/public/cpp/application/lib/weak_service_provider.cc +++ b/mojo/public/cpp/application/lib/weak_service_provider.cc @@ -3,17 +3,25 @@ // found in the LICENSE file. #include "mojo/public/cpp/application/lib/weak_service_provider.h" + +#include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" namespace mojo { namespace internal { -WeakServiceProvider::WeakServiceProvider(ServiceProvider* service_provider) - : service_provider_(service_provider) {} +WeakServiceProvider::WeakServiceProvider(ServiceProviderImpl* creator, + ServiceProvider* service_provider) + : creator_(creator), + service_provider_(service_provider) {} -WeakServiceProvider::~WeakServiceProvider() {} +WeakServiceProvider::~WeakServiceProvider() { + if (creator_) + creator_->ClearRemote(); +} void WeakServiceProvider::Clear() { + creator_ = NULL; service_provider_ = NULL; } diff --git a/mojo/public/cpp/application/lib/weak_service_provider.h b/mojo/public/cpp/application/lib/weak_service_provider.h index e23a2f055bdbd..78ef632d967dd 100644 --- a/mojo/public/cpp/application/lib/weak_service_provider.h +++ b/mojo/public/cpp/application/lib/weak_service_provider.h @@ -8,6 +8,7 @@ #include "mojo/public/interfaces/application/service_provider.mojom.h" namespace mojo { +class ServiceProviderImpl; namespace internal { class ServiceConnectorBase; @@ -17,7 +18,8 @@ class ServiceConnectorBase; // Calls to ConnectToService() are silently dropped when the pipe is closed. class WeakServiceProvider : public ServiceProvider { public: - explicit WeakServiceProvider(ServiceProvider* service_provider); + WeakServiceProvider(ServiceProviderImpl* creator, + ServiceProvider* service_provider); virtual ~WeakServiceProvider(); void Clear(); @@ -28,6 +30,7 @@ class WeakServiceProvider : public ServiceProvider { const String& service_name, ScopedMessagePipeHandle client_handle) MOJO_OVERRIDE; + ServiceProviderImpl* creator_; ServiceProvider* service_provider_; MOJO_DISALLOW_COPY_AND_ASSIGN(WeakServiceProvider); diff --git a/mojo/public/cpp/application/service_provider_impl.h b/mojo/public/cpp/application/service_provider_impl.h index 657be22b12a5a..0f56a0d530c4b 100644 --- a/mojo/public/cpp/application/service_provider_impl.h +++ b/mojo/public/cpp/application/service_provider_impl.h @@ -39,6 +39,8 @@ class ServiceProviderImpl : public InterfaceImpl { typedef std::map NameToServiceConnectorMap; + friend class internal::WeakServiceProvider; + // Overridden from ServiceProvider: virtual void ConnectToService( const String& service_name, @@ -52,6 +54,8 @@ class ServiceProviderImpl : public InterfaceImpl { void RemoveServiceConnector( internal::ServiceConnectorBase* service_connector); + void ClearRemote(); + NameToServiceConnectorMap service_connectors_; internal::WeakServiceProvider* remote_; diff --git a/mojo/public/cpp/bindings/lib/array_internal.cc b/mojo/public/cpp/bindings/lib/array_internal.cc index 3f7f1ce0b16bb..835b78a5b9a47 100644 --- a/mojo/public/cpp/bindings/lib/array_internal.cc +++ b/mojo/public/cpp/bindings/lib/array_internal.cc @@ -52,19 +52,5 @@ void ArraySerializationHelper::DecodePointersAndHandles( DecodeHandle(&elements[i], handles); } -// static -bool ArraySerializationHelper::ValidateElements( - const ArrayHeader* header, - const ElementType* elements, - BoundsChecker* bounds_checker) { - for (uint32_t i = 0; i < header->num_elements; ++i) { - if (!bounds_checker->ClaimHandle(elements[i])) { - ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE); - return false; - } - } - return true; -} - } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/array_internal.h b/mojo/public/cpp/bindings/lib/array_internal.h index 190f6da9ea1c9..f74c857a35930 100644 --- a/mojo/public/cpp/bindings/lib/array_internal.h +++ b/mojo/public/cpp/bindings/lib/array_internal.h @@ -8,10 +8,12 @@ #include #include +#include "mojo/public/c/system/macros.h" #include "mojo/public/cpp/bindings/lib/bindings_internal.h" #include "mojo/public/cpp/bindings/lib/bindings_serialization.h" #include "mojo/public/cpp/bindings/lib/bounds_checker.h" #include "mojo/public/cpp/bindings/lib/buffer.h" +#include "mojo/public/cpp/bindings/lib/template_util.h" #include "mojo/public/cpp/bindings/lib/validation_errors.h" #include "mojo/public/cpp/environment/logging.h" @@ -110,11 +112,32 @@ struct ArrayDataTraits { } }; +// Array type information needed for valdiation. +template +class ArrayValidateParams { + public: + // Validation information for elements. It is either another specialization of + // ArrayValidateParams (if elements are arrays) or NoValidateParams. + typedef InElementValidateParams ElementValidateParams; + + // If |expected_num_elements| is not 0, the array is expected to have exactly + // that number of elements. + static const uint32_t expected_num_elements = in_expected_num_elements; + // Whether the elements are nullable. + static const bool element_nullable = in_element_nullable; +}; + +// NoValidateParams is used to indicate the end of an ArrayValidateParams chain. +class NoValidateParams { +}; + // What follows is code to support the serialization of Array_Data. There // are two interesting cases: arrays of primitives and arrays of objects. // Arrays of objects are represented as arrays of pointers to objects. -template struct ArraySerializationHelper; +template struct ArraySerializationHelper; template struct ArraySerializationHelper { @@ -130,9 +153,15 @@ struct ArraySerializationHelper { std::vector* handles) { } + template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { + MOJO_COMPILE_ASSERT(!element_nullable, + Primitive_type_should_be_non_nullable); + MOJO_COMPILE_ASSERT( + (IsSame::value), + Primitive_type_should_not_have_array_validate_params); return true; } }; @@ -149,9 +178,27 @@ struct ArraySerializationHelper { ElementType* elements, std::vector* handles); + template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, - BoundsChecker* bounds_checker); + BoundsChecker* bounds_checker) { + MOJO_COMPILE_ASSERT( + (IsSame::value), + Handle_type_should_not_have_array_validate_params); + + for (uint32_t i = 0; i < header->num_elements; ++i) { + if (IsNonNullableValidationEnabled() && !element_nullable && + elements[i].value() == kEncodedInvalidHandleValue) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE); + return false; + } + if (!bounds_checker->ClaimHandle(elements[i])) { + ReportValidationError(VALIDATION_ERROR_ILLEGAL_HANDLE); + return false; + } + } + return true; + } }; template @@ -172,11 +219,13 @@ struct ArraySerializationHelper { header, elements, handles); } + template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { - return ArraySerializationHelper::ValidateElements( - header, elements, bounds_checker); + return ArraySerializationHelper:: + ValidateElements( + header, elements, bounds_checker); } }; @@ -198,19 +247,46 @@ struct ArraySerializationHelper { Decode(&elements[i], handles); } + template static bool ValidateElements(const ArrayHeader* header, const ElementType* elements, BoundsChecker* bounds_checker) { for (uint32_t i = 0; i < header->num_elements; ++i) { + if (IsNonNullableValidationEnabled() && !element_nullable && + !elements[i].offset) { + ReportValidationError(VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + return false; + } if (!ValidateEncodedPointer(&elements[i].offset)) { ReportValidationError(VALIDATION_ERROR_ILLEGAL_POINTER); return false; } - if (!P::Validate(DecodePointerRaw(&elements[i].offset), bounds_checker)) + if (!ValidateCaller::Run( + DecodePointerRaw(&elements[i].offset), bounds_checker)) { return false; + } } return true; } + + private: + template + struct ValidateCaller { + static bool Run(const void* data, BoundsChecker* bounds_checker) { + MOJO_COMPILE_ASSERT( + (IsSame::value), + Struct_type_should_not_have_array_validate_params); + + return T::Validate(data, bounds_checker); + } + }; + + template + struct ValidateCaller, Params> { + static bool Run(const void* data, BoundsChecker* bounds_checker) { + return Array_Data::template Validate(data, bounds_checker); + } + }; }; template @@ -229,11 +305,8 @@ class Array_Data { num_elements); } - // If expected_num_elements is not zero, the actual number of elements in the - // header must match that value or the message is rejected. - static bool Validate(const void* data, - BoundsChecker* bounds_checker, - uint32_t expected_num_elements = 0) { + template + static bool Validate(const void* data, BoundsChecker* bounds_checker) { if (!data) return true; if (!IsAligned(data)) { @@ -250,8 +323,8 @@ class Array_Data { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER); return false; } - if (expected_num_elements != 0 && - header->num_elements != expected_num_elements) { + if (Params::expected_num_elements != 0 && + header->num_elements != Params::expected_num_elements) { ReportValidationError(VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER); return false; } @@ -261,8 +334,9 @@ class Array_Data { } const Array_Data* object = static_cast*>(data); - return Helper::ValidateElements(&object->header_, object->storage(), - bounds_checker); + return Helper::template ValidateElements< + Params::element_nullable, typename Params::ElementValidateParams>( + &object->header_, object->storage(), bounds_checker); } size_t size() const { return header_.num_elements; } diff --git a/mojo/public/cpp/bindings/lib/validation_errors.cc b/mojo/public/cpp/bindings/lib/validation_errors.cc index c1bd9ae76421c..37cd7d2062bc8 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.cc +++ b/mojo/public/cpp/bindings/lib/validation_errors.cc @@ -28,8 +28,12 @@ const char* ValidationErrorToString(ValidationError error) { return "VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER"; case VALIDATION_ERROR_ILLEGAL_HANDLE: return "VALIDATION_ERROR_ILLEGAL_HANDLE"; + case VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE: + return "VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE"; case VALIDATION_ERROR_ILLEGAL_POINTER: return "VALIDATION_ERROR_ILLEGAL_POINTER"; + case VALIDATION_ERROR_UNEXPECTED_NULL_POINTER: + return "VALIDATION_ERROR_UNEXPECTED_NULL_POINTER"; case VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION: return "VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION"; case VALIDATION_ERROR_MESSAGE_HEADER_MISSING_REQUEST_ID: @@ -50,6 +54,8 @@ ValidationErrorObserverForTesting::ValidationErrorObserverForTesting() : last_error_(VALIDATION_ERROR_NONE) { MOJO_DCHECK(!g_validation_error_observer); g_validation_error_observer = this; + MOJO_LOG(WARNING) << "Non-nullable validation is turned on for testing but " + << "not for production code yet!"; } ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() { @@ -57,5 +63,9 @@ ValidationErrorObserverForTesting::~ValidationErrorObserverForTesting() { g_validation_error_observer = NULL; } +bool IsNonNullableValidationEnabled() { + return !!g_validation_error_observer; +} + } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/bindings/lib/validation_errors.h b/mojo/public/cpp/bindings/lib/validation_errors.h index 9b443c371e50e..6e698fc2e0ae3 100644 --- a/mojo/public/cpp/bindings/lib/validation_errors.h +++ b/mojo/public/cpp/bindings/lib/validation_errors.h @@ -28,11 +28,17 @@ enum ValidationError { // An array header doesn't make sense, for example: // - |num_bytes| is smaller than the size of the header plus the size required // to store |num_elements| elements. + // - For fixed-size arrays, |num_elements| is different than the specified + // size. VALIDATION_ERROR_UNEXPECTED_ARRAY_HEADER, // An encoded handle is illegal. VALIDATION_ERROR_ILLEGAL_HANDLE, + // A non-nullable handle field is set to invalid handle. + VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE, // An encoded pointer is illegal. VALIDATION_ERROR_ILLEGAL_POINTER, + // A non-nullable pointer field is set to null. + VALIDATION_ERROR_UNEXPECTED_NULL_POINTER, // |flags| in the message header is an invalid flag combination. VALIDATION_ERROR_MESSAGE_HEADER_INVALID_FLAG_COMBINATION, // |flags| in the message header indicates that a request ID is required but @@ -60,6 +66,14 @@ class ValidationErrorObserverForTesting { MOJO_DISALLOW_COPY_AND_ASSIGN(ValidationErrorObserverForTesting); }; +// Currently it only returns true when there is a +// ValidationErrorObserverForTesting object alive. In other words, non-nullable +// validation is only turned on during validation tests. +// +// TODO(yzshen): Remove this function and enable non-nullable validation by +// default. +bool IsNonNullableValidationEnabled(); + } // namespace internal } // namespace mojo diff --git a/mojo/public/cpp/gles2/gles2.h b/mojo/public/cpp/gles2/gles2.h deleted file mode 100644 index a408fc701ebeb..0000000000000 --- a/mojo/public/cpp/gles2/gles2.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_CPP_GLES2_GLES2_H_ -#define MOJO_PUBLIC_CPP_GLES2_GLES2_H_ - -#include "mojo/public/c/gles2/gles2.h" -#include "mojo/public/cpp/environment/environment.h" - -namespace mojo { - -class GLES2Initializer { - public: - explicit GLES2Initializer(const MojoAsyncWaiter* async_waiter = - Environment::GetDefaultAsyncWaiter()) { - MojoGLES2Initialize(async_waiter); - } - ~GLES2Initializer() { MojoGLES2Terminate(); } -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_CPP_GLES2_GLES2_H_ diff --git a/mojo/public/gles2/BUILD.gn b/mojo/public/gles2/BUILD.gn index b47063625c3bf..d84241c33c40a 100644 --- a/mojo/public/gles2/BUILD.gn +++ b/mojo/public/gles2/BUILD.gn @@ -22,8 +22,9 @@ shared_library("gles2") { sources = [ "../c/gles2/gles2.h", "../c/gles2/gles2_export.h", - "gles2_private.cc", - "gles2_private.h", + "../platform/native/gles2_thunks.cc", + "../platform/native/gles2_thunks.h", + "gles2_interface.h", ] if (is_mac) { diff --git a/mojo/public/gles2/gles2_private.cc b/mojo/public/gles2/gles2_private.cc deleted file mode 100644 index c82e9deede9fe..0000000000000 --- a/mojo/public/gles2/gles2_private.cc +++ /dev/null @@ -1,80 +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/public/gles2/gles2_private.h" - -#include -#include - -#include "mojo/public/c/gles2/gles2.h" -#include "mojo/public/gles2/gles2_interface.h" - -static mojo::GLES2Support* g_gles2_support = NULL; -static mojo::GLES2Interface* g_gles2_interface = NULL; - -extern "C" { - -void MojoGLES2Initialize(const MojoAsyncWaiter* async_waiter) { - assert(g_gles2_support); - return g_gles2_support->Initialize(async_waiter); -} - -void MojoGLES2Terminate() { - assert(g_gles2_support); - return g_gles2_support->Terminate(); -} - -MojoGLES2Context MojoGLES2CreateContext( - MojoHandle handle, - MojoGLES2ContextLost lost_callback, - void* closure) { - return g_gles2_support->CreateContext(mojo::MessagePipeHandle(handle), - lost_callback, - closure); -} - -void MojoGLES2DestroyContext(MojoGLES2Context context) { - return g_gles2_support->DestroyContext(context); -} - -void MojoGLES2MakeCurrent(MojoGLES2Context context) { - assert(g_gles2_support); - g_gles2_support->MakeCurrent(context); - g_gles2_interface = g_gles2_support->GetGLES2InterfaceForCurrentContext(); -} - -void MojoGLES2SwapBuffers() { - assert(g_gles2_support); - return g_gles2_support->SwapBuffers(); -} - -void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) { - assert(g_gles2_support); - return g_gles2_support->GetGLES2Interface(context); -} - -void* MojoGLES2GetContextSupport(MojoGLES2Context context) { - assert(g_gles2_support); - return g_gles2_support->GetContextSupport(context); -} - -#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ - ReturnType gl##Function PARAMETERS { \ - return g_gles2_interface->Function ARGUMENTS; \ - } -#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" -#undef VISIT_GL_CALL - -} // extern "C" - -namespace mojo { - -GLES2Support::~GLES2Support() {} - -void GLES2Support::Init(GLES2Support* gles2_support) { - assert(!g_gles2_support); - g_gles2_support = gles2_support; -} - -} // namespace mojo diff --git a/mojo/public/gles2/gles2_private.h b/mojo/public/gles2/gles2_private.h deleted file mode 100644 index 0854f6fc55c70..0000000000000 --- a/mojo/public/gles2/gles2_private.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_PUBLIC_GLES2_GLES2_PRIVATE_H_ -#define MOJO_PUBLIC_GLES2_GLES2_PRIVATE_H_ - -#include - -#include "mojo/public/c/environment/async_waiter.h" -#include "mojo/public/c/gles2/gles2_export.h" -#include "mojo/public/c/gles2/gles2_types.h" -#include "mojo/public/cpp/system/core.h" - -namespace mojo { -class GLES2Interface; - -// Implementors of the GLES2 APIs can use this interface to install their -// implementation into the mojo_gles2 dynamic library. Mojo clients should not -// call these functions directly. -class MOJO_GLES2_EXPORT GLES2Support { - public: - virtual ~GLES2Support(); - - static void Init(GLES2Support* gles2_support); - - virtual void Initialize(const MojoAsyncWaiter* async_waiter) = 0; - virtual void Terminate() = 0; - virtual MojoGLES2Context CreateContext( - MessagePipeHandle handle, - MojoGLES2ContextLost lost_callback, - void* closure) = 0; - virtual void DestroyContext(MojoGLES2Context context) = 0; - virtual void MakeCurrent(MojoGLES2Context context) = 0; - virtual void SwapBuffers() = 0; - virtual void* GetGLES2Interface(MojoGLES2Context context) = 0; - virtual void* GetContextSupport(MojoGLES2Context context) = 0; - virtual GLES2Interface* GetGLES2InterfaceForCurrentContext() = 0; -}; - -} // namespace mojo - -#endif // MOJO_PUBLIC_GLES2_GLES2_PRIVATE_H_ diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good_null_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good_null_struct.data deleted file mode 100644 index be267a103a8ae..0000000000000 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good_null_struct.data +++ /dev/null @@ -1,10 +0,0 @@ -[dist4]message_header // num_bytes -[u4]2 // num_fields -[u4]1 // name -[u4]0 // flags -[anchr]message_header - -[dist4]method1_params // num_bytes -[u4]1 // num_fields -[u8]0 // param0: A null pointer. -[anchr]method1_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data new file mode 100644 index 0000000000000..c53a78b15b0c6 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.data @@ -0,0 +1,10 @@ +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]1 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method1_params // num_bytes +[u4]1 // num_fields +[u8]0 // param0: An unexpected null pointer. +[anchr]method1_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected new file mode 100644 index 0000000000000..95d8db01bb51a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_unexpected_null_struct.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good_null_array.data deleted file mode 100644 index 2f73ee53b1d65..0000000000000 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good_null_array.data +++ /dev/null @@ -1,10 +0,0 @@ -[dist4]message_header // num_bytes -[u4]2 // num_fields -[u4]3 // name -[u4]0 // flags -[anchr]message_header - -[dist4]method3_params // num_bytes -[u4]1 // num_fields -[u8]0 // param0: A null pointer. -[anchr]method3_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data new file mode 100644 index 0000000000000..0edab4bcacac8 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.data @@ -0,0 +1,10 @@ +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]3 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method3_params // num_bytes +[u4]1 // num_fields +[u8]0 // param0: An unexpected null pointer. +[anchr]method3_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected new file mode 100644 index 0000000000000..95d8db01bb51a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_unexpected_null_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good_invalid_handle.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good_invalid_handle.data deleted file mode 100644 index a63e2d0d638ff..0000000000000 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good_invalid_handle.data +++ /dev/null @@ -1,36 +0,0 @@ -[handles]0 - -[dist4]message_header // num_bytes -[u4]2 // num_fields -[u4]5 // name -[u4]0 // flags -[anchr]message_header - -[dist4]method5_params // num_bytes -[u4]2 // num_fields -[dist8]param0_ptr // param0 -[s4]-1 // param1: An invalid handle. -[u4]0 // padding -[anchr]method5_params - -[anchr]param0_ptr -[dist4]struct_e // num_bytes -[u4]2 // num_fields -[dist8]struct_d_ptr // struct_d -[s4]-1 // data_pipe_consumer: An invalid handle. -[u4]0 // padding -[anchr]struct_e - -[anchr]struct_d_ptr -[dist4]struct_d // num_bytes -[u4]1 // num_fields -[dist8]message_pipes_ptr // message_pipes -[anchr]struct_d - -[anchr]message_pipes_ptr -[dist4]message_pipe_array // num_bytes -[u4]3 // num_elements -[s4]-1 // An invalid handle. -[s4]-1 // An invalid handle. -[s4]-1 // An invalid handle. -[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data index 557c2ec18c468..43cad7cdc48b1 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_handle_out_of_range.data @@ -8,8 +8,29 @@ [dist4]method5_params // num_bytes [u4]2 // num_fields -[u8]0 // param0 +[dist8]param0_ptr // param0 [u4]10 // param1: It is outside of the valid encoded handle - // range [0, 9). + // range [0, 10). [u4]0 // padding [anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]2 // num_fields +[dist8]struct_d_ptr // struct_d +[u4]3 // data_pipe_consumer +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]1 // num_fields +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]0 +[u4]1 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data index a736b5cbd6333..8b39c009c3210 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_multiple_handles_with_same_value_1.data @@ -16,7 +16,20 @@ [anchr]param0_ptr [dist4]struct_e // num_bytes [u4]2 // num_fields -[u8]0 // struct_d +[dist8]struct_d_ptr // struct_d [u4]4 // data_pipe_consumer: The same value as |param1| above. [u4]0 // padding [anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]1 // num_fields +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]2 // num_elements +[u4]0 +[u4]1 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data new file mode 100644 index 0000000000000..ef49285163e12 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.data @@ -0,0 +1,34 @@ +[handles]5 + +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]5 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method5_params // num_bytes +[u4]2 // num_fields +[dist8]param0_ptr // param0 +[u4]4 // param1 +[u4]0 // padding +[anchr]method5_params + +[anchr]param0_ptr +[dist4]struct_e // num_bytes +[u4]2 // num_fields +[dist8]struct_d_ptr // struct_d +[s4]-1 // data_pipe_consumer: An unexpected invalid handle. +[u4]0 // padding +[anchr]struct_e + +[anchr]struct_d_ptr +[dist4]struct_d // num_bytes +[u4]1 // num_fields +[dist8]message_pipes_ptr // message_pipes +[anchr]struct_d + +[anchr]message_pipes_ptr +[dist4]message_pipe_array // num_bytes +[u4]1 // num_elements +[u4]2 +[anchr]message_pipe_array diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected new file mode 100644 index 0000000000000..6768236f6eb90 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_unexpected_invalid_handle.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data index c6e97d47b9631..d6562af6adbc1 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_good.data @@ -8,14 +8,14 @@ [u4]2 // num_fields [dist8]param0_ptr // param0 [dist8]param1_ptr // param1 -[u8]0 // padding +[u8]0 // unknown [anchr]method7_params [anchr]param0_ptr -[dist4]struct_c // num_bytes +[dist4]struct_f // num_bytes [u4]1 // num_fields -[dist8]array_ptr // array -[anchr]struct_c +[dist8]array_ptr // fixed_size_array +[anchr]struct_f [anchr]array_ptr [dist4]array_member // num_bytes diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data index 861e74ff1f3a6..bf1ddd5f5b1d3 100644 --- a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_too_few_array_elements.data @@ -8,22 +8,21 @@ [u4]2 // num_fields [dist8]param0_ptr // param0 [dist8]param1_ptr // param1 -[u8]0 // padding [anchr]method7_params [anchr]param0_ptr -[dist4]struct_c // num_bytes +[dist4]struct_f // num_bytes [u4]1 // num_fields -[dist8]array_ptr // array -[anchr]struct_c +[dist8]array_ptr // fixed_size_array +[anchr]struct_f [anchr]array_ptr [dist4]array_member // num_bytes -[u4]2 // num_elements +[u4]2 // num_elements: Too few elements. 0 1 [anchr]array_member -[u4]0 [u1]0 [u1]0 // padding for alignment of next array +[u4]0 [u1]0 [u1]0 // Padding for alignment of next array. [anchr]param1_ptr [dist4]array_param // num_bytes diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data new file mode 100644 index 0000000000000..ab6917508292c --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.data @@ -0,0 +1,23 @@ +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]7 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method7_params // num_bytes +[u4]2 // num_fields +[dist8]param0_ptr // param0 +[dist8]param1_ptr // param1 +[anchr]method7_params + +[anchr]param0_ptr +[dist4]struct_f // num_bytes +[u4]1 // num_fields +[u8]0 // fixed_size_array: An unexpected null pointer. +[anchr]struct_f + +[anchr]param1_ptr +[dist4]array_param // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]array_param diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected new file mode 100644 index 0000000000000..95d8db01bb51a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd7_unexpected_null_fixed_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data new file mode 100644 index 0000000000000..fd5e174aafa71 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.data @@ -0,0 +1,30 @@ +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]8 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]1 // num_fields +[dist8]param0_ptr // param0 +[anchr]method8_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]3 // num_elements +[u8]0 // A null pointer, which is okay. +[dist8]nested_array_ptr +[u8]0 // A null pointer, which is okay. +[anchr]array_param + +[anchr]nested_array_ptr +[dist4]nested_array // num_bytes +[u4]1 // num_elements +[dist8]string_ptr +[anchr]nested_array + +[anchr]string_ptr +[dist4]string // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good_null_struct.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected similarity index 100% rename from mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd1_good_null_struct.expected rename to mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_good.expected diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data new file mode 100644 index 0000000000000..f1d8718e65102 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.data @@ -0,0 +1,10 @@ +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]8 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]1 // num_fields +[u8]0 // param0: An unexpected null pointer. +[anchr]method8_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected new file mode 100644 index 0000000000000..95d8db01bb51a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data new file mode 100644 index 0000000000000..104dddb0bd680 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.data @@ -0,0 +1,31 @@ +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]8 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method8_params // num_bytes +[u4]1 // num_fields +[dist8]param0_ptr // param0 +[anchr]method8_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]3 // num_elements +[u8]0 // A null pointer, which is okay. +[dist8]nested_array_ptr +[u8]0 // A null pointer, which is okay. +[anchr]array_param + +[anchr]nested_array_ptr +[dist4]nested_array // num_bytes +[u4]2 // num_elements +[dist8]string_ptr +[u8]0 // An unexpected null pointer. +[anchr]nested_array + +[anchr]string_ptr +[dist4]string // num_bytes +[u4]5 // num_elements +0 1 2 3 4 +[anchr]string diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected new file mode 100644 index 0000000000000..95d8db01bb51a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd8_unexpected_null_string.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data new file mode 100644 index 0000000000000..c548906f0bef6 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.data @@ -0,0 +1,34 @@ +[handles]4 + +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]9 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method9_params // num_bytes +[u4]1 // num_fields +[dist8]param0_ptr // param0 +[anchr]method9_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[dist8]nested_array_ptr_0 +[dist8]nested_array_ptr_1 +[anchr]array_param + +[anchr]nested_array_ptr_0 +[dist4]nested_array_0 // num_bytes +[u4]2 // num_elements +[u4]0 +[s4]-1 // An invalid handle, which is okay. +[anchr]nested_array_0 + +[anchr]nested_array_ptr_1 +[dist4]nested_array_1 // num_bytes +[u4]3 // num_elements +[u4]2 +[s4]-1 // An invalid handle, which is okay. +[u4]3 +[anchr]nested_array_1 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected similarity index 100% rename from mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd3_good_null_array.expected rename to mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good.expected diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data new file mode 100644 index 0000000000000..f0967d5c97f47 --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.data @@ -0,0 +1,12 @@ +[handles]4 + +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]9 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method9_params // num_bytes +[u4]1 // num_fields +[u8]0 // param0: A null pointer, which is okay. +[anchr]method9_params diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good_invalid_handle.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected similarity index 100% rename from mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd5_good_invalid_handle.expected rename to mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_good_null_array.expected diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data new file mode 100644 index 0000000000000..9c8f478655a8f --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.data @@ -0,0 +1,25 @@ +[handles]4 + +[dist4]message_header // num_bytes +[u4]2 // num_fields +[u4]9 // name +[u4]0 // flags +[anchr]message_header + +[dist4]method9_params // num_bytes +[u4]1 // num_fields +[dist8]param0_ptr // param0 +[anchr]method9_params + +[anchr]param0_ptr +[dist4]array_param // num_bytes +[u4]2 // num_elements +[dist8]nested_array_ptr_0 +[u8]0 // An unexpected null pointer. +[anchr]array_param + +[anchr]nested_array_ptr_0 +[dist4]nested_array_0 // num_bytes +[u4]1 // num_elements +[u4]0 +[anchr]nested_array_0 diff --git a/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected new file mode 100644 index 0000000000000..95d8db01bb51a --- /dev/null +++ b/mojo/public/interfaces/bindings/tests/data/validation/conformance_mthd9_unexpected_null_array.expected @@ -0,0 +1 @@ +VALIDATION_ERROR_UNEXPECTED_NULL_POINTER diff --git a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom index 310d916acda95..366de6bf4e8f4 100644 --- a/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom +++ b/mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom @@ -40,6 +40,8 @@ interface ConformanceTestInterface { Method5(StructE param0, handle param1); Method6(uint8[][] param0); Method7(StructF param0, uint8[5] param1); + Method8(string[]?[] param0); + Method9(handle?[][]? param0); }; struct BasicStruct { diff --git a/mojo/public/js/bindings/codec.js b/mojo/public/js/bindings/codec.js index 9365dd2fba121..02a701ce2e265 100644 --- a/mojo/public/js/bindings/codec.js +++ b/mojo/public/js/bindings/codec.js @@ -135,22 +135,17 @@ define("mojo/public/js/bindings/codec", [ var numberOfBytes = this.readUint32(); var numberOfElements = this.readUint32(); var val = new Array(numberOfElements); - for (var i = 0; i < numberOfElements; ++i) { - val[i] = cls.decode(this); - } - return val; - }; - - Decoder.prototype.decodeBoolArray = function() { - var numberOfBytes = this.readUint32(); - var numberOfElements = this.readUint32(); - - var val = new Array(numberOfElements); - var byte; - for (var i = 0; i < numberOfElements; ++i) { + if (cls.cls === PackedBool) { + var byte; + for (var i = 0; i < numberOfElements; ++i) { if (i % 8 === 0) - byte = this.readUint8(); + byte = this.readUint8(); val[i] = (byte & (1 << i % 8)) ? true : false; + } + } else { + for (var i = 0; i < numberOfElements; ++i) { + val[i] = cls.decode(this); + } } return val; }; @@ -175,14 +170,6 @@ define("mojo/public/js/bindings/codec", [ return this.decodeAndCreateDecoder(pointer).decodeArray(cls); }; - Decoder.prototype.decodeBoolArrayPointer = function() { - var pointer = this.decodePointer(); - if (!pointer) { - return null; - } - return this.decodeAndCreateDecoder(pointer).decodeBoolArray(); - }; - Decoder.prototype.decodeStringPointer = function() { var pointer = this.decodePointer(); if (!pointer) { @@ -296,14 +283,29 @@ define("mojo/public/js/bindings/codec", [ this.next += numberOfElements; }; - Encoder.prototype.encodeArray = function(cls, val, numberOfElements) { + Encoder.prototype.encodeArray = + function(cls, val, numberOfElements, encodedSize) { if (numberOfElements === undefined) numberOfElements = val.length; - var numberOfBytes = kArrayHeaderSize + cls.encodedSize * val.length; - this.writeUint32(numberOfBytes); + if (encodedSize === undefined) + encodedSize = kArrayHeaderSize + cls.encodedSize * numberOfElements; + + this.writeUint32(encodedSize); this.writeUint32(numberOfElements); - for (var i = 0; i < val.length; ++i) { - cls.encode(this, val[i]); + + if (cls.cls === PackedBool) { + var byte = 0; + for (i = 0; i < numberOfElements; ++i) { + if (val[i]) + byte |= (1 << i % 8); + if (i % 8 === 7 || i == numberOfElements - 1) { + Uint8.encode(this, byte); + byte = 0; + } + } + } else { + for (var i = 0; i < numberOfElements; ++i) + cls.encode(this, val[i]); } }; @@ -321,30 +323,15 @@ define("mojo/public/js/bindings/codec", [ }; Encoder.prototype.encodeArrayPointer = function(cls, val) { - if (!val) { - this.encodePointer(val); - return; - } - var encodedSize = kArrayHeaderSize + cls.encodedSize * val.length; - var encoder = this.createAndEncodeEncoder(encodedSize); - encoder.encodeArray(cls, val); - }; - - Encoder.prototype.encodeBoolArrayPointer = function(val) { if (!val) { this.encodePointer(val); return; } var numberOfElements = val.length; - var encodedSize = kArrayHeaderSize + Math.ceil(numberOfElements / 8); + var encodedSize = kArrayHeaderSize + ((cls.cls === PackedBool) ? + Math.ceil(numberOfElements / 8) : cls.encodedSize * numberOfElements); var encoder = this.createAndEncodeEncoder(encodedSize); - - var bits = new Uint8Array(Math.ceil(numberOfElements / 8)); - for (var i = 0; i < numberOfElements; i++) { - if (val[i]) - bits[Math.floor(i / 8)] |= (1 << i % 8); - } - encoder.encodeArray(Uint8, bits, numberOfElements); + encoder.encodeArray(cls, val, numberOfElements, encodedSize); }; Encoder.prototype.encodeStringPointer = function(val) { @@ -475,6 +462,10 @@ define("mojo/public/js/bindings/codec", [ // Built-in types ----------------------------------------------------------- + // This type is only used with ArrayOf(PackedBool). + function PackedBool() { + } + function Int8() { } @@ -660,19 +651,6 @@ define("mojo/public/js/bindings/codec", [ encoder.encodeArrayPointer(this.cls, val); }; - function ArrayOfBoolArrayPointers() { - } - - ArrayOfBoolArrayPointers.prototype.encodedSize = 8; - - ArrayOfBoolArrayPointers.prototype.decode = function(decoder) { - return decoder.decodeBoolArrayPointer(); - }; - - ArrayOfBoolArrayPointers.prototype.encode = function(encoder, val) { - encoder.encodeBoolArrayPointer(val); - }; - function Handle() { } @@ -712,7 +690,7 @@ define("mojo/public/js/bindings/codec", [ exports.String = String; exports.PointerTo = PointerTo; exports.ArrayOf = ArrayOf; - exports.ArrayOfBoolArrayPointers = ArrayOfBoolArrayPointers; + exports.PackedBool = PackedBool; exports.Handle = Handle; return exports; }); diff --git a/mojo/public/js/bindings/validation_unittests.js b/mojo/public/js/bindings/validation_unittests.js index 88ae7a8446ff7..ae276fbc1d44b 100644 --- a/mojo/public/js/bindings/validation_unittests.js +++ b/mojo/public/js/bindings/validation_unittests.js @@ -5,11 +5,12 @@ define([ "file", "gin/test/expect", + "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom", "mojo/public/js/bindings/buffer", "mojo/public/js/bindings/codec", "mojo/public/js/bindings/tests/validation_test_input_parser", "mojo/public/js/bindings/validator", - ], function(file, expect, buffer, codec, parser, validator) { + ], function(file, expect, testInterface, buffer, codec, parser, validator) { function checkTestMessageParser() { function TestMessageParserFailure(message, input) { diff --git a/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc new file mode 100644 index 0000000000000..0a47f335f73fc --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.cc @@ -0,0 +1,32 @@ +// 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/public/platform/native/gles2_impl_chromium_sync_point_thunks.h" + +#include + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { +static MojoGLES2ImplChromiumSyncPointThunks g_impl_chromium_sync_point_thunks = + {0}; + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType gl##Function PARAMETERS { \ + assert(g_impl_chromium_sync_point_thunks.Function); \ + return g_impl_chromium_sync_point_thunks.Function ARGUMENTS; \ + } +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h" +#undef VISIT_GL_CALL + +extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumSyncPointThunks( + const MojoGLES2ImplChromiumSyncPointThunks* + gles2_impl_chromium_sync_point_thunks) { + if (gles2_impl_chromium_sync_point_thunks->size >= + sizeof(g_impl_chromium_sync_point_thunks)) + g_impl_chromium_sync_point_thunks = *gles2_impl_chromium_sync_point_thunks; + return sizeof(g_impl_chromium_sync_point_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h new file mode 100644 index 0000000000000..af6817e8c5305 --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h @@ -0,0 +1,44 @@ +// 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_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_ + +#include + +#include "mojo/public/c/gles2/chromium_sync_point.h" + +// Specifies the frozen API for the GLES2 CHROMIUM_sync_point extension. +#pragma pack(push, 8) +struct MojoGLES2ImplChromiumSyncPointThunks { + size_t size; // Should be set to sizeof(*this). + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType(*Function) PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h" +#undef VISIT_GL_CALL +}; +#pragma pack(pop) + +// Intended to be called from the embedder to get the embedder's implementation +// of GLES2. +inline MojoGLES2ImplChromiumSyncPointThunks +MojoMakeGLES2ImplChromiumSyncPointThunks() { + MojoGLES2ImplChromiumSyncPointThunks gles2_impl_chromium_sync_point_thunks = { + sizeof(MojoGLES2ImplChromiumSyncPointThunks), +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function, +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_sync_point_autogen.h" +#undef VISIT_GL_CALL + }; + + return gles2_impl_chromium_sync_point_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. +// The contents of |gles2_impl_chromium_sync_point_thunks| are copied. +typedef size_t (*MojoSetGLES2ImplChromiumSyncPointThunksFn)( + const MojoGLES2ImplChromiumSyncPointThunks* thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_SYNC_POINT_THUNKS_H_ diff --git a/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc new file mode 100644 index 0000000000000..db2bc819a3865 --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.cc @@ -0,0 +1,33 @@ +// 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/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h" + +#include + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { +static MojoGLES2ImplChromiumTextureMailboxThunks + g_impl_chromium_texture_mailbox_thunks = {0}; + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType gl##Function PARAMETERS { \ + assert(g_impl_chromium_texture_mailbox_thunks.Function); \ + return g_impl_chromium_texture_mailbox_thunks.Function ARGUMENTS; \ + } +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h" +#undef VISIT_GL_CALL + +extern "C" THUNK_EXPORT size_t MojoSetGLES2ImplChromiumTextureMailboxThunks( + const MojoGLES2ImplChromiumTextureMailboxThunks* + gles2_impl_chromium_texture_mailbox_thunks) { + if (gles2_impl_chromium_texture_mailbox_thunks->size >= + sizeof(g_impl_chromium_texture_mailbox_thunks)) + g_impl_chromium_texture_mailbox_thunks = + *gles2_impl_chromium_texture_mailbox_thunks; + return sizeof(g_impl_chromium_texture_mailbox_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h new file mode 100644 index 0000000000000..839e4419a04ad --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h @@ -0,0 +1,45 @@ +// 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_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_ + +#include + +#include "mojo/public/c/gles2/chromium_texture_mailbox.h" + +// Specifies the frozen API for the GLES2 CHROMIUM_texture_mailbox extension. +#pragma pack(push, 8) +struct MojoGLES2ImplChromiumTextureMailboxThunks { + size_t size; // Should be set to sizeof(*this). + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType(*Function) PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h" +#undef VISIT_GL_CALL +}; +#pragma pack(pop) + +// Intended to be called from the embedder to get the embedder's implementation +// of GLES2. +inline MojoGLES2ImplChromiumTextureMailboxThunks +MojoMakeGLES2ImplChromiumTextureMailboxThunks() { + MojoGLES2ImplChromiumTextureMailboxThunks + gles2_impl_chromium_texture_mailbox_thunks = { + sizeof(MojoGLES2ImplChromiumTextureMailboxThunks), +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function, +#include "mojo/public/c/gles2/gles2_call_visitor_chromium_texture_mailbox_autogen.h" +#undef VISIT_GL_CALL + }; + + return gles2_impl_chromium_texture_mailbox_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. +// The contents of |gles2_impl_chromium_texture_mailbox_thunks| are copied. +typedef size_t (*MojoSetGLES2ImplChromiumTextureMailboxThunksFn)( + const MojoGLES2ImplChromiumTextureMailboxThunks* thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_CHROMIUM_TEXTURE_MAILBOX_THUNKS_H_ diff --git a/mojo/public/platform/native/gles2_impl_thunks.cc b/mojo/public/platform/native/gles2_impl_thunks.cc new file mode 100644 index 0000000000000..66dbc396cab15 --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_thunks.cc @@ -0,0 +1,29 @@ +// 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/public/platform/native/gles2_impl_thunks.h" + +#include + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { +static MojoGLES2ImplThunks g_impl_thunks = {0}; + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType gl##Function PARAMETERS { \ + assert(g_impl_thunks.Function); \ + return g_impl_thunks.Function ARGUMENTS; \ + } +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL + +extern "C" THUNK_EXPORT size_t +MojoSetGLES2ImplThunks(const MojoGLES2ImplThunks* gles2_impl_thunks) { + if (gles2_impl_thunks->size >= sizeof(g_impl_thunks)) + g_impl_thunks = *gles2_impl_thunks; + return sizeof(g_impl_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_impl_thunks.h b/mojo/public/platform/native/gles2_impl_thunks.h new file mode 100644 index 0000000000000..04174faa909ca --- /dev/null +++ b/mojo/public/platform/native/gles2_impl_thunks.h @@ -0,0 +1,49 @@ +// 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_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_ + +#include + +#include "mojo/public/c/gles2/gles2.h" + +// Like MojoGLES2ControlThunks, but specifies the frozen GLES2 API. Separated +// out as MojoGLES2ControlThunks may be modified and added to, but this +// interface is frozen. +#pragma pack(push, 8) +struct MojoGLES2ImplThunks { + size_t size; // Should be set to sizeof(MojoGLES2ImplThunks). + +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) \ + ReturnType(*Function) PARAMETERS; +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL +}; +#pragma pack(pop) + +// Intended to be called from the embedder to get the embedder's implementation +// of GLES2. +inline MojoGLES2ImplThunks MojoMakeGLES2ImplThunks() { + MojoGLES2ImplThunks gles2_impl_thunks = { + sizeof(MojoGLES2ImplThunks), +#define VISIT_GL_CALL(Function, ReturnType, PARAMETERS, ARGUMENTS) gl##Function, +#include "mojo/public/c/gles2/gles2_call_visitor_autogen.h" +#undef VISIT_GL_CALL + }; + + return gles2_impl_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetGLES2ImplThunksFn mojo_set_gles2_impl_thunks_fn = +// reinterpret_cast( +// app_library.GetFunctionPointer("MojoSetGLES2ImplThunks")); +// The expected size of |gles2_impl_thunks| is returned. +// The contents of |gles2_impl_thunks| are copied. +typedef size_t (*MojoSetGLES2ImplThunksFn)( + const MojoGLES2ImplThunks* gles2_impl_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_IMPL_THUNKS_H_ diff --git a/mojo/public/platform/native/gles2_thunks.cc b/mojo/public/platform/native/gles2_thunks.cc new file mode 100644 index 0000000000000..365fac95da1b4 --- /dev/null +++ b/mojo/public/platform/native/gles2_thunks.cc @@ -0,0 +1,56 @@ +// 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/public/platform/native/gles2_thunks.h" + +#include + +#include "mojo/public/platform/native/thunk_export.h" + +extern "C" { + +static MojoGLES2ControlThunks g_control_thunks = {0}; + +MojoGLES2Context MojoGLES2CreateContext(MojoHandle handle, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter) { + assert(g_control_thunks.GLES2CreateContext); + return g_control_thunks.GLES2CreateContext( + handle, lost_callback, closure, async_waiter); +} + +void MojoGLES2DestroyContext(MojoGLES2Context context) { + assert(g_control_thunks.GLES2DestroyContext); + g_control_thunks.GLES2DestroyContext(context); +} + +void MojoGLES2MakeCurrent(MojoGLES2Context context) { + assert(g_control_thunks.GLES2MakeCurrent); + g_control_thunks.GLES2MakeCurrent(context); +} + +void MojoGLES2SwapBuffers() { + assert(g_control_thunks.GLES2SwapBuffers); + g_control_thunks.GLES2SwapBuffers(); +} + +void* MojoGLES2GetGLES2Interface(MojoGLES2Context context) { + assert(g_control_thunks.GLES2GetGLES2Interface); + return g_control_thunks.GLES2GetGLES2Interface(context); +} + +void* MojoGLES2GetContextSupport(MojoGLES2Context context) { + assert(g_control_thunks.GLES2GetContextSupport); + return g_control_thunks.GLES2GetContextSupport(context); +} + +extern "C" THUNK_EXPORT size_t MojoSetGLES2ControlThunks( + const MojoGLES2ControlThunks* gles2_control_thunks) { + if (gles2_control_thunks->size >= sizeof(g_control_thunks)) + g_control_thunks = *gles2_control_thunks; + return sizeof(g_control_thunks); +} + +} // extern "C" diff --git a/mojo/public/platform/native/gles2_thunks.h b/mojo/public/platform/native/gles2_thunks.h new file mode 100644 index 0000000000000..4718ab3752844 --- /dev/null +++ b/mojo/public/platform/native/gles2_thunks.h @@ -0,0 +1,62 @@ +// 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_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_ +#define MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_ + +#include + +#include "mojo/public/c/gles2/gles2.h" + +// Structure used to bind the interface which manipulates GLES2 surfaces to a +// DSO to theose of the embedder. +// +// This is the ABI between the embedder and the DSO. It can only have new +// functions added to the end. No other changes are supported. +#pragma pack(push, 8) +struct MojoGLES2ControlThunks { + size_t size; // Should be set to sizeof(MojoGLES2ControlThunks). + + MojoGLES2Context (*GLES2CreateContext)(MojoHandle handle, + MojoGLES2ContextLost lost_callback, + void* closure, + const MojoAsyncWaiter* async_waiter); + void (*GLES2DestroyContext)(MojoGLES2Context context); + void (*GLES2MakeCurrent)(MojoGLES2Context context); + void (*GLES2SwapBuffers)(); + + // TODO(piman): We shouldn't have to leak these 2 interfaces, especially in a + // type-unsafe way. + void* (*GLES2GetGLES2Interface)(MojoGLES2Context context); + void* (*GLES2GetContextSupport)(MojoGLES2Context context); +}; +#pragma pack(pop) + +// Intended to be called from the embedder. Returns an object initialized to +// contain pointers to each of the embedder's MojoGLES2ControlThunks functions. +inline MojoGLES2ControlThunks MojoMakeGLES2ControlThunks() { + MojoGLES2ControlThunks gles2_control_thunks = { + sizeof(MojoGLES2ControlThunks), + MojoGLES2CreateContext, + MojoGLES2DestroyContext, + MojoGLES2MakeCurrent, + MojoGLES2SwapBuffers, + MojoGLES2GetGLES2Interface, + MojoGLES2GetContextSupport + }; + + return gles2_control_thunks; +} + +// Use this type for the function found by dynamically discovering it in +// a DSO linked with mojo_system. For example: +// MojoSetGLES2ControlThunksFn mojo_set_gles2_control_thunks_fn = +// reinterpret_cast( +// app_library.GetFunctionPointer("MojoSetGLES2ControlThunks")); +// The expected size of |gles2_control_thunks| is returned. +// The contents of |gles2_control_thunks| are copied. +typedef size_t (*MojoSetGLES2ControlThunksFn)( + const MojoGLES2ControlThunks* gles2_control_thunks); + +#endif // MOJO_PUBLIC_PLATFORM_NATIVE_GLES2_THUNKS_H_ diff --git a/mojo/public/platform/native/system_thunks.cc b/mojo/public/platform/native/system_thunks.cc index 6fdc4f4530ff0..fed6db9d9b981 100644 --- a/mojo/public/platform/native/system_thunks.cc +++ b/mojo/public/platform/native/system_thunks.cc @@ -6,6 +6,8 @@ #include +#include "mojo/public/platform/native/thunk_export.h" + extern "C" { static MojoSystemThunks g_thunks = {0}; @@ -152,14 +154,6 @@ MojoResult MojoUnmapBuffer(void* buffer) { return g_thunks.UnmapBuffer(buffer); } -// Call this function by looking -// Always export this api. -#if defined(WIN32) -#define THUNK_EXPORT __declspec(dllexport) -#else -#define THUNK_EXPORT __attribute__((visibility("default"))) -#endif - extern "C" THUNK_EXPORT size_t MojoSetSystemThunks( const MojoSystemThunks* system_thunks) { if (system_thunks->size >= sizeof(g_thunks)) diff --git a/mojo/public/platform/native/thunk_export.h b/mojo/public/platform/native/thunk_export.h new file mode 100644 index 0000000000000..d118fc5562845 --- /dev/null +++ b/mojo/public/platform/native/thunk_export.h @@ -0,0 +1,18 @@ +// 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_PUBLIC_PLATFORM_THUNK_EXPORT_H_ +#define MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_ + +// Call this function by looking inside the resulting shared object and +// grabbing the symbol manually. +// +// Always export this api. +#if defined(WIN32) +#define THUNK_EXPORT __declspec(dllexport) +#else +#define THUNK_EXPORT __attribute__((visibility("default"))) +#endif + +#endif // MOJO_PUBLIC_PLATFORM_THUNK_EXPORT_H_ diff --git a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 0164c2d491754..17f75b6f08a47 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -22,7 +22,7 @@ p{{loop.index}}.Pass() mojo::MakeProxy<{{param.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}}))) {%- elif param.kind|is_interface_request_kind -%} mojo::MakeRequest<{{param.kind.kind|get_name_for_kind}}>(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}}))) -{%- elif param.kind|is_handle_kind -%} +{%- elif param.kind|is_any_handle_kind -%} mojo::MakeScopedHandle(mojo::internal::FetchAndReset(¶ms->{{param.name}})) {%- elif param.kind|is_enum_kind -%} static_cast<{{param.kind|cpp_wrapper_type}}>(params->{{param.name}}) @@ -61,7 +61,7 @@ params->{{param.name}} {%- elif param.kind|is_interface_request_kind %} // Delegate handle. params->{{param.name}} = in_{{param.name}}.PassMessagePipe().release(); -{%- elif param.kind|is_handle_kind %} +{%- elif param.kind|is_any_handle_kind %} params->{{param.name}} = in_{{param.name}}.release(); {%- else %} params->{{param.name}} = in_{{param.name}}; diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl index f55b137bd728d..12e2dbba45b51 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_macros.tmpl @@ -13,22 +13,42 @@ {%- for packed_field in struct.packed.packed_fields %} {%- set name = packed_field.field.name %} -{%- if packed_field.field.kind|is_object_kind %} -{%- set wrapper_type = packed_field.field.kind|cpp_wrapper_type %} +{%- set kind = packed_field.field.kind %} +{%- if kind|is_object_kind %} +{%- set wrapper_type = kind|cpp_wrapper_type %} +{%- if not kind|is_nullable_kind %} + if (mojo::internal::IsNonNullableValidationEnabled() && + !object->{{name}}.offset) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_NULL_POINTER); + return false; + } +{%- endif %} if (!mojo::internal::ValidateEncodedPointer(&object->{{name}}.offset)) { ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_POINTER); return false; } +{%- if kind|is_any_array_kind or kind|is_string_kind %} + if (!{{wrapper_type}}::Data_::Validate< + {{kind|get_array_validate_params|indent(10)}}>( + mojo::internal::DecodePointerRaw(&object->{{name}}.offset), + bounds_checker)) { +{%- else %} if (!{{wrapper_type}}::Data_::Validate( mojo::internal::DecodePointerRaw(&object->{{name}}.offset), - bounds_checker -{%- if packed_field.field.kind|is_array_kind -%} - , {{packed_field.field.kind|expected_array_size}} -{%- endif -%} - )) { + bounds_checker)) { +{%- endif %} return false; } -{%- elif packed_field.field.kind|is_handle_kind %} +{%- elif kind|is_any_handle_kind %} +{%- if not kind|is_nullable_kind %} + if (mojo::internal::IsNonNullableValidationEnabled() && + object->{{name}}.value() == mojo::internal::kEncodedInvalidHandleValue) { + ReportValidationError( + mojo::internal::VALIDATION_ERROR_UNEXPECTED_INVALID_HANDLE); + return false; + } +{%- endif %} if (!bounds_checker->ClaimHandle(object->{{name}})) { ReportValidationError(mojo::internal::VALIDATION_ERROR_ILLEGAL_HANDLE); return false; @@ -78,7 +98,7 @@ {%- for pf in struct.packed.packed_fields %} {%- if pf.field.kind|is_object_kind %} mojo::internal::Encode(&{{pf.field.name}}, handles); -{%- elif pf.field.kind|is_handle_kind %} +{%- elif pf.field.kind|is_any_handle_kind %} mojo::internal::EncodeHandle(&{{pf.field.name}}, handles); {%- endif %} {%- endfor %} @@ -88,7 +108,7 @@ mojo::internal::EncodeHandle(&{{pf.field.name}}, handles); {%- for pf in struct.packed.packed_fields %} {%- if pf.field.kind|is_object_kind %} mojo::internal::Decode(&{{pf.field.name}}, handles); -{%- elif pf.field.kind|is_handle_kind %} +{%- elif pf.field.kind|is_any_handle_kind %} mojo::internal::DecodeHandle(&{{pf.field.name}}, handles); {%- endif %} {%- endfor %} diff --git a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl index 5d8b944fb1a5a..f48cca72fb457 100644 --- a/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl +++ b/mojo/public/tools/bindings/generators/cpp_templates/struct_serialization_definition.tmpl @@ -18,7 +18,7 @@ void Serialize_({{struct.name}}Ptr input, mojo::internal::Buffer* buf, Serialize_(mojo::internal::Forward(input->{{pf.field.name}}), buf, &result->{{pf.field.name}}.ptr); {%- elif pf.field.kind|is_interface_kind %} result->{{pf.field.name}} = input->{{pf.field.name}}.PassMessagePipe().release(); -{%- elif pf.field.kind|is_handle_kind %} +{%- elif pf.field.kind|is_any_handle_kind %} result->{{pf.field.name}} = input->{{pf.field.name}}.release(); {%- else %} result->{{pf.field.name}} = input->{{pf.field.name}}; @@ -40,7 +40,7 @@ void Deserialize_(internal::{{struct.name}}_Data* input, {%- elif pf.field.kind|is_interface_kind %} if (input->{{pf.field.name}}.is_valid()) result->{{pf.field.name}}.Bind(mojo::MakeScopedHandle(mojo::internal::FetchAndReset(&input->{{pf.field.name}}))); -{%- elif pf.field.kind|is_handle_kind %} +{%- elif pf.field.kind|is_any_handle_kind %} result->{{pf.field.name}}.reset(mojo::internal::FetchAndReset(&input->{{pf.field.name}})); {%- elif pf.field.kind|is_enum_kind %} result->{{pf.field.name}} = static_cast<{{pf.field.kind|cpp_wrapper_type}}>( diff --git a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index 98f56b6074327..27c1b2bf5f871 100644 --- a/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -11,22 +11,27 @@ _kind_to_cpp_type = { - mojom.BOOL: "bool", - mojom.INT8: "int8_t", - mojom.UINT8: "uint8_t", - mojom.INT16: "int16_t", - mojom.UINT16: "uint16_t", - mojom.INT32: "int32_t", - mojom.UINT32: "uint32_t", - mojom.FLOAT: "float", - mojom.HANDLE: "mojo::Handle", - mojom.DCPIPE: "mojo::DataPipeConsumerHandle", - mojom.DPPIPE: "mojo::DataPipeProducerHandle", - mojom.MSGPIPE: "mojo::MessagePipeHandle", - mojom.SHAREDBUFFER: "mojo::SharedBufferHandle", - mojom.INT64: "int64_t", - mojom.UINT64: "uint64_t", - mojom.DOUBLE: "double", + mojom.BOOL: "bool", + mojom.INT8: "int8_t", + mojom.UINT8: "uint8_t", + mojom.INT16: "int16_t", + mojom.UINT16: "uint16_t", + mojom.INT32: "int32_t", + mojom.UINT32: "uint32_t", + mojom.FLOAT: "float", + mojom.HANDLE: "mojo::Handle", + mojom.DCPIPE: "mojo::DataPipeConsumerHandle", + mojom.DPPIPE: "mojo::DataPipeProducerHandle", + mojom.MSGPIPE: "mojo::MessagePipeHandle", + mojom.SHAREDBUFFER: "mojo::SharedBufferHandle", + mojom.NULLABLE_HANDLE: "mojo::Handle", + mojom.NULLABLE_DCPIPE: "mojo::DataPipeConsumerHandle", + mojom.NULLABLE_DPPIPE: "mojo::DataPipeProducerHandle", + mojom.NULLABLE_MSGPIPE: "mojo::MessagePipeHandle", + mojom.NULLABLE_SHAREDBUFFER: "mojo::SharedBufferHandle", + mojom.INT64: "int64_t", + mojom.UINT64: "uint64_t", + mojom.DOUBLE: "double", } _kind_to_cpp_literal_suffix = { @@ -42,7 +47,7 @@ def ConstantValue(constant): def DefaultValue(field): if field.default: - if isinstance(field.kind, mojom.Struct): + if mojom.IsStructKind(field.kind): assert field.default == "default" return "%s::New()" % GetNameForKind(field.kind) return ExpressionToText(field.default, kind=field.kind) @@ -63,149 +68,147 @@ def GetNameForKind(kind, internal = False): return "::".join(parts) def GetCppType(kind): - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "%s_Data*" % GetNameForKind(kind, internal=True) - if isinstance(kind, (mojom.Array, mojom.FixedArray)): + if mojom.IsAnyArrayKind(kind): return "mojo::internal::Array_Data<%s>*" % GetCppType(kind.kind) - if isinstance(kind, mojom.Interface) or \ - isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): return "mojo::MessagePipeHandle" - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return "int32_t" - if kind.spec == 's': + if mojom.IsStringKind(kind): return "mojo::internal::String_Data*" return _kind_to_cpp_type[kind] def GetCppPodType(kind): - if kind.spec == 's': + if mojom.IsStringKind(kind): return "char*" return _kind_to_cpp_type[kind] def GetCppArrayArgWrapperType(kind): - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, (mojom.Array, mojom.FixedArray)): + if mojom.IsAnyArrayKind(kind): return "mojo::Array<%s> " % GetCppArrayArgWrapperType(kind.kind) - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): raise Exception("Arrays of interfaces not yet supported!") - if isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(kind): raise Exception("Arrays of interface requests not yet supported!") - if kind.spec == 's': + if mojom.IsStringKind(kind): return "mojo::String" - if kind.spec == 'h': + if mojom.IsHandleKind(kind): return "mojo::ScopedHandle" - if kind.spec == 'h:d:c': + if mojom.IsDataPipeConsumerKind(kind): return "mojo::ScopedDataPipeConsumerHandle" - if kind.spec == 'h:d:p': + if mojom.IsDataPipeProducerKind(kind): return "mojo::ScopedDataPipeProducerHandle" - if kind.spec == 'h:m': + if mojom.IsMessagePipeKind(kind): return "mojo::ScopedMessagePipeHandle" - if kind.spec == 'h:s': + if mojom.IsSharedBufferKind(kind): return "mojo::ScopedSharedBufferHandle" return _kind_to_cpp_type[kind] def GetCppResultWrapperType(kind): - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, (mojom.Array, mojom.FixedArray)): + if mojom.IsAnyArrayKind(kind): return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(kind): return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind) - if kind.spec == 's': + if mojom.IsStringKind(kind): return "mojo::String" - if kind.spec == 'h': + if mojom.IsHandleKind(kind): return "mojo::ScopedHandle" - if kind.spec == 'h:d:c': + if mojom.IsDataPipeConsumerKind(kind): return "mojo::ScopedDataPipeConsumerHandle" - if kind.spec == 'h:d:p': + if mojom.IsDataPipeProducerKind(kind): return "mojo::ScopedDataPipeProducerHandle" - if kind.spec == 'h:m': + if mojom.IsMessagePipeKind(kind): return "mojo::ScopedMessagePipeHandle" - if kind.spec == 'h:s': + if mojom.IsSharedBufferKind(kind): return "mojo::ScopedSharedBufferHandle" return _kind_to_cpp_type[kind] def GetCppWrapperType(kind): - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, (mojom.Array, mojom.FixedArray)): + if mojom.IsAnyArrayKind(kind): return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(kind): raise Exception("InterfaceRequest fields not supported!") - if kind.spec == 's': + if mojom.IsStringKind(kind): return "mojo::String" - if kind.spec == 'h': + if mojom.IsHandleKind(kind): return "mojo::ScopedHandle" - if kind.spec == 'h:d:c': + if mojom.IsDataPipeConsumerKind(kind): return "mojo::ScopedDataPipeConsumerHandle" - if kind.spec == 'h:d:p': + if mojom.IsDataPipeProducerKind(kind): return "mojo::ScopedDataPipeProducerHandle" - if kind.spec == 'h:m': + if mojom.IsMessagePipeKind(kind): return "mojo::ScopedMessagePipeHandle" - if kind.spec == 'h:s': + if mojom.IsSharedBufferKind(kind): return "mojo::ScopedSharedBufferHandle" return _kind_to_cpp_type[kind] def GetCppConstWrapperType(kind): - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, (mojom.Array, mojom.FixedArray)): + if mojom.IsAnyArrayKind(kind): return "mojo::Array<%s>" % GetCppArrayArgWrapperType(kind.kind) - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): return "%sPtr" % GetNameForKind(kind) - if isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(kind): return "mojo::InterfaceRequest<%s>" % GetNameForKind(kind.kind) - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if kind.spec == 's': + if mojom.IsStringKind(kind): return "const mojo::String&" - if kind.spec == 'h': + if mojom.IsHandleKind(kind): return "mojo::ScopedHandle" - if kind.spec == 'h:d:c': + if mojom.IsDataPipeConsumerKind(kind): return "mojo::ScopedDataPipeConsumerHandle" - if kind.spec == 'h:d:p': + if mojom.IsDataPipeProducerKind(kind): return "mojo::ScopedDataPipeProducerHandle" - if kind.spec == 'h:m': + if mojom.IsMessagePipeKind(kind): return "mojo::ScopedMessagePipeHandle" - if kind.spec == 'h:s': + if mojom.IsSharedBufferKind(kind): return "mojo::ScopedSharedBufferHandle" if not kind in _kind_to_cpp_type: print "missing:", kind.spec return _kind_to_cpp_type[kind] def GetCppFieldType(kind): - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return ("mojo::internal::StructPointer<%s_Data>" % GetNameForKind(kind, internal=True)) - if isinstance(kind, (mojom.Array, mojom.FixedArray)): + if mojom.IsAnyArrayKind(kind): return "mojo::internal::ArrayPointer<%s>" % GetCppType(kind.kind) - if isinstance(kind, mojom.Interface) or \ - isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): return "mojo::MessagePipeHandle" - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return GetNameForKind(kind) - if kind.spec == 's': + if mojom.IsStringKind(kind): return "mojo::internal::StringPointer" return _kind_to_cpp_type[kind] def IsStructWithHandles(struct): for pf in struct.packed.packed_fields: - if generator.IsHandleKind(pf.field.kind): + if mojom.IsAnyHandleKind(pf.field.kind): return True return False def TranslateConstants(token, kind): - if isinstance(token, (mojom.NamedValue, mojom.EnumValue)): + if isinstance(token, mojom.NamedValue): # Both variable and enum constants are constructed like: # Namespace::Struct::CONSTANT_NAME # For enums, CONSTANT_NAME is ENUM_NAME_ENUM_VALUE. @@ -236,10 +239,28 @@ def ShouldInlineStruct(struct): if len(struct.fields) > 4: return False for field in struct.fields: - if generator.IsHandleKind(field.kind) or generator.IsObjectKind(field.kind): + if mojom.IsMoveOnlyKind(field.kind): return False return True +def GetArrayValidateParams(kind): + if not mojom.IsAnyArrayKind(kind) and not mojom.IsStringKind(kind): + return "mojo::internal::NoValidateParams" + + if mojom.IsStringKind(kind): + expected_num_elements = 0 + element_nullable = False + element_validate_params = "mojo::internal::NoValidateParams" + else: + expected_num_elements = generator.ExpectedArraySize(kind) + element_nullable = mojom.IsNullableKind(kind.kind) + element_validate_params = GetArrayValidateParams(kind.kind) + + return "mojo::internal::ArrayValidateParams<%d, %s,\n%s> " % ( + expected_num_elements, + 'true' if element_nullable else 'false', + element_validate_params) + _HEADER_SIZE = 8 class Generator(generator.Generator): @@ -255,18 +276,20 @@ class Generator(generator.Generator): "default_value": DefaultValue, "expected_array_size": generator.ExpectedArraySize, "expression_to_text": ExpressionToText, + "get_array_validate_params": GetArrayValidateParams, "get_name_for_kind": GetNameForKind, "get_pad": pack.GetPad, "has_callbacks": HasCallbacks, "should_inline": ShouldInlineStruct, - "is_array_kind": generator.IsArrayKind, - "is_enum_kind": generator.IsEnumKind, - "is_move_only_kind": generator.IsMoveOnlyKind, - "is_handle_kind": generator.IsHandleKind, - "is_interface_kind": generator.IsInterfaceKind, - "is_interface_request_kind": generator.IsInterfaceRequestKind, - "is_object_kind": generator.IsObjectKind, - "is_string_kind": generator.IsStringKind, + "is_any_array_kind": mojom.IsAnyArrayKind, + "is_enum_kind": mojom.IsEnumKind, + "is_move_only_kind": mojom.IsMoveOnlyKind, + "is_any_handle_kind": mojom.IsAnyHandleKind, + "is_interface_kind": mojom.IsInterfaceKind, + "is_interface_request_kind": mojom.IsInterfaceRequestKind, + "is_nullable_kind": mojom.IsNullableKind, + "is_object_kind": mojom.IsObjectKind, + "is_string_kind": mojom.IsStringKind, "is_struct_with_handles": IsStructWithHandles, "struct_size": lambda ps: ps.GetTotalSize() + _HEADER_SIZE, "struct_from_method": generator.GetStructFromMethod, diff --git a/mojo/public/tools/bindings/generators/mojom_java_generator.py b/mojo/public/tools/bindings/generators/mojom_java_generator.py index ee121e75cc5d6..829582115b84c 100644 --- a/mojo/public/tools/bindings/generators/mojom_java_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_java_generator.py @@ -21,43 +21,58 @@ _HEADER_SIZE = 8 _spec_to_java_type = { - 'b': 'boolean', - 'd': 'double', - 'f': 'float', - 'h:d:c': 'org.chromium.mojo.system.DataPipe.ConsumerHandle', - 'h:d:p': 'org.chromium.mojo.system.DataPipe.ProducerHandle', - 'h:m': 'org.chromium.mojo.system.MessagePipeHandle', - 'h': 'org.chromium.mojo.system.UntypedHandle', - 'h:s': 'org.chromium.mojo.system.SharedBufferHandle', - 'i16': 'short', - 'i32': 'int', - 'i64': 'long', - 'i8': 'byte', - 's': 'String', - 'u16': 'short', - 'u32': 'int', - 'u64': 'long', - 'u8': 'byte', + mojom.BOOL.spec: 'boolean', + mojom.DCPIPE.spec: 'org.chromium.mojo.system.DataPipe.ConsumerHandle', + mojom.DOUBLE.spec: 'double', + mojom.DPPIPE.spec: 'org.chromium.mojo.system.DataPipe.ProducerHandle', + mojom.FLOAT.spec: 'float', + mojom.HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle', + mojom.INT16.spec: 'short', + mojom.INT32.spec: 'int', + mojom.INT64.spec: 'long', + mojom.INT8.spec: 'byte', + mojom.MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle', + mojom.NULLABLE_DCPIPE.spec: + 'org.chromium.mojo.system.DataPipe.ConsumerHandle', + mojom.NULLABLE_DPPIPE.spec: + 'org.chromium.mojo.system.DataPipe.ProducerHandle', + mojom.NULLABLE_HANDLE.spec: 'org.chromium.mojo.system.UntypedHandle', + mojom.NULLABLE_MSGPIPE.spec: 'org.chromium.mojo.system.MessagePipeHandle', + mojom.NULLABLE_SHAREDBUFFER.spec: + 'org.chromium.mojo.system.SharedBufferHandle', + mojom.NULLABLE_STRING.spec: 'String', + mojom.SHAREDBUFFER.spec: 'org.chromium.mojo.system.SharedBufferHandle', + mojom.STRING.spec: 'String', + mojom.UINT16.spec: 'short', + mojom.UINT32.spec: 'int', + mojom.UINT64.spec: 'long', + mojom.UINT8.spec: 'byte', } _spec_to_decode_method = { - 'b': 'readBoolean', - 'd': 'readDouble', - 'f': 'readFloat', - 'h:d:c': 'readConsumerHandle', - 'h:d:p': 'readProducerHandle', - 'h:m': 'readMessagePipeHandle', - 'h': 'readUntypedHandle', - 'h:s': 'readSharedBufferHandle', - 'i16': 'readShort', - 'i32': 'readInt', - 'i64': 'readLong', - 'i8': 'readByte', - 's': 'readString', - 'u16': 'readShort', - 'u32': 'readInt', - 'u64': 'readLong', - 'u8': 'readByte', + mojom.BOOL.spec: 'readBoolean', + mojom.DCPIPE.spec: 'readConsumerHandle', + mojom.DOUBLE.spec: 'readDouble', + mojom.DPPIPE.spec: 'readProducerHandle', + mojom.FLOAT.spec: 'readFloat', + mojom.HANDLE.spec: 'readUntypedHandle', + mojom.INT16.spec: 'readShort', + mojom.INT32.spec: 'readInt', + mojom.INT64.spec: 'readLong', + mojom.INT8.spec: 'readByte', + mojom.MSGPIPE.spec: 'readMessagePipeHandle', + mojom.NULLABLE_DCPIPE.spec: 'readConsumerHandle', + mojom.NULLABLE_DPPIPE.spec: 'readProducerHandle', + mojom.NULLABLE_HANDLE.spec: 'readUntypedHandle', + mojom.NULLABLE_MSGPIPE.spec: 'readMessagePipeHandle', + mojom.NULLABLE_SHAREDBUFFER.spec: 'readSharedBufferHandle', + mojom.NULLABLE_STRING.spec: 'readString', + mojom.SHAREDBUFFER.spec: 'readSharedBufferHandle', + mojom.STRING.spec: 'readString', + mojom.UINT16.spec: 'readShort', + mojom.UINT32.spec: 'readInt', + mojom.UINT64.spec: 'readLong', + mojom.UINT8.spec: 'readByte', } _java_primitive_to_boxed_type = { @@ -97,11 +112,10 @@ def ConstantStyle(name): return '_'.join([x.upper() for x in components]) def GetNameForElement(element): - if isinstance(element, (mojom.Enum, - mojom.Interface, - mojom.Struct)): + if (mojom.IsEnumKind(element) or mojom.IsInterfaceKind(element) or + mojom.IsStructKind(element)): return UpperCamelCase(element.name) - if isinstance(element, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(element): return GetNameForElement(element.kind) if isinstance(element, (mojom.Method, mojom.Parameter, @@ -122,28 +136,25 @@ def ParseStringAttribute(attribute): assert isinstance(attribute, basestring) return attribute -def IsArray(kind): - return isinstance(kind, (mojom.Array, mojom.FixedArray)) - @contextfilter def DecodeMethod(context, kind, offset, bit): def _DecodeMethodName(kind): - if IsArray(kind): + if mojom.IsAnyArrayKind(kind): return _DecodeMethodName(kind.kind) + 's' - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return _DecodeMethodName(mojom.INT32) - if isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(kind): return "readInterfaceRequest" - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): return "readServiceInterface" return _spec_to_decode_method[kind.spec] methodName = _DecodeMethodName(kind) additionalParams = '' if (kind == mojom.BOOL): additionalParams = ', %d' % bit - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): additionalParams = ', %s.BUILDER' % GetJavaType(context, kind) - if IsArray(kind) and isinstance(kind.kind, mojom.Interface): + if mojom.IsAnyArrayKind(kind) and mojom.IsInterfaceKind(kind.kind): additionalParams = ', %s.BUILDER' % GetJavaType(context, kind.kind) return '%s(%s%s)' % (methodName, offset, additionalParams) @@ -152,9 +163,9 @@ def EncodeMethod(context, kind, variable, offset, bit): additionalParams = '' if (kind == mojom.BOOL): additionalParams = ', %d' % bit - if isinstance(kind, mojom.Interface): + if mojom.IsInterfaceKind(kind): additionalParams = ', %s.BUILDER' % GetJavaType(context, kind) - if IsArray(kind) and isinstance(kind.kind, mojom.Interface): + if mojom.IsAnyArrayKind(kind) and mojom.IsInterfaceKind(kind.kind): additionalParams = ', %s.BUILDER' % GetJavaType(context, kind.kind) return 'encode(%s, %s%s)' % (variable, offset, additionalParams) @@ -189,20 +200,17 @@ def GetBoxedJavaType(context, kind): def GetJavaType(context, kind, boxed=False): if boxed: return GetBoxedJavaType(context, kind) - if isinstance(kind, (mojom.Struct, mojom.Interface)): + if mojom.IsStructKind(kind) or mojom.IsInterfaceKind(kind): return GetNameForKind(context, kind) - if isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceRequestKind(kind): return ("org.chromium.mojo.bindings.InterfaceRequest<%s>" % GetNameForKind(context, kind.kind)) - if IsArray(kind): + if mojom.IsAnyArrayKind(kind): return "%s[]" % GetJavaType(context, kind.kind) - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return "int" return _spec_to_java_type[kind.spec] -def IsHandle(kind): - return kind.spec[0] == 'h' - @contextfilter def DefaultValue(context, field): assert field.default @@ -221,7 +229,7 @@ def ConstantValue(context, constant): @contextfilter def NewArray(context, kind, size): - if IsArray(kind.kind): + if mojom.IsAnyArrayKind(kind.kind): return NewArray(context, kind.kind, size) + '[]' return 'new %s[%s]' % (GetJavaType(context, kind.kind), size) @@ -255,10 +263,10 @@ def _TranslateNamedValue(named_value): return token def IsPointerArrayKind(kind): - if not IsArray(kind): + if not mojom.IsAnyArrayKind(kind): return False sub_kind = kind.kind - return generator.IsObjectKind(sub_kind) + return mojom.IsObjectKind(sub_kind) def GetConstantsMainEntityName(module): if 'JavaConstantsClassName' in module.attributes: @@ -277,9 +285,9 @@ class Generator(generator.Generator): "decode_method": DecodeMethod, "expression_to_text": ExpressionToText, "encode_method": EncodeMethod, - "is_handle": IsHandle, + "is_handle": mojom.IsNonInterfaceHandleKind, "is_pointer_array_kind": IsPointerArrayKind, - "is_struct_kind": lambda kind: isinstance(kind, mojom.Struct), + "is_struct_kind": mojom.IsStructKind, "java_type": GetJavaType, "name": GetNameForElement, "new_array": NewArray, diff --git a/mojo/public/tools/bindings/generators/mojom_js_generator.py b/mojo/public/tools/bindings/generators/mojom_js_generator.py index 59208328e54b4..5b83974d97943 100644 --- a/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -10,42 +10,48 @@ from mojom.generate.template_expander import UseJinja _kind_to_javascript_default_value = { - mojom.BOOL: "false", - mojom.INT8: "0", - mojom.UINT8: "0", - mojom.INT16: "0", - mojom.UINT16: "0", - mojom.INT32: "0", - mojom.UINT32: "0", - mojom.FLOAT: "0", - mojom.HANDLE: "null", - mojom.DCPIPE: "null", - mojom.DPPIPE: "null", - mojom.MSGPIPE: "null", - mojom.SHAREDBUFFER: "null", - mojom.INT64: "0", - mojom.UINT64: "0", - mojom.DOUBLE: "0", - mojom.STRING: '""', + mojom.BOOL: "false", + mojom.INT8: "0", + mojom.UINT8: "0", + mojom.INT16: "0", + mojom.UINT16: "0", + mojom.INT32: "0", + mojom.UINT32: "0", + mojom.FLOAT: "0", + mojom.HANDLE: "null", + mojom.DCPIPE: "null", + mojom.DPPIPE: "null", + mojom.MSGPIPE: "null", + mojom.SHAREDBUFFER: "null", + mojom.NULLABLE_HANDLE: "null", + mojom.NULLABLE_DCPIPE: "null", + mojom.NULLABLE_DPPIPE: "null", + mojom.NULLABLE_MSGPIPE: "null", + mojom.NULLABLE_SHAREDBUFFER: "null", + mojom.INT64: "0", + mojom.UINT64: "0", + mojom.DOUBLE: "0", + mojom.STRING: '""', + mojom.NULLABLE_STRING: '""' } def JavaScriptDefaultValue(field): if field.default: - if isinstance(field.kind, mojom.Struct): + if mojom.IsStructKind(field.kind): assert field.default == "default" return "new %s()" % JavascriptType(field.kind) return ExpressionToText(field.default) if field.kind in mojom.PRIMITIVES: return _kind_to_javascript_default_value[field.kind] - if isinstance(field.kind, mojom.Struct): + if mojom.IsStructKind(field.kind): return "null" - if isinstance(field.kind, mojom.Array): + if mojom.IsArrayKind(field.kind): return "null" - if isinstance(field.kind, mojom.Interface) or \ - isinstance(field.kind, mojom.InterfaceRequest): + if mojom.IsInterfaceKind(field.kind) or \ + mojom.IsInterfaceRequestKind(field.kind): return _kind_to_javascript_default_value[mojom.MSGPIPE] - if isinstance(field.kind, mojom.Enum): + if mojom.IsEnumKind(field.kind): return "0" @@ -60,39 +66,44 @@ def JavaScriptPayloadSize(packed): _kind_to_codec_type = { - mojom.BOOL: "codec.Uint8", - mojom.INT8: "codec.Int8", - mojom.UINT8: "codec.Uint8", - mojom.INT16: "codec.Int16", - mojom.UINT16: "codec.Uint16", - mojom.INT32: "codec.Int32", - mojom.UINT32: "codec.Uint32", - mojom.FLOAT: "codec.Float", - mojom.HANDLE: "codec.Handle", - mojom.DCPIPE: "codec.Handle", - mojom.DPPIPE: "codec.Handle", - mojom.MSGPIPE: "codec.Handle", - mojom.SHAREDBUFFER: "codec.Handle", - mojom.INT64: "codec.Int64", - mojom.UINT64: "codec.Uint64", - mojom.DOUBLE: "codec.Double", - mojom.STRING: "codec.String", + mojom.BOOL: "codec.Uint8", + mojom.INT8: "codec.Int8", + mojom.UINT8: "codec.Uint8", + mojom.INT16: "codec.Int16", + mojom.UINT16: "codec.Uint16", + mojom.INT32: "codec.Int32", + mojom.UINT32: "codec.Uint32", + mojom.FLOAT: "codec.Float", + mojom.HANDLE: "codec.Handle", + mojom.DCPIPE: "codec.Handle", + mojom.DPPIPE: "codec.Handle", + mojom.MSGPIPE: "codec.Handle", + mojom.SHAREDBUFFER: "codec.Handle", + mojom.NULLABLE_HANDLE: "codec.Handle", + mojom.NULLABLE_DCPIPE: "codec.Handle", + mojom.NULLABLE_DPPIPE: "codec.Handle", + mojom.NULLABLE_MSGPIPE: "codec.Handle", + mojom.NULLABLE_SHAREDBUFFER: "codec.Handle", + mojom.INT64: "codec.Int64", + mojom.UINT64: "codec.Uint64", + mojom.DOUBLE: "codec.Double", + mojom.STRING: "codec.String", + mojom.NULLABLE_STRING: "codec.String", } def CodecType(kind): if kind in mojom.PRIMITIVES: return _kind_to_codec_type[kind] - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "new codec.PointerTo(%s)" % CodecType(kind.name) - if isinstance(kind, mojom.Array) and kind.kind == mojom.BOOL: - return "new codec.ArrayOfBoolArrayPointers()" - if isinstance(kind, mojom.Array): + if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): + return "new codec.ArrayOf(new codec.ArrayOf(codec.PackedBool))" + if mojom.IsArrayKind(kind): return "new codec.ArrayOf(%s)" % CodecType(kind.kind) - if isinstance(kind, mojom.Interface) or \ - isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): return CodecType(mojom.MSGPIPE) - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return _kind_to_codec_type[mojom.INT32] return kind @@ -100,32 +111,30 @@ def CodecType(kind): def JavaScriptDecodeSnippet(kind): if kind in mojom.PRIMITIVES: return "decodeStruct(%s)" % CodecType(kind) - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "decodeStructPointer(%s)" % CodecType(kind.name) - if isinstance(kind, mojom.Array) and kind.kind == mojom.BOOL: - return "decodeBoolArrayPointer()" - if isinstance(kind, mojom.Array): + if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): + return "decodeArrayPointer(new codec.ArrayOf(codec.PackedBool))" + if mojom.IsArrayKind(kind): return "decodeArrayPointer(%s)" % CodecType(kind.kind) - if isinstance(kind, mojom.Interface) or \ - isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): return JavaScriptDecodeSnippet(mojom.MSGPIPE) - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return JavaScriptDecodeSnippet(mojom.INT32) def JavaScriptEncodeSnippet(kind): if kind in mojom.PRIMITIVES: return "encodeStruct(%s, " % CodecType(kind) - if isinstance(kind, mojom.Struct): + if mojom.IsStructKind(kind): return "encodeStructPointer(%s, " % CodecType(kind.name) - if isinstance(kind, mojom.Array) and kind.kind == mojom.BOOL: - return "encodeBoolArrayPointer("; - if isinstance(kind, mojom.Array): + if mojom.IsArrayKind(kind) and mojom.IsBoolKind(kind.kind): + return "encodeArrayPointer(new codec.ArrayOf(codec.PackedBool), "; + if mojom.IsAnyArrayKind(kind): return "encodeArrayPointer(%s, " % CodecType(kind.kind) - if isinstance(kind, mojom.Interface) or \ - isinstance(kind, mojom.InterfaceRequest): + if mojom.IsInterfaceKind(kind) or mojom.IsInterfaceRequestKind(kind): return JavaScriptEncodeSnippet(mojom.MSGPIPE) - if isinstance(kind, mojom.Enum): + if mojom.IsEnumKind(kind): return JavaScriptEncodeSnippet(mojom.INT32) diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py index c818455070a78..eb433bfb801a0 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/generator.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/generator.py @@ -36,37 +36,10 @@ def GetDataHeader(exported, struct): return struct def ExpectedArraySize(kind): - if isinstance(kind, mojom.FixedArray): + if mojom.IsFixedArrayKind(kind): return kind.length return 0 -def IsArrayKind(kind): - return isinstance(kind, (mojom.Array, mojom.FixedArray)) - -def IsStringKind(kind): - return kind.spec == 's' - -def IsEnumKind(kind): - return isinstance(kind, mojom.Enum) - -def IsObjectKind(kind): - return isinstance(kind, (mojom.Struct, mojom.Array, mojom.FixedArray)) or \ - IsStringKind(kind) - -def IsHandleKind(kind): - return kind.spec.startswith('h') or \ - isinstance(kind, mojom.Interface) or \ - isinstance(kind, mojom.InterfaceRequest) - -def IsInterfaceKind(kind): - return isinstance(kind, mojom.Interface) - -def IsInterfaceRequestKind(kind): - return isinstance(kind, mojom.InterfaceRequest) - -def IsMoveOnlyKind(kind): - return IsObjectKind(kind) or IsHandleKind(kind) - def StudlyCapsToCamel(studly): return studly[0].lower() + studly[1:] diff --git a/mojo/public/tools/bindings/pylib/mojom/generate/module.py b/mojo/public/tools/bindings/pylib/mojom/generate/module.py index 4c6e768150791..448f7474e195a 100644 --- a/mojo/public/tools/bindings/pylib/mojom/generate/module.py +++ b/mojo/public/tools/bindings/pylib/mojom/generate/module.py @@ -317,3 +317,86 @@ def AddStruct(self, name): struct=Struct(name, module=self) self.structs.append(struct) return struct + + +def IsBoolKind(kind): + return kind.spec == BOOL.spec + + +def IsStringKind(kind): + return kind.spec == STRING.spec or kind.spec == NULLABLE_STRING.spec + + +def IsHandleKind(kind): + return kind.spec == HANDLE.spec or kind.spec == NULLABLE_HANDLE.spec + + +def IsDataPipeConsumerKind(kind): + return kind.spec == DCPIPE.spec or kind.spec == NULLABLE_DCPIPE.spec + + +def IsDataPipeProducerKind(kind): + return kind.spec == DPPIPE.spec or kind.spec == NULLABLE_DPPIPE.spec + + +def IsMessagePipeKind(kind): + return kind.spec == MSGPIPE.spec or kind.spec == NULLABLE_MSGPIPE.spec + + +def IsSharedBufferKind(kind): + return (kind.spec == SHAREDBUFFER.spec or + kind.spec == NULLABLE_SHAREDBUFFER.spec) + + +def IsStructKind(kind): + return isinstance(kind, Struct) + + +def IsArrayKind(kind): + return isinstance(kind, Array) + + +def IsFixedArrayKind(kind): + return isinstance(kind, FixedArray) + + +def IsInterfaceKind(kind): + return isinstance(kind, Interface) + + +def IsInterfaceRequestKind(kind): + return isinstance(kind, InterfaceRequest) + + +def IsEnumKind(kind): + return isinstance(kind, Enum) + + +def IsNullableKind(kind): + return isinstance(kind, ReferenceKind) and kind.is_nullable + + +def IsAnyArrayKind(kind): + return IsArrayKind(kind) or IsFixedArrayKind(kind) + + +def IsObjectKind(kind): + return IsStructKind(kind) or IsAnyArrayKind(kind) or IsStringKind(kind) + + +def IsNonInterfaceHandleKind(kind): + return (IsHandleKind(kind) or + IsDataPipeConsumerKind(kind) or + IsDataPipeProducerKind(kind) or + IsMessagePipeKind(kind) or + IsSharedBufferKind(kind)) + + +def IsAnyHandleKind(kind): + return (IsNonInterfaceHandleKind(kind) or + IsInterfaceKind(kind) or + IsInterfaceRequestKind(kind)) + + +def IsMoveOnlyKind(kind): + return IsObjectKind(kind) or IsAnyHandleKind(kind) diff --git a/mojo/service_manager/BUILD.gn b/mojo/service_manager/BUILD.gn deleted file mode 100644 index c526e6d91ff3a..0000000000000 --- a/mojo/service_manager/BUILD.gn +++ /dev/null @@ -1,33 +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. - -# GYP version: mojo.gyp:mojo_service_manager -component("service_manager") { - output_name = "mojo_service_manager" - sources = [ - "background_shell_service_loader.cc", - "background_shell_service_loader.h", - "service_loader.h", - "service_manager.cc", - "service_manager.h", - "service_manager_export.h", - ] - - defines = [ "MOJO_SERVICE_MANAGER_IMPLEMENTATION" ] - - deps = [ - "//base", - "//base/third_party/dynamic_annotations", - "//net", - "//url", - "//mojo/common", - "//mojo/environment:chromium", - "//mojo/public/interfaces/application:application", - "//mojo/system", - ] - - forward_dependent_configs_from = [ - "//mojo/public/interfaces/application:application", - ] -} diff --git a/mojo/service_manager/background_shell_service_loader.cc b/mojo/service_manager/background_shell_service_loader.cc deleted file mode 100644 index ef97255f283cc..0000000000000 --- a/mojo/service_manager/background_shell_service_loader.cc +++ /dev/null @@ -1,114 +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/service_manager/background_shell_service_loader.h" - -#include "base/bind.h" -#include "base/run_loop.h" -#include "mojo/service_manager/service_manager.h" - -namespace mojo { - -class BackgroundShellServiceLoader::BackgroundLoader { - public: - explicit BackgroundLoader(ServiceLoader* loader) : loader_(loader) {} - ~BackgroundLoader() {} - - void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - loader_->LoadService(manager, url, shell_handle.Pass()); - } - - void OnServiceError(ServiceManager* manager, const GURL& url) { - loader_->OnServiceError(manager, url); - } - - private: - ServiceLoader* loader_; // Owned by BackgroundShellServiceLoader - - DISALLOW_COPY_AND_ASSIGN(BackgroundLoader); -}; - -BackgroundShellServiceLoader::BackgroundShellServiceLoader( - scoped_ptr real_loader, - const std::string& thread_name, - base::MessageLoop::Type message_loop_type) - : loader_(real_loader.Pass()), - message_loop_type_(message_loop_type), - thread_name_(thread_name), - message_loop_created_(true, false), - background_loader_(NULL) { -} - -BackgroundShellServiceLoader::~BackgroundShellServiceLoader() { - if (thread_) - thread_->Join(); -} - -void BackgroundShellServiceLoader::LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - if (!thread_) { - // TODO(tim): It'd be nice if we could just have each LoadService call - // result in a new thread like DynamicService{Loader, Runner}. But some - // loaders are creating multiple ApplicationImpls (NetworkServiceLoader) - // sharing a delegate (etc). So we have to keep it single threaded, wait - // for the thread to initialize, and post to the TaskRunner for subsequent - // LoadService calls for now. - thread_.reset(new base::DelegateSimpleThread(this, thread_name_)); - thread_->Start(); - message_loop_created_.Wait(); - DCHECK(task_runner_); - } - - task_runner_->PostTask(FROM_HERE, - base::Bind(&BackgroundShellServiceLoader::LoadServiceOnBackgroundThread, - base::Unretained(this), manager, url, - base::Owned( - new ScopedMessagePipeHandle(shell_handle.Pass())))); -} - -void BackgroundShellServiceLoader::OnServiceError( - ServiceManager* manager, const GURL& url) { - task_runner_->PostTask(FROM_HERE, - base::Bind( - &BackgroundShellServiceLoader::OnServiceErrorOnBackgroundThread, - base::Unretained(this), manager, url)); -} - -void BackgroundShellServiceLoader::Run() { - base::MessageLoop message_loop(message_loop_type_); - base::RunLoop loop; - task_runner_ = message_loop.task_runner(); - quit_closure_ = loop.QuitClosure(); - message_loop_created_.Signal(); - loop.Run(); - - delete background_loader_; - background_loader_ = NULL; - // Destroy |loader_| on the thread it's actually used on. - loader_.reset(); -} - -void BackgroundShellServiceLoader::LoadServiceOnBackgroundThread( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle* shell_handle) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - if (!background_loader_) - background_loader_ = new BackgroundLoader(loader_.get()); - background_loader_->LoadService(manager, url, shell_handle->Pass()); -} - -void BackgroundShellServiceLoader::OnServiceErrorOnBackgroundThread( - ServiceManager* manager, const GURL& url) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - if (!background_loader_) - background_loader_ = new BackgroundLoader(loader_.get()); - background_loader_->OnServiceError(manager, url); -} - -} // namespace mojo diff --git a/mojo/service_manager/background_shell_service_loader.h b/mojo/service_manager/background_shell_service_loader.h deleted file mode 100644 index 3f4b3fc568799..0000000000000 --- a/mojo/service_manager/background_shell_service_loader.h +++ /dev/null @@ -1,77 +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_SERVICE_MANAGER_BACKGROUND_SHELL_SERVICE_LOADER_H_ -#define MOJO_SERVICE_MANAGER_BACKGROUND_SHELL_SERVICE_LOADER_H_ - -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/synchronization/waitable_event.h" -#include "base/threading/simple_thread.h" -#include "mojo/service_manager/service_loader.h" - -namespace mojo { - -// TODO(tim): Eventually this should be Android-only to support services -// that we need to bundle with the shell (such as NetworkService). Perhaps -// we should move it to shell/ as well. -class MOJO_SERVICE_MANAGER_EXPORT BackgroundShellServiceLoader : - public ServiceLoader, - public base::DelegateSimpleThread::Delegate { - public: - BackgroundShellServiceLoader(scoped_ptr real_loader, - const std::string& thread_name, - base::MessageLoop::Type message_loop_type); - virtual ~BackgroundShellServiceLoader(); - - // ServiceLoader overrides: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE; - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE; - - private: - class BackgroundLoader; - - // |base::DelegateSimpleThread::Delegate| method: - virtual void Run() OVERRIDE; - - // These functions are exected on the background thread. They call through - // to |background_loader_| to do the actual loading. - // TODO: having this code take a |manager| is fragile (as ServiceManager isn't - // thread safe). - void LoadServiceOnBackgroundThread( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle* shell_handle); - void OnServiceErrorOnBackgroundThread(ServiceManager* manager, - const GURL& url); - bool quit_on_shutdown_; - scoped_ptr loader_; - - const base::MessageLoop::Type message_loop_type_; - const std::string thread_name_; - - // Created on |thread_| during construction of |this|. Protected against - // uninitialized use by |message_loop_created_|, and protected against - // use-after-free by holding a reference to the thread-safe object. Note - // that holding a reference won't hold |thread_| from exiting. - scoped_refptr task_runner_; - base::WaitableEvent message_loop_created_; - - // Lives on |thread_|. - base::Closure quit_closure_; - - scoped_ptr thread_; - - // Lives on |thread_|. Trivial interface that calls through to |loader_|. - BackgroundLoader* background_loader_; - - DISALLOW_COPY_AND_ASSIGN(BackgroundShellServiceLoader); -}; - -} // namespace mojo - -#endif // MOJO_SERVICE_MANAGER_BACKGROUND_SHELL_SERVICE_LOADER_H_ diff --git a/mojo/service_manager/background_shell_service_loader_unittest.cc b/mojo/service_manager/background_shell_service_loader_unittest.cc deleted file mode 100644 index 7469b0f7bb3ab..0000000000000 --- a/mojo/service_manager/background_shell_service_loader_unittest.cc +++ /dev/null @@ -1,55 +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/service_manager/background_shell_service_loader.h" - -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { - -namespace { - -class DummyLoader : public ServiceLoader { - public: - DummyLoader() : simulate_app_quit_(true) {} - virtual ~DummyLoader() {} - - // ServiceLoader overrides: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE { - if (simulate_app_quit_) - base::MessageLoop::current()->Quit(); - } - - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE { - } - - void DontSimulateAppQuit() { simulate_app_quit_ = false; } - - private: - bool simulate_app_quit_; -}; - -} // namespace - -// Tests that the loader can start and stop gracefully. -TEST(BackgroundShellServiceLoaderTest, StartStop) { - scoped_ptr real_loader(new DummyLoader()); - BackgroundShellServiceLoader loader(real_loader.Pass(), "test", - base::MessageLoop::TYPE_DEFAULT); -} - -// Tests that the loader can load a service that is well behaved (quits -// itself). -TEST(BackgroundShellServiceLoaderTest, Load) { - scoped_ptr real_loader(new DummyLoader()); - BackgroundShellServiceLoader loader(real_loader.Pass(), "test", - base::MessageLoop::TYPE_DEFAULT); - MessagePipe dummy; - loader.LoadService(NULL, GURL(), dummy.handle0.Pass()); -} - -} // namespace mojo diff --git a/mojo/service_manager/service_loader.h b/mojo/service_manager/service_loader.h deleted file mode 100644 index 07be7d9a1bf0a..0000000000000 --- a/mojo/service_manager/service_loader.h +++ /dev/null @@ -1,38 +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_SERVICE_MANAGER_SERVICE_LOADER_H_ -#define MOJO_SERVICE_MANAGER_SERVICE_LOADER_H_ - -#include "mojo/public/cpp/system/core.h" -#include "mojo/service_manager/service_manager_export.h" -#include "url/gurl.h" - -namespace mojo { - -class ServiceManager; - -// Interface to allowing loading behavior to be established for schemes, -// specific urls or as the default. -// A ServiceLoader is responsible to using whatever mechanism is appropriate -// to load the application at url. -// TODO(davemoore): change name to ApplicationLoader. -// The handle to the shell is passed to that application so it can bind it to -// a Shell instance. This will give the Application a way to connect to other -// apps and services. -class MOJO_SERVICE_MANAGER_EXPORT ServiceLoader { - public: - virtual ~ServiceLoader() {} - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) = 0; - virtual void OnServiceError(ServiceManager* manager, const GURL& url) = 0; - - protected: - ServiceLoader() {} -}; - -} // namespace mojo - -#endif // MOJO_SERVICE_MANAGER_SERVICE_LOADER_H_ diff --git a/mojo/service_manager/service_manager.cc b/mojo/service_manager/service_manager.cc deleted file mode 100644 index 51efd41feddd0..0000000000000 --- a/mojo/service_manager/service_manager.cc +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include - -#include "mojo/service_manager/service_manager.h" - -#include "base/lazy_instance.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/stl_util.h" -#include "mojo/common/common_type_converters.h" -#include "mojo/public/interfaces/application/application.mojom.h" -#include "mojo/public/interfaces/application/shell.mojom.h" -#include "mojo/service_manager/service_loader.h" - -namespace mojo { - -namespace { -// Used by TestAPI. -bool has_created_instance = false; - -class StubServiceProvider : public InterfaceImpl { - public: - ServiceProvider* GetRemoteServiceProvider() { - return client(); - } - - private: - virtual void ConnectToService( - const String& service_name, - ScopedMessagePipeHandle client_handle) MOJO_OVERRIDE {} -}; - -} - -class ServiceManager::ShellImpl : public InterfaceImpl { - public: - ShellImpl(ServiceManager* manager, const GURL& url) - : manager_(manager), - url_(url) { - } - - virtual ~ShellImpl() { - } - - void ConnectToClient(const GURL& requestor_url, - ServiceProviderPtr service_provider) { - client()->AcceptConnection(String::From(requestor_url), - service_provider.Pass()); - } - - // ServiceProvider implementation: - virtual void ConnectToApplication( - const String& app_url, - InterfaceRequest in_service_provider) OVERRIDE { - ServiceProviderPtr out_service_provider; - out_service_provider.Bind(in_service_provider.PassMessagePipe()); - manager_->ConnectToApplication( - app_url.To(), - url_, - out_service_provider.Pass()); - } - - const GURL& url() const { return url_; } - - private: - virtual void OnConnectionError() OVERRIDE { - manager_->OnShellImplError(this); - } - - ServiceManager* const manager_; - const GURL url_; - - DISALLOW_COPY_AND_ASSIGN(ShellImpl); -}; - -// static -ServiceManager::TestAPI::TestAPI(ServiceManager* manager) : manager_(manager) { -} - -ServiceManager::TestAPI::~TestAPI() { -} - -bool ServiceManager::TestAPI::HasCreatedInstance() { - return has_created_instance; -} - -bool ServiceManager::TestAPI::HasFactoryForURL(const GURL& url) const { - return manager_->url_to_shell_impl_.find(url) != - manager_->url_to_shell_impl_.end(); -} - -ServiceManager::ServiceManager() : interceptor_(NULL) { -} - -ServiceManager::~ServiceManager() { - TerminateShellConnections(); - STLDeleteValues(&url_to_loader_); - STLDeleteValues(&scheme_to_loader_); -} - -void ServiceManager::TerminateShellConnections() { - STLDeleteValues(&url_to_shell_impl_); -} - -// static -ServiceManager* ServiceManager::GetInstance() { - static base::LazyInstance instance = - LAZY_INSTANCE_INITIALIZER; - has_created_instance = true; - return &instance.Get(); -} - -void ServiceManager::ConnectToApplication(const GURL& url, - const GURL& requestor_url, - ServiceProviderPtr service_provider) { - URLToShellImplMap::const_iterator shell_it = url_to_shell_impl_.find(url); - ShellImpl* shell_impl; - if (shell_it != url_to_shell_impl_.end()) { - shell_impl = shell_it->second; - } else { - MessagePipe pipe; - GetLoaderForURL(url)->LoadService(this, url, pipe.handle0.Pass()); - shell_impl = WeakBindToPipe(new ShellImpl(this, url), pipe.handle1.Pass()); - url_to_shell_impl_[url] = shell_impl; - } - if (interceptor_) { - shell_impl->ConnectToClient( - requestor_url, - interceptor_->OnConnectToClient(url, service_provider.Pass())); - } else { - shell_impl->ConnectToClient(requestor_url, service_provider.Pass()); - } -} - -void ServiceManager::SetLoaderForURL(scoped_ptr loader, - const GURL& url) { - URLToLoaderMap::iterator it = url_to_loader_.find(url); - if (it != url_to_loader_.end()) - delete it->second; - url_to_loader_[url] = loader.release(); -} - -void ServiceManager::SetLoaderForScheme(scoped_ptr loader, - const std::string& scheme) { - SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme); - if (it != scheme_to_loader_.end()) - delete it->second; - scheme_to_loader_[scheme] = loader.release(); -} - -void ServiceManager::SetInterceptor(Interceptor* interceptor) { - interceptor_ = interceptor; -} - -ServiceLoader* ServiceManager::GetLoaderForURL(const GURL& url) { - URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url); - if (url_it != url_to_loader_.end()) - return url_it->second; - SchemeToLoaderMap::const_iterator scheme_it = - scheme_to_loader_.find(url.scheme()); - if (scheme_it != scheme_to_loader_.end()) - return scheme_it->second; - return default_loader_.get(); -} - -void ServiceManager::OnShellImplError(ShellImpl* shell_impl) { - // Called from ~ShellImpl, so we do not need to call Destroy here. - const GURL url = shell_impl->url(); - URLToShellImplMap::iterator it = url_to_shell_impl_.find(url); - DCHECK(it != url_to_shell_impl_.end()); - delete it->second; - url_to_shell_impl_.erase(it); - ServiceLoader* loader = GetLoaderForURL(url); - if (loader) - loader->OnServiceError(this, url); -} - -ScopedMessagePipeHandle ServiceManager::ConnectToServiceByName( - const GURL& application_url, - const std::string& interface_name) { - StubServiceProvider* stub_sp = new StubServiceProvider; - ServiceProviderPtr spp; - BindToProxy(stub_sp, &spp); - ConnectToApplication(application_url, GURL(), spp.Pass()); - MessagePipe pipe; - stub_sp->GetRemoteServiceProvider()->ConnectToService( - interface_name, pipe.handle1.Pass()); - return pipe.handle0.Pass(); -} -} // namespace mojo diff --git a/mojo/service_manager/service_manager.h b/mojo/service_manager/service_manager.h deleted file mode 100644 index 69ebda65d7be3..0000000000000 --- a/mojo/service_manager/service_manager.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICE_MANAGER_SERVICE_MANAGER_H_ -#define MOJO_SERVICE_MANAGER_SERVICE_MANAGER_H_ - -#include - -#include "base/basictypes.h" -#include "base/gtest_prod_util.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/service_manager/service_manager_export.h" -#include "url/gurl.h" - -namespace mojo { - -class MOJO_SERVICE_MANAGER_EXPORT ServiceManager { - public: - // API for testing. - class MOJO_SERVICE_MANAGER_EXPORT TestAPI { - public: - explicit TestAPI(ServiceManager* manager); - ~TestAPI(); - - // Returns true if the shared instance has been created. - static bool HasCreatedInstance(); - // Returns true if there is a ShellImpl for this URL. - bool HasFactoryForURL(const GURL& url) const; - - private: - ServiceManager* manager_; - - DISALLOW_COPY_AND_ASSIGN(TestAPI); - }; - - // Interface class for debugging only. - class Interceptor { - public: - virtual ~Interceptor() {} - // Called when ServiceManager::Connect is called. - virtual ServiceProviderPtr OnConnectToClient( - const GURL& url, ServiceProviderPtr service_provider) = 0; - }; - - ServiceManager(); - ~ServiceManager(); - - // Returns a shared instance, creating it if necessary. - static ServiceManager* GetInstance(); - - // Loads a service if necessary and establishes a new client connection. - void ConnectToApplication(const GURL& application_url, - const GURL& requestor_url, - ServiceProviderPtr service_provider); - - template - inline void ConnectToService(const GURL& application_url, - InterfacePtr* ptr) { - ScopedMessagePipeHandle service_handle = - ConnectToServiceByName(application_url, Interface::Name_); - ptr->Bind(service_handle.Pass()); - } - - ScopedMessagePipeHandle ConnectToServiceByName( - const GURL& application_url, - const std::string& interface_name); - - // Sets the default Loader to be used if not overridden by SetLoaderForURL() - // or SetLoaderForScheme(). - void set_default_loader(scoped_ptr loader) { - default_loader_ = loader.Pass(); - } - // Sets a Loader to be used for a specific url. - void SetLoaderForURL(scoped_ptr loader, const GURL& url); - // Sets a Loader to be used for a specific url scheme. - void SetLoaderForScheme(scoped_ptr loader, - const std::string& scheme); - // Allows to interpose a debugger to service connections. - void SetInterceptor(Interceptor* interceptor); - - // Destroys all Shell-ends of connections established with Applications. - // Applications connected by this ServiceManager will observe pipe errors - // and have a chance to shutdown. - void TerminateShellConnections(); - - private: - class ShellImpl; - typedef std::map SchemeToLoaderMap; - typedef std::map URLToLoaderMap; - typedef std::map URLToShellImplMap; - - // Returns the Loader to use for a url (using default if not overridden.) - // The preference is to use a loader that's been specified for an url first, - // then one that's been specified for a scheme, then the default. - ServiceLoader* GetLoaderForURL(const GURL& url); - - // Removes a ShellImpl when it encounters an error. - void OnShellImplError(ShellImpl* shell_impl); - - // Loader management. - URLToLoaderMap url_to_loader_; - SchemeToLoaderMap scheme_to_loader_; - scoped_ptr default_loader_; - Interceptor* interceptor_; - - URLToShellImplMap url_to_shell_impl_; - - DISALLOW_COPY_AND_ASSIGN(ServiceManager); -}; - -} // namespace mojo - -#endif // MOJO_SERVICE_MANAGER_SERVICE_MANAGER_H_ diff --git a/mojo/service_manager/service_manager_export.h b/mojo/service_manager/service_manager_export.h deleted file mode 100644 index 251aad95d52c0..0000000000000 --- a/mojo/service_manager/service_manager_export.h +++ /dev/null @@ -1,32 +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_SERVICE_MANAGER_SERVICE_MANAGER_EXPORT_H_ -#define MOJO_SERVICE_MANAGER_SERVICE_MANAGER_EXPORT_H_ - -#if defined(COMPONENT_BUILD) - -#if defined(WIN32) - -#if defined(MOJO_SERVICE_MANAGER_IMPLEMENTATION) -#define MOJO_SERVICE_MANAGER_EXPORT __declspec(dllexport) -#else -#define MOJO_SERVICE_MANAGER_EXPORT __declspec(dllimport) -#endif - -#else // !defined(WIN32) - -#if defined(MOJO_SERVICE_MANAGER_IMPLEMENTATION) -#define MOJO_SERVICE_MANAGER_EXPORT __attribute__((visibility("default"))) -#else -#define MOJO_SERVICE_MANAGER_EXPORT -#endif - -#endif // defined(WIN32) - -#else // !defined(COMPONENT_BUILD) -#define MOJO_SERVICE_MANAGER_EXPORT -#endif - -#endif // MOJO_SERVICE_MANAGER_SERVICE_MANAGER_EXPORT_H_ diff --git a/mojo/service_manager/service_manager_unittest.cc b/mojo/service_manager/service_manager_unittest.cc deleted file mode 100644 index f42113f5a66ca..0000000000000 --- a/mojo/service_manager/service_manager_unittest.cc +++ /dev/null @@ -1,650 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/at_exit.h" -#include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "mojo/public/cpp/application/application_connection.h" -#include "mojo/public/cpp/application/application_delegate.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/public/cpp/application/interface_factory.h" -#include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/service_manager/background_shell_service_loader.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/service_manager/service_manager.h" -#include "mojo/service_manager/test.mojom.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace { - -const char kTestURLString[] = "test:testService"; -const char kTestAURLString[] = "test:TestA"; -const char kTestBURLString[] = "test:TestB"; - -struct TestContext { - TestContext() : num_impls(0), num_loader_deletes(0) {} - std::string last_test_string; - int num_impls; - int num_loader_deletes; -}; - -class QuitMessageLoopErrorHandler : public ErrorHandler { - public: - QuitMessageLoopErrorHandler() {} - virtual ~QuitMessageLoopErrorHandler() {} - - // |ErrorHandler| implementation: - virtual void OnConnectionError() OVERRIDE { - base::MessageLoop::current()->QuitWhenIdle(); - } - - private: - DISALLOW_COPY_AND_ASSIGN(QuitMessageLoopErrorHandler); -}; - -class TestServiceImpl : public InterfaceImpl { - public: - explicit TestServiceImpl(TestContext* context) : context_(context) { - ++context_->num_impls; - } - - virtual ~TestServiceImpl() { - --context_->num_impls; - } - - virtual void OnConnectionError() OVERRIDE { - if (!base::MessageLoop::current()->is_running()) - return; - base::MessageLoop::current()->Quit(); - } - - // TestService implementation: - virtual void Test(const String& test_string) OVERRIDE { - context_->last_test_string = test_string; - client()->AckTest(); - } - - private: - TestContext* context_; -}; - -class TestClientImpl : public TestClient { - public: - explicit TestClientImpl(TestServicePtr service) - : service_(service.Pass()), - quit_after_ack_(false) { - service_.set_client(this); - } - - virtual ~TestClientImpl() { - service_.reset(); - } - - virtual void AckTest() OVERRIDE { - if (quit_after_ack_) - base::MessageLoop::current()->Quit(); - } - - void Test(std::string test_string) { - quit_after_ack_ = true; - service_->Test(test_string); - } - - private: - TestServicePtr service_; - bool quit_after_ack_; - DISALLOW_COPY_AND_ASSIGN(TestClientImpl); -}; - -class TestServiceLoader : public ServiceLoader, - public ApplicationDelegate, - public InterfaceFactory { - public: - TestServiceLoader() - : context_(NULL), - num_loads_(0) { - } - - virtual ~TestServiceLoader() { - if (context_) - ++context_->num_loader_deletes; - test_app_.reset(NULL); - } - - void set_context(TestContext* context) { context_ = context; } - int num_loads() const { return num_loads_; } - - private: - // ServiceLoader implementation. - virtual void LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle service_provider_handle) OVERRIDE { - ++num_loads_; - test_app_.reset(new ApplicationImpl(this, service_provider_handle.Pass())); - } - - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE { - } - - // ApplicationDelegate implementation. - virtual bool ConfigureIncomingConnection( - ApplicationConnection* connection) OVERRIDE { - connection->AddService(this); - return true; - } - - // InterfaceFactory implementation. - virtual void Create(ApplicationConnection* connection, - InterfaceRequest request) OVERRIDE { - BindToRequest(new TestServiceImpl(context_), &request); - } - - scoped_ptr test_app_; - TestContext* context_; - int num_loads_; - DISALLOW_COPY_AND_ASSIGN(TestServiceLoader); -}; - -class TesterContext { - public: - explicit TesterContext(base::MessageLoop* loop) - : num_b_calls_(0), - num_c_calls_(0), - num_a_deletes_(0), - num_b_deletes_(0), - num_c_deletes_(0), - tester_called_quit_(false), - a_called_quit_(false), - loop_(loop) {} - - void IncrementNumBCalls() { - base::AutoLock lock(lock_); - num_b_calls_++; - } - - void IncrementNumCCalls() { - base::AutoLock lock(lock_); - num_c_calls_++; - } - - void IncrementNumADeletes() { - base::AutoLock lock(lock_); - num_a_deletes_++; - } - - void IncrementNumBDeletes() { - base::AutoLock lock(lock_); - num_b_deletes_++; - } - - void IncrementNumCDeletes() { - base::AutoLock lock(lock_); - num_c_deletes_++; - } - - void set_tester_called_quit() { - base::AutoLock lock(lock_); - tester_called_quit_ = true; - } - - void set_a_called_quit() { - base::AutoLock lock(lock_); - a_called_quit_ = true; - } - - int num_b_calls() { - base::AutoLock lock(lock_); - return num_b_calls_; - } - int num_c_calls() { - base::AutoLock lock(lock_); - return num_c_calls_; - } - int num_a_deletes() { - base::AutoLock lock(lock_); - return num_a_deletes_; - } - int num_b_deletes() { - base::AutoLock lock(lock_); - return num_b_deletes_; - } - int num_c_deletes() { - base::AutoLock lock(lock_); - return num_c_deletes_; - } - bool tester_called_quit() { - base::AutoLock lock(lock_); - return tester_called_quit_; - } - bool a_called_quit() { - base::AutoLock lock(lock_); - return a_called_quit_; - } - - void QuitSoon() { - loop_->PostTask(FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); - } - - private: - // lock_ protects all members except for loop_ which must be unchanged for the - // lifetime of this class. - base::Lock lock_; - int num_b_calls_; - int num_c_calls_; - int num_a_deletes_; - int num_b_deletes_; - int num_c_deletes_; - bool tester_called_quit_; - bool a_called_quit_; - - base::MessageLoop* loop_; -}; - -// Used to test that the requestor url will be correctly passed. -class TestAImpl : public InterfaceImpl { - public: - TestAImpl(ApplicationConnection* connection, TesterContext* test_context) - : test_context_(test_context) { - connection->ConnectToApplication(kTestBURLString)->ConnectToService(&b_); - } - virtual ~TestAImpl() { - test_context_->IncrementNumADeletes(); - if (base::MessageLoop::current()->is_running()) - Quit(); - } - - private: - virtual void CallB() OVERRIDE { - b_->B(base::Bind(&TestAImpl::Quit, base::Unretained(this))); - } - - virtual void CallCFromB() OVERRIDE { - b_->CallC(base::Bind(&TestAImpl::Quit, base::Unretained(this))); - } - - void Quit() { - base::MessageLoop::current()->Quit(); - test_context_->set_a_called_quit(); - test_context_->QuitSoon(); - } - - TesterContext* test_context_; - TestBPtr b_; -}; - -class TestBImpl : public InterfaceImpl { - public: - TestBImpl(ApplicationConnection* connection, TesterContext* test_context) - : test_context_(test_context) { - connection->ConnectToService(&c_); - } - - virtual ~TestBImpl() { - test_context_->IncrementNumBDeletes(); - if (base::MessageLoop::current()->is_running()) - base::MessageLoop::current()->Quit(); - test_context_->QuitSoon(); - } - - private: - virtual void B(const mojo::Callback& callback) OVERRIDE { - test_context_->IncrementNumBCalls(); - callback.Run(); - } - - virtual void CallC(const mojo::Callback& callback) OVERRIDE { - test_context_->IncrementNumBCalls(); - c_->C(callback); - } - - TesterContext* test_context_; - TestCPtr c_; -}; - -class TestCImpl : public InterfaceImpl { - public: - TestCImpl(ApplicationConnection* connection, TesterContext* test_context) - : test_context_(test_context) {} - - virtual ~TestCImpl() { test_context_->IncrementNumCDeletes(); } - - private: - virtual void C(const mojo::Callback& callback) OVERRIDE { - test_context_->IncrementNumCCalls(); - callback.Run(); - } - TesterContext* test_context_; -}; - -class Tester : public ApplicationDelegate, - public ServiceLoader, - public InterfaceFactory, - public InterfaceFactory, - public InterfaceFactory { - public: - Tester(TesterContext* context, const std::string& requestor_url) - : context_(context), - requestor_url_(requestor_url) {} - virtual ~Tester() {} - - private: - virtual void LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE { - app_.reset(new ApplicationImpl(this, shell_handle.Pass())); - } - - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE {} - - virtual bool ConfigureIncomingConnection( - ApplicationConnection* connection) OVERRIDE { - if (!requestor_url_.empty() && - requestor_url_ != connection->GetRemoteApplicationURL()) { - context_->set_tester_called_quit(); - context_->QuitSoon(); - base::MessageLoop::current()->Quit(); - return false; - } - // If we're coming from A, then add B, otherwise A. - if (connection->GetRemoteApplicationURL() == kTestAURLString) - connection->AddService(this); - else - connection->AddService(this); - return true; - } - - virtual bool ConfigureOutgoingConnection( - ApplicationConnection* connection) OVERRIDE { - // If we're connecting to B, then add C. - if (connection->GetRemoteApplicationURL() == kTestBURLString) - connection->AddService(this); - return true; - } - - virtual void Create(ApplicationConnection* connection, - InterfaceRequest request) OVERRIDE { - BindToRequest(new TestAImpl(connection, context_), &request); - } - - virtual void Create(ApplicationConnection* connection, - InterfaceRequest request) OVERRIDE { - BindToRequest(new TestBImpl(connection, context_), &request); - } - - virtual void Create(ApplicationConnection* connection, - InterfaceRequest request) OVERRIDE { - BindToRequest(new TestCImpl(connection, context_), &request); - } - - TesterContext* context_; - scoped_ptr app_; - std::string requestor_url_; -}; - -class TestServiceInterceptor : public ServiceManager::Interceptor { - public: - TestServiceInterceptor() : call_count_(0) {} - - virtual ServiceProviderPtr OnConnectToClient( - const GURL& url, ServiceProviderPtr service_provider) OVERRIDE { - ++call_count_; - url_ = url; - return service_provider.Pass(); - } - - std::string url_spec() const { - if (!url_.is_valid()) - return "invalid url"; - return url_.spec(); - } - - int call_count() const { - return call_count_; - } - - private: - int call_count_; - GURL url_; - DISALLOW_COPY_AND_ASSIGN(TestServiceInterceptor); -}; - -} // namespace - -class ServiceManagerTest : public testing::Test { - public: - ServiceManagerTest() : tester_context_(&loop_) {} - - virtual ~ServiceManagerTest() {} - - virtual void SetUp() OVERRIDE { - service_manager_.reset(new ServiceManager); - TestServiceLoader* default_loader = new TestServiceLoader; - default_loader->set_context(&context_); - service_manager_->set_default_loader( - scoped_ptr(default_loader)); - - TestServicePtr service_proxy; - service_manager_->ConnectToService(GURL(kTestURLString), &service_proxy); - test_client_.reset(new TestClientImpl(service_proxy.Pass())); - } - - virtual void TearDown() OVERRIDE { - test_client_.reset(NULL); - service_manager_.reset(NULL); - } - - scoped_ptr MakeLoader( - const std::string& requestor_url) { - scoped_ptr real_loader( - new Tester(&tester_context_, requestor_url)); - scoped_ptr loader( - new BackgroundShellServiceLoader(real_loader.Pass(), std::string(), - base::MessageLoop::TYPE_DEFAULT)); - return loader.Pass(); - } - - void AddLoaderForURL(const GURL& url, const std::string& requestor_url) { - service_manager_->SetLoaderForURL( - MakeLoader(requestor_url).PassAs(), url); - } - - bool HasFactoryForTestURL() { - ServiceManager::TestAPI manager_test_api(service_manager_.get()); - return manager_test_api.HasFactoryForURL(GURL(kTestURLString)); - } - - protected: - base::ShadowingAtExitManager at_exit_; - TesterContext tester_context_; - TestContext context_; - base::MessageLoop loop_; - scoped_ptr test_client_; - scoped_ptr service_manager_; - DISALLOW_COPY_AND_ASSIGN(ServiceManagerTest); -}; - -TEST_F(ServiceManagerTest, Basic) { - test_client_->Test("test"); - loop_.Run(); - EXPECT_EQ(std::string("test"), context_.last_test_string); -} - -TEST_F(ServiceManagerTest, ClientError) { - test_client_->Test("test"); - EXPECT_TRUE(HasFactoryForTestURL()); - loop_.Run(); - EXPECT_EQ(1, context_.num_impls); - test_client_.reset(NULL); - loop_.Run(); - EXPECT_EQ(0, context_.num_impls); - EXPECT_TRUE(HasFactoryForTestURL()); -} - -TEST_F(ServiceManagerTest, Deletes) { - { - ServiceManager sm; - TestServiceLoader* default_loader = new TestServiceLoader; - default_loader->set_context(&context_); - TestServiceLoader* url_loader1 = new TestServiceLoader; - TestServiceLoader* url_loader2 = new TestServiceLoader; - url_loader1->set_context(&context_); - url_loader2->set_context(&context_); - TestServiceLoader* scheme_loader1 = new TestServiceLoader; - TestServiceLoader* scheme_loader2 = new TestServiceLoader; - scheme_loader1->set_context(&context_); - scheme_loader2->set_context(&context_); - sm.set_default_loader(scoped_ptr(default_loader)); - sm.SetLoaderForURL(scoped_ptr(url_loader1), - GURL("test:test1")); - sm.SetLoaderForURL(scoped_ptr(url_loader2), - GURL("test:test1")); - sm.SetLoaderForScheme(scoped_ptr(scheme_loader1), "test"); - sm.SetLoaderForScheme(scoped_ptr(scheme_loader2), "test"); - } - EXPECT_EQ(5, context_.num_loader_deletes); -} - -// Confirm that both urls and schemes can have their loaders explicitly set. -TEST_F(ServiceManagerTest, SetLoaders) { - ServiceManager sm; - TestServiceLoader* default_loader = new TestServiceLoader; - TestServiceLoader* url_loader = new TestServiceLoader; - TestServiceLoader* scheme_loader = new TestServiceLoader; - service_manager_->set_default_loader( - scoped_ptr(default_loader)); - service_manager_->SetLoaderForURL(scoped_ptr(url_loader), - GURL("test:test1")); - service_manager_->SetLoaderForScheme(scoped_ptr(scheme_loader), - "test"); - - // test::test1 should go to url_loader. - TestServicePtr test_service; - service_manager_->ConnectToService(GURL("test:test1"), &test_service); - EXPECT_EQ(1, url_loader->num_loads()); - EXPECT_EQ(0, scheme_loader->num_loads()); - EXPECT_EQ(0, default_loader->num_loads()); - - // test::test2 should go to scheme loader. - service_manager_->ConnectToService(GURL("test:test2"), &test_service); - EXPECT_EQ(1, url_loader->num_loads()); - EXPECT_EQ(1, scheme_loader->num_loads()); - EXPECT_EQ(0, default_loader->num_loads()); - - // http::test1 should go to default loader. - service_manager_->ConnectToService(GURL("http:test1"), &test_service); - EXPECT_EQ(1, url_loader->num_loads()); - EXPECT_EQ(1, scheme_loader->num_loads()); - EXPECT_EQ(1, default_loader->num_loads()); -} - -// Confirm that the url of a service is correctly passed to another service that -// it loads. -TEST_F(ServiceManagerTest, ACallB) { - // Any url can load a. - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // Only a can load b. - AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); - - TestAPtr a; - service_manager_->ConnectToService(GURL(kTestAURLString), &a); - a->CallB(); - loop_.Run(); - EXPECT_EQ(1, tester_context_.num_b_calls()); - EXPECT_TRUE(tester_context_.a_called_quit()); -} - -// A calls B which calls C. -TEST_F(ServiceManagerTest, BCallC) { - // Any url can load a. - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // Only a can load b. - AddLoaderForURL(GURL(kTestBURLString), kTestAURLString); - - TestAPtr a; - service_manager_->ConnectToService(GURL(kTestAURLString), &a); - a->CallCFromB(); - loop_.Run(); - - EXPECT_EQ(1, tester_context_.num_b_calls()); - EXPECT_EQ(1, tester_context_.num_c_calls()); - EXPECT_TRUE(tester_context_.a_called_quit()); -} - -// Confirm that a service impl will be deleted if the app that connected to -// it goes away. -TEST_F(ServiceManagerTest, BDeleted) { - AddLoaderForURL(GURL(kTestAURLString), std::string()); - AddLoaderForURL(GURL(kTestBURLString), std::string()); - - TestAPtr a; - service_manager_->ConnectToService(GURL(kTestAURLString), &a); - - a->CallB(); - loop_.Run(); - - // Kills the a app. - service_manager_->SetLoaderForURL(scoped_ptr(), - GURL(kTestAURLString)); - loop_.Run(); - - EXPECT_EQ(1, tester_context_.num_b_deletes()); -} - -// Confirm that the url of a service is correctly passed to another service that -// it loads, and that it can be rejected. -TEST_F(ServiceManagerTest, ANoLoadB) { - // Any url can load a. - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // Only c can load b, so this will fail. - AddLoaderForURL(GURL(kTestBURLString), "test:TestC"); - - TestAPtr a; - service_manager_->ConnectToService(GURL(kTestAURLString), &a); - a->CallB(); - loop_.Run(); - EXPECT_EQ(0, tester_context_.num_b_calls()); - - EXPECT_FALSE(tester_context_.a_called_quit()); - EXPECT_TRUE(tester_context_.tester_called_quit()); -} - -TEST_F(ServiceManagerTest, NoServiceNoLoad) { - AddLoaderForURL(GURL(kTestAURLString), std::string()); - - // There is no TestC service implementation registered with ServiceManager, - // so this cannot succeed (but also shouldn't crash). - TestCPtr c; - service_manager_->ConnectToService(GURL(kTestAURLString), &c); - QuitMessageLoopErrorHandler quitter; - c.set_error_handler(&quitter); - - loop_.Run(); - EXPECT_TRUE(c.encountered_error()); -} - -TEST_F(ServiceManagerTest, Interceptor) { - TestServiceInterceptor interceptor; - TestServiceLoader* default_loader = new TestServiceLoader; - service_manager_->set_default_loader( - scoped_ptr(default_loader)); - service_manager_->SetInterceptor(&interceptor); - - std::string url("test:test3"); - TestServicePtr test_service; - service_manager_->ConnectToService(GURL(url), &test_service); - - EXPECT_EQ(1, interceptor.call_count()); - EXPECT_EQ(url, interceptor.url_spec()); - EXPECT_EQ(1, default_loader->num_loads()); -} - -} // namespace mojo diff --git a/mojo/services/BUILD.gn b/mojo/services/BUILD.gn index 09753e07e1e47..619a9f9fcc1c6 100644 --- a/mojo/services/BUILD.gn +++ b/mojo/services/BUILD.gn @@ -6,6 +6,7 @@ group("services") { deps = [ "//mojo/services/dbus_echo:bindings", "//mojo/services/gles2:bindings", + "//mojo/services/public/interfaces/content_handler", "//mojo/services/public/interfaces/input_events", "//mojo/services/public/interfaces/geometry", "//mojo/services/public/interfaces/native_viewport", diff --git a/mojo/services/gles2/command_buffer_impl.cc b/mojo/services/gles2/command_buffer_impl.cc index 8674f121b236c..d3b9137f40fb4 100644 --- a/mojo/services/gles2/command_buffer_impl.cc +++ b/mojo/services/gles2/command_buffer_impl.cc @@ -21,7 +21,6 @@ #include "ui/gl/gl_surface.h" namespace mojo { -namespace services { namespace { @@ -179,5 +178,4 @@ void CommandBufferImpl::OnResize(gfx::Size size, float scale_factor) { surface_->Resize(size); } -} // namespace services } // namespace mojo diff --git a/mojo/services/gles2/command_buffer_impl.h b/mojo/services/gles2/command_buffer_impl.h index fc79c5f0d4c2f..fc90d348aa9ef 100644 --- a/mojo/services/gles2/command_buffer_impl.h +++ b/mojo/services/gles2/command_buffer_impl.h @@ -26,7 +26,6 @@ class GLSurface; } namespace mojo { -namespace services { class CommandBufferImpl : public InterfaceImpl { public: @@ -65,7 +64,6 @@ class CommandBufferImpl : public InterfaceImpl { DISALLOW_COPY_AND_ASSIGN(CommandBufferImpl); }; -} // namespace services } // namespace mojo #endif // MOJO_SERVICES_GLES2_COMMAND_BUFFER_IMPL_H_ diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.cc b/mojo/services/html_viewer/blink_url_request_type_converters.cc new file mode 100644 index 0000000000000..1f6553eba83a2 --- /dev/null +++ b/mojo/services/html_viewer/blink_url_request_type_converters.cc @@ -0,0 +1,110 @@ +// 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/services/html_viewer/blink_url_request_type_converters.h" + +#include "base/strings/string_util.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" +#include "third_party/WebKit/public/platform/WebURLRequest.h" + +namespace mojo { +namespace { + +// Ripped from web_url_loader_impl.cc. +class HeaderFlattener : public blink::WebHTTPHeaderVisitor { + public: + HeaderFlattener() : has_accept_header_(false) {} + + virtual void visitHeader(const blink::WebString& name, + const blink::WebString& value) { + // Headers are latin1. + const std::string& name_latin1 = name.latin1(); + const std::string& value_latin1 = value.latin1(); + + // Skip over referrer headers found in the header map because we already + // pulled it out as a separate parameter. + if (LowerCaseEqualsASCII(name_latin1, "referer")) + return; + + if (LowerCaseEqualsASCII(name_latin1, "accept")) + has_accept_header_ = true; + + buffer_.push_back(name_latin1 + ": " + value_latin1); + } + + Array GetBuffer() { + // In some cases, WebKit doesn't add an Accept header, but not having the + // header confuses some web servers. See bug 808613. + if (!has_accept_header_) { + buffer_.push_back("Accept: */*"); + has_accept_header_ = true; + } + return buffer_.Pass(); + } + + private: + Array buffer_; + bool has_accept_header_; +}; + +void AddRequestBody(URLRequest* url_request, + const blink::WebURLRequest& request) { + if (request.httpBody().isNull()) + return; + + uint32_t i = 0; + blink::WebHTTPBody::Element element; + while (request.httpBody().elementAt(i++, element)) { + switch (element.type) { + case blink::WebHTTPBody::Element::TypeData: + if (!element.data.isEmpty()) { + // WebKit sometimes gives up empty data to append. These aren't + // necessary so we just optimize those out here. + uint32_t num_bytes = static_cast(element.data.size()); + MojoCreateDataPipeOptions options; + options.struct_size = sizeof(MojoCreateDataPipeOptions); + options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; + options.element_num_bytes = 1; + options.capacity_num_bytes = num_bytes; + DataPipe data_pipe(options); + url_request->body.push_back( + data_pipe.consumer_handle.Pass()); + WriteDataRaw(data_pipe.producer_handle.get(), + element.data.data(), + &num_bytes, + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); + } + break; + case blink::WebHTTPBody::Element::TypeFile: + case blink::WebHTTPBody::Element::TypeFileSystemURL: + case blink::WebHTTPBody::Element::TypeBlob: + // TODO(mpcomplete): handle these. + NOTIMPLEMENTED(); + break; + default: + NOTREACHED(); + } + } +} + +} // namespace + +URLRequestPtr TypeConverter::ConvertFrom( + const blink::WebURLRequest& request) { + URLRequestPtr url_request(URLRequest::New()); + url_request->url = request.url().string().utf8(); + url_request->method = request.httpMethod().utf8(); + + HeaderFlattener flattener; + request.visitHTTPHeaderFields(&flattener); + url_request->headers = flattener.GetBuffer().Pass(); + + AddRequestBody(url_request.get(), request); + + return url_request.Pass(); +} + +} // namespace mojo + diff --git a/mojo/services/html_viewer/blink_url_request_type_converters.h b/mojo/services/html_viewer/blink_url_request_type_converters.h new file mode 100644 index 0000000000000..21c34920fd19e --- /dev/null +++ b/mojo/services/html_viewer/blink_url_request_type_converters.h @@ -0,0 +1,24 @@ +// 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_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ +#define MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ + +#include "mojo/services/public/interfaces/network/url_loader.mojom.h" + +namespace blink { +class WebURLRequest; +} + +namespace mojo { + +template<> +class TypeConverter { + public: + static URLRequestPtr ConvertFrom(const blink::WebURLRequest& request); +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_HTML_VIEWER_BLINK_URL_REQUEST_TYPE_CONVERTERS_H_ diff --git a/mojo/services/html_viewer/html_document_view.cc b/mojo/services/html_viewer/html_document_view.cc index 812493aa71086..7751aa4e07dc7 100644 --- a/mojo/services/html_viewer/html_document_view.cc +++ b/mojo/services/html_viewer/html_document_view.cc @@ -11,11 +11,10 @@ #include "base/thread_task_runner_handle.h" #include "mojo/public/cpp/system/data_pipe.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/webstoragenamespace_impl.h" #include "mojo/services/html_viewer/weburlloader_impl.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view.h" -#include "mojo/services/public/cpp/view_manager/view_observer.h" #include "skia/ext/refptr.h" #include "third_party/WebKit/public/platform/Platform.h" #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" @@ -34,83 +33,6 @@ namespace mojo { namespace { -// Ripped from web_url_loader_impl.cc. Why is everything so complicated? -class HeaderFlattener : public blink::WebHTTPHeaderVisitor { - public: - HeaderFlattener() : has_accept_header_(false) {} - - virtual void visitHeader(const blink::WebString& name, - const blink::WebString& value) { - // Headers are latin1. - const std::string& name_latin1 = name.latin1(); - const std::string& value_latin1 = value.latin1(); - - // Skip over referrer headers found in the header map because we already - // pulled it out as a separate parameter. - if (LowerCaseEqualsASCII(name_latin1, "referer")) - return; - - if (LowerCaseEqualsASCII(name_latin1, "accept")) - has_accept_header_ = true; - - buffer_.push_back(name_latin1 + ": " + value_latin1); - } - - Array GetBuffer() { - // In some cases, WebKit doesn't add an Accept header, but not having the - // header confuses some web servers. See bug 808613. - if (!has_accept_header_) { - buffer_.push_back("Accept: */*"); - has_accept_header_ = true; - } - return buffer_.Pass(); - } - - private: - Array buffer_; - bool has_accept_header_; -}; - -void AddRequestBody(NavigationDetails* nav_details, - const blink::WebURLRequest& request) { - if (request.httpBody().isNull()) - return; - - uint32_t i = 0; - blink::WebHTTPBody::Element element; - while (request.httpBody().elementAt(i++, element)) { - switch (element.type) { - case blink::WebHTTPBody::Element::TypeData: - if (!element.data.isEmpty()) { - // WebKit sometimes gives up empty data to append. These aren't - // necessary so we just optimize those out here. - uint32_t num_bytes = static_cast(element.data.size()); - MojoCreateDataPipeOptions options; - options.struct_size = sizeof(MojoCreateDataPipeOptions); - options.flags = MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE; - options.element_num_bytes = 1; - options.capacity_num_bytes = num_bytes; - DataPipe data_pipe(options); - nav_details->request->body.push_back( - data_pipe.consumer_handle.Pass()); - WriteDataRaw(data_pipe.producer_handle.get(), - element.data.data(), - &num_bytes, - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE); - } - break; - case blink::WebHTTPBody::Element::TypeFile: - case blink::WebHTTPBody::Element::TypeFileSystemURL: - case blink::WebHTTPBody::Element::TypeBlob: - // TODO(mpcomplete): handle these. - NOTIMPLEMENTED(); - break; - default: - NOTREACHED(); - } - } -} - void ConfigureSettings(blink::WebSettings* settings) { settings->setAcceleratedCompositingEnabled(false); settings->setCookieEnabled(true); @@ -159,27 +81,23 @@ bool CanNavigateLocally(blink::WebFrame* frame, HTMLDocumentView::HTMLDocumentView(ServiceProvider* service_provider, ViewManager* view_manager) : view_manager_(view_manager), - view_(View::Create(view_manager_)), web_view_(NULL), root_(NULL), repaint_pending_(false), navigator_host_(service_provider), weak_factory_(this) { - view_->AddObserver(this); } HTMLDocumentView::~HTMLDocumentView() { - view_->RemoveObserver(this); if (web_view_) web_view_->close(); if (root_) root_->RemoveObserver(this); } -void HTMLDocumentView::AttachToNode(Node* node) { - root_ = node; - root_->SetActiveView(view_); - view_->SetColor(SK_ColorCYAN); // Dummy background color. +void HTMLDocumentView::AttachToView(View* view) { + root_ = view; + root_->SetColor(SK_ColorCYAN); // Dummy background color. web_view_ = blink::WebView::create(this); ConfigureSettings(web_view_->settings()); @@ -241,17 +159,10 @@ blink::WebNavigationPolicy HTMLDocumentView::decidePolicyForNavigation( return default_policy; NavigationDetailsPtr nav_details(NavigationDetails::New()); - nav_details->request->url = request.url().string().utf8(); - nav_details->request->method = request.httpMethod().utf8(); - - HeaderFlattener flattener; - request.visitHTTPHeaderFields(&flattener); - nav_details->request->headers = flattener.GetBuffer().Pass(); - - AddRequestBody(nav_details.get(), request); + nav_details->request = URLRequest::From(request); navigator_host_->RequestNavigate( - view_->node()->id(), + root_->id(), WebNavigationPolicyToNavigationTarget(default_policy), nav_details.Pass()); @@ -268,32 +179,31 @@ void HTMLDocumentView::didAddMessageToConsole( void HTMLDocumentView::didNavigateWithinPage( blink::WebLocalFrame* frame, const blink::WebHistoryItem& history_item, blink::WebHistoryCommitType commit_type) { - navigator_host_->DidNavigateLocally(view_->node()->id(), + navigator_host_->DidNavigateLocally(root_->id(), history_item.urlString().utf8()); } -void HTMLDocumentView::OnViewInputEvent(View* view, - const EventPtr& event) { - scoped_ptr web_event = - TypeConverter >::ConvertTo( - event); - if (web_event) - web_view_->handleInputEvent(*web_event); -} - -void HTMLDocumentView::OnNodeBoundsChanged(Node* node, +void HTMLDocumentView::OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { - DCHECK_EQ(node, root_); - web_view_->resize(node->bounds().size()); + DCHECK_EQ(view, root_); + web_view_->resize(view->bounds().size()); } -void HTMLDocumentView::OnNodeDestroyed(Node* node) { - DCHECK_EQ(node, root_); - node->RemoveObserver(this); +void HTMLDocumentView::OnViewDestroyed(View* view) { + DCHECK_EQ(view, root_); + view->RemoveObserver(this); root_ = NULL; } +void HTMLDocumentView::OnViewInputEvent(View* view, const EventPtr& event) { + scoped_ptr web_event = + TypeConverter >::ConvertTo( + event); + if (web_event) + web_view_->handleInputEvent(*web_event); +} + void HTMLDocumentView::Repaint() { repaint_pending_ = false; @@ -308,7 +218,7 @@ void HTMLDocumentView::Repaint() { web_view_->paint(canvas.get(), gfx::Rect(0, 0, width, height)); - view_->SetContents(canvas->getDevice()->accessBitmap(false)); + root_->SetContents(canvas->getDevice()->accessBitmap(false)); } } // namespace mojo diff --git a/mojo/services/html_viewer/html_document_view.h b/mojo/services/html_viewer/html_document_view.h index e2972c10ec893..af315bb75fb25 100644 --- a/mojo/services/html_viewer/html_document_view.h +++ b/mojo/services/html_viewer/html_document_view.h @@ -8,7 +8,6 @@ #include "base/compiler_specific.h" #include "base/memory/weak_ptr.h" #include "mojo/public/cpp/application/lazy_interface_ptr.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/view_observer.h" #include "mojo/services/public/interfaces/navigation/navigation.mojom.h" #include "mojo/services/public/interfaces/network/url_loader.mojom.h" @@ -17,21 +16,19 @@ namespace mojo { -class Node; class ViewManager; class View; // A view for a single HTML document. class HTMLDocumentView : public blink::WebViewClient, public blink::WebFrameClient, - public ViewObserver, - public NodeObserver { + public ViewObserver { public: HTMLDocumentView(ServiceProvider* service_provider, ViewManager* view_manager); virtual ~HTMLDocumentView(); - void AttachToNode(Node* node); + void AttachToView(View* view); void Load(URLResponsePtr response); @@ -60,20 +57,18 @@ class HTMLDocumentView : public blink::WebViewClient, blink::WebHistoryCommitType commit_type); // ViewObserver methods: - virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE; - - // NodeObserver methods: - virtual void OnNodeBoundsChanged(Node* node, + virtual void OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE; - virtual void OnNodeDestroyed(Node* node) OVERRIDE; + virtual void OnViewDestroyed(View* view) OVERRIDE; + virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE; void Repaint(); ViewManager* view_manager_; View* view_; blink::WebView* web_view_; - Node* root_; + View* root_; bool repaint_pending_; LazyInterfacePtr navigator_host_; diff --git a/mojo/services/html_viewer/html_viewer.cc b/mojo/services/html_viewer/html_viewer.cc index 9ab3b893ab634..014f2381c4d8c 100644 --- a/mojo/services/html_viewer/html_viewer.cc +++ b/mojo/services/html_viewer/html_viewer.cc @@ -9,7 +9,6 @@ #include "mojo/public/cpp/application/interface_factory_impl.h" #include "mojo/services/html_viewer/blink_platform_impl.h" #include "mojo/services/html_viewer/html_document_view.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/types.h" #include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" @@ -30,7 +29,7 @@ class NavigatorImpl : public InterfaceImpl { private: // Overridden from Navigator: virtual void Navigate( - uint32_t node_id, + uint32_t view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) OVERRIDE; @@ -73,14 +72,14 @@ class HTMLViewer : public ApplicationDelegate, public ViewManagerDelegate { // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { document_view_ = new HTMLDocumentView( application_impl_->ConnectToApplication("mojo://mojo_window_manager/")-> GetServiceProvider(), view_manager); - document_view_->AttachToNode(root); + document_view_->AttachToView(root); MaybeLoad(); } virtual void OnViewManagerDisconnected(ViewManager* view_manager) OVERRIDE { @@ -105,7 +104,7 @@ class HTMLViewer : public ApplicationDelegate, public ViewManagerDelegate { }; void NavigatorImpl::Navigate( - uint32_t node_id, + uint32_t view_id, NavigationDetailsPtr navigation_details, ResponseDetailsPtr response_details) { viewer_->Load(response_details.Pass()); diff --git a/mojo/services/html_viewer/weburlloader_impl.cc b/mojo/services/html_viewer/weburlloader_impl.cc index 16f5950c2a6cf..39afbc2aadfb4 100644 --- a/mojo/services/html_viewer/weburlloader_impl.cc +++ b/mojo/services/html_viewer/weburlloader_impl.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "base/thread_task_runner_handle.h" #include "mojo/common/common_type_converters.h" +#include "mojo/services/html_viewer/blink_url_request_type_converters.h" #include "mojo/services/public/interfaces/network/network_service.mojom.h" #include "net/base/net_errors.h" #include "third_party/WebKit/public/platform/WebURLError.h" @@ -65,11 +66,8 @@ void WebURLLoaderImpl::loadAsynchronously(const blink::WebURLRequest& request, client_ = client; url_ = request.url(); - URLRequestPtr url_request(URLRequest::New()); - url_request->url = String::From(url_); - url_request->method = request.httpMethod().utf8(); + URLRequestPtr url_request = URLRequest::From(request); url_request->auto_follow_redirects = false; - // TODO(darin): Copy other fields. if (request.extraData()) { WebURLRequestExtraData* extra_data = diff --git a/mojo/services/native_viewport/BUILD.gn b/mojo/services/native_viewport/BUILD.gn index 31113ce7f9ccd..b1089d1ccd6c6 100644 --- a/mojo/services/native_viewport/BUILD.gn +++ b/mojo/services/native_viewport/BUILD.gn @@ -3,8 +3,9 @@ # found in the LICENSE file. import("//build/config/ui.gni") +import("//mojo/system.gni") -component("native_viewport") { +static_library("native_viewport") { output_name = "mojo_native_viewport" deps = [ @@ -22,19 +23,18 @@ component("native_viewport") { "//mojo/services/public/interfaces/native_viewport", ] - defines = [ "MOJO_NATIVE_VIEWPORT_IMPLEMENTATION" ] - sources = [ - "native_viewport.h", - "native_viewport_android.cc", - "native_viewport_mac.mm", - "native_viewport_service.cc", - "native_viewport_service.h", - "native_viewport_win.cc", + "native_viewport_impl.cc", + "native_viewport_impl.h", + "platform_viewport.h", + "platform_viewport_android.cc", + "platform_viewport_android.h", + "platform_viewport_mac.mm", + "platform_viewport_win.cc", ] if (is_ios) { - sources += [ "native_viewport_stub.cc" ] + sources += [ "platform_viewport_stub.cc" ] } if (is_android) { @@ -49,15 +49,11 @@ component("native_viewport") { } if (use_x11) { - sources += [ "native_viewport_x11.cc" ] + sources += [ "platform_viewport_x11.cc" ] deps += [ "//ui/platform_window/x11" ] } if (use_ozone) { - sources += [ "native_viewport_ozone.cc" ] - } - - if (is_component_build) { - deps += [ "//mojo/system" ] + sources += [ "platform_viewport_ozone.cc" ] } } diff --git a/mojo/services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java b/mojo/services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java deleted file mode 100644 index 9ec2fc0a6bc3f..0000000000000 --- a/mojo/services/native_viewport/android/src/org/chromium/mojo/NativeViewportAndroid.java +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.mojo; - -import android.app.Activity; -import android.content.Context; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.SurfaceHolder; -import android.view.SurfaceView; - -import org.chromium.base.CalledByNative; -import org.chromium.base.JNINamespace; - -/** - * Exposes SurfaceView to native code. - */ -@JNINamespace("mojo::services") -public class NativeViewportAndroid extends SurfaceView { - - private long mNativeMojoViewport; - private final SurfaceHolder.Callback mSurfaceCallback; - - @SuppressWarnings("unused") - @CalledByNative - public static void createForActivity(Activity activity, long nativeViewport) { - activity.setContentView(new NativeViewportAndroid(activity, nativeViewport)); - } - - public NativeViewportAndroid(Context context, long nativeViewport) { - super(context); - - mNativeMojoViewport = nativeViewport; - assert mNativeMojoViewport != 0; - - mSurfaceCallback = new SurfaceHolder.Callback() { - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - assert mNativeMojoViewport != 0; - nativeSurfaceSetSize(mNativeMojoViewport, width, height); - } - - @Override - public void surfaceCreated(SurfaceHolder holder) { - assert mNativeMojoViewport != 0; - nativeSurfaceCreated(mNativeMojoViewport, holder.getSurface()); - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - assert mNativeMojoViewport != 0; - nativeSurfaceDestroyed(mNativeMojoViewport); - } - }; - getHolder().addCallback(mSurfaceCallback); - - } - - // TODO(abarth): Someone needs to call destroy at some point. - public void destroy() { - getHolder().removeCallback(mSurfaceCallback); - nativeDestroy(mNativeMojoViewport); - mNativeMojoViewport = 0; - } - - @Override - public boolean onTouchEvent(MotionEvent event) { - return nativeTouchEvent(mNativeMojoViewport, - event.getPointerId(0), - event.getAction(), - event.getX(), event.getY(), - event.getEventTime()); - } - - private static native void nativeDestroy(long nativeNativeViewportAndroid); - private static native void nativeSurfaceCreated( - long nativeNativeViewportAndroid, Surface surface); - private static native void nativeSurfaceDestroyed( - long nativeNativeViewportAndroid); - private static native void nativeSurfaceSetSize( - long nativeNativeViewportAndroid, - int width, int height); - private static native boolean nativeTouchEvent( - long nativeNativeViewportAndroid, - int pointerId, - int action, - float x, float y, - long timeMs); -}; diff --git a/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java b/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java new file mode 100644 index 0000000000000..c3f8075ceef80 --- /dev/null +++ b/mojo/services/native_viewport/android/src/org/chromium/mojo/PlatformViewportAndroid.java @@ -0,0 +1,91 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.mojo; + +import android.app.Activity; +import android.content.Context; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +import org.chromium.base.CalledByNative; +import org.chromium.base.JNINamespace; + +/** + * Exposes SurfaceView to native code. + */ +@JNINamespace("mojo") +public class PlatformViewportAndroid extends SurfaceView { + + private long mNativeMojoViewport; + private final SurfaceHolder.Callback mSurfaceCallback; + + @SuppressWarnings("unused") + @CalledByNative + public static void createForActivity(Activity activity, long nativeViewport) { + activity.setContentView(new PlatformViewportAndroid(activity, nativeViewport)); + } + + public PlatformViewportAndroid(Context context, long nativeViewport) { + super(context); + + mNativeMojoViewport = nativeViewport; + assert mNativeMojoViewport != 0; + + mSurfaceCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + assert mNativeMojoViewport != 0; + nativeSurfaceSetSize(mNativeMojoViewport, width, height); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + assert mNativeMojoViewport != 0; + nativeSurfaceCreated(mNativeMojoViewport, holder.getSurface()); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + assert mNativeMojoViewport != 0; + nativeSurfaceDestroyed(mNativeMojoViewport); + } + }; + getHolder().addCallback(mSurfaceCallback); + + } + + // TODO(abarth): Someone needs to call destroy at some point. + public void destroy() { + getHolder().removeCallback(mSurfaceCallback); + nativeDestroy(mNativeMojoViewport); + mNativeMojoViewport = 0; + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + return nativeTouchEvent(mNativeMojoViewport, + event.getPointerId(0), + event.getAction(), + event.getX(), event.getY(), + event.getEventTime()); + } + + private static native void nativeDestroy(long nativePlatformViewportAndroid); + private static native void nativeSurfaceCreated( + long nativePlatformViewportAndroid, Surface surface); + private static native void nativeSurfaceDestroyed( + long nativePlatformViewportAndroid); + private static native void nativeSurfaceSetSize( + long nativePlatformViewportAndroid, + int width, int height); + private static native boolean nativeTouchEvent( + long nativePlatformViewportAndroid, + int pointerId, + int action, + float x, float y, + long timeMs); +}; diff --git a/mojo/services/native_viewport/native_viewport.h b/mojo/services/native_viewport/native_viewport.h deleted file mode 100644 index ae2b0610f1a62..0000000000000 --- a/mojo/services/native_viewport/native_viewport.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_ -#define MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_ - -#include "base/memory/scoped_ptr.h" -#include "mojo/services/native_viewport/native_viewport_export.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/size.h" - -namespace gfx { -class Rect; -} - -namespace ui { -class Event; -} - -namespace mojo { -namespace services { - -class NativeViewportDelegate { - public: - virtual ~NativeViewportDelegate() {} - - virtual void OnBoundsChanged(const gfx::Rect& size) = 0; - virtual void OnAcceleratedWidgetAvailable(gfx::AcceleratedWidget widget) = 0; - virtual bool OnEvent(ui::Event* ui_event) = 0; - virtual void OnDestroyed() = 0; -}; - -// Encapsulation of platform-specific Viewport. -// TODO(abarth): Rename this class so that it doesn't conflict with the name of -// the service. -class NativeViewport { - public: - virtual ~NativeViewport() {} - - virtual void Init(const gfx::Rect& bounds) = 0; - virtual void Show() = 0; - virtual void Hide() = 0; - virtual void Close() = 0; - virtual gfx::Size GetSize() = 0; - virtual void SetBounds(const gfx::Rect& bounds) = 0; - - virtual void SetCapture() = 0; - virtual void ReleaseCapture() = 0; - - static scoped_ptr Create(NativeViewportDelegate* delegate); -}; - -} // namespace services -} // namespace mojo - -#endif // MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_H_ diff --git a/mojo/services/native_viewport/native_viewport_android.cc b/mojo/services/native_viewport/native_viewport_android.cc deleted file mode 100644 index b9d9baaee9827..0000000000000 --- a/mojo/services/native_viewport/native_viewport_android.cc +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/native_viewport/native_viewport_android.h" - -#include -#include - -#include "base/android/jni_android.h" -#include "jni/NativeViewportAndroid_jni.h" -#include "ui/events/event.h" -#include "ui/gfx/point.h" - -namespace mojo { -namespace services { - -ui::EventType MotionEventActionToEventType(jint action) { - switch (action) { - case AMOTION_EVENT_ACTION_DOWN: - return ui::ET_TOUCH_PRESSED; - case AMOTION_EVENT_ACTION_MOVE: - return ui::ET_TOUCH_MOVED; - case AMOTION_EVENT_ACTION_UP: - return ui::ET_TOUCH_RELEASED; - default: - NOTREACHED(); - } - return ui::ET_UNKNOWN; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewportAndroid, public: - -// static -bool NativeViewportAndroid::Register(JNIEnv* env) { - return RegisterNativesImpl(env); -} - -NativeViewportAndroid::NativeViewportAndroid(NativeViewportDelegate* delegate) - : delegate_(delegate), - window_(NULL), - id_generator_(0), - weak_factory_(this) { -} - -NativeViewportAndroid::~NativeViewportAndroid() { - if (window_) - ReleaseWindow(); -} - -void NativeViewportAndroid::Destroy(JNIEnv* env, jobject obj) { - delegate_->OnDestroyed(); -} - -void NativeViewportAndroid::SurfaceCreated(JNIEnv* env, - jobject obj, - jobject jsurface) { - base::android::ScopedJavaLocalRef protector(env, jsurface); - // Note: This ensures that any local references used by - // ANativeWindow_fromSurface are released immediately. This is needed as a - // workaround for https://code.google.com/p/android/issues/detail?id=68174 - { - base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env); - window_ = ANativeWindow_fromSurface(env, jsurface); - } - delegate_->OnAcceleratedWidgetAvailable(window_); -} - -void NativeViewportAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) { - DCHECK(window_); - ReleaseWindow(); -} - -void NativeViewportAndroid::SurfaceSetSize(JNIEnv* env, jobject obj, - jint width, jint height) { - bounds_ = gfx::Rect(width, height); - delegate_->OnBoundsChanged(bounds_); -} - -bool NativeViewportAndroid::TouchEvent(JNIEnv* env, jobject obj, - jint pointer_id, - jint action, - jfloat x, jfloat y, - jlong time_ms) { - gfx::Point location(static_cast(x), static_cast(y)); - ui::TouchEvent event(MotionEventActionToEventType(action), location, - id_generator_.GetGeneratedID(pointer_id), - base::TimeDelta::FromMilliseconds(time_ms)); - // TODO(beng): handle multiple touch-points. - delegate_->OnEvent(&event); - if (action == ui::ET_TOUCH_RELEASED) - id_generator_.ReleaseNumber(pointer_id); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewportAndroid, NativeViewport implementation: - -void NativeViewportAndroid::Init(const gfx::Rect& bounds) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_NativeViewportAndroid_createForActivity( - env, - base::android::GetApplicationContext(), - reinterpret_cast(this)); -} - -void NativeViewportAndroid::Show() { - // Nothing to do. View is created visible. -} - -void NativeViewportAndroid::Hide() { - // Nothing to do. View is always visible. -} - -void NativeViewportAndroid::Close() { - // TODO(beng): close activity containing MojoView? - - // TODO(beng): perform this in response to view destruction. - delegate_->OnDestroyed(); -} - -gfx::Size NativeViewportAndroid::GetSize() { - return bounds_.size(); -} - -void NativeViewportAndroid::SetBounds(const gfx::Rect& bounds) { - NOTIMPLEMENTED(); -} - -void NativeViewportAndroid::SetCapture() { - NOTIMPLEMENTED(); -} - -void NativeViewportAndroid::ReleaseCapture() { - NOTIMPLEMENTED(); -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewportAndroid, private: - -void NativeViewportAndroid::ReleaseWindow() { - ANativeWindow_release(window_); - window_ = NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// NativeViewport, public: - -// static -scoped_ptr NativeViewport::Create( - NativeViewportDelegate* delegate) { - return scoped_ptr(new NativeViewportAndroid(delegate)).Pass(); -} - -} // namespace services -} // namespace mojo diff --git a/mojo/services/native_viewport/native_viewport_android.h b/mojo/services/native_viewport/native_viewport_android.h deleted file mode 100644 index e9c8677366592..0000000000000 --- a/mojo/services/native_viewport/native_viewport_android.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_ -#define MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_ - -#include "base/android/jni_weak_ref.h" -#include "base/android/scoped_java_ref.h" -#include "base/memory/weak_ptr.h" -#include "mojo/services/native_viewport/native_viewport.h" -#include "mojo/services/native_viewport/native_viewport_export.h" -#include "ui/events/event_constants.h" -#include "ui/gfx/rect.h" -#include "ui/gfx/sequential_id_generator.h" -#include "ui/gfx/size.h" - -namespace gpu { -class GLInProcessContext; -} - -struct ANativeWindow; - -namespace mojo { -namespace services { - -class MOJO_NATIVE_VIEWPORT_EXPORT NativeViewportAndroid - : public NativeViewport { - public: - static MOJO_NATIVE_VIEWPORT_EXPORT bool Register(JNIEnv* env); - - explicit NativeViewportAndroid(NativeViewportDelegate* delegate); - virtual ~NativeViewportAndroid(); - - void Destroy(JNIEnv* env, jobject obj); - void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface); - void SurfaceDestroyed(JNIEnv* env, jobject obj); - void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height); - bool TouchEvent(JNIEnv* env, jobject obj, jint pointer_id, jint action, - jfloat x, jfloat y, jlong time_ms); - - private: - // Overridden from NativeViewport: - virtual void Init(const gfx::Rect& bounds) OVERRIDE; - virtual void Show() OVERRIDE; - virtual void Hide() OVERRIDE; - virtual void Close() OVERRIDE; - virtual gfx::Size GetSize() OVERRIDE; - virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; - virtual void SetCapture() OVERRIDE; - virtual void ReleaseCapture() OVERRIDE; - - void ReleaseWindow(); - - NativeViewportDelegate* delegate_; - ANativeWindow* window_; - gfx::Rect bounds_; - ui::SequentialIDGenerator id_generator_; - - base::WeakPtrFactory weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewportAndroid); -}; - - -} // namespace services -} // namespace mojo - -#endif // MOJO_SERVICES_NATIVE_VIEWPORT_NATIVE_VIEWPORT_ANDROID_H_ diff --git a/mojo/services/native_viewport/native_viewport_export.h b/mojo/services/native_viewport/native_viewport_export.h deleted file mode 100644 index 4870c566f9d91..0000000000000 --- a/mojo/services/native_viewport/native_viewport_export.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_ -#define MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_ - -#if defined(COMPONENT_BUILD) - -#if defined(WIN32) - -#if defined(MOJO_NATIVE_VIEWPORT_IMPLEMENTATION) -#define MOJO_NATIVE_VIEWPORT_EXPORT __declspec(dllexport) -#else -#define MOJO_NATIVE_VIEWPORT_EXPORT __declspec(dllimport) -#endif - -#else // !defined(WIN32) - -#if defined(MOJO_NATIVE_VIEWPORT_IMPLEMENTATION) -#define MOJO_NATIVE_VIEWPORT_EXPORT __attribute__((visibility("default"))) -#else -#define MOJO_NATIVE_VIEWPORT_EXPORT -#endif - -#endif // defined(WIN32) - -#else // !defined(COMPONENT_BUILD) -#define MOJO_NATIVE_VIEWPORT_EXPORT -#endif - -#endif // MOJO_SERVICES_NATIVE_VIEWPORT_EXPORT_H_ diff --git a/mojo/services/native_viewport/native_viewport_impl.cc b/mojo/services/native_viewport/native_viewport_impl.cc new file mode 100644 index 0000000000000..8755dfd1c74b8 --- /dev/null +++ b/mojo/services/native_viewport/native_viewport_impl.cc @@ -0,0 +1,138 @@ +// 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/services/native_viewport/native_viewport_impl.h" + +#include "base/macros.h" +#include "base/message_loop/message_loop.h" +#include "base/time/time.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/interface_factory.h" +#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" +#include "mojo/services/public/cpp/input_events/input_events_type_converters.h" +#include "ui/events/event.h" + +namespace mojo { +namespace { + +bool IsRateLimitedEventType(ui::Event* event) { + return event->type() == ui::ET_MOUSE_MOVED || + event->type() == ui::ET_MOUSE_DRAGGED || + event->type() == ui::ET_TOUCH_MOVED; +} + +} // namespace + +NativeViewportImpl::NativeViewportImpl() + : widget_(gfx::kNullAcceleratedWidget), + waiting_for_event_ack_(false), + weak_factory_(this) {} + +NativeViewportImpl::~NativeViewportImpl() { + // Destroy the NativeViewport early on as it may call us back during + // destruction and we want to be in a known state. + platform_viewport_.reset(); +} + +void NativeViewportImpl::Create(RectPtr bounds) { + platform_viewport_ = PlatformViewport::Create(this); + platform_viewport_->Init(bounds.To()); + client()->OnCreated(); + OnBoundsChanged(bounds.To()); +} + +void NativeViewportImpl::Show() { + platform_viewport_->Show(); +} + +void NativeViewportImpl::Hide() { + platform_viewport_->Hide(); +} + +void NativeViewportImpl::Close() { + command_buffer_.reset(); + DCHECK(platform_viewport_); + platform_viewport_->Close(); +} + +void NativeViewportImpl::SetBounds(RectPtr bounds) { + platform_viewport_->SetBounds(bounds.To()); +} + +void NativeViewportImpl::CreateGLES2Context( + InterfaceRequest command_buffer_request) { + if (command_buffer_ || command_buffer_request_.is_pending()) { + LOG(ERROR) << "Can't create multiple contexts on a NativeViewport"; + return; + } + command_buffer_request_ = command_buffer_request.Pass(); + CreateCommandBufferIfNeeded(); +} + +void NativeViewportImpl::OnBoundsChanged(const gfx::Rect& bounds) { + CreateCommandBufferIfNeeded(); + client()->OnBoundsChanged(Rect::From(bounds)); +} + +void NativeViewportImpl::OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) { + widget_ = widget; + CreateCommandBufferIfNeeded(); +} + +bool NativeViewportImpl::OnEvent(ui::Event* ui_event) { + // Must not return early before updating capture. + switch (ui_event->type()) { + case ui::ET_MOUSE_PRESSED: + case ui::ET_TOUCH_PRESSED: + platform_viewport_->SetCapture(); + break; + case ui::ET_MOUSE_RELEASED: + case ui::ET_TOUCH_RELEASED: + platform_viewport_->ReleaseCapture(); + break; + default: + break; + } + + if (waiting_for_event_ack_ && IsRateLimitedEventType(ui_event)) + return false; + + client()->OnEvent( + TypeConverter::ConvertFrom(*ui_event), + base::Bind(&NativeViewportImpl::AckEvent, + weak_factory_.GetWeakPtr())); + waiting_for_event_ack_ = true; + return false; +} + +void NativeViewportImpl::OnDestroyed() { + client()->OnDestroyed(base::Bind(&NativeViewportImpl::AckDestroyed, + base::Unretained(this))); +} + +void NativeViewportImpl::AckEvent() { + waiting_for_event_ack_ = false; +} + +void NativeViewportImpl::CreateCommandBufferIfNeeded() { + if (!command_buffer_request_.is_pending()) + return; + DCHECK(!command_buffer_.get()); + if (widget_ == gfx::kNullAcceleratedWidget) + return; + gfx::Size size = platform_viewport_->GetSize(); + if (size.IsEmpty()) + return; + command_buffer_.reset( + new CommandBufferImpl(widget_, platform_viewport_->GetSize())); + WeakBindToRequest(command_buffer_.get(), &command_buffer_request_); +} + +void NativeViewportImpl::AckDestroyed() { + command_buffer_.reset(); +} + +} // namespace mojo + diff --git a/mojo/services/native_viewport/native_viewport_impl.h b/mojo/services/native_viewport/native_viewport_impl.h new file mode 100644 index 0000000000000..5d26da582511a --- /dev/null +++ b/mojo/services/native_viewport/native_viewport_impl.h @@ -0,0 +1,57 @@ +// 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_NATIVE_VIEWPORT_IMPL_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_ + +#include "base/memory/weak_ptr.h" +#include "mojo/services/gles2/command_buffer_impl.h" +#include "mojo/services/native_viewport/platform_viewport.h" +#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" + +namespace ui { +class Event; +} + +namespace mojo { + +class NativeViewportImpl : public InterfaceImpl, + public PlatformViewport::Delegate { + public: + NativeViewportImpl(); + virtual ~NativeViewportImpl(); + + // InterfaceImpl implementation. + virtual void Create(RectPtr bounds) OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void Close() OVERRIDE; + virtual void SetBounds(RectPtr bounds) OVERRIDE; + virtual void CreateGLES2Context( + InterfaceRequest command_buffer_request) OVERRIDE; + + // PlatformViewport::Delegate implementation. + virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE; + virtual void OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) OVERRIDE; + virtual bool OnEvent(ui::Event* ui_event) OVERRIDE; + virtual void OnDestroyed() OVERRIDE; + + void AckEvent(); + void CreateCommandBufferIfNeeded(); + + private: + void AckDestroyed(); + + gfx::AcceleratedWidget widget_; + scoped_ptr platform_viewport_; + InterfaceRequest command_buffer_request_; + scoped_ptr command_buffer_; + bool waiting_for_event_ack_; + base::WeakPtrFactory weak_factory_; +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_IMPL_H_ diff --git a/mojo/services/native_viewport/native_viewport_mac.mm b/mojo/services/native_viewport/native_viewport_mac.mm deleted file mode 100644 index 4d4a55f26ba27..0000000000000 --- a/mojo/services/native_viewport/native_viewport_mac.mm +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/native_viewport/native_viewport.h" - -#import -#import -#import - -#include "base/bind.h" -#include "ui/gfx/rect.h" - -namespace mojo { -namespace services { - -class NativeViewportMac : public NativeViewport { - public: - NativeViewportMac(NativeViewportDelegate* delegate) - : delegate_(delegate), - window_(nil) { - } - - virtual ~NativeViewportMac() { - [window_ orderOut:nil]; - [window_ close]; - } - - private: - // Overridden from NativeViewport: - virtual void Init(const gfx::Rect& bounds) OVERRIDE { - [NSApplication sharedApplication]; - - rect_ = bounds; - window_ = [[NSWindow alloc] - initWithContentRect:NSRectFromCGRect(rect_.ToCGRect()) - styleMask:NSTitledWindowMask - backing:NSBackingStoreBuffered - defer:NO]; - delegate_->OnAcceleratedWidgetAvailable([window_ contentView]); - delegate_->OnBoundsChanged(rect_); - } - - virtual void Show() OVERRIDE { - [window_ orderFront:nil]; - } - - virtual void Hide() OVERRIDE { - [window_ orderOut:nil]; - } - - virtual void Close() OVERRIDE { - // TODO(beng): perform this in response to NSWindow destruction. - delegate_->OnDestroyed(); - } - - virtual gfx::Size GetSize() OVERRIDE { - return rect_.size(); - } - - virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { - NOTIMPLEMENTED(); - } - - virtual void SetCapture() OVERRIDE { - NOTIMPLEMENTED(); - } - - virtual void ReleaseCapture() OVERRIDE { - NOTIMPLEMENTED(); - } - - NativeViewportDelegate* delegate_; - NSWindow* window_; - gfx::Rect rect_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewportMac); -}; - -// static -scoped_ptr NativeViewport::Create( - NativeViewportDelegate* delegate) { - return scoped_ptr(new NativeViewportMac(delegate)).Pass(); -} - -} // namespace services -} // namespace mojo diff --git a/mojo/services/native_viewport/native_viewport_ozone.cc b/mojo/services/native_viewport/native_viewport_ozone.cc deleted file mode 100644 index fa5648f5a5f28..0000000000000 --- a/mojo/services/native_viewport/native_viewport_ozone.cc +++ /dev/null @@ -1,99 +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/services/native_viewport/native_viewport.h" - -#include "ui/events/event.h" -#include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/events/platform/platform_event_source.h" -#include "ui/ozone/public/cursor_factory_ozone.h" -#include "ui/ozone/public/event_factory_ozone.h" -#include "ui/ozone/public/ozone_platform.h" -#include "ui/ozone/public/surface_factory_ozone.h" -#include "ui/platform_window/platform_window.h" -#include "ui/platform_window/platform_window_delegate.h" - -namespace mojo { -namespace services { - -// TODO(spang): Deduplicate with NativeViewportX11.. but there's a hack -// in there that prevents this. -class NativeViewportOzone : public NativeViewport, - public ui::PlatformWindowDelegate { - public: - explicit NativeViewportOzone(NativeViewportDelegate* delegate) - : delegate_(delegate) { - ui::OzonePlatform::InitializeForUI(); - } - - virtual ~NativeViewportOzone() { - // Destroy the platform-window while |this| is still alive. - platform_window_.reset(); - } - - private: - // Overridden from NativeViewport: - virtual void Init(const gfx::Rect& bounds) OVERRIDE { - platform_window_ = - ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds); - } - - virtual void Show() OVERRIDE { platform_window_->Show(); } - - virtual void Hide() OVERRIDE { platform_window_->Hide(); } - - virtual void Close() OVERRIDE { platform_window_->Close(); } - - virtual gfx::Size GetSize() OVERRIDE { - return platform_window_->GetBounds().size(); - } - - virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { - platform_window_->SetBounds(bounds); - } - - virtual void SetCapture() OVERRIDE { platform_window_->SetCapture(); } - - virtual void ReleaseCapture() OVERRIDE { platform_window_->ReleaseCapture(); } - - // ui::PlatformWindowDelegate: - virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE { - delegate_->OnBoundsChanged(new_bounds); - } - - virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {} - - virtual void DispatchEvent(ui::Event* event) OVERRIDE { - delegate_->OnEvent(event); - } - - virtual void OnCloseRequest() OVERRIDE { platform_window_->Close(); } - - virtual void OnClosed() OVERRIDE { delegate_->OnDestroyed(); } - - virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {} - - virtual void OnLostCapture() OVERRIDE {} - - virtual void OnAcceleratedWidgetAvailable( - gfx::AcceleratedWidget widget) OVERRIDE { - delegate_->OnAcceleratedWidgetAvailable(widget); - } - - virtual void OnActivationChanged(bool active) OVERRIDE {} - - scoped_ptr platform_window_; - NativeViewportDelegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewportOzone); -}; - -// static -scoped_ptr NativeViewport::Create( - NativeViewportDelegate* delegate) { - return scoped_ptr(new NativeViewportOzone(delegate)).Pass(); -} - -} // namespace services -} // namespace mojo diff --git a/mojo/services/native_viewport/native_viewport_service.cc b/mojo/services/native_viewport/native_viewport_service.cc deleted file mode 100644 index 2903de0b441fa..0000000000000 --- a/mojo/services/native_viewport/native_viewport_service.cc +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/native_viewport/native_viewport_service.h" - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "base/time/time.h" -#include "mojo/public/cpp/application/application_delegate.h" -#include "mojo/public/cpp/application/interface_factory.h" -#include "mojo/services/gles2/command_buffer_impl.h" -#include "mojo/services/native_viewport/native_viewport.h" -#include "mojo/services/public/cpp/geometry/geometry_type_converters.h" -#include "mojo/services/public/cpp/input_events/input_events_type_converters.h" -#include "mojo/services/public/interfaces/native_viewport/native_viewport.mojom.h" -#include "ui/events/event.h" - -namespace mojo { -namespace services { -namespace { - -bool IsRateLimitedEventType(ui::Event* event) { - return event->type() == ui::ET_MOUSE_MOVED || - event->type() == ui::ET_MOUSE_DRAGGED || - event->type() == ui::ET_TOUCH_MOVED; -} - -} // namespace - -class NativeViewportImpl : public InterfaceImpl, - public NativeViewportDelegate { - public: - NativeViewportImpl() - : widget_(gfx::kNullAcceleratedWidget), - waiting_for_event_ack_(false), - weak_factory_(this) {} - virtual ~NativeViewportImpl() { - // Destroy the NativeViewport early on as it may call us back during - // destruction and we want to be in a known state. - native_viewport_.reset(); - } - - virtual void Create(RectPtr bounds) OVERRIDE { - native_viewport_ = services::NativeViewport::Create(this); - native_viewport_->Init(bounds.To()); - client()->OnCreated(); - OnBoundsChanged(bounds.To()); - } - - virtual void Show() OVERRIDE { - native_viewport_->Show(); - } - - virtual void Hide() OVERRIDE { - native_viewport_->Hide(); - } - - virtual void Close() OVERRIDE { - command_buffer_.reset(); - DCHECK(native_viewport_); - native_viewport_->Close(); - } - - virtual void SetBounds(RectPtr bounds) OVERRIDE { - native_viewport_->SetBounds(bounds.To()); - } - - virtual void CreateGLES2Context( - InterfaceRequest command_buffer_request) OVERRIDE { - if (command_buffer_ || command_buffer_request_.is_pending()) { - LOG(ERROR) << "Can't create multiple contexts on a NativeViewport"; - return; - } - command_buffer_request_ = command_buffer_request.Pass(); - CreateCommandBufferIfNeeded(); - } - - void AckEvent() { - waiting_for_event_ack_ = false; - } - - void CreateCommandBufferIfNeeded() { - if (!command_buffer_request_.is_pending()) - return; - DCHECK(!command_buffer_.get()); - if (widget_ == gfx::kNullAcceleratedWidget) - return; - gfx::Size size = native_viewport_->GetSize(); - if (size.IsEmpty()) - return; - command_buffer_.reset( - new CommandBufferImpl(widget_, native_viewport_->GetSize())); - WeakBindToRequest(command_buffer_.get(), &command_buffer_request_); - } - - virtual bool OnEvent(ui::Event* ui_event) OVERRIDE { - // Must not return early before updating capture. - switch (ui_event->type()) { - case ui::ET_MOUSE_PRESSED: - case ui::ET_TOUCH_PRESSED: - native_viewport_->SetCapture(); - break; - case ui::ET_MOUSE_RELEASED: - case ui::ET_TOUCH_RELEASED: - native_viewport_->ReleaseCapture(); - break; - default: - break; - } - - if (waiting_for_event_ack_ && IsRateLimitedEventType(ui_event)) - return false; - - client()->OnEvent( - TypeConverter::ConvertFrom(*ui_event), - base::Bind(&NativeViewportImpl::AckEvent, - weak_factory_.GetWeakPtr())); - waiting_for_event_ack_ = true; - return false; - } - - virtual void OnAcceleratedWidgetAvailable( - gfx::AcceleratedWidget widget) OVERRIDE { - widget_ = widget; - CreateCommandBufferIfNeeded(); - } - - virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE { - CreateCommandBufferIfNeeded(); - client()->OnBoundsChanged(Rect::From(bounds)); - } - - virtual void OnDestroyed() OVERRIDE { - client()->OnDestroyed(base::Bind(&NativeViewportImpl::AckDestroyed, - base::Unretained(this))); - } - - private: - void AckDestroyed() { - command_buffer_.reset(); - } - - gfx::AcceleratedWidget widget_; - scoped_ptr native_viewport_; - InterfaceRequest command_buffer_request_; - scoped_ptr command_buffer_; - bool waiting_for_event_ack_; - base::WeakPtrFactory weak_factory_; -}; - -class NVSDelegate : public ApplicationDelegate, - public InterfaceFactory { - public: - NVSDelegate() {} - virtual ~NVSDelegate() {} - - // ApplicationDelegate implementation. - virtual bool ConfigureIncomingConnection( - mojo::ApplicationConnection* connection) OVERRIDE { - connection->AddService(this); - return true; - } - - // ServiceFactory implementation. - virtual void Create(ApplicationConnection* connection, - InterfaceRequest request) OVERRIDE { - BindToRequest(new NativeViewportImpl, &request); - } -}; - -MOJO_NATIVE_VIEWPORT_EXPORT mojo::ApplicationImpl* - CreateNativeViewportService( - ScopedMessagePipeHandle service_provider_handle) { - ApplicationImpl* app = new ApplicationImpl( - new NVSDelegate(), service_provider_handle.Pass()); - return app; -} - -} // namespace services -} // namespace mojo - diff --git a/mojo/services/native_viewport/native_viewport_service.h b/mojo/services/native_viewport/native_viewport_service.h deleted file mode 100644 index 761301111ab4b..0000000000000 --- a/mojo/services/native_viewport/native_viewport_service.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_ -#define MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_ - -#include "base/memory/scoped_vector.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/services/native_viewport/native_viewport_export.h" -#include "mojo/shell/context.h" - -namespace mojo { -namespace services { - -MOJO_NATIVE_VIEWPORT_EXPORT mojo::ApplicationImpl* - CreateNativeViewportService( - ScopedMessagePipeHandle service_provider_handle); - -} // namespace services -} // namespace mojo - -#endif // MOJO_SERVICES_NATIVE_VIEWPORT_SERVICE_H_ diff --git a/mojo/services/native_viewport/native_viewport_stub.cc b/mojo/services/native_viewport/native_viewport_stub.cc deleted file mode 100644 index f685c0be40801..0000000000000 --- a/mojo/services/native_viewport/native_viewport_stub.cc +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/native_viewport/native_viewport.h" - -// Stub to build on platforms we don't fully support yet. - -namespace mojo { -namespace services { - -class NativeViewportStub : public NativeViewport { - public: - NativeViewportStub(NativeViewportDelegate* delegate) - : delegate_(delegate) { - } - virtual ~NativeViewportStub() { - } - - private: - // Overridden from NativeViewport: - virtual void Init() OVERRIDE { - } - virtual void Show() OVERRIDE { - } - virtual void Hide() OVERRIDE { - } - virtual void Close() OVERRIDE { - delegate_->OnDestroyed(); - } - virtual gfx::Size GetSize() OVERRIDE { - return gfx::Size(); - } - virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { - } - - NativeViewportDelegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewportStub); -}; - -// static -scoped_ptr NativeViewport::Create( - NativeViewportDelegate* delegate) { - return scoped_ptr(new NativeViewportStub(delegate)).Pass(); -} - -} // namespace services -} // namespace mojo diff --git a/mojo/services/native_viewport/native_viewport_win.cc b/mojo/services/native_viewport/native_viewport_win.cc deleted file mode 100644 index 26312ae0c82a5..0000000000000 --- a/mojo/services/native_viewport/native_viewport_win.cc +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/native_viewport/native_viewport.h" - -#include "base/memory/scoped_ptr.h" -#include "ui/gfx/rect.h" -#include "ui/platform_window/platform_window_delegate.h" -#include "ui/platform_window/win/win_window.h" - -namespace mojo { -namespace services { - -class NativeViewportWin : public NativeViewport, - public ui::PlatformWindowDelegate { - public: - explicit NativeViewportWin(NativeViewportDelegate* delegate) - : delegate_(delegate) { - } - - virtual ~NativeViewportWin() { - // Destroy the platform-window while |this| is still alive. - platform_window_.reset(); - } - - private: - // Overridden from NativeViewport: - virtual void Init(const gfx::Rect& bounds) OVERRIDE { - platform_window_.reset(new ui::WinWindow(this, bounds)); - } - - virtual void Show() OVERRIDE { - platform_window_->Show(); - } - - virtual void Hide() OVERRIDE { - platform_window_->Hide(); - } - - virtual void Close() OVERRIDE { - platform_window_->Close(); - } - - virtual gfx::Size GetSize() OVERRIDE { - return platform_window_->GetBounds().size(); - } - - virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { - platform_window_->SetBounds(bounds); - } - - virtual void SetCapture() OVERRIDE { - platform_window_->SetCapture(); - } - - virtual void ReleaseCapture() OVERRIDE { - platform_window_->ReleaseCapture(); - } - - // ui::PlatformWindowDelegate: - virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE { - delegate_->OnBoundsChanged(new_bounds); - } - - virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE { - } - - virtual void DispatchEvent(ui::Event* event) OVERRIDE { - delegate_->OnEvent(event); - } - - virtual void OnCloseRequest() OVERRIDE { - platform_window_->Close(); - } - - virtual void OnClosed() OVERRIDE { - delegate_->OnDestroyed(); - } - - virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE { - } - - virtual void OnLostCapture() OVERRIDE { - } - - virtual void OnAcceleratedWidgetAvailable( - gfx::AcceleratedWidget widget) OVERRIDE { - delegate_->OnAcceleratedWidgetAvailable(widget); - } - - virtual void OnActivationChanged(bool active) OVERRIDE {} - - scoped_ptr platform_window_; - NativeViewportDelegate* delegate_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewportWin); -}; - -// static -scoped_ptr NativeViewport::Create( - NativeViewportDelegate* delegate) { - return scoped_ptr(new NativeViewportWin(delegate)).Pass(); -} - -} // namespace services -} // namespace mojo diff --git a/mojo/services/native_viewport/native_viewport_x11.cc b/mojo/services/native_viewport/native_viewport_x11.cc deleted file mode 100644 index c1f5037e40bdf..0000000000000 --- a/mojo/services/native_viewport/native_viewport_x11.cc +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/services/native_viewport/native_viewport.h" - -#include "base/command_line.h" -#include "base/message_loop/message_loop.h" -#include "ui/events/event.h" -#include "ui/events/event_utils.h" -#include "ui/events/platform/platform_event_dispatcher.h" -#include "ui/events/platform/platform_event_source.h" -#include "ui/gfx/rect.h" -#include "ui/platform_window/platform_window.h" -#include "ui/platform_window/platform_window_delegate.h" -#include "ui/platform_window/x11/x11_window.h" - -namespace mojo { -namespace services { - -class NativeViewportX11 : public NativeViewport, - public ui::PlatformWindowDelegate { - public: - explicit NativeViewportX11(NativeViewportDelegate* delegate) - : delegate_(delegate) { - } - - virtual ~NativeViewportX11() { - // Destroy the platform-window while |this| is still alive. - platform_window_.reset(); - } - - private: - // Overridden from NativeViewport: - virtual void Init(const gfx::Rect& bounds) OVERRIDE { - CHECK(!event_source_); - CHECK(!platform_window_); - - event_source_ = ui::PlatformEventSource::CreateDefault(); - - platform_window_.reset(new ui::X11Window(this)); - platform_window_->SetBounds(bounds); - } - - virtual void Show() OVERRIDE { - platform_window_->Show(); - } - - virtual void Hide() OVERRIDE { - platform_window_->Hide(); - } - - virtual void Close() OVERRIDE { - platform_window_->Close(); - } - - virtual gfx::Size GetSize() OVERRIDE { - return bounds_.size(); - } - - virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { - platform_window_->SetBounds(bounds); - } - - virtual void SetCapture() OVERRIDE { - platform_window_->SetCapture(); - } - - virtual void ReleaseCapture() OVERRIDE { - platform_window_->ReleaseCapture(); - } - - // ui::PlatformWindowDelegate: - virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE { - bounds_ = new_bounds; - delegate_->OnBoundsChanged(new_bounds); - } - - virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE { - } - - virtual void DispatchEvent(ui::Event* event) OVERRIDE { - delegate_->OnEvent(event); - } - - virtual void OnCloseRequest() OVERRIDE { - platform_window_->Close(); - } - - virtual void OnClosed() OVERRIDE { - delegate_->OnDestroyed(); - } - - virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE { - } - - virtual void OnLostCapture() OVERRIDE { - } - - virtual void OnAcceleratedWidgetAvailable( - gfx::AcceleratedWidget widget) OVERRIDE { - delegate_->OnAcceleratedWidgetAvailable(widget); - } - - virtual void OnActivationChanged(bool active) OVERRIDE {} - - scoped_ptr event_source_; - scoped_ptr platform_window_; - NativeViewportDelegate* delegate_; - gfx::Rect bounds_; - - DISALLOW_COPY_AND_ASSIGN(NativeViewportX11); -}; - -// static -scoped_ptr NativeViewport::Create( - NativeViewportDelegate* delegate) { - return scoped_ptr(new NativeViewportX11(delegate)).Pass(); -} - -} // namespace services -} // namespace mojo diff --git a/mojo/services/native_viewport/platform_viewport.h b/mojo/services/native_viewport/platform_viewport.h new file mode 100644 index 0000000000000..f42eb7d273454 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport.h @@ -0,0 +1,53 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_ + +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/native_widget_types.h" +#include "ui/gfx/size.h" + +namespace gfx { +class Rect; +} + +namespace ui { +class Event; +} + +namespace mojo { + +// Encapsulation of platform-specific Viewport. +class PlatformViewport { + public: + class Delegate { + public: + virtual ~Delegate() {} + + virtual void OnBoundsChanged(const gfx::Rect& size) = 0; + virtual void OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) = 0; + virtual bool OnEvent(ui::Event* ui_event) = 0; + virtual void OnDestroyed() = 0; + }; + + virtual ~PlatformViewport() {} + + virtual void Init(const gfx::Rect& bounds) = 0; + virtual void Show() = 0; + virtual void Hide() = 0; + virtual void Close() = 0; + virtual gfx::Size GetSize() = 0; + virtual void SetBounds(const gfx::Rect& bounds) = 0; + + virtual void SetCapture() = 0; + virtual void ReleaseCapture() = 0; + + static scoped_ptr Create(Delegate* delegate); +}; + +} // namespace mojo + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_H_ diff --git a/mojo/services/native_viewport/platform_viewport_android.cc b/mojo/services/native_viewport/platform_viewport_android.cc new file mode 100644 index 0000000000000..bbe22c9534964 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_android.cc @@ -0,0 +1,156 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/platform_viewport_android.h" + +#include +#include + +#include "base/android/jni_android.h" +#include "jni/PlatformViewportAndroid_jni.h" +#include "ui/events/event.h" +#include "ui/gfx/point.h" + +namespace mojo { + +ui::EventType MotionEventActionToEventType(jint action) { + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: + return ui::ET_TOUCH_PRESSED; + case AMOTION_EVENT_ACTION_MOVE: + return ui::ET_TOUCH_MOVED; + case AMOTION_EVENT_ACTION_UP: + return ui::ET_TOUCH_RELEASED; + default: + NOTREACHED(); + } + return ui::ET_UNKNOWN; +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformViewportAndroid, public: + +// static +bool PlatformViewportAndroid::Register(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +PlatformViewportAndroid::PlatformViewportAndroid(Delegate* delegate) + : delegate_(delegate), + window_(NULL), + id_generator_(0), + weak_factory_(this) { +} + +PlatformViewportAndroid::~PlatformViewportAndroid() { + if (window_) + ReleaseWindow(); +} + +void PlatformViewportAndroid::Destroy(JNIEnv* env, jobject obj) { + delegate_->OnDestroyed(); +} + +void PlatformViewportAndroid::SurfaceCreated(JNIEnv* env, + jobject obj, + jobject jsurface) { + base::android::ScopedJavaLocalRef protector(env, jsurface); + // Note: This ensures that any local references used by + // ANativeWindow_fromSurface are released immediately. This is needed as a + // workaround for https://code.google.com/p/android/issues/detail?id=68174 + { + base::android::ScopedJavaLocalFrame scoped_local_reference_frame(env); + window_ = ANativeWindow_fromSurface(env, jsurface); + } + delegate_->OnAcceleratedWidgetAvailable(window_); +} + +void PlatformViewportAndroid::SurfaceDestroyed(JNIEnv* env, jobject obj) { + DCHECK(window_); + ReleaseWindow(); +} + +void PlatformViewportAndroid::SurfaceSetSize(JNIEnv* env, jobject obj, + jint width, jint height) { + bounds_ = gfx::Rect(width, height); + delegate_->OnBoundsChanged(bounds_); +} + +bool PlatformViewportAndroid::TouchEvent(JNIEnv* env, jobject obj, + jint pointer_id, + jint action, + jfloat x, jfloat y, + jlong time_ms) { + gfx::Point location(static_cast(x), static_cast(y)); + ui::TouchEvent event(MotionEventActionToEventType(action), location, + id_generator_.GetGeneratedID(pointer_id), + base::TimeDelta::FromMilliseconds(time_ms)); + // TODO(beng): handle multiple touch-points. + delegate_->OnEvent(&event); + if (action == ui::ET_TOUCH_RELEASED) + id_generator_.ReleaseNumber(pointer_id); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformViewportAndroid, PlatformViewport implementation: + +void PlatformViewportAndroid::Init(const gfx::Rect& bounds) { + JNIEnv* env = base::android::AttachCurrentThread(); + Java_PlatformViewportAndroid_createForActivity( + env, + base::android::GetApplicationContext(), + reinterpret_cast(this)); +} + +void PlatformViewportAndroid::Show() { + // Nothing to do. View is created visible. +} + +void PlatformViewportAndroid::Hide() { + // Nothing to do. View is always visible. +} + +void PlatformViewportAndroid::Close() { + // TODO(beng): close activity containing MojoView? + + // TODO(beng): perform this in response to view destruction. + delegate_->OnDestroyed(); +} + +gfx::Size PlatformViewportAndroid::GetSize() { + return bounds_.size(); +} + +void PlatformViewportAndroid::SetBounds(const gfx::Rect& bounds) { + NOTIMPLEMENTED(); +} + +void PlatformViewportAndroid::SetCapture() { + NOTIMPLEMENTED(); +} + +void PlatformViewportAndroid::ReleaseCapture() { + NOTIMPLEMENTED(); +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformViewportAndroid, private: + +void PlatformViewportAndroid::ReleaseWindow() { + ANativeWindow_release(window_); + window_ = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// PlatformViewport, public: + +// static +scoped_ptr PlatformViewport::Create(Delegate* delegate) { + return scoped_ptr( + new PlatformViewportAndroid(delegate)).Pass(); +} + +} // namespace mojo diff --git a/mojo/services/native_viewport/platform_viewport_android.h b/mojo/services/native_viewport/platform_viewport_android.h new file mode 100644 index 0000000000000..faf7275ca21d8 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_android.h @@ -0,0 +1,65 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_ +#define MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_ + +#include "base/android/jni_weak_ref.h" +#include "base/android/scoped_java_ref.h" +#include "base/memory/weak_ptr.h" +#include "mojo/services/native_viewport/platform_viewport.h" +#include "ui/events/event_constants.h" +#include "ui/gfx/rect.h" +#include "ui/gfx/sequential_id_generator.h" +#include "ui/gfx/size.h" + +namespace gpu { +class GLInProcessContext; +} + +struct ANativeWindow; + +namespace mojo { + +class PlatformViewportAndroid : public PlatformViewport { + public: + static bool Register(JNIEnv* env); + + explicit PlatformViewportAndroid(Delegate* delegate); + virtual ~PlatformViewportAndroid(); + + void Destroy(JNIEnv* env, jobject obj); + void SurfaceCreated(JNIEnv* env, jobject obj, jobject jsurface); + void SurfaceDestroyed(JNIEnv* env, jobject obj); + void SurfaceSetSize(JNIEnv* env, jobject obj, jint width, jint height); + bool TouchEvent(JNIEnv* env, jobject obj, jint pointer_id, jint action, + jfloat x, jfloat y, jlong time_ms); + + private: + // Overridden from PlatformViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE; + virtual void Show() OVERRIDE; + virtual void Hide() OVERRIDE; + virtual void Close() OVERRIDE; + virtual gfx::Size GetSize() OVERRIDE; + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE; + virtual void SetCapture() OVERRIDE; + virtual void ReleaseCapture() OVERRIDE; + + void ReleaseWindow(); + + Delegate* delegate_; + ANativeWindow* window_; + gfx::Rect bounds_; + ui::SequentialIDGenerator id_generator_; + + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(PlatformViewportAndroid); +}; + + +} // namespace mojo + +#endif // MOJO_SERVICES_NATIVE_VIEWPORT_PLATFORM_VIEWPORT_ANDROID_H_ diff --git a/mojo/services/native_viewport/platform_viewport_mac.mm b/mojo/services/native_viewport/platform_viewport_mac.mm new file mode 100644 index 0000000000000..9d11052781693 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_mac.mm @@ -0,0 +1,84 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/platform_viewport.h" + +#import +#import +#import + +#include "base/bind.h" +#include "ui/gfx/rect.h" + +namespace mojo { + +class PlatformViewportMac : public PlatformViewport { + public: + PlatformViewportMac(Delegate* delegate) + : delegate_(delegate), + window_(nil) { + } + + virtual ~PlatformViewportMac() { + [window_ orderOut:nil]; + [window_ close]; + } + + private: + // Overridden from PlatformViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + [NSApplication sharedApplication]; + + rect_ = bounds; + window_ = [[NSWindow alloc] + initWithContentRect:NSRectFromCGRect(rect_.ToCGRect()) + styleMask:NSTitledWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + delegate_->OnAcceleratedWidgetAvailable([window_ contentView]); + delegate_->OnBoundsChanged(rect_); + } + + virtual void Show() OVERRIDE { + [window_ orderFront:nil]; + } + + virtual void Hide() OVERRIDE { + [window_ orderOut:nil]; + } + + virtual void Close() OVERRIDE { + // TODO(beng): perform this in response to NSWindow destruction. + delegate_->OnDestroyed(); + } + + virtual gfx::Size GetSize() OVERRIDE { + return rect_.size(); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void SetCapture() OVERRIDE { + NOTIMPLEMENTED(); + } + + virtual void ReleaseCapture() OVERRIDE { + NOTIMPLEMENTED(); + } + + Delegate* delegate_; + NSWindow* window_; + gfx::Rect rect_; + + DISALLOW_COPY_AND_ASSIGN(PlatformViewportMac); +}; + +// static +scoped_ptr PlatformViewport::Create(Delegate* delegate) { + return scoped_ptr(new PlatformViewportMac(delegate)).Pass(); +} + +} // namespace mojo diff --git a/mojo/services/native_viewport/platform_viewport_ozone.cc b/mojo/services/native_viewport/platform_viewport_ozone.cc new file mode 100644 index 0000000000000..b8076fb997c20 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_ozone.cc @@ -0,0 +1,95 @@ +// 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/services/native_viewport/platform_viewport.h" + +#include "ui/events/event.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/ozone/public/cursor_factory_ozone.h" +#include "ui/ozone/public/ozone_platform.h" +#include "ui/ozone/public/surface_factory_ozone.h" +#include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_delegate.h" + +namespace mojo { + +// TODO(spang): Deduplicate with PlatformViewportX11.. but there's a hack +// in there that prevents this. +class PlatformViewportOzone : public PlatformViewport, + public ui::PlatformWindowDelegate { + public: + explicit PlatformViewportOzone(Delegate* delegate) : delegate_(delegate) { + ui::OzonePlatform::InitializeForUI(); + } + + virtual ~PlatformViewportOzone() { + // Destroy the platform-window while |this| is still alive. + platform_window_.reset(); + } + + private: + // Overridden from PlatformViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + platform_window_ = + ui::OzonePlatform::GetInstance()->CreatePlatformWindow(this, bounds); + } + + virtual void Show() OVERRIDE { platform_window_->Show(); } + + virtual void Hide() OVERRIDE { platform_window_->Hide(); } + + virtual void Close() OVERRIDE { platform_window_->Close(); } + + virtual gfx::Size GetSize() OVERRIDE { + return platform_window_->GetBounds().size(); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + platform_window_->SetBounds(bounds); + } + + virtual void SetCapture() OVERRIDE { platform_window_->SetCapture(); } + + virtual void ReleaseCapture() OVERRIDE { platform_window_->ReleaseCapture(); } + + // ui::PlatformWindowDelegate: + virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE { + delegate_->OnBoundsChanged(new_bounds); + } + + virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE {} + + virtual void DispatchEvent(ui::Event* event) OVERRIDE { + delegate_->OnEvent(event); + } + + virtual void OnCloseRequest() OVERRIDE { platform_window_->Close(); } + + virtual void OnClosed() OVERRIDE { delegate_->OnDestroyed(); } + + virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE {} + + virtual void OnLostCapture() OVERRIDE {} + + virtual void OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) OVERRIDE { + delegate_->OnAcceleratedWidgetAvailable(widget); + } + + virtual void OnActivationChanged(bool active) OVERRIDE {} + + scoped_ptr platform_window_; + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(PlatformViewportOzone); +}; + +// static +scoped_ptr PlatformViewport::Create(Delegate* delegate) { + return scoped_ptr( + new PlatformViewportOzone(delegate)).Pass(); +} + +} // namespace mojo diff --git a/mojo/services/native_viewport/platform_viewport_stub.cc b/mojo/services/native_viewport/platform_viewport_stub.cc new file mode 100644 index 0000000000000..9ed83fd72fc44 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_stub.cc @@ -0,0 +1,46 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/platform_viewport.h" + +// Stub to build on platforms we don't fully support yet. + +namespace mojo { + +class PlatformViewportStub : public PlatformViewport { + public: + PlatformViewportStub(Delegate* delegate) : delegate_(delegate) { + } + virtual ~PlatformViewportStub() { + } + + private: + // Overridden from PlatformViewport: + virtual void Init() OVERRIDE { + } + virtual void Show() OVERRIDE { + } + virtual void Hide() OVERRIDE { + } + virtual void Close() OVERRIDE { + delegate_->OnDestroyed(); + } + virtual gfx::Size GetSize() OVERRIDE { + return gfx::Size(); + } + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + } + + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(PlatformViewportStub); +}; + +// static +scoped_ptr PlatformViewport::Create(Delegate* delegate) { + return scoped_ptr( + new PlatformViewportStub(delegate)).Pass(); +} + +} // namespace mojo diff --git a/mojo/services/native_viewport/platform_viewport_win.cc b/mojo/services/native_viewport/platform_viewport_win.cc new file mode 100644 index 0000000000000..222711222c042 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_win.cc @@ -0,0 +1,104 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/platform_viewport.h" + +#include "base/memory/scoped_ptr.h" +#include "ui/gfx/rect.h" +#include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/win/win_window.h" + +namespace mojo { + +class PlatformViewportWin : public PlatformViewport, + public ui::PlatformWindowDelegate { + public: + explicit PlatformViewportWin(Delegate* delegate) + : delegate_(delegate) { + } + + virtual ~PlatformViewportWin() { + // Destroy the platform-window while |this| is still alive. + platform_window_.reset(); + } + + private: + // Overridden from PlatformViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + platform_window_.reset(new ui::WinWindow(this, bounds)); + } + + virtual void Show() OVERRIDE { + platform_window_->Show(); + } + + virtual void Hide() OVERRIDE { + platform_window_->Hide(); + } + + virtual void Close() OVERRIDE { + platform_window_->Close(); + } + + virtual gfx::Size GetSize() OVERRIDE { + return platform_window_->GetBounds().size(); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + platform_window_->SetBounds(bounds); + } + + virtual void SetCapture() OVERRIDE { + platform_window_->SetCapture(); + } + + virtual void ReleaseCapture() OVERRIDE { + platform_window_->ReleaseCapture(); + } + + // ui::PlatformWindowDelegate: + virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE { + delegate_->OnBoundsChanged(new_bounds); + } + + virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE { + } + + virtual void DispatchEvent(ui::Event* event) OVERRIDE { + delegate_->OnEvent(event); + } + + virtual void OnCloseRequest() OVERRIDE { + platform_window_->Close(); + } + + virtual void OnClosed() OVERRIDE { + delegate_->OnDestroyed(); + } + + virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE { + } + + virtual void OnLostCapture() OVERRIDE { + } + + virtual void OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) OVERRIDE { + delegate_->OnAcceleratedWidgetAvailable(widget); + } + + virtual void OnActivationChanged(bool active) OVERRIDE {} + + scoped_ptr platform_window_; + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(PlatformViewportWin); +}; + +// static +scoped_ptr PlatformViewport::Create(Delegate* delegate) { + return scoped_ptr(new PlatformViewportWin(delegate)).Pass(); +} + +} // namespace mojo diff --git a/mojo/services/native_viewport/platform_viewport_x11.cc b/mojo/services/native_viewport/platform_viewport_x11.cc new file mode 100644 index 0000000000000..6654cf59b2d43 --- /dev/null +++ b/mojo/services/native_viewport/platform_viewport_x11.cc @@ -0,0 +1,118 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/services/native_viewport/platform_viewport.h" + +#include "base/command_line.h" +#include "base/message_loop/message_loop.h" +#include "ui/events/event.h" +#include "ui/events/event_utils.h" +#include "ui/events/platform/platform_event_dispatcher.h" +#include "ui/events/platform/platform_event_source.h" +#include "ui/gfx/rect.h" +#include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/x11/x11_window.h" + +namespace mojo { + +class PlatformViewportX11 : public PlatformViewport, + public ui::PlatformWindowDelegate { + public: + explicit PlatformViewportX11(Delegate* delegate) : delegate_(delegate) { + } + + virtual ~PlatformViewportX11() { + // Destroy the platform-window while |this| is still alive. + platform_window_.reset(); + } + + private: + // Overridden from PlatformViewport: + virtual void Init(const gfx::Rect& bounds) OVERRIDE { + CHECK(!event_source_); + CHECK(!platform_window_); + + event_source_ = ui::PlatformEventSource::CreateDefault(); + + platform_window_.reset(new ui::X11Window(this)); + platform_window_->SetBounds(bounds); + } + + virtual void Show() OVERRIDE { + platform_window_->Show(); + } + + virtual void Hide() OVERRIDE { + platform_window_->Hide(); + } + + virtual void Close() OVERRIDE { + platform_window_->Close(); + } + + virtual gfx::Size GetSize() OVERRIDE { + return bounds_.size(); + } + + virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE { + platform_window_->SetBounds(bounds); + } + + virtual void SetCapture() OVERRIDE { + platform_window_->SetCapture(); + } + + virtual void ReleaseCapture() OVERRIDE { + platform_window_->ReleaseCapture(); + } + + // ui::PlatformWindowDelegate: + virtual void OnBoundsChanged(const gfx::Rect& new_bounds) OVERRIDE { + bounds_ = new_bounds; + delegate_->OnBoundsChanged(new_bounds); + } + + virtual void OnDamageRect(const gfx::Rect& damaged_region) OVERRIDE { + } + + virtual void DispatchEvent(ui::Event* event) OVERRIDE { + delegate_->OnEvent(event); + } + + virtual void OnCloseRequest() OVERRIDE { + platform_window_->Close(); + } + + virtual void OnClosed() OVERRIDE { + delegate_->OnDestroyed(); + } + + virtual void OnWindowStateChanged(ui::PlatformWindowState state) OVERRIDE { + } + + virtual void OnLostCapture() OVERRIDE { + } + + virtual void OnAcceleratedWidgetAvailable( + gfx::AcceleratedWidget widget) OVERRIDE { + delegate_->OnAcceleratedWidgetAvailable(widget); + } + + virtual void OnActivationChanged(bool active) OVERRIDE {} + + scoped_ptr event_source_; + scoped_ptr platform_window_; + Delegate* delegate_; + gfx::Rect bounds_; + + DISALLOW_COPY_AND_ASSIGN(PlatformViewportX11); +}; + +// static +scoped_ptr PlatformViewport::Create(Delegate* delegate) { + return scoped_ptr(new PlatformViewportX11(delegate)).Pass(); +} + +} // namespace mojo diff --git a/mojo/services/network/url_loader_impl.cc b/mojo/services/network/url_loader_impl.cc index 7e53c4035852e..52f61480ce629 100644 --- a/mojo/services/network/url_loader_impl.cc +++ b/mojo/services/network/url_loader_impl.cc @@ -13,6 +13,7 @@ #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/http/http_response_headers.h" +#include "net/url_request/redirect_info.h" namespace mojo { namespace { @@ -240,7 +241,7 @@ void URLLoaderImpl::QueryStatus( } void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer_redirect) { DCHECK(url_request == url_request_.get()); DCHECK(url_request->status().is_success()); @@ -253,10 +254,8 @@ void URLLoaderImpl::OnReceivedRedirect(net::URLRequest* url_request, *defer_redirect = true; URLResponsePtr response = MakeURLResponse(url_request); - response->redirect_method = - net::URLRequest::ComputeMethodForRedirect(url_request->method(), - response->status_code); - response->redirect_url = String::From(new_url); + response->redirect_method = redirect_info.new_method; + response->redirect_url = String::From(redirect_info.new_url); SendResponse(response.Pass()); } diff --git a/mojo/services/network/url_loader_impl.h b/mojo/services/network/url_loader_impl.h index 6785501fcf567..5c5af8a9e6c2e 100644 --- a/mojo/services/network/url_loader_impl.h +++ b/mojo/services/network/url_loader_impl.h @@ -38,7 +38,7 @@ class URLLoaderImpl : public InterfaceImpl, // net::URLRequest::Delegate methods: virtual void OnReceivedRedirect(net::URLRequest* url_request, - const GURL& new_url, + const net::RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE; virtual void OnResponseStarted(net::URLRequest* url_request) OVERRIDE; virtual void OnReadCompleted(net::URLRequest* url_request, int bytes_read) diff --git a/mojo/services/public/cpp/geometry/BUILD.gn b/mojo/services/public/cpp/geometry/BUILD.gn index 5f8a9b96fa42f..21cf5d97613d4 100644 --- a/mojo/services/public/cpp/geometry/BUILD.gn +++ b/mojo/services/public/cpp/geometry/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/system.gni") + component("geometry") { output_name = "mojo_geometry_lib" @@ -12,6 +14,7 @@ component("geometry") { "//mojo/environment:chromium", "//mojo/services/public/interfaces/geometry", ] + deps += mojo_system_for_component forward_dependent_configs_from = [ "//ui/gfx" ] @@ -24,8 +27,4 @@ component("geometry") { "geometry_type_converters.h", "mojo_geometry_export.h", ] - - if (is_component_build) { - deps += [ "//mojo/system" ] - } } diff --git a/mojo/services/public/cpp/input_events/BUILD.gn b/mojo/services/public/cpp/input_events/BUILD.gn index 92222dbbba034..92cc205984464 100644 --- a/mojo/services/public/cpp/input_events/BUILD.gn +++ b/mojo/services/public/cpp/input_events/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/system.gni") + component("input_events") { deps = [ "//base", @@ -12,6 +14,7 @@ component("input_events") { "//mojo/services/public/interfaces/geometry", "//mojo/services/public/cpp/geometry", ] + deps += mojo_system_for_component defines = [ "MOJO_INPUT_EVENTS_IMPLEMENTATION", @@ -22,8 +25,4 @@ component("input_events") { "input_events_type_converters.h", "mojo_input_events_export.h", ] - - if (is_component_build) { - deps += [ "//mojo/system" ] - } } diff --git a/mojo/services/public/cpp/view_manager/BUILD.gn b/mojo/services/public/cpp/view_manager/BUILD.gn index 65d586f3209fa..aa6f6b91856f1 100644 --- a/mojo/services/public/cpp/view_manager/BUILD.gn +++ b/mojo/services/public/cpp/view_manager/BUILD.gn @@ -11,6 +11,7 @@ source_set("view_manager") { "//mojo/services/public/cpp/geometry", "//mojo/services/public/interfaces/geometry", "//mojo/services/public/interfaces/view_manager", + "//mojo/services/public/interfaces/window_manager", "//skia", "//ui/events", "//ui/gfx", @@ -18,18 +19,13 @@ source_set("view_manager") { ] sources = [ - "lib/node.cc", - "lib/node_observer.cc", - "lib/node_private.cc", - "lib/node_private.h", "lib/view.cc", - "lib/view_private.cc", - "lib/view_private.h", "lib/view_manager_client_factory.cc", "lib/view_manager_client_impl.cc", "lib/view_manager_client_impl.h", - "node.h", - "node_observer.h", + "lib/view_observer.cc", + "lib/view_private.cc", + "lib/view_private.h", "view.h", "view_manager.h", "view_manager_client_factory.h", diff --git a/mojo/services/public/cpp/view_manager/lib/node.cc b/mojo/services/public/cpp/view_manager/lib/node.cc deleted file mode 100644 index f313d8aed2646..0000000000000 --- a/mojo/services/public/cpp/view_manager/lib/node.cc +++ /dev/null @@ -1,422 +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/services/public/cpp/view_manager/node.h" - -#include "mojo/public/cpp/application/service_provider_impl.h" -#include "mojo/services/public/cpp/view_manager/lib/node_private.h" -#include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" -#include "mojo/services/public/cpp/view_manager/lib/view_private.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" -#include "mojo/services/public/cpp/view_manager/view.h" - -namespace mojo { - -namespace { - -void NotifyViewTreeChangeAtReceiver( - Node* receiver, - const NodeObserver::TreeChangeParams& params, - bool change_applied) { - NodeObserver::TreeChangeParams local_params = params; - local_params.receiver = receiver; - if (change_applied) { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(receiver).observers(), - OnTreeChanged(local_params)); - } else { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(receiver).observers(), - OnTreeChanging(local_params)); - } -} - -void NotifyViewTreeChangeUp( - Node* start_at, - const NodeObserver::TreeChangeParams& params, - bool change_applied) { - for (Node* current = start_at; current; current = current->parent()) - NotifyViewTreeChangeAtReceiver(current, params, change_applied); -} - -void NotifyViewTreeChangeDown( - Node* start_at, - const NodeObserver::TreeChangeParams& params, - bool change_applied) { - NotifyViewTreeChangeAtReceiver(start_at, params, change_applied); - Node::Children::const_iterator it = start_at->children().begin(); - for (; it != start_at->children().end(); ++it) - NotifyViewTreeChangeDown(*it, params, change_applied); -} - -void NotifyViewTreeChange( - const NodeObserver::TreeChangeParams& params, - bool change_applied) { - NotifyViewTreeChangeDown(params.target, params, change_applied); - if (params.old_parent) - NotifyViewTreeChangeUp(params.old_parent, params, change_applied); - if (params.new_parent) - NotifyViewTreeChangeUp(params.new_parent, params, change_applied); -} - -class ScopedTreeNotifier { - public: - ScopedTreeNotifier(Node* target, Node* old_parent, Node* new_parent) { - params_.target = target; - params_.old_parent = old_parent; - params_.new_parent = new_parent; - NotifyViewTreeChange(params_, false); - } - ~ScopedTreeNotifier() { - NotifyViewTreeChange(params_, true); - } - - private: - NodeObserver::TreeChangeParams params_; - - DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); -}; - -void RemoveChildImpl(Node* child, Node::Children* children) { - Node::Children::iterator it = - std::find(children->begin(), children->end(), child); - if (it != children->end()) { - children->erase(it); - NodePrivate(child).ClearParent(); - } -} - -class ScopedOrderChangedNotifier { - public: - ScopedOrderChangedNotifier(Node* node, - Node* relative_node, - OrderDirection direction) - : node_(node), - relative_node_(relative_node), - direction_(direction) { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(node_).observers(), - OnNodeReordering(node_, relative_node_, direction_)); - } - ~ScopedOrderChangedNotifier() { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(node_).observers(), - OnNodeReordered(node_, relative_node_, direction_)); - } - - private: - Node* node_; - Node* relative_node_; - OrderDirection direction_; - - DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier); -}; - -// Returns true if the order actually changed. -bool ReorderImpl(Node::Children* children, - Node* node, - Node* relative, - OrderDirection direction) { - DCHECK(relative); - DCHECK_NE(node, relative); - DCHECK_EQ(node->parent(), relative->parent()); - - const size_t child_i = - std::find(children->begin(), children->end(), node) - children->begin(); - const size_t target_i = - std::find(children->begin(), children->end(), relative) - - children->begin(); - if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) || - (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) { - return false; - } - - ScopedOrderChangedNotifier notifier(node, relative, direction); - - const size_t dest_i = direction == ORDER_DIRECTION_ABOVE - ? (child_i < target_i ? target_i : target_i + 1) - : (child_i < target_i ? target_i - 1 : target_i); - children->erase(children->begin() + child_i); - children->insert(children->begin() + dest_i, node); - - return true; -} - -class ScopedSetActiveViewNotifier { - public: - ScopedSetActiveViewNotifier(Node* node, View* old_view, View* new_view) - : node_(node), - old_view_(old_view), - new_view_(new_view) { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(node).observers(), - OnNodeActiveViewChanging(node_, old_view_, new_view_)); - } - ~ScopedSetActiveViewNotifier() { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(node_).observers(), - OnNodeActiveViewChanged(node_, old_view_, new_view_)); - } - - private: - Node* node_; - View* old_view_; - View* new_view_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSetActiveViewNotifier); -}; - -class ScopedSetBoundsNotifier { - public: - ScopedSetBoundsNotifier(Node* node, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) - : node_(node), - old_bounds_(old_bounds), - new_bounds_(new_bounds) { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(node_).observers(), - OnNodeBoundsChanging(node_, old_bounds_, new_bounds_)); - } - ~ScopedSetBoundsNotifier() { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(node_).observers(), - OnNodeBoundsChanged(node_, old_bounds_, new_bounds_)); - } - - private: - Node* node_; - const gfx::Rect old_bounds_; - const gfx::Rect new_bounds_; - - DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); -}; - -// Some operations are only permitted in the connection that created the node. -bool OwnsNode(ViewManager* manager, Node* node) { - return !manager || - static_cast(manager)->OwnsNode(node->id()); -} - -} // namespace - -//////////////////////////////////////////////////////////////////////////////// -// Node, public: - -// static -Node* Node::Create(ViewManager* view_manager) { - Node* node = new Node(view_manager); - static_cast(view_manager)->AddNode(node); - return node; -} - -void Node::Destroy() { - if (!OwnsNode(manager_, this)) - return; - - if (manager_) - static_cast(manager_)->DestroyNode(id_); - while (!children_.empty()) { - Node* child = children_.front(); - if (!OwnsNode(manager_, child)) { - NodePrivate(child).ClearParent(); - children_.erase(children_.begin()); - } else { - child->Destroy(); - DCHECK(std::find(children_.begin(), children_.end(), child) == - children_.end()); - } - } - LocalDestroy(); -} - -void Node::SetBounds(const gfx::Rect& bounds) { - if (!OwnsNode(manager_, this)) - return; - - if (manager_) - static_cast(manager_)->SetBounds(id_, bounds); - LocalSetBounds(bounds_, bounds); -} - -void Node::SetVisible(bool value) { - if (manager_) - static_cast(manager_)->SetVisible(id_, value); -} - -void Node::AddObserver(NodeObserver* observer) { - observers_.AddObserver(observer); -} - -void Node::RemoveObserver(NodeObserver* observer) { - observers_.RemoveObserver(observer); -} - -void Node::AddChild(Node* child) { - // TODO(beng): not necessarily valid to all connections, but possibly to the - // embeddee in an embedder-embeddee relationship. - if (manager_) - CHECK_EQ(NodePrivate(child).view_manager(), manager_); - LocalAddChild(child); - if (manager_) - static_cast(manager_)->AddChild(child->id(), id_); -} - -void Node::RemoveChild(Node* child) { - // TODO(beng): not necessarily valid to all connections, but possibly to the - // embeddee in an embedder-embeddee relationship. - if (manager_) - CHECK_EQ(NodePrivate(child).view_manager(), manager_); - LocalRemoveChild(child); - if (manager_) { - static_cast(manager_)->RemoveChild(child->id(), - id_); - } -} - -void Node::MoveToFront() { - Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE); -} - -void Node::MoveToBack() { - Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW); -} - -void Node::Reorder(Node* relative, OrderDirection direction) { - if (!LocalReorder(relative, direction)) - return; - if (manager_) { - static_cast(manager_)->Reorder(id_, - relative->id(), - direction); - } -} - -bool Node::Contains(Node* child) const { - if (manager_) - CHECK_EQ(NodePrivate(child).view_manager(), manager_); - for (Node* p = child->parent(); p; p = p->parent()) { - if (p == this) - return true; - } - return false; -} - -Node* Node::GetChildById(Id id) { - if (id == id_) - return this; - // TODO(beng): this could be improved depending on how we decide to own nodes. - Children::const_iterator it = children_.begin(); - for (; it != children_.end(); ++it) { - Node* node = (*it)->GetChildById(id); - if (node) - return node; - } - return NULL; -} - -void Node::SetActiveView(View* view) { - // TODO(beng): not necessarily valid to all connections, but possibly to the - // embeddee in an embedder-embeddee relationship. - if (manager_) - CHECK_EQ(ViewPrivate(view).view_manager(), manager_); - LocalSetActiveView(view); - if (manager_) { - static_cast(manager_)->SetActiveView( - id_, active_view_->id()); - } -} - -void Node::SetFocus() { - if (manager_) - static_cast(manager_)->SetFocus(id_); -} - -void Node::Embed(const String& url) { - static_cast(manager_)->Embed(url, id_); -} - -scoped_ptr - Node::Embed(const String& url, - scoped_ptr exported_services) { - scoped_ptr imported_services; - // BindToProxy() takes ownership of |exported_services|. - ServiceProviderImpl* registry = exported_services.release(); - ServiceProviderPtr sp; - if (registry) { - BindToProxy(registry, &sp); - imported_services.reset(exported_services->CreateRemoteServiceProvider()); - } - static_cast(manager_)->Embed(url, id_, sp.Pass()); - return imported_services.Pass(); -} - -//////////////////////////////////////////////////////////////////////////////// -// Node, protected: - -Node::Node() - : manager_(NULL), - id_(static_cast(-1)), - parent_(NULL), - active_view_(NULL) {} - -Node::~Node() { - FOR_EACH_OBSERVER(NodeObserver, observers_, OnNodeDestroying(this)); - if (parent_) - parent_->LocalRemoveChild(this); - // TODO(beng): It'd be better to do this via a destruction observer in the - // ViewManagerClientImpl. - if (manager_) - static_cast(manager_)->RemoveNode(id_); - FOR_EACH_OBSERVER(NodeObserver, observers_, OnNodeDestroyed(this)); -} - -//////////////////////////////////////////////////////////////////////////////// -// Node, private: - -Node::Node(ViewManager* manager) - : manager_(manager), - id_(static_cast(manager_)->CreateNode()), - parent_(NULL), - active_view_(NULL) {} - -void Node::LocalDestroy() { - delete this; -} - -void Node::LocalAddChild(Node* child) { - ScopedTreeNotifier notifier(child, child->parent(), this); - if (child->parent()) - RemoveChildImpl(child, &child->parent_->children_); - children_.push_back(child); - child->parent_ = this; -} - -void Node::LocalRemoveChild(Node* child) { - DCHECK_EQ(this, child->parent()); - ScopedTreeNotifier notifier(child, this, NULL); - RemoveChildImpl(child, &children_); -} - -bool Node::LocalReorder(Node* relative, OrderDirection direction) { - return ReorderImpl(&parent_->children_, this, relative, direction); -} - -void Node::LocalSetActiveView(View* view) { - ScopedSetActiveViewNotifier notifier(this, active_view_, view); - if (active_view_) - ViewPrivate(active_view_).set_node(NULL); - active_view_ = view; - if (active_view_) - ViewPrivate(active_view_).set_node(this); -} - -void Node::LocalSetBounds(const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) { - DCHECK(old_bounds == bounds_); - ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds); - bounds_ = new_bounds; -} - -} // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/lib/node_observer.cc b/mojo/services/public/cpp/view_manager/lib/node_observer.cc deleted file mode 100644 index 1bbf155c7334c..0000000000000 --- a/mojo/services/public/cpp/view_manager/lib/node_observer.cc +++ /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. - -#include "mojo/services/public/cpp/view_manager/node_observer.h" - -#include "base/basictypes.h" - -namespace mojo { - -//////////////////////////////////////////////////////////////////////////////// -// NodeObserver, public: - -NodeObserver::TreeChangeParams::TreeChangeParams() - : target(NULL), - old_parent(NULL), - new_parent(NULL), - receiver(NULL) {} - -} // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/lib/node_private.cc b/mojo/services/public/cpp/view_manager/lib/node_private.cc deleted file mode 100644 index 8bf4bb8256807..0000000000000 --- a/mojo/services/public/cpp/view_manager/lib/node_private.cc +++ /dev/null @@ -1,21 +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/services/public/cpp/view_manager/lib/node_private.h" - -namespace mojo { - -NodePrivate::NodePrivate(Node* node) - : node_(node) { -} - -NodePrivate::~NodePrivate() { -} - -// static -Node* NodePrivate::LocalCreate() { - return new Node; -} - -} // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/lib/node_private.h b/mojo/services/public/cpp/view_manager/lib/node_private.h deleted file mode 100644 index 65db923cb6d81..0000000000000 --- a/mojo/services/public/cpp/view_manager/lib/node_private.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_ -#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_ - -#include "base/basictypes.h" - -#include "mojo/services/public/cpp/view_manager/node.h" - -namespace mojo { - -class NodePrivate { - public: - explicit NodePrivate(Node* node); - ~NodePrivate(); - - static Node* LocalCreate(); - - ObserverList* observers() { return &node_->observers_; } - - void ClearParent() { node_->parent_ = NULL; } - - void set_id(Id id) { node_->id_ = id; } - - ViewManager* view_manager() { return node_->manager_; } - void set_view_manager(ViewManager* manager) { - node_->manager_ = manager; - } - - void LocalDestroy() { - node_->LocalDestroy(); - } - void LocalAddChild(Node* child) { - node_->LocalAddChild(child); - } - void LocalRemoveChild(Node* child) { - node_->LocalRemoveChild(child); - } - void LocalReorder(Node* relative, OrderDirection direction) { - node_->LocalReorder(relative, direction); - } - void LocalSetActiveView(View* view) { - node_->LocalSetActiveView(view); - } - void LocalSetBounds(const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) { - node_->LocalSetBounds(old_bounds, new_bounds); - } - - private: - Node* node_; - - DISALLOW_COPY_AND_ASSIGN(NodePrivate); -}; - -} // namespace mojo - -#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_LIB_NODE_PRIVATE_H_ diff --git a/mojo/services/public/cpp/view_manager/lib/view.cc b/mojo/services/public/cpp/view_manager/lib/view.cc index 949a958bd368e..d2f6f54efb84b 100644 --- a/mojo/services/public/cpp/view_manager/lib/view.cc +++ b/mojo/services/public/cpp/view_manager/lib/view.cc @@ -4,27 +4,222 @@ #include "mojo/services/public/cpp/view_manager/view.h" +#include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" #include "mojo/services/public/cpp/view_manager/lib/view_private.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/view_observer.h" #include "ui/gfx/canvas.h" namespace mojo { +namespace { + +void NotifyViewTreeChangeAtReceiver( + View* receiver, + const ViewObserver::TreeChangeParams& params, + bool change_applied) { + ViewObserver::TreeChangeParams local_params = params; + local_params.receiver = receiver; + if (change_applied) { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(receiver).observers(), + OnTreeChanged(local_params)); + } else { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(receiver).observers(), + OnTreeChanging(local_params)); + } +} + +void NotifyViewTreeChangeUp( + View* start_at, + const ViewObserver::TreeChangeParams& params, + bool change_applied) { + for (View* current = start_at; current; current = current->parent()) + NotifyViewTreeChangeAtReceiver(current, params, change_applied); +} + +void NotifyViewTreeChangeDown( + View* start_at, + const ViewObserver::TreeChangeParams& params, + bool change_applied) { + NotifyViewTreeChangeAtReceiver(start_at, params, change_applied); + View::Children::const_iterator it = start_at->children().begin(); + for (; it != start_at->children().end(); ++it) + NotifyViewTreeChangeDown(*it, params, change_applied); +} + +void NotifyViewTreeChange( + const ViewObserver::TreeChangeParams& params, + bool change_applied) { + NotifyViewTreeChangeDown(params.target, params, change_applied); + if (params.old_parent) + NotifyViewTreeChangeUp(params.old_parent, params, change_applied); + if (params.new_parent) + NotifyViewTreeChangeUp(params.new_parent, params, change_applied); +} + +class ScopedTreeNotifier { + public: + ScopedTreeNotifier(View* target, View* old_parent, View* new_parent) { + params_.target = target; + params_.old_parent = old_parent; + params_.new_parent = new_parent; + NotifyViewTreeChange(params_, false); + } + ~ScopedTreeNotifier() { + NotifyViewTreeChange(params_, true); + } + + private: + ViewObserver::TreeChangeParams params_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTreeNotifier); +}; + +void RemoveChildImpl(View* child, View::Children* children) { + View::Children::iterator it = + std::find(children->begin(), children->end(), child); + if (it != children->end()) { + children->erase(it); + ViewPrivate(child).ClearParent(); + } +} + +class ScopedOrderChangedNotifier { + public: + ScopedOrderChangedNotifier(View* view, + View* relative_view, + OrderDirection direction) + : view_(view), + relative_view_(relative_view), + direction_(direction) { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(view_).observers(), + OnViewReordering(view_, relative_view_, direction_)); + } + ~ScopedOrderChangedNotifier() { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(view_).observers(), + OnViewReordered(view_, relative_view_, direction_)); + } + + private: + View* view_; + View* relative_view_; + OrderDirection direction_; + + DISALLOW_COPY_AND_ASSIGN(ScopedOrderChangedNotifier); +}; + +// Returns true if the order actually changed. +bool ReorderImpl(View::Children* children, + View* view, + View* relative, + OrderDirection direction) { + DCHECK(relative); + DCHECK_NE(view, relative); + DCHECK_EQ(view->parent(), relative->parent()); + + const size_t child_i = + std::find(children->begin(), children->end(), view) - children->begin(); + const size_t target_i = + std::find(children->begin(), children->end(), relative) - + children->begin(); + if ((direction == ORDER_DIRECTION_ABOVE && child_i == target_i + 1) || + (direction == ORDER_DIRECTION_BELOW && child_i + 1 == target_i)) { + return false; + } + + ScopedOrderChangedNotifier notifier(view, relative, direction); + + const size_t dest_i = direction == ORDER_DIRECTION_ABOVE + ? (child_i < target_i ? target_i : target_i + 1) + : (child_i < target_i ? target_i - 1 : target_i); + children->erase(children->begin() + child_i); + children->insert(children->begin() + dest_i, view); + + return true; +} + +class ScopedSetBoundsNotifier { + public: + ScopedSetBoundsNotifier(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) + : view_(view), + old_bounds_(old_bounds), + new_bounds_(new_bounds) { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(view_).observers(), + OnViewBoundsChanging(view_, old_bounds_, new_bounds_)); + } + ~ScopedSetBoundsNotifier() { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(view_).observers(), + OnViewBoundsChanged(view_, old_bounds_, new_bounds_)); + } + + private: + View* view_; + const gfx::Rect old_bounds_; + const gfx::Rect new_bounds_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSetBoundsNotifier); +}; + +// Some operations are only permitted in the connection that created the view. +bool OwnsView(ViewManager* manager, View* view) { + return !manager || + static_cast(manager)->OwnsView(view->id()); +} + +} // namespace + +//////////////////////////////////////////////////////////////////////////////// +// View, public: + // static -View* View::Create(ViewManager* manager) { - View* view = new View(manager); - static_cast(manager)->AddView(view); +View* View::Create(ViewManager* view_manager) { + View* view = new View(view_manager); + static_cast(view_manager)->AddView(view); return view; } void View::Destroy() { + if (!OwnsView(manager_, this)) + return; + if (manager_) static_cast(manager_)->DestroyView(id_); + while (!children_.empty()) { + View* child = children_.front(); + if (!OwnsView(manager_, child)) { + ViewPrivate(child).ClearParent(); + children_.erase(children_.begin()); + } else { + child->Destroy(); + DCHECK(std::find(children_.begin(), children_.end(), child) == + children_.end()); + } + } LocalDestroy(); } +void View::SetBounds(const gfx::Rect& bounds) { + if (!OwnsView(manager_, this)) + return; + + if (manager_) + static_cast(manager_)->SetBounds(id_, bounds); + LocalSetBounds(bounds_, bounds); +} + +void View::SetVisible(bool value) { + if (manager_) + static_cast(manager_)->SetVisible(id_, value); +} + void View::AddObserver(ViewObserver* observer) { observers_.AddObserver(observer); } @@ -33,41 +228,160 @@ void View::RemoveObserver(ViewObserver* observer) { observers_.RemoveObserver(observer); } +void View::AddChild(View* child) { + // TODO(beng): not necessarily valid to all connections, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (manager_) + CHECK_EQ(ViewPrivate(child).view_manager(), manager_); + LocalAddChild(child); + if (manager_) + static_cast(manager_)->AddChild(child->id(), id_); +} + +void View::RemoveChild(View* child) { + // TODO(beng): not necessarily valid to all connections, but possibly to the + // embeddee in an embedder-embeddee relationship. + if (manager_) + CHECK_EQ(ViewPrivate(child).view_manager(), manager_); + LocalRemoveChild(child); + if (manager_) { + static_cast(manager_)->RemoveChild(child->id(), + id_); + } +} + +void View::MoveToFront() { + Reorder(parent_->children_.back(), ORDER_DIRECTION_ABOVE); +} + +void View::MoveToBack() { + Reorder(parent_->children_.front(), ORDER_DIRECTION_BELOW); +} + +void View::Reorder(View* relative, OrderDirection direction) { + if (!LocalReorder(relative, direction)) + return; + if (manager_) { + static_cast(manager_)->Reorder(id_, + relative->id(), + direction); + } +} + +bool View::Contains(View* child) const { + if (manager_) + CHECK_EQ(ViewPrivate(child).view_manager(), manager_); + for (View* p = child->parent(); p; p = p->parent()) { + if (p == this) + return true; + } + return false; +} + +View* View::GetChildById(Id id) { + if (id == id_) + return this; + // TODO(beng): this could be improved depending on how we decide to own views. + Children::const_iterator it = children_.begin(); + for (; it != children_.end(); ++it) { + View* view = (*it)->GetChildById(id); + if (view) + return view; + } + return NULL; +} + void View::SetContents(const SkBitmap& contents) { if (manager_) { static_cast(manager_)->SetViewContents(id_, - contents); + contents); } } void View::SetColor(SkColor color) { - gfx::Canvas canvas(node_->bounds().size(), 1.0f, true); + gfx::Canvas canvas(bounds_.size(), 1.0f, true); canvas.DrawColor(color); SetContents(skia::GetTopDevice(*canvas.sk_canvas())->accessBitmap(true)); } -View::View(ViewManager* manager) - : id_(static_cast(manager)->CreateView()), - node_(NULL), - manager_(manager) {} +void View::SetFocus() { + if (manager_) + static_cast(manager_)->SetFocus(id_); +} + +void View::Embed(const String& url) { + static_cast(manager_)->Embed(url, id_); +} + +scoped_ptr + View::Embed(const String& url, + scoped_ptr exported_services) { + scoped_ptr imported_services; + // BindToProxy() takes ownership of |exported_services|. + ServiceProviderImpl* registry = exported_services.release(); + ServiceProviderPtr sp; + if (registry) { + BindToProxy(registry, &sp); + imported_services.reset(registry->CreateRemoteServiceProvider()); + } + static_cast(manager_)->Embed(url, id_, sp.Pass()); + return imported_services.Pass(); +} + +//////////////////////////////////////////////////////////////////////////////// +// View, protected: View::View() - : id_(static_cast(-1)), - node_(NULL), - manager_(NULL) {} + : manager_(NULL), + id_(static_cast(-1)), + parent_(NULL) {} View::~View() { FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroying(this)); + if (parent_) + parent_->LocalRemoveChild(this); // TODO(beng): It'd be better to do this via a destruction observer in the // ViewManagerClientImpl. if (manager_) static_cast(manager_)->RemoveView(id_); - FOR_EACH_OBSERVER(ViewObserver, observers_, OnViewDestroyed(this)); } +//////////////////////////////////////////////////////////////////////////////// +// View, private: + +View::View(ViewManager* manager) + : manager_(manager), + id_(static_cast(manager_)->CreateView()), + parent_(NULL) {} + void View::LocalDestroy() { delete this; } +void View::LocalAddChild(View* child) { + ScopedTreeNotifier notifier(child, child->parent(), this); + if (child->parent()) + RemoveChildImpl(child, &child->parent_->children_); + children_.push_back(child); + child->parent_ = this; +} + +void View::LocalRemoveChild(View* child) { + DCHECK_EQ(this, child->parent()); + ScopedTreeNotifier notifier(child, this, NULL); + RemoveChildImpl(child, &children_); +} + +bool View::LocalReorder(View* relative, OrderDirection direction) { + return ReorderImpl(&parent_->children_, this, relative, direction); +} + +void View::LocalSetBounds(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + DCHECK(old_bounds == bounds_); + ScopedSetBoundsNotifier notifier(this, old_bounds, new_bounds); + bounds_ = new_bounds; +} + } // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc index 9d66d5d104c35..791d1222e009b 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc +++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_factory.cc @@ -20,7 +20,7 @@ ViewManagerClientFactory::~ViewManagerClientFactory() { void ViewManagerClientFactory::Create( ApplicationConnection* connection, InterfaceRequest request) { - BindToRequest(new ViewManagerClientImpl(delegate_), &request); + BindToRequest(new ViewManagerClientImpl(delegate_, connection), &request); } } // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc index 32e568c9a5b10..ec4ec6125e49e 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc +++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc @@ -5,15 +5,14 @@ #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" #include "base/bind.h" +#include "base/command_line.h" #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/services/public/cpp/view_manager/lib/node_private.h" #include "mojo/services/public/cpp/view_manager/lib/view_private.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/util.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" #include "mojo/services/public/cpp/view_manager/view_observer.h" @@ -28,84 +27,69 @@ Id MakeTransportId(ConnectionSpecificId connection_id, return (connection_id << 16) | local_id; } -// Helper called to construct a local node/view object from transport data. -Node* AddNodeToViewManager(ViewManagerClientImpl* client, - Node* parent, - Id node_id, +// Helper called to construct a local view object from transport data. +View* AddViewToViewManager(ViewManagerClientImpl* client, + View* parent, Id view_id, const gfx::Rect& bounds) { // We don't use the ctor that takes a ViewManager here, since it will call - // back to the service and attempt to create a new node. - Node* node = NodePrivate::LocalCreate(); - NodePrivate private_node(node); - private_node.set_view_manager(client); - private_node.set_id(node_id); - client->AddNode(node); - private_node.LocalSetBounds(gfx::Rect(), bounds); + // back to the service and attempt to create a new view. + View* view = ViewPrivate::LocalCreate(); + ViewPrivate private_view(view); + private_view.set_view_manager(client); + private_view.set_id(view_id); + client->AddView(view); + private_view.LocalSetBounds(gfx::Rect(), bounds); if (parent) - NodePrivate(parent).LocalAddChild(node); - - // View. - if (view_id != 0) { - View* view = ViewPrivate::LocalCreate(); - ViewPrivate private_view(view); - private_view.set_view_manager(client); - private_view.set_id(view_id); - private_view.set_node(node); - client->AddView(view); - // TODO(beng): this broadcasts notifications locally... do we want this? I - // don't think so. same story for LocalAddChild above! - private_node.LocalSetActiveView(view); - } - return node; + ViewPrivate(parent).LocalAddChild(view); + return view; } -Node* BuildNodeTree(ViewManagerClientImpl* client, - const Array& nodes, - Node* initial_parent) { - std::vector parents; - Node* root = NULL; - Node* last_node = NULL; +View* BuildViewTree(ViewManagerClientImpl* client, + const Array& views, + View* initial_parent) { + std::vector parents; + View* root = NULL; + View* last_view = NULL; if (initial_parent) parents.push_back(initial_parent); - for (size_t i = 0; i < nodes.size(); ++i) { - if (last_node && nodes[i]->parent_id == last_node->id()) { - parents.push_back(last_node); + for (size_t i = 0; i < views.size(); ++i) { + if (last_view && views[i]->parent_id == last_view->id()) { + parents.push_back(last_view); } else if (!parents.empty()) { - while (parents.back()->id() != nodes[i]->parent_id) + while (parents.back()->id() != views[i]->parent_id) parents.pop_back(); } - Node* node = AddNodeToViewManager( + View* view = AddViewToViewManager( client, !parents.empty() ? parents.back() : NULL, - nodes[i]->node_id, - nodes[i]->view_id, - nodes[i]->bounds.To()); - if (!last_node) - root = node; - last_node = node; + views[i]->view_id, + views[i]->bounds.To()); + if (!last_view) + root = view; + last_view = view; } return root; } -// Responsible for removing a root from the ViewManager when that node is +// Responsible for removing a root from the ViewManager when that view is // destroyed. -class RootObserver : public NodeObserver { +class RootObserver : public ViewObserver { public: - explicit RootObserver(Node* root) : root_(root) {} + explicit RootObserver(View* root) : root_(root) {} virtual ~RootObserver() {} private: - // Overridden from NodeObserver: - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK_EQ(node, root_); + // Overridden from ViewObserver: + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK_EQ(view, root_); static_cast( - NodePrivate(root_).view_manager())->RemoveRoot(root_); - node->RemoveObserver(this); + ViewPrivate(root_).view_manager())->RemoveRoot(root_); + view->RemoveObserver(this); delete this; } - Node* root_; + View* root_; DISALLOW_COPY_AND_ASSIGN(RootObserver); }; @@ -133,34 +117,39 @@ bool CreateMapAndDupSharedBuffer(size_t size, return true; } -ViewManagerClientImpl::ViewManagerClientImpl(ViewManagerDelegate* delegate) +ViewManagerClientImpl::ViewManagerClientImpl( + ViewManagerDelegate* delegate, + ApplicationConnection* app_connection) : connected_(false), connection_id_(0), next_id_(1), delegate_(delegate), window_manager_delegate_(NULL) { + // TODO(beng): Come up with a better way of establishing a configuration for + // what the active window manager is. + std::string window_manager_url = "mojo:mojo_window_manager"; + if (base::CommandLine::ForCurrentProcess()->HasSwitch("window-manager")) { + window_manager_url = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + "window-manager"); + } + app_connection->ConnectToService(window_manager_url, &window_manager_); + window_manager_.set_client(this); } ViewManagerClientImpl::~ViewManagerClientImpl() { - std::vector non_owned; - while (!nodes_.empty()) { - IdToNodeMap::iterator it = nodes_.begin(); - if (OwnsNode(it->second->id())) { - it->second->Destroy(); - } else { - non_owned.push_back(it->second); - nodes_.erase(it); - } - } + std::vector non_owned; while (!views_.empty()) { IdToViewMap::iterator it = views_.begin(); - if (OwnsView(it->second->id())) + if (OwnsView(it->second->id())) { it->second->Destroy(); - else + } else { + non_owned.push_back(it->second); views_.erase(it); + } } - // Delete the non-owned nodes last. In the typical case these are roots. The - // exception is the window manager, which may know aboutother random nodes + // Delete the non-owned views last. In the typical case these are roots. The + // exception is the window manager, which may know aboutother random views // that it doesn't own. // NOTE: we manually delete as we're a friend. for (size_t i = 0; i < non_owned.size(); ++i) @@ -168,22 +157,10 @@ ViewManagerClientImpl::~ViewManagerClientImpl() { delegate_->OnViewManagerDisconnected(this); } -Id ViewManagerClientImpl::CreateNode() { - DCHECK(connected_); - const Id node_id = MakeTransportId(connection_id_, ++next_id_); - service_->CreateNode(node_id, ActionCompletedCallbackWithErrorCode()); - return node_id; -} - -void ViewManagerClientImpl::DestroyNode(Id node_id) { - DCHECK(connected_); - service_->DeleteNode(node_id, ActionCompletedCallback()); -} - Id ViewManagerClientImpl::CreateView() { DCHECK(connected_); const Id view_id = MakeTransportId(connection_id_, ++next_id_); - service_->CreateView(view_id, ActionCompletedCallback()); + service_->CreateView(view_id, ActionCompletedCallbackWithErrorCode()); return view_id; } @@ -194,39 +171,30 @@ void ViewManagerClientImpl::DestroyView(Id view_id) { void ViewManagerClientImpl::AddChild(Id child_id, Id parent_id) { DCHECK(connected_); - service_->AddNode(parent_id, child_id, ActionCompletedCallback()); + service_->AddView(parent_id, child_id, ActionCompletedCallback()); } void ViewManagerClientImpl::RemoveChild(Id child_id, Id parent_id) { DCHECK(connected_); - service_->RemoveNodeFromParent(child_id, ActionCompletedCallback()); + service_->RemoveViewFromParent(child_id, ActionCompletedCallback()); } void ViewManagerClientImpl::Reorder( - Id node_id, - Id relative_node_id, + Id view_id, + Id relative_view_id, OrderDirection direction) { DCHECK(connected_); - service_->ReorderNode(node_id, relative_node_id, direction, + service_->ReorderView(view_id, relative_view_id, direction, ActionCompletedCallback()); } -bool ViewManagerClientImpl::OwnsNode(Id id) const { - return HiWord(id) == connection_id_; -} - bool ViewManagerClientImpl::OwnsView(Id id) const { return HiWord(id) == connection_id_; } -void ViewManagerClientImpl::SetActiveView(Id node_id, Id view_id) { - DCHECK(connected_); - service_->SetView(node_id, view_id, ActionCompletedCallback()); -} - -void ViewManagerClientImpl::SetBounds(Id node_id, const gfx::Rect& bounds) { +void ViewManagerClientImpl::SetBounds(Id view_id, const gfx::Rect& bounds) { DCHECK(connected_); - service_->SetNodeBounds(node_id, Rect::From(bounds), + service_->SetViewBounds(view_id, Rect::From(bounds), ActionCompletedCallback()); } @@ -252,42 +220,30 @@ void ViewManagerClientImpl::SetViewContents(Id view_id, ActionCompletedCallback()); } -void ViewManagerClientImpl::SetFocus(Id node_id) { - DCHECK(connected_); - service_->SetFocus(node_id, ActionCompletedCallback()); +void ViewManagerClientImpl::SetFocus(Id view_id) { + window_manager_->FocusWindow(view_id, ActionCompletedCallback()); } -void ViewManagerClientImpl::SetVisible(Id node_id, bool visible) { +void ViewManagerClientImpl::SetVisible(Id view_id, bool visible) { DCHECK(connected_); - service_->SetNodeVisibility(node_id, visible, ActionCompletedCallback()); + service_->SetViewVisibility(view_id, visible, ActionCompletedCallback()); } -void ViewManagerClientImpl::Embed(const String& url, Id node_id) { +void ViewManagerClientImpl::Embed(const String& url, Id view_id) { ServiceProviderPtr sp; BindToProxy(new ServiceProviderImpl, &sp); - Embed(url, node_id, sp.Pass()); + Embed(url, view_id, sp.Pass()); } void ViewManagerClientImpl::Embed( const String& url, - Id node_id, + Id view_id, ServiceProviderPtr service_provider) { DCHECK(connected_); - service_->Embed(url, node_id, service_provider.Pass(), + service_->Embed(url, view_id, service_provider.Pass(), ActionCompletedCallback()); } -void ViewManagerClientImpl::AddNode(Node* node) { - DCHECK(nodes_.find(node->id()) == nodes_.end()); - nodes_[node->id()] = node; -} - -void ViewManagerClientImpl::RemoveNode(Id node_id) { - IdToNodeMap::iterator it = nodes_.find(node_id); - if (it != nodes_.end()) - nodes_.erase(it); -} - void ViewManagerClientImpl::AddView(View* view) { DCHECK(views_.find(view->id()) == views_.end()); views_[view->id()] = view; @@ -304,7 +260,8 @@ void ViewManagerClientImpl::RemoveView(Id view_id) { void ViewManagerClientImpl::SetWindowManagerDelegate( WindowManagerDelegate* window_manager_delegate) { - CHECK(NULL != GetNodeById(1)); + CHECK(NULL != GetViewById(1)); + CHECK(!window_manager_delegate_); window_manager_delegate_ = window_manager_delegate; } @@ -317,15 +274,10 @@ const std::string& ViewManagerClientImpl::GetEmbedderURL() const { return creator_url_; } -const std::vector& ViewManagerClientImpl::GetRoots() const { +const std::vector& ViewManagerClientImpl::GetRoots() const { return roots_; } -Node* ViewManagerClientImpl::GetNodeById(Id id) { - IdToNodeMap::const_iterator it = nodes_.find(id); - return it != nodes_.end() ? it->second : NULL; -} - View* ViewManagerClientImpl::GetViewById(Id id) { IdToViewMap::const_iterator it = views_.find(id); return it != views_.end() ? it->second : NULL; @@ -344,7 +296,7 @@ void ViewManagerClientImpl::OnConnectionEstablished() { void ViewManagerClientImpl::OnEmbed( ConnectionSpecificId connection_id, const String& creator_url, - NodeDataPtr root_data, + ViewDataPtr root_data, InterfaceRequest service_provider) { if (!connected_) { connected_ = true; @@ -357,8 +309,7 @@ void ViewManagerClientImpl::OnEmbed( // A new root must not already exist as a root or be contained by an existing // hierarchy visible to this view manager. - Node* root = AddNodeToViewManager(this, NULL, root_data->node_id, - root_data->view_id, + View* root = AddViewToViewManager(this, NULL, root_data->view_id, root_data->bounds.To()); roots_.push_back(root); root->AddObserver(new RootObserver(root)); @@ -371,65 +322,40 @@ void ViewManagerClientImpl::OnEmbed( delegate_->OnEmbed(this, root, exported_services, remote.Pass()); } -void ViewManagerClientImpl::OnNodeBoundsChanged(Id node_id, +void ViewManagerClientImpl::OnViewBoundsChanged(Id view_id, RectPtr old_bounds, RectPtr new_bounds) { - Node* node = GetNodeById(node_id); - NodePrivate(node).LocalSetBounds(old_bounds.To(), + View* view = GetViewById(view_id); + ViewPrivate(view).LocalSetBounds(old_bounds.To(), new_bounds.To()); } -void ViewManagerClientImpl::OnNodeHierarchyChanged( - Id node_id, +void ViewManagerClientImpl::OnViewHierarchyChanged( + Id view_id, Id new_parent_id, Id old_parent_id, - mojo::Array nodes) { - Node* initial_parent = nodes.size() ? - GetNodeById(nodes[0]->parent_id) : NULL; + mojo::Array views) { + View* initial_parent = views.size() ? + GetViewById(views[0]->parent_id) : NULL; - BuildNodeTree(this, nodes, initial_parent); + BuildViewTree(this, views, initial_parent); - Node* new_parent = GetNodeById(new_parent_id); - Node* old_parent = GetNodeById(old_parent_id); - Node* node = GetNodeById(node_id); + View* new_parent = GetViewById(new_parent_id); + View* old_parent = GetViewById(old_parent_id); + View* view = GetViewById(view_id); if (new_parent) - NodePrivate(new_parent).LocalAddChild(node); + ViewPrivate(new_parent).LocalAddChild(view); else - NodePrivate(old_parent).LocalRemoveChild(node); + ViewPrivate(old_parent).LocalRemoveChild(view); } -void ViewManagerClientImpl::OnNodeReordered(Id node_id, - Id relative_node_id, +void ViewManagerClientImpl::OnViewReordered(Id view_id, + Id relative_view_id, OrderDirection direction) { - Node* node = GetNodeById(node_id); - Node* relative_node = GetNodeById(relative_node_id); - if (node && relative_node) - NodePrivate(node).LocalReorder(relative_node, direction); -} - -void ViewManagerClientImpl::OnNodeDeleted(Id node_id) { - Node* node = GetNodeById(node_id); - if (node) - NodePrivate(node).LocalDestroy(); -} - -void ViewManagerClientImpl::OnNodeViewReplaced(Id node_id, - Id new_view_id, - Id old_view_id) { - Node* node = GetNodeById(node_id); - View* new_view = GetViewById(new_view_id); - if (!new_view && new_view_id != 0) { - // This client wasn't aware of this View until now. - new_view = ViewPrivate::LocalCreate(); - ViewPrivate private_view(new_view); - private_view.set_view_manager(this); - private_view.set_id(new_view_id); - private_view.set_node(node); - AddView(new_view); - } - View* old_view = GetViewById(old_view_id); - DCHECK_EQ(old_view, node->active_view()); - NodePrivate(node).LocalSetActiveView(new_view); + View* view = GetViewById(view_id); + View* relative_view = GetViewById(relative_view_id); + if (view && relative_view) + ViewPrivate(view).LocalReorder(relative_view, direction); } void ViewManagerClientImpl::OnViewDeleted(Id view_id) { @@ -451,38 +377,49 @@ void ViewManagerClientImpl::OnViewInputEvent( ack_callback.Run(); } -void ViewManagerClientImpl::OnFocusChanged(Id gained_focus_id, - Id lost_focus_id) { - Node* focused = GetNodeById(gained_focus_id); - Node* blurred = GetNodeById(lost_focus_id); - if (blurred) { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(blurred).observers(), - OnNodeFocusChanged(focused, blurred)); - } - if (focused) { - FOR_EACH_OBSERVER(NodeObserver, - *NodePrivate(focused).observers(), - OnNodeFocusChanged(focused, blurred)); - } -} - void ViewManagerClientImpl::Embed( const String& url, InterfaceRequest service_provider) { window_manager_delegate_->Embed(url, service_provider.Pass()); } -void ViewManagerClientImpl::DispatchOnViewInputEvent(Id view_id, - EventPtr event) { - window_manager_delegate_->DispatchEvent(GetViewById(view_id), event.Pass()); +void ViewManagerClientImpl::DispatchOnViewInputEvent(EventPtr event) { + if (window_manager_delegate_) + window_manager_delegate_->DispatchEvent(event.Pass()); } +//////////////////////////////////////////////////////////////////////////////// +// ViewManagerClientImpl, WindowManagerClient implementation: + +void ViewManagerClientImpl::OnWindowManagerReady() {} + +void ViewManagerClientImpl::OnCaptureChanged(Id old_capture_view_id, + Id new_capture_view_id) {} + +void ViewManagerClientImpl::OnFocusChanged(Id old_focused_view_id, + Id new_focused_view_id) { + View* focused = GetViewById(new_focused_view_id); + View* blurred = GetViewById(old_focused_view_id); + if (blurred) { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(blurred).observers(), + OnViewFocusChanged(focused, blurred)); + } + if (focused) { + FOR_EACH_OBSERVER(ViewObserver, + *ViewPrivate(focused).observers(), + OnViewFocusChanged(focused, blurred)); + } +} + +void ViewManagerClientImpl::OnActiveWindowChanged(Id old_focused_window, + Id new_focused_window) {} + //////////////////////////////////////////////////////////////////////////////// // ViewManagerClientImpl, private: -void ViewManagerClientImpl::RemoveRoot(Node* root) { - std::vector::iterator it = +void ViewManagerClientImpl::RemoveRoot(View* root) { + std::vector::iterator it = std::find(roots_.begin(), roots_.end(), root); if (it != roots_.end()) roots_.erase(it); diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h index fb983e66e613c..38ed6de9e99d4 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h +++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h @@ -10,10 +10,11 @@ #include "base/memory/scoped_vector.h" #include "base/memory/weak_ptr.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" +#include "mojo/services/public/interfaces/window_manager/window_manager.mojom.h" class SkBitmap; @@ -25,43 +26,40 @@ class ViewManagerTransaction; // Manages the connection with the View Manager service. class ViewManagerClientImpl : public ViewManager, - public InterfaceImpl { + public InterfaceImpl, + public WindowManagerClient { public: - explicit ViewManagerClientImpl(ViewManagerDelegate* delegate); + ViewManagerClientImpl(ViewManagerDelegate* delegate, + ApplicationConnection* app_connection); virtual ~ViewManagerClientImpl(); bool connected() const { return connected_; } ConnectionSpecificId connection_id() const { return connection_id_; } - // API exposed to the node/view implementations that pushes local changes to - // the service. - Id CreateNode(); - void DestroyNode(Id node_id); - + // API exposed to the view implementations that pushes local changes to the + // service. Id CreateView(); void DestroyView(Id view_id); // These methods take TransportIds. For views owned by the current connection, // the connection id high word can be zero. In all cases, the TransportId 0x1 - // refers to the root node. + // refers to the root view. void AddChild(Id child_id, Id parent_id); void RemoveChild(Id child_id, Id parent_id); - void Reorder(Id node_id, Id relative_node_id, OrderDirection direction); + void Reorder(Id view_id, Id relative_view_id, OrderDirection direction); - // Returns true if the specified node/view was created by this connection. - bool OwnsNode(Id id) const; + // Returns true if the specified view was created by this connection. bool OwnsView(Id id) const; - void SetActiveView(Id node_id, Id view_id); - void SetBounds(Id node_id, const gfx::Rect& bounds); + void SetBounds(Id view_id, const gfx::Rect& bounds); void SetViewContents(Id view_id, const SkBitmap& contents); - void SetFocus(Id node_id); - void SetVisible(Id node_id, bool visible); + void SetFocus(Id view_id); + void SetVisible(Id view_id, bool visible); - void Embed(const String& url, Id node_id); + void Embed(const String& url, Id view_id); void Embed(const String& url, - Id node_id, + Id view_id, ServiceProviderPtr service_provider); void set_change_acked_callback(const base::Callback& callback) { @@ -71,18 +69,14 @@ class ViewManagerClientImpl : public ViewManager, change_acked_callback_ = base::Callback(); } - // Start/stop tracking nodes & views. While tracked, they can be retrieved via - // ViewManager::GetNode/ViewById. - void AddNode(Node* node); - void RemoveNode(Id node_id); - + // Start/stop tracking views. While tracked, they can be retrieved via + // ViewManager::GetViewById. void AddView(View* view); void RemoveView(Id view_id); private: friend class RootObserver; - typedef std::map IdToNodeMap; typedef std::map IdToViewMap; // Overridden from ViewManager: @@ -90,8 +84,7 @@ class ViewManagerClientImpl : public ViewManager, WindowManagerDelegate* delegate) OVERRIDE; virtual void DispatchEvent(View* target, EventPtr event) OVERRIDE; virtual const std::string& GetEmbedderURL() const OVERRIDE; - virtual const std::vector& GetRoots() const OVERRIDE; - virtual Node* GetNodeById(Id id) OVERRIDE; + virtual const std::vector& GetRoots() const OVERRIDE; virtual View* GetViewById(Id id) OVERRIDE; // Overridden from InterfaceImpl: @@ -100,33 +93,37 @@ class ViewManagerClientImpl : public ViewManager, // Overridden from ViewManagerClient: virtual void OnEmbed(ConnectionSpecificId connection_id, const String& creator_url, - NodeDataPtr root, + ViewDataPtr root, InterfaceRequest services) OVERRIDE; - virtual void OnNodeBoundsChanged(Id node_id, + virtual void OnViewBoundsChanged(Id view_id, RectPtr old_bounds, RectPtr new_bounds) OVERRIDE; - virtual void OnNodeHierarchyChanged(Id node_id, + virtual void OnViewHierarchyChanged(Id view_id, Id new_parent_id, Id old_parent_id, - Array nodes) OVERRIDE; - virtual void OnNodeReordered(Id node_id, - Id relative_node_id, + Array views) OVERRIDE; + virtual void OnViewReordered(Id view_id, + Id relative_view_id, OrderDirection direction) OVERRIDE; - virtual void OnNodeDeleted(Id node_id) OVERRIDE; - virtual void OnNodeViewReplaced(Id node, - Id new_view_id, - Id old_view_id) OVERRIDE; virtual void OnViewDeleted(Id view_id) OVERRIDE; virtual void OnViewInputEvent(Id view, EventPtr event, const Callback& callback) OVERRIDE; - virtual void OnFocusChanged(Id gained_focus_id, Id lost_focus_id) OVERRIDE; virtual void Embed( const String& url, InterfaceRequest service_provider) OVERRIDE; - virtual void DispatchOnViewInputEvent(Id view_id, EventPtr event) OVERRIDE; + virtual void DispatchOnViewInputEvent(EventPtr event) OVERRIDE; - void RemoveRoot(Node* root); + // Overridden from WindowManagerClient: + virtual void OnWindowManagerReady() OVERRIDE; + virtual void OnCaptureChanged(Id old_capture_view_id, + Id new_capture_view_id) OVERRIDE; + virtual void OnFocusChanged(Id old_focused_view_id, + Id new_focused_view_id) OVERRIDE; + virtual void OnActiveWindowChanged(Id old_focused_window, + Id new_focused_window) OVERRIDE; + + void RemoveRoot(View* root); void OnActionCompleted(bool success); void OnActionCompletedWithErrorCode(ErrorCode code); @@ -145,13 +142,14 @@ class ViewManagerClientImpl : public ViewManager, ViewManagerDelegate* delegate_; WindowManagerDelegate* window_manager_delegate_; - std::vector roots_; + std::vector roots_; - IdToNodeMap nodes_; IdToViewMap views_; ViewManagerService* service_; + WindowManagerServicePtr window_manager_; + DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl); }; diff --git a/mojo/services/public/cpp/view_manager/lib/view_observer.cc b/mojo/services/public/cpp/view_manager/lib/view_observer.cc new file mode 100644 index 0000000000000..812c028dce8d7 --- /dev/null +++ b/mojo/services/public/cpp/view_manager/lib/view_observer.cc @@ -0,0 +1,20 @@ +// 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/services/public/cpp/view_manager/view_observer.h" + +#include "base/basictypes.h" + +namespace mojo { + +//////////////////////////////////////////////////////////////////////////////// +// ViewObserver, public: + +ViewObserver::TreeChangeParams::TreeChangeParams() + : target(NULL), + old_parent(NULL), + new_parent(NULL), + receiver(NULL) {} + +} // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/lib/view_private.h b/mojo/services/public/cpp/view_manager/lib/view_private.h index 9be1b247acecf..91736885fb9be 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_private.h +++ b/mojo/services/public/cpp/view_manager/lib/view_private.h @@ -17,19 +17,34 @@ class ViewPrivate { ~ViewPrivate(); static View* LocalCreate(); - void LocalDestroy() { - view_->LocalDestroy(); - } + + ObserverList* observers() { return &view_->observers_; } + + void ClearParent() { view_->parent_ = NULL; } void set_id(Id id) { view_->id_ = id; } - void set_node(Node* node) { view_->node_ = node; } ViewManager* view_manager() { return view_->manager_; } void set_view_manager(ViewManager* manager) { view_->manager_ = manager; } - ObserverList* observers() { return &view_->observers_; } + void LocalDestroy() { + view_->LocalDestroy(); + } + void LocalAddChild(View* child) { + view_->LocalAddChild(child); + } + void LocalRemoveChild(View* child) { + view_->LocalRemoveChild(child); + } + void LocalReorder(View* relative, OrderDirection direction) { + view_->LocalReorder(relative, direction); + } + void LocalSetBounds(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + view_->LocalSetBounds(old_bounds, new_bounds); + } private: View* view_; diff --git a/mojo/services/public/cpp/view_manager/node.h b/mojo/services/public/cpp/view_manager/node.h deleted file mode 100644 index aee4b21d77ec6..0000000000000 --- a/mojo/services/public/cpp/view_manager/node.h +++ /dev/null @@ -1,116 +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_PUBLIC_CPP_VIEW_MANAGER_NODE_H_ -#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_ - -#include - -#include "base/basictypes.h" -#include "base/observer_list.h" -#include "mojo/public/cpp/bindings/array.h" -#include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/services/public/cpp/view_manager/types.h" -#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h" -#include "ui/gfx/geometry/rect.h" - -namespace mojo { - -class ServiceProviderImpl; -class View; -class ViewManager; -class NodeObserver; - -// Nodes are owned by the ViewManager. -// TODO(beng): Right now, you'll have to implement a NodeObserver to track -// destruction and NULL any pointers you have. -// Investigate some kind of smart pointer or weak pointer for these. -class Node { - public: - typedef std::vector Children; - - static Node* Create(ViewManager* view_manager); - - // Destroys this node and all its children. - void Destroy(); - - // Configuration. - Id id() const { return id_; } - - // Geometric disposition. - const gfx::Rect& bounds() { return bounds_; } - void SetBounds(const gfx::Rect& bounds); - - // Visibility. - void SetVisible(bool value); - // TODO(sky): add accessor. - - // Observation. - void AddObserver(NodeObserver* observer); - void RemoveObserver(NodeObserver* observer); - - // Tree. - Node* parent() { return parent_; } - const Node* parent() const { return parent_; } - const Children& children() const { return children_; } - - void AddChild(Node* child); - void RemoveChild(Node* child); - - void Reorder(Node* relative, OrderDirection direction); - void MoveToFront(); - void MoveToBack(); - - bool Contains(Node* child) const; - - Node* GetChildById(Id id); - - // View. - void SetActiveView(View* view); - View* active_view() { return active_view_; } - - // Focus. - void SetFocus(); - - // Embedding. - void Embed(const String& url); - scoped_ptr Embed( - const String& url, - scoped_ptr exported_services); - - protected: - // This class is subclassed only by test classes that provide a public ctor. - Node(); - ~Node(); - - private: - friend class NodePrivate; - friend class ViewManagerClientImpl; - - explicit Node(ViewManager* manager); - - void LocalDestroy(); - void LocalAddChild(Node* child); - void LocalRemoveChild(Node* child); - // Returns true if the order actually changed. - bool LocalReorder(Node* relative, OrderDirection direction); - void LocalSetActiveView(View* view); - void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds); - - ViewManager* manager_; - Id id_; - Node* parent_; - Children children_; - - ObserverList observers_; - - gfx::Rect bounds_; - View* active_view_; - - DISALLOW_COPY_AND_ASSIGN(Node); -}; - -} // namespace mojo - -#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_H_ diff --git a/mojo/services/public/cpp/view_manager/node_observer.h b/mojo/services/public/cpp/view_manager/node_observer.h deleted file mode 100644 index d32894cbbe983..0000000000000 --- a/mojo/services/public/cpp/view_manager/node_observer.h +++ /dev/null @@ -1,78 +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_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_ -#define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_ - -#include - -#include "base/basictypes.h" - -#include "mojo/services/public/cpp/view_manager/node.h" - -namespace gfx { -class Rect; -} - -namespace mojo { - -class Node; -class View; - -// A note on -ing and -ed suffixes: -// -// -ing methods are called before changes are applied to the local node model. -// -ed methods are called after changes are applied to the local node model. -// -// If the change originated from another connection to the view manager, it's -// possible that the change has already been applied to the service-side model -// prior to being called, so for example in the case of OnNodeDestroying(), it's -// possible the node has already been destroyed on the service side. - -class NodeObserver { - public: - struct TreeChangeParams { - TreeChangeParams(); - Node* target; - Node* old_parent; - Node* new_parent; - Node* receiver; - }; - - virtual void OnTreeChanging(const TreeChangeParams& params) {} - virtual void OnTreeChanged(const TreeChangeParams& params) {} - - virtual void OnNodeReordering(Node* node, - Node* relative_node, - OrderDirection direction) {} - virtual void OnNodeReordered(Node* node, - Node* relative_node, - OrderDirection direction) {} - - virtual void OnNodeDestroying(Node* node) {} - virtual void OnNodeDestroyed(Node* node) {} - - virtual void OnNodeActiveViewChanging(Node* node, - View* old_view, - View* new_view) {} - virtual void OnNodeActiveViewChanged(Node* node, - View* old_view, - View* new_view) {} - - virtual void OnNodeBoundsChanging(Node* node, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) {} - virtual void OnNodeBoundsChanged(Node* node, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) {} - - virtual void OnNodeFocusChanged(Node* gained_focus, Node* lost_focus) {} - - protected: - virtual ~NodeObserver() {} -}; - -} // namespace mojo - -#endif // MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_NODE_OBSERVER_H_ diff --git a/mojo/services/public/cpp/view_manager/tests/DEPS b/mojo/services/public/cpp/view_manager/tests/DEPS index a02406914276b..2ecf3db12de41 100644 --- a/mojo/services/public/cpp/view_manager/tests/DEPS +++ b/mojo/services/public/cpp/view_manager/tests/DEPS @@ -1,3 +1,3 @@ include_rules = [ - "+mojo/service_manager", + "+mojo/application_manager", ] diff --git a/mojo/services/public/cpp/view_manager/tests/node_unittest.cc b/mojo/services/public/cpp/view_manager/tests/node_unittest.cc deleted file mode 100644 index 6bfffd5f9f993..0000000000000 --- a/mojo/services/public/cpp/view_manager/tests/node_unittest.cc +++ /dev/null @@ -1,544 +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/services/public/cpp/view_manager/node.h" - -#include "base/logging.h" -#include "base/strings/stringprintf.h" -#include "mojo/services/public/cpp/view_manager/lib/node_private.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" -#include "mojo/services/public/cpp/view_manager/util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { - -// Node ------------------------------------------------------------------------ - -typedef testing::Test NodeTest; - -// Subclass with public ctor/dtor. -class TestNode : public Node { - public: - TestNode() { - NodePrivate(this).set_id(1); - } - ~TestNode() {} - - private: - DISALLOW_COPY_AND_ASSIGN(TestNode); -}; - -TEST_F(NodeTest, AddChild) { - TestNode v1; - TestNode v11; - v1.AddChild(&v11); - EXPECT_EQ(1U, v1.children().size()); -} - -TEST_F(NodeTest, RemoveChild) { - TestNode v1; - TestNode v11; - v1.AddChild(&v11); - EXPECT_EQ(1U, v1.children().size()); - v1.RemoveChild(&v11); - EXPECT_EQ(0U, v1.children().size()); -} - -TEST_F(NodeTest, Reparent) { - TestNode v1; - TestNode v2; - TestNode v11; - v1.AddChild(&v11); - EXPECT_EQ(1U, v1.children().size()); - v2.AddChild(&v11); - EXPECT_EQ(1U, v2.children().size()); - EXPECT_EQ(0U, v1.children().size()); -} - -TEST_F(NodeTest, Contains) { - TestNode v1; - - // Direct descendant. - TestNode v11; - v1.AddChild(&v11); - EXPECT_TRUE(v1.Contains(&v11)); - - // Indirect descendant. - TestNode v111; - v11.AddChild(&v111); - EXPECT_TRUE(v1.Contains(&v111)); -} - -TEST_F(NodeTest, GetChildById) { - TestNode v1; - NodePrivate(&v1).set_id(1); - TestNode v11; - NodePrivate(&v11).set_id(11); - v1.AddChild(&v11); - TestNode v111; - NodePrivate(&v111).set_id(111); - v11.AddChild(&v111); - - // Find direct & indirect descendents. - EXPECT_EQ(&v11, v1.GetChildById(v11.id())); - EXPECT_EQ(&v111, v1.GetChildById(v111.id())); -} - -// NodeObserver -------------------------------------------------------- - -typedef testing::Test NodeObserverTest; - -bool TreeChangeParamsMatch(const NodeObserver::TreeChangeParams& lhs, - const NodeObserver::TreeChangeParams& rhs) { - return lhs.target == rhs.target && lhs.old_parent == rhs.old_parent && - lhs.new_parent == rhs.new_parent && lhs.receiver == rhs.receiver; -} - -class TreeChangeObserver : public NodeObserver { - public: - explicit TreeChangeObserver(Node* observee) : observee_(observee) { - observee_->AddObserver(this); - } - virtual ~TreeChangeObserver() { - observee_->RemoveObserver(this); - } - - void Reset() { - received_params_.clear(); - } - - const std::vector& received_params() { - return received_params_; - } - - private: - // Overridden from NodeObserver: - virtual void OnTreeChanging(const TreeChangeParams& params) OVERRIDE { - received_params_.push_back(params); - } - virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE { - received_params_.push_back(params); - } - - Node* observee_; - std::vector received_params_; - - DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver); -}; - -// Adds/Removes v11 to v1. -TEST_F(NodeObserverTest, TreeChange_SimpleAddRemove) { - TestNode v1; - TreeChangeObserver o1(&v1); - EXPECT_TRUE(o1.received_params().empty()); - - TestNode v11; - TreeChangeObserver o11(&v11); - EXPECT_TRUE(o11.received_params().empty()); - - // Add. - - v1.AddChild(&v11); - - EXPECT_EQ(2U, o1.received_params().size()); - NodeObserver::TreeChangeParams p1; - p1.target = &v11; - p1.receiver = &v1; - p1.old_parent = NULL; - p1.new_parent = &v1; - EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back())); - - EXPECT_EQ(2U, o11.received_params().size()); - NodeObserver::TreeChangeParams p11 = p1; - p11.receiver = &v11; - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back())); - - o1.Reset(); - o11.Reset(); - EXPECT_TRUE(o1.received_params().empty()); - EXPECT_TRUE(o11.received_params().empty()); - - // Remove. - - v1.RemoveChild(&v11); - - EXPECT_EQ(2U, o1.received_params().size()); - p1.target = &v11; - p1.receiver = &v1; - p1.old_parent = &v1; - p1.new_parent = NULL; - EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front())); - - EXPECT_EQ(2U, o11.received_params().size()); - p11 = p1; - p11.receiver = &v11; - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back())); -} - -// Creates these two trees: -// v1 -// +- v11 -// v111 -// +- v1111 -// +- v1112 -// Then adds/removes v111 from v11. -TEST_F(NodeObserverTest, TreeChange_NestedAddRemove) { - TestNode v1, v11, v111, v1111, v1112; - - // Root tree. - v1.AddChild(&v11); - - // Tree to be attached. - v111.AddChild(&v1111); - v111.AddChild(&v1112); - - TreeChangeObserver o1(&v1), o11(&v11), o111(&v111), o1111(&v1111), - o1112(&v1112); - NodeObserver::TreeChangeParams p1, p11, p111, p1111, p1112; - - // Add. - - v11.AddChild(&v111); - - EXPECT_EQ(2U, o1.received_params().size()); - p1.target = &v111; - p1.receiver = &v1; - p1.old_parent = NULL; - p1.new_parent = &v11; - EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back())); - - EXPECT_EQ(2U, o11.received_params().size()); - p11 = p1; - p11.receiver = &v11; - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back())); - - EXPECT_EQ(2U, o111.received_params().size()); - p111 = p11; - p111.receiver = &v111; - EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back())); - - EXPECT_EQ(2U, o1111.received_params().size()); - p1111 = p111; - p1111.receiver = &v1111; - EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back())); - - EXPECT_EQ(2U, o1112.received_params().size()); - p1112 = p111; - p1112.receiver = &v1112; - EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back())); - - // Remove. - o1.Reset(); - o11.Reset(); - o111.Reset(); - o1111.Reset(); - o1112.Reset(); - EXPECT_TRUE(o1.received_params().empty()); - EXPECT_TRUE(o11.received_params().empty()); - EXPECT_TRUE(o111.received_params().empty()); - EXPECT_TRUE(o1111.received_params().empty()); - EXPECT_TRUE(o1112.received_params().empty()); - - v11.RemoveChild(&v111); - - EXPECT_EQ(2U, o1.received_params().size()); - p1.target = &v111; - p1.receiver = &v1; - p1.old_parent = &v11; - p1.new_parent = NULL; - EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front())); - - EXPECT_EQ(2U, o11.received_params().size()); - p11 = p1; - p11.receiver = &v11; - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); - - EXPECT_EQ(2U, o111.received_params().size()); - p111 = p11; - p111.receiver = &v111; - EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back())); - - EXPECT_EQ(2U, o1111.received_params().size()); - p1111 = p111; - p1111.receiver = &v1111; - EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back())); - - EXPECT_EQ(2U, o1112.received_params().size()); - p1112 = p111; - p1112.receiver = &v1112; - EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back())); -} - -TEST_F(NodeObserverTest, TreeChange_Reparent) { - TestNode v1, v11, v12, v111; - v1.AddChild(&v11); - v1.AddChild(&v12); - v11.AddChild(&v111); - - TreeChangeObserver o1(&v1), o11(&v11), o12(&v12), o111(&v111); - - // Reparent. - v12.AddChild(&v111); - - // v1 (root) should see both changing and changed notifications. - EXPECT_EQ(4U, o1.received_params().size()); - NodeObserver::TreeChangeParams p1; - p1.target = &v111; - p1.receiver = &v1; - p1.old_parent = &v11; - p1.new_parent = &v12; - EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back())); - - // v11 should see changing notifications. - EXPECT_EQ(2U, o11.received_params().size()); - NodeObserver::TreeChangeParams p11; - p11 = p1; - p11.receiver = &v11; - EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); - - // v12 should see changed notifications. - EXPECT_EQ(2U, o12.received_params().size()); - NodeObserver::TreeChangeParams p12; - p12 = p1; - p12.receiver = &v12; - EXPECT_TRUE(TreeChangeParamsMatch(p12, o12.received_params().back())); - - // v111 should see both changing and changed notifications. - EXPECT_EQ(2U, o111.received_params().size()); - NodeObserver::TreeChangeParams p111; - p111 = p1; - p111.receiver = &v111; - EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front())); - EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back())); -} - -namespace { - -class OrderChangeObserver : public NodeObserver { - public: - struct Change { - Node* node; - Node* relative_node; - OrderDirection direction; - }; - typedef std::vector Changes; - - explicit OrderChangeObserver(Node* observee) : observee_(observee) { - observee_->AddObserver(this); - } - virtual ~OrderChangeObserver() { - observee_->RemoveObserver(this); - } - - Changes GetAndClearChanges() { - Changes changes; - changes_.swap(changes); - return changes; - } - - private: - // Overridden from NodeObserver: - virtual void OnNodeReordering(Node* node, - Node* relative_node, - OrderDirection direction) OVERRIDE { - OnNodeReordered(node, relative_node, direction); - } - - virtual void OnNodeReordered(Node* node, - Node* relative_node, - OrderDirection direction) OVERRIDE { - Change change; - change.node = node; - change.relative_node = relative_node; - change.direction = direction; - changes_.push_back(change); - } - - Node* observee_; - Changes changes_; - - DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); -}; - -} // namespace - -TEST_F(NodeObserverTest, Order) { - TestNode v1, v11, v12, v13; - v1.AddChild(&v11); - v1.AddChild(&v12); - v1.AddChild(&v13); - - // Order: v11, v12, v13 - EXPECT_EQ(3U, v1.children().size()); - EXPECT_EQ(&v11, v1.children().front()); - EXPECT_EQ(&v13, v1.children().back()); - - { - OrderChangeObserver observer(&v11); - - // Move v11 to front. - // Resulting order: v12, v13, v11 - v11.MoveToFront(); - EXPECT_EQ(&v12, v1.children().front()); - EXPECT_EQ(&v11, v1.children().back()); - - OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); - EXPECT_EQ(2U, changes.size()); - EXPECT_EQ(&v11, changes[0].node); - EXPECT_EQ(&v13, changes[0].relative_node); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction); - - EXPECT_EQ(&v11, changes[1].node); - EXPECT_EQ(&v13, changes[1].relative_node); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction); - } - - { - OrderChangeObserver observer(&v11); - - // Move v11 to back. - // Resulting order: v11, v12, v13 - v11.MoveToBack(); - EXPECT_EQ(&v11, v1.children().front()); - EXPECT_EQ(&v13, v1.children().back()); - - OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); - EXPECT_EQ(2U, changes.size()); - EXPECT_EQ(&v11, changes[0].node); - EXPECT_EQ(&v12, changes[0].relative_node); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction); - - EXPECT_EQ(&v11, changes[1].node); - EXPECT_EQ(&v12, changes[1].relative_node); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction); - } - - { - OrderChangeObserver observer(&v11); - - // Move v11 above v12. - // Resulting order: v12. v11, v13 - v11.Reorder(&v12, ORDER_DIRECTION_ABOVE); - EXPECT_EQ(&v12, v1.children().front()); - EXPECT_EQ(&v13, v1.children().back()); - - OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); - EXPECT_EQ(2U, changes.size()); - EXPECT_EQ(&v11, changes[0].node); - EXPECT_EQ(&v12, changes[0].relative_node); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction); - - EXPECT_EQ(&v11, changes[1].node); - EXPECT_EQ(&v12, changes[1].relative_node); - EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction); - } - - { - OrderChangeObserver observer(&v11); - - // Move v11 below v12. - // Resulting order: v11, v12, v13 - v11.Reorder(&v12, ORDER_DIRECTION_BELOW); - EXPECT_EQ(&v11, v1.children().front()); - EXPECT_EQ(&v13, v1.children().back()); - - OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); - EXPECT_EQ(2U, changes.size()); - EXPECT_EQ(&v11, changes[0].node); - EXPECT_EQ(&v12, changes[0].relative_node); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction); - - EXPECT_EQ(&v11, changes[1].node); - EXPECT_EQ(&v12, changes[1].relative_node); - EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction); - } -} - -namespace { - -typedef std::vector Changes; - -std::string NodeIdToString(Id id) { - return (id == 0) ? "null" : - base::StringPrintf("%d,%d", HiWord(id), LoWord(id)); -} - -std::string RectToString(const gfx::Rect& rect) { - return base::StringPrintf("%d,%d %dx%d", - rect.x(), rect.y(), rect.width(), rect.height()); -} - -class BoundsChangeObserver : public NodeObserver { - public: - explicit BoundsChangeObserver(Node* node) : node_(node) { - node_->AddObserver(this); - } - virtual ~BoundsChangeObserver() { - node_->RemoveObserver(this); - } - - Changes GetAndClearChanges() { - Changes changes; - changes.swap(changes_); - return changes; - } - - private: - // Overridden from NodeObserver: - virtual void OnNodeBoundsChanging(Node* node, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) OVERRIDE { - changes_.push_back( - base::StringPrintf( - "node=%s old_bounds=%s new_bounds=%s phase=changing", - NodeIdToString(node->id()).c_str(), - RectToString(old_bounds).c_str(), - RectToString(new_bounds).c_str())); - } - virtual void OnNodeBoundsChanged(Node* node, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds) OVERRIDE { - changes_.push_back( - base::StringPrintf( - "node=%s old_bounds=%s new_bounds=%s phase=changed", - NodeIdToString(node->id()).c_str(), - RectToString(old_bounds).c_str(), - RectToString(new_bounds).c_str())); - } - - Node* node_; - Changes changes_; - - DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); -}; - -} // namespace - -TEST_F(NodeObserverTest, SetBounds) { - TestNode v1; - { - BoundsChangeObserver observer(&v1); - v1.SetBounds(gfx::Rect(0, 0, 100, 100)); - - Changes changes = observer.GetAndClearChanges(); - EXPECT_EQ(2U, changes.size()); - EXPECT_EQ( - "node=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changing", - changes[0]); - EXPECT_EQ( - "node=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changed", - changes[1]); - } -} - -} // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc index f56a060e222ff..9f4729292b5af 100644 --- a/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc +++ b/mojo/services/public/cpp/view_manager/tests/view_manager_unittest.cc @@ -7,17 +7,15 @@ #include "base/auto_reset.h" #include "base/bind.h" #include "base/logging.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/service_manager/service_manager.h" -#include "mojo/services/public/cpp/view_manager/lib/node_private.h" #include "mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" +#include "mojo/services/public/cpp/view_manager/lib/view_private.h" #include "mojo/services/public/cpp/view_manager/util.h" -#include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" #include "mojo/services/public/cpp/view_manager/view_observer.h" @@ -43,29 +41,31 @@ void QuitRunLoop() { current_run_loop->Quit(); } -class ConnectServiceLoader : public ServiceLoader, - public ApplicationDelegate, - public ViewManagerDelegate { +class ConnectApplicationLoader : public ApplicationLoader, + public ApplicationDelegate, + public ViewManagerDelegate { public: - typedef base::Callback LoadedCallback; + typedef base::Callback LoadedCallback; - explicit ConnectServiceLoader(const LoadedCallback& callback) + explicit ConnectApplicationLoader(const LoadedCallback& callback) : callback_(callback), view_manager_client_factory_(this) {} - virtual ~ConnectServiceLoader() {} + virtual ~ConnectApplicationLoader() {} private: - // Overridden from ServiceLoader: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE { + // Overridden from ApplicationLoader: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; scoped_ptr app(new ApplicationImpl(this, shell_handle.Pass())); apps_.push_back(app.release()); } - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE { - } + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE {} virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) OVERRIDE { @@ -75,7 +75,7 @@ class ConnectServiceLoader : public ServiceLoader, // Overridden from ViewManagerDelegate: virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) OVERRIDE { callback_.Run(view_manager, root); @@ -86,126 +86,87 @@ class ConnectServiceLoader : public ServiceLoader, LoadedCallback callback_; ViewManagerClientFactory view_manager_client_factory_; - DISALLOW_COPY_AND_ASSIGN(ConnectServiceLoader); -}; - -class ActiveViewChangedObserver : public NodeObserver { - public: - explicit ActiveViewChangedObserver(Node* node) - : node_(node) {} - virtual ~ActiveViewChangedObserver() {} - - private: - // Overridden from NodeObserver: - virtual void OnNodeActiveViewChanged(Node* node, - View* old_view, - View* new_view) OVERRIDE { - DCHECK_EQ(node, node_); - QuitRunLoop(); - } - - Node* node_; - - DISALLOW_COPY_AND_ASSIGN(ActiveViewChangedObserver); + DISALLOW_COPY_AND_ASSIGN(ConnectApplicationLoader); }; -// Waits until the active view id of the supplied node changes. -void WaitForActiveViewToChange(Node* node) { - ActiveViewChangedObserver observer(node); - node->AddObserver(&observer); - DoRunLoop(); - node->RemoveObserver(&observer); -} - -class BoundsChangeObserver : public NodeObserver { +class BoundsChangeObserver : public ViewObserver { public: - explicit BoundsChangeObserver(Node* node) : node_(node) {} + explicit BoundsChangeObserver(View* view) : view_(view) {} virtual ~BoundsChangeObserver() {} private: - // Overridden from NodeObserver: - virtual void OnNodeBoundsChanged(Node* node, + // Overridden from ViewObserver: + virtual void OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE { - DCHECK_EQ(node, node_); + DCHECK_EQ(view, view_); QuitRunLoop(); } - Node* node_; + View* view_; DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); }; -// Wait until the bounds of the supplied node change. -void WaitForBoundsToChange(Node* node) { - BoundsChangeObserver observer(node); - node->AddObserver(&observer); +// Wait until the bounds of the supplied view change. +void WaitForBoundsToChange(View* view) { + BoundsChangeObserver observer(view); + view->AddObserver(&observer); DoRunLoop(); - node->RemoveObserver(&observer); + view->RemoveObserver(&observer); } -// Spins a runloop until the tree beginning at |root| has |tree_size| nodes +// Spins a runloop until the tree beginning at |root| has |tree_size| views // (including |root|). -class TreeSizeMatchesObserver : public NodeObserver { +class TreeSizeMatchesObserver : public ViewObserver { public: - TreeSizeMatchesObserver(Node* tree, size_t tree_size) + TreeSizeMatchesObserver(View* tree, size_t tree_size) : tree_(tree), tree_size_(tree_size) {} virtual ~TreeSizeMatchesObserver() {} bool IsTreeCorrectSize() { - return CountNodes(tree_) == tree_size_; + return CountViews(tree_) == tree_size_; } private: - // Overridden from NodeObserver: + // Overridden from ViewObserver: virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE { if (IsTreeCorrectSize()) QuitRunLoop(); } - size_t CountNodes(const Node* node) const { + size_t CountViews(const View* view) const { size_t count = 1; - Node::Children::const_iterator it = node->children().begin(); - for (; it != node->children().end(); ++it) - count += CountNodes(*it); + View::Children::const_iterator it = view->children().begin(); + for (; it != view->children().end(); ++it) + count += CountViews(*it); return count; } - Node* tree_; + View* tree_; size_t tree_size_; DISALLOW_COPY_AND_ASSIGN(TreeSizeMatchesObserver); }; -void WaitForTreeSizeToMatch(Node* node, size_t tree_size) { - TreeSizeMatchesObserver observer(node, tree_size); +void WaitForTreeSizeToMatch(View* view, size_t tree_size) { + TreeSizeMatchesObserver observer(view, tree_size); if (observer.IsTreeCorrectSize()) return; - node->AddObserver(&observer); + view->AddObserver(&observer); DoRunLoop(); - node->RemoveObserver(&observer); + view->RemoveObserver(&observer); } -// Utility class that waits for the destruction of some number of nodes and +// Utility class that waits for the destruction of some number of views and // views. -class DestructionObserver : public NodeObserver, public ViewObserver { +class DestructionObserver : public ViewObserver { public: - // |nodes| or |views| can be NULL. - DestructionObserver(std::set* nodes, std::set* views) - : nodes_(nodes), - views_(views) {} + // |views| or |views| can be NULL. + explicit DestructionObserver(std::set* views) : views_(views) {} private: - // Overridden from NodeObserver: - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - std::set::iterator it = nodes_->find(node->id()); - if (it != nodes_->end()) - nodes_->erase(it); - if (CanQuit()) - QuitRunLoop(); - } - // Overridden from ViewObserver: virtual void OnViewDestroyed(View* view) OVERRIDE { std::set::iterator it = views_->find(view->id()); @@ -216,26 +177,17 @@ class DestructionObserver : public NodeObserver, public ViewObserver { } bool CanQuit() { - return (!nodes_ || nodes_->empty()) && (!views_ || views_->empty()); + return !views_ || views_->empty(); } - std::set* nodes_; std::set* views_; DISALLOW_COPY_AND_ASSIGN(DestructionObserver); }; -void WaitForDestruction(ViewManager* view_manager, - std::set* nodes, - std::set* views) { - DestructionObserver observer(nodes, views); - DCHECK(nodes || views); - if (nodes) { - for (std::set::const_iterator it = nodes->begin(); - it != nodes->end(); ++it) { - view_manager->GetNodeById(*it)->AddObserver(&observer); - } - } +void WaitForDestruction(ViewManager* view_manager, std::set* views) { + DestructionObserver observer(views); + DCHECK(views); if (views) { for (std::set::const_iterator it = views->begin(); it != views->end(); ++it) { @@ -245,58 +197,58 @@ void WaitForDestruction(ViewManager* view_manager, DoRunLoop(); } -class OrderChangeObserver : public NodeObserver { +class OrderChangeObserver : public ViewObserver { public: - OrderChangeObserver(Node* node) : node_(node) { - node_->AddObserver(this); + OrderChangeObserver(View* view) : view_(view) { + view_->AddObserver(this); } virtual ~OrderChangeObserver() { - node_->RemoveObserver(this); + view_->RemoveObserver(this); } private: - // Overridden from NodeObserver: - virtual void OnNodeReordered(Node* node, - Node* relative_node, + // Overridden from ViewObserver: + virtual void OnViewReordered(View* view, + View* relative_view, OrderDirection direction) OVERRIDE { - DCHECK_EQ(node, node_); + DCHECK_EQ(view, view_); QuitRunLoop(); } - Node* node_; + View* view_; DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); }; -void WaitForOrderChange(ViewManager* view_manager, Node* node) { - OrderChangeObserver observer(node); +void WaitForOrderChange(ViewManager* view_manager, View* view) { + OrderChangeObserver observer(view); DoRunLoop(); } -// Tracks a node's destruction. Query is_valid() for current state. -class NodeTracker : public NodeObserver { +// Tracks a view's destruction. Query is_valid() for current state. +class ViewTracker : public ViewObserver { public: - explicit NodeTracker(Node* node) : node_(node) { - node_->AddObserver(this); + explicit ViewTracker(View* view) : view_(view) { + view_->AddObserver(this); } - virtual ~NodeTracker() { - if (node_) - node_->RemoveObserver(this); + virtual ~ViewTracker() { + if (view_) + view_->RemoveObserver(this); } - bool is_valid() const { return !!node_; } + bool is_valid() const { return !!view_; } private: - // Overridden from NodeObserver: - virtual void OnNodeDestroyed(Node* node) OVERRIDE { - DCHECK_EQ(node, node_); - node_ = NULL; + // Overridden from ViewObserver: + virtual void OnViewDestroyed(View* view) OVERRIDE { + DCHECK_EQ(view, view_); + view_ = NULL; } int id_; - Node* node_; + View* view_; - DISALLOW_COPY_AND_ASSIGN(NodeTracker); + DISALLOW_COPY_AND_ASSIGN(ViewTracker); }; } // namespace @@ -304,7 +256,7 @@ class NodeTracker : public NodeObserver { // ViewManager ----------------------------------------------------------------- // These tests model synchronization of two peer connections to the view manager -// service, that are given access to some root node. +// service, that are given access to some root view. class ViewManagerTest : public testing::Test { public: @@ -317,17 +269,17 @@ class ViewManagerTest : public testing::Test { protected: ViewManager* window_manager() { return window_manager_; } - Node* CreateNodeInParent(Node* parent) { - ViewManager* parent_manager = NodePrivate(parent).view_manager(); - Node* node = Node::Create(parent_manager); - parent->AddChild(node); - return node; + View* CreateViewInParent(View* parent) { + ViewManager* parent_manager = ViewPrivate(parent).view_manager(); + View* view = View::Create(parent_manager); + parent->AddChild(view); + return view; } - // Embeds another version of the test app @ node. - ViewManager* Embed(ViewManager* view_manager, Node* node) { - DCHECK_EQ(view_manager, NodePrivate(node).view_manager()); - node->Embed(kEmbeddedApp1URL); + // Embeds another version of the test app @ view. + ViewManager* Embed(ViewManager* view_manager, View* view) { + DCHECK_EQ(view_manager, ViewPrivate(view).view_manager()); + view->Embed(kEmbeddedApp1URL); RunRunLoop(); return GetLoadedViewManager(); } @@ -339,24 +291,25 @@ class ViewManagerTest : public testing::Test { } void UnloadApplication(const GURL& url) { - test_helper_.SetLoaderForURL(scoped_ptr(), url); + test_helper_.SetLoaderForURL(scoped_ptr(), url); } private: // Overridden from testing::Test: virtual void SetUp() OVERRIDE { - ConnectServiceLoader::LoadedCallback ready_callback = - base::Bind(&ViewManagerTest::OnViewManagerLoaded, - base::Unretained(this)); + ConnectApplicationLoader::LoadedCallback ready_callback = base::Bind( + &ViewManagerTest::OnViewManagerLoaded, base::Unretained(this)); test_helper_.Init(); test_helper_.SetLoaderForURL( - scoped_ptr(new ConnectServiceLoader(ready_callback)), + scoped_ptr( + new ConnectApplicationLoader(ready_callback)), GURL(kWindowManagerURL)); test_helper_.SetLoaderForURL( - scoped_ptr(new ConnectServiceLoader(ready_callback)), + scoped_ptr( + new ConnectApplicationLoader(ready_callback)), GURL(kEmbeddedApp1URL)); - test_helper_.service_manager()->ConnectToService( + test_helper_.application_manager()->ConnectToService( GURL("mojo:mojo_view_manager"), &view_manager_init_); ASSERT_TRUE(EmbedRoot(view_manager_init_.get(), kWindowManagerURL)); } @@ -379,7 +332,7 @@ class ViewManagerTest : public testing::Test { return result; } - void OnViewManagerLoaded(ViewManager* view_manager, Node* root) { + void OnViewManagerLoaded(ViewManager* view_manager, View* root) { loaded_view_manager_ = view_manager; connect_loop_->Quit(); } @@ -397,7 +350,7 @@ class ViewManagerTest : public testing::Test { // Used to receive the most recent view manager loaded by an embed action. ViewManager* loaded_view_manager_; // The View Manager connection held by the window manager (app running at the - // root node). + // root view). ViewManager* window_manager_; int commit_count_; @@ -407,26 +360,26 @@ class ViewManagerTest : public testing::Test { TEST_F(ViewManagerTest, SetUp) {} TEST_F(ViewManagerTest, Embed) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); + View* view = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view); + ViewManager* embedded = Embed(window_manager(), view); EXPECT_TRUE(NULL != embedded); - Node* node_in_embedded = embedded->GetRoots().front(); - EXPECT_EQ(node->parent(), window_manager()->GetRoots().front()); - EXPECT_EQ(NULL, node_in_embedded->parent()); + View* view_in_embedded = embedded->GetRoots().front(); + EXPECT_EQ(view->parent(), window_manager()->GetRoots().front()); + EXPECT_EQ(NULL, view_in_embedded->parent()); } -// Window manager has two nodes, N1 and N11. Embeds A at N1. A should not see +// Window manager has two views, N1 and N11. Embeds A at N1. A should not see // N11. // TODO(sky): Update client lib to match server. TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - Node* nested = Node::Create(window_manager()); - node->AddChild(nested); + View* view = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view); + View* nested = View::Create(window_manager()); + view->AddChild(nested); - ViewManager* embedded = Embed(window_manager(), node); + ViewManager* embedded = Embed(window_manager(), view); EXPECT_EQ(embedded->GetRoots().front()->children().front()->id(), nested->id()); EXPECT_TRUE(embedded->GetRoots().front()->children().empty()); @@ -434,237 +387,135 @@ TEST_F(ViewManagerTest, DISABLED_EmbeddedDoesntSeeChild) { } // http://crbug.com/396300 -TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupNode) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); - - Id node_id = node->id(); - - UnloadApplication(GURL(kWindowManagerURL)); - - std::set nodes; - nodes.insert(node_id); - WaitForDestruction(embedded, &nodes, NULL); - - EXPECT_TRUE(embedded->GetRoots().empty()); -} - -TEST_F(ViewManagerTest, SetActiveView) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); - +TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupView) { View* view = View::Create(window_manager()); - node->SetActiveView(view); + window_manager()->GetRoots().front()->AddChild(view); + ViewManager* embedded = Embed(window_manager(), view); - Node* node_in_embedded = embedded->GetNodeById(node->id()); - WaitForActiveViewToChange(node_in_embedded); - - EXPECT_EQ(node_in_embedded->active_view()->id(), view->id()); -} - -// TODO(sky): rethink this and who should be notified when views are -// detached/destroyed. -TEST_F(ViewManagerTest, DISABLED_DestroyView) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); - - View* view = View::Create(window_manager()); - node->SetActiveView(view); - - Node* node_in_embedded = embedded->GetNodeById(node->id()); - WaitForActiveViewToChange(node_in_embedded); - - EXPECT_EQ(node_in_embedded->active_view()->id(), view->id()); - - Id view_id = view->id(); - view->Destroy(); - - std::set views; - views.insert(view_id); - WaitForDestruction(embedded, NULL, &views); - EXPECT_EQ(NULL, node_in_embedded->active_view()); - EXPECT_EQ(NULL, embedded->GetViewById(view_id)); -} - -// Destroying the connection that created a node and view should result in that -// node and view disappearing from all connections that see them. -// http://crbug.com/396300 -TEST_F(ViewManagerTest, DISABLED_ViewManagerDestroyed_CleanupNodeAndView) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - View* view = View::Create(window_manager()); - node->SetActiveView(view); - ViewManager* embedded = Embed(window_manager(), node); - - Id node_id = node->id(); Id view_id = view->id(); UnloadApplication(GURL(kWindowManagerURL)); - std::set observed_nodes; - observed_nodes.insert(node_id); - std::set observed_views; - observed_views.insert(view_id); - WaitForDestruction(embedded, &observed_nodes, &observed_views); + std::set views; + views.insert(view_id); + WaitForDestruction(embedded, &views); EXPECT_TRUE(embedded->GetRoots().empty()); - EXPECT_EQ(NULL, embedded->GetNodeById(node_id)); - EXPECT_EQ(NULL, embedded->GetViewById(view_id)); } +// TODO(beng): write a replacement test for the one that once existed here: // This test validates the following scenario: -// - a node originating from one connection +// - a view originating from one connection // - a view originating from a second connection -// + the connection originating the node is destroyed +// + the connection originating the view is destroyed // -> the view should still exist (since the second connection is live) but -// should be disconnected from any nodes. +// should be disconnected from any views. // http://crbug.com/396300 -TEST_F( - ViewManagerTest, - DISABLED_ViewManagerDestroyed_CleanupNodeAndViewFromDifferentConnections) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); - View* view_in_embedded = View::Create(embedded); - Node* node_in_embedded = embedded->GetNodeById(node->id()); - node_in_embedded->SetActiveView(view_in_embedded); - - WaitForActiveViewToChange(node); - - Id node_id = node->id(); - Id view_id = view_in_embedded->id(); +// +// TODO(beng): The new test should validate the scenario as described above +// except that the second connection still has a valid tree. - UnloadApplication(GURL(kWindowManagerURL)); - std::set nodes; - nodes.insert(node_id); - WaitForDestruction(embedded, &nodes, NULL); - - EXPECT_TRUE(embedded->GetRoots().empty()); - // node was owned by the window manager, so it should be gone. - EXPECT_EQ(NULL, embedded->GetNodeById(node_id)); - // view_in_embedded was owned by the embedded app, so it should still exist, - // but disconnected from the node tree. - EXPECT_EQ(view_in_embedded, embedded->GetViewById(view_id)); - EXPECT_EQ(NULL, view_in_embedded->node()); -} - -// This test verifies that it is not possible to set the active view to a view -// defined in a different connection. -// TODO(beng): write these tests for Node::AddChild(), RemoveChild() and -// Contains(). -TEST_F(ViewManagerTest, SetActiveViewAcrossConnection) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); - - View* view_in_embedded = View::Create(embedded); - EXPECT_DEATH_IF_SUPPORTED(node->SetActiveView(view_in_embedded), ""); -} - -// Verifies that bounds changes applied to a node hierarchy in one connection +// Verifies that bounds changes applied to a view hierarchy in one connection // are reflected to another. TEST_F(ViewManagerTest, SetBounds) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); + View* view = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view); + ViewManager* embedded = Embed(window_manager(), view); - Node* node_in_embedded = embedded->GetNodeById(node->id()); - EXPECT_EQ(node->bounds(), node_in_embedded->bounds()); + View* view_in_embedded = embedded->GetViewById(view->id()); + EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); - node->SetBounds(gfx::Rect(100, 100)); - EXPECT_NE(node->bounds(), node_in_embedded->bounds()); - WaitForBoundsToChange(node_in_embedded); - EXPECT_EQ(node->bounds(), node_in_embedded->bounds()); + view->SetBounds(gfx::Rect(100, 100)); + EXPECT_NE(view->bounds(), view_in_embedded->bounds()); + WaitForBoundsToChange(view_in_embedded); + EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); } -// Verifies that bounds changes applied to a node owned by a different +// Verifies that bounds changes applied to a view owned by a different // connection are refused. TEST_F(ViewManagerTest, SetBoundsSecurity) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); + View* view = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view); + ViewManager* embedded = Embed(window_manager(), view); - Node* node_in_embedded = embedded->GetNodeById(node->id()); - node->SetBounds(gfx::Rect(800, 600)); - WaitForBoundsToChange(node_in_embedded); + View* view_in_embedded = embedded->GetViewById(view->id()); + view->SetBounds(gfx::Rect(800, 600)); + WaitForBoundsToChange(view_in_embedded); - node_in_embedded->SetBounds(gfx::Rect(1024, 768)); + view_in_embedded->SetBounds(gfx::Rect(1024, 768)); // Bounds change should have been rejected. - EXPECT_EQ(node->bounds(), node_in_embedded->bounds()); + EXPECT_EQ(view->bounds(), view_in_embedded->bounds()); } -// Verifies that a node can only be destroyed by the connection that created it. +// Verifies that a view can only be destroyed by the connection that created it. TEST_F(ViewManagerTest, DestroySecurity) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); + View* view = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view); + ViewManager* embedded = Embed(window_manager(), view); - Node* node_in_embedded = embedded->GetNodeById(node->id()); + View* view_in_embedded = embedded->GetViewById(view->id()); - NodeTracker tracker2(node_in_embedded); - node_in_embedded->Destroy(); - // Node should not have been destroyed. + ViewTracker tracker2(view_in_embedded); + view_in_embedded->Destroy(); + // View should not have been destroyed. EXPECT_TRUE(tracker2.is_valid()); - NodeTracker tracker1(node); - node->Destroy(); + ViewTracker tracker1(view); + view->Destroy(); EXPECT_FALSE(tracker1.is_valid()); } TEST_F(ViewManagerTest, MultiRoots) { - Node* node1 = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node1); - Node* node2 = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node2); - ViewManager* embedded1 = Embed(window_manager(), node1); - ViewManager* embedded2 = Embed(window_manager(), node2); + View* view1 = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view1); + View* view2 = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view2); + ViewManager* embedded1 = Embed(window_manager(), view1); + ViewManager* embedded2 = Embed(window_manager(), view2); EXPECT_EQ(embedded1, embedded2); } TEST_F(ViewManagerTest, EmbeddingIdentity) { - Node* node = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node); - ViewManager* embedded = Embed(window_manager(), node); + View* view = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view); + ViewManager* embedded = Embed(window_manager(), view); EXPECT_EQ(kWindowManagerURL, embedded->GetEmbedderURL()); } TEST_F(ViewManagerTest, Reorder) { - Node* node1 = Node::Create(window_manager()); - window_manager()->GetRoots().front()->AddChild(node1); + View* view1 = View::Create(window_manager()); + window_manager()->GetRoots().front()->AddChild(view1); - ViewManager* embedded = Embed(window_manager(), node1); + ViewManager* embedded = Embed(window_manager(), view1); - Node* node11 = Node::Create(embedded); - embedded->GetRoots().front()->AddChild(node11); - Node* node12 = Node::Create(embedded); - embedded->GetRoots().front()->AddChild(node12); + View* view11 = View::Create(embedded); + embedded->GetRoots().front()->AddChild(view11); + View* view12 = View::Create(embedded); + embedded->GetRoots().front()->AddChild(view12); - Node* node1_in_wm = window_manager()->GetNodeById(node1->id()); + View* view1_in_wm = window_manager()->GetViewById(view1->id()); { - WaitForTreeSizeToMatch(node1, 2u); - node11->MoveToFront(); + WaitForTreeSizeToMatch(view1, 2u); + view11->MoveToFront(); WaitForOrderChange(window_manager(), - window_manager()->GetNodeById(node11->id())); + window_manager()->GetViewById(view11->id())); - EXPECT_EQ(node1_in_wm->children().front(), - window_manager()->GetNodeById(node12->id())); - EXPECT_EQ(node1_in_wm->children().back(), - window_manager()->GetNodeById(node11->id())); + EXPECT_EQ(view1_in_wm->children().front(), + window_manager()->GetViewById(view12->id())); + EXPECT_EQ(view1_in_wm->children().back(), + window_manager()->GetViewById(view11->id())); } { - node11->MoveToBack(); + view11->MoveToBack(); WaitForOrderChange(window_manager(), - window_manager()->GetNodeById(node11->id())); + window_manager()->GetViewById(view11->id())); - EXPECT_EQ(node1_in_wm->children().front(), - window_manager()->GetNodeById(node11->id())); - EXPECT_EQ(node1_in_wm->children().back(), - window_manager()->GetNodeById(node12->id())); + EXPECT_EQ(view1_in_wm->children().front(), + window_manager()->GetViewById(view11->id())); + EXPECT_EQ(view1_in_wm->children().back(), + window_manager()->GetViewById(view12->id())); } } @@ -672,8 +523,8 @@ TEST_F(ViewManagerTest, Reorder) { // - verify that we see events for all views. // TODO(beng): tests for focus: -// - focus between two nodes known to a connection -// - focus between nodes unknown to one of the connections. -// - focus between nodes unknown to either connection. +// - focus between two views known to a connection +// - focus between views unknown to one of the connections. +// - focus between views unknown to either connection. } // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/tests/view_unittest.cc b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc index e69de29bb2d1d..c63089eeddd38 100644 --- a/mojo/services/public/cpp/view_manager/tests/view_unittest.cc +++ b/mojo/services/public/cpp/view_manager/tests/view_unittest.cc @@ -0,0 +1,544 @@ +// 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/services/public/cpp/view_manager/view.h" + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "mojo/services/public/cpp/view_manager/lib/view_private.h" +#include "mojo/services/public/cpp/view_manager/util.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { + +// View ------------------------------------------------------------------------ + +typedef testing::Test ViewTest; + +// Subclass with public ctor/dtor. +class TestView : public View { + public: + TestView() { + ViewPrivate(this).set_id(1); + } + ~TestView() {} + + private: + DISALLOW_COPY_AND_ASSIGN(TestView); +}; + +TEST_F(ViewTest, AddChild) { + TestView v1; + TestView v11; + v1.AddChild(&v11); + EXPECT_EQ(1U, v1.children().size()); +} + +TEST_F(ViewTest, RemoveChild) { + TestView v1; + TestView v11; + v1.AddChild(&v11); + EXPECT_EQ(1U, v1.children().size()); + v1.RemoveChild(&v11); + EXPECT_EQ(0U, v1.children().size()); +} + +TEST_F(ViewTest, Reparent) { + TestView v1; + TestView v2; + TestView v11; + v1.AddChild(&v11); + EXPECT_EQ(1U, v1.children().size()); + v2.AddChild(&v11); + EXPECT_EQ(1U, v2.children().size()); + EXPECT_EQ(0U, v1.children().size()); +} + +TEST_F(ViewTest, Contains) { + TestView v1; + + // Direct descendant. + TestView v11; + v1.AddChild(&v11); + EXPECT_TRUE(v1.Contains(&v11)); + + // Indirect descendant. + TestView v111; + v11.AddChild(&v111); + EXPECT_TRUE(v1.Contains(&v111)); +} + +TEST_F(ViewTest, GetChildById) { + TestView v1; + ViewPrivate(&v1).set_id(1); + TestView v11; + ViewPrivate(&v11).set_id(11); + v1.AddChild(&v11); + TestView v111; + ViewPrivate(&v111).set_id(111); + v11.AddChild(&v111); + + // Find direct & indirect descendents. + EXPECT_EQ(&v11, v1.GetChildById(v11.id())); + EXPECT_EQ(&v111, v1.GetChildById(v111.id())); +} + +// ViewObserver -------------------------------------------------------- + +typedef testing::Test ViewObserverTest; + +bool TreeChangeParamsMatch(const ViewObserver::TreeChangeParams& lhs, + const ViewObserver::TreeChangeParams& rhs) { + return lhs.target == rhs.target && lhs.old_parent == rhs.old_parent && + lhs.new_parent == rhs.new_parent && lhs.receiver == rhs.receiver; +} + +class TreeChangeObserver : public ViewObserver { + public: + explicit TreeChangeObserver(View* observee) : observee_(observee) { + observee_->AddObserver(this); + } + virtual ~TreeChangeObserver() { + observee_->RemoveObserver(this); + } + + void Reset() { + received_params_.clear(); + } + + const std::vector& received_params() { + return received_params_; + } + + private: + // Overridden from ViewObserver: + virtual void OnTreeChanging(const TreeChangeParams& params) OVERRIDE { + received_params_.push_back(params); + } + virtual void OnTreeChanged(const TreeChangeParams& params) OVERRIDE { + received_params_.push_back(params); + } + + View* observee_; + std::vector received_params_; + + DISALLOW_COPY_AND_ASSIGN(TreeChangeObserver); +}; + +// Adds/Removes v11 to v1. +TEST_F(ViewObserverTest, TreeChange_SimpleAddRemove) { + TestView v1; + TreeChangeObserver o1(&v1); + EXPECT_TRUE(o1.received_params().empty()); + + TestView v11; + TreeChangeObserver o11(&v11); + EXPECT_TRUE(o11.received_params().empty()); + + // Add. + + v1.AddChild(&v11); + + EXPECT_EQ(2U, o1.received_params().size()); + ViewObserver::TreeChangeParams p1; + p1.target = &v11; + p1.receiver = &v1; + p1.old_parent = NULL; + p1.new_parent = &v1; + EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back())); + + EXPECT_EQ(2U, o11.received_params().size()); + ViewObserver::TreeChangeParams p11 = p1; + p11.receiver = &v11; + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back())); + + o1.Reset(); + o11.Reset(); + EXPECT_TRUE(o1.received_params().empty()); + EXPECT_TRUE(o11.received_params().empty()); + + // Remove. + + v1.RemoveChild(&v11); + + EXPECT_EQ(2U, o1.received_params().size()); + p1.target = &v11; + p1.receiver = &v1; + p1.old_parent = &v1; + p1.new_parent = NULL; + EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front())); + + EXPECT_EQ(2U, o11.received_params().size()); + p11 = p1; + p11.receiver = &v11; + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back())); +} + +// Creates these two trees: +// v1 +// +- v11 +// v111 +// +- v1111 +// +- v1112 +// Then adds/removes v111 from v11. +TEST_F(ViewObserverTest, TreeChange_NestedAddRemove) { + TestView v1, v11, v111, v1111, v1112; + + // Root tree. + v1.AddChild(&v11); + + // Tree to be attached. + v111.AddChild(&v1111); + v111.AddChild(&v1112); + + TreeChangeObserver o1(&v1), o11(&v11), o111(&v111), o1111(&v1111), + o1112(&v1112); + ViewObserver::TreeChangeParams p1, p11, p111, p1111, p1112; + + // Add. + + v11.AddChild(&v111); + + EXPECT_EQ(2U, o1.received_params().size()); + p1.target = &v111; + p1.receiver = &v1; + p1.old_parent = NULL; + p1.new_parent = &v11; + EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back())); + + EXPECT_EQ(2U, o11.received_params().size()); + p11 = p1; + p11.receiver = &v11; + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().back())); + + EXPECT_EQ(2U, o111.received_params().size()); + p111 = p11; + p111.receiver = &v111; + EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back())); + + EXPECT_EQ(2U, o1111.received_params().size()); + p1111 = p111; + p1111.receiver = &v1111; + EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back())); + + EXPECT_EQ(2U, o1112.received_params().size()); + p1112 = p111; + p1112.receiver = &v1112; + EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back())); + + // Remove. + o1.Reset(); + o11.Reset(); + o111.Reset(); + o1111.Reset(); + o1112.Reset(); + EXPECT_TRUE(o1.received_params().empty()); + EXPECT_TRUE(o11.received_params().empty()); + EXPECT_TRUE(o111.received_params().empty()); + EXPECT_TRUE(o1111.received_params().empty()); + EXPECT_TRUE(o1112.received_params().empty()); + + v11.RemoveChild(&v111); + + EXPECT_EQ(2U, o1.received_params().size()); + p1.target = &v111; + p1.receiver = &v1; + p1.old_parent = &v11; + p1.new_parent = NULL; + EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front())); + + EXPECT_EQ(2U, o11.received_params().size()); + p11 = p1; + p11.receiver = &v11; + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); + + EXPECT_EQ(2U, o111.received_params().size()); + p111 = p11; + p111.receiver = &v111; + EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back())); + + EXPECT_EQ(2U, o1111.received_params().size()); + p1111 = p111; + p1111.receiver = &v1111; + EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p1111, o1111.received_params().back())); + + EXPECT_EQ(2U, o1112.received_params().size()); + p1112 = p111; + p1112.receiver = &v1112; + EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p1112, o1112.received_params().back())); +} + +TEST_F(ViewObserverTest, TreeChange_Reparent) { + TestView v1, v11, v12, v111; + v1.AddChild(&v11); + v1.AddChild(&v12); + v11.AddChild(&v111); + + TreeChangeObserver o1(&v1), o11(&v11), o12(&v12), o111(&v111); + + // Reparent. + v12.AddChild(&v111); + + // v1 (root) should see both changing and changed notifications. + EXPECT_EQ(4U, o1.received_params().size()); + ViewObserver::TreeChangeParams p1; + p1.target = &v111; + p1.receiver = &v1; + p1.old_parent = &v11; + p1.new_parent = &v12; + EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p1, o1.received_params().back())); + + // v11 should see changing notifications. + EXPECT_EQ(2U, o11.received_params().size()); + ViewObserver::TreeChangeParams p11; + p11 = p1; + p11.receiver = &v11; + EXPECT_TRUE(TreeChangeParamsMatch(p11, o11.received_params().front())); + + // v12 should see changed notifications. + EXPECT_EQ(2U, o12.received_params().size()); + ViewObserver::TreeChangeParams p12; + p12 = p1; + p12.receiver = &v12; + EXPECT_TRUE(TreeChangeParamsMatch(p12, o12.received_params().back())); + + // v111 should see both changing and changed notifications. + EXPECT_EQ(2U, o111.received_params().size()); + ViewObserver::TreeChangeParams p111; + p111 = p1; + p111.receiver = &v111; + EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().front())); + EXPECT_TRUE(TreeChangeParamsMatch(p111, o111.received_params().back())); +} + +namespace { + +class OrderChangeObserver : public ViewObserver { + public: + struct Change { + View* view; + View* relative_view; + OrderDirection direction; + }; + typedef std::vector Changes; + + explicit OrderChangeObserver(View* observee) : observee_(observee) { + observee_->AddObserver(this); + } + virtual ~OrderChangeObserver() { + observee_->RemoveObserver(this); + } + + Changes GetAndClearChanges() { + Changes changes; + changes_.swap(changes); + return changes; + } + + private: + // Overridden from ViewObserver: + virtual void OnViewReordering(View* view, + View* relative_view, + OrderDirection direction) OVERRIDE { + OnViewReordered(view, relative_view, direction); + } + + virtual void OnViewReordered(View* view, + View* relative_view, + OrderDirection direction) OVERRIDE { + Change change; + change.view = view; + change.relative_view = relative_view; + change.direction = direction; + changes_.push_back(change); + } + + View* observee_; + Changes changes_; + + DISALLOW_COPY_AND_ASSIGN(OrderChangeObserver); +}; + +} // namespace + +TEST_F(ViewObserverTest, Order) { + TestView v1, v11, v12, v13; + v1.AddChild(&v11); + v1.AddChild(&v12); + v1.AddChild(&v13); + + // Order: v11, v12, v13 + EXPECT_EQ(3U, v1.children().size()); + EXPECT_EQ(&v11, v1.children().front()); + EXPECT_EQ(&v13, v1.children().back()); + + { + OrderChangeObserver observer(&v11); + + // Move v11 to front. + // Resulting order: v12, v13, v11 + v11.MoveToFront(); + EXPECT_EQ(&v12, v1.children().front()); + EXPECT_EQ(&v11, v1.children().back()); + + OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); + EXPECT_EQ(2U, changes.size()); + EXPECT_EQ(&v11, changes[0].view); + EXPECT_EQ(&v13, changes[0].relative_view); + EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction); + + EXPECT_EQ(&v11, changes[1].view); + EXPECT_EQ(&v13, changes[1].relative_view); + EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction); + } + + { + OrderChangeObserver observer(&v11); + + // Move v11 to back. + // Resulting order: v11, v12, v13 + v11.MoveToBack(); + EXPECT_EQ(&v11, v1.children().front()); + EXPECT_EQ(&v13, v1.children().back()); + + OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); + EXPECT_EQ(2U, changes.size()); + EXPECT_EQ(&v11, changes[0].view); + EXPECT_EQ(&v12, changes[0].relative_view); + EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction); + + EXPECT_EQ(&v11, changes[1].view); + EXPECT_EQ(&v12, changes[1].relative_view); + EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction); + } + + { + OrderChangeObserver observer(&v11); + + // Move v11 above v12. + // Resulting order: v12. v11, v13 + v11.Reorder(&v12, ORDER_DIRECTION_ABOVE); + EXPECT_EQ(&v12, v1.children().front()); + EXPECT_EQ(&v13, v1.children().back()); + + OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); + EXPECT_EQ(2U, changes.size()); + EXPECT_EQ(&v11, changes[0].view); + EXPECT_EQ(&v12, changes[0].relative_view); + EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[0].direction); + + EXPECT_EQ(&v11, changes[1].view); + EXPECT_EQ(&v12, changes[1].relative_view); + EXPECT_EQ(ORDER_DIRECTION_ABOVE, changes[1].direction); + } + + { + OrderChangeObserver observer(&v11); + + // Move v11 below v12. + // Resulting order: v11, v12, v13 + v11.Reorder(&v12, ORDER_DIRECTION_BELOW); + EXPECT_EQ(&v11, v1.children().front()); + EXPECT_EQ(&v13, v1.children().back()); + + OrderChangeObserver::Changes changes = observer.GetAndClearChanges(); + EXPECT_EQ(2U, changes.size()); + EXPECT_EQ(&v11, changes[0].view); + EXPECT_EQ(&v12, changes[0].relative_view); + EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[0].direction); + + EXPECT_EQ(&v11, changes[1].view); + EXPECT_EQ(&v12, changes[1].relative_view); + EXPECT_EQ(ORDER_DIRECTION_BELOW, changes[1].direction); + } +} + +namespace { + +typedef std::vector Changes; + +std::string ViewIdToString(Id id) { + return (id == 0) ? "null" : + base::StringPrintf("%d,%d", HiWord(id), LoWord(id)); +} + +std::string RectToString(const gfx::Rect& rect) { + return base::StringPrintf("%d,%d %dx%d", + rect.x(), rect.y(), rect.width(), rect.height()); +} + +class BoundsChangeObserver : public ViewObserver { + public: + explicit BoundsChangeObserver(View* view) : view_(view) { + view_->AddObserver(this); + } + virtual ~BoundsChangeObserver() { + view_->RemoveObserver(this); + } + + Changes GetAndClearChanges() { + Changes changes; + changes.swap(changes_); + return changes; + } + + private: + // Overridden from ViewObserver: + virtual void OnViewBoundsChanging(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE { + changes_.push_back( + base::StringPrintf( + "view=%s old_bounds=%s new_bounds=%s phase=changing", + ViewIdToString(view->id()).c_str(), + RectToString(old_bounds).c_str(), + RectToString(new_bounds).c_str())); + } + virtual void OnViewBoundsChanged(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE { + changes_.push_back( + base::StringPrintf( + "view=%s old_bounds=%s new_bounds=%s phase=changed", + ViewIdToString(view->id()).c_str(), + RectToString(old_bounds).c_str(), + RectToString(new_bounds).c_str())); + } + + View* view_; + Changes changes_; + + DISALLOW_COPY_AND_ASSIGN(BoundsChangeObserver); +}; + +} // namespace + +TEST_F(ViewObserverTest, SetBounds) { + TestView v1; + { + BoundsChangeObserver observer(&v1); + v1.SetBounds(gfx::Rect(0, 0, 100, 100)); + + Changes changes = observer.GetAndClearChanges(); + EXPECT_EQ(2U, changes.size()); + EXPECT_EQ( + "view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changing", + changes[0]); + EXPECT_EQ( + "view=0,1 old_bounds=0,0 0x0 new_bounds=0,0 100x100 phase=changed", + changes[1]); + } +} + +} // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/types.h b/mojo/services/public/cpp/view_manager/types.h index 9cdcc148508dc..d72236ff00b48 100644 --- a/mojo/services/public/cpp/view_manager/types.h +++ b/mojo/services/public/cpp/view_manager/types.h @@ -12,12 +12,12 @@ namespace mojo { -// Used to identify nodes, views and change ids. +// Used to identify views and change ids. typedef uint32_t Id; -// Used to identify a connection as well as a connection specific view or node -// id. For example, the Id for a node consists of the ConnectionSpecificId of -// the connection and the ConnectionSpecificId of the node. +// Used to identify a connection as well as a connection specific view id. For +// example, the Id for a view consists of the ConnectionSpecificId of the +// connection and the ConnectionSpecificId of the view. typedef uint16_t ConnectionSpecificId; } // namespace mojo diff --git a/mojo/services/public/cpp/view_manager/view.h b/mojo/services/public/cpp/view_manager/view.h index 1d70e4519fb87..3f8ff98e9fa21 100644 --- a/mojo/services/public/cpp/view_manager/view.h +++ b/mojo/services/public/cpp/view_manager/view.h @@ -5,51 +5,110 @@ #ifndef MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_ #define MOJO_SERVICES_PUBLIC_CPP_VIEW_MANAGER_VIEW_H_ +#include + #include "base/basictypes.h" #include "base/observer_list.h" +#include "mojo/public/cpp/bindings/array.h" +#include "mojo/public/interfaces/application/service_provider.mojom.h" #include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/interfaces/view_manager/view_manager_constants.mojom.h" #include "third_party/skia/include/core/SkColor.h" +#include "ui/gfx/geometry/rect.h" class SkBitmap; namespace mojo { -class Node; +class ServiceProviderImpl; +class View; class ViewManager; class ViewObserver; // Views are owned by the ViewManager. +// TODO(beng): Right now, you'll have to implement a ViewObserver to track +// destruction and NULL any pointers you have. +// Investigate some kind of smart pointer or weak pointer for these. class View { public: - static View* Create(ViewManager* manager); + typedef std::vector Children; + + static View* Create(ViewManager* view_manager); + // Destroys this view and all its children. void Destroy(); + // Configuration. Id id() const { return id_; } - Node* node() { return node_; } + // Geometric disposition. + const gfx::Rect& bounds() { return bounds_; } + void SetBounds(const gfx::Rect& bounds); + + // Visibility. + void SetVisible(bool value); + // TODO(sky): add accessor. + + // Observation. void AddObserver(ViewObserver* observer); void RemoveObserver(ViewObserver* observer); + // Tree. + View* parent() { return parent_; } + const View* parent() const { return parent_; } + const Children& children() const { return children_; } + + void AddChild(View* child); + void RemoveChild(View* child); + + void Reorder(View* relative, OrderDirection direction); + void MoveToFront(); + void MoveToBack(); + + bool Contains(View* child) const; + + View* GetChildById(Id id); + // TODO(beng): temporary only. void SetContents(const SkBitmap& contents); void SetColor(SkColor color); + // Focus. + void SetFocus(); + + // Embedding. + void Embed(const String& url); + scoped_ptr Embed( + const String& url, + scoped_ptr exported_services); + + protected: + // This class is subclassed only by test classes that provide a public ctor. + View(); + ~View(); + private: friend class ViewPrivate; + friend class ViewManagerClientImpl; explicit View(ViewManager* manager); - View(); - ~View(); void LocalDestroy(); + void LocalAddChild(View* child); + void LocalRemoveChild(View* child); + // Returns true if the order actually changed. + bool LocalReorder(View* relative, OrderDirection direction); + void LocalSetBounds(const gfx::Rect& old_bounds, const gfx::Rect& new_bounds); - Id id_; - Node* node_; ViewManager* manager_; + Id id_; + View* parent_; + Children children_; ObserverList observers_; + gfx::Rect bounds_; + DISALLOW_COPY_AND_ASSIGN(View); }; diff --git a/mojo/services/public/cpp/view_manager/view_manager.h b/mojo/services/public/cpp/view_manager/view_manager.h index 163f5fea757a7..5d6a261dd3e84 100644 --- a/mojo/services/public/cpp/view_manager/view_manager.h +++ b/mojo/services/public/cpp/view_manager/view_manager.h @@ -13,7 +13,6 @@ namespace mojo { class ApplicationConnection; -class Node; class View; class ViewManagerDelegate; class WindowManagerDelegate; @@ -25,7 +24,7 @@ class WindowManagerDelegate; class ViewManager { public: // Sets the window manager delegate. Can only be called by the app embedded at - // the service root node. + // the service root view. Can only be called once. virtual void SetWindowManagerDelegate( WindowManagerDelegate* window_manager_delegate) = 0; @@ -36,11 +35,10 @@ class ViewManager { // Returns the URL of the application that embedded this application. virtual const std::string& GetEmbedderURL() const = 0; - // Returns all root nodes known to this connection. - virtual const std::vector& GetRoots() const = 0; + // Returns all root views known to this connection. + virtual const std::vector& GetRoots() const = 0; - // Returns a Node or View known to this connection. - virtual Node* GetNodeById(Id id) = 0; + // Returns a View known to this connection. virtual View* GetViewById(Id id) = 0; protected: diff --git a/mojo/services/public/cpp/view_manager/view_manager_delegate.h b/mojo/services/public/cpp/view_manager/view_manager_delegate.h index 424f62a48b60f..bbb9867608b9d 100644 --- a/mojo/services/public/cpp/view_manager/view_manager_delegate.h +++ b/mojo/services/public/cpp/view_manager/view_manager_delegate.h @@ -11,7 +11,7 @@ namespace mojo { class ServiceProviderImpl; -class Node; +class View; class ViewManager; // Interface implemented by an application using the view manager. @@ -32,7 +32,7 @@ class ViewManagerDelegate { // to the embedder and any services obtained from it are not broken and will // continue to be valid. virtual void OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) = 0; diff --git a/mojo/services/public/cpp/view_manager/view_observer.h b/mojo/services/public/cpp/view_manager/view_observer.h index c83f8c19a2e8b..2be9dbcdfe3c3 100644 --- a/mojo/services/public/cpp/view_manager/view_observer.h +++ b/mojo/services/public/cpp/view_manager/view_observer.h @@ -9,19 +9,59 @@ #include "base/basictypes.h" +#include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/interfaces/input_events/input_events.mojom.h" +namespace gfx { +class Rect; +} + namespace mojo { class View; -// See note about -ing/-ed suffixes for observer methods in node_observer.h. +// A note on -ing and -ed suffixes: +// +// -ing methods are called before changes are applied to the local view model. +// -ed methods are called after changes are applied to the local view model. +// +// If the change originated from another connection to the view manager, it's +// possible that the change has already been applied to the service-side model +// prior to being called, so for example in the case of OnViewDestroying(), it's +// possible the view has already been destroyed on the service side. class ViewObserver { public: + struct TreeChangeParams { + TreeChangeParams(); + View* target; + View* old_parent; + View* new_parent; + View* receiver; + }; + + virtual void OnTreeChanging(const TreeChangeParams& params) {} + virtual void OnTreeChanged(const TreeChangeParams& params) {} + + virtual void OnViewReordering(View* view, + View* relative_view, + OrderDirection direction) {} + virtual void OnViewReordered(View* view, + View* relative_view, + OrderDirection direction) {} + virtual void OnViewDestroying(View* view) {} virtual void OnViewDestroyed(View* view) {} + virtual void OnViewBoundsChanging(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) {} + virtual void OnViewBoundsChanged(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) {} + + virtual void OnViewFocusChanged(View* gained_focus, View* lost_focus) {} + virtual void OnViewInputEvent(View* view, const EventPtr& event) {} protected: diff --git a/mojo/services/public/cpp/view_manager/window_manager_delegate.h b/mojo/services/public/cpp/view_manager/window_manager_delegate.h index 6214832b4d51e..0ab271c05eebd 100644 --- a/mojo/services/public/cpp/view_manager/window_manager_delegate.h +++ b/mojo/services/public/cpp/view_manager/window_manager_delegate.h @@ -13,16 +13,16 @@ namespace mojo { class View; // A WindowManagerDelegate is provided by the application embedded at the -// service root node. +// service root view. class WindowManagerDelegate { public: - // Create an appropriate node to embed |url|. + // Create an appropriate view to embed |url|. virtual void Embed(const String& url, InterfaceRequest service_provider) {} // Dispatch the supplied input event to the appropriate view (taking into // account focus, activation, modality, etc.). - virtual void DispatchEvent(View* target, EventPtr event) = 0; + virtual void DispatchEvent(EventPtr event) = 0; protected: virtual ~WindowManagerDelegate() {} diff --git a/mojo/services/public/interfaces/content_handler/BUILD.gn b/mojo/services/public/interfaces/content_handler/BUILD.gn new file mode 100644 index 0000000000000..c72cc9a2c9173 --- /dev/null +++ b/mojo/services/public/interfaces/content_handler/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. + +import("//mojo/public/tools/bindings/mojom.gni") + +# GYP version: mojo/mojo_services.gypi:mojo_content_handler_bindings +mojom("content_handler") { + sources = [ + "content_handler.mojom", + ] + + deps = [ + "//mojo/public/interfaces/application", + "//mojo/services/public/interfaces/network", + ] +} diff --git a/mojo/services/public/interfaces/content_handler/content_handler.mojom b/mojo/services/public/interfaces/content_handler/content_handler.mojom new file mode 100644 index 0000000000000..6c07580e30a0c --- /dev/null +++ b/mojo/services/public/interfaces/content_handler/content_handler.mojom @@ -0,0 +1,16 @@ +// 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 "mojo/public/interfaces/application/service_provider.mojom" +import "mojo/services/public/interfaces/network/url_loader.mojom" + +module mojo { + +interface ContentHandler { + OnConnect(string url, + URLResponse url_response, + ServiceProvider service_provider); +}; + +} diff --git a/mojo/services/public/interfaces/surfaces/surfaces.mojom b/mojo/services/public/interfaces/surfaces/surfaces.mojom index 1837440997d3c..0aead1e6bb7e2 100644 --- a/mojo/services/public/interfaces/surfaces/surfaces.mojom +++ b/mojo/services/public/interfaces/surfaces/surfaces.mojom @@ -13,6 +13,7 @@ enum ResourceFormat { RGBA_8888, RGBA_4444, BGRA_8888, + ALPHA_8, LUMINANCE_8, RGB_565, ETC1, diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom index 3d6233c7d06e8..411f8cc7c107f 100644 --- a/mojo/services/public/interfaces/view_manager/view_manager.mojom +++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom @@ -9,9 +9,8 @@ import "mojo/services/public/interfaces/view_manager/view_manager_constants.mojo module mojo { -struct NodeData { +struct ViewData { uint32 parent_id; - uint32 node_id; uint32 view_id; mojo.Rect bounds; // TODO(sky): add visible. @@ -24,135 +23,119 @@ enum ErrorCode { }; // ViewManagerInitService is used to grant an application a connection to the -// ViewManager by embedding it at an approriate Node. +// ViewManager by embedding it at an approriate View. interface ViewManagerInitService { - // Embed the application @ |url| at an appropriate Node. + // Embed the application @ |url| at an appropriate View. // The first time this method is called in the lifetime of a View Manager - // application instance, the "appropriate Node" is defined as being the - // service root Node. + // application instance, the "appropriate View" is defined as being the + // service root View. // Subsequent times, implementation of this method is delegated to the - // application embedded at the service root Node. This application is + // application embedded at the service root View. This application is // typically referred to as the "window manager", and will have a specific - // definition of where within its Node hierarchy to embed an unparented URL. + // definition of where within its View hierarchy to embed an unparented URL. // See ViewManagerService below for more details about |service_provider|. Embed(string url, ServiceProvider service_provider) => (bool success); }; -// Nodes and Views are identified by a uint32. The upper 16 bits are the -// connection id, and the lower 16 the id assigned by the client. +// Views are identified by a uint32. The upper 16 bits are the connection id, +// and the lower 16 the id assigned by the client. // -// The root node is identified with a connection id of 0, and value of 1. +// The root view is identified with a connection id of 0, and value of 1. [Client=ViewManagerClient] interface ViewManagerService { - // Creates a new node with the specified id. It is up to the client to ensure + // Creates a new view with the specified id. It is up to the client to ensure // the id is unique to the connection (the id need not be globally unique). - // Additionally the connection id (embedded in |node_id|) must match that of + // Additionally the connection id (embedded in |view_id|) must match that of // the connection. // Errors: - // ERROR_CODE_VALUE_IN_USE: a node already exists with the specified id. - // ERROR_CODE_ILLEGAL_ARGUMENT: The connection part of |node_id| does not + // ERROR_CODE_VALUE_IN_USE: a view already exists with the specified id. + // ERROR_CODE_ILLEGAL_ARGUMENT: The connection part of |view_id| does not // match the connection id of the client. - CreateNode(uint32 node_id) => (ErrorCode error_code); + CreateView(uint32 view_id) => (ErrorCode error_code); - // Deletes a node. This does not recurse. No hierarchy change notifications - // are sent as a result of this. Only the connection that created the node can + // Deletes a view. This does not recurse. No hierarchy change notifications + // are sent as a result of this. Only the connection that created the view can // delete it. - DeleteNode(uint32 node_id) => (bool success); + DeleteView(uint32 view_id) => (bool success); - // Sets the specified bounds of the specified node. - SetNodeBounds(uint32 node_id, mojo.Rect bounds) => (bool success); + // Sets the specified bounds of the specified view. + SetViewBounds(uint32 view_id, mojo.Rect bounds) => (bool success); - // Sets the visibility of the specified node to |visible|. Connections are - // allowed to change the visibility of any node they have created, as well as + // Sets the visibility of the specified view to |visible|. Connections are + // allowed to change the visibility of any view they have created, as well as // any of their roots. - SetNodeVisibility(uint32 node_id, bool visible) => (bool success); + SetViewVisibility(uint32 view_id, bool visible) => (bool success); - // Reparents a node. + // Reparents a view. // This fails for any of the following reasons: - // . |parent| or |child| does not identify a valid node. + // . |parent| or |child| does not identify a valid view. // . |child| is an ancestor of |parent|. // . |child| is already a child of |parent|. // - // This may result in a connection getting OnNodeDeleted(). See - // RemoveNodeFromParent for details. - AddNode(uint32 parent, uint32 child) => (bool success); + // This may result in a connection getting OnViewDeleted(). See + // RemoveViewFromParent for details. + AddView(uint32 parent, uint32 child) => (bool success); - // Removes a view from its current parent. This fails if the node is not - // valid or the node already has no parent. + // Removes a view from its current parent. This fails if the view is not + // valid or the view already has no parent. // - // Removing a node from a parent may result in OnNodeDeleted() being sent to - // other connections. For example, connection A has nodes 1 and 2, with 2 a + // Removing a view from a parent may result in OnViewDeleted() being sent to + // other connections. For example, connection A has views 1 and 2, with 2 a // child of 1. Connection B has a root 1. If 2 is removed from 1 then B gets - // OnNodeDeleted(). This is done as node 2 is effectively no longer visible to + // OnViewDeleted(). This is done as view 2 is effectively no longer visible to // connection B. - RemoveNodeFromParent(uint32 node_id) => (bool success); + RemoveViewFromParent(uint32 view_id) => (bool success); - // Reorders a node in its parent, relative to |relative_node_id| according to + // Reorders a view in its parent, relative to |relative_view_id| according to // |direction|. - // Only the connection that created the node's parent can reorder its + // Only the connection that created the view's parent can reorder its // children. - ReorderNode(uint32 node_id, - uint32 relative_node_id, + ReorderView(uint32 view_id, + uint32 relative_view_id, OrderDirection direction) => (bool success); - // Returns the nodes comprising the tree starting at |node_id|. |node_id| is - // the first result in the return value, unless |node_id| is invalid, in which - // case an empty vector is returned. The nodes are visited using a depth first + // Returns the views comprising the tree starting at |view_id|. |view_id| is + // the first result in the return value, unless |view_id| is invalid, in which + // case an empty vector is returned. The views are visited using a depth first // search (pre-order). - GetNodeTree(uint32 node_id) => (NodeData[] nodes); - - // Creates a new view with the specified id. It is up to the client to ensure - // the id is unique to the connection (the id need not be globally unique). - // Additionally the connection id (embedded in |view_id|) must match that of - // the connection. - CreateView(uint32 view_id) => (bool success); - - // Deletes the view with the specified id. Only the connection that created - // the view can delete it. - DeleteView(uint32 view_id) => (bool success); - - // Sets the view a node is showing. - SetView(uint32 node_id, uint32 view_id) => (bool success); + GetViewTree(uint32 view_id) => (ViewData[] views); // Shows the specified image (png encoded) in the specified view. SetViewContents(uint32 view_id, handle buffer, uint32 buffer_size) => (bool success); - // Sets focus to the specified node. - SetFocus(uint32 node_id) => (bool success); - - // Embeds the app for |url| in the specified node. More specifically this + // Embeds the app for |url| in the specified view. More specifically this // creates a new connection to the specified url, expecting to get a - // ViewManagerClient and configures it with the root node |node|. Fails - // if |node| was not created by this connection. + // ViewManagerClient and configures it with the root view |view|. Fails + // if |view| was not created by this connection. // // If a particular client invokes Embed() multiple times with the same url, // the connection is reused. When this happens the ViewManagerClient is // notified of the additional roots by way of OnEmbed(). // - // A node may only be a root of one connection at a time. Subsequent calls to - // Embed() for the same node result in the node being removed from the + // A view may only be a root of one connection at a time. Subsequent calls to + // Embed() for the same view result in the view being removed from the // current connection. The current connection is told this by way of - // OnNodeDeleted(). + // OnViewDeleted(). // // When a connection embeds an app the connection no longer has priviledges - // to access or see any of the children of the node. If the node had existing + // to access or see any of the children of the view. If the view had existing // children the children are removed. The one exception is the root // connection. // - // If |node_id| is 0, the View Manager delegates determination of what node to - // embed |url| at to the app embedded at the service root node (i.e. the + // If |view_id| is 0, the View Manager delegates determination of what view to + // embed |url| at to the app embedded at the service root view (i.e. the // window manager). // // |service_provider| encapsulates services offered by the embedder to the // embedded app alongside this Embed() call. It also provides a means for // the embedder to connect to services symmetrically exposed by the embedded - // app. Note that if a different app is subsequently embedded at |node_id| + // app. Note that if a different app is subsequently embedded at |view_id| // the |service_provider|'s connection to its client in the embedded app and // any services it provided are not broken and continue to be valid. Embed(string url, - uint32 node_id, + uint32 view_id, ServiceProvider service_provider) => (bool success); // TODO(sky): move these to a separate interface when FIFO works. @@ -161,70 +144,54 @@ interface ViewManagerService { DispatchOnViewInputEvent(uint32 view_id, mojo.Event event); }; -// Changes to nodes/views are not sent to the connection that originated the -// change. For example, if connection 1 attaches a view to a node (SetView()) -// connection 1 does not receive OnNodeViewReplaced(). +// Changes to views are not sent to the connection that originated the +// change. For example, if connection 1 changes the bounds of a view by calling +// SetBounds(), connection 1 does not receive OnViewBoundsChanged(). [Client=ViewManagerService] interface ViewManagerClient { // Invoked when the client application has been embedded at |root|. // See Embed() on ViewManagerService for more details. OnEmbed(uint16 connection_id, string embedder_url, - NodeData root, + ViewData root, ServiceProvider& service_provider); - // Invoked when a node's bounds have changed. - OnNodeBoundsChanged(uint32 node, mojo.Rect old_bounds, mojo.Rect new_bounds); + // Invoked when a view's bounds have changed. + OnViewBoundsChanged(uint32 view, mojo.Rect old_bounds, mojo.Rect new_bounds); // Invoked when a change is done to the hierarchy. A value of 0 is used to - // identify a null node. For example, if the old_parent is NULL, 0 is + // identify a null view. For example, if the old_parent is NULL, 0 is // supplied. - // |nodes| contains any nodes that are that the client has not been told - // about. This is not sent for hierarchy changes of nodes not known to this + // |views| contains any views that are that the client has not been told + // about. This is not sent for hierarchy changes of views not known to this // client or not attached to the tree. - OnNodeHierarchyChanged(uint32 node, + OnViewHierarchyChanged(uint32 view, uint32 new_parent, uint32 old_parent, - NodeData[] nodes); + ViewData[] views); - // Invoked when the order of nodes within a parent changes. - OnNodeReordered(uint32 node_id, - uint32 relative_node_id, + // Invoked when the order of views within a parent changes. + OnViewReordered(uint32 view_id, + uint32 relative_view_id, OrderDirection direction); - // Invoked when a node is deleted. - OnNodeDeleted(uint32 node); - - // Invoked when the view associated with a node is replaced by another view. - // 0 is used to identify a null view. - OnNodeViewReplaced(uint32 node, uint32 new_view_id, uint32 old_view_id); - // Invoked when a view is deleted. OnViewDeleted(uint32 view); // Invoked when an event is targeted at the specified view. OnViewInputEvent(uint32 view, mojo.Event event) => (); - // Invoked when focus shifts from one Node to another. |gained_focus_id| is - // the id of the node that gained focus, or 0 if the node that gained focus is - // not known to this connection. |lost_focus_id| is likewise the node that - // lost focus. - // TODO(beng): once aura is removed from the service, focus management should - // entirely move to the window manager and this method can be - // removed. - OnFocusChanged(uint32 gained_focus_id, uint32 lost_focus_id); - // TODO(sky): The following methods represent an interface between the view - // manager and the application embedded at the service root node + // manager and the application embedded at the service root view // (i.e. the window manager). These methods are not called on // any other clients. They should be moved to a separate interface // once support for derived FIFOs is landed. - // Requests the window manager create a "top level" node embedding |url|. + // Requests the window manager create a "top level" view embedding |url|. Embed(string url, ServiceProvider& service_provider); - // Requests the view manager dispatch the event targeted at |view|. - DispatchOnViewInputEvent(uint32 view, mojo.Event event); + // Requests the view manager dispatch the event. + DispatchOnViewInputEvent(mojo.Event event); }; } diff --git a/mojo/services/public/interfaces/window_manager/window_manager.mojom b/mojo/services/public/interfaces/window_manager/window_manager.mojom index ee31416f5c527..41deb44e07cd6 100644 --- a/mojo/services/public/interfaces/window_manager/window_manager.mojom +++ b/mojo/services/public/interfaces/window_manager/window_manager.mojom @@ -6,11 +6,9 @@ module mojo { [Client=WindowManagerClient] interface WindowManagerService { - OpenWindow() => (uint32 node_id); - OpenWindowWithURL(string url) => (uint32 node_id); - SetCapture(uint32 node_id) => (bool success); - FocusWindow(uint32 node_id) => (bool success); - ActivateWindow(uint32 node_id) => (bool success); + SetCapture(uint32 view_id) => (bool success); + FocusWindow(uint32 view_id) => (bool success); + ActivateWindow(uint32 view_id) => (bool success); }; [Client=WindowManagerService] @@ -19,9 +17,9 @@ interface WindowManagerClient { // connects to it before it has been initialized). OnWindowManagerReady(); - // TODO(beng): how is the WM supposed to know if a node is known to a client + // TODO(beng): how is the WM supposed to know if a view is known to a client // or not? - OnCaptureChanged(uint32 old_capture_node_id, uint32 new_capture_node_id); + OnCaptureChanged(uint32 old_capture_view_id, uint32 new_capture_view_id); OnFocusChanged(uint32 old_focused_node_id, uint32 new_focused_node_id); OnActiveWindowChanged(uint32 old_focused_window, uint32 new_focused_window); diff --git a/mojo/services/surfaces/surfaces_impl.cc b/mojo/services/surfaces/surfaces_impl.cc index 141d0c4a0eea0..b05e16a0f3f2f 100644 --- a/mojo/services/surfaces/surfaces_impl.cc +++ b/mojo/services/surfaces/surfaces_impl.cc @@ -9,7 +9,6 @@ #include "cc/surfaces/display.h" #include "cc/surfaces/surface_id_allocator.h" #include "mojo/cc/context_provider_mojo.h" -#include "mojo/public/cpp/gles2/gles2.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" #include "mojo/services/public/cpp/surfaces/surfaces_type_converters.h" @@ -90,8 +89,6 @@ void SurfacesImpl::ReturnResources(const cc::ReturnedResourceArray& resources) { } scoped_ptr SurfacesImpl::CreateOutputSurface() { - static GLES2Initializer* gles2 = new GLES2Initializer; - DCHECK(gles2); return make_scoped_ptr(new cc::OutputSurface( new ContextProviderMojo(command_buffer_handle_.Pass()))); } diff --git a/mojo/services/surfaces/surfaces_impl.h b/mojo/services/surfaces/surfaces_impl.h index 28ac9ce4ec30e..b3c719d435179 100644 --- a/mojo/services/surfaces/surfaces_impl.h +++ b/mojo/services/surfaces/surfaces_impl.h @@ -18,7 +18,7 @@ class Display; } namespace mojo { -class ServiceManager; +class ApplicationManager; class SurfaceNativeViewportClient; diff --git a/mojo/services/test_service/BUILD.gn b/mojo/services/test_service/BUILD.gn index 684826ff36827..0d1d99c585470 100644 --- a/mojo/services/test_service/BUILD.gn +++ b/mojo/services/test_service/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//mojo/public/tools/bindings/mojom.gni") +import("//mojo/system.gni") # GYP version: mojo/mojo_services.gypi:mojo_test_service_bindings mojom("bindings") { @@ -11,3 +12,51 @@ mojom("bindings") { "test_service.mojom", ] } + +# GYP version: mojo/mojo_services.gypi:mojo_test_app +shared_library("mojo_test_app") { + deps = [ + ":bindings", + "//base", + "//mojo/public/cpp/application:main_standalone", + "//mojo/public/cpp/application:standalone", + "//mojo/public/cpp/environment:standalone", + "//mojo/public/cpp/utility", + ] + deps += mojo_system_for_shared_library + + sources = [ + "test_request_tracker_client_impl.cc", + "test_request_tracker_client_impl.h", + "test_service_application.cc", + "test_service_application.h", + "test_service_impl.cc", + "test_service_impl.h", + "test_time_service_impl.cc", + "test_time_service_impl.h", + ] +} + +# GYP version: //mojo/mojo_services.gypi:mojo_test_request_tracker_app +shared_library("mojo_test_request_tracker_app") { + deps = [ + ":bindings", + "//base", + "//mojo/public/cpp/application:main_standalone", + "//mojo/public/cpp/application:standalone", + "//mojo/public/cpp/environment:standalone", + "//mojo/public/cpp/utility", + ] + deps += mojo_system_for_shared_library + + sources = [ + "test_request_tracker_client_impl.cc", + "test_request_tracker_client_impl.h", + "test_request_tracker_application.cc", + "test_request_tracker_application.h", + "test_time_service_impl.cc", + "test_time_service_impl.h", + "test_request_tracker_impl.cc", + "test_request_tracker_impl.h", + ] +} diff --git a/mojo/services/view_manager/BUILD.gn b/mojo/services/view_manager/BUILD.gn index 8ee8b27aea4ee..bae54f1f9db3f 100644 --- a/mojo/services/view_manager/BUILD.gn +++ b/mojo/services/view_manager/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//mojo/system.gni") + component("view_manager") { deps = [ "//base", @@ -30,6 +32,7 @@ component("view_manager") { "//ui/gl", "//webkit/common/gpu", ] + deps += mojo_system_for_component defines = [ "MOJO_VIEW_MANAGER_IMPLEMENTATION", @@ -54,8 +57,6 @@ component("view_manager") { "root_view_manager_delegate.h", "screen_impl.cc", "screen_impl.h", - "view.cc", - "view.h", "view_manager_export.h", "view_manager_init_service_context.cc", "view_manager_init_service_context.h", @@ -68,8 +69,4 @@ component("view_manager") { "window_tree_host_impl.cc", "window_tree_host_impl.h", ] - - if (is_component_build) { - deps += [ "//mojo/system" ] - } } diff --git a/mojo/services/view_manager/DEPS b/mojo/services/view_manager/DEPS index e6f1501ffe15d..5933b40b67f33 100644 --- a/mojo/services/view_manager/DEPS +++ b/mojo/services/view_manager/DEPS @@ -17,6 +17,6 @@ include_rules = [ specific_include_rules = { "view_manager_unittest.cc": [ - "+mojo/service_manager/service_manager.h", + "+mojo/application_manager/application_manager.h", ], } diff --git a/mojo/services/view_manager/access_policy.h b/mojo/services/view_manager/access_policy.h index bde919d81e0f3..8c4e84d402644 100644 --- a/mojo/services/view_manager/access_policy.h +++ b/mojo/services/view_manager/access_policy.h @@ -29,17 +29,13 @@ class AccessPolicy { const Node* relative_node, OrderDirection direction) const = 0; virtual bool CanDeleteNode(const Node* node) const = 0; - virtual bool CanDeleteView(const View* view) const = 0; - // Note |view| may be NULL here. - virtual bool CanSetView(const Node* node, const View* view) const = 0; - virtual bool CanSetFocus(const Node* node) const = 0; virtual bool CanGetNodeTree(const Node* node) const = 0; // Used when building a node tree (GetNodeTree()) to decide if we should // descend into |node|. virtual bool CanDescendIntoNodeForNodeTree(const Node* node) const = 0; virtual bool CanEmbed(const Node* node) const = 0; virtual bool CanChangeNodeVisibility(const Node* node) const = 0; - virtual bool CanSetViewContents(const View* view) const = 0; + virtual bool CanSetNodeContents(const Node* node) const = 0; virtual bool CanSetNodeBounds(const Node* node) const = 0; // Returns whether the connection should notify on a hierarchy change. @@ -50,10 +46,6 @@ class AccessPolicy { const Node** new_parent, const Node** old_parent) const = 0; - // Returns the Id of the view to send to the client. |view| is or was - // associated with |node|. - virtual Id GetViewIdToSend(const Node* node, const View* view) const = 0; - virtual bool ShouldSendViewDeleted(const ViewId& view_id) const = 0; }; diff --git a/mojo/services/view_manager/default_access_policy.cc b/mojo/services/view_manager/default_access_policy.cc index 19a4a31c85e0a..7f87051f0b6e1 100644 --- a/mojo/services/view_manager/default_access_policy.cc +++ b/mojo/services/view_manager/default_access_policy.cc @@ -6,7 +6,6 @@ #include "mojo/services/view_manager/access_policy_delegate.h" #include "mojo/services/view_manager/node.h" -#include "mojo/services/view_manager/view.h" namespace mojo { namespace service { @@ -47,22 +46,6 @@ bool DefaultAccessPolicy::CanDeleteNode(const Node* node) const { return WasCreatedByThisConnection(node); } -bool DefaultAccessPolicy::CanDeleteView(const View* view) const { - return WasCreatedByThisConnection(view); -} - -bool DefaultAccessPolicy::CanSetView(const Node* node, const View* view) const { - if (view && !WasCreatedByThisConnection(view)) - return false; - - return WasCreatedByThisConnection(node) || IsNodeInRoots(node); -} - -bool DefaultAccessPolicy::CanSetFocus(const Node* node) const { - // TODO(beng): security. - return true; -} - bool DefaultAccessPolicy::CanGetNodeTree(const Node* node) const { return WasCreatedByThisConnection(node) || IsNodeInRoots(node); } @@ -81,8 +64,12 @@ bool DefaultAccessPolicy::CanChangeNodeVisibility(const Node* node) const { return WasCreatedByThisConnection(node) || IsNodeInRoots(node); } -bool DefaultAccessPolicy::CanSetViewContents(const View* view) const { - return WasCreatedByThisConnection(view); +bool DefaultAccessPolicy::CanSetNodeContents(const Node* node) const { + // Once a node embeds another app, the embedder app is no longer able to + // call SetNodeContents() - this ability is transferred to the embedded app. + if (delegate_->IsNodeRootOfAnotherConnectionForAccessPolicy(node)) + return false; + return WasCreatedByThisConnection(node) || IsNodeInRoots(node); } bool DefaultAccessPolicy::CanSetNodeBounds(const Node* node) const { @@ -108,12 +95,6 @@ bool DefaultAccessPolicy::ShouldNotifyOnHierarchyChange( return true; } -Id DefaultAccessPolicy::GetViewIdToSend(const Node* node, - const View* view) const { - // TODO(sky): should we send null if view is not from this connection? - return ViewIdToTransportId(view->id()); -} - bool DefaultAccessPolicy::ShouldSendViewDeleted(const ViewId& view_id) const { return view_id.connection_id == connection_id_; } diff --git a/mojo/services/view_manager/default_access_policy.h b/mojo/services/view_manager/default_access_policy.h index 370d22c5d2351..9f84378c9b49d 100644 --- a/mojo/services/view_manager/default_access_policy.h +++ b/mojo/services/view_manager/default_access_policy.h @@ -27,20 +27,16 @@ class DefaultAccessPolicy : public AccessPolicy { const Node* relative_node, OrderDirection direction) const OVERRIDE; virtual bool CanDeleteNode(const Node* node) const OVERRIDE; - virtual bool CanDeleteView(const View* view) const OVERRIDE; - virtual bool CanSetView(const Node* node, const View* view) const OVERRIDE; - virtual bool CanSetFocus(const Node* node) const OVERRIDE; virtual bool CanGetNodeTree(const Node* node) const OVERRIDE; virtual bool CanDescendIntoNodeForNodeTree(const Node* node) const OVERRIDE; virtual bool CanEmbed(const Node* node) const OVERRIDE; virtual bool CanChangeNodeVisibility(const Node* node) const OVERRIDE; - virtual bool CanSetViewContents(const View* view) const OVERRIDE; + virtual bool CanSetNodeContents(const Node* node) const OVERRIDE; virtual bool CanSetNodeBounds(const Node* node) const OVERRIDE; virtual bool ShouldNotifyOnHierarchyChange( const Node* node, const Node** new_parent, const Node** old_parent) const OVERRIDE; - virtual Id GetViewIdToSend(const Node* node, const View* view) const OVERRIDE; virtual bool ShouldSendViewDeleted(const ViewId& view_id) const OVERRIDE; private: diff --git a/mojo/services/view_manager/node.cc b/mojo/services/view_manager/node.cc index 65bc94684c728..96ecc8a137584 100644 --- a/mojo/services/view_manager/node.cc +++ b/mojo/services/view_manager/node.cc @@ -5,7 +5,6 @@ #include "mojo/services/view_manager/node.h" #include "mojo/services/view_manager/node_delegate.h" -#include "mojo/services/view_manager/view.h" #include "ui/aura/window_property.h" #include "ui/base/cursor/cursor.h" #include "ui/base/hit_test.h" @@ -23,7 +22,6 @@ DEFINE_WINDOW_PROPERTY_KEY(Node*, kNodeKey, NULL); Node::Node(NodeDelegate* delegate, const NodeId& id) : delegate_(delegate), id_(id), - view_(NULL), window_(this) { DCHECK(delegate); // Must provide a delegate. window_.set_owned_by_parent(false); @@ -41,11 +39,6 @@ Node::~Node() { if (window_.parent()) window_.parent()->RemoveChild(&window_); - // This must be done *after* updating the hierarchy since the hierarchy change - // will remove the node from the connections that know about it, preventing - // this notification from being sent after the destruction notification. - SetView(NULL); - delegate_->OnNodeDestroyed(this); } @@ -113,21 +106,9 @@ void Node::SetVisible(bool value) { window_.Hide(); } -void Node::SetView(View* view) { - if (view == view_) - return; - - // Detach view from existing node. This way notifications are sent out. - if (view && view->node()) - view->node()->SetView(NULL); - - View* old_view = view_; - if (view_) - view_->set_node(NULL); - view_ = view; - if (view) - view->set_node(this); - delegate_->OnNodeViewReplaced(this, view, old_view); +void Node::SetBitmap(const SkBitmap& bitmap) { + bitmap_ = bitmap; + window_.SchedulePaintInRect(gfx::Rect(window_.bounds().size())); } void Node::OnWindowHierarchyChanged( @@ -181,10 +162,7 @@ void Node::OnCaptureLost() { } void Node::OnPaint(gfx::Canvas* canvas) { - if (view_) { - canvas->DrawImageInt( - gfx::ImageSkia::CreateFrom1xBitmap(view_->bitmap()), 0, 0); - } + canvas->DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bitmap_), 0, 0); } void Node::OnDeviceScaleFactorChanged(float device_scale_factor) { @@ -206,10 +184,5 @@ bool Node::HasHitTestMask() const { void Node::GetHitTestMask(gfx::Path* mask) const { } -void Node::OnEvent(ui::Event* event) { - if (view_) - delegate_->OnViewInputEvent(view_, event); -} - } // namespace service } // namespace mojo diff --git a/mojo/services/view_manager/node.h b/mojo/services/view_manager/node.h index 48b66c253ce1c..4f11c86267689 100644 --- a/mojo/services/view_manager/node.h +++ b/mojo/services/view_manager/node.h @@ -11,6 +11,7 @@ #include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" #include "mojo/services/view_manager/ids.h" #include "mojo/services/view_manager/view_manager_export.h" +#include "third_party/skia/include/core/SkBitmap.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_observer.h" @@ -19,7 +20,6 @@ namespace mojo { namespace service { class NodeDelegate; -class View; // Represents a node in the graph. Delegate is informed of interesting events. class MOJO_VIEW_MANAGER_EXPORT Node @@ -31,9 +31,6 @@ class MOJO_VIEW_MANAGER_EXPORT Node static Node* NodeForWindow(aura::Window* window); - void set_view_id(const ViewId& view_id) { view_id_ = view_id; } - const ViewId& view_id() const { return view_id_; } - const NodeId& id() const { return id_; } void Add(Node* child); @@ -65,10 +62,8 @@ class MOJO_VIEW_MANAGER_EXPORT Node bool IsVisible() const; void SetVisible(bool value); - // Sets the view associated with this node. Node does not own its View. - void SetView(View* view); - View* view() { return view_; } - const View* view() const { return view_; } + void SetBitmap(const SkBitmap& contents); + const SkBitmap& bitmap() const { return bitmap_; } private: // WindowObserver overrides: @@ -95,18 +90,11 @@ class MOJO_VIEW_MANAGER_EXPORT Node virtual bool HasHitTestMask() const OVERRIDE; virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE; - // ui::EventHandler overrides: - virtual void OnEvent(ui::Event* event) OVERRIDE; - NodeDelegate* delegate_; const NodeId id_; - // Weak pointer to view associated with this node. - View* view_; - - ViewId view_id_; - aura::Window window_; + SkBitmap bitmap_; DISALLOW_COPY_AND_ASSIGN(Node); }; diff --git a/mojo/services/view_manager/node_delegate.h b/mojo/services/view_manager/node_delegate.h index 898c16c2671fc..981f4b33ef645 100644 --- a/mojo/services/view_manager/node_delegate.h +++ b/mojo/services/view_manager/node_delegate.h @@ -7,10 +7,6 @@ #include "mojo/services/view_manager/view_manager_export.h" -namespace ui { -class Event; -} - namespace gfx { class Rect; } @@ -19,7 +15,6 @@ namespace mojo { namespace service { class Node; -class View; class MOJO_VIEW_MANAGER_EXPORT NodeDelegate { public: @@ -36,15 +31,6 @@ class MOJO_VIEW_MANAGER_EXPORT NodeDelegate { const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) = 0; - // Invoked when the View associated with a node changes. - virtual void OnNodeViewReplaced(const Node* node, - const View* new_view, - const View* old_view) = 0; - - // Invoked when an input event is received by the View at this node. - virtual void OnViewInputEvent(const View* view, - const ui::Event* event) = 0; - protected: virtual ~NodeDelegate() {} }; diff --git a/mojo/services/view_manager/root_node_manager.cc b/mojo/services/view_manager/root_node_manager.cc index d9ed9fe31da3c..03a81d294317c 100644 --- a/mojo/services/view_manager/root_node_manager.cc +++ b/mojo/services/view_manager/root_node_manager.cc @@ -8,9 +8,7 @@ #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" #include "mojo/services/public/cpp/input_events/input_events_type_converters.h" -#include "mojo/services/view_manager/view.h" #include "mojo/services/view_manager/view_manager_service_impl.h" -#include "ui/aura/client/focus_client.h" #include "ui/aura/env.h" namespace mojo { @@ -54,9 +52,6 @@ RootNodeManager::RootNodeManager( } RootNodeManager::~RootNodeManager() { - aura::client::FocusClient* focus_client = - aura::client::GetFocusClient(root_->window()); - focus_client->RemoveObserver(this); while (!connections_created_by_connect_.empty()) delete *(connections_created_by_connect_.begin()); // All the connections should have been destroyed. @@ -122,11 +117,6 @@ Node* RootNodeManager::GetNode(const NodeId& id) { return i == connection_map_.end() ? NULL : i->second->GetNode(id); } -View* RootNodeManager::GetView(const ViewId& id) { - ConnectionMap::iterator i = connection_map_.find(id.connection_id); - return i == connection_map_.end() ? NULL : i->second->GetView(id); -} - void RootNodeManager::OnConnectionMessagedClient(ConnectionSpecificId id) { if (current_change_) current_change_->MarkConnectionAsMessaged(id); @@ -158,17 +148,13 @@ const ViewManagerServiceImpl* RootNodeManager::GetConnectionWithRoot( return NULL; } -void RootNodeManager::DispatchViewInputEventToWindowManager( - const View* view, - const ui::Event* event) { +void RootNodeManager::DispatchNodeInputEventToWindowManager(EventPtr event) { // Input events are forwarded to the WindowManager. The WindowManager // eventually calls back to us with DispatchOnViewInputEvent(). ViewManagerServiceImpl* connection = GetConnection(kWindowManagerConnection); if (!connection) return; - connection->client()->DispatchOnViewInputEvent( - ViewIdToTransportId(view->id()), - TypeConverter::ConvertFrom(*event)); + connection->client()->DispatchOnViewInputEvent(event.Pass()); } void RootNodeManager::ProcessNodeBoundsChanged(const Node* node, @@ -201,16 +187,6 @@ void RootNodeManager::ProcessNodeReorder(const Node* node, } } -void RootNodeManager::ProcessNodeViewReplaced(const Node* node, - const View* new_view, - const View* old_view) { - for (ConnectionMap::iterator i = connection_map_.begin(); - i != connection_map_.end(); ++i) { - i->second->ProcessNodeViewReplaced(node, new_view, old_view, - IsChangeSource(i->first)); - } -} - void RootNodeManager::ProcessNodeDeleted(const NodeId& node) { for (ConnectionMap::iterator i = connection_map_.begin(); i != connection_map_.end(); ++i) { @@ -218,24 +194,6 @@ void RootNodeManager::ProcessNodeDeleted(const NodeId& node) { } } -void RootNodeManager::ProcessViewDeleted(const ViewId& view) { - for (ConnectionMap::iterator i = connection_map_.begin(); - i != connection_map_.end(); ++i) { - i->second->ProcessViewDeleted(view, IsChangeSource(i->first)); - } -} - -void RootNodeManager::OnWindowFocused(aura::Window* gained_focus, - aura::Window* lost_focus) { - Node* focused_node = gained_focus ? Node::NodeForWindow(gained_focus) : NULL; - Node* blurred_node = lost_focus ? Node::NodeForWindow(lost_focus) : NULL; - for (ConnectionMap::iterator i = connection_map_.begin(); - i != connection_map_.end(); ++i) { - i->second->ProcessFocusChanged(focused_node, blurred_node, - IsChangeSource(i->first)); - } -} - void RootNodeManager::PrepareForChange(ScopedChange* change) { // Should only ever have one change in flight. CHECK(!current_change_); @@ -296,16 +254,5 @@ void RootNodeManager::OnNodeBoundsChanged(const Node* node, ProcessNodeBoundsChanged(node, old_bounds, new_bounds); } -void RootNodeManager::OnNodeViewReplaced(const Node* node, - const View* new_view, - const View* old_view) { - ProcessNodeViewReplaced(node, new_view, old_view); -} - -void RootNodeManager::OnViewInputEvent(const View* view, - const ui::Event* event) { - DispatchViewInputEventToWindowManager(view, event); -} - } // namespace service } // namespace mojo diff --git a/mojo/services/view_manager/root_node_manager.h b/mojo/services/view_manager/root_node_manager.h index e09b0364cb9f4..c09073b95e450 100644 --- a/mojo/services/view_manager/root_node_manager.h +++ b/mojo/services/view_manager/root_node_manager.h @@ -15,7 +15,6 @@ #include "mojo/services/view_manager/node_delegate.h" #include "mojo/services/view_manager/root_view_manager.h" #include "mojo/services/view_manager/view_manager_export.h" -#include "ui/aura/client/focus_change_observer.h" namespace ui { class Event; @@ -28,14 +27,11 @@ class ApplicationConnection; namespace service { class RootViewManagerDelegate; -class View; class ViewManagerServiceImpl; // RootNodeManager is responsible for managing the set of // ViewManagerServiceImpls as well as providing the root of the node hierarchy. -class MOJO_VIEW_MANAGER_EXPORT RootNodeManager - : public NodeDelegate, - public aura::client::FocusChangeObserver { +class MOJO_VIEW_MANAGER_EXPORT RootNodeManager : public NodeDelegate { public: // Create when a ViewManagerServiceImpl is about to make a change. Ensures // clients are notified of the correct change id. @@ -99,9 +95,6 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager // Returns the Node identified by |id|. Node* GetNode(const NodeId& id); - // Returns the View identified by |id|. - View* GetView(const ViewId& id); - Node* root() { return root_.get(); } bool IsProcessingChange() const { return current_change_ != NULL; } @@ -127,8 +120,7 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager } const ViewManagerServiceImpl* GetConnectionWithRoot(const NodeId& id) const; - void DispatchViewInputEventToWindowManager(const View* view, - const ui::Event* event); + void DispatchNodeInputEventToWindowManager(EventPtr event); // These functions trivially delegate to all ViewManagerServiceImpls, which in // term notify their clients. @@ -142,11 +134,7 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager void ProcessNodeReorder(const Node* node, const Node* relative_node, const OrderDirection direction); - void ProcessNodeViewReplaced(const Node* node, - const View* new_view_id, - const View* old_view_id); void ProcessNodeDeleted(const NodeId& node); - void ProcessViewDeleted(const ViewId& view); private: // Used to setup any static state needed by RootNodeManager. @@ -157,10 +145,6 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager typedef std::map ConnectionMap; - // Overridden from aura::client::FocusChangeObserver: - virtual void OnWindowFocused(aura::Window* gained_focus, - aura::Window* lost_focus) OVERRIDE; - // Invoked when a connection is about to make a change. Subsequently followed // by FinishChange() once the change is done. // @@ -192,11 +176,6 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager virtual void OnNodeBoundsChanged(const Node* node, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE; - virtual void OnNodeViewReplaced(const Node* node, - const View* new_view, - const View* old_view) OVERRIDE; - virtual void OnViewInputEvent(const View* view, - const ui::Event* event) OVERRIDE; Context context_; diff --git a/mojo/services/view_manager/root_view_manager.cc b/mojo/services/view_manager/root_view_manager.cc index 2156c447f0761..d44535dc57810 100644 --- a/mojo/services/view_manager/root_view_manager.cc +++ b/mojo/services/view_manager/root_view_manager.cc @@ -12,7 +12,6 @@ #include "mojo/services/view_manager/screen_impl.h" #include "mojo/services/view_manager/window_tree_host_impl.h" #include "ui/aura/client/default_capture_client.h" -#include "ui/aura/client/focus_change_observer.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/client/window_tree_client.h" #include "ui/aura/window.h" @@ -21,67 +20,21 @@ namespace mojo { namespace service { -// TODO(sky): revisit this, we may need a more sophisticated FocusClient -// implementation. -class FocusClientImpl : public aura::client::FocusClient, - public aura::WindowObserver { +// TODO(sky): Remove once aura is removed from the service. +class FocusClientImpl : public aura::client::FocusClient { public: - FocusClientImpl() - : focused_window_(NULL), - observer_manager_(this) { - } + FocusClientImpl() {} virtual ~FocusClientImpl() {} private: // Overridden from aura::client::FocusClient: - virtual void AddObserver(aura::client::FocusChangeObserver* observer) - OVERRIDE { - observers_.AddObserver(observer); - } - virtual void RemoveObserver(aura::client::FocusChangeObserver* observer) - OVERRIDE { - observers_.RemoveObserver(observer); - } - virtual void FocusWindow(aura::Window* window) OVERRIDE { - if (window && !window->CanFocus()) - return; - if (window == focused_window_) - return; - if (focused_window_) - observer_manager_.Remove(focused_window_); - aura::Window* old_focused_window = focused_window_; - focused_window_ = window; - if (focused_window_) - observer_manager_.Add(focused_window_); - - FOR_EACH_OBSERVER(aura::client::FocusChangeObserver, - observers_, - OnWindowFocused(focused_window_, old_focused_window)); - aura::client::FocusChangeObserver* observer = - aura::client::GetFocusChangeObserver(old_focused_window); - if (observer) - observer->OnWindowFocused(focused_window_, old_focused_window); - observer = aura::client::GetFocusChangeObserver(focused_window_); - if (observer) - observer->OnWindowFocused(focused_window_, old_focused_window); - } - virtual void ResetFocusWithinActiveWindow(aura::Window* window) OVERRIDE { - if (!window->Contains(focused_window_)) - FocusWindow(window); - } - virtual aura::Window* GetFocusedWindow() OVERRIDE { - return focused_window_; - } - - // Overridden from WindowObserver: - virtual void OnWindowDestroying(aura::Window* window) OVERRIDE { - DCHECK_EQ(window, focused_window_); - FocusWindow(NULL); - } - - aura::Window* focused_window_; - ScopedObserver observer_manager_; - ObserverList observers_; + virtual void AddObserver( + aura::client::FocusChangeObserver* observer) OVERRIDE {} + virtual void RemoveObserver( + aura::client::FocusChangeObserver* observer) OVERRIDE {} + virtual void FocusWindow(aura::Window* window) OVERRIDE {} + virtual void ResetFocusWithinActiveWindow(aura::Window* window) OVERRIDE {} + virtual aura::Window* GetFocusedWindow() OVERRIDE { return NULL; } DISALLOW_COPY_AND_ASSIGN(FocusClientImpl); }; @@ -133,7 +86,9 @@ RootViewManager::RootViewManager( gfx::Rect(800, 600), base::Bind(&RootViewManager::OnCompositorCreated, base::Unretained(this)), - native_viewport_closed_callback)); + native_viewport_closed_callback, + base::Bind(&RootNodeManager::DispatchNodeInputEventToWindowManager, + base::Unretained(root_node_manager_)))); } RootViewManager::~RootViewManager() { @@ -154,10 +109,9 @@ void RootViewManager::OnCompositorCreated() { window_tree_client_.reset( new WindowTreeClientImpl(window_tree_host_->window())); - focus_client_.reset(new FocusClientImpl()); + focus_client_.reset(new FocusClientImpl); aura::client::SetFocusClient(window_tree_host_->window(), focus_client_.get()); - focus_client_->AddObserver(root_node_manager_); window_tree_host_->Show(); diff --git a/mojo/services/view_manager/root_view_manager.h b/mojo/services/view_manager/root_view_manager.h index 8656d47b0d3ab..366e86bd39f16 100644 --- a/mojo/services/view_manager/root_view_manager.h +++ b/mojo/services/view_manager/root_view_manager.h @@ -10,7 +10,6 @@ #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "mojo/public/cpp/bindings/callback.h" -#include "mojo/public/cpp/gles2/gles2.h" #include "mojo/services/view_manager/view_manager_export.h" namespace aura { @@ -53,8 +52,6 @@ class MOJO_VIEW_MANAGER_EXPORT RootViewManager { RootNodeManager* root_node_manager_; - GLES2Initializer gles_initializer_; - // Returns true if adding the root node's window to |window_tree_host_|. bool in_setup_; diff --git a/mojo/services/view_manager/test_change_tracker.cc b/mojo/services/view_manager/test_change_tracker.cc index 9c8ee42981c76..c4027ea6712e0 100644 --- a/mojo/services/view_manager/test_change_tracker.cc +++ b/mojo/services/view_manager/test_change_tracker.cc @@ -60,21 +60,10 @@ std::string ChangeToDescription1(const Change& change) { return base::StringPrintf("NodeDeleted node=%s", NodeIdToString(change.node_id).c_str()); - case CHANGE_TYPE_VIEW_DELETED: - return base::StringPrintf("ViewDeleted view=%s", - NodeIdToString(change.view_id).c_str()); - - case CHANGE_TYPE_VIEW_REPLACED: - return base::StringPrintf( - "ViewReplaced node=%s new_view=%s old_view=%s", - NodeIdToString(change.node_id).c_str(), - NodeIdToString(change.view_id).c_str(), - NodeIdToString(change.view_id2).c_str()); - case CHANGE_TYPE_INPUT_EVENT: return base::StringPrintf( - "InputEvent view=%s event_action=%d", - NodeIdToString(change.view_id).c_str(), + "InputEvent node=%s event_action=%d", + NodeIdToString(change.node_id).c_str(), change.event_action); case CHANGE_TYPE_DELEGATE_EMBED: return base::StringPrintf("DelegateEmbed url=%s", @@ -102,18 +91,17 @@ std::string ChangeNodeDescription(const std::vector& changes) { return JoinString(node_strings, ','); } -TestNode NodeDataToTestNode(const NodeDataPtr& data) { +TestNode ViewDataToTestNode(const ViewDataPtr& data) { TestNode node; node.parent_id = data->parent_id; - node.node_id = data->node_id; - node.view_id = data->view_id; + node.node_id = data->view_id; return node; } -void NodeDatasToTestNodes(const Array& data, +void ViewDatasToTestNodes(const Array& data, std::vector* test_nodes) { for (size_t i = 0; i < data.size(); ++i) - test_nodes->push_back(NodeDataToTestNode(data[i])); + test_nodes->push_back(ViewDataToTestNode(data[i])); } Change::Change() @@ -122,8 +110,6 @@ Change::Change() node_id(0), node_id2(0), node_id3(0), - view_id(0), - view_id2(0), event_action(0), direction(ORDER_DIRECTION_ABOVE) { } @@ -140,12 +126,12 @@ TestChangeTracker::~TestChangeTracker() { void TestChangeTracker::OnEmbed(ConnectionSpecificId connection_id, const String& creator_url, - NodeDataPtr root) { + ViewDataPtr root) { Change change; change.type = CHANGE_TYPE_EMBED; change.connection_id = connection_id; change.creator_url = creator_url; - change.nodes.push_back(NodeDataToTestNode(root)); + change.nodes.push_back(ViewDataToTestNode(root)); AddChange(change); } @@ -163,13 +149,13 @@ void TestChangeTracker::OnNodeBoundsChanged(Id node_id, void TestChangeTracker::OnNodeHierarchyChanged(Id node_id, Id new_parent_id, Id old_parent_id, - Array nodes) { + Array nodes) { Change change; change.type = CHANGE_TYPE_NODE_HIERARCHY_CHANGED; change.node_id = node_id; change.node_id2 = new_parent_id; change.node_id3 = old_parent_id; - NodeDatasToTestNodes(nodes, &change.nodes); + ViewDatasToTestNodes(nodes, &change.nodes); AddChange(change); } @@ -191,28 +177,10 @@ void TestChangeTracker::OnNodeDeleted(Id node_id) { AddChange(change); } -void TestChangeTracker::OnViewDeleted(Id view_id) { - Change change; - change.type = CHANGE_TYPE_VIEW_DELETED; - change.view_id = view_id; - AddChange(change); -} - -void TestChangeTracker::OnNodeViewReplaced(Id node_id, - Id new_view_id, - Id old_view_id) { - Change change; - change.type = CHANGE_TYPE_VIEW_REPLACED; - change.node_id = node_id; - change.view_id = new_view_id; - change.view_id2 = old_view_id; - AddChange(change); -} - -void TestChangeTracker::OnViewInputEvent(Id view_id, EventPtr event) { +void TestChangeTracker::OnNodeInputEvent(Id node_id, EventPtr event) { Change change; change.type = CHANGE_TYPE_INPUT_EVENT; - change.view_id = view_id; + change.node_id = node_id; change.event_action = event->action; AddChange(change); } @@ -231,10 +199,9 @@ void TestChangeTracker::AddChange(const Change& change) { } std::string TestNode::ToString() const { - return base::StringPrintf("node=%s parent=%s view=%s", + return base::StringPrintf("node=%s parent=%s", NodeIdToString(node_id).c_str(), - NodeIdToString(parent_id).c_str(), - NodeIdToString(view_id).c_str()); + NodeIdToString(parent_id).c_str()); } } // namespace service diff --git a/mojo/services/view_manager/test_change_tracker.h b/mojo/services/view_manager/test_change_tracker.h index 3454dcc3279c7..3e4c238874fc0 100644 --- a/mojo/services/view_manager/test_change_tracker.h +++ b/mojo/services/view_manager/test_change_tracker.h @@ -22,20 +22,17 @@ enum ChangeType { CHANGE_TYPE_NODE_HIERARCHY_CHANGED, CHANGE_TYPE_NODE_REORDERED, CHANGE_TYPE_NODE_DELETED, - CHANGE_TYPE_VIEW_DELETED, - CHANGE_TYPE_VIEW_REPLACED, CHANGE_TYPE_INPUT_EVENT, CHANGE_TYPE_DELEGATE_EMBED, }; -// TODO(sky): consider nuking and converting directly to NodeData. +// TODO(sky): consider nuking and converting directly to ViewData. struct TestNode { // Returns a string description of this. std::string ToString() const; Id parent_id; Id node_id; - Id view_id; }; // Tracks a call to ViewManagerClient. See the individual functions for the @@ -50,8 +47,6 @@ struct Change { Id node_id; Id node_id2; Id node_id3; - Id view_id; - Id view_id2; gfx::Rect bounds; gfx::Rect bounds2; int32 event_action; @@ -68,8 +63,8 @@ std::vector ChangesToDescription1( // if change.size() != 1. std::string ChangeNodeDescription(const std::vector& changes); -// Converts NodeDatas to TestNodes. -void NodeDatasToTestNodes(const Array& data, +// Converts ViewDatas to TestNodes. +void ViewDatasToTestNodes(const Array& data, std::vector* test_nodes); // TestChangeTracker is used to record ViewManagerClient functions. It notifies @@ -89,7 +84,9 @@ class TestChangeTracker { TestChangeTracker(); ~TestChangeTracker(); - void set_delegate(Delegate* delegate) { delegate_ = delegate; } + void set_delegate(Delegate* delegate) { + delegate_ = delegate; + } std::vector* changes() { return &changes_; } @@ -97,19 +94,17 @@ class TestChangeTracker { // ViewManagerClient function. void OnEmbed(ConnectionSpecificId connection_id, const String& creator_url, - NodeDataPtr root); + ViewDataPtr root); void OnNodeBoundsChanged(Id node_id, RectPtr old_bounds, RectPtr new_bounds); void OnNodeHierarchyChanged(Id node_id, Id new_parent_id, Id old_parent_id, - Array nodes); + Array nodes); void OnNodeReordered(Id node_id, Id relative_node_id, OrderDirection direction); void OnNodeDeleted(Id node_id); - void OnViewDeleted(Id view_id); - void OnNodeViewReplaced(Id node_id, Id new_view_id, Id old_view_id); - void OnViewInputEvent(Id view_id, EventPtr event); + void OnNodeInputEvent(Id node_id, EventPtr event); void DelegateEmbed(const String& url); private: diff --git a/mojo/services/view_manager/view.cc b/mojo/services/view_manager/view.cc deleted file mode 100644 index 08f81a95c21ea..0000000000000 --- a/mojo/services/view_manager/view.cc +++ /dev/null @@ -1,26 +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/services/view_manager/view.h" - -#include "mojo/services/view_manager/node.h" - -namespace mojo { -namespace service { - -View::View(const ViewId& id) : id_(id), node_(NULL) {} - -View::~View() { -} - -void View::SetBitmap(const SkBitmap& bitmap) { - bitmap_ = bitmap; - if (node_) { - node_->window()->SchedulePaintInRect( - gfx::Rect(node_->window()->bounds().size())); - } -} - -} // namespace service -} // namespace mojo diff --git a/mojo/services/view_manager/view.h b/mojo/services/view_manager/view.h deleted file mode 100644 index 5680a5bdbfe70..0000000000000 --- a/mojo/services/view_manager/view.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SERVICES_VIEW_MANAGER_VIEW_H_ -#define MOJO_SERVICES_VIEW_MANAGER_VIEW_H_ - -#include - -#include "base/logging.h" -#include "mojo/services/view_manager/ids.h" -#include "mojo/services/view_manager/view_manager_export.h" -#include "third_party/skia/include/core/SkBitmap.h" - -namespace mojo { -namespace service { -class Node; - -// Represents a view. A view may be associated with a single Node. -class MOJO_VIEW_MANAGER_EXPORT View { - public: - explicit View(const ViewId& id); - ~View(); - - const ViewId& id() const { return id_; } - - Node* node() { return node_; } - - void SetBitmap(const SkBitmap& contents); - const SkBitmap& bitmap() const { return bitmap_; } - - private: - // Node is responsible for maintaining |node_|. - friend class Node; - - void set_node(Node* node) { node_ = node; } - - const ViewId id_; - Node* node_; - SkBitmap bitmap_; - - DISALLOW_COPY_AND_ASSIGN(View); -}; - -} // namespace service -} // namespace mojo - -#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_H_ diff --git a/mojo/services/view_manager/view_manager_init_service_context.cc b/mojo/services/view_manager/view_manager_init_service_context.cc index 66c8aa9a1e406..a833c1caf7f37 100644 --- a/mojo/services/view_manager/view_manager_init_service_context.cc +++ b/mojo/services/view_manager/view_manager_init_service_context.cc @@ -4,6 +4,7 @@ #include "mojo/services/view_manager/view_manager_init_service_context.h" +#include "base/auto_reset.h" #include "base/bind.h" #include "mojo/services/view_manager/root_node_manager.h" #include "mojo/services/view_manager/view_manager_init_service_impl.h" @@ -11,8 +12,13 @@ namespace mojo { namespace service { +ViewManagerInitServiceContext::ConnectParams::ConnectParams() {} + +ViewManagerInitServiceContext::ConnectParams::~ConnectParams() {} + ViewManagerInitServiceContext::ViewManagerInitServiceContext() - : is_tree_host_ready_(false) {} + : is_tree_host_ready_(false), + deleting_connection_(false) {} ViewManagerInitServiceContext::~ViewManagerInitServiceContext() {} void ViewManagerInitServiceContext::AddConnection( @@ -24,10 +30,12 @@ void ViewManagerInitServiceContext::AddConnection( void ViewManagerInitServiceContext::RemoveConnection( ViewManagerInitServiceImpl* connection) { - Connections::iterator it = - std::find(connections_.begin(), connections_.end(), connection); - DCHECK(it != connections_.end()); - connections_.erase(it); + if (!deleting_connection_) { + Connections::iterator it = + std::find(connections_.begin(), connections_.end(), connection); + DCHECK(it != connections_.end()); + connections_.erase(it); + } // This object is owned by an object that outlives the current thread's // message loop, so we need to destroy the RootNodeManager earlier, as it may @@ -47,20 +55,46 @@ void ViewManagerInitServiceContext::ConfigureIncomingConnection( } } -void ViewManagerInitServiceContext::OnNativeViewportDeleted() { - for (Connections::const_iterator it = connections_.begin(); - it != connections_.end(); ++it) { - (*it)->OnNativeViewportDeleted(); - } +void ViewManagerInitServiceContext::Embed( + const String& url, + ServiceProviderPtr service_provider, + const Callback& callback) { + ConnectParams* params = new ConnectParams; + params->url = url.To(); + params->callback = callback; + params->service_provider.Bind(service_provider.PassMessagePipe()); + connect_params_.push_back(params); + MaybeEmbed(); } void ViewManagerInitServiceContext::OnRootViewManagerWindowTreeHostCreated() { DCHECK(!is_tree_host_ready_); is_tree_host_ready_ = true; + MaybeEmbed(); +} + +void ViewManagerInitServiceContext::OnNativeViewportDeleted() { + // Prevent the connection from modifying the connection list during manual + // teardown. + base::AutoReset deleting_connection(&deleting_connection_, true); for (Connections::const_iterator it = connections_.begin(); it != connections_.end(); ++it) { - (*it)->OnRootViewManagerWindowTreeHostCreated(); + delete *it; + } + connections_.clear(); + root_node_manager_.reset(); +} + +void ViewManagerInitServiceContext::MaybeEmbed() { + if (!is_tree_host_ready_) + return; + + for (ScopedVector::const_iterator it = connect_params_.begin(); + it != connect_params_.end(); ++it) { + root_node_manager_->EmbedRoot((*it)->url, (*it)->service_provider.Pass()); + (*it)->callback.Run(true); } + connect_params_.clear(); } } // namespace service diff --git a/mojo/services/view_manager/view_manager_init_service_context.h b/mojo/services/view_manager/view_manager_init_service_context.h index 3e02069905e2d..6b45ef3f06c95 100644 --- a/mojo/services/view_manager/view_manager_init_service_context.h +++ b/mojo/services/view_manager/view_manager_init_service_context.h @@ -8,6 +8,7 @@ #include #include "base/memory/scoped_ptr.h" +#include "base/memory/scoped_vector.h" #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/services/view_manager/root_view_manager_delegate.h" @@ -31,6 +32,10 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceContext void ConfigureIncomingConnection(ApplicationConnection* connection); + void Embed(const String& url, + ServiceProviderPtr service_provider, + const Callback& callback); + RootNodeManager* root_node_manager() { return root_node_manager_.get(); } bool is_tree_host_ready() const { return is_tree_host_ready_; } @@ -38,16 +43,33 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceContext private: typedef std::vector Connections; + struct ConnectParams { + ConnectParams(); + ~ConnectParams(); + + std::string url; + InterfaceRequest service_provider; + Callback callback; + }; + // RootViewManagerDelegate overrides: virtual void OnRootViewManagerWindowTreeHostCreated() OVERRIDE; void OnNativeViewportDeleted(); + void MaybeEmbed(); + scoped_ptr root_node_manager_; Connections connections_; + // Stores information about inbound calls to Embed() pending execution on + // the window tree host being ready to use. + ScopedVector connect_params_; + bool is_tree_host_ready_; + bool deleting_connection_; + DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceContext); }; diff --git a/mojo/services/view_manager/view_manager_init_service_impl.cc b/mojo/services/view_manager/view_manager_init_service_impl.cc index deb8a3f1d47e3..ab30718973704 100644 --- a/mojo/services/view_manager/view_manager_init_service_impl.cc +++ b/mojo/services/view_manager/view_manager_init_service_impl.cc @@ -10,13 +10,8 @@ #include "mojo/services/view_manager/view_manager_service_impl.h" namespace mojo { -class ApplicationConnection; namespace service { -ViewManagerInitServiceImpl::ConnectParams::ConnectParams() {} - -ViewManagerInitServiceImpl::ConnectParams::~ConnectParams() {} - ViewManagerInitServiceImpl::ViewManagerInitServiceImpl( ApplicationConnection* connection, ViewManagerInitServiceContext* context) @@ -28,37 +23,11 @@ ViewManagerInitServiceImpl::~ViewManagerInitServiceImpl() { context_->RemoveConnection(this); } -void ViewManagerInitServiceImpl::OnNativeViewportDeleted() { - delete this; -} - -void ViewManagerInitServiceImpl::OnRootViewManagerWindowTreeHostCreated() { - MaybeEmbed(); -} - -void ViewManagerInitServiceImpl::MaybeEmbed() { - if (!context_->is_tree_host_ready()) - return; - - ScopedVector::const_iterator it = connect_params_.begin(); - for (; it != connect_params_.end(); ++it) { - context_->root_node_manager()->EmbedRoot((*it)->url, - (*it)->service_provider.Pass()); - (*it)->callback.Run(true); - } - connect_params_.clear(); -} - void ViewManagerInitServiceImpl::Embed( const String& url, ServiceProviderPtr service_provider, const Callback& callback) { - ConnectParams* params = new ConnectParams; - params->url = url.To(); - params->callback = callback; - params->service_provider.Bind(service_provider.PassMessagePipe()); - connect_params_.push_back(params); - MaybeEmbed(); + context_->Embed(url, service_provider.Pass(), callback); } } // namespace service diff --git a/mojo/services/view_manager/view_manager_init_service_impl.h b/mojo/services/view_manager/view_manager_init_service_impl.h index a070373c1d85d..64a206a65e939 100644 --- a/mojo/services/view_manager/view_manager_init_service_impl.h +++ b/mojo/services/view_manager/view_manager_init_service_impl.h @@ -9,7 +9,6 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/memory/scoped_vector.h" #include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" #include "mojo/services/view_manager/root_node_manager.h" #include "mojo/services/view_manager/view_manager_export.h" @@ -17,7 +16,6 @@ namespace mojo { class ApplicationConnection; -class ServiceProvider; namespace service { @@ -39,22 +37,7 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceImpl ViewManagerInitServiceContext* context); virtual ~ViewManagerInitServiceImpl(); - void OnNativeViewportDeleted(); - - void OnRootViewManagerWindowTreeHostCreated(); - private: - struct ConnectParams { - ConnectParams(); - ~ConnectParams(); - - std::string url; - InterfaceRequest service_provider; - Callback callback; - }; - - void MaybeEmbed(); - // ViewManagerInitService overrides: virtual void Embed(const String& url, ServiceProviderPtr service_provider, @@ -62,14 +45,6 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerInitServiceImpl ViewManagerInitServiceContext* context_; - ServiceProvider* service_provider_; - - // Stores information about inbound calls to Embed() pending execution on - // the window tree host being ready to use. - ScopedVector connect_params_; - - bool is_tree_host_ready_; - DISALLOW_COPY_AND_ASSIGN(ViewManagerInitServiceImpl); }; diff --git a/mojo/services/view_manager/view_manager_service_impl.cc b/mojo/services/view_manager/view_manager_service_impl.cc index 9291761db3c58..2311b74e7f7e7 100644 --- a/mojo/services/view_manager/view_manager_service_impl.cc +++ b/mojo/services/view_manager/view_manager_service_impl.cc @@ -10,7 +10,6 @@ #include "mojo/services/view_manager/default_access_policy.h" #include "mojo/services/view_manager/node.h" #include "mojo/services/view_manager/root_node_manager.h" -#include "mojo/services/view_manager/view.h" #include "mojo/services/view_manager/window_manager_access_policy.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/aura/window.h" @@ -42,13 +41,7 @@ ViewManagerServiceImpl::ViewManagerServiceImpl( } ViewManagerServiceImpl::~ViewManagerServiceImpl() { - // Delete any views we created. - while (!view_map_.empty()) { - bool result = DeleteViewImpl(this, view_map_.begin()->second); - DCHECK(result); - } - - // Ditto the nodes. + // Delete any nodes we created. if (!node_map_.empty()) { RootNodeManager::ScopedChange change(this, root_node_manager_, true); while (!node_map_.empty()) @@ -66,14 +59,6 @@ const Node* ViewManagerServiceImpl::GetNode(const NodeId& id) const { return root_node_manager_->GetNode(id); } -const View* ViewManagerServiceImpl::GetView(const ViewId& id) const { - if (id_ == id.connection_id) { - ViewMap::const_iterator i = view_map_.find(id.view_id); - return i == view_map_.end() ? NULL : i->second; - } - return root_node_manager_->GetView(id); -} - bool ViewManagerServiceImpl::HasRoot(const NodeId& id) const { return roots_.find(NodeIdToTransportId(id)) != roots_.end(); } @@ -91,7 +76,7 @@ void ViewManagerServiceImpl::ProcessNodeBoundsChanged( bool originated_change) { if (originated_change || !IsNodeKnown(node)) return; - client()->OnNodeBoundsChanged(NodeIdToTransportId(node->id()), + client()->OnViewBoundsChanged(NodeIdToTransportId(node->id()), Rect::From(old_bounds), Rect::From(new_bounds)); } @@ -122,10 +107,10 @@ void ViewManagerServiceImpl::ProcessNodeHierarchyChanged( GetUnknownNodesFrom(node, &to_send); const NodeId new_parent_id(new_parent ? new_parent->id() : NodeId()); const NodeId old_parent_id(old_parent ? old_parent->id() : NodeId()); - client()->OnNodeHierarchyChanged(NodeIdToTransportId(node->id()), + client()->OnViewHierarchyChanged(NodeIdToTransportId(node->id()), NodeIdToTransportId(new_parent_id), NodeIdToTransportId(old_parent_id), - NodesToNodeDatas(to_send)); + NodesToViewDatas(to_send)); root_node_manager_->OnConnectionMessagedClient(id_); } @@ -136,28 +121,11 @@ void ViewManagerServiceImpl::ProcessNodeReorder(const Node* node, if (originated_change || !IsNodeKnown(node) || !IsNodeKnown(relative_node)) return; - client()->OnNodeReordered(NodeIdToTransportId(node->id()), + client()->OnViewReordered(NodeIdToTransportId(node->id()), NodeIdToTransportId(relative_node->id()), direction); } -void ViewManagerServiceImpl::ProcessNodeViewReplaced( - const Node* node, - const View* new_view, - const View* old_view, - bool originated_change) { - if (originated_change || !IsNodeKnown(node) || - root_node_manager_->is_processing_delete_node()) { - return; - } - const Id new_view_id = new_view ? - access_policy_->GetViewIdToSend(node, new_view) : 0; - const Id old_view_id = old_view ? - access_policy_->GetViewIdToSend(node, old_view) : 0; - client()->OnNodeViewReplaced(NodeIdToTransportId(node->id()), - new_view_id, old_view_id); -} - void ViewManagerServiceImpl::ProcessNodeDeleted(const NodeId& node, bool originated_change) { node_map_.erase(node.node_id); @@ -169,35 +137,11 @@ void ViewManagerServiceImpl::ProcessNodeDeleted(const NodeId& node, return; if (in_known) { - client()->OnNodeDeleted(NodeIdToTransportId(node)); + client()->OnViewDeleted(NodeIdToTransportId(node)); root_node_manager_->OnConnectionMessagedClient(id_); } } -void ViewManagerServiceImpl::ProcessViewDeleted(const ViewId& view, - bool originated_change) { - if (!originated_change && access_policy_->ShouldSendViewDeleted(view)) - client()->OnViewDeleted(ViewIdToTransportId(view)); -} - -void ViewManagerServiceImpl::ProcessFocusChanged(const Node* focused_node, - const Node* blurred_node, - bool originated_change) { - if (originated_change) - return; - - // TODO(sky): this should not notify all clients. - Id focused_id = 0; - Id blurred_id = 0; - if (focused_node && IsNodeKnown(focused_node)) - focused_id = NodeIdToTransportId(focused_node->id()); - if (blurred_node && IsNodeKnown(blurred_node)) - blurred_id = NodeIdToTransportId(blurred_node->id()); - - if (focused_id != 0 || blurred_id != 0) - client()->OnFocusChanged(focused_id, blurred_id); -} - void ViewManagerServiceImpl::OnConnectionError() { if (delete_on_connection_error_) delete this; @@ -243,27 +187,6 @@ bool ViewManagerServiceImpl::DeleteNodeImpl(ViewManagerServiceImpl* source, return true; } -bool ViewManagerServiceImpl::DeleteViewImpl(ViewManagerServiceImpl* source, - View* view) { - DCHECK(view); - DCHECK_EQ(view->id().connection_id, id_); - RootNodeManager::ScopedChange change(source, root_node_manager_, false); - if (view->node()) - view->node()->SetView(NULL); - view_map_.erase(view->id().view_id); - const ViewId view_id(view->id()); - delete view; - root_node_manager_->ProcessViewDeleted(view_id); - return true; -} - -bool ViewManagerServiceImpl::SetViewImpl(Node* node, View* view) { - DCHECK(node); // CanSetView() should have verified node exists. - RootNodeManager::ScopedChange change(this, root_node_manager_, false); - node->SetView(view); - return true; -} - void ViewManagerServiceImpl::GetUnknownNodesFrom( const Node* node, std::vector* nodes) { @@ -310,7 +233,7 @@ void ViewManagerServiceImpl::AddRoot( to_send.push_back(node); } - client()->OnEmbed(id_, creator_url_, NodeToNodeData(to_send.front()), + client()->OnEmbed(id_, creator_url_, NodeToViewData(to_send.front()), service_provider.Pass()); root_node_manager_->OnConnectionMessagedClient(id_); } @@ -325,7 +248,7 @@ void ViewManagerServiceImpl::RemoveRoot(const NodeId& node_id) { if (node_id.connection_id == id_) return; - client()->OnNodeDeleted(transport_node_id); + client()->OnViewDeleted(transport_node_id); root_node_manager_->OnConnectionMessagedClient(id_); // This connection no longer knows about the node. Unparent any nodes that @@ -346,29 +269,26 @@ void ViewManagerServiceImpl::RemoveChildrenAsPartOfEmbed( node->Remove(children[i]); } -Array ViewManagerServiceImpl::NodesToNodeDatas( +Array ViewManagerServiceImpl::NodesToViewDatas( const std::vector& nodes) { - Array array(nodes.size()); + Array array(nodes.size()); for (size_t i = 0; i < nodes.size(); ++i) - array[i] = NodeToNodeData(nodes[i]).Pass(); + array[i] = NodeToViewData(nodes[i]).Pass(); return array.Pass(); } -NodeDataPtr ViewManagerServiceImpl::NodeToNodeData(const Node* node) { +ViewDataPtr ViewManagerServiceImpl::NodeToViewData(const Node* node) { DCHECK(IsNodeKnown(node)); const Node* parent = node->GetParent(); // If the parent isn't known, it means the parent is not visible to us (not // in roots), and should not be sent over. if (parent && !IsNodeKnown(parent)) parent = NULL; - NodeDataPtr node_data(NodeData::New()); - node_data->parent_id = NodeIdToTransportId(parent ? parent->id() : NodeId()); - node_data->node_id = NodeIdToTransportId(node->id()); - // TODO(sky): should the id only be sent if known? - node_data->view_id = - ViewIdToTransportId(node->view() ? node->view()->id() : ViewId()); - node_data->bounds = Rect::From(node->bounds()); - return node_data.Pass(); + ViewDataPtr view_data(ViewData::New()); + view_data->parent_id = NodeIdToTransportId(parent ? parent->id() : NodeId()); + view_data->view_id = NodeIdToTransportId(node->id()); + view_data->bounds = Rect::From(node->bounds()); + return view_data.Pass(); } void ViewManagerServiceImpl::GetNodeTreeImpl( @@ -389,10 +309,10 @@ void ViewManagerServiceImpl::GetNodeTreeImpl( GetNodeTreeImpl(children[i], nodes); } -void ViewManagerServiceImpl::CreateNode( - Id transport_node_id, +void ViewManagerServiceImpl::CreateView( + Id transport_view_id, const Callback& callback) { - const NodeId node_id(NodeIdFromTransportId(transport_node_id)); + const NodeId node_id(NodeIdFromTransportId(transport_view_id)); ErrorCode error_code = ERROR_CODE_NONE; if (node_id.connection_id != id_) { error_code = ERROR_CODE_ILLEGAL_ARGUMENT; @@ -400,15 +320,15 @@ void ViewManagerServiceImpl::CreateNode( error_code = ERROR_CODE_VALUE_IN_USE; } else { node_map_[node_id.node_id] = new Node(root_node_manager_, node_id); - known_nodes_.insert(transport_node_id); + known_nodes_.insert(transport_view_id); } callback.Run(error_code); } -void ViewManagerServiceImpl::DeleteNode( - Id transport_node_id, +void ViewManagerServiceImpl::DeleteView( + Id transport_view_id, const Callback& callback) { - Node* node = GetNode(NodeIdFromTransportId(transport_node_id)); + Node* node = GetNode(NodeIdFromTransportId(transport_view_id)); bool success = false; if (node && access_policy_->CanDeleteNode(node)) { ViewManagerServiceImpl* connection = root_node_manager_->GetConnection( @@ -418,7 +338,7 @@ void ViewManagerServiceImpl::DeleteNode( callback.Run(success); } -void ViewManagerServiceImpl::AddNode( +void ViewManagerServiceImpl::AddView( Id parent_id, Id child_id, const Callback& callback) { @@ -434,11 +354,11 @@ void ViewManagerServiceImpl::AddNode( callback.Run(success); } -void ViewManagerServiceImpl::RemoveNodeFromParent( - Id node_id, +void ViewManagerServiceImpl::RemoveViewFromParent( + Id view_id, const Callback& callback) { bool success = false; - Node* node = GetNode(NodeIdFromTransportId(node_id)); + Node* node = GetNode(NodeIdFromTransportId(view_id)); if (node && node->GetParent() && access_policy_->CanRemoveNodeFromParent(node)) { success = true; @@ -448,13 +368,13 @@ void ViewManagerServiceImpl::RemoveNodeFromParent( callback.Run(success); } -void ViewManagerServiceImpl::ReorderNode(Id node_id, - Id relative_node_id, +void ViewManagerServiceImpl::ReorderView(Id view_id, + Id relative_view_id, OrderDirection direction, const Callback& callback) { bool success = false; - Node* node = GetNode(NodeIdFromTransportId(node_id)); - Node* relative_node = GetNode(NodeIdFromTransportId(relative_node_id)); + Node* node = GetNode(NodeIdFromTransportId(view_id)); + Node* relative_node = GetNode(NodeIdFromTransportId(relative_view_id)); if (CanReorderNode(node, relative_node, direction)) { success = true; RootNodeManager::ScopedChange change(this, root_node_manager_, false); @@ -464,51 +384,16 @@ void ViewManagerServiceImpl::ReorderNode(Id node_id, callback.Run(success); } -void ViewManagerServiceImpl::GetNodeTree( - Id node_id, - const Callback)>& callback) { - Node* node = GetNode(NodeIdFromTransportId(node_id)); +void ViewManagerServiceImpl::GetViewTree( + Id view_id, + const Callback)>& callback) { + Node* node = GetNode(NodeIdFromTransportId(view_id)); std::vector nodes; if (node) { GetNodeTreeImpl(node, &nodes); // TODO(sky): this should map in nodes that weren't none. } - callback.Run(NodesToNodeDatas(nodes)); -} - -void ViewManagerServiceImpl::CreateView( - Id transport_view_id, - const Callback& callback) { - const ViewId view_id(ViewIdFromTransportId(transport_view_id)); - if (view_id.connection_id != id_ || view_map_.count(view_id.view_id)) { - callback.Run(false); - return; - } - view_map_[view_id.view_id] = new View(view_id); - callback.Run(true); -} - -void ViewManagerServiceImpl::DeleteView(Id transport_view_id, - const Callback& callback) { - View* view = GetView(ViewIdFromTransportId(transport_view_id)); - bool did_delete = false; - if (view && access_policy_->CanDeleteView(view)) { - ViewManagerServiceImpl* connection = root_node_manager_->GetConnection( - view->id().connection_id); - did_delete = (connection && connection->DeleteViewImpl(this, view)); - } - callback.Run(did_delete); -} - -void ViewManagerServiceImpl::SetView(Id transport_node_id, - Id transport_view_id, - const Callback& callback) { - Node* node = GetNode(NodeIdFromTransportId(transport_node_id)); - View* view = GetView(ViewIdFromTransportId(transport_view_id)); - const bool valid_view = view || - ViewIdFromTransportId(transport_node_id) != ViewId(); - callback.Run(valid_view && node && access_policy_->CanSetView(node, view) && - SetViewImpl(node, view)); + callback.Run(NodesToViewDatas(nodes)); } void ViewManagerServiceImpl::SetViewContents( @@ -516,9 +401,9 @@ void ViewManagerServiceImpl::SetViewContents( ScopedSharedBufferHandle buffer, uint32_t buffer_size, const Callback& callback) { - // TODO(sky): add coverage of not being able to set for random view. - View* view = GetView(ViewIdFromTransportId(view_id)); - if (!view || !access_policy_->CanSetViewContents(view)) { + // TODO(sky): add coverage of not being able to set for random node. + Node* node = GetNode(NodeIdFromTransportId(view_id)); + if (!node || !access_policy_->CanSetNodeContents(node)) { callback.Run(false); return; } @@ -531,27 +416,16 @@ void ViewManagerServiceImpl::SetViewContents( SkBitmap bitmap; gfx::PNGCodec::Decode(static_cast(handle_data), buffer_size, &bitmap); - view->SetBitmap(bitmap); + node->SetBitmap(bitmap); UnmapBuffer(handle_data); callback.Run(true); } -void ViewManagerServiceImpl::SetFocus(Id node_id, - const Callback & callback) { - bool success = false; - Node* node = GetNode(NodeIdFromTransportId(node_id)); - if (node && access_policy_->CanSetFocus(node)) { - success = true; - node->window()->Focus(); - } - callback.Run(success); -} - -void ViewManagerServiceImpl::SetNodeBounds( - Id node_id, +void ViewManagerServiceImpl::SetViewBounds( + Id view_id, RectPtr bounds, const Callback& callback) { - Node* node = GetNode(NodeIdFromTransportId(node_id)); + Node* node = GetNode(NodeIdFromTransportId(view_id)); const bool success = node && access_policy_->CanSetNodeBounds(node); if (success) { RootNodeManager::ScopedChange change(this, root_node_manager_, false); @@ -561,11 +435,11 @@ void ViewManagerServiceImpl::SetNodeBounds( callback.Run(success); } -void ViewManagerServiceImpl::SetNodeVisibility( - Id transport_node_id, +void ViewManagerServiceImpl::SetViewVisibility( + Id transport_view_id, bool visible, const Callback& callback) { - Node* node = GetNode(NodeIdFromTransportId(transport_node_id)); + Node* node = GetNode(NodeIdFromTransportId(transport_view_id)); const bool success = node && node->IsVisible() != visible && access_policy_->CanChangeNodeVisibility(node); if (success) { @@ -578,22 +452,22 @@ void ViewManagerServiceImpl::SetNodeVisibility( void ViewManagerServiceImpl::Embed( const String& url, - Id transport_node_id, + Id transport_view_id, ServiceProviderPtr service_provider, const Callback& callback) { InterfaceRequest spir; spir.Bind(service_provider.PassMessagePipe()); - if (NodeIdFromTransportId(transport_node_id) == InvalidNodeId()) { + if (NodeIdFromTransportId(transport_view_id) == InvalidNodeId()) { root_node_manager_->EmbedRoot(url, spir.Pass()); callback.Run(true); return; } - const Node* node = GetNode(NodeIdFromTransportId(transport_node_id)); + const Node* node = GetNode(NodeIdFromTransportId(transport_view_id)); bool success = node && access_policy_->CanEmbed(node); if (success) { // Only allow a node to be the root for one connection. - const NodeId node_id(NodeIdFromTransportId(transport_node_id)); + const NodeId node_id(NodeIdFromTransportId(transport_view_id)); ViewManagerServiceImpl* connection_by_url = root_node_manager_->GetConnectionByCreator(id_, url.To()); ViewManagerServiceImpl* connection_with_node_as_root = @@ -610,8 +484,7 @@ void ViewManagerServiceImpl::Embed( if (connection_by_url) { connection_by_url->AddRoot(node_id, spir.Pass()); } else { - root_node_manager_->Embed(id_, url, transport_node_id, - spir.Pass()); + root_node_manager_->Embed(id_, url, transport_view_id, spir.Pass()); } } else { success = false; @@ -627,9 +500,14 @@ void ViewManagerServiceImpl::DispatchOnViewInputEvent(Id transport_view_id, if (id_ != kWindowManagerConnection) return; - const ViewId view_id(ViewIdFromTransportId(transport_view_id)); - ViewManagerServiceImpl* connection = root_node_manager_->GetConnection( - view_id.connection_id); + const NodeId node_id(NodeIdFromTransportId(transport_view_id)); + + // If another app is embedded at this node, we forward the input event to the + // embedded app, rather than the app that created the node. + ViewManagerServiceImpl* connection = + root_node_manager_->GetConnectionWithRoot(node_id); + if (!connection) + connection = root_node_manager_->GetConnection(node_id.connection_id); if (connection) { connection->client()->OnViewInputEvent( transport_view_id, @@ -645,7 +523,7 @@ void ViewManagerServiceImpl::OnConnectionEstablished() { for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i) GetUnknownNodesFrom(GetNode(NodeIdFromTransportId(*i)), &to_send); - client()->OnEmbed(id_, creator_url_, NodeToNodeData(to_send.front()), + client()->OnEmbed(id_, creator_url_, NodeToViewData(to_send.front()), service_provider_.Pass()); } diff --git a/mojo/services/view_manager/view_manager_service_impl.h b/mojo/services/view_manager/view_manager_service_impl.h index 4d130ac983165..9d7c1a1a51761 100644 --- a/mojo/services/view_manager/view_manager_service_impl.h +++ b/mojo/services/view_manager/view_manager_service_impl.h @@ -28,7 +28,6 @@ namespace service { class AccessPolicy; class Node; class RootNodeManager; -class View; #if defined(OS_WIN) // Equivalent of NON_EXPORTED_BASE which does not work with the template snafu @@ -65,13 +64,6 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl } const Node* GetNode(const NodeId& id) const; - // Returns the View with the specified id. - View* GetView(const ViewId& id) { - return const_cast( - const_cast(this)->GetView(id)); - } - const View* GetView(const ViewId& id) const; - // Returns true if this has |id| as a root. bool HasRoot(const NodeId& id) const; @@ -93,15 +85,7 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl const Node* relative_node, OrderDirection direction, bool originated_change); - void ProcessNodeViewReplaced(const Node* node, - const View* new_view, - const View* old_view, - bool originated_change); void ProcessNodeDeleted(const NodeId& node, bool originated_change); - void ProcessViewDeleted(const ViewId& view, bool originated_change); - void ProcessFocusChanged(const Node* focused_node, - const Node* blurred_node, - bool originated_change); // TODO(sky): move this to private section (currently can't because of // bindings). @@ -110,7 +94,6 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl private: typedef std::map NodeMap; - typedef std::map ViewMap; typedef base::hash_set NodeIdSet; bool IsNodeKnown(const Node* node) const; @@ -125,13 +108,6 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl // is the connection that originated the change. bool DeleteNodeImpl(ViewManagerServiceImpl* source, Node* node); - // Deletes a view owned by this connection. Returns true on success. |source| - // is the connection that originated the change. - bool DeleteViewImpl(ViewManagerServiceImpl* source, View* view); - - // Sets the view associated with a node. - bool SetViewImpl(Node* node, View* view); - // If |node| is known (in |known_nodes_|) does nothing. Otherwise adds |node| // to |nodes|, marks |node| as known and recurses. void GetUnknownNodesFrom(const Node* node, std::vector* nodes); @@ -151,59 +127,49 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl void RemoveChildrenAsPartOfEmbed(const NodeId& node_id); - // Converts Node(s) to NodeData(s) for transport. This assumes all the nodes + // Converts Node(s) to ViewData(s) for transport. This assumes all the nodes // are valid for the client. The parent of nodes the client is not allowed to - // see are set to NULL (in the returned NodeData(s)). - Array NodesToNodeDatas(const std::vector& nodes); - NodeDataPtr NodeToNodeData(const Node* node); + // see are set to NULL (in the returned ViewData(s)). + Array NodesToViewDatas(const std::vector& nodes); + ViewDataPtr NodeToViewData(const Node* node); // Implementation of GetNodeTree(). Adds |node| to |nodes| and recurses if // CanDescendIntoNodeForNodeTree() returns true. void GetNodeTreeImpl(const Node* node, std::vector* nodes) const; // ViewManagerService: - virtual void CreateNode(Id transport_node_id, + virtual void CreateView(Id transport_view_id, const Callback& callback) OVERRIDE; - virtual void DeleteNode(Id transport_node_id, + virtual void DeleteView(Id transport_view_id, const Callback& callback) OVERRIDE; - virtual void AddNode(Id parent_id, + virtual void AddView(Id parent_id, Id child_id, const Callback& callback) OVERRIDE; - virtual void RemoveNodeFromParent( + virtual void RemoveViewFromParent( Id node_id, const Callback& callback) OVERRIDE; - virtual void ReorderNode(Id node_id, - Id relative_node_id, + virtual void ReorderView(Id view_id, + Id relative_view_id, OrderDirection direction, const Callback& callback) OVERRIDE; - virtual void GetNodeTree( - Id node_id, - const Callback)>& callback) OVERRIDE; - virtual void CreateView(Id transport_view_id, - const Callback& callback) OVERRIDE; - virtual void DeleteView(Id transport_view_id, - const Callback& callback) OVERRIDE; - virtual void SetView(Id transport_node_id, - Id transport_view_id, - const Callback& callback) OVERRIDE; + virtual void GetViewTree( + Id view_id, + const Callback)>& callback) OVERRIDE; virtual void SetViewContents(Id view_id, ScopedSharedBufferHandle buffer, uint32_t buffer_size, const Callback& callback) OVERRIDE; - virtual void SetFocus(Id node_id, - const Callback & callback) OVERRIDE; - virtual void SetNodeBounds(Id node_id, + virtual void SetViewBounds(Id view_id, RectPtr bounds, const Callback& callback) OVERRIDE; - virtual void SetNodeVisibility(Id transport_node_id, + virtual void SetViewVisibility(Id view_id, bool visible, const Callback& callback) OVERRIDE; virtual void Embed(const String& url, - Id transport_node_id, + Id view_id, ServiceProviderPtr service_provider, const Callback& callback) OVERRIDE; - virtual void DispatchOnViewInputEvent(Id transport_view_id, - EventPtr event) OVERRIDE; + virtual void DispatchOnViewInputEvent(Id view_id, EventPtr event) OVERRIDE; // InterfaceImpl: virtual void OnConnectionEstablished() MOJO_OVERRIDE; @@ -234,7 +200,6 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl // The nodes and views created by this connection. This connection owns these // objects. NodeMap node_map_; - ViewMap view_map_; // The set of nodes that has been communicated to the client. NodeIdSet known_nodes_; diff --git a/mojo/services/view_manager/view_manager_unittest.cc b/mojo/services/view_manager/view_manager_unittest.cc index 3d24a1832377b..5ea3b71899fcb 100644 --- a/mojo/services/view_manager/view_manager_unittest.cc +++ b/mojo/services/view_manager/view_manager_unittest.cc @@ -13,6 +13,7 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/common/common_type_converters.h" #include "mojo/public/cpp/application/application_connection.h" #include "mojo/public/cpp/application/application_delegate.h" @@ -20,7 +21,6 @@ #include "mojo/public/cpp/application/connect.h" #include "mojo/public/cpp/bindings/lib/router.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/service_manager/service_manager.h" #include "mojo/services/public/cpp/geometry/geometry_type_converters.h" #include "mojo/services/public/cpp/view_manager/types.h" #include "mojo/services/public/cpp/view_manager/util.h" @@ -50,13 +50,15 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { public: explicit ViewManagerProxy(TestChangeTracker* tracker) : tracker_(tracker), + main_loop_(NULL), view_manager_(NULL), quit_count_(0), router_(NULL) { SetInstance(this); } - virtual ~ViewManagerProxy() {} + virtual ~ViewManagerProxy() { + } // Runs a message loop until the single instance has been created. static ViewManagerProxy* WaitForInstance() { @@ -106,7 +108,7 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { bool CreateNode(Id node_id) { changes_.clear(); ErrorCode result = ERROR_CODE_NONE; - view_manager_->CreateNode( + view_manager_->CreateView( node_id, base::Bind(&ViewManagerProxy::GotResultWithErrorCode, base::Unretained(this), &result)); @@ -116,7 +118,7 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { ErrorCode CreateNodeWithErrorCode(Id node_id) { changes_.clear(); ErrorCode result = ERROR_CODE_NONE; - view_manager_->CreateNode( + view_manager_->CreateView( node_id, base::Bind(&ViewManagerProxy::GotResultWithErrorCode, base::Unretained(this), &result)); @@ -126,7 +128,7 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { bool AddNode(Id parent, Id child) { changes_.clear(); bool result = false; - view_manager_->AddNode(parent, child, + view_manager_->AddView(parent, child, base::Bind(&ViewManagerProxy::GotResult, base::Unretained(this), &result)); RunMainLoop(); @@ -135,7 +137,7 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { bool RemoveNodeFromParent(Id node_id) { changes_.clear(); bool result = false; - view_manager_->RemoveNodeFromParent(node_id, + view_manager_->RemoveViewFromParent(node_id, base::Bind(&ViewManagerProxy::GotResult, base::Unretained(this), &result)); RunMainLoop(); @@ -146,33 +148,15 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { OrderDirection direction) { changes_.clear(); bool result = false; - view_manager_->ReorderNode(node_id, relative_node_id, direction, + view_manager_->ReorderView(node_id, relative_node_id, direction, base::Bind(&ViewManagerProxy::GotResult, base::Unretained(this), &result)); RunMainLoop(); return result; } - bool SetView(Id node_id, Id view_id) { - changes_.clear(); - bool result = false; - view_manager_->SetView(node_id, view_id, - base::Bind(&ViewManagerProxy::GotResult, - base::Unretained(this), &result)); - RunMainLoop(); - return result; - } - bool CreateView(Id view_id) { - changes_.clear(); - bool result = false; - view_manager_->CreateView(view_id, - base::Bind(&ViewManagerProxy::GotResult, - base::Unretained(this), &result)); - RunMainLoop(); - return result; - } void GetNodeTree(Id node_id, std::vector* nodes) { changes_.clear(); - view_manager_->GetNodeTree(node_id, + view_manager_->GetViewTree(node_id, base::Bind(&ViewManagerProxy::GotNodeTree, base::Unretained(this), nodes)); RunMainLoop(); @@ -191,16 +175,7 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { bool DeleteNode(Id node_id) { changes_.clear(); bool result = false; - view_manager_->DeleteNode(node_id, - base::Bind(&ViewManagerProxy::GotResult, - base::Unretained(this), &result)); - RunMainLoop(); - return result; - } - bool DeleteView(Id view_id) { - changes_.clear(); - bool result = false; - view_manager_->DeleteView(view_id, + view_manager_->DeleteView(node_id, base::Bind(&ViewManagerProxy::GotResult, base::Unretained(this), &result)); RunMainLoop(); @@ -209,7 +184,7 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { bool SetNodeBounds(Id node_id, const gfx::Rect& bounds) { changes_.clear(); bool result = false; - view_manager_->SetNodeBounds(node_id, Rect::From(bounds), + view_manager_->SetViewBounds(node_id, Rect::From(bounds), base::Bind(&ViewManagerProxy::GotResult, base::Unretained(this), &result)); RunMainLoop(); @@ -263,8 +238,8 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { main_run_loop_->Quit(); } - void GotNodeTree(std::vector* nodes, Array results) { - NodeDatasToTestNodes(results, nodes); + void GotNodeTree(std::vector* nodes, Array results) { + ViewDatasToTestNodes(results, nodes); DCHECK(main_run_loop_); main_run_loop_->Quit(); } @@ -322,51 +297,40 @@ class TestViewManagerClientConnection virtual void OnEmbed( ConnectionSpecificId connection_id, const String& creator_url, - NodeDataPtr root, + ViewDataPtr root, InterfaceRequest services) OVERRIDE { tracker_.OnEmbed(connection_id, creator_url, root.Pass()); } - virtual void OnNodeBoundsChanged(Id node_id, + virtual void OnViewBoundsChanged(Id node_id, RectPtr old_bounds, RectPtr new_bounds) OVERRIDE { tracker_.OnNodeBoundsChanged(node_id, old_bounds.Pass(), new_bounds.Pass()); } - virtual void OnNodeHierarchyChanged(Id node, + virtual void OnViewHierarchyChanged(Id node, Id new_parent, Id old_parent, - Array nodes) OVERRIDE { + Array nodes) OVERRIDE { tracker_.OnNodeHierarchyChanged(node, new_parent, old_parent, nodes.Pass()); } - virtual void OnNodeReordered(Id node_id, + virtual void OnViewReordered(Id node_id, Id relative_node_id, OrderDirection direction) OVERRIDE { tracker_.OnNodeReordered(node_id, relative_node_id, direction); } - virtual void OnNodeDeleted(Id node) OVERRIDE { + virtual void OnViewDeleted(Id node) OVERRIDE { tracker_.OnNodeDeleted(node); } - virtual void OnViewDeleted(Id view) OVERRIDE { - tracker_.OnViewDeleted(view); - } - virtual void OnNodeViewReplaced(Id node, - Id new_view_id, - Id old_view_id) OVERRIDE { - tracker_.OnNodeViewReplaced(node, new_view_id, old_view_id); - } - virtual void OnViewInputEvent(Id view_id, + virtual void OnViewInputEvent(Id node_id, EventPtr event, const Callback& callback) OVERRIDE { - tracker_.OnViewInputEvent(view_id, event.Pass()); + tracker_.OnNodeInputEvent(node_id, event.Pass()); } - virtual void OnFocusChanged(Id gained_focus_id, - Id lost_focus_id) OVERRIDE {} virtual void Embed( const String& url, InterfaceRequest service_provider) OVERRIDE { tracker_.DelegateEmbed(url); } - virtual void DispatchOnViewInputEvent(Id view_id, - mojo::EventPtr event) OVERRIDE { + virtual void DispatchOnViewInputEvent(mojo::EventPtr event) OVERRIDE { } private: @@ -378,24 +342,26 @@ class TestViewManagerClientConnection // Used with ViewManagerService::Embed(). Creates a // TestViewManagerClientConnection, which creates and owns the ViewManagerProxy. -class EmbedServiceLoader : public ServiceLoader, - ApplicationDelegate, - public InterfaceFactory { +class EmbedApplicationLoader : public ApplicationLoader, + ApplicationDelegate, + public InterfaceFactory { public: - EmbedServiceLoader() {} - virtual ~EmbedServiceLoader() {} - - // ServiceLoader implementation: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE { + EmbedApplicationLoader() {} + virtual ~EmbedApplicationLoader() {} + + // ApplicationLoader implementation: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; scoped_ptr app(new ApplicationImpl(this, shell_handle.Pass())); apps_.push_back(app.release()); } - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE { - } + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE {} // ApplicationDelegate implementation: virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) @@ -413,7 +379,7 @@ class EmbedServiceLoader : public ServiceLoader, private: ScopedVector apps_; - DISALLOW_COPY_AND_ASSIGN(EmbedServiceLoader); + DISALLOW_COPY_AND_ASSIGN(EmbedApplicationLoader); }; // Creates an id used for transport from the specified parameters. @@ -422,12 +388,6 @@ Id BuildNodeId(ConnectionSpecificId connection_id, return (connection_id << 16) | node_id; } -// Creates an id used for transport from the specified parameters. -Id BuildViewId(ConnectionSpecificId connection_id, - ConnectionSpecificId view_id) { - return (connection_id << 16) | view_id; -} - // Callback from Embed(). |result| is the result of the // Embed() call and |run_loop| the nested RunLoop. void EmbedCallback(bool* result_cache, base::RunLoop* run_loop, bool result) { @@ -466,16 +426,15 @@ class ViewManagerTest : public testing::Test { test_helper_.Init(); test_helper_.SetLoaderForURL( - scoped_ptr(new EmbedServiceLoader()), + scoped_ptr(new EmbedApplicationLoader()), GURL(kTestServiceURL)); test_helper_.SetLoaderForURL( - scoped_ptr(new EmbedServiceLoader()), + scoped_ptr(new EmbedApplicationLoader()), GURL(kTestServiceURL2)); - test_helper_.service_manager()->ConnectToService( - GURL("mojo:mojo_view_manager"), - &view_manager_init_); + test_helper_.application_manager()->ConnectToService( + GURL("mojo:mojo_view_manager"), &view_manager_init_); ASSERT_TRUE(InitEmbed(view_manager_init_.get(), kTestServiceURL, 1)); connection_ = ViewManagerProxy::WaitForInstance(); @@ -512,7 +471,7 @@ class ViewManagerTest : public testing::Test { EXPECT_EQ("OnEmbed creator=mojo:test_url", ChangesToDescription1(changes)[0]); if (create_initial_node) { - EXPECT_EQ("[node=1,1 parent=null view=null]", + EXPECT_EQ("[node=1,1 parent=null]", ChangeNodeDescription(changes)); } } @@ -566,7 +525,13 @@ TEST_F(ViewManagerTest, MultipleEmbedRootsBeforeWTHReady) { } // Verifies client gets a valid id. -TEST_F(ViewManagerTest, ValidId) { +#if defined(OS_LINUX) +// http://crbug.com/396492 +#define MAYBE_ValidId DISABLED_ValidId +#else +#define MAYBE_ValidId ValidId +#endif +TEST_F(ViewManagerTest, MAYBE_ValidId) { // TODO(beng): this should really have the URL of the application that // connected to ViewManagerInit. EXPECT_EQ("OnEmbed creator=", @@ -597,7 +562,7 @@ TEST_F(ViewManagerTest, NodesRemovedWhenEmbedding) { ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2))); ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); - EXPECT_EQ("[node=1,1 parent=null view=null]", + EXPECT_EQ("[node=1,1 parent=null]", ChangeNodeDescription(connection2_->changes())); // Embed() removed node 2. @@ -605,7 +570,7 @@ TEST_F(ViewManagerTest, NodesRemovedWhenEmbedding) { std::vector nodes; connection_->GetNodeTree(BuildNodeId(1, 2), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=1,2 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,2 parent=null", nodes[0].ToString()); } // |connection2_| should not see node 2. @@ -613,7 +578,7 @@ TEST_F(ViewManagerTest, NodesRemovedWhenEmbedding) { std::vector nodes; connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=null", nodes[0].ToString()); } { std::vector nodes; @@ -635,12 +600,12 @@ TEST_F(ViewManagerTest, NodesRemovedWhenEmbedding) { std::vector nodes; connection2_->GetNodeTree(BuildNodeId(2, 3), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=2,3 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=2,3 parent=null", nodes[0].ToString()); nodes.clear(); connection2_->GetNodeTree(BuildNodeId(2, 4), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=2,4 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=2,4 parent=null", nodes[0].ToString()); } // And node 4 should not be visible to connection 3. @@ -648,7 +613,7 @@ TEST_F(ViewManagerTest, NodesRemovedWhenEmbedding) { std::vector nodes; connection3_->GetNodeTree(BuildNodeId(2, 3), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=2,3 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=2,3 parent=null", nodes[0].ToString()); } } @@ -672,7 +637,7 @@ TEST_F(ViewManagerTest, CantAccessChildrenOfEmbeddedNode) { std::vector nodes; connection2_->GetNodeTree(BuildNodeId(2, 2), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[0].ToString()); + EXPECT_EQ("node=2,2 parent=1,1", nodes[0].ToString()); } { @@ -693,9 +658,9 @@ TEST_F(ViewManagerTest, CantAccessChildrenOfEmbeddedNode) { std::vector nodes; connection_->GetNodeTree(BuildNodeId(1, 1), &nodes); ASSERT_EQ(3u, nodes.size()); - EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); - EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[1].ToString()); - EXPECT_EQ("node=3,3 parent=2,2 view=null", nodes[2].ToString()); + EXPECT_EQ("node=1,1 parent=null", nodes[0].ToString()); + EXPECT_EQ("node=2,2 parent=1,1", nodes[1].ToString()); + EXPECT_EQ("node=3,3 parent=2,2", nodes[2].ToString()); } } @@ -737,11 +702,6 @@ TEST_F(ViewManagerTest, CreateNode) { EXPECT_TRUE(connection_->changes().empty()); } -TEST_F(ViewManagerTest, CreateViewFailsWithBogusConnectionId) { - EXPECT_FALSE(connection_->CreateView(BuildViewId(2, 1))); - EXPECT_TRUE(connection_->changes().empty()); -} - // Verifies AddNode fails when node is already in position. TEST_F(ViewManagerTest, AddNodeWithNoChange) { ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); @@ -885,8 +845,8 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedAddingKnownToUnknown) { ASSERT_EQ(1u, changes.size()); EXPECT_EQ("HierarchyChanged node=2,2 new_parent=1,1 old_parent=null", changes[0]); - EXPECT_EQ("[node=2,2 parent=1,1 view=null]," - "[node=2,21 parent=2,2 view=null]", + EXPECT_EQ("[node=2,2 parent=1,1]," + "[node=2,21 parent=2,2]", ChangeNodeDescription(connection_->changes())); } } @@ -997,13 +957,6 @@ TEST_F(ViewManagerTest, DeleteNodeFromAnotherConnectionDisallowed) { EXPECT_FALSE(connection2_->DeleteNode(BuildNodeId(1, 1))); } -// Verifies DeleteView isn't allowed from a separate connection. -TEST_F(ViewManagerTest, DeleteViewFromAnotherConnectionDisallowed) { - ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 1))); - ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); - EXPECT_FALSE(connection2_->DeleteView(BuildViewId(1, 1))); -} - // Verifies if a node was deleted and then reused that other clients are // properly notified. TEST_F(ViewManagerTest, ReuseDeletedNodeId) { @@ -1018,7 +971,7 @@ TEST_F(ViewManagerTest, ReuseDeletedNodeId) { const Changes changes(ChangesToDescription1(connection_->changes())); EXPECT_EQ("HierarchyChanged node=2,2 new_parent=1,1 old_parent=null", changes[0]); - EXPECT_EQ("[node=2,2 parent=1,1 view=null]", + EXPECT_EQ("[node=2,2 parent=1,1]", ChangeNodeDescription(connection_->changes())); } @@ -1041,120 +994,11 @@ TEST_F(ViewManagerTest, ReuseDeletedNodeId) { const Changes changes(ChangesToDescription1(connection_->changes())); EXPECT_EQ("HierarchyChanged node=2,2 new_parent=1,1 old_parent=null", changes[0]); - EXPECT_EQ("[node=2,2 parent=1,1 view=null]", + EXPECT_EQ("[node=2,2 parent=1,1]", ChangeNodeDescription(connection_->changes())); } } -// Assertions around setting a view. -TEST_F(ViewManagerTest, SetView) { - ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); - - // Create nodes 1, 2 and 3 and the view 11. Nodes 2 and 3 are parented to 1. - ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 2))); - ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 3))); - ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11))); - ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 2))); - ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3))); - - // Do this to clear out the changes conncection_ has seen and ensure it's up - // to date. - connection_->CopyChangesFromTracker(); - ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 100))); - - // Set view 11 on node 1. - { - ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 11))); - - connection_->DoRunLoopUntilChangesCount(1); - const Changes changes(ChangesToDescription1(connection_->changes())); - ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ViewReplaced node=1,1 new_view=2,11 old_view=null", - changes[0]); - } - - // Set view 11 on node 2. - { - ASSERT_TRUE(connection2_->SetView(BuildNodeId(2, 2), BuildViewId(2, 11))); - - connection_->DoRunLoopUntilChangesCount(2); - const Changes changes(ChangesToDescription1(connection_->changes())); - ASSERT_EQ(2u, changes.size()); - EXPECT_EQ("ViewReplaced node=1,1 new_view=null old_view=2,11", - changes[0]); - EXPECT_EQ("ViewReplaced node=2,2 new_view=2,11 old_view=null", - changes[1]); - } -} - -// Verifies deleting a node with a view sends correct notifications. -TEST_F(ViewManagerTest, DeleteNodeWithView) { - ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); - - ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 2))); - ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 3))); - ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11))); - - // Set view 11 on node 2. - ASSERT_TRUE(connection2_->SetView(BuildNodeId(2, 2), BuildViewId(2, 11))); - - // Delete node 2. Connection 1 should not see this because the node was not - // known to it. - ASSERT_TRUE(connection2_->DeleteNode(BuildNodeId(2, 2))); - - // Parent 3 to 1. - ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3))); - connection_->DoRunLoopUntilChangesCount(1); - - // Set view 11 on node 3. - { - ASSERT_TRUE(connection2_->SetView(BuildNodeId(2, 3), BuildViewId(2, 11))); - - connection_->DoRunLoopUntilChangesCount(1); - const Changes changes(ChangesToDescription1(connection_->changes())); - ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ViewReplaced node=2,3 new_view=2,11 old_view=null", changes[0]); - } - - // Delete 3. - { - ASSERT_TRUE(connection2_->DeleteNode(BuildNodeId(2, 3))); - - connection_->DoRunLoopUntilChangesCount(1); - const Changes changes(ChangesToDescription1(connection_->changes())); - ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("NodeDeleted node=2,3", changes[0]); - } -} - -// Sets view from one connection on another. -TEST_F(ViewManagerTest, SetViewFromSecondConnection) { - ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); - - ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); - - // Create a view in the second connection. - ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 51))); - - // Attach view to node 1 in the first connection. - { - ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 51))); - connection_->DoRunLoopUntilChangesCount(1); - const Changes changes(ChangesToDescription1(connection_->changes())); - ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ViewReplaced node=1,1 new_view=2,51 old_view=null", changes[0]); - } - - // Shutdown the second connection and verify view is removed. - { - DestroySecondConnection(); - connection_->DoRunLoopUntilChangesCount(1); - const Changes changes(ChangesToDescription1(connection_->changes())); - ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ViewReplaced node=1,1 new_view=null old_view=2,51", changes[0]); - } -} - // Assertions for GetNodeTree. TEST_F(ViewManagerTest, GetNodeTree) { ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); @@ -1170,20 +1014,16 @@ TEST_F(ViewManagerTest, GetNodeTree) { ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 2))); ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3))); - // Attach view to node 11 in the first connection. - ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 51))); - ASSERT_TRUE(connection_->SetView(BuildNodeId(1, 11), BuildViewId(1, 51))); - // Verifies GetNodeTree() on the root. The root connection sees all. { std::vector nodes; connection_->GetNodeTree(BuildNodeId(0, 1), &nodes); ASSERT_EQ(5u, nodes.size()); - EXPECT_EQ("node=0,1 parent=null view=null", nodes[0].ToString()); - EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes[1].ToString()); - EXPECT_EQ("node=1,11 parent=1,1 view=1,51", nodes[2].ToString()); - EXPECT_EQ("node=2,2 parent=1,1 view=null", nodes[3].ToString()); - EXPECT_EQ("node=2,3 parent=1,1 view=null", nodes[4].ToString()); + EXPECT_EQ("node=0,1 parent=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=0,1", nodes[1].ToString()); + EXPECT_EQ("node=1,11 parent=1,1", nodes[2].ToString()); + EXPECT_EQ("node=2,2 parent=1,1", nodes[3].ToString()); + EXPECT_EQ("node=2,3 parent=1,1", nodes[4].ToString()); } // Verifies GetNodeTree() on the node 1,1. This does not include any children @@ -1192,7 +1032,7 @@ TEST_F(ViewManagerTest, GetNodeTree) { std::vector nodes; connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=null", nodes[0].ToString()); } // Connection 2 shouldn't be able to get the root tree. @@ -1272,32 +1112,12 @@ TEST_F(ViewManagerTest, CantRemoveNodesInOtherRoots) { std::vector nodes; connection_->GetNodeTree(BuildNodeId(0, 1), &nodes); ASSERT_EQ(3u, nodes.size()); - EXPECT_EQ("node=0,1 parent=null view=null", nodes[0].ToString()); - EXPECT_EQ("node=1,1 parent=0,1 view=null", nodes[1].ToString()); - EXPECT_EQ("node=1,2 parent=0,1 view=null", nodes[2].ToString()); + EXPECT_EQ("node=0,1 parent=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=0,1", nodes[1].ToString()); + EXPECT_EQ("node=1,2 parent=0,1", nodes[2].ToString()); } } -// Verify SetView fails for nodes that are not descendants of the roots. -TEST_F(ViewManagerTest, CantRemoveSetViewInOtherRoots) { - // Create 1 and 2 in the first connection and parent both to the root. - ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); - ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); - - ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1))); - ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 2))); - - ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); - - // Create a view in the second connection. - ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 51))); - - // Connection 2 should be able to set the view on node 1 (it's root), but not - // on 2. - ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 51))); - ASSERT_FALSE(connection2_->SetView(BuildNodeId(1, 2), BuildViewId(2, 51))); -} - // Verify GetNodeTree fails for nodes that are not descendants of the roots. TEST_F(ViewManagerTest, CantGetNodeTreeOfOtherRoots) { // Create 1 and 2 in the first connection and parent both to the root. @@ -1322,7 +1142,7 @@ TEST_F(ViewManagerTest, CantGetNodeTreeOfOtherRoots) { // Should get node 1 if asked for. connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=null", nodes[0].ToString()); } TEST_F(ViewManagerTest, ConnectTwice) { @@ -1342,29 +1162,26 @@ TEST_F(ViewManagerTest, ConnectTwice) { const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ("OnEmbed creator=mojo:test_url", changes[0]); - EXPECT_EQ("[node=1,2 parent=null view=null]", + EXPECT_EQ("[node=1,2 parent=null]", ChangeNodeDescription(connection2_->changes())); } } -TEST_F(ViewManagerTest, OnViewInput) { - // Create node 1 and assign a view from connection 2 to it. +TEST_F(ViewManagerTest, OnNodeInput) { ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 1))); ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); - ASSERT_TRUE(connection2_->CreateView(BuildViewId(2, 11))); - ASSERT_TRUE(connection2_->SetView(BuildNodeId(1, 1), BuildViewId(2, 11))); - // Dispatch an event to the view and verify its received. + // Dispatch an event to the node and verify its received. { EventPtr event(Event::New()); event->action = static_cast(1); connection_->view_manager()->DispatchOnViewInputEvent( - BuildViewId(2, 11), + BuildNodeId(1, 1), event.Pass()); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("InputEvent view=2,11 event_action=1", changes[0]); + EXPECT_EQ("InputEvent node=1,1 event_action=1", changes[0]); } } @@ -1422,7 +1239,7 @@ TEST_F(ViewManagerTest, EmbedWithSameNodeId2) { ASSERT_EQ(1u, changes.size()); EXPECT_EQ("OnEmbed creator=mojo:test_url", ChangesToDescription1(changes)[0]); - EXPECT_EQ("[node=1,1 parent=null view=null]", + EXPECT_EQ("[node=1,1 parent=null]", ChangeNodeDescription(changes)); // And 3 should get a delete. @@ -1445,7 +1262,7 @@ TEST_F(ViewManagerTest, EmbedWithSameNodeId2) { std::vector nodes; connection_->GetNodeTree(BuildNodeId(1, 1), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=1,1 parent=null", nodes[0].ToString()); } // Verify connection3_ can still see the node it created 3,1. @@ -1453,7 +1270,7 @@ TEST_F(ViewManagerTest, EmbedWithSameNodeId2) { std::vector nodes; connection3_->GetNodeTree(BuildNodeId(3, 1), &nodes); ASSERT_EQ(1u, nodes.size()); - EXPECT_EQ("node=3,1 parent=null view=null", nodes[0].ToString()); + EXPECT_EQ("node=3,1 parent=null", nodes[0].ToString()); } } @@ -1464,10 +1281,5 @@ TEST_F(ViewManagerTest, EmbedWithSameNodeId2) { // that SetBounsdNodes/AddNode and the like don't result in messages to the // originating connection. -// TODO(beng): Add tests for focus: -// - focus between two nodes known to a connection -// - focus between nodes unknown to one of the connections. -// - focus between nodes unknown to either connection. - } // namespace service } // namespace mojo diff --git a/mojo/services/view_manager/window_manager_access_policy.cc b/mojo/services/view_manager/window_manager_access_policy.cc index 2117898dd44b3..226630a7bab7b 100644 --- a/mojo/services/view_manager/window_manager_access_policy.cc +++ b/mojo/services/view_manager/window_manager_access_policy.cc @@ -6,7 +6,6 @@ #include "mojo/services/view_manager/access_policy_delegate.h" #include "mojo/services/view_manager/node.h" -#include "mojo/services/view_manager/view.h" namespace mojo { namespace service { @@ -44,20 +43,6 @@ bool WindowManagerAccessPolicy::CanDeleteNode(const Node* node) const { return node->id().connection_id == connection_id_; } -bool WindowManagerAccessPolicy::CanDeleteView(const View* view) const { - return view->id().connection_id == connection_id_; -} - -bool WindowManagerAccessPolicy::CanSetView(const Node* node, - const View* view) const { - return !view || view->id().connection_id == connection_id_; -} - -bool WindowManagerAccessPolicy::CanSetFocus(const Node* node) const { - // TODO(beng): security. - return true; -} - bool WindowManagerAccessPolicy::CanGetNodeTree(const Node* node) const { return true; } @@ -76,8 +61,12 @@ bool WindowManagerAccessPolicy::CanChangeNodeVisibility( return node->id().connection_id == connection_id_; } -bool WindowManagerAccessPolicy::CanSetViewContents(const View* view) const { - return view->id().connection_id == connection_id_; +bool WindowManagerAccessPolicy::CanSetNodeContents(const Node* node) const { + if (delegate_->IsNodeRootOfAnotherConnectionForAccessPolicy(node)) + return false; + return node->id().connection_id == connection_id_ || + (delegate_->GetRootsForAccessPolicy().count( + NodeIdToTransportId(node->id())) > 0); } bool WindowManagerAccessPolicy::CanSetNodeBounds(const Node* node) const { @@ -103,10 +92,5 @@ bool WindowManagerAccessPolicy::IsNodeKnown(const Node* node) const { return delegate_->IsNodeKnownForAccessPolicy(node); } -Id WindowManagerAccessPolicy::GetViewIdToSend(const Node* node, - const View* view) const { - return ViewIdToTransportId(view->id()); -} - } // namespace service } // namespace mojo diff --git a/mojo/services/view_manager/window_manager_access_policy.h b/mojo/services/view_manager/window_manager_access_policy.h index 35d1ed394cc01..3a628af1eafc1 100644 --- a/mojo/services/view_manager/window_manager_access_policy.h +++ b/mojo/services/view_manager/window_manager_access_policy.h @@ -26,20 +26,16 @@ class WindowManagerAccessPolicy : public AccessPolicy { const Node* relative_node, OrderDirection direction) const OVERRIDE; virtual bool CanDeleteNode(const Node* node) const OVERRIDE; - virtual bool CanDeleteView(const View* view) const OVERRIDE; - virtual bool CanSetView(const Node* node, const View* view) const OVERRIDE; - virtual bool CanSetFocus(const Node* node) const OVERRIDE; virtual bool CanGetNodeTree(const Node* node) const OVERRIDE; virtual bool CanDescendIntoNodeForNodeTree(const Node* node) const OVERRIDE; virtual bool CanEmbed(const Node* node) const OVERRIDE; virtual bool CanChangeNodeVisibility(const Node* node) const OVERRIDE; - virtual bool CanSetViewContents(const View* view) const OVERRIDE; + virtual bool CanSetNodeContents(const Node* node) const OVERRIDE; virtual bool CanSetNodeBounds(const Node* node) const OVERRIDE; virtual bool ShouldNotifyOnHierarchyChange( const Node* node, const Node** new_parent, const Node** old_parent) const OVERRIDE; - virtual Id GetViewIdToSend(const Node* node, const View* view) const OVERRIDE; virtual bool ShouldSendViewDeleted(const ViewId& view_id) const OVERRIDE; private: diff --git a/mojo/services/view_manager/window_tree_host_impl.cc b/mojo/services/view_manager/window_tree_host_impl.cc index e2d6747bd4179..9f201b0845a6a 100644 --- a/mojo/services/view_manager/window_tree_host_impl.cc +++ b/mojo/services/view_manager/window_tree_host_impl.cc @@ -69,10 +69,12 @@ WindowTreeHostImpl::WindowTreeHostImpl( NativeViewportPtr viewport, const gfx::Rect& bounds, const Callback& compositor_created_callback, - const Callback& native_viewport_closed_callback) + const Callback& native_viewport_closed_callback, + const Callback& event_received_callback) : native_viewport_(viewport.Pass()), compositor_created_callback_(compositor_created_callback), native_viewport_closed_callback_(native_viewport_closed_callback), + event_received_callback_(event_received_callback), bounds_(bounds) { native_viewport_.set_client(this); native_viewport_->Create(Rect::From(bounds)); @@ -185,10 +187,7 @@ void WindowTreeHostImpl::OnDestroyed(const mojo::Callback& callback) { void WindowTreeHostImpl::OnEvent(EventPtr event, const mojo::Callback& callback) { - scoped_ptr ui_event = - TypeConverter >::ConvertTo(event); - if (ui_event) - SendEventToProcessor(ui_event.get()); + event_received_callback_.Run(event.Pass()); callback.Run(); }; diff --git a/mojo/services/view_manager/window_tree_host_impl.h b/mojo/services/view_manager/window_tree_host_impl.h index bbbd4b1782784..877b81868e3e8 100644 --- a/mojo/services/view_manager/window_tree_host_impl.h +++ b/mojo/services/view_manager/window_tree_host_impl.h @@ -28,7 +28,8 @@ class WindowTreeHostImpl : public aura::WindowTreeHost, NativeViewportPtr viewport, const gfx::Rect& bounds, const Callback& compositor_created_callback, - const Callback& native_viewport_closed_callback); + const Callback& native_viewport_closed_callback, + const Callback& event_received_callback); virtual ~WindowTreeHostImpl(); gfx::Rect bounds() const { return bounds_; } @@ -64,6 +65,7 @@ class WindowTreeHostImpl : public aura::WindowTreeHost, NativeViewportPtr native_viewport_; Callback compositor_created_callback_; Callback native_viewport_closed_callback_; + Callback event_received_callback_; gfx::Rect bounds_; diff --git a/mojo/services/window_manager/DEPS b/mojo/services/window_manager/DEPS index 4d8d92ecedf24..fcc8ca73765bc 100644 --- a/mojo/services/window_manager/DEPS +++ b/mojo/services/window_manager/DEPS @@ -1,9 +1,11 @@ include_rules = [ "+mojo/aura", - "+mojo/service_manager", + "+mojo/application_manager", "+mojo/services/native_viewport", "+mojo/services/public", "+ui/aura", + "+ui/base", + "+ui/events", "+ui/gfx", "+ui/gl", "+ui/wm", diff --git a/mojo/services/window_manager/main.cc b/mojo/services/window_manager/main.cc index b77d3885a469c..6d8c019a48024 100644 --- a/mojo/services/window_manager/main.cc +++ b/mojo/services/window_manager/main.cc @@ -2,7 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/memory/scoped_ptr.h" #include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/service_provider_impl.h" +#include "mojo/services/public/cpp/view_manager/view_manager.h" +#include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h" #include "mojo/services/window_manager/window_manager_app.h" // ApplicationDelegate implementation file for WindowManager users (e.g. @@ -11,9 +16,62 @@ namespace mojo { +class DefaultWindowManager : public ApplicationDelegate, + public ViewManagerDelegate, + public WindowManagerDelegate { + public: + DefaultWindowManager() + : window_manager_app_(new WindowManagerApp(this, this)), + view_manager_(NULL), + root_(NULL) {} + virtual ~DefaultWindowManager() {} + + private: + // Overridden from ApplicationDelegate: + virtual void Initialize(ApplicationImpl* impl) MOJO_OVERRIDE { + window_manager_app_->Initialize(impl); + } + virtual bool ConfigureIncomingConnection( + ApplicationConnection* connection) MOJO_OVERRIDE { + window_manager_app_->ConfigureIncomingConnection(connection); + return true; + } + + // Overridden from ViewManagerDelegate: + virtual void OnEmbed( + ViewManager* view_manager, + View* root, + ServiceProviderImpl* exported_services, + scoped_ptr imported_services) MOJO_OVERRIDE { + view_manager_ = view_manager; + root_ = root; + view_manager_->SetWindowManagerDelegate(this); + } + virtual void OnViewManagerDisconnected( + ViewManager* view_manager) MOJO_OVERRIDE {} + + // Overridden from WindowManagerDelegate: + virtual void Embed( + const String& url, + InterfaceRequest service_provider) MOJO_OVERRIDE { + View* view = View::Create(view_manager_); + root_->AddChild(view); + view->Embed(url, scoped_ptr( + new mojo::ServiceProviderImpl).Pass()); + } + virtual void DispatchEvent(EventPtr event) MOJO_OVERRIDE {} + + scoped_ptr window_manager_app_; + + ViewManager* view_manager_; + View* root_; + + MOJO_DISALLOW_COPY_AND_ASSIGN(DefaultWindowManager); +}; + // static ApplicationDelegate* ApplicationDelegate::Create() { - return new WindowManagerApp(NULL); + return new DefaultWindowManager; } } // namespace mojo diff --git a/mojo/services/window_manager/window_manager_api_unittest.cc b/mojo/services/window_manager/window_manager_api_unittest.cc index 4450c3b23b538..f7a0e96d78ffe 100644 --- a/mojo/services/window_manager/window_manager_api_unittest.cc +++ b/mojo/services/window_manager/window_manager_api_unittest.cc @@ -4,13 +4,13 @@ #include "base/bind.h" #include "base/memory/scoped_vector.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" #include "mojo/public/cpp/application/service_provider_impl.h" #include "mojo/public/interfaces/application/service_provider.mojom.h" -#include "mojo/service_manager/service_manager.h" -#include "mojo/services/public/cpp/view_manager/node.h" #include "mojo/services/public/cpp/view_manager/types.h" +#include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" @@ -47,33 +47,6 @@ bool InitEmbed(ViewManagerInitService* view_manager_init, return result; } -void OpenWindowCallback(Id* id, - base::RunLoop* run_loop, - Id window_id) { - *id = window_id; - run_loop->Quit(); -} - -Id OpenWindow(WindowManagerService* window_manager) { - base::RunLoop run_loop; - Id id; - window_manager->OpenWindow( - base::Bind(&OpenWindowCallback, &id, &run_loop)); - run_loop.Run(); - return id; -} - -Id OpenWindowWithURL(WindowManagerService* window_manager, - const std::string& url) { - base::RunLoop run_loop; - Id id; - window_manager->OpenWindowWithURL( - url, - base::Bind(&OpenWindowCallback, &id, &run_loop)); - run_loop.Run(); - return id; -} - class TestWindowManagerClient : public WindowManagerClient { public: typedef base::Callback @@ -119,29 +92,31 @@ class TestWindowManagerClient : public WindowManagerClient { DISALLOW_COPY_AND_ASSIGN(TestWindowManagerClient); }; -class TestServiceLoader : public ServiceLoader, - public ApplicationDelegate, - public ViewManagerDelegate { +class TestApplicationLoader : public ApplicationLoader, + public ApplicationDelegate, + public ViewManagerDelegate { public: - typedef base::Callback RootAddedCallback; + typedef base::Callback RootAddedCallback; - explicit TestServiceLoader(const RootAddedCallback& root_added_callback) + explicit TestApplicationLoader(const RootAddedCallback& root_added_callback) : root_added_callback_(root_added_callback), view_manager_client_factory_(this) {} - virtual ~TestServiceLoader() {} + virtual ~TestApplicationLoader() {} private: - // Overridden from ServiceLoader: - virtual void LoadService(ServiceManager* service_manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) MOJO_OVERRIDE { + // Overridden from ApplicationLoader: + virtual void Load(ApplicationManager* application_manager, + const GURL& url, + scoped_refptr callbacks) MOJO_OVERRIDE { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; scoped_ptr app( new ApplicationImpl(this, shell_handle.Pass())); apps_.push_back(app.release()); } - virtual void OnServiceError(ServiceManager* service_manager, - const GURL& url) MOJO_OVERRIDE { - } + virtual void OnServiceError(ApplicationManager* application_manager, + const GURL& url) MOJO_OVERRIDE {} // Overridden from ApplicationDelegate: virtual bool ConfigureIncomingConnection( @@ -153,7 +128,7 @@ class TestServiceLoader : public ServiceLoader, // Overridden from ViewManagerDelegate: virtual void OnEmbed( ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) MOJO_OVERRIDE { root_added_callback_.Run(root); @@ -167,7 +142,7 @@ class TestServiceLoader : public ServiceLoader, ScopedVector apps_; ViewManagerClientFactory view_manager_client_factory_; - DISALLOW_COPY_AND_ASSIGN(TestServiceLoader); + DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader); }; } // namespace @@ -209,6 +184,15 @@ class WindowManagerApiTest : public testing::Test { return old_and_new; } + Id OpenWindow() { + return OpenWindowWithURL(kTestServiceURL); + } + + Id OpenWindowWithURL(const std::string& url) { + InitEmbed(view_manager_init_.get(), url); + return WaitForEmbed(); + } + TestWindowManagerClient* window_manager_client() { return window_manager_client_.get(); } @@ -220,13 +204,11 @@ class WindowManagerApiTest : public testing::Test { virtual void SetUp() MOJO_OVERRIDE { test_helper_.Init(); test_helper_.SetLoaderForURL( - scoped_ptr(new TestServiceLoader( - base::Bind(&WindowManagerApiTest::OnRootAdded, - base::Unretained(this)))), + scoped_ptr(new TestApplicationLoader(base::Bind( + &WindowManagerApiTest::OnRootAdded, base::Unretained(this)))), GURL(kTestServiceURL)); - test_helper_.service_manager()->ConnectToService( - GURL("mojo:mojo_view_manager"), - &view_manager_init_); + test_helper_.application_manager()->ConnectToService( + GURL("mojo:mojo_view_manager"), &view_manager_init_); ASSERT_TRUE(InitEmbed(view_manager_init_.get(), "mojo:mojo_core_window_manager")); ConnectToWindowManager(); @@ -234,23 +216,22 @@ class WindowManagerApiTest : public testing::Test { virtual void TearDown() MOJO_OVERRIDE {} void ConnectToWindowManager() { - test_helper_.service_manager()->ConnectToService( - GURL("mojo:mojo_core_window_manager"), - &window_manager_); + test_helper_.application_manager()->ConnectToService( + GURL("mojo:mojo_core_window_manager"), &window_manager_); base::RunLoop connect_loop; window_manager_client_.reset(new TestWindowManagerClient(&connect_loop)); window_manager_.set_client(window_manager_client()); connect_loop.Run(); } - void OnRootAdded(Node* root) { + void OnRootAdded(View* root) { if (!root_added_callback_.is_null()) root_added_callback_.Run(root); } void OnEmbed(Id* root_id, base::RunLoop* loop, - Node* root) { + View* root) { *root_id = root->id(); loop->Quit(); } @@ -278,28 +259,20 @@ class WindowManagerApiTest : public testing::Test { shell::ShellTestHelper test_helper_; ViewManagerInitServicePtr view_manager_init_; scoped_ptr window_manager_client_; - TestServiceLoader::RootAddedCallback root_added_callback_; + TestApplicationLoader::RootAddedCallback root_added_callback_; DISALLOW_COPY_AND_ASSIGN(WindowManagerApiTest); }; -TEST_F(WindowManagerApiTest, OpenWindow) { - OpenWindow(window_manager_.get()); - Id created_node = - OpenWindowWithURL(window_manager_.get(), kTestServiceURL); - Id embed_node = WaitForEmbed(); - EXPECT_EQ(created_node, embed_node); -} - TEST_F(WindowManagerApiTest, FocusAndActivateWindow) { - Id first_window = OpenWindow(window_manager_.get()); + Id first_window = OpenWindow(); window_manager_->FocusWindow(first_window, base::Bind(&EmptyResultCallback)); TwoIds ids = WaitForFocusChange(); EXPECT_TRUE(ids.first == 0); EXPECT_EQ(ids.second, first_window); - Id second_window = OpenWindow(window_manager_.get()); + Id second_window = OpenWindow(); window_manager_->ActivateWindow(second_window, base::Bind(&EmptyResultCallback)); ids = WaitForActiveWindowChange(); diff --git a/mojo/services/window_manager/window_manager_app.cc b/mojo/services/window_manager/window_manager_app.cc index 6ed9113ed0857..a6d57c9b6d219 100644 --- a/mojo/services/window_manager/window_manager_app.cc +++ b/mojo/services/window_manager/window_manager_app.cc @@ -7,26 +7,68 @@ #include "base/message_loop/message_loop.h" #include "base/stl_util.h" #include "mojo/aura/aura_init.h" -#include "mojo/aura/window_tree_host_mojo.h" #include "mojo/public/cpp/application/application_connection.h" -#include "mojo/services/public/cpp/view_manager/node.h" +#include "mojo/services/public/cpp/input_events/input_events_type_converters.h" +#include "mojo/services/public/cpp/view_manager/view.h" #include "mojo/services/public/cpp/view_manager/view_manager.h" #include "ui/aura/window.h" +#include "ui/aura/window_delegate.h" #include "ui/aura/window_property.h" +#include "ui/base/hit_test.h" #include "ui/wm/core/capture_controller.h" #include "ui/wm/core/focus_controller.h" #include "ui/wm/core/focus_rules.h" #include "ui/wm/public/activation_client.h" -DECLARE_WINDOW_PROPERTY_TYPE(mojo::Node*); +DECLARE_WINDOW_PROPERTY_TYPE(mojo::View*); namespace mojo { + +// The aura::Windows we use to track Views don't render, so we don't actually +// need to supply a fully functional WindowDelegate. We do need to provide _a_ +// delegate however, otherwise the event dispatcher won't dispatch events to +// these windows. (The aura WindowTargeter won't allow a delegate-less window +// to be the target of an event, since the window delegate is considered the +// "target handler"). +class DummyDelegate : public aura::WindowDelegate { + public: + DummyDelegate() {} + virtual ~DummyDelegate() {} + + private: + // WindowDelegate overrides: + virtual gfx::Size GetMinimumSize() const OVERRIDE { return gfx::Size(); } + virtual gfx::Size GetMaximumSize() const OVERRIDE { return gfx::Size(); } + virtual void OnBoundsChanged(const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) OVERRIDE {} + virtual gfx::NativeCursor GetCursor(const gfx::Point& point) OVERRIDE { + return gfx::kNullCursor; + } + virtual int GetNonClientComponent(const gfx::Point& point) const OVERRIDE { + return HTCAPTION; + } + virtual bool ShouldDescendIntoChildForEventHandling( + aura::Window* child, + const gfx::Point& location) OVERRIDE { return true; } + virtual bool CanFocus() OVERRIDE { return true; } + virtual void OnCaptureLost() OVERRIDE {} + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE {} + virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {} + virtual void OnWindowDestroying(aura::Window* window) OVERRIDE {} + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {} + virtual void OnWindowTargetVisibilityChanged(bool visible) OVERRIDE {} + virtual bool HasHitTestMask() const OVERRIDE { return false; } + virtual void GetHitTestMask(gfx::Path* mask) const OVERRIDE {} + + DISALLOW_COPY_AND_ASSIGN(DummyDelegate); +}; + namespace { -DEFINE_WINDOW_PROPERTY_KEY(Node*, kNodeKey, NULL); +DEFINE_WINDOW_PROPERTY_KEY(View*, kViewKey, NULL); Id GetIdForWindow(aura::Window* window) { - return window ? window->GetProperty(kNodeKey)->id() : 0; + return window ? WindowManagerApp::GetViewForWindow(window)->id() : 0; } class WMFocusRules : public wm::FocusRules { @@ -70,22 +112,23 @@ class WMFocusRules : public wm::FocusRules { //////////////////////////////////////////////////////////////////////////////// // WindowManagerApp, public: -WindowManagerApp::WindowManagerApp(ViewManagerDelegate* delegate) +WindowManagerApp::WindowManagerApp( + ViewManagerDelegate* view_manager_delegate, + WindowManagerDelegate* window_manager_delegate) : window_manager_service_factory_(this), - wrapped_delegate_(delegate), + wrapped_view_manager_delegate_(view_manager_delegate), + wrapped_window_manager_delegate_(window_manager_delegate), view_manager_(NULL), view_manager_client_factory_(this), - root_(NULL) { + root_(NULL), + dummy_delegate_(new DummyDelegate) { } -WindowManagerApp::~WindowManagerApp() { - // TODO(beng): Figure out if this should be done in - // OnViewManagerDisconnected(). - STLDeleteValues(&node_id_to_window_map_); - if (focus_client_.get()) - focus_client_->RemoveObserver(this); - if (activation_client_) - activation_client_->RemoveObserver(this); +WindowManagerApp::~WindowManagerApp() {} + +// static +View* WindowManagerApp::GetViewForWindow(aura::Window* window) { + return window->GetProperty(kViewKey); } void WindowManagerApp::AddConnection(WindowManagerServiceImpl* connection) { @@ -98,33 +141,20 @@ void WindowManagerApp::RemoveConnection(WindowManagerServiceImpl* connection) { connections_.erase(connection); } -Id WindowManagerApp::OpenWindow() { - Node* node = Node::Create(view_manager_); - root_->AddChild(node); - return node->id(); -} - -Id WindowManagerApp::OpenWindowWithURL(const String& url) { - Node* node = Node::Create(view_manager_); - root_->AddChild(node); - node->Embed(url); - return node->id(); -} - -void WindowManagerApp::SetCapture(Id node) { - capture_client_->capture_client()->SetCapture(GetWindowForNodeId(node)); +void WindowManagerApp::SetCapture(Id view) { + capture_client_->capture_client()->SetCapture(GetWindowForViewId(view)); // TODO(beng): notify connected clients that capture has changed, probably // by implementing some capture-client observer. } -void WindowManagerApp::FocusWindow(Id node) { - aura::Window* window = GetWindowForNodeId(node); +void WindowManagerApp::FocusWindow(Id view) { + aura::Window* window = GetWindowForViewId(view); DCHECK(window); focus_client_->FocusWindow(window); } -void WindowManagerApp::ActivateWindow(Id node) { - aura::Window* window = GetWindowForNodeId(node); +void WindowManagerApp::ActivateWindow(Id view) { + aura::Window* window = GetWindowForViewId(view); DCHECK(window); activation_client_->ActivateWindow(window); } @@ -151,17 +181,19 @@ bool WindowManagerApp::ConfigureIncomingConnection( // WindowManagerApp, ViewManagerDelegate implementation: void WindowManagerApp::OnEmbed(ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) { DCHECK(!view_manager_ && !root_); view_manager_ = view_manager; + view_manager_->SetWindowManagerDelegate(this); root_ = root; - root_->AddObserver(this); window_tree_host_.reset(new WindowTreeHostMojo(root_, this)); + window_tree_host_->window()->SetBounds(root->bounds()); + window_tree_host_->window()->Show(); - RegisterSubtree(root_->id(), window_tree_host_->window()); + RegisterSubtree(root_, window_tree_host_->window()); capture_client_.reset( new wm::ScopedCaptureClient(window_tree_host_->window())); @@ -169,13 +201,14 @@ void WindowManagerApp::OnEmbed(ViewManager* view_manager, new wm::FocusController(new WMFocusRules); activation_client_ = focus_controller; focus_client_.reset(focus_controller); + aura::client::SetFocusClient(window_tree_host_->window(), focus_controller); focus_client_->AddObserver(this); activation_client_->AddObserver(this); - if (wrapped_delegate_) { - wrapped_delegate_->OnEmbed(view_manager, root, exported_services, - imported_services.Pass()); + if (wrapped_view_manager_delegate_) { + wrapped_view_manager_delegate_->OnEmbed( + view_manager, root, exported_services, imported_services.Pass()); } for (Connections::const_iterator it = connections_.begin(); @@ -187,40 +220,74 @@ void WindowManagerApp::OnEmbed(ViewManager* view_manager, void WindowManagerApp::OnViewManagerDisconnected( ViewManager* view_manager) { DCHECK_EQ(view_manager_, view_manager); - if (wrapped_delegate_) - wrapped_delegate_->OnViewManagerDisconnected(view_manager); + if (wrapped_view_manager_delegate_) + wrapped_view_manager_delegate_->OnViewManagerDisconnected(view_manager); view_manager_ = NULL; base::MessageLoop::current()->Quit(); } //////////////////////////////////////////////////////////////////////////////// -// WindowManagerApp, NodeObserver implementation: +// WindowManagerApp, WindowManagerDelegate implementation: + +void WindowManagerApp::Embed( + const String& url, + InterfaceRequest service_provider) { + if (wrapped_window_manager_delegate_) + wrapped_window_manager_delegate_->Embed(url, service_provider.Pass()); +} + +void WindowManagerApp::DispatchEvent(EventPtr event) { + scoped_ptr ui_event = + TypeConverter >::ConvertTo(event); + if (ui_event) + window_tree_host_->SendEventToProcessor(ui_event.get()); +} + +//////////////////////////////////////////////////////////////////////////////// +// WindowManagerApp, ViewObserver implementation: void WindowManagerApp::OnTreeChanged( - const NodeObserver::TreeChangeParams& params) { - DCHECK_EQ(params.receiver, root_); + const ViewObserver::TreeChangeParams& params) { + if (params.receiver != root_) + return; DCHECK(params.old_parent || params.new_parent); if (!params.target) return; if (params.new_parent) { - if (node_id_to_window_map_.find(params.target->id()) == - node_id_to_window_map_.end()) { - NodeIdToWindowMap::const_iterator it = - node_id_to_window_map_.find(params.new_parent->id()); - DCHECK(it != node_id_to_window_map_.end()); - RegisterSubtree(params.target->id(), it->second); + if (view_id_to_window_map_.find(params.target->id()) == + view_id_to_window_map_.end()) { + ViewIdToWindowMap::const_iterator it = + view_id_to_window_map_.find(params.new_parent->id()); + DCHECK(it != view_id_to_window_map_.end()); + RegisterSubtree(params.target, it->second); } } else if (params.old_parent) { - UnregisterSubtree(params.target->id()); + UnregisterSubtree(params.target); } } -void WindowManagerApp::OnNodeDestroyed(Node* node) { +void WindowManagerApp::OnViewDestroyed(View* view) { + if (view != root_) + return; + aura::Window* window = GetWindowForViewId(view->id()); + window->RemovePreTargetHandler(this); root_ = NULL; + STLDeleteValues(&view_id_to_window_map_); + if (focus_client_.get()) + focus_client_->RemoveObserver(this); + if (activation_client_) + activation_client_->RemoveObserver(this); window_tree_host_.reset(); } +void WindowManagerApp::OnViewBoundsChanged(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) { + aura::Window* window = GetWindowForViewId(view->id()); + window->SetBounds(new_bounds); +} + //////////////////////////////////////////////////////////////////////////////// // WindowManagerApp, WindowTreeHostMojoDelegate implementation: @@ -229,6 +296,16 @@ void WindowManagerApp::CompositorContentsChanged(const SkBitmap& bitmap) { NOTREACHED(); } +//////////////////////////////////////////////////////////////////////////////// +// WindowManagerApp, ui::EventHandler implementation: + +void WindowManagerApp::OnEvent(ui::Event* event) { + aura::Window* window = static_cast(event->target()); + view_manager_->DispatchEvent( + GetViewForWindow(window), + TypeConverter::ConvertFrom(*event)); +} + //////////////////////////////////////////////////////////////////////////////// // WindowManagerApp, aura::client::FocusChangeObserver implementation: @@ -236,7 +313,7 @@ void WindowManagerApp::OnWindowFocused(aura::Window* gained_focus, aura::Window* lost_focus) { for (Connections::const_iterator it = connections_.begin(); it != connections_.end(); ++it) { - (*it)->NotifyNodeFocused(GetIdForWindow(gained_focus), + (*it)->NotifyViewFocused(GetIdForWindow(gained_focus), GetIdForWindow(lost_focus)); } } @@ -256,34 +333,40 @@ void WindowManagerApp::OnWindowActivated(aura::Window* gained_active, //////////////////////////////////////////////////////////////////////////////// // WindowManagerApp, private: -aura::Window* WindowManagerApp::GetWindowForNodeId( - Id node) const { - NodeIdToWindowMap::const_iterator it = node_id_to_window_map_.find(node); - return it != node_id_to_window_map_.end() ? it->second : NULL; +aura::Window* WindowManagerApp::GetWindowForViewId(Id view) const { + ViewIdToWindowMap::const_iterator it = view_id_to_window_map_.find(view); + return it != view_id_to_window_map_.end() ? it->second : NULL; } -void WindowManagerApp::RegisterSubtree(Id id, - aura::Window* parent) { - Node* node = view_manager_->GetNodeById(id); - DCHECK(node_id_to_window_map_.find(id) == node_id_to_window_map_.end()); - aura::Window* window = new aura::Window(NULL); - window->SetProperty(kNodeKey, node); +void WindowManagerApp::RegisterSubtree(View* view, aura::Window* parent) { + view->AddObserver(this); + DCHECK(view_id_to_window_map_.find(view->id()) == + view_id_to_window_map_.end()); + aura::Window* window = new aura::Window(dummy_delegate_.get()); + window->set_id(view->id()); + window->SetProperty(kViewKey, view); + // All events pass through the root during dispatch, so we only need a handler + // installed there. + if (view == root_) + window->AddPreTargetHandler(this); parent->AddChild(window); - node_id_to_window_map_[id] = window; - Node::Children::const_iterator it = node->children().begin(); - for (; it != node->children().end(); ++it) - RegisterSubtree((*it)->id(), window); + window->SetBounds(view->bounds()); + window->Show(); + view_id_to_window_map_[view->id()] = window; + View::Children::const_iterator it = view->children().begin(); + for (; it != view->children().end(); ++it) + RegisterSubtree(*it, window); } -void WindowManagerApp::UnregisterSubtree(Id id) { - Node* node = view_manager_->GetNodeById(id); - NodeIdToWindowMap::iterator it = node_id_to_window_map_.find(id); - DCHECK(it != node_id_to_window_map_.end()); +void WindowManagerApp::UnregisterSubtree(View* view) { + view->RemoveObserver(this); + ViewIdToWindowMap::iterator it = view_id_to_window_map_.find(view->id()); + DCHECK(it != view_id_to_window_map_.end()); scoped_ptr window(it->second); - node_id_to_window_map_.erase(it); - Node::Children::const_iterator child = node->children().begin(); - for (; child != node->children().end(); ++child) - UnregisterSubtree((*child)->id()); + view_id_to_window_map_.erase(it); + View::Children::const_iterator child = view->children().begin(); + for (; child != view->children().end(); ++child) + UnregisterSubtree(*child); } } // namespace mojo diff --git a/mojo/services/window_manager/window_manager_app.h b/mojo/services/window_manager/window_manager_app.h index 2396329b81839..63779903e390e 100644 --- a/mojo/services/window_manager/window_manager_app.h +++ b/mojo/services/window_manager/window_manager_app.h @@ -8,16 +8,19 @@ #include #include "base/memory/scoped_ptr.h" +#include "mojo/aura/window_tree_host_mojo.h" #include "mojo/aura/window_tree_host_mojo_delegate.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/interface_factory_impl.h" #include "mojo/public/cpp/bindings/string.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/types.h" #include "mojo/services/public/cpp/view_manager/view_manager_client_factory.h" #include "mojo/services/public/cpp/view_manager/view_manager_delegate.h" +#include "mojo/services/public/cpp/view_manager/view_observer.h" +#include "mojo/services/public/cpp/view_manager/window_manager_delegate.h" #include "mojo/services/window_manager/window_manager_service_impl.h" #include "ui/aura/client/focus_change_observer.h" +#include "ui/events/event_handler.h" #include "ui/wm/public/activation_change_observer.h" namespace aura { @@ -35,31 +38,52 @@ class ScopedCaptureClient; namespace mojo { class AuraInit; +class DummyDelegate; class WindowManagerServiceImpl; class WindowTreeHostMojo; +// Implements core window manager functionality that could conceivably be shared +// across multiple window managers implementing superficially different user +// experiences. Establishes communication with the view manager. +// A window manager wishing to use this core should create and own an instance +// of this object. They may implement the associated ViewManager/WindowManager +// delegate interfaces exposed by the view manager, this object provides the +// canonical implementation of said interfaces but will call out to the wrapped +// instances. +// This object maintains an aura::WindowTreeHost containing a hierarchy of +// aura::Windows. Window manager functionality (e.g. focus, activation, +// modality, etc.) are implemented using aura core window manager components. class WindowManagerApp : public ApplicationDelegate, public ViewManagerDelegate, - public NodeObserver, + public WindowManagerDelegate, + public ViewObserver, public WindowTreeHostMojoDelegate, + public ui::EventHandler, public aura::client::FocusChangeObserver, public aura::client::ActivationChangeObserver { public: - explicit WindowManagerApp(ViewManagerDelegate* delegate); + WindowManagerApp(ViewManagerDelegate* view_manager_delegate, + WindowManagerDelegate* window_manager_delegate); virtual ~WindowManagerApp(); + static View* GetViewForWindow(aura::Window* window); + + // Register/deregister new connections to the window manager service. void AddConnection(WindowManagerServiceImpl* connection); void RemoveConnection(WindowManagerServiceImpl* connection); - Id OpenWindow(); - Id OpenWindowWithURL(const String& url); - void SetCapture(Id node); - void FocusWindow(Id node); - void ActivateWindow(Id node); + // These are canonical implementations of the window manager API methods. + void SetCapture(Id view); + void FocusWindow(Id view); + void ActivateWindow(Id view); bool IsReady() const; + // A client of this object will use this accessor to gain access to the + // aura::Window hierarchy and attach event handlers. + aura::WindowTreeHost* host() { return window_tree_host_.get(); } + // Overridden from ApplicationDelegate: virtual void Initialize(ApplicationImpl* impl) MOJO_OVERRIDE; virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) @@ -67,25 +91,37 @@ class WindowManagerApp private: typedef std::set Connections; - typedef std::map NodeIdToWindowMap; + typedef std::map ViewIdToWindowMap; // Overridden from ViewManagerDelegate: virtual void OnEmbed( ViewManager* view_manager, - Node* root, + View* root, ServiceProviderImpl* exported_services, scoped_ptr imported_services) MOJO_OVERRIDE; virtual void OnViewManagerDisconnected( ViewManager* view_manager) MOJO_OVERRIDE; - // Overridden from NodeObserver: + // Overridden from WindowManagerDelegate: + virtual void Embed( + const String& url, + InterfaceRequest service_provider) OVERRIDE; + virtual void DispatchEvent(EventPtr event) OVERRIDE; + + // Overridden from ViewObserver: virtual void OnTreeChanged( - const NodeObserver::TreeChangeParams& params) MOJO_OVERRIDE; - virtual void OnNodeDestroyed(Node* node) MOJO_OVERRIDE; + const ViewObserver::TreeChangeParams& params) MOJO_OVERRIDE; + virtual void OnViewDestroyed(View* view) MOJO_OVERRIDE; + virtual void OnViewBoundsChanged(View* view, + const gfx::Rect& old_bounds, + const gfx::Rect& new_bounds) MOJO_OVERRIDE; // Overridden from WindowTreeHostMojoDelegate: virtual void CompositorContentsChanged(const SkBitmap& bitmap) MOJO_OVERRIDE; + // Overridden from ui::EventHandler: + virtual void OnEvent(ui::Event* event) MOJO_OVERRIDE; + // Overridden from aura::client::FocusChangeObserver: virtual void OnWindowFocused(aura::Window* gained_focus, aura::Window* lost_focus) MOJO_OVERRIDE; @@ -94,25 +130,26 @@ class WindowManagerApp virtual void OnWindowActivated(aura::Window* gained_active, aura::Window* lost_active) MOJO_OVERRIDE; - aura::Window* GetWindowForNodeId(Id node) const; + aura::Window* GetWindowForViewId(Id view) const; - // Creates an aura::Window for every node in the hierarchy beneath |id|, + // Creates an aura::Window for every view in the hierarchy beneath |view|, // and adds to the registry so that it can be retrieved later via - // GetWindowForNodeId(). - // TODO(beng): perhaps Node should have a property bag. - void RegisterSubtree(Id id, aura::Window* parent); + // GetWindowForViewId(). + // TODO(beng): perhaps View should have a property bag. + void RegisterSubtree(View* view, aura::Window* parent); // Deletes the aura::Windows associated with the hierarchy beneath |id|, // and removes from the registry. - void UnregisterSubtree(Id id); + void UnregisterSubtree(View* view); InterfaceFactoryImplWithContext window_manager_service_factory_; - ViewManagerDelegate* wrapped_delegate_; + ViewManagerDelegate* wrapped_view_manager_delegate_; + WindowManagerDelegate* wrapped_window_manager_delegate_; ViewManager* view_manager_; ViewManagerClientFactory view_manager_client_factory_; - Node* root_; + View* root_; scoped_ptr aura_init_; scoped_ptr window_tree_host_; @@ -122,7 +159,9 @@ class WindowManagerApp aura::client::ActivationClient* activation_client_; Connections connections_; - NodeIdToWindowMap node_id_to_window_map_; + ViewIdToWindowMap view_id_to_window_map_; + + scoped_ptr dummy_delegate_; DISALLOW_COPY_AND_ASSIGN(WindowManagerApp); }; diff --git a/mojo/services/window_manager/window_manager_service_impl.cc b/mojo/services/window_manager/window_manager_service_impl.cc index 408bb580f9f9e..aa1be19e67424 100644 --- a/mojo/services/window_manager/window_manager_service_impl.cc +++ b/mojo/services/window_manager/window_manager_service_impl.cc @@ -1,3 +1,4 @@ + // 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. @@ -25,7 +26,7 @@ void WindowManagerServiceImpl::NotifyReady() { client()->OnWindowManagerReady(); } -void WindowManagerServiceImpl::NotifyNodeFocused(Id new_focused_id, +void WindowManagerServiceImpl::NotifyViewFocused(Id new_focused_id, Id old_focused_id) { client()->OnFocusChanged(old_focused_id, new_focused_id); } @@ -38,55 +39,30 @@ void WindowManagerServiceImpl::NotifyWindowActivated(Id new_active_id, //////////////////////////////////////////////////////////////////////////////// // WindowManagerServiceImpl, WindowManager implementation: -void WindowManagerServiceImpl::OpenWindow( - const Callback& callback) { - bool success = window_manager_->IsReady(); - if (success) { - Id id = window_manager_->OpenWindow(); - callback.Run(id); - } else { - // TODO(beng): perhaps should take an error code for this. - callback.Run(0); - } -} - -void WindowManagerServiceImpl::OpenWindowWithURL( - const String& url, - const Callback& callback) { - bool success = window_manager_->IsReady(); - if (success) { - Id id = window_manager_->OpenWindowWithURL(url); - callback.Run(id); - } else { - // TODO(beng): perhaps should take an error code for this. - callback.Run(0); - } -} - void WindowManagerServiceImpl::SetCapture( - Id node, + Id view, const Callback& callback) { bool success = window_manager_->IsReady(); if (success) - window_manager_->SetCapture(node); + window_manager_->SetCapture(view); callback.Run(success); } void WindowManagerServiceImpl::FocusWindow( - Id node, + Id view, const Callback& callback) { bool success = window_manager_->IsReady(); if (success) - window_manager_->FocusWindow(node); + window_manager_->FocusWindow(view); callback.Run(success); } void WindowManagerServiceImpl::ActivateWindow( - Id node, + Id view, const Callback& callback) { bool success = window_manager_->IsReady(); if (success) - window_manager_->ActivateWindow(node); + window_manager_->ActivateWindow(view); callback.Run(success); } diff --git a/mojo/services/window_manager/window_manager_service_impl.h b/mojo/services/window_manager/window_manager_service_impl.h index 5756222db9151..bd7542689177b 100644 --- a/mojo/services/window_manager/window_manager_service_impl.h +++ b/mojo/services/window_manager/window_manager_service_impl.h @@ -19,21 +19,17 @@ class WindowManagerServiceImpl : public InterfaceImpl { virtual ~WindowManagerServiceImpl(); void NotifyReady(); - void NotifyNodeFocused(Id new_focused_id, Id old_focused_id); + void NotifyViewFocused(Id new_focused_id, Id old_focused_id); void NotifyWindowActivated(Id new_active_id, Id old_active_id); private: // Overridden from WindowManagerService: - virtual void OpenWindow(const Callback& callback) MOJO_OVERRIDE; - virtual void OpenWindowWithURL( - const String& url, - const Callback& callback) MOJO_OVERRIDE; - virtual void SetCapture(Id node, + virtual void SetCapture(Id view, const Callback& callback) MOJO_OVERRIDE; - virtual void FocusWindow(Id node, + virtual void FocusWindow(Id view, const Callback& callback) MOJO_OVERRIDE; virtual void ActivateWindow( - Id node, + Id view, const Callback& callback) MOJO_OVERRIDE; // Overridden from InterfaceImpl: diff --git a/mojo/shell/BUILD.gn b/mojo/shell/BUILD.gn index f380958c6d04f..1038b6f38d54e 100644 --- a/mojo/shell/BUILD.gn +++ b/mojo/shell/BUILD.gn @@ -11,7 +11,7 @@ executable("mojo_shell") { "//base", "//mojo/common", "//mojo/environment:chromium", - "//mojo/service_manager", + "//mojo/application_manager", "//mojo/system", "//third_party/icu", "//ui/gl", @@ -33,11 +33,12 @@ source_set("lib") { "//base", "//base/third_party/dynamic_annotations", "//base:base_static", + "//mojo/application_manager", "//mojo/common", "//mojo/gles2", "//mojo/public/cpp/application:chromium", + "//mojo/public/gles2", "//mojo/public/interfaces/application", - "//mojo/service_manager", "//mojo/services/native_viewport", "//mojo/services/public/interfaces/native_viewport", "//mojo/services/public/interfaces/network", @@ -58,10 +59,10 @@ source_set("lib") { "child_process_host.h", "context.cc", "context.h", - "dbus_service_loader_linux.cc", - "dbus_service_loader_linux.h", - "dynamic_service_loader.cc", - "dynamic_service_loader.h", + "dbus_application_loader_linux.cc", + "dbus_application_loader_linux.h", + "dynamic_application_loader.cc", + "dynamic_application_loader.h", "dynamic_service_runner.h", "init.cc", "init.h", @@ -81,8 +82,8 @@ source_set("lib") { "task_runners.h", "test_child_process.cc", "test_child_process.h", - "ui_service_loader_android.cc", - "ui_service_loader_android.h", + "ui_application_loader_android.cc", + "ui_application_loader_android.h", ] if (is_linux) { @@ -94,15 +95,14 @@ source_set("lib") { "//mojo/services/network", ] sources += [ - "network_service_loader.cc", - "network_service_loader.h", + "network_application_loader.cc", + "network_application_loader.h", ] } if (use_aura) { deps += [ # These are only necessary as long as we hard code use of ViewManager. "//skia", - "//mojo/public/gles2", "//mojo/services/view_manager", "//mojo/services/public/interfaces/view_manager", ] @@ -124,3 +124,42 @@ mojom("external_service_bindings") { "external_service.mojom" ] } + +test("mojo_shell_tests") { + deps = [ + ":lib", + "//base", + "//base:i18n", + "//base/test:test_support", + "//testing/gtest", + "//net:test_support", + "//url", + "//mojo/application_manager", + "//mojo/services/test_service:bindings", + "//mojo/common", + "//mojo/environment:chromium", + "//mojo/system", + ] + + datadeps = [ + "//mojo/services/test_service:mojo_test_app", + "//mojo/services/test_service:mojo_test_request_tracker_app", + ] + + sources = [ + "child_process_host_unittest.cc", + "dynamic_application_loader_unittest.cc", + "in_process_dynamic_service_runner_unittest.cc", + "shell_test_base.cc", + "shell_test_base.h", + "shell_test_base_unittest.cc", + "shell_test_main.cc", + ] + + if (is_android) { + deps += [ + # TODO(GYP): + #'../testing/android/native_test.gyp:native_test_native_code', + ] + } +} diff --git a/mojo/shell/android/library_loader.cc b/mojo/shell/android/library_loader.cc index 37d6592521f4f..26a99a55ad297 100644 --- a/mojo/shell/android/library_loader.cc +++ b/mojo/shell/android/library_loader.cc @@ -7,7 +7,7 @@ #include "base/android/jni_registrar.h" #include "base/android/library_loader/library_loader_hooks.h" #include "base/logging.h" -#include "mojo/services/native_viewport/native_viewport_android.h" +#include "mojo/services/native_viewport/platform_viewport_android.h" #include "mojo/shell/android/mojo_main.h" #include "net/android/net_jni_registrar.h" @@ -15,7 +15,8 @@ namespace { base::android::RegistrationMethod kMojoRegisteredMethods[] = { { "MojoMain", mojo::RegisterMojoMain }, - { "NativeViewportAndroid", mojo::services::NativeViewportAndroid::Register }, + { "PlatformViewportAndroid", + mojo::PlatformViewportAndroid::Register }, }; bool RegisterMojoJni(JNIEnv* env) { diff --git a/mojo/shell/android/mojo_main.cc b/mojo/shell/android/mojo_main.cc index 57c329d514707..2f8924d878740 100644 --- a/mojo/shell/android/mojo_main.cc +++ b/mojo/shell/android/mojo_main.cc @@ -14,8 +14,8 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "jni/MojoMain_jni.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/service_manager/service_manager.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/shell/context.h" #include "mojo/shell/init.h" #include "mojo/shell/run.h" diff --git a/mojo/shell/context.cc b/mojo/shell/context.cc index 76cc7d6f548bf..a6585e2d701be 100644 --- a/mojo/shell/context.cc +++ b/mojo/shell/context.cc @@ -4,30 +4,34 @@ #include "mojo/shell/context.h" -#include "build/build_config.h" +#include + #include "base/command_line.h" #include "base/lazy_instance.h" #include "base/memory/scoped_vector.h" +#include "base/strings/string_split.h" +#include "build/build_config.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/application_manager/application_manager.h" +#include "mojo/application_manager/background_shell_application_loader.h" #include "mojo/embedder/embedder.h" -#include "mojo/gles2/gles2_support_impl.h" +#include "mojo/public/cpp/application/application_connection.h" +#include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/application_impl.h" -#include "mojo/service_manager/background_shell_service_loader.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/service_manager/service_manager.h" -#include "mojo/services/native_viewport/native_viewport_service.h" -#include "mojo/shell/dynamic_service_loader.h" +#include "mojo/services/native_viewport/native_viewport_impl.h" +#include "mojo/shell/dynamic_application_loader.h" #include "mojo/shell/in_process_dynamic_service_runner.h" #include "mojo/shell/out_of_process_dynamic_service_runner.h" #include "mojo/shell/switches.h" -#include "mojo/shell/ui_service_loader_android.h" +#include "mojo/shell/ui_application_loader_android.h" #include "mojo/spy/spy.h" #if defined(OS_LINUX) -#include "mojo/shell/dbus_service_loader_linux.h" +#include "mojo/shell/dbus_application_loader_linux.h" #endif // defined(OS_LINUX) #if defined(OS_ANDROID) -#include "mojo/shell/network_service_loader.h" +#include "mojo/shell/network_application_loader.h" #endif // defined(OS_ANDROID) #if defined(USE_AURA) @@ -49,7 +53,6 @@ class Setup { public: Setup() { embedder::Init(); - gles2::GLES2SupportImpl::Init(); } ~Setup() { @@ -61,26 +64,70 @@ class Setup { static base::LazyInstance::Leaky setup = LAZY_INSTANCE_INITIALIZER; +void InitContentHandlers(DynamicApplicationLoader* loader, + base::CommandLine* command_line) { + std::string handlers_spec = command_line->GetSwitchValueASCII( + switches::kContentHandlers); + if (handlers_spec.empty()) + return; + + std::vector parts; + base::SplitString(handlers_spec, ',', &parts); + if (parts.size() % 2 != 0) { + LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers + << ": must be a comma-separated list of mimetype/url pairs."; + return; + } + + for (size_t i = 0; i < parts.size(); i += 2) { + GURL url(parts[i + 1]); + if (!url.is_valid()) { + LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers + << ": '" << parts[i + 1] << "' is not a valid URL."; + return; + } + loader->RegisterContentHandler(parts[i], url); + } +} + } // namespace -class Context::NativeViewportServiceLoader : public ServiceLoader { +class Context::NativeViewportApplicationLoader + : public ApplicationLoader, + public ApplicationDelegate, + public InterfaceFactory { public: - NativeViewportServiceLoader() {} - virtual ~NativeViewportServiceLoader() {} + NativeViewportApplicationLoader() {} + virtual ~NativeViewportApplicationLoader() {} private: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE { - app_.reset(services::CreateNativeViewportService(shell_handle.Pass())); + // ApplicationLoader implementation. + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (shell_handle.is_valid()) + app_.reset(new ApplicationImpl(this, shell_handle.Pass())); + } + + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE {} + + // ApplicationDelegate implementation. + virtual bool ConfigureIncomingConnection( + mojo::ApplicationConnection* connection) OVERRIDE { + connection->AddService(this); + return true; } - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE { + // InterfaceFactory implementation. + virtual void Create(ApplicationConnection* connection, + InterfaceRequest request) OVERRIDE { + BindToRequest(new NativeViewportImpl, &request); } scoped_ptr app_; - DISALLOW_COPY_AND_ASSIGN(NativeViewportServiceLoader); + DISALLOW_COPY_AND_ASSIGN(NativeViewportApplicationLoader); }; Context::Context() { @@ -95,67 +142,70 @@ void Context::Init() { for (size_t i = 0; i < arraysize(kLocalMojoURLs); ++i) mojo_url_resolver_.AddLocalFileMapping(GURL(kLocalMojoURLs[i])); - base::CommandLine* cmdline = base::CommandLine::ForCurrentProcess(); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); scoped_ptr runner_factory; - if (cmdline->HasSwitch(switches::kEnableMultiprocess)) + if (command_line->HasSwitch(switches::kEnableMultiprocess)) runner_factory.reset(new OutOfProcessDynamicServiceRunnerFactory()); else runner_factory.reset(new InProcessDynamicServiceRunnerFactory()); - service_manager_.set_default_loader( - scoped_ptr( - new DynamicServiceLoader(this, runner_factory.Pass()))); + DynamicApplicationLoader* dynamic_application_loader = + new DynamicApplicationLoader(this, runner_factory.Pass()); + InitContentHandlers(dynamic_application_loader, command_line); + application_manager_.set_default_loader( + scoped_ptr(dynamic_application_loader)); + // The native viewport service synchronously waits for certain messages. If we // don't run it on its own thread we can easily deadlock. Long term native // viewport should run its own process so that this isn't an issue. #if defined(OS_ANDROID) - service_manager_.SetLoaderForURL( - scoped_ptr( - new UIServiceLoader( - scoped_ptr(new NativeViewportServiceLoader()), - this)), + application_manager_.SetLoaderForURL( + scoped_ptr(new UIApplicationLoader( + scoped_ptr(new NativeViewportApplicationLoader()), + this)), GURL("mojo:mojo_native_viewport_service")); #else { - scoped_ptr loader( - new BackgroundShellServiceLoader( - scoped_ptr(new NativeViewportServiceLoader()), + scoped_ptr loader( + new BackgroundShellApplicationLoader( + scoped_ptr( + new NativeViewportApplicationLoader()), "native_viewport", - base::MessageLoop::TYPE_UI)); - service_manager_.SetLoaderForURL( - loader.PassAs(), + base::MessageLoop::TYPE_UI)); + application_manager_.SetLoaderForURL( + loader.PassAs(), GURL("mojo:mojo_native_viewport_service")); } #endif #if defined(USE_AURA) // TODO(sky): need a better way to find this. It shouldn't be linked in. - service_manager_.SetLoaderForURL( - scoped_ptr(new ViewManagerLoader()), + application_manager_.SetLoaderForURL( + scoped_ptr(new ViewManagerLoader()), GURL("mojo:mojo_view_manager")); #endif #if defined(OS_LINUX) - service_manager_.SetLoaderForScheme( - scoped_ptr(new DBusServiceLoader(this)), - "dbus"); + application_manager_.SetLoaderForScheme( + scoped_ptr(new DBusApplicationLoader(this)), "dbus"); #endif // defined(OS_LINUX) - if (cmdline->HasSwitch(switches::kSpy)) { - spy_.reset(new mojo::Spy(&service_manager_, - cmdline->GetSwitchValueASCII(switches::kSpy))); + if (command_line->HasSwitch(switches::kSpy)) { + spy_.reset( + new mojo::Spy(&application_manager_, + command_line->GetSwitchValueASCII(switches::kSpy))); } #if defined(OS_ANDROID) // On android, the network service is bundled with the shell because the // network stack depends on the android runtime. { - scoped_ptr loader( - new BackgroundShellServiceLoader( - scoped_ptr(new NetworkServiceLoader()), + scoped_ptr loader( + new BackgroundShellApplicationLoader( + scoped_ptr(new NetworkApplicationLoader()), "network_service", - base::MessageLoop::TYPE_IO)); - service_manager_.SetLoaderForURL(loader.PassAs(), - GURL("mojo:mojo_network_service")); + base::MessageLoop::TYPE_IO)); + application_manager_.SetLoaderForURL(loader.PassAs(), + GURL("mojo:mojo_network_service")); } #endif } diff --git a/mojo/shell/context.h b/mojo/shell/context.h index 544f66ab771ce..3c33a7590e664 100644 --- a/mojo/shell/context.h +++ b/mojo/shell/context.h @@ -7,7 +7,7 @@ #include -#include "mojo/service_manager/service_manager.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/shell/keep_alive.h" #include "mojo/shell/mojo_url_resolver.h" #include "mojo/shell/task_runners.h" @@ -22,7 +22,7 @@ class Spy; namespace shell { -class DynamicServiceLoader; +class DynamicApplicationLoader; // The "global" context for the shell's main process. class Context { @@ -33,7 +33,7 @@ class Context { void Init(); TaskRunners* task_runners() { return task_runners_.get(); } - ServiceManager* service_manager() { return &service_manager_; } + ApplicationManager* application_manager() { return &application_manager_; } KeepAliveCounter* keep_alive_counter() { return &keep_alive_counter_; } MojoURLResolver* mojo_url_resolver() { return &mojo_url_resolver_; } @@ -43,10 +43,10 @@ class Context { #endif // defined(OS_ANDROID) private: - class NativeViewportServiceLoader; + class NativeViewportApplicationLoader; scoped_ptr task_runners_; - ServiceManager service_manager_; + ApplicationManager application_manager_; MojoURLResolver mojo_url_resolver_; scoped_ptr spy_; #if defined(OS_ANDROID) diff --git a/mojo/shell/dbus_application_loader_linux.cc b/mojo/shell/dbus_application_loader_linux.cc new file mode 100644 index 0000000000000..5d992742e4e17 --- /dev/null +++ b/mojo/shell/dbus_application_loader_linux.cc @@ -0,0 +1,187 @@ +// 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/dbus_application_loader_linux.h" + +#include + +#include "base/command_line.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/task_runner_util.h" +#include "base/threading/thread_restrictions.h" +#include "dbus/bus.h" +#include "dbus/file_descriptor.h" +#include "dbus/message.h" +#include "dbus/object_path.h" +#include "dbus/object_proxy.h" +#include "mojo/dbus/dbus_external_service.h" +#include "mojo/embedder/channel_init.h" +#include "mojo/embedder/platform_channel_pair.h" +#include "mojo/shell/context.h" +#include "mojo/shell/external_service.mojom.h" +#include "mojo/shell/keep_alive.h" + +namespace mojo { +namespace shell { + +// Manages the connection to a single externally-running service. +class DBusApplicationLoader::LoadContext { + public: + // Kicks off the attempt to bootstrap a connection to the externally-running + // service specified by url_. + // Creates a MessagePipe and passes one end over DBus to the service. Then, + // calls ExternalService::Activate(ShellHandle) over the now-shared pipe. + LoadContext(DBusApplicationLoader* loader, + const scoped_refptr& bus, + const GURL& url, + ScopedMessagePipeHandle service_provider_handle) + : loader_(loader), + bus_(bus), + service_dbus_proxy_(NULL), + url_(url), + service_provider_handle_(service_provider_handle.Pass()), + keep_alive_(loader->context_) { + base::PostTaskAndReplyWithResult( + loader_->context_->task_runners()->io_runner(), + FROM_HERE, + base::Bind(&LoadContext::CreateChannelOnIOThread, + base::Unretained(this)), + base::Bind(&LoadContext::ConnectChannel, base::Unretained(this))); + } + + virtual ~LoadContext() {} + + private: + // Sets up a pipe to share with the externally-running service and returns + // the endpoint that should be sent over DBus. + // The FD for the endpoint must be validated on an IO thread. + scoped_ptr CreateChannelOnIOThread() { + base::ThreadRestrictions::AssertIOAllowed(); + CHECK(bus_->Connect()); + CHECK(bus_->SetUpAsyncOperations()); + + embedder::PlatformChannelPair channel_pair; + channel_init_.reset(new embedder::ChannelInit); + mojo::ScopedMessagePipeHandle bootstrap_message_pipe = + channel_init_->Init(channel_pair.PassServerHandle().release().fd, + loader_->context_->task_runners()->io_runner()); + CHECK(bootstrap_message_pipe.is_valid()); + + external_service_.Bind(bootstrap_message_pipe.Pass()); + + scoped_ptr client_fd(new dbus::FileDescriptor); + client_fd->PutValue(channel_pair.PassClientHandle().release().fd); + client_fd->CheckValidity(); // Must be run on an IO thread. + return client_fd.Pass(); + } + + // Sends client_fd over to the externally-running service. If that + // attempt is successful, the service will then be "activated" by + // sending it a ShellHandle. + void ConnectChannel(scoped_ptr client_fd) { + size_t first_slash = url_.path().find_first_of('/'); + DCHECK_NE(first_slash, std::string::npos); + + const std::string service_name = url_.path().substr(0, first_slash); + const std::string object_path = url_.path().substr(first_slash); + service_dbus_proxy_ = + bus_->GetObjectProxy(service_name, dbus::ObjectPath(object_path)); + + dbus::MethodCall call(kMojoDBusInterface, kMojoDBusConnectMethod); + dbus::MessageWriter writer(&call); + writer.AppendFileDescriptor(*client_fd.get()); + + // TODO(cmasone): handle errors! + service_dbus_proxy_->CallMethod( + &call, + dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, + base::Bind(&LoadContext::ActivateService, base::Unretained(this))); + } + + // Sends a ShellHandle over to the now-connected externally-running service, + // using the Mojo ExternalService API. + void ActivateService(dbus::Response* response) { + external_service_->Activate(mojo::ScopedMessagePipeHandle( + mojo::MessagePipeHandle(service_provider_handle_.release().value()))); + } + + // Should the ExternalService disappear completely, destroy connection state. + // NB: This triggers off of the service disappearing from + // DBus. Perhaps there's a way to watch at the Mojo layer instead, + // and that would be superior? + void HandleNameOwnerChanged(const std::string& old_owner, + const std::string& new_owner) { + DCHECK(loader_->context_->task_runners() + ->shell_runner() + ->BelongsToCurrentThread()); + + if (new_owner.empty()) { + loader_->context_->task_runners()->shell_runner()->PostTask( + FROM_HERE, + base::Bind(&DBusApplicationLoader::ForgetService, + base::Unretained(loader_), + url_)); + } + } + + DBusApplicationLoader* const loader_; + scoped_refptr bus_; + dbus::ObjectProxy* service_dbus_proxy_; // Owned by bus_; + const GURL url_; + ScopedMessagePipeHandle service_provider_handle_; + KeepAlive keep_alive_; + scoped_ptr channel_init_; + ExternalServicePtr external_service_; + + DISALLOW_COPY_AND_ASSIGN(LoadContext); +}; + +DBusApplicationLoader::DBusApplicationLoader(Context* context) + : context_(context) { + dbus::Bus::Options options; + options.bus_type = dbus::Bus::SESSION; + options.dbus_task_runner = context_->task_runners()->io_runner(); + bus_ = new dbus::Bus(options); +} + +DBusApplicationLoader::~DBusApplicationLoader() { + DCHECK(url_to_load_context_.empty()); +} + +void DBusApplicationLoader::Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) { + // TODO(aa): This could be delayed until later, when we know that loading is + // going to succeed. + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; + + DCHECK(url.SchemeIs("dbus")); + DCHECK(url_to_load_context_.find(url) == url_to_load_context_.end()); + url_to_load_context_[url] = + new LoadContext(this, bus_, url, shell_handle.Pass()); +} + +void DBusApplicationLoader::OnServiceError(ApplicationManager* manager, + const GURL& url) { + // TODO(cmasone): Anything at all in this method here. +} + +void DBusApplicationLoader::ForgetService(const GURL& url) { + DCHECK(context_->task_runners()->shell_runner()->BelongsToCurrentThread()); + DVLOG(2) << "Forgetting service (url: " << url << ")"; + + LoadContextMap::iterator it = url_to_load_context_.find(url); + DCHECK(it != url_to_load_context_.end()) << url; + + LoadContext* doomed = it->second; + url_to_load_context_.erase(it); + + delete doomed; +} + +} // namespace shell +} // namespace mojo diff --git a/mojo/shell/dbus_application_loader_linux.h b/mojo/shell/dbus_application_loader_linux.h new file mode 100644 index 0000000000000..b065cd74570cb --- /dev/null +++ b/mojo/shell/dbus_application_loader_linux.h @@ -0,0 +1,89 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_DBUS_APPLICATION_LOADER_LINUX_H_ +#define MOJO_SHELL_DBUS_APPLICATION_LOADER_LINUX_H_ + +#include + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/shell/keep_alive.h" +#include "url/gurl.h" + +namespace dbus { +class Bus; +} // namespace dbus + +namespace mojo { +namespace shell { + +class Context; + +// An implementation of ApplicationLoader that contacts a system service +// and bootstraps a Mojo connection to it over DBus. +// +// In order to allow the externally-running service to accept connections from +// a Mojo shell, we need to get it a ShellHandle. This class creates a +// dedicated MessagePipe, passes a handle to one end to the desired service +// over DBus, and then passes the ShellHandle over that pipe. +// +// This class assumes the following: +// 1) Your service is already running. +// 2) Your service implements the Mojo ExternalService API +// (from external_service.mojom). +// 3) Your service exports an object that implements the org.chromium.Mojo DBus +// interface: +// +// +// +// +// +class DBusApplicationLoader : public ApplicationLoader { + public: + DBusApplicationLoader(Context* context); + virtual ~DBusApplicationLoader(); + + // URL for DBus services are of the following format: + // dbus:tld.domain.ServiceName/path/to/DBusObject + // + // This is simply the scheme (dbus:) and then the DBus service name followed + // by the DBus object path of an object that implements the org.chromium.Mojo + // interface as discussed above. + // + // Example: + // dbus:org.chromium.EchoService/org/chromium/MojoImpl + // + // This will tell DBusApplicationLoader to reach out to a service with + // the name "org.chromium.EchoService" and invoke the method + // "org.chromium.Mojo.ConnectChannel" on the object exported at + // "/org/chromium/MojoImpl". + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE; + + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE; + + private: + class LoadContext; + + // Tosses out connection-related state to service at given URL. + void ForgetService(const GURL& url); + + Context* const context_; + scoped_refptr bus_; + + typedef std::map LoadContextMap; + LoadContextMap url_to_load_context_; + + DISALLOW_COPY_AND_ASSIGN(DBusApplicationLoader); +}; + +} // namespace shell +} // namespace mojo + +#endif // MOJO_SHELL_DBUS_APPLICATION_LOADER_LINUX_H_ diff --git a/mojo/shell/dbus_service_loader_linux.cc b/mojo/shell/dbus_service_loader_linux.cc deleted file mode 100644 index af4e0d4422ed0..0000000000000 --- a/mojo/shell/dbus_service_loader_linux.cc +++ /dev/null @@ -1,181 +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/dbus_service_loader_linux.h" - -#include - -#include "base/command_line.h" -#include "base/location.h" -#include "base/logging.h" -#include "base/task_runner_util.h" -#include "base/threading/thread_restrictions.h" -#include "dbus/bus.h" -#include "dbus/file_descriptor.h" -#include "dbus/message.h" -#include "dbus/object_path.h" -#include "dbus/object_proxy.h" -#include "mojo/dbus/dbus_external_service.h" -#include "mojo/embedder/channel_init.h" -#include "mojo/embedder/platform_channel_pair.h" -#include "mojo/shell/context.h" -#include "mojo/shell/external_service.mojom.h" -#include "mojo/shell/keep_alive.h" - -namespace mojo { -namespace shell { - -// Manages the connection to a single externally-running service. -class DBusServiceLoader::LoadContext { - public: - // Kicks off the attempt to bootstrap a connection to the externally-running - // service specified by url_. - // Creates a MessagePipe and passes one end over DBus to the service. Then, - // calls ExternalService::Activate(ShellHandle) over the now-shared pipe. - LoadContext(DBusServiceLoader* loader, - const scoped_refptr& bus, - const GURL& url, - ScopedMessagePipeHandle service_provider_handle) - : loader_(loader), - bus_(bus), - service_dbus_proxy_(NULL), - url_(url), - service_provider_handle_(service_provider_handle.Pass()), - keep_alive_(loader->context_) { - base::PostTaskAndReplyWithResult( - loader_->context_->task_runners()->io_runner(), - FROM_HERE, - base::Bind(&LoadContext::CreateChannelOnIOThread, - base::Unretained(this)), - base::Bind(&LoadContext::ConnectChannel, base::Unretained(this))); - } - - virtual ~LoadContext() { - } - - private: - // Sets up a pipe to share with the externally-running service and returns - // the endpoint that should be sent over DBus. - // The FD for the endpoint must be validated on an IO thread. - scoped_ptr CreateChannelOnIOThread() { - base::ThreadRestrictions::AssertIOAllowed(); - CHECK(bus_->Connect()); - CHECK(bus_->SetUpAsyncOperations()); - - embedder::PlatformChannelPair channel_pair; - channel_init_.reset(new embedder::ChannelInit); - mojo::ScopedMessagePipeHandle bootstrap_message_pipe = - channel_init_->Init(channel_pair.PassServerHandle().release().fd, - loader_->context_->task_runners()->io_runner()); - CHECK(bootstrap_message_pipe.is_valid()); - - external_service_.Bind(bootstrap_message_pipe.Pass()); - - scoped_ptr client_fd(new dbus::FileDescriptor); - client_fd->PutValue(channel_pair.PassClientHandle().release().fd); - client_fd->CheckValidity(); // Must be run on an IO thread. - return client_fd.Pass(); - } - - // Sends client_fd over to the externally-running service. If that - // attempt is successful, the service will then be "activated" by - // sending it a ShellHandle. - void ConnectChannel(scoped_ptr client_fd) { - size_t first_slash = url_.path().find_first_of('/'); - DCHECK_NE(first_slash, std::string::npos); - - const std::string service_name = url_.path().substr(0, first_slash); - const std::string object_path = url_.path().substr(first_slash); - service_dbus_proxy_ = - bus_->GetObjectProxy(service_name, dbus::ObjectPath(object_path)); - - dbus::MethodCall call(kMojoDBusInterface, kMojoDBusConnectMethod); - dbus::MessageWriter writer(&call); - writer.AppendFileDescriptor(*client_fd.get()); - - // TODO(cmasone): handle errors! - service_dbus_proxy_->CallMethod( - &call, - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, - base::Bind(&LoadContext::ActivateService, base::Unretained(this))); - } - - // Sends a ShellHandle over to the now-connected externally-running service, - // using the Mojo ExternalService API. - void ActivateService(dbus::Response* response) { - external_service_->Activate( - mojo::ScopedMessagePipeHandle( - mojo::MessagePipeHandle( - service_provider_handle_.release().value()))); - } - - // Should the ExternalService disappear completely, destroy connection state. - // NB: This triggers off of the service disappearing from - // DBus. Perhaps there's a way to watch at the Mojo layer instead, - // and that would be superior? - void HandleNameOwnerChanged(const std::string& old_owner, - const std::string& new_owner) { - DCHECK(loader_->context_->task_runners()->shell_runner()-> - BelongsToCurrentThread()); - - if (new_owner.empty()) { - loader_->context_->task_runners()->shell_runner()->PostTask( - FROM_HERE, - base::Bind(&DBusServiceLoader::ForgetService, - base::Unretained(loader_), url_)); - } - } - - DBusServiceLoader* const loader_; - scoped_refptr bus_; - dbus::ObjectProxy* service_dbus_proxy_; // Owned by bus_; - const GURL url_; - ScopedMessagePipeHandle service_provider_handle_; - KeepAlive keep_alive_; - scoped_ptr channel_init_; - ExternalServicePtr external_service_; - - DISALLOW_COPY_AND_ASSIGN(LoadContext); -}; - -DBusServiceLoader::DBusServiceLoader(Context* context) : context_(context) { - dbus::Bus::Options options; - options.bus_type = dbus::Bus::SESSION; - options.dbus_task_runner = context_->task_runners()->io_runner(); - bus_ = new dbus::Bus(options); -} - -DBusServiceLoader::~DBusServiceLoader() { - DCHECK(url_to_load_context_.empty()); -} - -void DBusServiceLoader::LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - DCHECK(url.SchemeIs("dbus")); - DCHECK(url_to_load_context_.find(url) == url_to_load_context_.end()); - url_to_load_context_[url] = - new LoadContext(this, bus_, url, shell_handle.Pass()); -} - -void DBusServiceLoader::OnServiceError(ServiceManager* manager, - const GURL& url) { - // TODO(cmasone): Anything at all in this method here. -} - -void DBusServiceLoader::ForgetService(const GURL& url) { - DCHECK(context_->task_runners()->shell_runner()->BelongsToCurrentThread()); - DVLOG(2) << "Forgetting service (url: " << url << ")"; - - LoadContextMap::iterator it = url_to_load_context_.find(url); - DCHECK(it != url_to_load_context_.end()) << url; - - LoadContext* doomed = it->second; - url_to_load_context_.erase(it); - - delete doomed; -} - -} // namespace shell -} // namespace mojo diff --git a/mojo/shell/dbus_service_loader_linux.h b/mojo/shell/dbus_service_loader_linux.h deleted file mode 100644 index a24af5fdc5b8c..0000000000000 --- a/mojo/shell/dbus_service_loader_linux.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_DBUS_SERVICE_LOADER_H_ -#define MOJO_SHELL_DBUS_SERVICE_LOADER_H_ - -#include - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "mojo/public/cpp/system/core.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/shell/keep_alive.h" -#include "url/gurl.h" - -namespace dbus { -class Bus; -} // namespace dbus - -namespace mojo { -namespace shell { - -class Context; - -// An implementation of ServiceLoader that contacts a system service -// and bootstraps a Mojo connection to it over DBus. -// -// In order to allow the externally-running service to accept connections from -// a Mojo shell, we need to get it a ShellHandle. This class creates a -// dedicated MessagePipe, passes a handle to one end to the desired service -// over DBus, and then passes the ShellHandle over that pipe. -// -// This class assumes the following: -// 1) Your service is already running. -// 2) Your service implements the Mojo ExternalService API -// (from external_service.mojom). -// 3) Your service exports an object that implements the org.chromium.Mojo DBus -// interface: -// -// -// -// -// -class DBusServiceLoader : public ServiceLoader { - public: - DBusServiceLoader(Context* context); - virtual ~DBusServiceLoader(); - - // URL for DBus services are of the following format: - // dbus:tld.domain.ServiceName/path/to/DBusObject - // - // This is simply the scheme (dbus:) and then the DBus service name followed - // by the DBus object path of an object that implements the org.chromium.Mojo - // interface as discussed above. - // - // Example: - // dbus:org.chromium.EchoService/org/chromium/MojoImpl - // - // This will tell DBusServiceLoader to reach out to a service with - // the name "org.chromium.EchoService" and invoke the method - // "org.chromium.Mojo.ConnectChannel" on the object exported at - // "/org/chromium/MojoImpl". - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE; - - virtual void OnServiceError(ServiceManager* manager, const GURL& url) - OVERRIDE; - - private: - class LoadContext; - - // Tosses out connection-related state to service at given URL. - void ForgetService(const GURL& url); - - Context* const context_; - scoped_refptr bus_; - - typedef std::map LoadContextMap; - LoadContextMap url_to_load_context_; - - DISALLOW_COPY_AND_ASSIGN(DBusServiceLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_DBUS_SERVICE_LOADER_H_ diff --git a/mojo/shell/dynamic_application_loader.cc b/mojo/shell/dynamic_application_loader.cc new file mode 100644 index 0000000000000..b0d343e2c20e5 --- /dev/null +++ b/mojo/shell/dynamic_application_loader.cc @@ -0,0 +1,174 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/shell/dynamic_application_loader.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "mojo/common/common_type_converters.h" +#include "mojo/common/data_pipe_utils.h" +#include "mojo/services/public/interfaces/network/url_loader.mojom.h" +#include "mojo/shell/context.h" +#include "mojo/shell/keep_alive.h" +#include "mojo/shell/switches.h" +#include "net/base/filename_util.h" + +namespace mojo { +namespace shell { + +namespace { + +void RunLibraryComplete(DynamicServiceRunner* runner, + const base::FilePath& temp_file) { + delete runner; + if (!temp_file.empty()) + base::DeleteFile(temp_file, false); +} + +} // namespace + +DynamicApplicationLoader::DynamicApplicationLoader( + Context* context, + scoped_ptr runner_factory) + : context_(context), + runner_factory_(runner_factory.Pass()), + weak_ptr_factory_(this) { +} + +DynamicApplicationLoader::~DynamicApplicationLoader() { +} + +void DynamicApplicationLoader::RegisterContentHandler( + const std::string& mime_type, + const GURL& content_handler_url) { + mime_type_to_url_[mime_type] = content_handler_url; +} + +void DynamicApplicationLoader::Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) { + GURL resolved_url; + if (url.SchemeIs("mojo")) { + resolved_url = context_->mojo_url_resolver()->Resolve(url); + } else { + resolved_url = url; + } + + if (resolved_url.SchemeIsFile()) + LoadLocalService(resolved_url, callbacks); + else + LoadNetworkService(resolved_url, callbacks); +} + +void DynamicApplicationLoader::LoadLocalService( + const GURL& resolved_url, + scoped_refptr callbacks) { + base::FilePath path; + net::FileURLToFilePath(resolved_url, &path); + const bool kDeleteFileAfter = false; + + // Async for consistency with network case. + base::MessageLoop::current()->PostTask( + FROM_HERE, + base::Bind(&DynamicApplicationLoader::RunLibrary, + weak_ptr_factory_.GetWeakPtr(), + path, + callbacks, + kDeleteFileAfter, + base::PathExists(path))); +} + +void DynamicApplicationLoader::LoadNetworkService( + const GURL& resolved_url, + scoped_refptr callbacks) { + if (!network_service_) { + context_->application_manager()->ConnectToService( + GURL("mojo:mojo_network_service"), &network_service_); + } + if (!url_loader_) + network_service_->CreateURLLoader(Get(&url_loader_)); + + URLRequestPtr request(URLRequest::New()); + request->url = String::From(resolved_url); + request->auto_follow_redirects = true; + + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableCache)) { + request->bypass_cache = true; + } + + url_loader_->Start( + request.Pass(), + base::Bind(&DynamicApplicationLoader::OnLoadNetworkServiceComplete, + weak_ptr_factory_.GetWeakPtr(), + callbacks)); +} + +void DynamicApplicationLoader::OnLoadNetworkServiceComplete( + scoped_refptr callbacks, + URLResponsePtr response) { + if (response->error) { + LOG(ERROR) << "Error (" << response->error->code << ": " + << response->error->description << ") while fetching " + << response->url; + } + + MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(response->mime_type); + if (iter != mime_type_to_url_.end()) { + callbacks->LoadWithContentHandler(iter->second, response.Pass()); + return; + } + + base::FilePath file; + base::CreateTemporaryFile(&file); + + const bool kDeleteFileAfter = true; + common::CopyToFile(response->body.Pass(), + file, + context_->task_runners()->blocking_pool(), + base::Bind(&DynamicApplicationLoader::RunLibrary, + weak_ptr_factory_.GetWeakPtr(), + file, + callbacks, + kDeleteFileAfter)); +} + +void DynamicApplicationLoader::RunLibrary( + const base::FilePath& path, + scoped_refptr callbacks, + bool delete_file_after, + bool path_exists) { + // TODO(aa): We need to create a runner, even if we're not going to use it, + // because it getting destroyed is what causes shell to shut down. If we don't + // create this, in the case where shell never successfully creates even one + // app, then shell will never shut down, because no runners are ever + // destroyed. + scoped_ptr runner(runner_factory_->Create(context_)); + if (!path_exists) + return; + + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; + + DynamicServiceRunner* runner_raw = runner.release(); + runner_raw->Start(path, + shell_handle.Pass(), + base::Bind(&RunLibraryComplete, + base::Unretained(runner_raw), + delete_file_after ? path : base::FilePath())); +} + +void DynamicApplicationLoader::OnServiceError(ApplicationManager* manager, + const GURL& url) { + // TODO(darin): What should we do about service errors? This implies that + // the app closed its handle to the service manager. Maybe we don't care? +} + +} // namespace shell +} // namespace mojo diff --git a/mojo/shell/dynamic_application_loader.h b/mojo/shell/dynamic_application_loader.h new file mode 100644 index 0000000000000..b365904c8184e --- /dev/null +++ b/mojo/shell/dynamic_application_loader.h @@ -0,0 +1,72 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_DYNAMIC_APPLICATION_LOADER_H_ +#define MOJO_SHELL_DYNAMIC_APPLICATION_LOADER_H_ + +#include + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/public/cpp/system/core.h" +#include "mojo/services/public/interfaces/network/network_service.mojom.h" +#include "mojo/shell/dynamic_service_runner.h" +#include "mojo/shell/keep_alive.h" +#include "url/gurl.h" + +namespace mojo { +namespace shell { + +class Context; +class DynamicServiceRunnerFactory; + +// An implementation of ApplicationLoader that retrieves a dynamic library +// containing the implementation of the service and loads/runs it (via a +// DynamicServiceRunner). +class DynamicApplicationLoader : public ApplicationLoader { + public: + DynamicApplicationLoader( + Context* context, + scoped_ptr runner_factory); + virtual ~DynamicApplicationLoader(); + + void RegisterContentHandler(const std::string& mime_type, + const GURL& content_handler_url); + + // ApplicationLoader methods: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE; + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE; + + private: + typedef std::map MimeTypeToURLMap; + + void LoadLocalService(const GURL& resolved_url, + scoped_refptr callbacks); + void LoadNetworkService(const GURL& resolved_url, + scoped_refptr callbacks); + void OnLoadNetworkServiceComplete(scoped_refptr callbacks, + URLResponsePtr url_response); + void RunLibrary(const base::FilePath& response_file, + scoped_refptr callbacks, + bool delete_file_after, + bool response_path_exists); + + Context* const context_; + scoped_ptr runner_factory_; + NetworkServicePtr network_service_; + URLLoaderPtr url_loader_; + MimeTypeToURLMap mime_type_to_url_; + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DynamicApplicationLoader); +}; + +} // namespace shell +} // namespace mojo + +#endif // MOJO_SHELL_DYNAMIC_APPLICATION_LOADER_H_ diff --git a/mojo/shell/dynamic_application_loader_unittest.cc b/mojo/shell/dynamic_application_loader_unittest.cc new file mode 100644 index 0000000000000..953cbdd2ffbc7 --- /dev/null +++ b/mojo/shell/dynamic_application_loader_unittest.cc @@ -0,0 +1,88 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/scoped_temp_dir.h" +#include "mojo/shell/context.h" +#include "mojo/shell/dynamic_application_loader.h" +#include "mojo/shell/dynamic_service_runner.h" +#include "net/base/filename_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace shell { + +namespace { + +struct TestState { + TestState() : runner_was_started(false), runner_was_destroyed(false) {} + bool runner_was_started; + bool runner_was_destroyed; +}; + +class TestDynamicServiceRunner : public DynamicServiceRunner { + public: + explicit TestDynamicServiceRunner(TestState* state) : state_(state) {} + virtual ~TestDynamicServiceRunner() { + state_->runner_was_destroyed = true; + base::MessageLoop::current()->Quit(); + } + virtual void Start(const base::FilePath& app_path, + ScopedMessagePipeHandle service_handle, + const base::Closure& app_completed_callback) OVERRIDE { + state_->runner_was_started = true; + } + + private: + TestState* state_; +}; + +class TestDynamicServiceRunnerFactory : public DynamicServiceRunnerFactory { + public: + explicit TestDynamicServiceRunnerFactory(TestState* state) : state_(state) {} + virtual ~TestDynamicServiceRunnerFactory() {} + virtual scoped_ptr Create(Context* context) OVERRIDE { + return scoped_ptr( + new TestDynamicServiceRunner(state_)); + } + + private: + TestState* state_; +}; + +} // namespace + +class DynamicApplicationLoaderTest : public testing::Test { + public: + DynamicApplicationLoaderTest() {} + virtual ~DynamicApplicationLoaderTest() {} + virtual void SetUp() OVERRIDE { + context_.Init(); + scoped_ptr factory( + new TestDynamicServiceRunnerFactory(&state_)); + loader_.reset(new DynamicApplicationLoader(&context_, factory.Pass())); + } + + protected: + Context context_; + base::MessageLoop loop_; + scoped_ptr loader_; + TestState state_; +}; + +TEST_F(DynamicApplicationLoaderTest, DoesNotExist) { + base::ScopedTempDir temp_dir; + ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); + base::FilePath nonexistent_file(FILE_PATH_LITERAL("nonexistent.txt")); + GURL url(net::FilePathToFileURL(temp_dir.path().Append(nonexistent_file))); + MessagePipe pipe; + scoped_refptr callbacks( + new ApplicationLoader::SimpleLoadCallbacks(pipe.handle0.Pass())); + loader_->Load(context_.application_manager(), url, callbacks); + loop_.Run(); + EXPECT_FALSE(state_.runner_was_started); + EXPECT_TRUE(state_.runner_was_destroyed); +} + +} // namespace shell +} // namespace mojo diff --git a/mojo/shell/dynamic_service_loader.cc b/mojo/shell/dynamic_service_loader.cc deleted file mode 100644 index a8e6914ae9e5a..0000000000000 --- a/mojo/shell/dynamic_service_loader.cc +++ /dev/null @@ -1,185 +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/dynamic_service_loader.h" - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop.h" -#include "mojo/common/common_type_converters.h" -#include "mojo/common/data_pipe_utils.h" -#include "mojo/services/public/interfaces/network/url_loader.mojom.h" -#include "mojo/shell/context.h" -#include "mojo/shell/keep_alive.h" -#include "mojo/shell/switches.h" -#include "net/base/filename_util.h" - -namespace mojo { -namespace shell { -namespace { - -class Loader { - public: - explicit Loader(scoped_ptr runner) - : runner_(runner.Pass()) { - } - - virtual void Start(const GURL& url, - ScopedMessagePipeHandle service_handle, - Context* context) = 0; - - void StartService(const base::FilePath& path, - ScopedMessagePipeHandle service_handle, - bool path_is_valid) { - if (path_is_valid) { - runner_->Start(path, service_handle.Pass(), - base::Bind(&Loader::AppCompleted, base::Unretained(this))); - } else { - AppCompleted(); - } - } - - protected: - virtual ~Loader() {} - - private: - void AppCompleted() { - delete this; - } - - scoped_ptr runner_; -}; - -// For loading services via file:// URLs. -class LocalLoader : public Loader { - public: - explicit LocalLoader(scoped_ptr runner) - : Loader(runner.Pass()) { - } - - virtual void Start(const GURL& url, - ScopedMessagePipeHandle service_handle, - Context* context) OVERRIDE { - base::FilePath path; - net::FileURLToFilePath(url, &path); - - // Complete asynchronously for consistency with NetworkServiceLoader. - base::MessageLoop::current()->PostTask( - FROM_HERE, - base::Bind(&Loader::StartService, - base::Unretained(this), - path, - base::Passed(&service_handle), - base::PathExists(path))); - } -}; - -// For loading services via the network stack. -class NetworkLoader : public Loader { - public: - explicit NetworkLoader(scoped_ptr runner, - NetworkService* network_service) - : Loader(runner.Pass()) { - network_service->CreateURLLoader(Get(&url_loader_)); - } - - virtual void Start(const GURL& url, - ScopedMessagePipeHandle service_handle, - Context* context) OVERRIDE { - service_handle_ = service_handle.Pass(); - context_ = context; - - URLRequestPtr request(URLRequest::New()); - request->url = String::From(url); - request->auto_follow_redirects = true; - - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableCache)) { - request->bypass_cache = true; - } - - url_loader_->Start(request.Pass(), - base::Bind(&NetworkLoader::OnReceivedResponse, - base::Unretained(this))); - } - - private: - virtual ~NetworkLoader() { - if (!file_.empty()) - base::DeleteFile(file_, false); - } - - void OnReceivedResponse(URLResponsePtr response) { - if (response->error) { - LOG(ERROR) << "Error (" << response->error->code << ": " - << response->error->description << ") while fetching " - << response->url; - } - - base::CreateTemporaryFile(&file_); - common::CopyToFile(response->body.Pass(), - file_, - context_->task_runners()->blocking_pool(), - base::Bind(&Loader::StartService, - base::Unretained(this), - file_, - base::Passed(&service_handle_))); - } - - Context* context_; - NetworkServicePtr network_service_; - URLLoaderPtr url_loader_; - ScopedMessagePipeHandle service_handle_; - base::FilePath file_; -}; - -} // namespace - -DynamicServiceLoader::DynamicServiceLoader( - Context* context, - scoped_ptr runner_factory) - : context_(context), - runner_factory_(runner_factory.Pass()) { -} - -DynamicServiceLoader::~DynamicServiceLoader() { -} - -void DynamicServiceLoader::LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - scoped_ptr runner = runner_factory_->Create(context_); - - GURL resolved_url; - if (url.SchemeIs("mojo")) { - resolved_url = context_->mojo_url_resolver()->Resolve(url); - } else { - resolved_url = url; - } - - Loader* loader; - if (resolved_url.SchemeIsFile()) { - loader = new LocalLoader(runner.Pass()); - } else { - if (!network_service_) { - context_->service_manager()->ConnectToService( - GURL("mojo:mojo_network_service"), - &network_service_); - } - loader = new NetworkLoader(runner.Pass(), network_service_.get()); - } - loader->Start(resolved_url, shell_handle.Pass(), context_); -} - -void DynamicServiceLoader::OnServiceError(ServiceManager* manager, - const GURL& url) { - // TODO(darin): What should we do about service errors? This implies that - // the app closed its handle to the service manager. Maybe we don't care? -} - -} // namespace shell -} // namespace mojo diff --git a/mojo/shell/dynamic_service_loader.h b/mojo/shell/dynamic_service_loader.h deleted file mode 100644 index 72bf093e56e09..0000000000000 --- a/mojo/shell/dynamic_service_loader.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_DYNAMIC_SERVICE_LOADER_H_ -#define MOJO_SHELL_DYNAMIC_SERVICE_LOADER_H_ - -#include - -#include "base/macros.h" -#include "mojo/public/cpp/system/core.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/services/public/interfaces/network/network_service.mojom.h" -#include "mojo/shell/dynamic_service_runner.h" -#include "mojo/shell/keep_alive.h" -#include "url/gurl.h" - -namespace mojo { -namespace shell { - -class Context; -class DynamicServiceRunnerFactory; - -// An implementation of ServiceLoader that retrieves a dynamic library -// containing the implementation of the service and loads/runs it (via a -// DynamicServiceRunner). -class DynamicServiceLoader : public ServiceLoader { - public: - DynamicServiceLoader(Context* context, - scoped_ptr runner_factory); - virtual ~DynamicServiceLoader(); - - // ServiceLoader methods: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle service_handle) OVERRIDE; - virtual void OnServiceError(ServiceManager* manager, const GURL& url) - OVERRIDE; - - private: - Context* const context_; - scoped_ptr runner_factory_; - NetworkServicePtr network_service_; - - DISALLOW_COPY_AND_ASSIGN(DynamicServiceLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_DYNAMIC_SERVICE_LOADER_H_ diff --git a/mojo/shell/dynamic_service_loader_unittest.cc b/mojo/shell/dynamic_service_loader_unittest.cc deleted file mode 100644 index f70d11506c20b..0000000000000 --- a/mojo/shell/dynamic_service_loader_unittest.cc +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/files/scoped_temp_dir.h" -#include "mojo/shell/context.h" -#include "mojo/shell/dynamic_service_loader.h" -#include "mojo/shell/dynamic_service_runner.h" -#include "net/base/filename_util.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace shell { - -namespace { - -struct TestState { - TestState() : runner_was_started(false), runner_was_destroyed(false) {} - bool runner_was_started; - bool runner_was_destroyed; -}; - -class TestDynamicServiceRunner : public DynamicServiceRunner { - public: - explicit TestDynamicServiceRunner(TestState* state) : state_(state) {} - virtual ~TestDynamicServiceRunner() { - state_->runner_was_destroyed = true; - base::MessageLoop::current()->Quit(); - } - virtual void Start(const base::FilePath& app_path, - ScopedMessagePipeHandle service_handle, - const base::Closure& app_completed_callback) OVERRIDE { - state_->runner_was_started = true; - } - private: - TestState* state_; -}; - -class TestDynamicServiceRunnerFactory : public DynamicServiceRunnerFactory { - public: - explicit TestDynamicServiceRunnerFactory(TestState* state) : state_(state) {} - virtual ~TestDynamicServiceRunnerFactory() {} - virtual scoped_ptr Create(Context* context) OVERRIDE { - return scoped_ptr( - new TestDynamicServiceRunner(state_)); - } - private: - TestState* state_; -}; - -} // namespace - -class DynamicServiceLoaderTest : public testing::Test { - public: - DynamicServiceLoaderTest() {} - virtual ~DynamicServiceLoaderTest() {} - virtual void SetUp() OVERRIDE { - context_.Init(); - scoped_ptr factory( - new TestDynamicServiceRunnerFactory(&state_)); - loader_.reset(new DynamicServiceLoader(&context_, factory.Pass())); - } - protected: - Context context_; - base::MessageLoop loop_; - scoped_ptr loader_; - TestState state_; -}; - -TEST_F(DynamicServiceLoaderTest, DoesNotExist) { - base::ScopedTempDir temp_dir; - ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); - base::FilePath nonexistent_file(FILE_PATH_LITERAL("nonexistent.txt")); - GURL url(net::FilePathToFileURL(temp_dir.path().Append(nonexistent_file))); - MessagePipe pipe; - loader_->LoadService(context_.service_manager(), url, pipe.handle0.Pass()); - loop_.Run(); - EXPECT_FALSE(state_.runner_was_started); - EXPECT_TRUE(state_.runner_was_destroyed); -} - -} // namespace shell -} // namespace mojo diff --git a/mojo/shell/in_process_dynamic_service_runner.cc b/mojo/shell/in_process_dynamic_service_runner.cc index 3bfb163462ce5..8b38239172cad 100644 --- a/mojo/shell/in_process_dynamic_service_runner.cc +++ b/mojo/shell/in_process_dynamic_service_runner.cc @@ -9,11 +9,37 @@ #include "base/location.h" #include "base/logging.h" #include "base/message_loop/message_loop_proxy.h" +#include "mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h" +#include "mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h" +#include "mojo/public/platform/native/gles2_impl_thunks.h" +#include "mojo/public/platform/native/gles2_thunks.h" #include "mojo/public/platform/native/system_thunks.h" namespace mojo { namespace shell { +namespace { + +template +bool SetThunks(Thunks (*make_thunks)(), + const char* function_name, + base::ScopedNativeLibrary* library) { + typedef size_t (*SetThunksFn)(const Thunks* thunks); + SetThunksFn set_thunks = + reinterpret_cast(library->GetFunctionPointer(function_name)); + if (!set_thunks) + return false; + Thunks thunks = make_thunks(); + size_t expected_size = set_thunks(&thunks); + if (expected_size > sizeof(Thunks)) { + LOG(ERROR) << "Invalid app library: expected " << function_name + << " to return thunks of size: " << expected_size; + return false; + } + return true; +} +} + InProcessDynamicServiceRunner::InProcessDynamicServiceRunner( Context* context) : keep_alive_(context) { @@ -65,19 +91,8 @@ void InProcessDynamicServiceRunner::Run() { break; } - MojoSetSystemThunksFn mojo_set_system_thunks_fn = - reinterpret_cast(app_library_.GetFunctionPointer( - "MojoSetSystemThunks")); - if (mojo_set_system_thunks_fn) { - MojoSystemThunks system_thunks = MojoMakeSystemThunks(); - size_t expected_size = mojo_set_system_thunks_fn(&system_thunks); - if (expected_size > sizeof(MojoSystemThunks)) { - LOG(ERROR) - << "Invalid app library: expected MojoSystemThunks size: " - << expected_size; - break; - } - } else { + if (!SetThunks( + &MojoMakeSystemThunks, "MojoSetSystemThunks", &app_library_)) { // In the component build, Mojo Apps link against mojo_system_impl. #if !defined(COMPONENT_BUILD) // Strictly speaking this is not required, but it's very unusual to have @@ -86,6 +101,36 @@ void InProcessDynamicServiceRunner::Run() { #endif } + if (SetThunks(&MojoMakeGLES2ControlThunks, + "MojoSetGLES2ControlThunks", + &app_library_)) { + // If we have the control thunks, we probably also have the + // GLES2 implementation thunks. + if (!SetThunks(&MojoMakeGLES2ImplThunks, + "MojoSetGLES2ImplThunks", + &app_library_)) { + // In the component build, Mojo Apps link against mojo_gles2_impl. +#if !defined(COMPONENT_BUILD) + // Warn on this really weird case: The library requires the GLES2 + // control functions, but doesn't require the GLES2 implementation. + LOG(WARNING) << "App library has MojoSetGLES2ControlThunks, but " + "doesn't have MojoSetGLES2ImplThunks."; +#endif + } + + // If the application is using GLES2 extension points, register those + // thunks. Applications may use or not use any of these, so don't warn if + // they are missing. + SetThunks(MojoMakeGLES2ImplChromiumTextureMailboxThunks, + "MojoSetGLES2ImplChromiumTextureMailboxThunks", + &app_library_); + SetThunks(MojoMakeGLES2ImplChromiumSyncPointThunks, + "MojoSetGLES2ImplChromiumSyncPointThunks", + &app_library_); + } + // Unlike system thunks, we don't warn on a lack of GLES2 thunks because + // not everything is a visual app. + typedef MojoResult (*MojoMainFunction)(MojoHandle); MojoMainFunction main_function = reinterpret_cast( app_library_.GetFunctionPointer("MojoMain")); diff --git a/mojo/shell/network_application_loader.cc b/mojo/shell/network_application_loader.cc new file mode 100644 index 0000000000000..1a7cf3c12ba81 --- /dev/null +++ b/mojo/shell/network_application_loader.cc @@ -0,0 +1,70 @@ +// 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/network_application_loader.h" + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "mojo/public/cpp/application/application_connection.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/application_impl.h" +#include "mojo/services/network/network_service_impl.h" + +namespace { +base::FilePath GetBasePath() { + base::FilePath path; + CHECK(PathService::Get(base::DIR_TEMP, &path)); + return path.Append(FILE_PATH_LITERAL("network_service")); +} +} + +namespace mojo { +namespace shell { + +NetworkApplicationLoader::NetworkApplicationLoader() { +} + +NetworkApplicationLoader::~NetworkApplicationLoader() { +} + +void NetworkApplicationLoader::Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; + + uintptr_t key = reinterpret_cast(manager); + if (apps_.find(key) == apps_.end()) { + scoped_ptr app( + new ApplicationImpl(this, shell_handle.Pass())); + apps_.add(key, app.Pass()); + } +} + +void NetworkApplicationLoader::OnServiceError(ApplicationManager* manager, + const GURL& url) { + apps_.erase(reinterpret_cast(manager)); +} + +void NetworkApplicationLoader::Initialize(ApplicationImpl* app) { + // The context must be created on the same thread as the network service. + context_.reset(new NetworkContext(GetBasePath())); +} + +bool NetworkApplicationLoader::ConfigureIncomingConnection( + ApplicationConnection* connection) { + connection->AddService(this); + return true; +} + +void NetworkApplicationLoader::Create( + ApplicationConnection* connection, + InterfaceRequest request) { + BindToRequest(new NetworkServiceImpl(connection, context_.get()), &request); +} + +} // namespace shell +} // namespace mojo diff --git a/mojo/shell/network_application_loader.h b/mojo/shell/network_application_loader.h new file mode 100644 index 0000000000000..f8ff2919eaf45 --- /dev/null +++ b/mojo/shell/network_application_loader.h @@ -0,0 +1,58 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_NETWORK_APPLICATION_LOADER_H_ +#define MOJO_SHELL_NETWORK_APPLICATION_LOADER_H_ + +#include + +#include "base/containers/scoped_ptr_hash_map.h" +#include "base/memory/scoped_ptr.h" +#include "mojo/application_manager/application_loader.h" +#include "mojo/public/cpp/application/application_delegate.h" +#include "mojo/public/cpp/application/interface_factory.h" +#include "mojo/services/network/network_context.h" + +namespace mojo { + +class ApplicationImpl; +class NetworkService; + +namespace shell { + +// ApplicationLoader responsible for creating connections to the NetworkService. +class NetworkApplicationLoader : public ApplicationLoader, + public ApplicationDelegate, + public InterfaceFactory { + public: + NetworkApplicationLoader(); + virtual ~NetworkApplicationLoader(); + + private: + // ApplicationLoader overrides: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE; + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE; + + // ApplicationDelegate overrides. + virtual void Initialize(ApplicationImpl* app) OVERRIDE; + virtual bool ConfigureIncomingConnection( + ApplicationConnection* connection) OVERRIDE; + + // InterfaceFactory overrides. + virtual void Create(ApplicationConnection* connection, + InterfaceRequest request) OVERRIDE; + + base::ScopedPtrHashMap apps_; + scoped_ptr context_; + + DISALLOW_COPY_AND_ASSIGN(NetworkApplicationLoader); +}; + +} // namespace shell +} // namespace mojo + +#endif // MOJO_SHELL_NETWORK_APPLICATION_LOADER_H_ diff --git a/mojo/shell/network_service_loader.cc b/mojo/shell/network_service_loader.cc deleted file mode 100644 index 9f8f87f0b7f62..0000000000000 --- a/mojo/shell/network_service_loader.cc +++ /dev/null @@ -1,66 +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/network_service_loader.h" - -#include "base/base_paths.h" -#include "base/files/file_path.h" -#include "base/path_service.h" -#include "mojo/public/cpp/application/application_connection.h" -#include "mojo/public/cpp/application/application_delegate.h" -#include "mojo/public/cpp/application/application_impl.h" -#include "mojo/services/network/network_service_impl.h" - -namespace { -base::FilePath GetBasePath() { - base::FilePath path; - CHECK(PathService::Get(base::DIR_TEMP, &path)); - return path.Append(FILE_PATH_LITERAL("network_service")); -} -} - -namespace mojo { -namespace shell { - -NetworkServiceLoader::NetworkServiceLoader() { -} - -NetworkServiceLoader::~NetworkServiceLoader() { -} - -void NetworkServiceLoader::LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - uintptr_t key = reinterpret_cast(manager); - if (apps_.find(key) == apps_.end()) { - scoped_ptr app( - new ApplicationImpl(this, shell_handle.Pass())); - apps_.add(key, app.Pass()); - } -} - -void NetworkServiceLoader::OnServiceError(ServiceManager* manager, - const GURL& url) { - apps_.erase(reinterpret_cast(manager)); -} - -void NetworkServiceLoader::Initialize(ApplicationImpl* app) { - // The context must be created on the same thread as the network service. - context_.reset(new NetworkContext(GetBasePath())); -} - -bool NetworkServiceLoader::ConfigureIncomingConnection( - ApplicationConnection* connection) { - connection->AddService(this); - return true; -} - -void NetworkServiceLoader::Create(ApplicationConnection* connection, - InterfaceRequest request) { - BindToRequest(new NetworkServiceImpl(connection, context_.get()), &request); -} - -} // namespace shell -} // namespace mojo diff --git a/mojo/shell/network_service_loader.h b/mojo/shell/network_service_loader.h deleted file mode 100644 index 2184072f6152a..0000000000000 --- a/mojo/shell/network_service_loader.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_NETWORK_SERVICE_LOADER_H_ -#define MOJO_SHELL_NETWORK_SERVICE_LOADER_H_ - -#include - -#include "base/containers/scoped_ptr_hash_map.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/public/cpp/application/application_delegate.h" -#include "mojo/public/cpp/application/interface_factory.h" -#include "mojo/service_manager/service_loader.h" -#include "mojo/services/network/network_context.h" - -namespace mojo { - -class ApplicationImpl; -class NetworkService; - -namespace shell { - -// ServiceLoader responsible for creating connections to the NetworkService. -class NetworkServiceLoader : public ServiceLoader, - public ApplicationDelegate, - public InterfaceFactory { - public: - NetworkServiceLoader(); - virtual ~NetworkServiceLoader(); - - private: - // ServiceLoader overrides: - virtual void LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE; - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE; - - // ApplicationDelegate overrides. - virtual void Initialize(ApplicationImpl* app) OVERRIDE; - virtual bool ConfigureIncomingConnection(ApplicationConnection* connection) - OVERRIDE; - - // InterfaceFactory overrides. - virtual void Create(ApplicationConnection* connection, - InterfaceRequest request) OVERRIDE; - - base::ScopedPtrHashMap apps_; - scoped_ptr context_; - - DISALLOW_COPY_AND_ASSIGN(NetworkServiceLoader); -}; - -} // namespace shell -} // namespace mojo - -#endif // MOJO_SHELL_NETWORK_SERVICE_LOADER_H_ diff --git a/mojo/shell/run.cc b/mojo/shell/run.cc index 7cd120016f2e1..733b19132de21 100644 --- a/mojo/shell/run.cc +++ b/mojo/shell/run.cc @@ -5,7 +5,7 @@ #include "mojo/shell/run.h" #include "base/logging.h" -#include "mojo/service_manager/service_manager.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/shell/context.h" #include "mojo/shell/keep_alive.h" @@ -37,7 +37,7 @@ void Run(Context* context, const std::vector& app_urls) { ServiceProviderPtr spp; BindToProxy(stub_sp, &spp); - context->service_manager()->ConnectToApplication( + context->application_manager()->ConnectToApplication( *it, GURL(), spp.Pass()); } } diff --git a/mojo/shell/shell_test_base.cc b/mojo/shell/shell_test_base.cc index 0fc57a24fb9ab..da643a0d00ec2 100644 --- a/mojo/shell/shell_test_base.cc +++ b/mojo/shell/shell_test_base.cc @@ -39,8 +39,9 @@ ScopedMessagePipeHandle ShellTestBase::ConnectToServiceViaNetwork( shell_context_.mojo_url_resolver()->SetBaseURL( test_server_->base_url()); - return shell_context_.service_manager()->ConnectToServiceByName( - application_url, service_name).Pass(); + return shell_context_.application_manager() + ->ConnectToServiceByName(application_url, service_name) + .Pass(); } ScopedMessagePipeHandle ShellTestBase::ConnectToService( @@ -55,8 +56,9 @@ ScopedMessagePipeHandle ShellTestBase::ConnectToService( shell_context_.mojo_url_resolver()->SetBaseURL( net::FilePathToFileURL(service_dir)); - return shell_context_.service_manager()->ConnectToServiceByName( - application_url, service_name).Pass(); + return shell_context_.application_manager() + ->ConnectToServiceByName(application_url, service_name) + .Pass(); } } // namespace test diff --git a/mojo/shell/shell_test_base_unittest.cc b/mojo/shell/shell_test_base_unittest.cc index 9812997f9f27b..18d1a4e655a13 100644 --- a/mojo/shell/shell_test_base_unittest.cc +++ b/mojo/shell/shell_test_base_unittest.cc @@ -156,7 +156,7 @@ TEST_F(ShellTestBaseTest, DISABLED_ConnectBasicNetwork) { // TODO(tim): crbug.com/392685. Calling this explicitly shouldn't be // necessary once the shell terminates if the primordial app exits, which // we could enforce here by resetting |service|. - shell_context()->service_manager()->TerminateShellConnections(); + shell_context()->application_manager()->TerminateShellConnections(); message_loop()->Run(); // Waits for all connections to die. } @@ -178,7 +178,7 @@ TEST_F(ShellTestBaseTest, DISABLED_ConnectInvalidServiceNetwork) { // TODO(tim): crbug.com/392685. Calling this explicitly shouldn't be // necessary once the shell terminates if the primordial app exits, which // we could enforce here by resetting |service|. - shell_context()->service_manager()->TerminateShellConnections(); + shell_context()->application_manager()->TerminateShellConnections(); message_loop()->Run(); // Waits for all connections to die. } diff --git a/mojo/shell/shell_test_helper.cc b/mojo/shell/shell_test_helper.cc index 52eebc9d14716..691fe76224d39 100644 --- a/mojo/shell/shell_test_helper.cc +++ b/mojo/shell/shell_test_helper.cc @@ -25,16 +25,17 @@ ShellTestHelper::~ShellTestHelper() { void ShellTestHelper::Init() { context_.Init(); - test_api_.reset(new ServiceManager::TestAPI(context_.service_manager())); + test_api_.reset( + new ApplicationManager::TestAPI(context_.application_manager())); base::FilePath service_dir; CHECK(PathService::Get(base::DIR_MODULE, &service_dir)); context_.mojo_url_resolver()->SetBaseURL( net::FilePathToFileURL(service_dir)); } -void ShellTestHelper::SetLoaderForURL(scoped_ptr loader, +void ShellTestHelper::SetLoaderForURL(scoped_ptr loader, const GURL& url) { - context_.service_manager()->SetLoaderForURL(loader.Pass(), url); + context_.application_manager()->SetLoaderForURL(loader.Pass(), url); } } // namespace shell diff --git a/mojo/shell/shell_test_helper.h b/mojo/shell/shell_test_helper.h index e83bf2640d09d..38fb008a7b9a8 100644 --- a/mojo/shell/shell_test_helper.h +++ b/mojo/shell/shell_test_helper.h @@ -8,20 +8,20 @@ #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/run_loop.h" -#include "mojo/service_manager/service_loader.h" +#include "mojo/application_manager/application_loader.h" #include "mojo/shell/context.h" class GURL; namespace mojo { -class ServiceLoader; +class ApplicationLoader; namespace shell { // ShellTestHelper is useful for tests to establish a connection to the -// ServiceManager. Invoke Init() to establish the connection. Once done, -// service_manager() returns the ServiceManager. +// ApplicationManager. Invoke Init() to establish the connection. Once done, +// application_manager() returns the ApplicationManager. class ShellTestHelper { public: ShellTestHelper(); @@ -29,16 +29,19 @@ class ShellTestHelper { void Init(); - ServiceManager* service_manager() { return context_.service_manager(); } + ApplicationManager* application_manager() { + return context_.application_manager(); + } - // Sets a ServiceLoader for the specified URL. |loader| is ultimately used on + // Sets a ApplicationLoader for the specified URL. |loader| is ultimately used + // on // the thread this class spawns. - void SetLoaderForURL(scoped_ptr loader, const GURL& url); + void SetLoaderForURL(scoped_ptr loader, const GURL& url); private: Context context_; base::MessageLoop shell_loop_; - scoped_ptr test_api_; + scoped_ptr test_api_; DISALLOW_COPY_AND_ASSIGN(ShellTestHelper); }; diff --git a/mojo/shell/switches.cc b/mojo/shell/switches.cc index c597218f412db..cf3697f645a76 100644 --- a/mojo/shell/switches.cc +++ b/mojo/shell/switches.cc @@ -10,6 +10,10 @@ namespace switches { // |ChildProcess::Type|). const char kChildProcessType[] = "child-process-type"; +// Comma separated list like: +// text/html,mojo://mojo_html_viewer,application/bravo,https://abarth.com/bravo +const char kContentHandlers[] = "content-handlers"; + // Force dynamically loaded apps / services to be loaded irrespective of cache // instructions. const char kDisableCache[] = "disable-cache"; diff --git a/mojo/shell/switches.h b/mojo/shell/switches.h index a1d4ac578f9f9..d2271cf03b2ce 100644 --- a/mojo/shell/switches.h +++ b/mojo/shell/switches.h @@ -10,6 +10,7 @@ namespace switches { // All switches in alphabetical order. The switches should be documented // alongside the definition of their values in the .cc file. extern const char kChildProcessType[]; +extern const char kContentHandlers[]; extern const char kDisableCache[]; extern const char kEnableMultiprocess[]; extern const char kOrigin[]; diff --git a/mojo/shell/ui_application_loader_android.cc b/mojo/shell/ui_application_loader_android.cc new file mode 100644 index 0000000000000..d56211e78b935 --- /dev/null +++ b/mojo/shell/ui_application_loader_android.cc @@ -0,0 +1,97 @@ +// 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/ui_application_loader_android.h" + +#include "base/bind.h" +#include "mojo/application_manager/application_manager.h" +#include "mojo/shell/context.h" + +namespace mojo { + +class UIApplicationLoader::UILoader { + public: + explicit UILoader(ApplicationLoader* loader) : loader_(loader) {} + ~UILoader() {} + + void Load(ApplicationManager* manager, + const GURL& url, + ScopedMessagePipeHandle shell_handle) { + scoped_refptr callbacks( + new ApplicationLoader::SimpleLoadCallbacks(shell_handle.Pass())); + loader_->Load(manager, url, callbacks); + } + + void OnServiceError(ApplicationManager* manager, const GURL& url) { + loader_->OnServiceError(manager, url); + } + + private: + ApplicationLoader* loader_; // Owned by UIApplicationLoader + + DISALLOW_COPY_AND_ASSIGN(UILoader); +}; + +UIApplicationLoader::UIApplicationLoader( + scoped_ptr real_loader, + shell::Context* context) + : loader_(real_loader.Pass()), context_(context) { +} + +UIApplicationLoader::~UIApplicationLoader() { + context_->ui_loop()->PostTask( + FROM_HERE, + base::Bind(&UIApplicationLoader::ShutdownOnUIThread, + base::Unretained(this))); +} + +void UIApplicationLoader::Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; + context_->ui_loop()->PostTask( + FROM_HERE, + base::Bind( + &UIApplicationLoader::LoadOnUIThread, + base::Unretained(this), + manager, + url, + base::Owned(new ScopedMessagePipeHandle(shell_handle.Pass())))); +} + +void UIApplicationLoader::OnServiceError(ApplicationManager* manager, + const GURL& url) { + context_->ui_loop()->PostTask( + FROM_HERE, + base::Bind(&UIApplicationLoader::OnServiceErrorOnUIThread, + base::Unretained(this), + manager, + url)); +} + +void UIApplicationLoader::LoadOnUIThread( + ApplicationManager* manager, + const GURL& url, + ScopedMessagePipeHandle* shell_handle) { + if (!ui_loader_) + ui_loader_ = new UILoader(loader_.get()); + ui_loader_->Load(manager, url, shell_handle->Pass()); +} + +void UIApplicationLoader::OnServiceErrorOnUIThread(ApplicationManager* manager, + const GURL& url) { + if (!ui_loader_) + ui_loader_ = new UILoader(loader_.get()); + ui_loader_->OnServiceError(manager, url); +} + +void UIApplicationLoader::ShutdownOnUIThread() { + delete ui_loader_; + // Destroy |loader_| on the thread it's actually used on. + loader_.reset(); +} + +} // namespace mojo diff --git a/mojo/shell/ui_application_loader_android.h b/mojo/shell/ui_application_loader_android.h new file mode 100644 index 0000000000000..3195b8e0f9cbb --- /dev/null +++ b/mojo/shell/ui_application_loader_android.h @@ -0,0 +1,59 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_SHELL_UI_APPLICATION_LOADER_ANDROID_H_ +#define MOJO_SHELL_UI_APPLICATION_LOADER_ANDROID_H_ + +#include "base/memory/scoped_ptr.h" +#include "mojo/application_manager/application_loader.h" + +namespace mojo { + +class ApplicationManager; + +namespace shell { +class Context; +} + +// ApplicationLoader implementation that creates a background thread and issues +// load +// requests there. +class UIApplicationLoader : public ApplicationLoader { + public: + UIApplicationLoader(scoped_ptr real_loader, + shell::Context* context); + virtual ~UIApplicationLoader(); + + // ApplicationLoader overrides: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE; + virtual void OnServiceError(ApplicationManager* manager, + const GURL& url) OVERRIDE; + + private: + class UILoader; + + // These functions are exected on the background thread. They call through + // to |background_loader_| to do the actual loading. + // TODO: having this code take a |manager| is fragile (as ApplicationManager + // isn't thread safe). + void LoadOnUIThread(ApplicationManager* manager, + const GURL& url, + ScopedMessagePipeHandle* shell_handle); + void OnServiceErrorOnUIThread(ApplicationManager* manager, const GURL& url); + void ShutdownOnUIThread(); + + scoped_ptr loader_; + shell::Context* context_; + + // Lives on the UI thread. Trivial interface that calls through to |loader_|. + UILoader* ui_loader_; + + DISALLOW_COPY_AND_ASSIGN(UIApplicationLoader); +}; + +} // namespace mojo + +#endif // MOJO_SHELL_UI_APPLICATION_LOADER_ANDROID_H_ diff --git a/mojo/shell/ui_service_loader_android.cc b/mojo/shell/ui_service_loader_android.cc deleted file mode 100644 index 3960858c2f73a..0000000000000 --- a/mojo/shell/ui_service_loader_android.cc +++ /dev/null @@ -1,89 +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/ui_service_loader_android.h" - -#include "base/bind.h" -#include "mojo/service_manager/service_manager.h" -#include "mojo/shell/context.h" - -namespace mojo { - -class UIServiceLoader::UILoader { - public: - explicit UILoader(ServiceLoader* loader) : loader_(loader) {} - ~UILoader() {} - - void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - loader_->LoadService(manager, url, shell_handle.Pass()); - } - - void OnServiceError(ServiceManager* manager, const GURL& url) { - loader_->OnServiceError(manager, url); - } - - private: - ServiceLoader* loader_; // Owned by UIServiceLoader - - DISALLOW_COPY_AND_ASSIGN(UILoader); -}; - -UIServiceLoader::UIServiceLoader(scoped_ptr real_loader, - shell::Context* context) - : loader_(real_loader.Pass()), context_(context) { -} - -UIServiceLoader::~UIServiceLoader() { - context_->ui_loop()->PostTask( - FROM_HERE, - base::Bind(&UIServiceLoader::ShutdownOnUIThread, base::Unretained(this))); -} - -void UIServiceLoader::LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { - context_->ui_loop()->PostTask( - FROM_HERE, - base::Bind( - &UIServiceLoader::LoadServiceOnUIThread, - base::Unretained(this), - manager, - url, - base::Owned(new ScopedMessagePipeHandle(shell_handle.Pass())))); -} - -void UIServiceLoader::OnServiceError(ServiceManager* manager, const GURL& url) { - context_->ui_loop()->PostTask( - FROM_HERE, - base::Bind(&UIServiceLoader::OnServiceErrorOnUIThread, - base::Unretained(this), - manager, - url)); -} - -void UIServiceLoader::LoadServiceOnUIThread( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle* shell_handle) { - if (!ui_loader_) - ui_loader_ = new UILoader(loader_.get()); - ui_loader_->LoadService(manager, url, shell_handle->Pass()); -} - -void UIServiceLoader::OnServiceErrorOnUIThread(ServiceManager* manager, - const GURL& url) { - if (!ui_loader_) - ui_loader_ = new UILoader(loader_.get()); - ui_loader_->OnServiceError(manager, url); -} - -void UIServiceLoader::ShutdownOnUIThread() { - delete ui_loader_; - // Destroy |loader_| on the thread it's actually used on. - loader_.reset(); -} - -} // namespace mojo diff --git a/mojo/shell/ui_service_loader_android.h b/mojo/shell/ui_service_loader_android.h deleted file mode 100644 index ebe6df9800ab2..0000000000000 --- a/mojo/shell/ui_service_loader_android.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_SHELL_UI_SERVICE_LOADER_ANDROID_H_ -#define MOJO_SHELL_UI_SERVICE_LOADER_ANDROID_H_ - -#include "base/memory/scoped_ptr.h" -#include "mojo/service_manager/service_loader.h" - -namespace mojo { - -class ServiceManager; - -namespace shell { -class Context; -} - -// ServiceLoader implementation that creates a background thread and issues load -// requests there. -class UIServiceLoader : public ServiceLoader { - public: - UIServiceLoader(scoped_ptr real_loader, - shell::Context* context); - virtual ~UIServiceLoader(); - - // ServiceLoader overrides: - virtual void LoadService(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE; - virtual void OnServiceError(ServiceManager* manager, - const GURL& url) OVERRIDE; - - private: - class UILoader; - - // These functions are exected on the background thread. They call through - // to |background_loader_| to do the actual loading. - // TODO: having this code take a |manager| is fragile (as ServiceManager isn't - // thread safe). - void LoadServiceOnUIThread(ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle* shell_handle); - void OnServiceErrorOnUIThread(ServiceManager* manager, const GURL& url); - void ShutdownOnUIThread(); - - scoped_ptr loader_; - shell::Context* context_; - - // Lives on the UI thread. Trivial interface that calls through to |loader_|. - UILoader* ui_loader_; - - DISALLOW_COPY_AND_ASSIGN(UIServiceLoader); -}; - -} // namespace mojo - -#endif // MOJO_SHELL_UI_SERVICE_LOADER_ANDROID_H_ diff --git a/mojo/shell/view_manager_loader.cc b/mojo/shell/view_manager_loader.cc index bbcca5ae64042..844e8a772dfd0 100644 --- a/mojo/shell/view_manager_loader.cc +++ b/mojo/shell/view_manager_loader.cc @@ -20,10 +20,13 @@ ViewManagerLoader::ViewManagerLoader() { ViewManagerLoader::~ViewManagerLoader() { } -void ViewManagerLoader::LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) { +void ViewManagerLoader::Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) { + ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); + if (!shell_handle.is_valid()) + return; + // TODO(sky): this needs some sort of authentication as well as making sure // we only ever have one active at a time. scoped_ptr app( @@ -31,7 +34,7 @@ void ViewManagerLoader::LoadService( apps_.push_back(app.release()); } -void ViewManagerLoader::OnServiceError(ServiceManager* manager, +void ViewManagerLoader::OnServiceError(ApplicationManager* manager, const GURL& url) { } diff --git a/mojo/shell/view_manager_loader.h b/mojo/shell/view_manager_loader.h index bb3a615471552..e744cae3c7c25 100644 --- a/mojo/shell/view_manager_loader.h +++ b/mojo/shell/view_manager_loader.h @@ -7,9 +7,9 @@ #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" +#include "mojo/application_manager/application_loader.h" #include "mojo/public/cpp/application/application_delegate.h" #include "mojo/public/cpp/application/interface_factory.h" -#include "mojo/service_manager/service_loader.h" #include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h" #include "mojo/services/view_manager/view_manager_init_service_context.h" @@ -19,22 +19,20 @@ class Application; namespace shell { -// ServiceLoader responsible for creating connections to the ViewManager. -class ViewManagerLoader - : public ServiceLoader, - public ApplicationDelegate, - public InterfaceFactory { +// ApplicationLoader responsible for creating connections to the ViewManager. +class ViewManagerLoader : public ApplicationLoader, + public ApplicationDelegate, + public InterfaceFactory { public: ViewManagerLoader(); virtual ~ViewManagerLoader(); private: - // ServiceLoader overrides: - virtual void LoadService( - ServiceManager* manager, - const GURL& url, - ScopedMessagePipeHandle shell_handle) OVERRIDE; - virtual void OnServiceError(ServiceManager* manager, + // ApplicationLoader overrides: + virtual void Load(ApplicationManager* manager, + const GURL& url, + scoped_refptr callbacks) OVERRIDE; + virtual void OnServiceError(ApplicationManager* manager, const GURL& url) OVERRIDE; // ApplicationDelegate overrides. diff --git a/mojo/spy/BUILD.gn b/mojo/spy/BUILD.gn index 2fc59be5886fe..51b2a94e571fe 100644 --- a/mojo/spy/BUILD.gn +++ b/mojo/spy/BUILD.gn @@ -11,7 +11,7 @@ source_set("spy") { "//base:base_static", "//net:http_server", "//url", - "//mojo/service_manager", + "//mojo/application_manager", ] sources = [ diff --git a/mojo/spy/spy.cc b/mojo/spy/spy.cc index 4c7ed0d511881..9def0460359d0 100644 --- a/mojo/spy/spy.cc +++ b/mojo/spy/spy.cc @@ -17,8 +17,8 @@ #include "base/threading/thread.h" #include "base/threading/worker_pool.h" #include "base/time/time.h" +#include "mojo/application_manager/application_manager.h" #include "mojo/public/cpp/system/core.h" -#include "mojo/service_manager/service_manager.h" #include "mojo/spy/common.h" #include "mojo/spy/public/spy.mojom.h" #include "mojo/spy/spy_server_impl.h" @@ -222,7 +222,7 @@ class MessageProcessor : }; // In charge of intercepting access to the service manager. -class SpyInterceptor : public mojo::ServiceManager::Interceptor { +class SpyInterceptor : public mojo::ApplicationManager::Interceptor { public: explicit SpyInterceptor(scoped_refptr spy_server, base::MessageLoopProxy* control_loop_proxy) @@ -310,7 +310,8 @@ SpyOptions ProcessOptions(const std::string& options) { namespace mojo { -Spy::Spy(mojo::ServiceManager* service_manager, const std::string& options) { +Spy::Spy(mojo::ApplicationManager* application_manager, + const std::string& options) { SpyOptions spy_options = ProcessOptions(options); spy_server_ = new SpyServerImpl(); @@ -325,13 +326,13 @@ Spy::Spy(mojo::ServiceManager* service_manager, const std::string& options) { base::Passed(spy_server_->ServerPipe()))); // Start intercepting mojo services. - service_manager->SetInterceptor(new SpyInterceptor( - spy_server_, control_thread_->message_loop_proxy())); + application_manager->SetInterceptor( + new SpyInterceptor(spy_server_, control_thread_->message_loop_proxy())); } Spy::~Spy() { // TODO(cpu): Do not leak the interceptor. Lifetime between the - // service_manager and the spy is still unclear hence the leak. + // application_manager and the spy is still unclear hence the leak. } } // namespace mojo diff --git a/mojo/spy/spy.h b/mojo/spy/spy.h index 020167fd07212..f2b6be9100c9e 100644 --- a/mojo/spy/spy.h +++ b/mojo/spy/spy.h @@ -15,7 +15,7 @@ class Thread; namespace mojo { -class ServiceManager; +class ApplicationManager; class SpyServerImpl; // mojo::Spy is a troubleshooting and debugging aid. It helps tracking @@ -29,7 +29,8 @@ class SpyServerImpl; // class Spy { public: - Spy(mojo::ServiceManager* service_manager, const std::string& options); + Spy(mojo::ApplicationManager* application_manager, + const std::string& options); ~Spy(); private: diff --git a/mojo/system.gni b/mojo/system.gni new file mode 100644 index 0000000000000..c7c07ad721bc3 --- /dev/null +++ b/mojo/system.gni @@ -0,0 +1,24 @@ +# 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. + +# The following mojo_system-prefixed variables are used to express a dependency +# on the mojo system APIs. +# +# In an is_component_build build, everything can link against +# mojo_system_impl because it is built as a shared library. However, in a static +# build, mojo_system_impl is linked into an executable (e.g., +# mojo_shell), and must be injected into other shared libraries (i.e., Mojo +# Apps) that need the mojo system API. +# +# For component targets, add mojo_system_for_component to your deps section. +# For shared_library targets (e.g., a Mojo App), add +# mojo_system_for_shared_library to your deps + +if (is_component_build) { + mojo_system_for_component = [ "//mojo/system" ] + mojo_system_for_shared_library = [ "//mojo/system" ] +} else { + mojo_system_for_component = [] # nothing to link against here + mojo_system_for_shared_library = [ "//mojo/public/c/system" ] +} diff --git a/mojo/system/BUILD.gn b/mojo/system/BUILD.gn index 5ce0cd3a20fb3..c45c4c8486e11 100644 --- a/mojo/system/BUILD.gn +++ b/mojo/system/BUILD.gn @@ -30,7 +30,15 @@ component("system") { "../embedder/platform_handle_utils_posix.cc", "../embedder/platform_handle_utils_win.cc", "../embedder/platform_handle_vector.h", + "../embedder/platform_shared_buffer.h", + "../embedder/platform_support.h", "../embedder/scoped_platform_handle.h", + "../embedder/simple_platform_shared_buffer.cc", + "../embedder/simple_platform_shared_buffer.h", + "../embedder/simple_platform_shared_buffer_posix.cc", + "../embedder/simple_platform_shared_buffer_win.cc", + "../embedder/simple_platform_support.cc", + "../embedder/simple_platform_support.h", "channel.cc", "channel.h", "constants.h", @@ -75,10 +83,6 @@ component("system") { "raw_channel.h", "raw_channel_posix.cc", "raw_channel_win.cc", - "raw_shared_buffer.cc", - "raw_shared_buffer.h", - "raw_shared_buffer_posix.cc", - "raw_shared_buffer_win.cc", "shared_buffer_dispatcher.cc", "shared_buffer_dispatcher.h", "simple_dispatcher.cc", diff --git a/mojo/system/PRESUBMIT.py b/mojo/system/PRESUBMIT.py index 97f67c0ed9ce1..36cc56a3d7518 100644 --- a/mojo/system/PRESUBMIT.py +++ b/mojo/system/PRESUBMIT.py @@ -2,7 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -"""Presubmit script for mojo/embedder. +"""Presubmit script for mojo/system. See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts for more details about the presubmit API built into depot_tools. diff --git a/mojo/system/channel.cc b/mojo/system/channel.cc index aeb24c3719c08..fd5364eaba4a4 100644 --- a/mojo/system/channel.cc +++ b/mojo/system/channel.cc @@ -36,7 +36,10 @@ Channel::EndpointInfo::EndpointInfo(scoped_refptr message_pipe, Channel::EndpointInfo::~EndpointInfo() { } -Channel::Channel() : is_running_(false), next_local_id_(kBootstrapEndpointId) { +Channel::Channel() + : is_running_(false), + is_shutting_down_(false), + next_local_id_(kBootstrapEndpointId) { } bool Channel::Init(scoped_ptr raw_channel) { @@ -45,7 +48,7 @@ bool Channel::Init(scoped_ptr raw_channel) { // No need to take |lock_|, since this must be called before this object // becomes thread-safe. - DCHECK(!is_running_no_lock()); + DCHECK(!is_running_); raw_channel_ = raw_channel.Pass(); if (!raw_channel_->Init(this)) { @@ -63,11 +66,11 @@ void Channel::Shutdown() { IdToEndpointInfoMap to_destroy; { base::AutoLock locker(lock_); - if (!is_running_no_lock()) + if (!is_running_) return; // Note: Don't reset |raw_channel_|, in case we're being called from within - // |OnReadMessage()| or |OnFatalError()|. + // |OnReadMessage()| or |OnError()|. raw_channel_->Shutdown(); is_running_ = false; @@ -93,6 +96,11 @@ void Channel::Shutdown() { << " zombies"; } +void Channel::WillShutdownSoon() { + base::AutoLock locker(lock_); + is_shutting_down_ = true; +} + MessageInTransit::EndpointId Channel::AttachMessagePipeEndpoint( scoped_refptr message_pipe, unsigned port) { @@ -103,6 +111,9 @@ MessageInTransit::EndpointId Channel::AttachMessagePipeEndpoint( { base::AutoLock locker(lock_); + DLOG_IF(WARNING, is_shutting_down_) + << "AttachMessagePipeEndpoint() while shutting down"; + while (next_local_id_ == MessageInTransit::kInvalidEndpointId || local_id_to_endpoint_info_map_.find(next_local_id_) != local_id_to_endpoint_info_map_.end()) @@ -151,6 +162,9 @@ bool Channel::RunMessagePipeEndpoint(MessageInTransit::EndpointId local_id, { base::AutoLock locker(lock_); + DLOG_IF(WARNING, is_shutting_down_) + << "RunMessagePipeEndpoint() while shutting down"; + IdToEndpointInfoMap::const_iterator it = local_id_to_endpoint_info_map_.find(local_id); if (it == local_id_to_endpoint_info_map_.end()) @@ -197,19 +211,20 @@ void Channel::RunRemoteMessagePipeEndpoint( bool Channel::WriteMessage(scoped_ptr message) { base::AutoLock locker(lock_); - if (!is_running_no_lock()) { + if (!is_running_) { // TODO(vtl): I think this is probably not an error condition, but I should // think about it (and the shutdown sequence) more carefully. LOG(WARNING) << "WriteMessage() after shutdown"; return false; } + DLOG_IF(WARNING, is_shutting_down_) << "WriteMessage() while shutting down"; return raw_channel_->WriteMessage(message.Pass()); } bool Channel::IsWriteBufferEmpty() { base::AutoLock locker(lock_); - if (!is_running_no_lock()) + if (!is_running_) return true; return raw_channel_->IsWriteBufferEmpty(); } @@ -222,7 +237,7 @@ void Channel::DetachMessagePipeEndpoint( bool should_send_remove_message = false; { base::AutoLock locker_(lock_); - if (!is_running_no_lock()) + if (!is_running_) return; IdToEndpointInfoMap::iterator it = @@ -268,7 +283,7 @@ size_t Channel::GetSerializedPlatformHandleSize() const { Channel::~Channel() { // The channel should have been shut down first. - DCHECK(!is_running_no_lock()); + DCHECK(!is_running_); } void Channel::OnReadMessage( @@ -290,17 +305,30 @@ void Channel::OnReadMessage( } } -void Channel::OnFatalError(FatalError fatal_error) { - switch (fatal_error) { - case FATAL_ERROR_READ: - // Most read errors aren't notable: they just reflect that the other side - // tore down the channel. - DVLOG(1) << "RawChannel fatal error (read)"; +void Channel::OnError(Error error) { + switch (error) { + case ERROR_READ_SHUTDOWN: + // The other side was cleanly closed, so this isn't actually an error. + DVLOG(1) << "RawChannel read error (shutdown)"; + break; + case ERROR_READ_BROKEN: { + base::AutoLock locker(lock_); + LOG_IF(ERROR, !is_shutting_down_) + << "RawChannel read error (connection broken)"; + break; + } + case ERROR_READ_BAD_MESSAGE: + // Receiving a bad message means either a bug, data corruption, or + // malicious attack (probably due to some other bug). + LOG(ERROR) << "RawChannel read error (received bad message)"; + break; + case ERROR_READ_UNKNOWN: + LOG(ERROR) << "RawChannel read error (unknown)"; break; - case FATAL_ERROR_WRITE: + case ERROR_WRITE: // Write errors are slightly notable: they probably shouldn't happen under // normal operation (but maybe the other side crashed). - LOG(WARNING) << "RawChannel fatal error (write)"; + LOG(WARNING) << "RawChannel write error"; break; } Shutdown(); @@ -325,7 +353,7 @@ void Channel::OnReadMessageForDownstream( // Since we own |raw_channel_|, and this method and |Shutdown()| should only // be called from the creation thread, |raw_channel_| should never be null // here. - DCHECK(is_running_no_lock()); + DCHECK(is_running_); IdToEndpointInfoMap::const_iterator it = local_id_to_endpoint_info_map_.find(local_id); diff --git a/mojo/system/channel.h b/mojo/system/channel.h index ac71b3ecea96b..61895d6bcfe0e 100644 --- a/mojo/system/channel.h +++ b/mojo/system/channel.h @@ -67,6 +67,12 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel // happen on any thread). void Shutdown(); + // Signals that |Shutdown()| will be called soon (this may be called from any + // thread, unlike |Shutdown()|). Warnings will be issued if, e.g., messages + // are written after this is called; other warnings may be suppressed. (This + // may be called multiple times, or not at all.) + void WillShutdownSoon(); + // Attaches the given message pipe/port's endpoint (which must be a // |ProxyMessagePipeEndpoint|) to this channel. This assigns it a local ID, // which it returns. The first message pipe endpoint attached will always have @@ -145,7 +151,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel virtual void OnReadMessage( const MessageInTransit::View& message_view, embedder::ScopedPlatformHandleVectorPtr platform_handles) OVERRIDE; - virtual void OnFatalError(FatalError fatal_error) OVERRIDE; + virtual void OnError(Error error) OVERRIDE; // Helpers for |OnReadMessage|: void OnReadMessageForDownstream( @@ -172,8 +178,6 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel MessageInTransit::EndpointId source_id, MessageInTransit::EndpointId destination_id); - bool is_running_no_lock() const { return is_running_; } - base::ThreadChecker creation_thread_checker_; // Note: |MessagePipe|s MUST NOT be used under |lock_|. I.e., |lock_| can only @@ -185,6 +189,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Channel scoped_ptr raw_channel_; bool is_running_; + // Set when |WillShutdownSoon()| is called. + bool is_shutting_down_; typedef base::hash_map IdToEndpointInfoMap; diff --git a/mojo/system/channel_unittest.cc b/mojo/system/channel_unittest.cc index c7ffe218d1478..33fa6ec6d45d4 100644 --- a/mojo/system/channel_unittest.cc +++ b/mojo/system/channel_unittest.cc @@ -125,11 +125,11 @@ class MockRawChannelOnInitFails : public RawChannel { // |RawChannel| protected methods: virtual IOResult Read(size_t*) OVERRIDE { CHECK(false); - return IO_FAILED; + return IO_FAILED_UNKNOWN; } virtual IOResult ScheduleRead() OVERRIDE { CHECK(false); - return IO_FAILED; + return IO_FAILED_UNKNOWN; } virtual embedder::ScopedPlatformHandleVectorPtr GetReadPlatformHandles( size_t, @@ -139,11 +139,11 @@ class MockRawChannelOnInitFails : public RawChannel { } virtual IOResult WriteNoLock(size_t*, size_t*) OVERRIDE { CHECK(false); - return IO_FAILED; + return IO_FAILED_UNKNOWN; } virtual IOResult ScheduleWriteNoLock() OVERRIDE { CHECK(false); - return IO_FAILED; + return IO_FAILED_UNKNOWN; } virtual bool OnInit() OVERRIDE { EXPECT_FALSE(on_init_called_); diff --git a/mojo/system/core.cc b/mojo/system/core.cc index 46c5bceb4876c..47d63f66580b0 100644 --- a/mojo/system/core.cc +++ b/mojo/system/core.cc @@ -8,17 +8,19 @@ #include "base/logging.h" #include "base/time/time.h" +#include "mojo/embedder/platform_shared_buffer.h" +#include "mojo/embedder/simple_platform_support.h" // TODO(vtl): Remove this. #include "mojo/public/c/system/macros.h" #include "mojo/system/constants.h" #include "mojo/system/data_pipe.h" #include "mojo/system/data_pipe_consumer_dispatcher.h" #include "mojo/system/data_pipe_producer_dispatcher.h" #include "mojo/system/dispatcher.h" +#include "mojo/system/handle_signals_state.h" #include "mojo/system/local_data_pipe.h" #include "mojo/system/memory.h" #include "mojo/system/message_pipe.h" #include "mojo/system/message_pipe_dispatcher.h" -#include "mojo/system/raw_shared_buffer.h" #include "mojo/system/shared_buffer_dispatcher.h" #include "mojo/system/waiter.h" @@ -118,15 +120,27 @@ MojoResult Core::Close(MojoHandle handle) { MojoResult Core::Wait(MojoHandle handle, MojoHandleSignals signals, - MojoDeadline deadline) { + MojoDeadline deadline, + UserPointer signals_state) { uint32_t unused = static_cast(-1); - return WaitManyInternal(&handle, &signals, 1, deadline, &unused); + HandleSignalsState hss; + MojoResult rv = WaitManyInternal(&handle, + &signals, + 1, + deadline, + &unused, + signals_state.IsNull() ? NULL : &hss); + if (rv != MOJO_RESULT_INVALID_ARGUMENT && !signals_state.IsNull()) + signals_state.Put(hss); + return rv; } MojoResult Core::WaitMany(UserPointer handles, UserPointer signals, uint32_t num_handles, - MojoDeadline deadline) { + MojoDeadline deadline, + UserPointer result_index, + UserPointer signals_states) { if (num_handles < 1) return MOJO_RESULT_INVALID_ARGUMENT; if (num_handles > kMaxWaitManyNumHandles) @@ -135,14 +149,33 @@ MojoResult Core::WaitMany(UserPointer handles, UserPointer::Reader handles_reader(handles, num_handles); UserPointer::Reader signals_reader(signals, num_handles); - uint32_t result_index = static_cast(-1); - MojoResult result = WaitManyInternal(handles_reader.GetPointer(), - signals_reader.GetPointer(), - num_handles, - deadline, - &result_index); - return (result == MOJO_RESULT_OK) ? static_cast(result_index) - : result; + uint32_t index = static_cast(-1); + MojoResult rv; + if (signals_states.IsNull()) { + rv = WaitManyInternal(handles_reader.GetPointer(), + signals_reader.GetPointer(), + num_handles, + deadline, + &index, + NULL); + } else { + UserPointer::Writer signals_states_writer( + signals_states, num_handles); + // Note: The |reinterpret_cast| is safe, since |HandleSignalsState| is a + // subclass of |MojoHandleSignalsState| that doesn't add any data members. + rv = WaitManyInternal(handles_reader.GetPointer(), + signals_reader.GetPointer(), + num_handles, + deadline, + &index, + reinterpret_cast( + signals_states_writer.GetPointer())); + if (rv != MOJO_RESULT_INVALID_ARGUMENT) + signals_states_writer.Commit(); + } + if (index != static_cast(-1) && !result_index.IsNull()) + result_index.Put(index); + return rv; } MojoResult Core::CreateMessagePipe( @@ -430,9 +463,12 @@ MojoResult Core::CreateSharedBuffer( if (result != MOJO_RESULT_OK) return result; + // TODO(vtl): |Core| should have a |PlatformSupport| passed in at creation + // time, and we should use that instead. + embedder::SimplePlatformSupport platform_support; scoped_refptr dispatcher; - result = - SharedBufferDispatcher::Create(validated_options, num_bytes, &dispatcher); + result = SharedBufferDispatcher::Create( + &platform_support, validated_options, num_bytes, &dispatcher); if (result != MOJO_RESULT_OK) { DCHECK(!dispatcher); return result; @@ -484,13 +520,13 @@ MojoResult Core::MapBuffer(MojoHandle buffer_handle, if (!dispatcher) return MOJO_RESULT_INVALID_ARGUMENT; - scoped_ptr mapping; + scoped_ptr mapping; MojoResult result = dispatcher->MapBuffer(offset, num_bytes, flags, &mapping); if (result != MOJO_RESULT_OK) return result; DCHECK(mapping); - void* address = mapping->base(); + void* address = mapping->GetBase(); { base::AutoLock locker(mapping_table_lock_); result = mapping_table_.AddMapping(mapping.Pass()); @@ -515,7 +551,8 @@ MojoResult Core::WaitManyInternal(const MojoHandle* handles, const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline, - uint32_t* result_index) { + uint32_t* result_index, + HandleSignalsState* signals_states) { DCHECK_GT(num_handles, 0u); DCHECK_EQ(*result_index, static_cast(-1)); @@ -537,7 +574,8 @@ MojoResult Core::WaitManyInternal(const MojoHandle* handles, uint32_t i; MojoResult rv = MOJO_RESULT_OK; for (i = 0; i < num_handles; i++) { - rv = dispatchers[i]->AddWaiter(&waiter, signals[i], i, NULL); + rv = dispatchers[i]->AddWaiter( + &waiter, signals[i], i, signals_states ? &signals_states[i] : NULL); if (rv != MOJO_RESULT_OK) { *result_index = i; break; @@ -553,8 +591,14 @@ MojoResult Core::WaitManyInternal(const MojoHandle* handles, // Make sure no other dispatchers try to wake |waiter| for the current // |Wait()|/|WaitMany()| call. (Only after doing this can |waiter| be // destroyed, but this would still be required if the waiter were in TLS.) - for (i = 0; i < num_added; i++) - dispatchers[i]->RemoveWaiter(&waiter, NULL); + for (i = 0; i < num_added; i++) { + dispatchers[i]->RemoveWaiter(&waiter, + signals_states ? &signals_states[i] : NULL); + } + if (signals_states) { + for (; i < num_handles; i++) + signals_states[i] = dispatchers[i]->GetHandleSignalsState(); + } return rv; } diff --git a/mojo/system/core.h b/mojo/system/core.h index c71720c1db59e..d590db2c99875 100644 --- a/mojo/system/core.h +++ b/mojo/system/core.h @@ -23,6 +23,7 @@ namespace mojo { namespace system { class Dispatcher; +struct HandleSignalsState; // |Core| is an object that implements the Mojo system calls. All public methods // are thread-safe. @@ -45,11 +46,14 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { MojoResult Close(MojoHandle handle); MojoResult Wait(MojoHandle handle, MojoHandleSignals signals, - MojoDeadline deadline); + MojoDeadline deadline, + UserPointer signals_state); MojoResult WaitMany(UserPointer handles, UserPointer signals, uint32_t num_handles, - MojoDeadline deadline); + MojoDeadline deadline, + UserPointer result_index, + UserPointer signals_states); MojoResult CreateMessagePipe( UserPointer options, UserPointer message_pipe_handle0, @@ -116,7 +120,8 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline, - uint32_t* result_index); + uint32_t* result_index, + HandleSignalsState* signals_states); // --------------------------------------------------------------------------- diff --git a/mojo/system/core_unittest.cc b/mojo/system/core_unittest.cc index 2b7b520ec45c2..8d1441c6e4cb1 100644 --- a/mojo/system/core_unittest.cc +++ b/mojo/system/core_unittest.cc @@ -16,6 +16,9 @@ namespace mojo { namespace system { namespace { +const MojoHandleSignalsState kEmptyMojoHandleSignalsState = {0u, 0u}; +const MojoHandleSignalsState kFullMojoHandleSignalsState = {~0u, ~0u}; + typedef test::CoreTestBase CoreTest; TEST_F(CoreTest, GetTimeTicksNow) { @@ -103,23 +106,79 @@ TEST_F(CoreTest, Basic) { EXPECT_EQ(1u, info.GetEndReadDataCallCount()); EXPECT_EQ(0u, info.GetAddWaiterCallCount()); - EXPECT_EQ( - MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE)); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h, + ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, + NullUserPointer())); EXPECT_EQ(1u, info.GetAddWaiterCallCount()); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 0)); + core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 0, NullUserPointer())); EXPECT_EQ(2u, info.GetAddWaiterCallCount()); + MojoHandleSignalsState hss = kFullMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000)); + core()->Wait(h, + ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&hss))); EXPECT_EQ(3u, info.GetAddWaiterCallCount()); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(0u, hss.satisfiable_signals); + EXPECT_EQ( + MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, NullUserPointer())); + EXPECT_EQ(4u, info.GetAddWaiterCallCount()); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait( + h, ~MOJO_HANDLE_SIGNAL_NONE, 10 * 1000, MakeUserPointer(&hss))); + EXPECT_EQ(5u, info.GetAddWaiterCallCount()); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(0u, hss.satisfiable_signals); + MojoHandleSignals handle_signals = ~MOJO_HANDLE_SIGNAL_NONE; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, core()->WaitMany(MakeUserPointer(&h), MakeUserPointer(&handle_signals), 1, - MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ(4u, info.GetAddWaiterCallCount()); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); + EXPECT_EQ(6u, info.GetAddWaiterCallCount()); + uint32_t result_index = static_cast(-1); + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WaitMany(MakeUserPointer(&h), + MakeUserPointer(&handle_signals), + 1, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + NullUserPointer())); + EXPECT_EQ(7u, info.GetAddWaiterCallCount()); + EXPECT_EQ(0u, result_index); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WaitMany(MakeUserPointer(&h), + MakeUserPointer(&handle_signals), + 1, + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + MakeUserPointer(&hss))); + EXPECT_EQ(8u, info.GetAddWaiterCallCount()); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(0u, hss.satisfiable_signals); + result_index = static_cast(-1); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, + core()->WaitMany(MakeUserPointer(&h), + MakeUserPointer(&handle_signals), + 1, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(&hss))); + EXPECT_EQ(9u, info.GetAddWaiterCallCount()); + EXPECT_EQ(0u, result_index); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(0u, hss.satisfiable_signals); EXPECT_EQ(0u, info.GetDtorCallCount()); EXPECT_EQ(0u, info.GetCloseCallCount()); @@ -154,10 +213,36 @@ TEST_F(CoreTest, InvalidArguments) { EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->Wait(MOJO_HANDLE_INVALID, ~MOJO_HANDLE_SIGNAL_NONE, - MOJO_DEADLINE_INDEFINITE)); - EXPECT_EQ( - MOJO_RESULT_INVALID_ARGUMENT, - core()->Wait(10, ~MOJO_HANDLE_SIGNAL_NONE, MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer())); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->Wait(10, + ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, + NullUserPointer())); + + MojoHandleSignalsState hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->Wait(MOJO_HANDLE_INVALID, + ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&hss))); + // On invalid argument, it shouldn't modify the handle signals state. + EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, + hss.satisfied_signals); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, + hss.satisfiable_signals); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->Wait(10, + ~MOJO_HANDLE_SIGNAL_NONE, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&hss))); + // On invalid argument, it shouldn't modify the handle signals state. + EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, + hss.satisfied_signals); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, + hss.satisfiable_signals); } // |WaitMany()|: @@ -169,48 +254,115 @@ TEST_F(CoreTest, InvalidArguments) { core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 0, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->WaitMany(NullUserPointer(), MakeUserPointer(signals), 0, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); + // If |num_handles| is invalid, it should leave |result_index| and + // |signals_states| alone. + // (We use -1 internally; make sure that doesn't leak.) + uint32_t result_index = 123; + MojoHandleSignalsState hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(NullUserPointer(), + MakeUserPointer(signals), + 0, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(&hss))); + EXPECT_EQ(123u, result_index); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, + hss.satisfied_signals); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, + hss.satisfiable_signals); + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->WaitMany(MakeUserPointer(handles), NullUserPointer(), 0, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 1, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); + // But if a handle is bad, then it should set |result_index| but still leave + // |signals_states| alone. + result_index = static_cast(-1); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, + core()->WaitMany(MakeUserPointer(handles), + MakeUserPointer(signals), + 1, + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(&hss))); + EXPECT_EQ(0u, result_index); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, + hss.satisfied_signals); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, + hss.satisfiable_signals); MockHandleInfo info[2]; handles[0] = CreateMockHandle(&info[0]); + result_index = static_cast(-1); + hss = kFullMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 1, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(&hss))); + EXPECT_EQ(0u, result_index); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(0u, hss.satisfiable_signals); + + // On invalid argument, it'll leave |signals_states| alone. + result_index = static_cast(-1); + hss = kFullMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(&hss))); + EXPECT_EQ(1u, result_index); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfied_signals, + hss.satisfied_signals); + EXPECT_EQ(kFullMojoHandleSignalsState.satisfiable_signals, + hss.satisfiable_signals); handles[1] = handles[0] + 1; // Invalid handle. EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); handles[1] = CreateMockHandle(&info[1]); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, core()->WaitMany(MakeUserPointer(handles), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer())); + + // TODO(vtl): Test one where we get "failed precondition" only for the + // second handle (and the first one is valid to wait on). EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[0])); EXPECT_EQ(MOJO_RESULT_OK, core()->Close(handles[1])); @@ -386,19 +538,24 @@ TEST_F(CoreTest, InvalidArgumentsDeath) { // |WaitMany()|: { - MojoHandle handles[2] = {MOJO_HANDLE_INVALID, MOJO_HANDLE_INVALID}; - MojoHandleSignals signals[2] = {~MOJO_HANDLE_SIGNAL_NONE, - ~MOJO_HANDLE_SIGNAL_NONE}; + MojoHandle handle = MOJO_HANDLE_INVALID; + MojoHandleSignals signals = ~MOJO_HANDLE_SIGNAL_NONE; EXPECT_DEATH_IF_SUPPORTED(core()->WaitMany(NullUserPointer(), - MakeUserPointer(signals), + MakeUserPointer(&signals), 1, - MOJO_DEADLINE_INDEFINITE), + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer()), kMemoryCheckFailedRegex); - EXPECT_DEATH_IF_SUPPORTED(core()->WaitMany(MakeUserPointer(handles), + EXPECT_DEATH_IF_SUPPORTED(core()->WaitMany(MakeUserPointer(&handle), NullUserPointer(), 1, - MOJO_DEADLINE_INDEFINITE), + MOJO_DEADLINE_INDEFINITE, + NullUserPointer(), + NullUserPointer()), kMemoryCheckFailedRegex); + // TODO(vtl): |result_index| and |signals_states| are optional. Test them + // with non-null invalid pointers? } // |CreateMessagePipe()|: @@ -465,6 +622,8 @@ TEST_F(CoreTest, InvalidArgumentsDeath) { TEST_F(CoreTest, MessagePipe) { MojoHandle h[2]; + MojoHandleSignalsState hss[2]; + uint32_t result_index; EXPECT_EQ( MOJO_RESULT_OK, @@ -478,9 +637,23 @@ TEST_F(CoreTest, MessagePipe) { // Neither should be readable. MojoHandleSignals signals[2] = {MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE}; - EXPECT_EQ( - MOJO_RESULT_DEADLINE_EXCEEDED, - core()->WaitMany(MakeUserPointer(h), MakeUserPointer(signals), 2, 0)); + result_index = static_cast(-1); + hss[0] = kEmptyMojoHandleSignalsState; + hss[1] = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, + core()->WaitMany(MakeUserPointer(h), + MakeUserPointer(signals), + 2, + 0, + MakeUserPointer(&result_index), + MakeUserPointer(hss))); + EXPECT_EQ(static_cast(-1), result_index); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfiable_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[1].satisfiable_signals); // Try to read anyway. char buffer[1] = {'a'}; @@ -497,19 +670,45 @@ TEST_F(CoreTest, MessagePipe) { EXPECT_EQ(1u, buffer_size); // Both should be writable. + hss[0] = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h[0], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000)); + core()->Wait(h[0], + MOJO_HANDLE_SIGNAL_WRITABLE, + 1000000000, + MakeUserPointer(&hss[0]))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfiable_signals); + hss[0] = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000)); + core()->Wait(h[1], + MOJO_HANDLE_SIGNAL_WRITABLE, + 1000000000, + MakeUserPointer(&hss[0]))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfiable_signals); // Also check that |h[1]| is writable using |WaitMany()|. signals[0] = MOJO_HANDLE_SIGNAL_READABLE; signals[1] = MOJO_HANDLE_SIGNAL_WRITABLE; - EXPECT_EQ(1, + result_index = static_cast(-1); + hss[0] = kEmptyMojoHandleSignalsState; + hss[1] = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, core()->WaitMany(MakeUserPointer(h), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(hss))); + EXPECT_EQ(1u, result_index); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfiable_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[1].satisfiable_signals); // Write to |h[1]|. buffer[0] = 'b'; @@ -524,11 +723,24 @@ TEST_F(CoreTest, MessagePipe) { // Check that |h[0]| is now readable. signals[0] = MOJO_HANDLE_SIGNAL_READABLE; signals[1] = MOJO_HANDLE_SIGNAL_READABLE; - EXPECT_EQ(0, + result_index = static_cast(-1); + hss[0] = kEmptyMojoHandleSignalsState; + hss[1] = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, core()->WaitMany(MakeUserPointer(h), MakeUserPointer(signals), 2, - MOJO_DEADLINE_INDEFINITE)); + MOJO_DEADLINE_INDEFINITE, + MakeUserPointer(&result_index), + MakeUserPointer(hss))); + EXPECT_EQ(0u, result_index); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfiable_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[1].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[1].satisfiable_signals); // Read from |h[0]|. // First, get only the size. @@ -555,8 +767,14 @@ TEST_F(CoreTest, MessagePipe) { EXPECT_EQ(1u, buffer_size); // |h[0]| should no longer be readable. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(h[0], MOJO_HANDLE_SIGNAL_READABLE, 0)); + hss[0] = kEmptyMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_DEADLINE_EXCEEDED, + core()->Wait( + h[0], MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss[0]))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss[0].satisfiable_signals); // Write to |h[0]|. buffer[0] = 'd'; @@ -572,12 +790,24 @@ TEST_F(CoreTest, MessagePipe) { EXPECT_EQ(MOJO_RESULT_OK, core()->Close(h[0])); // Check that |h[1]| is no longer writable (and will never be). + hss[0] = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h[1], MOJO_HANDLE_SIGNAL_WRITABLE, 1000000000)); + core()->Wait(h[1], + MOJO_HANDLE_SIGNAL_WRITABLE, + 1000000000, + MakeUserPointer(&hss[0]))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfiable_signals); // Check that |h[1]| is still readable (for the moment). + hss[0] = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + core()->Wait(h[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss[0]))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss[0].satisfiable_signals); // Discard a message from |h[1]|. EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, @@ -589,8 +819,14 @@ TEST_F(CoreTest, MessagePipe) { MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)); // |h[1]| is no longer readable (and will never be). + hss[0] = kFullMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(h[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + core()->Wait(h[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss[0]))); + EXPECT_EQ(0u, hss[0].satisfied_signals); + EXPECT_EQ(0u, hss[0].satisfiable_signals); // Try writing to |h[1]|. buffer[0] = 'e'; @@ -616,6 +852,7 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { uint32_t num_bytes; MojoHandle handles[10]; uint32_t num_handles; + MojoHandleSignalsState hss; MojoHandle h_received; MojoHandle h_passing[2]; @@ -632,9 +869,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { NullUserPointer(), 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ( - MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -679,8 +923,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { NullUserPointer(), 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); + hss = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_passed[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + core()->Wait(h_passed[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -702,9 +954,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { MakeUserPointer(&h_passed[1]), 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ( - MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -738,8 +997,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { NullUserPointer(), 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); + hss = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(h_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + core()->Wait(h_received, + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -761,6 +1028,7 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing1) { TEST_F(CoreTest, DataPipe) { MojoHandle ph, ch; // p is for producer and c is for consumer. + MojoHandleSignalsState hss; EXPECT_EQ(MOJO_RESULT_OK, core()->CreateDataPipe( @@ -771,15 +1039,32 @@ TEST_F(CoreTest, DataPipe) { EXPECT_NE(ph, ch); // Producer should be never-readable, but already writable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ph, MOJO_HANDLE_SIGNAL_READABLE, 0)); - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(ph, MOJO_HANDLE_SIGNAL_WRITABLE, 0)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(ph, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_OK, + core()->Wait(ph, MOJO_HANDLE_SIGNAL_WRITABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfiable_signals); // Consumer should be never-writable, and not yet readable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0)); - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0)); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(ch, MOJO_HANDLE_SIGNAL_WRITABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_DEADLINE_EXCEEDED, + core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); // Write. char elements[2] = {'A', 'B'}; @@ -792,7 +1077,12 @@ TEST_F(CoreTest, DataPipe) { EXPECT_EQ(2u, num_bytes); // Consumer should now be readable. - EXPECT_EQ(MOJO_RESULT_OK, core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_OK, + core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); // Read one character. elements[0] = -1; @@ -884,8 +1174,12 @@ TEST_F(CoreTest, DataPipe) { EXPECT_EQ(MOJO_RESULT_OK, core()->EndReadData(ch, 2u)); // Consumer should now be no longer readable. - EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0)); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_DEADLINE_EXCEEDED, + core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); // TODO(vtl): More. @@ -893,8 +1187,12 @@ TEST_F(CoreTest, DataPipe) { EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ph)); // The consumer should now be never-readable. - EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0)); + hss = kFullMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_FAILED_PRECONDITION, + core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 0, MakeUserPointer(&hss))); + EXPECT_EQ(0u, hss.satisfied_signals); + EXPECT_EQ(0u, hss.satisfiable_signals); EXPECT_EQ(MOJO_RESULT_OK, core()->Close(ch)); } @@ -910,6 +1208,7 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { uint32_t num_bytes; MojoHandle handles[10]; uint32_t num_handles; + MojoHandleSignalsState hss; MojoHandle h_passing[2]; EXPECT_EQ(MOJO_RESULT_OK, @@ -930,9 +1229,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { MakeUserPointer(&ch), 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ( - MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -965,8 +1271,14 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { UserPointer(kWorld), MakeUserPointer(&num_bytes), MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + hss = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + core()->Wait(ch_received, + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); num_bytes = kBufferSize; EXPECT_EQ(MOJO_RESULT_OK, core()->ReadData(ch_received, @@ -984,9 +1296,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { MakeUserPointer(&ph), 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); - EXPECT_EQ( - MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -1019,8 +1338,14 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { UserPointer(kHello), MakeUserPointer(&num_bytes), MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + hss = kEmptyMojoHandleSignalsState; EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch_received, MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + core()->Wait(ch_received, + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); num_bytes = kBufferSize; EXPECT_EQ(MOJO_RESULT_OK, core()->ReadData(ch_received, @@ -1061,9 +1386,11 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); ch = MOJO_HANDLE_INVALID; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + NullUserPointer())); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, @@ -1084,8 +1411,13 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { EXPECT_EQ(MOJO_RESULT_OK, core()->EndWriteData(ph, 1)); // Wait for |ch| to be readable. - EXPECT_EQ(MOJO_RESULT_OK, - core()->Wait(ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ( + MOJO_RESULT_OK, + core()->Wait( + ch, MOJO_HANDLE_SIGNAL_READABLE, 1000000000, MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfiable_signals); // Make sure that |ch| can't be sent if it's in a two-phase read. const void* read_ptr = NULL; @@ -1112,9 +1444,16 @@ TEST_F(CoreTest, MessagePipeBasicLocalHandlePassing2) { 1, MOJO_WRITE_MESSAGE_FLAG_NONE)); ph = MOJO_HANDLE_INVALID; - EXPECT_EQ( - MOJO_RESULT_OK, - core()->Wait(h_passing[1], MOJO_HANDLE_SIGNAL_READABLE, 1000000000)); + hss = kEmptyMojoHandleSignalsState; + EXPECT_EQ(MOJO_RESULT_OK, + core()->Wait(h_passing[1], + MOJO_HANDLE_SIGNAL_READABLE, + 1000000000, + MakeUserPointer(&hss))); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfied_signals); + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, + hss.satisfiable_signals); num_bytes = kBufferSize; num_handles = arraysize(handles); EXPECT_EQ(MOJO_RESULT_OK, diff --git a/mojo/system/dispatcher.cc b/mojo/system/dispatcher.cc index 18d9be8645401..263ca2961315d 100644 --- a/mojo/system/dispatcher.cc +++ b/mojo/system/dispatcher.cc @@ -199,10 +199,11 @@ MojoResult Dispatcher::DuplicateBufferHandle( return DuplicateBufferHandleImplNoLock(options, new_dispatcher); } -MojoResult Dispatcher::MapBuffer(uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr* mapping) { +MojoResult Dispatcher::MapBuffer( + uint64_t offset, + uint64_t num_bytes, + MojoMapBufferFlags flags, + scoped_ptr* mapping) { base::AutoLock locker(lock_); if (is_closed_) return MOJO_RESULT_INVALID_ARGUMENT; @@ -353,7 +354,7 @@ MojoResult Dispatcher::MapBufferImplNoLock( uint64_t /*offset*/, uint64_t /*num_bytes*/, MojoMapBufferFlags /*flags*/, - scoped_ptr* /*mapping*/) { + scoped_ptr* /*mapping*/) { lock_.AssertAcquired(); DCHECK(!is_closed_); // By default, not supported. Only needed for buffer dispatchers. diff --git a/mojo/system/dispatcher.h b/mojo/system/dispatcher.h index 7a43dfd9eb1de..26f8091c86282 100644 --- a/mojo/system/dispatcher.h +++ b/mojo/system/dispatcher.h @@ -25,6 +25,11 @@ #include "mojo/system/system_impl_export.h" namespace mojo { + +namespace embedder { +class PlatformSharedBufferMapping; +} + namespace system { class Channel; @@ -34,7 +39,6 @@ class DispatcherTransport; class HandleTable; class LocalMessagePipeEndpoint; class ProxyMessagePipeEndpoint; -class RawSharedBufferMapping; class TransportData; class Waiter; @@ -113,10 +117,11 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher MojoResult DuplicateBufferHandle( UserPointer options, scoped_refptr* new_dispatcher); - MojoResult MapBuffer(uint64_t offset, - uint64_t num_bytes, - MojoMapBufferFlags flags, - scoped_ptr* mapping); + MojoResult MapBuffer( + uint64_t offset, + uint64_t num_bytes, + MojoMapBufferFlags flags, + scoped_ptr* mapping); // Gets the current handle signals state. (The default implementation simply // returns a default-constructed |HandleSignalsState|, i.e., no signals @@ -260,7 +265,7 @@ class MOJO_SYSTEM_IMPL_EXPORT Dispatcher uint64_t offset, uint64_t num_bytes, MojoMapBufferFlags flags, - scoped_ptr* mapping); + scoped_ptr* mapping); virtual HandleSignalsState GetHandleSignalsStateImplNoLock() const; virtual MojoResult AddWaiterImplNoLock(Waiter* waiter, MojoHandleSignals signals, diff --git a/mojo/system/dispatcher_unittest.cc b/mojo/system/dispatcher_unittest.cc index 888af3c06b0b2..1a7bba5759fca 100644 --- a/mojo/system/dispatcher_unittest.cc +++ b/mojo/system/dispatcher_unittest.cc @@ -9,8 +9,8 @@ #include "base/memory/scoped_vector.h" #include "base/synchronization/waitable_event.h" #include "base/threading/simple_thread.h" +#include "mojo/embedder/platform_shared_buffer.h" #include "mojo/system/memory.h" -#include "mojo/system/raw_shared_buffer.h" #include "mojo/system/waiter.h" #include "testing/gtest/include/gtest/gtest.h" @@ -223,7 +223,7 @@ class ThreadSafetyStressThread : public base::SimpleThread { break; } case MAP_BUFFER: { - scoped_ptr unused; + scoped_ptr unused; EXPECT_EQ( MOJO_RESULT_INVALID_ARGUMENT, dispatcher_->MapBuffer(0u, 0u, MOJO_MAP_BUFFER_FLAG_NONE, &unused)); diff --git a/mojo/system/entrypoints.cc b/mojo/system/entrypoints.cc index fc4bdc6933013..32c7235ed2519 100644 --- a/mojo/system/entrypoints.cc +++ b/mojo/system/entrypoints.cc @@ -4,6 +4,7 @@ #include "mojo/system/entrypoints.h" +#include "base/logging.h" #include "mojo/public/c/system/buffer.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/functions.h" @@ -43,17 +44,23 @@ MojoResult MojoClose(MojoHandle handle) { MojoResult MojoWait(MojoHandle handle, MojoHandleSignals signals, MojoDeadline deadline) { - return g_core->Wait(handle, signals, deadline); + return g_core->Wait( + handle, signals, deadline, mojo::system::NullUserPointer()); } MojoResult MojoWaitMany(const MojoHandle* handles, const MojoHandleSignals* signals, uint32_t num_handles, MojoDeadline deadline) { - return g_core->WaitMany(MakeUserPointer(handles), - MakeUserPointer(signals), - num_handles, - deadline); + uint32_t result_index = static_cast(-1); + MojoResult result = g_core->WaitMany(MakeUserPointer(handles), + MakeUserPointer(signals), + num_handles, + deadline, + MakeUserPointer(&result_index), + mojo::system::NullUserPointer()); + return (result == MOJO_RESULT_OK) ? static_cast(result_index) + : result; } MojoResult MojoCreateMessagePipe(const MojoCreateMessagePipeOptions* options, diff --git a/mojo/system/mapping_table.cc b/mojo/system/mapping_table.cc index 497b9e0d6a2f3..7aeb04d17c4b1 100644 --- a/mojo/system/mapping_table.cc +++ b/mojo/system/mapping_table.cc @@ -5,8 +5,8 @@ #include "mojo/system/mapping_table.h" #include "base/logging.h" +#include "mojo/embedder/platform_shared_buffer.h" #include "mojo/system/constants.h" -#include "mojo/system/raw_shared_buffer.h" namespace mojo { namespace system { @@ -20,13 +20,13 @@ MappingTable::~MappingTable() { } MojoResult MappingTable::AddMapping( - scoped_ptr mapping) { + scoped_ptr mapping) { DCHECK(mapping); if (address_to_mapping_map_.size() >= kMaxMappingTableSize) return MOJO_RESULT_RESOURCE_EXHAUSTED; - uintptr_t address = reinterpret_cast(mapping->base()); + uintptr_t address = reinterpret_cast(mapping->GetBase()); DCHECK(address_to_mapping_map_.find(address) == address_to_mapping_map_.end()); address_to_mapping_map_[address] = mapping.release(); @@ -37,7 +37,7 @@ MojoResult MappingTable::RemoveMapping(uintptr_t address) { AddressToMappingMap::iterator it = address_to_mapping_map_.find(address); if (it == address_to_mapping_map_.end()) return MOJO_RESULT_INVALID_ARGUMENT; - RawSharedBufferMapping* mapping_to_delete = it->second; + embedder::PlatformSharedBufferMapping* mapping_to_delete = it->second; address_to_mapping_map_.erase(it); delete mapping_to_delete; return MOJO_RESULT_OK; diff --git a/mojo/system/mapping_table.h b/mojo/system/mapping_table.h index e21d685b12214..5581e1eabfd86 100644 --- a/mojo/system/mapping_table.h +++ b/mojo/system/mapping_table.h @@ -16,10 +16,14 @@ #include "mojo/system/system_impl_export.h" namespace mojo { + +namespace embedder { +class PlatformSharedBufferMapping; +} + namespace system { class Core; -class RawSharedBufferMapping; // Test-only function (defined/used in embedder/test_embedder.cc). Declared here // so it can be friended. @@ -28,7 +32,7 @@ bool ShutdownCheckNoLeaks(Core*); } // This class provides the (global) table of memory mappings (owned by |Core|), -// which maps mapping base addresses to |RawSharedBuffer::Mapping|s. +// which maps mapping base addresses to |PlatformSharedBufferMapping|s. // // This class is NOT thread-safe; locking is left to |Core|. class MOJO_SYSTEM_IMPL_EXPORT MappingTable { @@ -38,13 +42,14 @@ class MOJO_SYSTEM_IMPL_EXPORT MappingTable { // Tries to add a mapping. (Takes ownership of the mapping in all cases; on // failure, it will be destroyed.) - MojoResult AddMapping(scoped_ptr mapping); + MojoResult AddMapping( + scoped_ptr mapping); MojoResult RemoveMapping(uintptr_t address); private: friend bool internal::ShutdownCheckNoLeaks(Core*); - typedef base::hash_map + typedef base::hash_map AddressToMappingMap; AddressToMappingMap address_to_mapping_map_; diff --git a/mojo/system/memory.cc b/mojo/system/memory.cc index 601123e48ef52..2df2ff2588099 100644 --- a/mojo/system/memory.cc +++ b/mojo/system/memory.cc @@ -35,6 +35,7 @@ void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer(const void* pointer) { // Explicitly instantiate the sizes we need. Add instantiations as needed. template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<1, 1>(const void*); template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<4, 4>(const void*); +template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<8, 4>(const void*); template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointer<8, 8>(const void*); template @@ -49,6 +50,8 @@ template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointerWithCount<1, 1>(const void*, size_t); template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointerWithCount<4, 4>(const void*, size_t); +template void MOJO_SYSTEM_IMPL_EXPORT + CheckUserPointerWithCount<8, 4>(const void*, size_t); template void MOJO_SYSTEM_IMPL_EXPORT CheckUserPointerWithCount<8, 8>(const void*, size_t); diff --git a/mojo/system/multiprocess_message_pipe_unittest.cc b/mojo/system/multiprocess_message_pipe_unittest.cc index 64a7e54b6b198..41a6c91c0a675 100644 --- a/mojo/system/multiprocess_message_pipe_unittest.cc +++ b/mojo/system/multiprocess_message_pipe_unittest.cc @@ -21,7 +21,9 @@ #include "build/build_config.h" // TODO(vtl): Remove this. #include "mojo/common/test/multiprocess_test_helper.h" #include "mojo/common/test/test_utils.h" +#include "mojo/embedder/platform_shared_buffer.h" #include "mojo/embedder/scoped_platform_handle.h" +#include "mojo/embedder/simple_platform_support.h" #include "mojo/system/channel.h" #include "mojo/system/dispatcher.h" #include "mojo/system/local_message_pipe_endpoint.h" @@ -29,7 +31,6 @@ #include "mojo/system/platform_handle_dispatcher.h" #include "mojo/system/proxy_message_pipe_endpoint.h" #include "mojo/system/raw_channel.h" -#include "mojo/system/raw_shared_buffer.h" #include "mojo/system/shared_buffer_dispatcher.h" #include "mojo/system/test_utils.h" #include "mojo/system/waiter.h" @@ -358,16 +359,16 @@ MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) { static_cast(dispatchers[0].get())); // Make a mapping. - scoped_ptr mapping; + scoped_ptr mapping; CHECK_EQ(dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping), MOJO_RESULT_OK); CHECK(mapping); - CHECK(mapping->base()); - CHECK_EQ(mapping->length(), 100u); + CHECK(mapping->GetBase()); + CHECK_EQ(mapping->GetLength(), 100u); // Write some stuff to the shared buffer. static const char kHello[] = "hello"; - memcpy(mapping->base(), kHello, sizeof(kHello)); + memcpy(mapping->GetBase(), kHello, sizeof(kHello)); // We should be able to close the dispatcher now. dispatcher->Close(); @@ -404,7 +405,7 @@ MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) { // It should have written something to the shared buffer. static const char kWorld[] = "world!!!"; - CHECK_EQ(memcmp(mapping->base(), kWorld, sizeof(kWorld)), 0); + CHECK_EQ(memcmp(mapping->GetBase(), kWorld, sizeof(kWorld)), 0); // And we're done. mp->Close(0); @@ -427,20 +428,23 @@ TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) { Init(mp); // Make a shared buffer. + embedder::SimplePlatformSupport platform_support; scoped_refptr dispatcher; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + &platform_support, + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher)); ASSERT_TRUE(dispatcher); // Make a mapping. - scoped_ptr mapping; + scoped_ptr mapping; EXPECT_EQ(MOJO_RESULT_OK, dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); ASSERT_TRUE(mapping); - ASSERT_TRUE(mapping->base()); - ASSERT_EQ(100u, mapping->length()); + ASSERT_TRUE(mapping->GetBase()); + ASSERT_EQ(100u, mapping->GetLength()); // Send the shared buffer. const std::string go1("go 1"); @@ -483,11 +487,11 @@ TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) { // After we get it, the child should have written something to the shared // buffer. static const char kHello[] = "hello"; - EXPECT_EQ(0, memcmp(mapping->base(), kHello, sizeof(kHello))); + EXPECT_EQ(0, memcmp(mapping->GetBase(), kHello, sizeof(kHello))); // Now we'll write some stuff to the shared buffer. static const char kWorld[] = "world!!!"; - memcpy(mapping->base(), kWorld, sizeof(kWorld)); + memcpy(mapping->GetBase(), kWorld, sizeof(kWorld)); // And send a message to signal that we've written stuff. const std::string go3("go 3"); diff --git a/mojo/system/raw_channel.cc b/mojo/system/raw_channel.cc index 3bcee9934de40..036fa798c3408 100644 --- a/mojo/system/raw_channel.cc +++ b/mojo/system/raw_channel.cc @@ -191,13 +191,14 @@ bool RawChannel::Init(Delegate* delegate) { return false; } - if (ScheduleRead() != IO_PENDING) { + IOResult io_result = ScheduleRead(); + if (io_result != IO_PENDING) { // This will notify the delegate about the read failure. Although we're on // the I/O thread, don't call it in the nested context. message_loop_for_io_->PostTask(FROM_HERE, base::Bind(&RawChannel::OnReadCompleted, weak_ptr_factory_.GetWeakPtr(), - false, + io_result, 0)); } @@ -246,14 +247,14 @@ bool RawChannel::WriteMessage(scoped_ptr message) { return true; bool result = OnWriteCompletedNoLock( - io_result == IO_SUCCEEDED, platform_handles_written, bytes_written); + io_result, platform_handles_written, bytes_written); if (!result) { - // Even if we're on the I/O thread, don't call |OnFatalError()| in the - // nested context. + // Even if we're on the I/O thread, don't call |OnError()| in the nested + // context. message_loop_for_io_->PostTask(FROM_HERE, - base::Bind(&RawChannel::CallOnFatalError, + base::Bind(&RawChannel::CallOnError, weak_ptr_factory_.GetWeakPtr(), - Delegate::FATAL_ERROR_WRITE)); + Delegate::ERROR_WRITE)); } return result; @@ -265,7 +266,7 @@ bool RawChannel::IsWriteBufferEmpty() { return write_buffer_->message_queue_.empty(); } -void RawChannel::OnReadCompleted(bool result, size_t bytes_read) { +void RawChannel::OnReadCompleted(IOResult io_result, size_t bytes_read) { DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); if (read_stopped_) { @@ -273,18 +274,24 @@ void RawChannel::OnReadCompleted(bool result, size_t bytes_read) { return; } - IOResult io_result = result ? IO_SUCCEEDED : IO_FAILED; - // Keep reading data in a loop, and dispatch messages if enough data is // received. Exit the loop if any of the following happens: // - one or more messages were dispatched; // - the last read failed, was a partial read or would block; // - |Shutdown()| was called. do { - if (io_result != IO_SUCCEEDED) { - read_stopped_ = true; - CallOnFatalError(Delegate::FATAL_ERROR_READ); - return; + switch (io_result) { + case IO_SUCCEEDED: + break; + case IO_FAILED_SHUTDOWN: + case IO_FAILED_BROKEN: + case IO_FAILED_UNKNOWN: + read_stopped_ = true; + CallOnError(ReadIOResultToError(io_result)); + return; + case IO_PENDING: + NOTREACHED(); + return; } read_buffer_->num_valid_bytes_ += bytes_read; @@ -316,16 +323,16 @@ void RawChannel::OnReadCompleted(bool result, size_t bytes_read) { if (!message_view.IsValid(GetSerializedPlatformHandleSize(), &error_message)) { DCHECK(error_message); - LOG(WARNING) << "Received invalid message: " << error_message; + LOG(ERROR) << "Received invalid message: " << error_message; read_stopped_ = true; - CallOnFatalError(Delegate::FATAL_ERROR_READ); + CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); return; } if (message_view.type() == MessageInTransit::kTypeRawChannel) { if (!OnReadMessageForRawChannel(message_view)) { read_stopped_ = true; - CallOnFatalError(Delegate::FATAL_ERROR_READ); + CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); return; } } else { @@ -343,9 +350,9 @@ void RawChannel::OnReadCompleted(bool result, size_t bytes_read) { GetReadPlatformHandles(num_platform_handles, platform_handle_table).Pass(); if (!platform_handles) { - LOG(WARNING) << "Invalid number of platform handles received"; + LOG(ERROR) << "Invalid number of platform handles received"; read_stopped_ = true; - CallOnFatalError(Delegate::FATAL_ERROR_READ); + CallOnError(Delegate::ERROR_READ_BAD_MESSAGE); return; } } @@ -410,10 +417,11 @@ void RawChannel::OnReadCompleted(bool result, size_t bytes_read) { } while (io_result != IO_PENDING); } -void RawChannel::OnWriteCompleted(bool result, +void RawChannel::OnWriteCompleted(IOResult io_result, size_t platform_handles_written, size_t bytes_written) { DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); + DCHECK_NE(io_result, IO_PENDING); bool did_fail = false; { @@ -426,11 +434,11 @@ void RawChannel::OnWriteCompleted(bool result, } did_fail = !OnWriteCompletedNoLock( - result, platform_handles_written, bytes_written); + io_result, platform_handles_written, bytes_written); } if (did_fail) - CallOnFatalError(Delegate::FATAL_ERROR_WRITE); + CallOnError(Delegate::ERROR_WRITE); } void RawChannel::EnqueueMessageNoLock(scoped_ptr message) { @@ -446,14 +454,32 @@ bool RawChannel::OnReadMessageForRawChannel( return false; } -void RawChannel::CallOnFatalError(Delegate::FatalError fatal_error) { +// static +RawChannel::Delegate::Error RawChannel::ReadIOResultToError( + IOResult io_result) { + switch (io_result) { + case IO_FAILED_SHUTDOWN: + return Delegate::ERROR_READ_SHUTDOWN; + case IO_FAILED_BROKEN: + return Delegate::ERROR_READ_BROKEN; + case IO_FAILED_UNKNOWN: + return Delegate::ERROR_READ_UNKNOWN; + case IO_SUCCEEDED: + case IO_PENDING: + NOTREACHED(); + break; + } + return Delegate::ERROR_READ_UNKNOWN; +} + +void RawChannel::CallOnError(Delegate::Error error) { DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io_); // TODO(vtl): Add a "write_lock_.AssertNotAcquired()"? if (delegate_) - delegate_->OnFatalError(fatal_error); + delegate_->OnError(error); } -bool RawChannel::OnWriteCompletedNoLock(bool result, +bool RawChannel::OnWriteCompletedNoLock(IOResult io_result, size_t platform_handles_written, size_t bytes_written) { write_lock_.AssertAcquired(); @@ -461,7 +487,7 @@ bool RawChannel::OnWriteCompletedNoLock(bool result, DCHECK(!write_stopped_); DCHECK(!write_buffer_->message_queue_.empty()); - if (result) { + if (io_result == IO_SUCCEEDED) { write_buffer_->platform_handles_offset_ += platform_handles_written; write_buffer_->data_offset_ += bytes_written; @@ -479,10 +505,10 @@ bool RawChannel::OnWriteCompletedNoLock(bool result, } // Schedule the next write. - IOResult io_result = ScheduleWriteNoLock(); + io_result = ScheduleWriteNoLock(); if (io_result == IO_PENDING) return true; - DCHECK_EQ(io_result, IO_FAILED); + DCHECK_NE(io_result, IO_SUCCEEDED); } write_stopped_ = true; diff --git a/mojo/system/raw_channel.h b/mojo/system/raw_channel.h index 4a35da0cdbf8a..c40f43c314069 100644 --- a/mojo/system/raw_channel.h +++ b/mojo/system/raw_channel.h @@ -48,7 +48,19 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { // (passed in on creation). class MOJO_SYSTEM_IMPL_EXPORT Delegate { public: - enum FatalError { FATAL_ERROR_READ = 0, FATAL_ERROR_WRITE }; + enum Error { + // Failed read due to raw channel shutdown (e.g., on the other side). + ERROR_READ_SHUTDOWN, + // Failed read due to raw channel being broken (e.g., if the other side + // died without shutting down). + ERROR_READ_BROKEN, + // Received a bad message. + ERROR_READ_BAD_MESSAGE, + // Unknown read error. + ERROR_READ_UNKNOWN, + // Generic write error. + ERROR_WRITE + }; // Called when a message is read. This may call |Shutdown()| (on the // |RawChannel|), but must not destroy it. @@ -56,15 +68,13 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { const MessageInTransit::View& message_view, embedder::ScopedPlatformHandleVectorPtr platform_handles) = 0; - // Called when there's a fatal error, which leads to the channel no longer - // being viable. This may call |Shutdown()| (on the |RawChannel()|), but - // must not destroy it. + // Called when there's a (fatal) error. This may call the raw channel's + // |Shutdown()|, but must not destroy it. // - // For each raw channel, at most one |FATAL_ERROR_READ| and at most one - // |FATAL_ERROR_WRITE| notification will be issued (both may be issued). - // After a |OnFatalError(FATAL_ERROR_READ)|, there will be no further calls - // to |OnReadMessage()|. - virtual void OnFatalError(FatalError fatal_error) = 0; + // For each raw channel, there'll be at most one |ERROR_READ_...| and at + // most one |ERROR_WRITE| notification. After |OnError(ERROR_READ_...)|, + // |OnReadMessage()| won't be called again. + virtual void OnError(Error error) = 0; protected: virtual ~Delegate() {} @@ -103,8 +113,17 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { virtual size_t GetSerializedPlatformHandleSize() const = 0; protected: - // Return values of |[Schedule]Read()| and |[Schedule]WriteNoLock()|. - enum IOResult { IO_SUCCEEDED, IO_FAILED, IO_PENDING }; + // Result of I/O operations. + enum IOResult { + IO_SUCCEEDED, + // Failed due to a (probably) clean shutdown (e.g., of the other end). + IO_FAILED_SHUTDOWN, + // Failed due to the connection being broken (e.g., the other end dying). + IO_FAILED_BROKEN, + // Failed due to some other (unexpected) reason. + IO_FAILED_UNKNOWN, + IO_PENDING + }; class MOJO_SYSTEM_IMPL_EXPORT ReadBuffer { public: @@ -179,10 +198,12 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { RawChannel(); - // Must be called on the I/O thread WITHOUT |write_lock_| held. - void OnReadCompleted(bool result, size_t bytes_read); - // Must be called on the I/O thread WITHOUT |write_lock_| held. - void OnWriteCompleted(bool result, + // |result| must not be |IO_PENDING|. Must be called on the I/O thread WITHOUT + // |write_lock_| held. + void OnReadCompleted(IOResult io_result, size_t bytes_read); + // |result| must not be |IO_PENDING|. Must be called on the I/O thread WITHOUT + // |write_lock_| held. + void OnWriteCompleted(IOResult io_result, size_t platform_handles_written, size_t bytes_written); @@ -226,7 +247,7 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { virtual IOResult Read(size_t* bytes_read) = 0; // Similar to |Read()|, except that the implementing subclass must also // guarantee that the method doesn't succeed synchronously, i.e., it only - // returns |IO_FAILED| or |IO_PENDING|. + // returns |IO_FAILED_...| or |IO_PENDING|. virtual IOResult ScheduleRead() = 0; // Called by |OnReadCompleted()| to get the platform handles associated with @@ -255,7 +276,7 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { size_t* bytes_written) = 0; // Similar to |WriteNoLock()|, except that the implementing subclass must also // guarantee that the method doesn't succeed synchronously, i.e., it only - // returns |IO_FAILED| or |IO_PENDING|. + // returns |IO_FAILED_...| or |IO_PENDING|. virtual IOResult ScheduleWriteNoLock() = 0; // Must be called on the I/O thread WITHOUT |write_lock_| held. @@ -267,16 +288,19 @@ class MOJO_SYSTEM_IMPL_EXPORT RawChannel { scoped_ptr write_buffer) = 0; private: - // Calls |delegate_->OnFatalError(fatal_error)|. Must be called on the I/O - // thread WITHOUT |write_lock_| held. - void CallOnFatalError(Delegate::FatalError fatal_error); - - // If |result| is true, updates the write buffer and schedules a write - // operation to run later if there are more contents to write. If |result| is - // false or any error occurs during the method execution, cancels pending - // writes and returns false. - // Must be called only if |write_stopped_| is false and under |write_lock_|. - bool OnWriteCompletedNoLock(bool result, + // Converts an |IO_FAILED_...| for a read to a |Delegate::Error|. + static Delegate::Error ReadIOResultToError(IOResult io_result); + + // Calls |delegate_->OnError(error)|. Must be called on the I/O thread WITHOUT + // |write_lock_| held. + void CallOnError(Delegate::Error error); + + // If |io_result| is |IO_SUCCESS|, updates the write buffer and schedules a + // write operation to run later if there is more to write. If |io_result| is + // failure or any other error occurs, cancels pending writes and returns + // false. Must be called under |write_lock_| and only if |write_stopped_| is + // false. + bool OnWriteCompletedNoLock(IOResult io_result, size_t platform_handles_written, size_t bytes_written); diff --git a/mojo/system/raw_channel_posix.cc b/mojo/system/raw_channel_posix.cc index a08e4e7038171..01453f63be484 100644 --- a/mojo/system/raw_channel_posix.cc +++ b/mojo/system/raw_channel_posix.cc @@ -63,6 +63,9 @@ class RawChannelPosix : public RawChannel, virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE; virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE; + // Implements most of |Read()| (except for a bit of clean-up): + IOResult ReadImpl(size_t* bytes_read); + // Watches for |fd_| to become writable. Must be called on the I/O thread. void WaitToWrite(); @@ -173,48 +176,12 @@ RawChannel::IOResult RawChannelPosix::Read(size_t* bytes_read) { DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); DCHECK(!pending_read_); - char* buffer = NULL; - size_t bytes_to_read = 0; - read_buffer()->GetBuffer(&buffer, &bytes_to_read); - - size_t old_num_platform_handles = read_platform_handles_.size(); - ssize_t read_result = embedder::PlatformChannelRecvmsg( - fd_.get(), buffer, bytes_to_read, &read_platform_handles_); - if (read_platform_handles_.size() > old_num_platform_handles) { - DCHECK_LE(read_platform_handles_.size() - old_num_platform_handles, - embedder::kPlatformChannelMaxNumHandles); - - // We should never accumulate more than |TransportData::kMaxPlatformHandles - // + embedder::kPlatformChannelMaxNumHandles| handles. (The latter part is - // possible because we could have accumulated all the handles for a message, - // then received the message data plus the first set of handles for the next - // message in the subsequent |recvmsg()|.) - if (read_platform_handles_.size() > - (TransportData::kMaxPlatformHandles + - embedder::kPlatformChannelMaxNumHandles)) { - LOG(WARNING) << "Received too many platform handles"; - embedder::CloseAllPlatformHandles(&read_platform_handles_); - read_platform_handles_.clear(); - return IO_FAILED; - } - } - - if (read_result > 0) { - *bytes_read = static_cast(read_result); - return IO_SUCCEEDED; - } - - // |read_result == 0| means "end of file". - if (read_result == 0 || (errno != EAGAIN && errno != EWOULDBLOCK)) { - PLOG_IF(WARNING, read_result != 0) << "recvmsg"; - + IOResult rv = ReadImpl(bytes_read); + if (rv != IO_SUCCEEDED && rv != IO_PENDING) { // Make sure that |OnFileCanReadWithoutBlocking()| won't be called again. read_watcher_.reset(); - - return IO_FAILED; } - - return ScheduleRead(); + return rv; } RawChannel::IOResult RawChannelPosix::ScheduleRead() { @@ -309,9 +276,12 @@ RawChannel::IOResult RawChannelPosix::WriteNoLock( return IO_SUCCEEDED; } + if (errno == EPIPE) + return IO_FAILED_SHUTDOWN; + if (errno != EAGAIN && errno != EWOULDBLOCK) { - PLOG(ERROR) << "sendmsg/write/writev"; - return IO_FAILED; + PLOG(WARNING) << "sendmsg/write/writev"; + return IO_FAILED_UNKNOWN; } return ScheduleWriteNoLock(); @@ -342,7 +312,7 @@ RawChannel::IOResult RawChannelPosix::ScheduleWriteNoLock() { return IO_PENDING; } - return IO_FAILED; + return IO_FAILED_UNKNOWN; } bool RawChannelPosix::OnInit() { @@ -399,9 +369,9 @@ void RawChannelPosix::OnFileCanReadWithoutBlocking(int fd) { pending_read_ = false; size_t bytes_read = 0; - IOResult result = Read(&bytes_read); - if (result != IO_PENDING) - OnReadCompleted(result == IO_SUCCEEDED, bytes_read); + IOResult io_result = Read(&bytes_read); + if (io_result != IO_PENDING) + OnReadCompleted(io_result, bytes_read); // On failure, |read_watcher_| must have been reset; on success, // we assume that |OnReadCompleted()| always schedules another read. @@ -418,7 +388,7 @@ void RawChannelPosix::OnFileCanWriteWithoutBlocking(int fd) { DCHECK_EQ(fd, fd_.get().fd); DCHECK_EQ(base::MessageLoop::current(), message_loop_for_io()); - IOResult result = IO_FAILED; + IOResult io_result; size_t platform_handles_written = 0; size_t bytes_written = 0; { @@ -427,13 +397,57 @@ void RawChannelPosix::OnFileCanWriteWithoutBlocking(int fd) { DCHECK(pending_write_); pending_write_ = false; - result = WriteNoLock(&platform_handles_written, &bytes_written); + io_result = WriteNoLock(&platform_handles_written, &bytes_written); } - if (result != IO_PENDING) { - OnWriteCompleted( - result == IO_SUCCEEDED, platform_handles_written, bytes_written); + if (io_result != IO_PENDING) + OnWriteCompleted(io_result, platform_handles_written, bytes_written); +} + +RawChannel::IOResult RawChannelPosix::ReadImpl(size_t* bytes_read) { + char* buffer = NULL; + size_t bytes_to_read = 0; + read_buffer()->GetBuffer(&buffer, &bytes_to_read); + + size_t old_num_platform_handles = read_platform_handles_.size(); + ssize_t read_result = embedder::PlatformChannelRecvmsg( + fd_.get(), buffer, bytes_to_read, &read_platform_handles_); + if (read_platform_handles_.size() > old_num_platform_handles) { + DCHECK_LE(read_platform_handles_.size() - old_num_platform_handles, + embedder::kPlatformChannelMaxNumHandles); + + // We should never accumulate more than |TransportData::kMaxPlatformHandles + // + embedder::kPlatformChannelMaxNumHandles| handles. (The latter part is + // possible because we could have accumulated all the handles for a message, + // then received the message data plus the first set of handles for the next + // message in the subsequent |recvmsg()|.) + if (read_platform_handles_.size() > + (TransportData::kMaxPlatformHandles + + embedder::kPlatformChannelMaxNumHandles)) { + LOG(ERROR) << "Received too many platform handles"; + embedder::CloseAllPlatformHandles(&read_platform_handles_); + read_platform_handles_.clear(); + return IO_FAILED_UNKNOWN; + } } + + if (read_result > 0) { + *bytes_read = static_cast(read_result); + return IO_SUCCEEDED; + } + + // |read_result == 0| means "end of file". + if (read_result == 0) + return IO_FAILED_SHUTDOWN; + + if (errno == EAGAIN || errno == EWOULDBLOCK) + return ScheduleRead(); + + if (errno == ECONNRESET) + return IO_FAILED_BROKEN; + + PLOG(WARNING) << "recvmsg"; + return IO_FAILED_UNKNOWN; } void RawChannelPosix::WaitToWrite() { @@ -453,7 +467,7 @@ void RawChannelPosix::WaitToWrite() { DCHECK(pending_write_); pending_write_ = false; } - OnWriteCompleted(false, 0, 0); + OnWriteCompleted(IO_FAILED_UNKNOWN, 0, 0); } } diff --git a/mojo/system/raw_channel_unittest.cc b/mojo/system/raw_channel_unittest.cc index 2158b76f82fa1..eaea1ed7e10e8 100644 --- a/mojo/system/raw_channel_unittest.cc +++ b/mojo/system/raw_channel_unittest.cc @@ -111,9 +111,9 @@ class WriteOnlyRawChannelDelegate : public RawChannel::Delegate { embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) OVERRIDE { CHECK(false); // Should not get called. } - virtual void OnFatalError(FatalError fatal_error) OVERRIDE { - // We'll get a read error when the connection is closed. - CHECK_EQ(fatal_error, FATAL_ERROR_READ); + virtual void OnError(Error error) OVERRIDE { + // We'll get a read (shutdown) error when the connection is closed. + CHECK_EQ(error, ERROR_READ_SHUTDOWN); } private: @@ -250,9 +250,9 @@ class ReadCheckerRawChannelDelegate : public RawChannel::Delegate { if (should_signal) done_event_.Signal(); } - virtual void OnFatalError(FatalError fatal_error) OVERRIDE { - // We'll get a read error when the connection is closed. - CHECK_EQ(fatal_error, FATAL_ERROR_READ); + virtual void OnError(Error error) OVERRIDE { + // We'll get a read (shutdown) error when the connection is closed. + CHECK_EQ(error, ERROR_READ_SHUTDOWN); } // Waits for all the messages (of sizes |expected_sizes_|) to be seen. @@ -354,9 +354,9 @@ class ReadCountdownRawChannelDelegate : public RawChannel::Delegate { if (count_ >= expected_count_) done_event_.Signal(); } - virtual void OnFatalError(FatalError fatal_error) OVERRIDE { - // We'll get a read error when the connection is closed. - CHECK_EQ(fatal_error, FATAL_ERROR_READ); + virtual void OnError(Error error) OVERRIDE { + // We'll get a read (shutdown) error when the connection is closed. + CHECK_EQ(error, ERROR_READ_SHUTDOWN); } // Waits for all the messages to have been seen. @@ -415,53 +415,65 @@ TEST_F(RawChannelTest, WriteMessageAndOnReadMessage) { base::Bind(&RawChannel::Shutdown, base::Unretained(writer_rc.get()))); } -// RawChannelTest.OnFatalError ------------------------------------------------- +// RawChannelTest.OnError ------------------------------------------------------ -class FatalErrorRecordingRawChannelDelegate +class ErrorRecordingRawChannelDelegate : public ReadCountdownRawChannelDelegate { public: - FatalErrorRecordingRawChannelDelegate(size_t expected_read_count, - bool expect_read_error, - bool expect_write_error) + ErrorRecordingRawChannelDelegate(size_t expected_read_count, + bool expect_read_error, + bool expect_write_error) : ReadCountdownRawChannelDelegate(expected_read_count), - got_read_fatal_error_event_(false, false), - got_write_fatal_error_event_(false, false), + got_read_error_event_(false, false), + got_write_error_event_(false, false), expecting_read_error_(expect_read_error), expecting_write_error_(expect_write_error) {} - virtual ~FatalErrorRecordingRawChannelDelegate() {} + virtual ~ErrorRecordingRawChannelDelegate() {} - virtual void OnFatalError(FatalError fatal_error) OVERRIDE { - switch (fatal_error) { - case FATAL_ERROR_READ: + virtual void OnError(Error error) OVERRIDE { + switch (error) { + case ERROR_READ_SHUTDOWN: ASSERT_TRUE(expecting_read_error_); expecting_read_error_ = false; - got_read_fatal_error_event_.Signal(); + got_read_error_event_.Signal(); break; - case FATAL_ERROR_WRITE: + case ERROR_READ_BROKEN: + // TODO(vtl): Test broken connections. + CHECK(false); + break; + case ERROR_READ_BAD_MESSAGE: + // TODO(vtl): Test reception/detection of bad messages. + CHECK(false); + break; + case ERROR_READ_UNKNOWN: + // TODO(vtl): Test however it is we might get here. + CHECK(false); + break; + case ERROR_WRITE: ASSERT_TRUE(expecting_write_error_); expecting_write_error_ = false; - got_write_fatal_error_event_.Signal(); + got_write_error_event_.Signal(); break; } } - void WaitForReadFatalError() { got_read_fatal_error_event_.Wait(); } - void WaitForWriteFatalError() { got_write_fatal_error_event_.Wait(); } + void WaitForReadError() { got_read_error_event_.Wait(); } + void WaitForWriteError() { got_write_error_event_.Wait(); } private: - base::WaitableEvent got_read_fatal_error_event_; - base::WaitableEvent got_write_fatal_error_event_; + base::WaitableEvent got_read_error_event_; + base::WaitableEvent got_write_error_event_; bool expecting_read_error_; bool expecting_write_error_; - DISALLOW_COPY_AND_ASSIGN(FatalErrorRecordingRawChannelDelegate); + DISALLOW_COPY_AND_ASSIGN(ErrorRecordingRawChannelDelegate); }; -// Tests fatal errors. -TEST_F(RawChannelTest, OnFatalError) { - FatalErrorRecordingRawChannelDelegate delegate(0, true, true); +// Tests (fatal) errors. +TEST_F(RawChannelTest, OnError) { + ErrorRecordingRawChannelDelegate delegate(0, true, true); scoped_ptr rc(RawChannel::Create(handles[0].Pass())); io_thread()->PostTaskAndWait( FROM_HERE, @@ -472,25 +484,25 @@ TEST_F(RawChannelTest, OnFatalError) { EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1))); - // We should get a write fatal error. - delegate.WaitForWriteFatalError(); + // We should get a write error. + delegate.WaitForWriteError(); - // We should also get a read fatal error. - delegate.WaitForReadFatalError(); + // We should also get a read error. + delegate.WaitForReadError(); EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(2))); - // Sleep a bit, to make sure we don't get another |OnFatalError()| - // notification. (If we actually get another one, |OnFatalError()| crashes.) + // Sleep a bit, to make sure we don't get another |OnError()| + // notification. (If we actually get another one, |OnError()| crashes.) base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(20)); io_thread()->PostTaskAndWait( FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); } -// RawChannelTest.ReadUnaffectedByWriteFatalError ------------------------------ +// RawChannelTest.ReadUnaffectedByWriteError ----------------------------------- -TEST_F(RawChannelTest, ReadUnaffectedByWriteFatalError) { +TEST_F(RawChannelTest, ReadUnaffectedByWriteError) { const size_t kMessageCount = 5; // Write a few messages into the other end. @@ -504,7 +516,7 @@ TEST_F(RawChannelTest, ReadUnaffectedByWriteFatalError) { // Only start up reading here. The system buffer should still contain the // messages that were written. - FatalErrorRecordingRawChannelDelegate delegate(kMessageCount, true, true); + ErrorRecordingRawChannelDelegate delegate(kMessageCount, true, true); scoped_ptr rc(RawChannel::Create(handles[0].Pass())); io_thread()->PostTaskAndWait( FROM_HERE, @@ -512,14 +524,14 @@ TEST_F(RawChannelTest, ReadUnaffectedByWriteFatalError) { EXPECT_FALSE(rc->WriteMessage(MakeTestMessage(1))); - // We should definitely get a write fatal error. - delegate.WaitForWriteFatalError(); + // We should definitely get a write error. + delegate.WaitForWriteError(); // Wait for reading to finish. A writing failure shouldn't affect reading. delegate.Wait(); - // And then we should get a read fatal error. - delegate.WaitForReadFatalError(); + // And then we should get a read error. + delegate.WaitForReadError(); io_thread()->PostTaskAndWait( FROM_HERE, base::Bind(&RawChannel::Shutdown, base::Unretained(rc.get()))); @@ -563,7 +575,7 @@ class ShutdownOnReadMessageRawChannelDelegate : public RawChannel::Delegate { did_shutdown_ = true; done_event_.Signal(); } - virtual void OnFatalError(FatalError /*fatal_error*/) OVERRIDE { + virtual void OnError(Error /*error*/) OVERRIDE { CHECK(false); // Should not get called. } @@ -596,17 +608,17 @@ TEST_F(RawChannelTest, ShutdownOnReadMessage) { delegate.Wait(); } -// RawChannelTest.ShutdownOnFatalError{Read, Write} ---------------------------- +// RawChannelTest.ShutdownOnError{Read, Write} --------------------------------- -class ShutdownOnFatalErrorRawChannelDelegate : public RawChannel::Delegate { +class ShutdownOnErrorRawChannelDelegate : public RawChannel::Delegate { public: - ShutdownOnFatalErrorRawChannelDelegate(RawChannel* raw_channel, - FatalError shutdown_on_error_type) + ShutdownOnErrorRawChannelDelegate(RawChannel* raw_channel, + Error shutdown_on_error_type) : raw_channel_(raw_channel), shutdown_on_error_type_(shutdown_on_error_type), done_event_(false, false), did_shutdown_(false) {} - virtual ~ShutdownOnFatalErrorRawChannelDelegate() {} + virtual ~ShutdownOnErrorRawChannelDelegate() {} // |RawChannel::Delegate| implementation (called on the I/O thread): virtual void OnReadMessage( @@ -614,9 +626,9 @@ class ShutdownOnFatalErrorRawChannelDelegate : public RawChannel::Delegate { embedder::ScopedPlatformHandleVectorPtr /*platform_handles*/) OVERRIDE { CHECK(false); // Should not get called. } - virtual void OnFatalError(FatalError fatal_error) OVERRIDE { + virtual void OnError(Error error) OVERRIDE { EXPECT_FALSE(did_shutdown_); - if (fatal_error != shutdown_on_error_type_) + if (error != shutdown_on_error_type_) return; raw_channel_->Shutdown(); did_shutdown_ = true; @@ -631,17 +643,17 @@ class ShutdownOnFatalErrorRawChannelDelegate : public RawChannel::Delegate { private: RawChannel* const raw_channel_; - const FatalError shutdown_on_error_type_; + const Error shutdown_on_error_type_; base::WaitableEvent done_event_; bool did_shutdown_; - DISALLOW_COPY_AND_ASSIGN(ShutdownOnFatalErrorRawChannelDelegate); + DISALLOW_COPY_AND_ASSIGN(ShutdownOnErrorRawChannelDelegate); }; -TEST_F(RawChannelTest, ShutdownOnFatalErrorRead) { +TEST_F(RawChannelTest, ShutdownOnErrorRead) { scoped_ptr rc(RawChannel::Create(handles[0].Pass())); - ShutdownOnFatalErrorRawChannelDelegate delegate( - rc.get(), RawChannel::Delegate::FATAL_ERROR_READ); + ShutdownOnErrorRawChannelDelegate delegate( + rc.get(), RawChannel::Delegate::ERROR_READ_SHUTDOWN); io_thread()->PostTaskAndWait( FROM_HERE, base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); @@ -653,10 +665,10 @@ TEST_F(RawChannelTest, ShutdownOnFatalErrorRead) { delegate.Wait(); } -TEST_F(RawChannelTest, ShutdownOnFatalErrorWrite) { +TEST_F(RawChannelTest, ShutdownOnErrorWrite) { scoped_ptr rc(RawChannel::Create(handles[0].Pass())); - ShutdownOnFatalErrorRawChannelDelegate delegate( - rc.get(), RawChannel::Delegate::FATAL_ERROR_WRITE); + ShutdownOnErrorRawChannelDelegate delegate(rc.get(), + RawChannel::Delegate::ERROR_WRITE); io_thread()->PostTaskAndWait( FROM_HERE, base::Bind(&InitOnIOThread, rc.get(), base::Unretained(&delegate))); diff --git a/mojo/system/raw_channel_win.cc b/mojo/system/raw_channel_win.cc index fee6c86f854de..0132795e135a2 100644 --- a/mojo/system/raw_channel_win.cc +++ b/mojo/system/raw_channel_win.cc @@ -303,14 +303,16 @@ void RawChannelWin::RawChannelIOHandler::OnReadCompleted(DWORD bytes_read, if (!owner_) return; - if (error != ERROR_SUCCESS) { + if (error == ERROR_SUCCESS) { + DCHECK_GT(bytes_read, 0u); + owner_->OnReadCompleted(IO_SUCCEEDED, bytes_read); + } else if (error == ERROR_BROKEN_PIPE) { DCHECK_EQ(bytes_read, 0u); - LOG_IF(ERROR, error != ERROR_BROKEN_PIPE) - << "ReadFile: " << logging::SystemErrorCodeToString(error); - owner_->OnReadCompleted(false, 0); + owner_->OnReadCompleted(IO_FAILED_SHUTDOWN, 0); } else { - DCHECK_GT(bytes_read, 0u); - owner_->OnReadCompleted(true, bytes_read); + DCHECK_EQ(bytes_read, 0u); + LOG(WARNING) << "ReadFile: " << logging::SystemErrorCodeToString(error); + owner_->OnReadCompleted(IO_FAILED_UNKNOWN, 0); } } @@ -333,11 +335,13 @@ void RawChannelWin::RawChannelIOHandler::OnWriteCompleted(DWORD bytes_written, pending_write_ = false; } - if (error != ERROR_SUCCESS) { - LOG(ERROR) << "WriteFile: " << logging::SystemErrorCodeToString(error); - owner_->OnWriteCompleted(false, 0, 0); + if (error == ERROR_SUCCESS) { + owner_->OnWriteCompleted(IO_SUCCEEDED, 0, bytes_written); + } else if (error == ERROR_BROKEN_PIPE) { + owner_->OnWriteCompleted(IO_FAILED_SHUTDOWN, 0, 0); } else { - owner_->OnWriteCompleted(true, 0, bytes_written); + LOG(WARNING) << "WriteFile: " << logging::SystemErrorCodeToString(error); + owner_->OnWriteCompleted(IO_FAILED_UNKNOWN, 0, 0); } } @@ -376,10 +380,11 @@ RawChannel::IOResult RawChannelWin::Read(size_t* bytes_read) { if (!result) { DCHECK_EQ(bytes_read_dword, 0u); DWORD error = GetLastError(); + if (error == ERROR_BROKEN_PIPE) + return IO_FAILED_SHUTDOWN; if (error != ERROR_IO_PENDING) { - LOG_IF(ERROR, error != ERROR_BROKEN_PIPE) - << "ReadFile: " << logging::SystemErrorCodeToString(error); - return IO_FAILED; + LOG(WARNING) << "ReadFile: " << logging::SystemErrorCodeToString(error); + return IO_FAILED_UNKNOWN; } } @@ -460,9 +465,14 @@ RawChannel::IOResult RawChannelWin::WriteNoLock( static_cast(buffers[0].size), &bytes_written_dword, &io_handler_->write_context_no_lock()->overlapped); - if (!result && GetLastError() != ERROR_IO_PENDING) { - PLOG(ERROR) << "WriteFile"; - return IO_FAILED; + if (!result) { + DWORD error = GetLastError(); + if (error == ERROR_BROKEN_PIPE) + return IO_FAILED_SHUTDOWN; + if (error != ERROR_IO_PENDING) { + LOG(WARNING) << "WriteFile: " << logging::SystemErrorCodeToString(error); + return IO_FAILED_UNKNOWN; + } } if (result && skip_completion_port_on_success_) { diff --git a/mojo/system/raw_shared_buffer.cc b/mojo/system/raw_shared_buffer.cc deleted file mode 100644 index e6e3ebce2390e..0000000000000 --- a/mojo/system/raw_shared_buffer.cc +++ /dev/null @@ -1,86 +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/system/raw_shared_buffer.h" - -#include "base/logging.h" -#include "mojo/embedder/platform_handle_utils.h" - -namespace mojo { -namespace system { - -// static -RawSharedBuffer* RawSharedBuffer::Create(size_t num_bytes) { - DCHECK_GT(num_bytes, 0u); - - RawSharedBuffer* rv = new RawSharedBuffer(num_bytes); - if (!rv->Init()) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr deleter(rv); - return NULL; - } - - return rv; -} - -RawSharedBuffer* RawSharedBuffer::CreateFromPlatformHandle( - size_t num_bytes, - embedder::ScopedPlatformHandle platform_handle) { - DCHECK_GT(num_bytes, 0u); - - RawSharedBuffer* rv = new RawSharedBuffer(num_bytes); - if (!rv->InitFromPlatformHandle(platform_handle.Pass())) { - // We can't just delete it directly, due to the "in destructor" (debug) - // check. - scoped_refptr deleter(rv); - return NULL; - } - - return rv; -} - -scoped_ptr RawSharedBuffer::Map(size_t offset, - size_t length) { - if (!IsValidMap(offset, length)) - return scoped_ptr(); - - return MapNoCheck(offset, length); -} - -bool RawSharedBuffer::IsValidMap(size_t offset, size_t length) { - if (offset > num_bytes_ || length == 0) - return false; - - // Note: This is an overflow-safe check of |offset + length > num_bytes_| - // (that |num_bytes >= offset| is verified above). - if (length > num_bytes_ - offset) - return false; - - return true; -} - -scoped_ptr RawSharedBuffer::MapNoCheck(size_t offset, - size_t length) { - DCHECK(IsValidMap(offset, length)); - return MapImpl(offset, length); -} - -embedder::ScopedPlatformHandle RawSharedBuffer::DuplicatePlatformHandle() { - return embedder::DuplicatePlatformHandle(handle_.get()); -} - -embedder::ScopedPlatformHandle RawSharedBuffer::PassPlatformHandle() { - DCHECK(HasOneRef()); - return handle_.Pass(); -} - -RawSharedBuffer::RawSharedBuffer(size_t num_bytes) : num_bytes_(num_bytes) { -} - -RawSharedBuffer::~RawSharedBuffer() { -} - -} // namespace system -} // namespace mojo diff --git a/mojo/system/raw_shared_buffer.h b/mojo/system/raw_shared_buffer.h deleted file mode 100644 index cc2a787cf7c7f..0000000000000 --- a/mojo/system/raw_shared_buffer.h +++ /dev/null @@ -1,139 +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_SYSTEM_RAW_SHARED_BUFFER_H_ -#define MOJO_SYSTEM_RAW_SHARED_BUFFER_H_ - -#include - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "mojo/embedder/scoped_platform_handle.h" -#include "mojo/system/system_impl_export.h" - -namespace mojo { -namespace system { - -class RawSharedBufferMapping; - -// |RawSharedBuffer| is a thread-safe, ref-counted wrapper around OS-specific -// shared memory. It has the following features: -// - A |RawSharedBuffer| simply represents a piece of shared memory that *may* -// be mapped and *may* be shared to another process. -// - A single |RawSharedBuffer| may be mapped multiple times. The lifetime of -// the mapping (owned by |RawSharedBufferMapping|) is separate from the -// lifetime of the |RawSharedBuffer|. -// - Sizes/offsets (of the shared memory and mappings) are arbitrary, and not -// restricted by page size. However, more memory may actually be mapped than -// requested. -// -// It currently does NOT support the following: -// - Sharing read-only. (This will probably eventually be supported.) -// -// TODO(vtl): Rectify this with |base::SharedMemory|. -class MOJO_SYSTEM_IMPL_EXPORT RawSharedBuffer - : public base::RefCountedThreadSafe { - public: - // Creates a shared buffer of size |num_bytes| bytes (initially zero-filled). - // |num_bytes| must be nonzero. Returns null on failure. - static RawSharedBuffer* Create(size_t num_bytes); - - static RawSharedBuffer* CreateFromPlatformHandle( - size_t num_bytes, - embedder::ScopedPlatformHandle platform_handle); - - // Maps (some) of the shared buffer into memory; [|offset|, |offset + length|] - // must be contained in [0, |num_bytes|], and |length| must be at least 1. - // Returns null on failure. - scoped_ptr Map(size_t offset, size_t length); - - // Checks if |offset| and |length| are valid arguments. - bool IsValidMap(size_t offset, size_t length); - - // Like |Map()|, but doesn't check its arguments (which should have been - // preflighted using |IsValidMap()|). - scoped_ptr MapNoCheck(size_t offset, size_t length); - - // Duplicates the underlying platform handle and passes it to the caller. - embedder::ScopedPlatformHandle DuplicatePlatformHandle(); - - // Passes the underlying platform handle to the caller. This should only be - // called if there's a unique reference to this object (owned by the caller). - // After calling this, this object should no longer be used, but should only - // be disposed of. - embedder::ScopedPlatformHandle PassPlatformHandle(); - - size_t num_bytes() const { return num_bytes_; } - - private: - friend class base::RefCountedThreadSafe; - - explicit RawSharedBuffer(size_t num_bytes); - ~RawSharedBuffer(); - - // Implemented in raw_shared_buffer_{posix,win}.cc: - - // This is called by |Create()| before this object is given to anyone. - bool Init(); - - // This is like |Init()|, but for |CreateFromPlatformHandle()|. (Note: It - // should verify that |platform_handle| is an appropriate handle for the - // claimed |num_bytes_|.) - bool InitFromPlatformHandle(embedder::ScopedPlatformHandle platform_handle); - - // The platform-dependent part of |Map()|; doesn't check arguments. - scoped_ptr MapImpl(size_t offset, size_t length); - - const size_t num_bytes_; - - // This is set in |Init()|/|InitFromPlatformHandle()| and never modified - // (except by |PassPlatformHandle()|; see the comments above its declaration), - // hence does not need to be protected by a lock. - embedder::ScopedPlatformHandle handle_; - - DISALLOW_COPY_AND_ASSIGN(RawSharedBuffer); -}; - -// A mapping of a |RawSharedBuffer| (compararable to a "file view" in Windows); -// see above. Created by |RawSharedBuffer::Map()|. Automatically unmaps memory -// on destruction. -// -// Mappings are NOT thread-safe. -// -// Note: This is an entirely separate class (instead of -// |RawSharedBuffer::Mapping|) so that it can be forward-declared. -class MOJO_SYSTEM_IMPL_EXPORT RawSharedBufferMapping { - public: - ~RawSharedBufferMapping() { Unmap(); } - - void* base() const { return base_; } - size_t length() const { return length_; } - - private: - friend class RawSharedBuffer; - - RawSharedBufferMapping(void* base, - size_t length, - void* real_base, - size_t real_length) - : base_(base), - length_(length), - real_base_(real_base), - real_length_(real_length) {} - void Unmap(); - - void* const base_; - const size_t length_; - - void* const real_base_; - const size_t real_length_; - - DISALLOW_COPY_AND_ASSIGN(RawSharedBufferMapping); -}; - -} // namespace system -} // namespace mojo - -#endif // MOJO_SYSTEM_RAW_SHARED_BUFFER_H_ diff --git a/mojo/system/raw_shared_buffer_posix.cc b/mojo/system/raw_shared_buffer_posix.cc deleted file mode 100644 index 779bcd6cb0256..0000000000000 --- a/mojo/system/raw_shared_buffer_posix.cc +++ /dev/null @@ -1,155 +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/system/raw_shared_buffer.h" - -#include -#include // For |fileno()|. -#include // For |mmap()|/|munmap()|. -#include -#include // For |off_t|. -#include - -#include - -#include "base/file_util.h" -#include "base/files/file_path.h" -#include "base/files/scoped_file.h" -#include "base/logging.h" -#include "base/macros.h" -#include "base/posix/eintr_wrapper.h" -#include "base/sys_info.h" -#include "base/threading/thread_restrictions.h" -#include "mojo/embedder/platform_handle.h" - -// We assume that |size_t| and |off_t| (type for |ftruncate()|) fits in a -// |uint64_t|. -COMPILE_ASSERT(sizeof(size_t) <= sizeof(uint64_t), size_t_too_big); -COMPILE_ASSERT(sizeof(off_t) <= sizeof(uint64_t), off_t_too_big); - -namespace mojo { -namespace system { - -// RawSharedBuffer ------------------------------------------------------------- - -bool RawSharedBuffer::Init() { - DCHECK(!handle_.is_valid()); - - base::ThreadRestrictions::ScopedAllowIO allow_io; - - if (static_cast(num_bytes_) > - static_cast(std::numeric_limits::max())) { - return false; - } - - // TODO(vtl): This is stupid. The implementation of - // |CreateAndOpenTemporaryFileInDir()| starts with an FD, |fdopen()|s to get a - // |FILE*|, and then we have to |dup(fileno(fp))| to get back to an FD that we - // can own. (base/memory/shared_memory_posix.cc does this too, with more - // |fstat()|s thrown in for good measure.) - base::FilePath shared_buffer_dir; - if (!base::GetShmemTempDir(false, &shared_buffer_dir)) { - LOG(ERROR) << "Failed to get temporary directory for shared memory"; - return false; - } - base::FilePath shared_buffer_file; - base::ScopedFILE fp(base::CreateAndOpenTemporaryFileInDir( - shared_buffer_dir, &shared_buffer_file)); - if (!fp) { - LOG(ERROR) << "Failed to create/open temporary file for shared memory"; - return false; - } - // Note: |unlink()| is not interruptible. - if (unlink(shared_buffer_file.value().c_str()) != 0) { - PLOG(WARNING) << "unlink"; - // This isn't "fatal" (e.g., someone else may have unlinked the file first), - // so we may as well continue. - } - - // Note: |dup()| is not interruptible (but |dup2()|/|dup3()| are). - base::ScopedFD fd(dup(fileno(fp.get()))); - if (!fd.is_valid()) { - PLOG(ERROR) << "dup"; - return false; - } - - if (HANDLE_EINTR(ftruncate(fd.get(), static_cast(num_bytes_))) != 0) { - PLOG(ERROR) << "ftruncate"; - return false; - } - - handle_.reset(embedder::PlatformHandle(fd.release())); - return true; -} - -bool RawSharedBuffer::InitFromPlatformHandle( - embedder::ScopedPlatformHandle platform_handle) { - DCHECK(!handle_.is_valid()); - - if (static_cast(num_bytes_) > - static_cast(std::numeric_limits::max())) { - return false; - } - - struct stat sb = {}; - // Note: |fstat()| isn't interruptible. - if (fstat(platform_handle.get().fd, &sb) != 0) { - PLOG(ERROR) << "fstat"; - return false; - } - - if (!S_ISREG(sb.st_mode)) { - LOG(ERROR) << "Platform handle not to a regular file"; - return false; - } - - if (sb.st_size != static_cast(num_bytes_)) { - LOG(ERROR) << "Shared memory file has the wrong size"; - return false; - } - - // TODO(vtl): More checks? - - handle_ = platform_handle.Pass(); - return true; -} - -scoped_ptr RawSharedBuffer::MapImpl(size_t offset, - size_t length) { - size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity(); - size_t real_offset = offset - offset_rounding; - size_t real_length = length + offset_rounding; - - // This should hold (since we checked |num_bytes| versus the maximum value of - // |off_t| on creation, but it never hurts to be paranoid. - DCHECK_LE(static_cast(real_offset), - static_cast(std::numeric_limits::max())); - - void* real_base = mmap(NULL, - real_length, - PROT_READ | PROT_WRITE, - MAP_SHARED, - handle_.get().fd, - static_cast(real_offset)); - // |mmap()| should return |MAP_FAILED| (a.k.a. -1) on error. But it shouldn't - // return null either. - if (real_base == MAP_FAILED || !real_base) { - PLOG(ERROR) << "mmap"; - return scoped_ptr(); - } - - void* base = static_cast(real_base) + offset_rounding; - return make_scoped_ptr( - new RawSharedBufferMapping(base, length, real_base, real_length)); -} - -// RawSharedBufferMapping ------------------------------------------------------ - -void RawSharedBufferMapping::Unmap() { - int result = munmap(real_base_, real_length_); - PLOG_IF(ERROR, result != 0) << "munmap"; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/system/raw_shared_buffer_unittest.cc b/mojo/system/raw_shared_buffer_unittest.cc deleted file mode 100644 index 862bb832cc217..0000000000000 --- a/mojo/system/raw_shared_buffer_unittest.cc +++ /dev/null @@ -1,181 +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/system/raw_shared_buffer.h" - -#include - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace mojo { -namespace system { -namespace { - -TEST(RawSharedBufferTest, Basic) { - const size_t kNumInts = 100; - const size_t kNumBytes = kNumInts * sizeof(int); - // A fudge so that we're not just writing zero bytes 75% of the time. - const int kFudge = 1234567890; - - // Make some memory. - scoped_refptr buffer(RawSharedBuffer::Create(kNumBytes)); - ASSERT_TRUE(buffer); - - // Map it all, scribble some stuff, and then unmap it. - { - EXPECT_TRUE(buffer->IsValidMap(0, kNumBytes)); - scoped_ptr mapping(buffer->Map(0, kNumBytes)); - ASSERT_TRUE(mapping); - ASSERT_TRUE(mapping->base()); - int* stuff = static_cast(mapping->base()); - for (size_t i = 0; i < kNumInts; i++) - stuff[i] = static_cast(i) + kFudge; - } - - // Map it all again, check that our scribbling is still there, then do a - // partial mapping and scribble on that, check that everything is coherent, - // unmap the first mapping, scribble on some of the second mapping, and then - // unmap it. - { - ASSERT_TRUE(buffer->IsValidMap(0, kNumBytes)); - // Use |MapNoCheck()| this time. - scoped_ptr mapping1( - buffer->MapNoCheck(0, kNumBytes)); - ASSERT_TRUE(mapping1); - ASSERT_TRUE(mapping1->base()); - int* stuff1 = static_cast(mapping1->base()); - for (size_t i = 0; i < kNumInts; i++) - EXPECT_EQ(static_cast(i) + kFudge, stuff1[i]) << i; - - scoped_ptr mapping2( - buffer->Map((kNumInts / 2) * sizeof(int), 2 * sizeof(int))); - ASSERT_TRUE(mapping2); - ASSERT_TRUE(mapping2->base()); - int* stuff2 = static_cast(mapping2->base()); - EXPECT_EQ(static_cast(kNumInts / 2) + kFudge, stuff2[0]); - EXPECT_EQ(static_cast(kNumInts / 2) + 1 + kFudge, stuff2[1]); - - stuff2[0] = 123; - stuff2[1] = 456; - EXPECT_EQ(123, stuff1[kNumInts / 2]); - EXPECT_EQ(456, stuff1[kNumInts / 2 + 1]); - - mapping1.reset(); - - EXPECT_EQ(123, stuff2[0]); - EXPECT_EQ(456, stuff2[1]); - stuff2[1] = 789; - } - - // Do another partial mapping and check that everything is the way we expect - // it to be. - { - EXPECT_TRUE(buffer->IsValidMap(sizeof(int), kNumBytes - sizeof(int))); - scoped_ptr mapping( - buffer->Map(sizeof(int), kNumBytes - sizeof(int))); - ASSERT_TRUE(mapping); - ASSERT_TRUE(mapping->base()); - int* stuff = static_cast(mapping->base()); - - for (size_t j = 0; j < kNumInts - 1; j++) { - int i = static_cast(j) + 1; - if (i == kNumInts / 2) { - EXPECT_EQ(123, stuff[j]); - } else if (i == kNumInts / 2 + 1) { - EXPECT_EQ(789, stuff[j]); - } else { - EXPECT_EQ(i + kFudge, stuff[j]) << i; - } - } - } -} - -// TODO(vtl): Bigger buffers. - -TEST(RawSharedBufferTest, InvalidMappings) { - scoped_refptr buffer(RawSharedBuffer::Create(100)); - ASSERT_TRUE(buffer); - - // Zero length not allowed. - EXPECT_FALSE(buffer->Map(0, 0)); - EXPECT_FALSE(buffer->IsValidMap(0, 0)); - - // Okay: - EXPECT_TRUE(buffer->Map(0, 100)); - EXPECT_TRUE(buffer->IsValidMap(0, 100)); - // Offset + length too big. - EXPECT_FALSE(buffer->Map(0, 101)); - EXPECT_FALSE(buffer->IsValidMap(0, 101)); - EXPECT_FALSE(buffer->Map(1, 100)); - EXPECT_FALSE(buffer->IsValidMap(1, 100)); - - // Okay: - EXPECT_TRUE(buffer->Map(50, 50)); - EXPECT_TRUE(buffer->IsValidMap(50, 50)); - // Offset + length too big. - EXPECT_FALSE(buffer->Map(50, 51)); - EXPECT_FALSE(buffer->IsValidMap(50, 51)); - EXPECT_FALSE(buffer->Map(51, 50)); - EXPECT_FALSE(buffer->IsValidMap(51, 50)); -} - -TEST(RawSharedBufferTest, TooBig) { - // If |size_t| is 32-bit, it's quite possible/likely that |Create()| succeeds - // (since it only involves creating a 4 GB file). - const size_t kMaxSizeT = std::numeric_limits::max(); - scoped_refptr buffer(RawSharedBuffer::Create(kMaxSizeT)); - // But, assuming |sizeof(size_t) == sizeof(void*)|, mapping all of it should - // always fail. - if (buffer) - EXPECT_FALSE(buffer->Map(0, kMaxSizeT)); -} - -// Tests that separate mappings get distinct addresses. -// Note: It's not inconceivable that the OS could ref-count identical mappings -// and reuse the same address, in which case we'd have to be more careful about -// using the address as the key for unmapping. -TEST(RawSharedBufferTest, MappingsDistinct) { - scoped_refptr buffer(RawSharedBuffer::Create(100)); - scoped_ptr mapping1(buffer->Map(0, 100)); - scoped_ptr mapping2(buffer->Map(0, 100)); - EXPECT_NE(mapping1->base(), mapping2->base()); -} - -TEST(RawSharedBufferTest, BufferZeroInitialized) { - static const size_t kSizes[] = {10, 100, 1000, 10000, 100000}; - for (size_t i = 0; i < arraysize(kSizes); i++) { - scoped_refptr buffer(RawSharedBuffer::Create(kSizes[i])); - scoped_ptr mapping(buffer->Map(0, kSizes[i])); - for (size_t j = 0; j < kSizes[i]; j++) { - // "Assert" instead of "expect" so we don't spam the output with thousands - // of failures if we fail. - ASSERT_EQ('\0', static_cast(mapping->base())[j]) - << "size " << kSizes[i] << ", offset " << j; - } - } -} - -TEST(RawSharedBufferTest, MappingsOutliveBuffer) { - scoped_ptr mapping1; - scoped_ptr mapping2; - - { - scoped_refptr buffer(RawSharedBuffer::Create(100)); - mapping1 = buffer->Map(0, 100).Pass(); - mapping2 = buffer->Map(50, 50).Pass(); - static_cast(mapping1->base())[50] = 'x'; - } - - EXPECT_EQ('x', static_cast(mapping2->base())[0]); - - static_cast(mapping2->base())[1] = 'y'; - EXPECT_EQ('y', static_cast(mapping1->base())[51]); -} - -} // namespace -} // namespace system -} // namespace mojo diff --git a/mojo/system/raw_shared_buffer_win.cc b/mojo/system/raw_shared_buffer_win.cc deleted file mode 100644 index 14f9c2e16b6df..0000000000000 --- a/mojo/system/raw_shared_buffer_win.cc +++ /dev/null @@ -1,94 +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/system/raw_shared_buffer.h" - -#include - -#include - -#include "base/logging.h" -#include "base/sys_info.h" -#include "mojo/embedder/platform_handle.h" -#include "mojo/embedder/scoped_platform_handle.h" - -namespace mojo { -namespace system { - -// RawSharedBuffer ------------------------------------------------------------- - -bool RawSharedBuffer::Init() { - DCHECK(!handle_.is_valid()); - - // TODO(vtl): Currently, we only support mapping up to 2^32-1 bytes. - if (static_cast(num_bytes_) > - static_cast(std::numeric_limits::max())) { - return false; - } - - // IMPORTANT NOTE: Unnamed objects are NOT SECURABLE. Thus if we ever want to - // share read-only to other processes, we'll have to name our file mapping - // object. - // TODO(vtl): Unlike |base::SharedMemory|, we don't round up the size (to a - // multiple of 64 KB). This may cause problems with NaCl. Cross this bridge - // when we get there. crbug.com/210609 - handle_.reset( - embedder::PlatformHandle(CreateFileMapping(INVALID_HANDLE_VALUE, - NULL, - PAGE_READWRITE, - 0, - static_cast(num_bytes_), - NULL))); - if (!handle_.is_valid()) { - PLOG(ERROR) << "CreateFileMapping"; - return false; - } - - return true; -} - -bool RawSharedBuffer::InitFromPlatformHandle( - embedder::ScopedPlatformHandle platform_handle) { - DCHECK(!handle_.is_valid()); - - // TODO(vtl): Implement. - NOTIMPLEMENTED(); - return false; -} - -scoped_ptr RawSharedBuffer::MapImpl(size_t offset, - size_t length) { - size_t offset_rounding = offset % base::SysInfo::VMAllocationGranularity(); - size_t real_offset = offset - offset_rounding; - size_t real_length = length + offset_rounding; - - // This should hold (since we checked |num_bytes| versus the maximum value of - // |off_t| on creation, but it never hurts to be paranoid. - DCHECK_LE(static_cast(real_offset), - static_cast(std::numeric_limits::max())); - - void* real_base = MapViewOfFile(handle_.get().handle, - FILE_MAP_READ | FILE_MAP_WRITE, - 0, - static_cast(real_offset), - real_length); - if (!real_base) { - PLOG(ERROR) << "MapViewOfFile"; - return scoped_ptr(); - } - - void* base = static_cast(real_base) + offset_rounding; - return make_scoped_ptr( - new RawSharedBufferMapping(base, length, real_base, real_length)); -} - -// RawSharedBufferMapping ------------------------------------------------------ - -void RawSharedBufferMapping::Unmap() { - BOOL result = UnmapViewOfFile(real_base_); - PLOG_IF(ERROR, !result) << "UnmapViewOfFile"; -} - -} // namespace system -} // namespace mojo diff --git a/mojo/system/remote_message_pipe_unittest.cc b/mojo/system/remote_message_pipe_unittest.cc index e026c30aa8653..0a961bfa73a3e 100644 --- a/mojo/system/remote_message_pipe_unittest.cc +++ b/mojo/system/remote_message_pipe_unittest.cc @@ -21,7 +21,9 @@ #include "build/build_config.h" // TODO(vtl): Remove this. #include "mojo/common/test/test_utils.h" #include "mojo/embedder/platform_channel_pair.h" +#include "mojo/embedder/platform_shared_buffer.h" #include "mojo/embedder/scoped_platform_handle.h" +#include "mojo/embedder/simple_platform_support.h" #include "mojo/system/channel.h" #include "mojo/system/local_message_pipe_endpoint.h" #include "mojo/system/message_pipe.h" @@ -700,24 +702,27 @@ TEST_F(RemoteMessagePipeTest, MAYBE_SharedBufferPassing) { ConnectMessagePipes(mp0, mp1); // We'll try to pass this dispatcher. + embedder::SimplePlatformSupport platform_support; scoped_refptr dispatcher; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + &platform_support, + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher)); ASSERT_TRUE(dispatcher); // Make a mapping. - scoped_ptr mapping0; + scoped_ptr mapping0; EXPECT_EQ( MOJO_RESULT_OK, dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping0)); ASSERT_TRUE(mapping0); - ASSERT_TRUE(mapping0->base()); - ASSERT_EQ(100u, mapping0->length()); - static_cast(mapping0->base())[0] = 'A'; - static_cast(mapping0->base())[50] = 'B'; - static_cast(mapping0->base())[99] = 'C'; + ASSERT_TRUE(mapping0->GetBase()); + ASSERT_EQ(100u, mapping0->GetLength()); + static_cast(mapping0->GetBase())[0] = 'A'; + static_cast(mapping0->GetBase())[50] = 'B'; + static_cast(mapping0->GetBase())[99] = 'C'; // Prepare to wait on MP 1, port 1. (Add the waiter now. Otherwise, if we do // it later, it might already be readable.) @@ -780,27 +785,27 @@ TEST_F(RemoteMessagePipeTest, MAYBE_SharedBufferPassing) { dispatcher = static_cast(read_dispatchers[0].get()); // Make another mapping. - scoped_ptr mapping1; + scoped_ptr mapping1; EXPECT_EQ( MOJO_RESULT_OK, dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping1)); ASSERT_TRUE(mapping1); - ASSERT_TRUE(mapping1->base()); - ASSERT_EQ(100u, mapping1->length()); - EXPECT_NE(mapping1->base(), mapping0->base()); - EXPECT_EQ('A', static_cast(mapping1->base())[0]); - EXPECT_EQ('B', static_cast(mapping1->base())[50]); - EXPECT_EQ('C', static_cast(mapping1->base())[99]); + ASSERT_TRUE(mapping1->GetBase()); + ASSERT_EQ(100u, mapping1->GetLength()); + EXPECT_NE(mapping1->GetBase(), mapping0->GetBase()); + EXPECT_EQ('A', static_cast(mapping1->GetBase())[0]); + EXPECT_EQ('B', static_cast(mapping1->GetBase())[50]); + EXPECT_EQ('C', static_cast(mapping1->GetBase())[99]); // Write stuff either way. - static_cast(mapping1->base())[1] = 'x'; - EXPECT_EQ('x', static_cast(mapping0->base())[1]); - static_cast(mapping0->base())[2] = 'y'; - EXPECT_EQ('y', static_cast(mapping1->base())[2]); + static_cast(mapping1->GetBase())[1] = 'x'; + EXPECT_EQ('x', static_cast(mapping0->GetBase())[1]); + static_cast(mapping0->GetBase())[2] = 'y'; + EXPECT_EQ('y', static_cast(mapping1->GetBase())[2]); // Kill the first mapping; the second should still be valid. mapping0.reset(); - EXPECT_EQ('A', static_cast(mapping1->base())[0]); + EXPECT_EQ('A', static_cast(mapping1->GetBase())[0]); // Close everything that belongs to us. mp0->Close(0); @@ -808,7 +813,7 @@ TEST_F(RemoteMessagePipeTest, MAYBE_SharedBufferPassing) { EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); // The second mapping should still be good. - EXPECT_EQ('x', static_cast(mapping1->base())[1]); + EXPECT_EQ('x', static_cast(mapping1->GetBase())[1]); } #if defined(OS_POSIX) diff --git a/mojo/system/shared_buffer_dispatcher.cc b/mojo/system/shared_buffer_dispatcher.cc index e5c4059953eee..fc1a66413b8f1 100644 --- a/mojo/system/shared_buffer_dispatcher.cc +++ b/mojo/system/shared_buffer_dispatcher.cc @@ -8,11 +8,12 @@ #include "base/logging.h" #include "base/memory/scoped_ptr.h" +#include "mojo/embedder/platform_support.h" +#include "mojo/embedder/simple_platform_shared_buffer.h" // TODO(vtl): Remove. #include "mojo/public/c/system/macros.h" #include "mojo/system/constants.h" #include "mojo/system/memory.h" #include "mojo/system/options_validation.h" -#include "mojo/system/raw_shared_buffer.h" namespace mojo { namespace system { @@ -62,6 +63,7 @@ MojoResult SharedBufferDispatcher::ValidateCreateOptions( // static MojoResult SharedBufferDispatcher::Create( + embedder::PlatformSupport* platform_support, const MojoCreateSharedBufferOptions& /*validated_options*/, uint64_t num_bytes, scoped_refptr* result) { @@ -70,8 +72,8 @@ MojoResult SharedBufferDispatcher::Create( if (num_bytes > kMaxSharedMemoryNumBytes) return MOJO_RESULT_RESOURCE_EXHAUSTED; - scoped_refptr shared_buffer( - RawSharedBuffer::Create(static_cast(num_bytes))); + scoped_refptr shared_buffer( + platform_support->CreateSharedBuffer(static_cast(num_bytes))); if (!shared_buffer) return MOJO_RESULT_RESOURCE_EXHAUSTED; @@ -119,8 +121,11 @@ scoped_refptr SharedBufferDispatcher::Deserialize( // Wrapping |platform_handle| in a |ScopedPlatformHandle| means that it'll be // closed even if creation fails. - scoped_refptr shared_buffer( - RawSharedBuffer::CreateFromPlatformHandle( + // TODO(vtl): This is obviously wrong -- but we need to have a + // |PlatformSupport| plumbed through (probably via the |Channel|), and use its + // |CreateSharedBufferFromHandle()|. + scoped_refptr shared_buffer( + embedder::SimplePlatformSharedBuffer::CreateFromPlatformHandle( num_bytes, embedder::ScopedPlatformHandle(platform_handle))); if (!shared_buffer) { LOG(ERROR) @@ -133,7 +138,7 @@ scoped_refptr SharedBufferDispatcher::Deserialize( } SharedBufferDispatcher::SharedBufferDispatcher( - scoped_refptr shared_buffer) + scoped_refptr shared_buffer) : shared_buffer_(shared_buffer) { DCHECK(shared_buffer_); } @@ -183,7 +188,7 @@ scoped_refptr SharedBufferDispatcher::CreateEquivalentDispatcherAndCloseImplNoLock() { lock().AssertAcquired(); DCHECK(shared_buffer_); - scoped_refptr shared_buffer; + scoped_refptr shared_buffer; shared_buffer.swap(shared_buffer_); return scoped_refptr(new SharedBufferDispatcher(shared_buffer)); } @@ -206,7 +211,7 @@ MojoResult SharedBufferDispatcher::MapBufferImplNoLock( uint64_t offset, uint64_t num_bytes, MojoMapBufferFlags flags, - scoped_ptr* mapping) { + scoped_ptr* mapping) { lock().AssertAcquired(); DCHECK(shared_buffer_); @@ -258,7 +263,7 @@ bool SharedBufferDispatcher::EndSerializeAndCloseImplNoLock( return false; } - serialization->num_bytes = shared_buffer_->num_bytes(); + serialization->num_bytes = shared_buffer_->GetNumBytes(); serialization->platform_handle_index = platform_handles->size(); platform_handles->push_back(platform_handle.release()); *actual_size = sizeof(SerializedSharedBufferDispatcher); diff --git a/mojo/system/shared_buffer_dispatcher.h b/mojo/system/shared_buffer_dispatcher.h index 640781d08ec47..fb5c32e798277 100644 --- a/mojo/system/shared_buffer_dispatcher.h +++ b/mojo/system/shared_buffer_dispatcher.h @@ -6,12 +6,17 @@ #define MOJO_SYSTEM_SHARED_BUFFER_DISPATCHER_H_ #include "base/macros.h" +#include "mojo/embedder/platform_shared_buffer.h" #include "mojo/system/memory.h" -#include "mojo/system/raw_shared_buffer.h" #include "mojo/system/simple_dispatcher.h" #include "mojo/system/system_impl_export.h" namespace mojo { + +namespace embedder { +class PlatformSupport; +} + namespace system { // TODO(vtl): We derive from SimpleDispatcher, even though we don't currently @@ -36,6 +41,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher { // Static factory method: |validated_options| must be validated (obviously). // On failure, |*result| will be left as-is. static MojoResult Create( + embedder::PlatformSupport* platform_support, const MojoCreateSharedBufferOptions& validated_options, uint64_t num_bytes, scoped_refptr* result); @@ -53,7 +59,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher { private: explicit SharedBufferDispatcher( - scoped_refptr shared_buffer_); + scoped_refptr shared_buffer_); virtual ~SharedBufferDispatcher(); // Validates and/or sets default options for @@ -76,7 +82,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher { uint64_t offset, uint64_t num_bytes, MojoMapBufferFlags flags, - scoped_ptr* mapping) OVERRIDE; + scoped_ptr* mapping) OVERRIDE; virtual void StartSerializeImplNoLock(Channel* channel, size_t* max_size, size_t* max_platform_handles) OVERRIDE; @@ -86,7 +92,7 @@ class MOJO_SYSTEM_IMPL_EXPORT SharedBufferDispatcher : public SimpleDispatcher { size_t* actual_size, embedder::PlatformHandleVector* platform_handles) OVERRIDE; - scoped_refptr shared_buffer_; + scoped_refptr shared_buffer_; DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcher); }; diff --git a/mojo/system/shared_buffer_dispatcher_unittest.cc b/mojo/system/shared_buffer_dispatcher_unittest.cc index 0e53330adb9ad..9ea5832f7d5fe 100644 --- a/mojo/system/shared_buffer_dispatcher_unittest.cc +++ b/mojo/system/shared_buffer_dispatcher_unittest.cc @@ -8,8 +8,9 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "mojo/embedder/platform_shared_buffer.h" +#include "mojo/embedder/simple_platform_support.h" #include "mojo/system/dispatcher.h" -#include "mojo/system/raw_shared_buffer.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { @@ -39,8 +40,21 @@ void RevalidateCreateOptions( EXPECT_EQ(validated_options.flags, revalidated_options.flags); } +class SharedBufferDispatcherTest : public testing::Test { + public: + SharedBufferDispatcherTest() {} + virtual ~SharedBufferDispatcherTest() {} + + embedder::PlatformSupport* platform_support() { return &platform_support_; } + + private: + embedder::SimplePlatformSupport platform_support_; + + DISALLOW_COPY_AND_ASSIGN(SharedBufferDispatcherTest); +}; + // Tests valid inputs to |ValidateCreateOptions()|. -TEST(SharedBufferDispatcherTest, ValidateCreateOptionsValid) { +TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsValid) { // Default options. { MojoCreateSharedBufferOptions validated_options = {}; @@ -73,7 +87,7 @@ TEST(SharedBufferDispatcherTest, ValidateCreateOptionsValid) { } } -TEST(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) { +TEST_F(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) { // Invalid |struct_size|. { MojoCreateSharedBufferOptions options = { @@ -99,56 +113,60 @@ TEST(SharedBufferDispatcherTest, ValidateCreateOptionsInvalid) { } } -TEST(SharedBufferDispatcherTest, CreateAndMapBuffer) { +TEST_F(SharedBufferDispatcherTest, CreateAndMapBuffer) { scoped_refptr dispatcher; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + platform_support(), + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher)); ASSERT_TRUE(dispatcher); EXPECT_EQ(Dispatcher::kTypeSharedBuffer, dispatcher->GetType()); // Make a couple of mappings. - scoped_ptr mapping1; + scoped_ptr mapping1; EXPECT_EQ( MOJO_RESULT_OK, dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping1)); ASSERT_TRUE(mapping1); - ASSERT_TRUE(mapping1->base()); - EXPECT_EQ(100u, mapping1->length()); + ASSERT_TRUE(mapping1->GetBase()); + EXPECT_EQ(100u, mapping1->GetLength()); // Write something. - static_cast(mapping1->base())[50] = 'x'; + static_cast(mapping1->GetBase())[50] = 'x'; - scoped_ptr mapping2; + scoped_ptr mapping2; EXPECT_EQ( MOJO_RESULT_OK, dispatcher->MapBuffer(50, 50, MOJO_MAP_BUFFER_FLAG_NONE, &mapping2)); ASSERT_TRUE(mapping2); - ASSERT_TRUE(mapping2->base()); - EXPECT_EQ(50u, mapping2->length()); - EXPECT_EQ('x', static_cast(mapping2->base())[0]); + ASSERT_TRUE(mapping2->GetBase()); + EXPECT_EQ(50u, mapping2->GetLength()); + EXPECT_EQ('x', static_cast(mapping2->GetBase())[0]); EXPECT_EQ(MOJO_RESULT_OK, dispatcher->Close()); // Check that we can still read/write to mappings after the dispatcher has // gone away. - static_cast(mapping2->base())[1] = 'y'; - EXPECT_EQ('y', static_cast(mapping1->base())[51]); + static_cast(mapping2->GetBase())[1] = 'y'; + EXPECT_EQ('y', static_cast(mapping1->GetBase())[51]); } -TEST(SharedBufferDispatcher, DuplicateBufferHandle) { +TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandle) { scoped_refptr dispatcher1; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher1)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + platform_support(), + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher1)); // Map and write something. - scoped_ptr mapping; + scoped_ptr mapping; EXPECT_EQ( MOJO_RESULT_OK, dispatcher1->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - static_cast(mapping->base())[0] = 'x'; + static_cast(mapping->GetBase())[0] = 'x'; mapping.reset(); // Duplicate |dispatcher1| and then close it. @@ -165,17 +183,19 @@ TEST(SharedBufferDispatcher, DuplicateBufferHandle) { EXPECT_EQ( MOJO_RESULT_OK, dispatcher2->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); - EXPECT_EQ('x', static_cast(mapping->base())[0]); + EXPECT_EQ('x', static_cast(mapping->GetBase())[0]); EXPECT_EQ(MOJO_RESULT_OK, dispatcher2->Close()); } -TEST(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsValid) { +TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsValid) { scoped_refptr dispatcher1; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher1)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + platform_support(), + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher1)); MojoDuplicateBufferHandleOptions options[] = { {sizeof(MojoDuplicateBufferHandleOptions), @@ -194,12 +214,14 @@ TEST(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsValid) { EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close()); } -TEST(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsInvalid) { +TEST_F(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsInvalid) { scoped_refptr dispatcher1; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher1)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + platform_support(), + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher1)); // Invalid |struct_size|. { @@ -226,11 +248,12 @@ TEST(SharedBufferDispatcherTest, DuplicateBufferHandleOptionsInvalid) { EXPECT_EQ(MOJO_RESULT_OK, dispatcher1->Close()); } -TEST(SharedBufferDispatcherTest, CreateInvalidNumBytes) { +TEST_F(SharedBufferDispatcherTest, CreateInvalidNumBytes) { // Size too big. scoped_refptr dispatcher; EXPECT_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, SharedBufferDispatcher::Create( + platform_support(), SharedBufferDispatcher::kDefaultCreateOptions, std::numeric_limits::max(), &dispatcher)); @@ -239,18 +262,23 @@ TEST(SharedBufferDispatcherTest, CreateInvalidNumBytes) { // Zero size. EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 0, &dispatcher)); + platform_support(), + SharedBufferDispatcher::kDefaultCreateOptions, + 0, + &dispatcher)); EXPECT_FALSE(dispatcher); } -TEST(SharedBufferDispatcherTest, MapBufferInvalidArguments) { +TEST_F(SharedBufferDispatcherTest, MapBufferInvalidArguments) { scoped_refptr dispatcher; - EXPECT_EQ( - MOJO_RESULT_OK, - SharedBufferDispatcher::Create( - SharedBufferDispatcher::kDefaultCreateOptions, 100, &dispatcher)); + EXPECT_EQ(MOJO_RESULT_OK, + SharedBufferDispatcher::Create( + platform_support(), + SharedBufferDispatcher::kDefaultCreateOptions, + 100, + &dispatcher)); - scoped_ptr mapping; + scoped_ptr mapping; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, dispatcher->MapBuffer(0, 101, MOJO_MAP_BUFFER_FLAG_NONE, &mapping)); EXPECT_FALSE(mapping); diff --git a/mojo/tools/data/unittests b/mojo/tools/data/unittests index 4497798598d0d..0bafba4441d56 100644 --- a/mojo/tools/data/unittests +++ b/mojo/tools/data/unittests @@ -13,8 +13,8 @@ mojo_public_system_unittests mojo_public_utility_unittests # Non-system, non-public tests: +mojo_application_manager_unittests mojo_common_unittests -mojo_service_manager_unittests mojo_view_manager_lib_unittests mojo_view_manager_unittests mojo_surfaces_lib_unittests diff --git a/mojo/tools/mojob.sh b/mojo/tools/mojob.sh index 57362190c9bf7..7a9389b42a2f0 100755 --- a/mojo/tools/mojob.sh +++ b/mojo/tools/mojob.sh @@ -37,9 +37,9 @@ option (which will only apply to following commands) should be one of: Component options: --shared Build components as shared libraries (default). --static Build components as static libraries. - Mojo in chromium/content (crbug.com/353602): - --use-mojo - Enabled (default). - --no-use-mojo - Disabled. + Use goma: + --use-goma - Use goma if \$GOMA_DIR is set or \$HOME/goma exists (default). + --no-use-goma - Do not use goma. Note: It will abort on the first failure (if any). EOF @@ -47,7 +47,11 @@ EOF do_build() { echo "Building in out/$1 ..." - ninja -C "out/$1" mojo || exit 1 + if [ "$GOMA" = "auto" -a -v GOMA_DIR ]; then + ninja -j 1000 -C "out/$1" mojo || exit 1 + else + ninja -C "out/$1" mojo || exit 1 + fi } do_unittests() { @@ -95,6 +99,8 @@ should_do_Release() { COMPILER=clang # Valid values: shared or static. COMPONENT=shared +# Valid values: auto or disabled. +GOMA=auto make_gyp_defines() { local options=() # Always include these options. @@ -115,7 +121,33 @@ make_gyp_defines() { options+=("component=static_library") ;; esac - echo ${options[*]} + case "$GOMA" in + auto) + if [ -v GOMA_DIR ]; then + options+=("use_goma=1" "gomadir=\"${GOMA_DIR}\"") + else + options+=("use_goma=0") + fi + ;; + disabled) + options+=("use_goma=0") + ;; + esac + echo "${options[*]}" +} + +set_goma_dir_if_necessary() { + if [ "$GOMA" = "auto" -a ! -v GOMA_DIR ]; then + if [ -d "${HOME}/goma" ]; then + GOMA_DIR="${HOME}/goma" + fi + fi +} + +start_goma_if_necessary() { + if [ "$GOMA" = "auto" -a -v GOMA_DIR ]; then + "${GOMA_DIR}/goma_ctl.py" ensure_start + fi } # We're in src/mojo/tools. We want to get to src. @@ -134,6 +166,8 @@ for arg in "$@"; do exit 0 ;; build) + set_goma_dir_if_necessary + start_goma_if_necessary should_do_Debug && do_build Debug should_do_Release && do_build Release ;; @@ -149,9 +183,11 @@ for arg in "$@"; do do_pytests ;; gyp) + set_goma_dir_if_necessary do_gyp ;; gypall) + set_goma_dir_if_necessary do_gypall ;; sync) @@ -188,6 +224,12 @@ for arg in "$@"; do --static) COMPONENT=static ;; + --use-goma) + GOMA=auto + ;; + --no-use-goma) + GOMA=disabled + ;; *) echo "Unknown command \"${arg}\". Try \"$(basename "$0") help\"." exit 1 diff --git a/mojo/views/native_widget_view_manager.cc b/mojo/views/native_widget_view_manager.cc index 8aeb7c7a6e33c..1e036762d5940 100644 --- a/mojo/views/native_widget_view_manager.cc +++ b/mojo/views/native_widget_view_manager.cc @@ -6,7 +6,6 @@ #include "mojo/aura/window_tree_host_mojo.h" #include "mojo/services/public/cpp/input_events/input_events_type_converters.h" -#include "mojo/services/public/cpp/view_manager/view.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/default_capture_client.h" #include "ui/aura/window.h" @@ -87,14 +86,11 @@ class MinimalInputEventFilter : public ui::internal::InputMethodDelegate, } // namespace NativeWidgetViewManager::NativeWidgetViewManager( - views::internal::NativeWidgetDelegate* delegate, Node* node) + views::internal::NativeWidgetDelegate* delegate, View* view) : NativeWidgetAura(delegate), - node_(node), - view_(node_->active_view()) { - node_->AddObserver(this); - if (view_) - view_->AddObserver(this); - window_tree_host_.reset(new WindowTreeHostMojo(node_, this)); + view_(view) { + view_->AddObserver(this); + window_tree_host_.reset(new WindowTreeHostMojo(view_, this)); window_tree_host_->InitHost(); ime_filter_.reset( @@ -116,8 +112,6 @@ NativeWidgetViewManager::NativeWidgetViewManager( NativeWidgetViewManager::~NativeWidgetViewManager() { if (view_) view_->RemoveObserver(this); - if (node_) - node_->RemoveObserver(this); } void NativeWidgetViewManager::InitNativeWidget( @@ -135,28 +129,17 @@ void NativeWidgetViewManager::CompositorContentsChanged( view_->SetContents(bitmap); } -void NativeWidgetViewManager::OnNodeDestroyed(Node* node) { - DCHECK_EQ(node, node_); - node->RemoveObserver(this); - node_ = NULL; +void NativeWidgetViewManager::OnViewDestroyed(View* view) { + DCHECK_EQ(view, view_); + view->RemoveObserver(this); + view_ = NULL; window_tree_host_.reset(); } -void NativeWidgetViewManager::OnNodeBoundsChanged(Node* node, +void NativeWidgetViewManager::OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) { - GetWidget()->SetBounds(gfx::Rect(node->bounds().size())); -} - -void NativeWidgetViewManager::OnNodeActiveViewChanged( - Node* node, - View* old_view, - View* new_view) { - if (old_view) - old_view->RemoveObserver(this); - if (new_view) - new_view->AddObserver(this); - view_ = new_view; + GetWidget()->SetBounds(gfx::Rect(view->bounds().size())); } void NativeWidgetViewManager::OnViewInputEvent(View* view, @@ -166,10 +149,4 @@ void NativeWidgetViewManager::OnViewInputEvent(View* view, window_tree_host_->SendEventToProcessor(ui_event.get()); } -void NativeWidgetViewManager::OnViewDestroyed(View* view) { - DCHECK_EQ(view, view_); - view->RemoveObserver(this); - view_ = NULL; -} - } // namespace mojo diff --git a/mojo/views/native_widget_view_manager.h b/mojo/views/native_widget_view_manager.h index 37096fec0525e..2e5879574515a 100644 --- a/mojo/views/native_widget_view_manager.h +++ b/mojo/views/native_widget_view_manager.h @@ -6,7 +6,6 @@ #define MOJO_VIEWS_NATIVE_WIDGET_VIEW_MANAGER_H_ #include "mojo/aura/window_tree_host_mojo_delegate.h" -#include "mojo/services/public/cpp/view_manager/node_observer.h" #include "mojo/services/public/cpp/view_manager/view_observer.h" #include "ui/views/widget/native_widget_aura.h" @@ -27,11 +26,10 @@ class WindowTreeHostMojo; class NativeWidgetViewManager : public views::NativeWidgetAura, public WindowTreeHostMojoDelegate, - public ViewObserver, - public NodeObserver { + public ViewObserver { public: NativeWidgetViewManager(views::internal::NativeWidgetDelegate* delegate, - Node* node); + View* view); virtual ~NativeWidgetViewManager(); private: @@ -42,18 +40,12 @@ class NativeWidgetViewManager : public views::NativeWidgetAura, // WindowTreeHostMojoDelegate: virtual void CompositorContentsChanged(const SkBitmap& bitmap) OVERRIDE; - // NodeObserver: - virtual void OnNodeDestroyed(Node* node) OVERRIDE; - virtual void OnNodeActiveViewChanged(Node* node, - View* old_view, - View* new_view) OVERRIDE; - virtual void OnNodeBoundsChanged(Node* node, + // ViewObserver: + virtual void OnViewDestroyed(View* view) OVERRIDE; + virtual void OnViewBoundsChanged(View* view, const gfx::Rect& old_bounds, const gfx::Rect& new_bounds) OVERRIDE; - - // ViewObserver virtual void OnViewInputEvent(View* view, const EventPtr& event) OVERRIDE; - virtual void OnViewDestroyed(View* view) OVERRIDE; scoped_ptr window_tree_host_; @@ -61,7 +53,6 @@ class NativeWidgetViewManager : public views::NativeWidgetAura, scoped_ptr ime_filter_; - Node* node_; View* view_; scoped_ptr capture_client_; diff --git a/native_client_sdk/src/README b/native_client_sdk/src/README index c44f214f27872..923bb69ec4953 100644 --- a/native_client_sdk/src/README +++ b/native_client_sdk/src/README @@ -4,6 +4,7 @@ Welcome to the Native Client SDK Native Client Tools Bundle Version: ${VERSION} Chrome Revision: ${CHROME_REVISION} +Chrome Commit Position: ${CHROME_COMMIT_POSITION} Native Client Revision: ${NACL_REVISION} Build Date: ${DATE} diff --git a/native_client_sdk/src/build_tools/build_sdk.py b/native_client_sdk/src/build_tools/build_sdk.py index d4e7f0c700049..ccc52b7deda71 100755 --- a/native_client_sdk/src/build_tools/build_sdk.py +++ b/native_client_sdk/src/build_tools/build_sdk.py @@ -182,6 +182,8 @@ def BuildStepCopyTextFiles(pepperdir, pepper_ver, chrome_revision, readme_text = open(os.path.join(SDK_SRC_DIR, 'README')).read() readme_text = readme_text.replace('${VERSION}', pepper_ver) readme_text = readme_text.replace('${CHROME_REVISION}', chrome_revision) + readme_text = readme_text.replace('${CHROME_COMMIT_POSITION}', + build_version.ChromeCommitPosition()) readme_text = readme_text.replace('${NACL_REVISION}', nacl_revision) # Year/Month/Day Hour:Minute:Second diff --git a/native_client_sdk/src/build_tools/build_version.py b/native_client_sdk/src/build_tools/build_version.py index 6e0aaa7421b3e..014b86e992781 100644 --- a/native_client_sdk/src/build_tools/build_version.py +++ b/native_client_sdk/src/build_tools/build_version.py @@ -6,6 +6,7 @@ """ import os +import re import sys # pylint: disable=E0602 @@ -27,11 +28,12 @@ def ChromeVersion(): Returns: Chrome version string or trunk + svn rev. ''' - info = lastchange.FetchVersionInfo(None) - if info.url.startswith('/trunk/'): - return 'trunk.%s' % info.revision - else: - return ChromeVersionNoTrunk() + info = FetchGitCommitPosition() + if info.url == 'git': + _, ref, revision = ParseCommitPosition(info.revision) + if ref == 'refs/heads/master': + return 'trunk.%s' % revision + return ChromeVersionNoTrunk() def ChromeVersionNoTrunk(): @@ -58,10 +60,26 @@ def ChromeMajorVersion(): def ChromeRevision(): '''Extract chrome revision from svn. + Now that the Chrome source-of-truth is git, this will return the + Cr-Commit-Position instead. Fortunately, this value is equal to the SVN + revision if one exists. + Returns: The Chrome revision as a string. e.g. "12345" ''' - return lastchange.FetchVersionInfo(None).revision + version = FetchGitCommitPosition() + return ParseCommitPosition(version.revision)[2] + + +def ChromeCommitPosition(): + '''Return the full git sha and commit position. + + Returns: + A value like: + 0178d4831bd36b5fb9ff477f03dc43b11626a6dc-refs/heads/master@{#292238} + ''' + return FetchGitCommitPosition().revision + def NaClRevision(): '''Extract NaCl revision from svn. @@ -70,4 +88,49 @@ def NaClRevision(): The NaCl revision as a string. e.g. "12345" ''' nacl_dir = os.path.join(SRC_DIR, 'native_client') - return lastchange.FetchVersionInfo(None, nacl_dir).revision + return lastchange.FetchVersionInfo(None, nacl_dir, 'native_client').revision + + +def FetchGitCommitPosition(directory=None): + '''Return the "commit-position" of the Chromium git repo. This should be + equivalent to the SVN revision if one exists. + ''' + SEARCH_LIMIT = 100 + for i in xrange(SEARCH_LIMIT): + cmd = ['show', '-s', '--format=%H%n%B', 'HEAD~%d' % i] + proc = lastchange.RunGitCommand(directory, cmd) + if not proc: + break + + output = proc.communicate()[0] + if not (proc.returncode == 0 and output): + break + + lines = output.splitlines() + + # First line is the hash. + hsh = lines[0] + if not re.match(r'[0-9a-fA-F]+', hsh): + break + + for line in reversed(lines): + if line.startswith('Cr-Commit-Position:'): + pos = line.rsplit()[-1].strip() + return lastchange.VersionInfo('git', '%s-%s' % (hsh, pos)) + + raise Exception('Unable to fetch a Git Commit Position.') + + + +def ParseCommitPosition(commit_position): + '''Parse a Chrome commit position into its components. + + Given a commit position like: + 0178d4831bd36b5fb9ff477f03dc43b11626a6dc-refs/heads/master@{#292238} + Returns: + ("0178d4831bd36b5fb9ff477f03dc43b11626a6dc", "refs/heads/master", "292238") + ''' + m = re.match(r'([0-9a-fA-F]+)(?:-([^@]+)@{#(\d+)})?', commit_position) + if m: + return m.groups() + return None diff --git a/native_client_sdk/src/build_tools/tests/build_version_test.py b/native_client_sdk/src/build_tools/tests/build_version_test.py new file mode 100755 index 0000000000000..f8f60a26ce359 --- /dev/null +++ b/native_client_sdk/src/build_tools/tests/build_version_test.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# Copyright (c) 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 os +import sys +import collections +import unittest + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) +BUILD_TOOLS_DIR = os.path.dirname(SCRIPT_DIR) +CHROME_SRC = os.path.dirname(os.path.dirname(os.path.dirname(BUILD_TOOLS_DIR))) +MOCK_DIR = os.path.join(CHROME_SRC, "third_party", "pymock") + +# For the mock library +sys.path.append(MOCK_DIR) +import mock + +sys.path.append(BUILD_TOOLS_DIR) +import build_version + +ProcInfo = collections.namedtuple('ProcInfo', ['returncode', 'output']) + +class TestCase(unittest.TestCase): + def setUp(self): + self.fetch_svn = mock.patch('lastchange.FetchSVNRevision').start() + self.fetch_git_svn = mock.patch('lastchange.FetchGitSVNRevision').start() + self.run_git = mock.patch('lastchange.RunGitCommand').start() + + self.fetch_svn.return_value = None + self.fetch_git_svn.return_value = None + + def tearDown(self): + mock.patch.stopall() + + def mockGitCommand(self, *args): + side_effects = [] + for proc_info in args: + mock_proc = mock.MagicMock() + mock_proc.returncode = proc_info.returncode + comm_result = mock_proc.MagicMock() + comm_result.__getitem__.return_value = proc_info.output + mock_proc.communicate.return_value = comm_result + side_effects.append(mock_proc) + + self.run_git.side_effect = side_effects + + def mockDefaultGitCommand(self): + output = """\ +6a8b61d6be4656e682eba005a1dd7f129789129c +[NaCl SDK] Update build_sdk.py to display Cr-Commit-Position in README. + +BUG=none +R=bradnelson@google.com, bradnelson@chromium.org + +Review URL: https://codereview.chromium.org/495423010 + +Cr-Commit-Position: refs/heads/master@{#292480}""" + self.mockGitCommand(ProcInfo(0, output)) + + def mockDepthTwoGitCommand(self): + output0 = """\ +ae4b444a0aa09a1fa73e59b180d7d957b9a36bf2 +.""" + + output1 = """\ +6a8b61d6be4656e682eba005a1dd7f129789129c +[NaCl SDK] Update build_sdk.py to display Cr-Commit-Position in README. + +BUG=none +R=bradnelson@google.com, bradnelson@chromium.org + +Review URL: https://codereview.chromium.org/495423010 + +Cr-Commit-Position: refs/heads/master@{#292480}""" + self.mockGitCommand(ProcInfo(0, output0), ProcInfo(0, output1)) + + + def assertGitShowCalled(self, depth=0): + cmd = ['show', '-s', '--format=%H%n%B', 'HEAD~%d' % depth] + self.run_git.assert_called_with(None, cmd) + + def testChromeVersion(self): + self.mockDefaultGitCommand() + result = build_version.ChromeVersion() + self.assertGitShowCalled() + self.assertEqual(result, 'trunk.292480') + + def testChromeRevision(self): + self.mockDefaultGitCommand() + result = build_version.ChromeRevision() + self.assertGitShowCalled() + self.assertEqual(result, '292480') + + def testChromeCommitPosition(self): + self.mockDefaultGitCommand() + result = build_version.ChromeCommitPosition() + self.assertGitShowCalled() + self.assertEqual( + result, + '6a8b61d6be4656e682eba005a1dd7f129789129c-refs/heads/master@{#292480}') + + def testChromeCommitPositionDepthTwo(self): + self.mockDepthTwoGitCommand() + result = build_version.ChromeCommitPosition() + self.assertEqual( + result, + '6a8b61d6be4656e682eba005a1dd7f129789129c-refs/heads/master@{#292480}') + + +if __name__ == '__main__': + unittest.main() diff --git a/native_client_sdk/src/build_tools/verify_ppapi.py b/native_client_sdk/src/build_tools/verify_ppapi.py index 8a51e3c288113..54cb8c0753870 100755 --- a/native_client_sdk/src/build_tools/verify_ppapi.py +++ b/native_client_sdk/src/build_tools/verify_ppapi.py @@ -19,6 +19,18 @@ import parse_dsc +# Add a file to this list if it should not be added to a .dsc file; i.e. if it +# should not be included in the Native Client SDK. This will silence the +# presubmit warning. +# +# Some examples of files that should not be added to the SDK are: Dev and +# Private interfaces that are either not available to NaCl plugins or are only +# available to Flash or other privileged plugins. +IGNORED_FILES = set([ + 'pp_video_dev.h' +]) + + class VerifyException(Exception): def __init__(self, lib_path, expected, unexpected): self.expected = expected @@ -47,6 +59,10 @@ def PartitionFiles(filenames): continue parts = filename.split(os.sep) + basename = os.path.basename(filename) + if basename in IGNORED_FILES: + continue + if 'private' in filename: if 'flash' in filename: continue diff --git a/native_client_sdk/src/examples/api/video_decode/video_decode.cc b/native_client_sdk/src/examples/api/video_decode/video_decode.cc index 5db1b71e05476..80088b9e27358 100644 --- a/native_client_sdk/src/examples/api/video_decode/video_decode.cc +++ b/native_client_sdk/src/examples/api/video_decode/video_decode.cc @@ -246,7 +246,7 @@ Decoder::Decoder(MyInstance* instance, pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264 - const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8MAIN; + const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8_ANY; #else const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN; #endif diff --git a/native_client_sdk/src/examples/demo/life_simd/example.dsc b/native_client_sdk/src/examples/demo/life_simd/example.dsc index a27736eb20faa..2768624ae5889 100644 --- a/native_client_sdk/src/examples/demo/life_simd/example.dsc +++ b/native_client_sdk/src/examples/demo/life_simd/example.dsc @@ -5,12 +5,15 @@ 'NAME' : 'life_simd', 'TYPE' : 'main', 'SOURCES' : [ - 'life.c', + 'life.cc', ], 'DEPS': ['ppapi_simple', 'nacl_io'], - 'LIBS': ['ppapi_simple', 'nacl_io', 'ppapi_cpp', 'ppapi', 'pthread'] + 'LIBS': ['ppapi_simple', 'nacl_io', 'sdk_util', 'ppapi_cpp', 'ppapi', 'pthread'] } ], + 'DATA': [ + 'example.js' + ], 'DEST': 'examples/demo', 'NAME': 'life_simd', 'TITLE': "Conway's Life (SIMD version)", diff --git a/native_client_sdk/src/examples/demo/life_simd/example.js b/native_client_sdk/src/examples/demo/life_simd/example.js new file mode 100644 index 0000000000000..81426b934aa9f --- /dev/null +++ b/native_client_sdk/src/examples/demo/life_simd/example.js @@ -0,0 +1,51 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +function moduleDidLoad() { +} + + +// Add event listeners after the NaCl module has loaded. These listeners will +// forward messages to the NaCl module via postMessage() +function attachListeners() { + document.getElementById('benchmark').addEventListener('click', + function() { + common.naclModule.postMessage({'message' : 'run_benchmark'}); + common.updateStatus('BENCHMARKING... (please wait)'); + }); + document.getElementById('simd').addEventListener('click', + function() { + var simd = document.getElementById('simd'); + common.naclModule.postMessage({'message' : 'set_simd', + 'value' : simd.checked}); + }); + document.getElementById('multithread').addEventListener('click', + function() { + var multithread = document.getElementById('multithread'); + common.naclModule.postMessage({'message' : 'set_threading', + 'value' : multithread.checked}); + }); + document.getElementById('large').addEventListener('click', + function() { + var large = document.getElementById('large'); + var nacl = document.getElementById('nacl_module'); + nacl.setAttribute('width', large.checked ? 1280 : 640); + nacl.setAttribute('height', large.checked ? 1024 : 640); + }); +} + + +// Handle a message coming from the NaCl module. +function handleMessage(message_event) { + if (message_event.data.message == 'benchmark_result') { + // benchmark result + var result = message_event.data.value; + console.log('Benchmark result:' + result); + result = (Math.round(result * 1000) / 1000).toFixed(3); + document.getElementById('result').textContent = + 'Result: ' + result + ' seconds'; + common.updateStatus('SUCCESS'); + } +} + diff --git a/native_client_sdk/src/examples/demo/life_simd/index.html b/native_client_sdk/src/examples/demo/life_simd/index.html index 5d354fdf153c0..b1f2e02fd9ba2 100644 --- a/native_client_sdk/src/examples/demo/life_simd/index.html +++ b/native_client_sdk/src/examples/demo/life_simd/index.html @@ -10,12 +10,24 @@ {{title}} +

    {{title}}

    Status: NO-STATUS

    +
    + Conway's game of life is a cellular automaton by British mathematician John + Horton Conway. Use the touch screen or mouse pointer to interact with the + simulation. +
    + Use SIMD
    + Use multiple threads
    + Use large field
    + + +
    diff --git a/native_client_sdk/src/examples/demo/life_simd/life.c b/native_client_sdk/src/examples/demo/life_simd/life.c deleted file mode 100644 index e928a4a678a72..0000000000000 --- a/native_client_sdk/src/examples/demo/life_simd/life.c +++ /dev/null @@ -1,457 +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 -#include -#include -#include -#include - -#include "ppapi/c/pp_resource.h" -#include "ppapi/c/ppb_core.h" -#include "ppapi/c/ppb_fullscreen.h" -#include "ppapi/c/ppb_graphics_2d.h" -#include "ppapi/c/ppb_image_data.h" -#include "ppapi/c/ppb_input_event.h" -#include "ppapi/c/ppb_instance.h" -#include "ppapi/c/ppb_view.h" - -#include "ppapi_simple/ps_event.h" -#include "ppapi_simple/ps_main.h" - -PPB_Core* g_pCore; -PPB_Fullscreen* g_pFullscreen; -PPB_Graphics2D* g_pGraphics2D; -PPB_ImageData* g_pImageData; -PPB_Instance* g_pInstance; -PPB_View* g_pView; -PPB_InputEvent* g_pInputEvent; -PPB_KeyboardInputEvent* g_pKeyboardInput; -PPB_MouseInputEvent* g_pMouseInput; -PPB_TouchInputEvent* g_pTouchInput; - -struct { - PP_Resource ctx; - struct PP_Size size; - int bound; - uint8_t* cell_in; - uint8_t* cell_out; - int32_t cell_stride; -} g_Context; - - -const unsigned int kInitialRandSeed = 0xC0DE533D; -const int kCellAlignment = 0x10; - -#define INLINE inline __attribute__((always_inline)) - -/* BGRA helper macro, for constructing a pixel for a BGRA buffer. */ -#define MakeBGRA(b, g, r, a) \ - (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) - -/* 128 bit vector types */ -typedef uint8_t u8x16_t __attribute__ ((vector_size (16))); - -/* Helper function to broadcast x across 16 element vector. */ -INLINE u8x16_t broadcast(uint8_t x) { - u8x16_t r = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x}; - return r; -} - - -/* - * Convert a count value into a live (green) or dead color value. - */ -const uint32_t kNeighborColors[] = { - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0xFF, 0x00, 0xFF), - MakeBGRA(0x00, 0xFF, 0x00, 0xFF), - MakeBGRA(0x00, 0xFF, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), - MakeBGRA(0x00, 0x00, 0x00, 0xFF), -}; - -/* - * These represent the new health value of a cell based on its neighboring - * values. The health is binary: either alive or dead. - */ -const uint8_t kIsAlive[] = { - 0, 0, 0, 0, 0, 1, 1, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -void UpdateContext(uint32_t width, uint32_t height) { - int stride = (width + kCellAlignment - 1) & ~kCellAlignment; - if (width != g_Context.size.width || height != g_Context.size.height) { - - size_t size = stride * height; - size_t index; - - free(g_Context.cell_in); - free(g_Context.cell_out); - - /* Create a new context */ - void* in_buffer = NULL; - void* out_buffer = NULL; - /* alloc buffers aligned on 16 bytes */ - posix_memalign(&in_buffer, kCellAlignment, size); - posix_memalign(&out_buffer, kCellAlignment, size); - g_Context.cell_in = (uint8_t*) in_buffer; - g_Context.cell_out = (uint8_t*) out_buffer; - - memset(g_Context.cell_out, 0, size); - for (index = 0; index < size; index++) { - g_Context.cell_in[index] = rand() & 1; - } - } - - /* Recreate the graphics context on a view change */ - g_pCore->ReleaseResource(g_Context.ctx); - g_Context.size.width = width; - g_Context.size.height = height; - g_Context.cell_stride = stride; - g_Context.ctx = - g_pGraphics2D->Create(PSGetInstanceId(), &g_Context.size, PP_TRUE); - g_Context.bound = - g_pInstance->BindGraphics(PSGetInstanceId(), g_Context.ctx); -} - -void DrawCell(int32_t x, int32_t y) { - int32_t width = g_Context.size.width; - int32_t height = g_Context.size.height; - int32_t stride = g_Context.cell_stride; - - if (!g_Context.cell_in) return; - - if (x > 0 && x < width - 1 && y > 0 && y < height - 1) { - g_Context.cell_in[x - 1 + y * stride] = 1; - g_Context.cell_in[x + 1 + y * stride] = 1; - g_Context.cell_in[x + (y - 1) * stride] = 1; - g_Context.cell_in[x + (y + 1) * stride] = 1; - } -} - -void ProcessTouchEvent(PSEvent* event) { - uint32_t count = g_pTouchInput->GetTouchCount(event->as_resource, - PP_TOUCHLIST_TYPE_TOUCHES); - uint32_t i, j; - for (i = 0; i < count; i++) { - struct PP_TouchPoint touch = g_pTouchInput->GetTouchByIndex( - event->as_resource, PP_TOUCHLIST_TYPE_TOUCHES, i); - int radius = (int)touch.radius.x; - int x = (int)touch.position.x; - int y = (int)touch.position.y; - /* num = 1/100th the area of touch point */ - int num = (int)(M_PI * radius * radius / 100.0f); - for (j = 0; j < num; j++) { - int dx = rand() % (radius * 2) - radius; - int dy = rand() % (radius * 2) - radius; - /* only plot random cells within the touch area */ - if (dx * dx + dy * dy <= radius * radius) - DrawCell(x + dx, y + dy); - } - } -} - -void ProcessEvent(PSEvent* event) { - switch(event->type) { - /* If the view updates, build a new Graphics 2D Context */ - case PSE_INSTANCE_DIDCHANGEVIEW: { - struct PP_Rect rect; - - g_pView->GetRect(event->as_resource, &rect); - UpdateContext(rect.size.width, rect.size.height); - break; - } - - case PSE_INSTANCE_HANDLEINPUT: { - PP_InputEvent_Type type = g_pInputEvent->GetType(event->as_resource); - PP_InputEvent_Modifier modifiers = - g_pInputEvent->GetModifiers(event->as_resource); - - switch(type) { - case PP_INPUTEVENT_TYPE_MOUSEDOWN: - case PP_INPUTEVENT_TYPE_MOUSEMOVE: { - struct PP_Point location = - g_pMouseInput->GetPosition(event->as_resource); - /* If the button is down, draw */ - if (modifiers & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { - DrawCell(location.x, location.y); - } - break; - } - - case PP_INPUTEVENT_TYPE_TOUCHSTART: - case PP_INPUTEVENT_TYPE_TOUCHMOVE: - ProcessTouchEvent(event); - break; - - case PP_INPUTEVENT_TYPE_KEYDOWN: { - PP_Bool fullscreen = g_pFullscreen->IsFullscreen(PSGetInstanceId()); - g_pFullscreen->SetFullscreen(PSGetInstanceId(), - fullscreen ? PP_FALSE : PP_TRUE); - break; - } - - default: - break; - } - /* case PSE_INSTANCE_HANDLEINPUT */ - break; - } - - default: - break; - } -} - - -void Stir() { - int32_t width = g_Context.size.width; - int32_t height = g_Context.size.height; - int32_t stride = g_Context.cell_stride; - int32_t i; - if (g_Context.cell_in == NULL || g_Context.cell_out == NULL) - return; - - for (i = 0; i < width; ++i) { - g_Context.cell_in[i] = rand() & 1; - g_Context.cell_in[i + (height - 1) * stride] = rand() & 1; - } - for (i = 0; i < height; ++i) { - g_Context.cell_in[i * stride] = rand() & 1; - g_Context.cell_in[i * stride + (width - 1)] = rand() & 1; - } -} - - -void Render() { - struct PP_Size* psize = &g_Context.size; - PP_ImageDataFormat format = PP_IMAGEDATAFORMAT_BGRA_PREMUL; - - /* - * Create a buffer to draw into. Since we are waiting until the next flush - * chrome has an opportunity to cache this buffer see ppb_graphics_2d.h. - */ - PP_Resource image = - g_pImageData->Create(PSGetInstanceId(), format, psize, PP_FALSE); - uint8_t* pixels = g_pImageData->Map(image); - - struct PP_ImageDataDesc desc; - uint8_t* cell_temp; - uint32_t x, y; - - /* If we somehow have not allocated these pointers yet, skip this frame. */ - if (!g_Context.cell_in || !g_Context.cell_out) return; - - /* Get the pixel stride. */ - g_pImageData->Describe(image, &desc); - - /* Stir up the edges to prevent the simulation from reaching steady state. */ - Stir(); - - /* - * Do neighbor summation; apply rules, output pixel color. Note that a 1 cell - * wide perimeter is excluded from the simulation update; only cells from - * x = 1 to x < width - 1 and y = 1 to y < height - 1 are updated. - */ - - for (y = 1; y < g_Context.size.height - 1; ++y) { - uint8_t *src0 = (g_Context.cell_in + (y - 1) * g_Context.cell_stride); - uint8_t *src1 = src0 + g_Context.cell_stride; - uint8_t *src2 = src1 + g_Context.cell_stride; - uint8_t *dst = (g_Context.cell_out + y * g_Context.cell_stride) + 1; - uint32_t *pixel_line = (uint32_t*) (pixels + y * desc.stride); - const u8x16_t kOne = broadcast(1); - const u8x16_t kFour = broadcast(4); - const u8x16_t kEight = broadcast(8); - const u8x16_t kZero255 = {0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - - /* Prime the src */ - u8x16_t src00 = *(u8x16_t*)&src0[0]; - u8x16_t src01 = *(u8x16_t*)&src0[16]; - u8x16_t src10 = *(u8x16_t*)&src1[0]; - u8x16_t src11 = *(u8x16_t*)&src1[16]; - u8x16_t src20 = *(u8x16_t*)&src2[0]; - u8x16_t src21 = *(u8x16_t*)&src2[16]; - - /* This inner loop is SIMD - each loop iteration will process 16 cells. */ - for (x = 1; (x + 15) < (g_Context.size.width - 1); x += 16) { - - /* - * Construct jittered source temps, using __builtin_shufflevector(..) to - * extract a shifted 16 element vector from the 32 element concatenation - * of two source vectors. - */ - u8x16_t src0j0 = src00; - u8x16_t src0j1 = __builtin_shufflevector(src00, src01, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - u8x16_t src0j2 = __builtin_shufflevector(src00, src01, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); - u8x16_t src1j0 = src10; - u8x16_t src1j1 = __builtin_shufflevector(src10, src11, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - u8x16_t src1j2 = __builtin_shufflevector(src10, src11, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); - u8x16_t src2j0 = src20; - u8x16_t src2j1 = __builtin_shufflevector(src20, src21, - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); - u8x16_t src2j2 = __builtin_shufflevector(src20, src21, - 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); - - /* Sum the jittered sources to construct neighbor count. */ - u8x16_t count = src0j0 + src0j1 + src0j2 + - src1j0 + + src1j2 + - src2j0 + src2j1 + src2j2; - /* Add the center cell. */ - count = count + count + src1j1; - /* If count > 4 and < 8, center cell will be alive in the next frame. */ - u8x16_t alive1 = count > kFour; - u8x16_t alive2 = count < kEight; - /* Intersect the two comparisons from above. */ - u8x16_t alive = alive1 & alive2; - - /* - * At this point, alive[x] will be one of two values: - * 0x00 for a dead cell - * 0xFF for an alive cell. - * - * Next, convert alive cells to green pixel color. - * Use __builtin_shufflevector(..) to construct output pixels from - * concantination of alive vector and kZero255 const vector. - * Indices 0..15 select the 16 cells from alive vector. - * Index 16 is zero constant from kZero255 constant vector. - * Index 17 is 255 constant from kZero255 constant vector. - * Output pixel color values are in BGRABGRABGRABGRA order. - * Since each pixel needs 4 bytes of color information, 16 cells will - * need to expand to 4 seperate 16 byte pixel splats. - */ - u8x16_t pixel0_3 = __builtin_shufflevector(alive, kZero255, - 16, 0, 16, 17, 16, 1, 16, 17, 16, 2, 16, 17, 16, 3, 16, 17); - u8x16_t pixel4_7 = __builtin_shufflevector(alive, kZero255, - 16, 4, 16, 17, 16, 5, 16, 17, 16, 6, 16, 17, 16, 7, 16, 17); - u8x16_t pixel8_11 = __builtin_shufflevector(alive, kZero255, - 16, 8, 16, 17, 16, 9, 16, 17, 16, 10, 16, 17, 16, 11, 16, 17); - u8x16_t pixel12_15 = __builtin_shufflevector(alive, kZero255, - 16, 12, 16, 17, 16, 13, 16, 17, 16, 14, 16, 17, 16, 15, 16, 17); - - /* Write 16 pixels to output pixel buffer. */ - *(u8x16_t*)(pixel_line + 0) = pixel0_3; - *(u8x16_t*)(pixel_line + 4) = pixel4_7; - *(u8x16_t*)(pixel_line + 8) = pixel8_11; - *(u8x16_t*)(pixel_line + 12) = pixel12_15; - - /* Convert alive mask to 1 or 0 and store in destination cell array. */ - *(u8x16_t*)dst = alive & kOne; - - /* Increment pointers. */ - pixel_line += 16; - dst += 16; - src0 += 16; - src1 += 16; - src2 += 16; - - /* Shift source over by 16 cells and read the next 16 cells. */ - src00 = src01; - src01 = *(u8x16_t*)&src0[16]; - src10 = src11; - src11 = *(u8x16_t*)&src1[16]; - src20 = src21; - src21 = *(u8x16_t*)&src2[16]; - } - - /* - * The SIMD loop above does 16 cells at a time. The loop below is the - * regular version which processes one cell at a time. It is used to - * finish the remainder of the scanline not handled by the SIMD loop. - */ - for (; x < (g_Context.size.width - 1); ++x) { - /* Sum the jittered sources to construct neighbor count. */ - int count = src0[0] + src0[1] + src0[2] + - src1[0] + + src1[2] + - src2[0] + src2[1] + src2[2]; - /* Add the center cell. */ - count = count + count + src1[1]; - /* Use table lookup indexed by count to determine pixel & alive state. */ - uint32_t color = kNeighborColors[count]; - *pixel_line++ = color; - *dst++ = kIsAlive[count]; - ++src0; - ++src1; - ++src2; - } - } - - cell_temp = g_Context.cell_in; - g_Context.cell_in = g_Context.cell_out; - g_Context.cell_out = cell_temp; - - /* Unmap the range, we no longer need it. */ - g_pImageData->Unmap(image); - - /* Replace the contexts, and block until it's on the screen. */ - g_pGraphics2D->ReplaceContents(g_Context.ctx, image); - g_pGraphics2D->Flush(g_Context.ctx, PP_BlockUntilComplete()); - - /* Release the image data, we no longer need it. */ - g_pCore->ReleaseResource(image); -} - -/* - * Starting point for the module. We do not use main since it would - * collide with main in libppapi_cpp. - */ -int example_main(int argc, char *argv[]) { - fprintf(stdout,"Started main.\n"); - g_pCore = (PPB_Core*)PSGetInterface(PPB_CORE_INTERFACE); - g_pFullscreen = (PPB_Fullscreen*)PSGetInterface(PPB_FULLSCREEN_INTERFACE); - g_pGraphics2D = (PPB_Graphics2D*)PSGetInterface(PPB_GRAPHICS_2D_INTERFACE); - g_pInstance = (PPB_Instance*)PSGetInterface(PPB_INSTANCE_INTERFACE); - g_pImageData = (PPB_ImageData*)PSGetInterface(PPB_IMAGEDATA_INTERFACE); - g_pView = (PPB_View*)PSGetInterface(PPB_VIEW_INTERFACE); - - g_pInputEvent = - (PPB_InputEvent*) PSGetInterface(PPB_INPUT_EVENT_INTERFACE); - g_pKeyboardInput = (PPB_KeyboardInputEvent*) - PSGetInterface(PPB_KEYBOARD_INPUT_EVENT_INTERFACE); - g_pMouseInput = - (PPB_MouseInputEvent*) PSGetInterface(PPB_MOUSE_INPUT_EVENT_INTERFACE); - g_pTouchInput = - (PPB_TouchInputEvent*) PSGetInterface(PPB_TOUCH_INPUT_EVENT_INTERFACE); - - PSEventSetFilter(PSE_ALL); - while (1) { - /* Process all waiting events without blocking */ - PSEvent* event; - while ((event = PSEventTryAcquire()) != NULL) { - ProcessEvent(event); - PSEventRelease(event); - } - - /* Render a frame, blocking until complete. */ - if (g_Context.bound) { - Render(); - } - } - return 0; -} - -/* - * Register the function to call once the Instance Object is initialized. - * see: pappi_simple/ps_main.h - */ -PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/examples/demo/life_simd/life.cc b/native_client_sdk/src/examples/demo/life_simd/life.cc new file mode 100644 index 0000000000000..e784b99981c8c --- /dev/null +++ b/native_client_sdk/src/examples/demo/life_simd/life.cc @@ -0,0 +1,524 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ppapi_simple/ps.h" +#include "ppapi_simple/ps_context_2d.h" +#include "ppapi_simple/ps_event.h" +#include "ppapi_simple/ps_instance.h" +#include "ppapi_simple/ps_interface.h" +#include "ppapi_simple/ps_main.h" +#include "sdk_util/macros.h" +#include "sdk_util/thread_pool.h" + +using namespace sdk_util; // For sdk_util::ThreadPool + +namespace { + +#define INLINE inline __attribute__((always_inline)) + +// BGRA helper macro, for constructing a pixel for a BGRA buffer. +#define MakeBGRA(b, g, r, a) \ + (((a) << 24) | ((r) << 16) | ((g) << 8) | (b)) + +const int kFramesToBenchmark = 100; +const int kCellAlignment = 0x10; + +// 128 bit vector types +typedef uint8_t u8x16_t __attribute__ ((vector_size (16))); + +// Helper function to broadcast x across 16 element vector. +INLINE u8x16_t broadcast(uint8_t x) { + u8x16_t r = {x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x}; + return r; +} + +// Convert a count value into a live (green) or dead color value. +const uint32_t kNeighborColors[] = { + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0xFF, 0x00, 0xFF), + MakeBGRA(0x00, 0xFF, 0x00, 0xFF), + MakeBGRA(0x00, 0xFF, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), + MakeBGRA(0x00, 0x00, 0x00, 0xFF), +}; + +// These represent the new health value of a cell based on its neighboring +// values. The health is binary: either alive or dead. +const uint8_t kIsAlive[] = { + 0, 0, 0, 0, 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// Timer helper for benchmarking. Returns seconds elapsed since program start, +// as a double. +timeval start_tv; +int start_tv_retv = gettimeofday(&start_tv, NULL); + +inline double getseconds() { + const double usec_to_sec = 0.000001; + timeval tv; + if ((0 == start_tv_retv) && (0 == gettimeofday(&tv, NULL))) + return (tv.tv_sec - start_tv.tv_sec) + tv.tv_usec * usec_to_sec; + return 0.0; +} +} // namespace + + +class Life { + public: + Life(); + virtual ~Life(); + // Runs a tick of the simulations, update 2D output. + void Update(); + // Handle event from user, or message from JS. + void HandleEvent(PSEvent* ps_event); + private: + void UpdateContext(); + void DrawCell(int32_t x, int32_t y); + void ProcessTouchEvent(const pp::TouchInputEvent& touches); + void PostUpdateMessage(const char* message, double value); + void StartBenchmark(); + void EndBenchmark(); + void Stir(); + void wSimulate(int y); + static void wSimulateEntry(int y, void* data); + void Simulate(); + + bool simd_; + bool multithread_; + bool benchmarking_; + int benchmark_frame_counter_; + double bench_start_time_; + double bench_end_time_; + uint8_t* cell_in_; + uint8_t* cell_out_; + int32_t cell_stride_; + int32_t width_; + int32_t height_; + PSContext2D_t* ps_context_; + ThreadPool* workers_; +}; + +Life::Life() : + simd_(true), + multithread_(true), + benchmarking_(false), + benchmark_frame_counter_(0), + bench_start_time_(0.0), + bench_end_time_(0.0), + cell_in_(NULL), + cell_out_(NULL), + cell_stride_(0), + width_(0), + height_(0) { + ps_context_ = PSContext2DAllocate(PP_IMAGEDATAFORMAT_BGRA_PREMUL); + // Query system for number of processors via sysconf() + int num_threads = sysconf(_SC_NPROCESSORS_ONLN); + if (num_threads < 2) + num_threads = 2; + workers_ = new ThreadPool(num_threads); + PSEventSetFilter(PSE_ALL); +} + +Life::~Life() { + delete workers_; + PSContext2DFree(ps_context_); +} + +void Life::UpdateContext() { + cell_stride_ = (ps_context_->width + kCellAlignment - 1) & + ~(kCellAlignment - 1); + size_t size = cell_stride_ * ps_context_->height; + + if (ps_context_->width != width_ || ps_context_->height != height_) { + free(cell_in_); + free(cell_out_); + + // Create a new context + void* in_buffer = NULL; + void* out_buffer = NULL; + // alloc buffers aligned on 16 bytes + posix_memalign(&in_buffer, kCellAlignment, size); + posix_memalign(&out_buffer, kCellAlignment, size); + cell_in_ = (uint8_t*) in_buffer; + cell_out_ = (uint8_t*) out_buffer; + + memset(cell_out_, 0, size); + for (size_t index = 0; index < size; index++) { + cell_in_[index] = rand() & 1; + } + width_ = ps_context_->width; + height_ = ps_context_->height; + } +} + +void Life::DrawCell(int32_t x, int32_t y) { + if (!cell_in_) return; + if (x > 0 && x < ps_context_->width - 1 && + y > 0 && y < ps_context_->height - 1) { + cell_in_[x - 1 + y * cell_stride_] = 1; + cell_in_[x + 1 + y * cell_stride_] = 1; + cell_in_[x + (y - 1) * cell_stride_] = 1; + cell_in_[x + (y + 1) * cell_stride_] = 1; + } +} + +void Life::ProcessTouchEvent(const pp::TouchInputEvent& touches) { + uint32_t count = touches.GetTouchCount(PP_TOUCHLIST_TYPE_TOUCHES); + uint32_t i, j; + for (i = 0; i < count; i++) { + pp::TouchPoint touch = + touches.GetTouchByIndex(PP_TOUCHLIST_TYPE_TOUCHES, i); + int radius = (int)(touch.radii().x()); + int x = (int)(touch.position().x()); + int y = (int)(touch.position().y()); + // num = 1/100th the area of touch point + uint32_t num = (uint32_t)(M_PI * radius * radius / 100.0f); + for (j = 0; j < num; j++) { + int dx = rand() % (radius * 2) - radius; + int dy = rand() % (radius * 2) - radius; + // only plot random cells within the touch area + if (dx * dx + dy * dy <= radius * radius) + DrawCell(x + dx, y + dy); + } + } +} + +void Life::PostUpdateMessage(const char* message_name, double value) { + pp::VarDictionary message; + message.Set("message", message_name); + message.Set("value", value); + PSInterfaceMessaging()->PostMessage(PSGetInstanceId(), message.pp_var()); +} + +void Life::StartBenchmark() { + printf("Running benchmark... (SIMD: %s, multi-threading: %s, size: %dx%d)\n", + simd_ ? "enabled" : "disabled", + multithread_ ? "enabled" : "disabled", + ps_context_->width, + ps_context_->height); + benchmarking_ = true; + bench_start_time_ = getseconds(); + benchmark_frame_counter_ = kFramesToBenchmark; +} + +void Life::EndBenchmark() { + double total_time; + bench_end_time_ = getseconds(); + benchmarking_ = false; + total_time = bench_end_time_ - bench_start_time_; + printf("Finished - benchmark took %f seconds\n", total_time); + // Send benchmark result to JS. + PostUpdateMessage("benchmark_result", total_time); +} + +void Life::HandleEvent(PSEvent* ps_event) { + // Give the 2D context a chance to process the event. + if (0 != PSContext2DHandleEvent(ps_context_, ps_event)) { + UpdateContext(); + return; + } + + switch(ps_event->type) { + + case PSE_INSTANCE_HANDLEINPUT: { + pp::InputEvent event(ps_event->as_resource); + + switch(event.GetType()) { + case PP_INPUTEVENT_TYPE_MOUSEDOWN: + case PP_INPUTEVENT_TYPE_MOUSEMOVE: { + pp::MouseInputEvent mouse = pp::MouseInputEvent(event); + // If the button is down, draw + if (mouse.GetModifiers() & PP_INPUTEVENT_MODIFIER_LEFTBUTTONDOWN) { + PP_Point location = mouse.GetPosition(); + DrawCell(location.x, location.y); + } + break; + } + + case PP_INPUTEVENT_TYPE_TOUCHSTART: + case PP_INPUTEVENT_TYPE_TOUCHMOVE: { + pp::TouchInputEvent touches = pp::TouchInputEvent(event); + ProcessTouchEvent(touches); + break; + } + + case PP_INPUTEVENT_TYPE_KEYDOWN: { + pp::Fullscreen fullscreen(PSInstance::GetInstance()); + bool isFullscreen = fullscreen.IsFullscreen(); + fullscreen.SetFullscreen(!isFullscreen); + break; + } + + default: + break; + } + break; // case PSE_INSTANCE_HANDLEINPUT + } + + case PSE_INSTANCE_HANDLEMESSAGE: { + // Convert Pepper Simple message to PPAPI C++ vars + pp::Var var(ps_event->as_var); + if (var.is_dictionary()) { + pp::VarDictionary dictionary(var); + std::string message = dictionary.Get("message").AsString(); + if (message == "run_benchmark" && !benchmarking_) { + StartBenchmark(); + } else if (message == "set_simd") { + simd_ = dictionary.Get("value").AsBool(); + } else if (message == "set_threading") { + multithread_ = dictionary.Get("value").AsBool(); + } + } + break; // case PSE_INSTANCE_HANDLEMESSAGE + } + + default: + break; + } +} + +void Life::Stir() { + int32_t width = ps_context_->width; + int32_t height = ps_context_->height; + int32_t stride = cell_stride_; + int32_t i; + if (cell_in_ == NULL || cell_out_ == NULL) + return; + + for (i = 0; i < width; ++i) { + cell_in_[i] = rand() & 1; + cell_in_[i + (height - 1) * stride] = rand() & 1; + } + for (i = 0; i < height; ++i) { + cell_in_[i * stride] = rand() & 1; + cell_in_[i * stride + (width - 1)] = rand() & 1; + } +} + +void Life::wSimulate(int y) { + // Don't run simulation on top and bottom borders + if (y < 1 || y >= ps_context_->height - 1) + return; + + // Do neighbor summation; apply rules, output pixel color. Note that a 1 cell + // wide perimeter is excluded from the simulation update; only cells from + // x = 1 to x < width - 1 and y = 1 to y < height - 1 are updated. + uint8_t *src0 = (cell_in_ + (y - 1) * cell_stride_); + uint8_t *src1 = src0 + cell_stride_; + uint8_t *src2 = src1 + cell_stride_; + uint8_t *dst = (cell_out_ + y * cell_stride_) + 1; + uint32_t *pixels = static_cast(ps_context_->data); + uint32_t *pixel_line = // static_cast + (pixels + y * ps_context_->stride / sizeof(uint32_t)); + int32_t x = 1; + + if (simd_) { + const u8x16_t kOne = broadcast(1); + const u8x16_t kFour = broadcast(4); + const u8x16_t kEight = broadcast(8); + const u8x16_t kZero255 = {0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + + // Prime the src + u8x16_t src00 = *reinterpret_cast(&src0[0]); + u8x16_t src01 = *reinterpret_cast(&src0[16]); + u8x16_t src10 = *reinterpret_cast(&src1[0]); + u8x16_t src11 = *reinterpret_cast(&src1[16]); + u8x16_t src20 = *reinterpret_cast(&src2[0]); + u8x16_t src21 = *reinterpret_cast(&src2[16]); + + // This inner loop is SIMD - each loop iteration will process 16 cells. + for (; (x + 15) < (ps_context_->width - 1); x += 16) { + + // Construct jittered source temps, using __builtin_shufflevector(..) to + // extract a shifted 16 element vector from the 32 element concatenation + // of two source vectors. + u8x16_t src0j0 = src00; + u8x16_t src0j1 = __builtin_shufflevector(src00, src01, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + u8x16_t src0j2 = __builtin_shufflevector(src00, src01, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); + u8x16_t src1j0 = src10; + u8x16_t src1j1 = __builtin_shufflevector(src10, src11, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + u8x16_t src1j2 = __builtin_shufflevector(src10, src11, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); + u8x16_t src2j0 = src20; + u8x16_t src2j1 = __builtin_shufflevector(src20, src21, + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); + u8x16_t src2j2 = __builtin_shufflevector(src20, src21, + 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17); + + // Sum the jittered sources to construct neighbor count. + u8x16_t count = src0j0 + src0j1 + src0j2 + + src1j0 + + src1j2 + + src2j0 + src2j1 + src2j2; + // Add the center cell. + count = count + count + src1j1; + // If count > 4 and < 8, center cell will be alive in the next frame. + u8x16_t alive1 = count > kFour; + u8x16_t alive2 = count < kEight; + // Intersect the two comparisons from above. + u8x16_t alive = alive1 & alive2; + + // At this point, alive[x] will be one of two values: + // 0x00 for a dead cell + // 0xFF for an alive cell. + // + // Next, convert alive cells to green pixel color. + // Use __builtin_shufflevector(..) to construct output pixels from + // concantination of alive vector and kZero255 const vector. + // Indices 0..15 select the 16 cells from alive vector. + // Index 16 is zero constant from kZero255 constant vector. + // Index 17 is 255 constant from kZero255 constant vector. + // Output pixel color values are in BGRABGRABGRABGRA order. + // Since each pixel needs 4 bytes of color information, 16 cells will + // need to expand to 4 seperate 16 byte pixel splats. + u8x16_t pixel0_3 = __builtin_shufflevector(alive, kZero255, + 16, 0, 16, 17, 16, 1, 16, 17, 16, 2, 16, 17, 16, 3, 16, 17); + u8x16_t pixel4_7 = __builtin_shufflevector(alive, kZero255, + 16, 4, 16, 17, 16, 5, 16, 17, 16, 6, 16, 17, 16, 7, 16, 17); + u8x16_t pixel8_11 = __builtin_shufflevector(alive, kZero255, + 16, 8, 16, 17, 16, 9, 16, 17, 16, 10, 16, 17, 16, 11, 16, 17); + u8x16_t pixel12_15 = __builtin_shufflevector(alive, kZero255, + 16, 12, 16, 17, 16, 13, 16, 17, 16, 14, 16, 17, 16, 15, 16, 17); + + // Write 16 pixels to output pixel buffer. + *reinterpret_cast(pixel_line + 0) = pixel0_3; + *reinterpret_cast(pixel_line + 4) = pixel4_7; + *reinterpret_cast(pixel_line + 8) = pixel8_11; + *reinterpret_cast(pixel_line + 12) = pixel12_15; + + // Convert alive mask to 1 or 0 and store in destination cell array. + *reinterpret_cast(dst) = alive & kOne; + + // Increment pointers. + pixel_line += 16; + dst += 16; + src0 += 16; + src1 += 16; + src2 += 16; + + // Shift source over by 16 cells and read the next 16 cells. + src00 = src01; + src01 = *reinterpret_cast(&src0[16]); + src10 = src11; + src11 = *reinterpret_cast(&src1[16]); + src20 = src21; + src21 = *reinterpret_cast(&src2[16]); + } + } + + // The SIMD loop above does 16 cells at a time. The loop below is the + // regular version which processes one cell at a time. It is used to + // finish the remainder of the scanline not handled by the SIMD loop. + for (; x < (ps_context_->width - 1); ++x) { + // Sum the jittered sources to construct neighbor count. + int count = src0[0] + src0[1] + src0[2] + + src1[0] + + src1[2] + + src2[0] + src2[1] + src2[2]; + // Add the center cell. + count = count + count + src1[1]; + // Use table lookup indexed by count to determine pixel & alive state. + uint32_t color = kNeighborColors[count]; + *pixel_line++ = color; + *dst++ = kIsAlive[count]; + ++src0; + ++src1; + ++src2; + } +} + +// Static entry point for worker thread. +void Life::wSimulateEntry(int slice, void* thiz) { + static_cast(thiz)->wSimulate(slice); +} + +void Life::Simulate() { + // Stir up the edges to prevent the simulation from reaching steady state. + Stir(); + + if (multithread_) { + // If multi-threading enabled, dispatch tasks to pool of worker threads. + workers_->Dispatch(ps_context_->height, wSimulateEntry, this); + } else { + // Else manually simulate each line on this thread. + for (int y = 0; y < ps_context_->height; y++) { + wSimulateEntry(y, this); + } + } + std::swap(cell_in_, cell_out_); +} + +void Life::Update() { + + PSContext2DGetBuffer(ps_context_); + if (NULL == ps_context_->data) + return; + + // If we somehow have not allocated these pointers yet, skip this frame. + if (!cell_in_ || !cell_out_) return; + + // Simulate one (or more if benchmarking) frames + do { + Simulate(); + if (!benchmarking_) + break; + --benchmark_frame_counter_; + } while(benchmark_frame_counter_ > 0); + if (benchmarking_) + EndBenchmark(); + + PSContext2DSwapBuffer(ps_context_); +} + +// Starting point for the module. We do not use main since it would +// collide with main in libppapi_cpp. +int example_main(int argc, char* argv[]) { + Life life; + while (true) { + PSEvent* ps_event; + // Consume all available events + while ((ps_event = PSEventTryAcquire()) != NULL) { + life.HandleEvent(ps_event); + PSEventRelease(ps_event); + } + // Do simulation, render and present. + life.Update(); + } + return 0; +} + +// Register the function to call once the Instance Object is initialized. +// see: pappi_simple/ps_main.h +PPAPI_SIMPLE_REGISTER_MAIN(example_main); diff --git a/native_client_sdk/src/libraries/nacl_io/fifo_char.cc b/native_client_sdk/src/libraries/nacl_io/fifo_char.cc index 56245559724f4..144f5842be632 100644 --- a/native_client_sdk/src/libraries/nacl_io/fifo_char.cc +++ b/native_client_sdk/src/libraries/nacl_io/fifo_char.cc @@ -4,6 +4,7 @@ #include "nacl_io/fifo_char.h" +#include #include #include @@ -13,12 +14,14 @@ namespace nacl_io { FIFOChar::FIFOChar(size_t size) : buffer_(NULL), size_(size), avail_(0), tail_(0) { - if (size) - buffer_ = new char[size]; + if (size) { + buffer_ = (char*)malloc(size); + assert(buffer_ != NULL); + } } FIFOChar::~FIFOChar() { - delete[] buffer_; + free(buffer_); } bool FIFOChar::IsEmpty() { @@ -34,13 +37,11 @@ bool FIFOChar::Resize(size_t len) { if (len < avail_) return false; - // Read current data into new buffer - char* data = new char[len]; - avail_ = Read(data, avail_); - - // Replace buffer - delete[] buffer_; - buffer_ = data; + // Resize buffer + buffer_ = (char*)realloc(buffer_, len); + assert(buffer_ != NULL); + if (buffer_ == NULL) + return false; size_ = len; return true; } diff --git a/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs.cc b/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs.cc index 95809996b8bf6..cc44cfa0ba209 100644 --- a/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs.cc @@ -226,11 +226,11 @@ Error HttpFs::Init(const FsInitArgs& args) { error = ParseManifest(text); if (error) { - delete[] text; + free(text); return error; } - delete[] text; + free(text); } else if (iter->first == "allow_cross_origin_requests") { allow_cors_ = iter->second == "true"; } else if (iter->first == "allow_credentials") { @@ -396,7 +396,10 @@ Error HttpFs::LoadManifest(const std::string& manifest_name, if (error) return error; - char* text = new char[size + 1]; + char* text = (char*)malloc(size + 1); + assert(text != NULL); + if (text == NULL) + return ENOMEM; int len; error = manifest_node->Read(HandleAttr(), text, size, &len); if (error) diff --git a/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.cc b/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.cc index 653c646daa254..b41cefbda0ea5 100644 --- a/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.cc @@ -228,6 +228,8 @@ HttpFsNode::HttpFsNode(Filesystem* filesystem, bool cache_content) : Node(filesystem), url_(url), + buffer_(NULL), + buffer_len_(0), cache_content_(cache_content), has_cached_size_(false) { } @@ -557,12 +559,18 @@ Error HttpFsNode::ReadEntireResponseToTemp(const ScopedResource& loader, *out_bytes = 0; const int kBytesToRead = MAX_READ_BUFFER_SIZE; - buffer_.resize(kBytesToRead); + buffer_ = (char*)realloc(buffer_, kBytesToRead); + assert(buffer_); + if (!buffer_) { + buffer_len_ = 0; + return ENOMEM; + } + buffer_len_ = kBytesToRead; while (true) { int bytes_read; Error error = - ReadResponseToBuffer(loader, buffer_.data(), kBytesToRead, &bytes_read); + ReadResponseToBuffer(loader, buffer_, kBytesToRead, &bytes_read); if (error) return error; @@ -604,16 +612,23 @@ Error HttpFsNode::ReadResponseToTemp(const ScopedResource& loader, int* out_bytes) { *out_bytes = 0; - if (buffer_.size() < static_cast(count)) - buffer_.resize(std::min(count, MAX_READ_BUFFER_SIZE)); + if (buffer_len_ < count) { + int new_len = std::min(count, MAX_READ_BUFFER_SIZE); + buffer_ = (char*)realloc(buffer_, new_len); + assert(buffer_); + if (!buffer_) { + buffer_len_ = 0; + return ENOMEM; + } + buffer_len_ = new_len; + } int bytes_left = count; while (bytes_left > 0) { - int bytes_to_read = - std::min(static_cast(bytes_left), buffer_.size()); + int bytes_to_read = std::min(bytes_left, buffer_len_); int bytes_read; Error error = ReadResponseToBuffer( - loader, buffer_.data(), bytes_to_read, &bytes_read); + loader, buffer_, bytes_to_read, &bytes_read); if (error) return error; diff --git a/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.h b/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.h index a779995edae88..cccd335d70845 100644 --- a/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.h +++ b/native_client_sdk/src/libraries/nacl_io/httpfs/http_fs_node.h @@ -86,7 +86,8 @@ class HttpFsNode : public Node { int* out_bytes); std::string url_; - std::vector buffer_; + char* buffer_; + int buffer_len_; bool cache_content_; bool has_cached_size_; diff --git a/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.cc b/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.cc index 1e41591ca0a3b..0040df3faeb8b 100644 --- a/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.cc @@ -2,14 +2,20 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS +#endif + #include "nacl_io/memfs/mem_fs_node.h" +#include #include #include #include #include "nacl_io/kernel_handle.h" +#include "nacl_io/osinttypes.h" #include "nacl_io/osstat.h" #include "sdk_util/auto_lock.h" @@ -23,7 +29,10 @@ const size_t kMaxResizeIncrement = 16 * 1024 * 1024; } // namespace -MemFsNode::MemFsNode(Filesystem* filesystem) : Node(filesystem) { +MemFsNode::MemFsNode(Filesystem* filesystem) + : Node(filesystem), + data_(NULL), + data_capacity_(0) { SetType(S_IFREG); } @@ -46,7 +55,7 @@ Error MemFsNode::Read(const HandleAttr& attr, count = size - attr.offs; } - memcpy(buf, &data_[attr.offs], count); + memcpy(buf, data_ + attr.offs, count); *out_bytes = static_cast(count); return 0; } @@ -56,40 +65,56 @@ Error MemFsNode::Write(const HandleAttr& attr, size_t count, int* out_bytes) { *out_bytes = 0; - AUTO_LOCK(node_lock_); if (count == 0) return 0; - if (count + attr.offs > static_cast(stat_.st_size)) { - Resize(count + attr.offs); - count = stat_.st_size - attr.offs; + AUTO_LOCK(node_lock_); + off_t new_size = attr.offs + count; + if (new_size > stat_.st_size) { + Error error = Resize(new_size); + if (error) { + LOG_ERROR("memfs: resize (%" PRIoff ") failed: %s", new_size, + strerror(error)); + return error; + } } - memcpy(&data_[attr.offs], buf, count); + memcpy(data_ + attr.offs, buf, count); *out_bytes = static_cast(count); return 0; } Error MemFsNode::FTruncate(off_t new_size) { AUTO_LOCK(node_lock_); - Resize(new_size); - return 0; + return Resize(new_size); } -void MemFsNode::Resize(off_t new_size) { - if (new_size > static_cast(data_.capacity())) { +Error MemFsNode::Resize(off_t new_length) { + if (new_length < 0) + return EINVAL; + size_t new_size = static_cast(new_length); + + if (new_size > data_capacity_) { // While the node size is small, grow exponentially. When it starts to get // larger, grow linearly. - size_t extra = std::min(new_size, kMaxResizeIncrement); - data_.reserve(new_size + extra); - } else if (new_size < stat_.st_size) { - // Shrink to fit. std::vector usually doesn't reduce allocation size, so - // use the swap trick. - std::vector(data_).swap(data_); + size_t extra = std::min(new_size, kMaxResizeIncrement); + data_capacity_ = new_size + extra; + } else { + data_capacity_ = new_size; } - data_.resize(new_size); - stat_.st_size = new_size; + + data_ = (char*)realloc(data_, data_capacity_); + if (data_capacity_ != 0) { + assert(data_ != NULL); + if (data_ == NULL) + return ENOMEM; + if (new_length > stat_.st_size) + memset(data_ + stat_.st_size, 0, new_length - stat_.st_size); + } + + stat_.st_size = new_length; + return 0; } } // namespace nacl_io diff --git a/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.h b/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.h index e734b1d5ac346..77a327a31f02a 100644 --- a/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.h +++ b/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs_node.h @@ -7,8 +7,6 @@ #include "nacl_io/node.h" -#include - namespace nacl_io { class MemFsNode : public Node { @@ -31,9 +29,10 @@ class MemFsNode : public Node { virtual Error FTruncate(off_t size); private: - void Resize(off_t size); + Error Resize(off_t size); - std::vector data_; + char* data_; + size_t data_capacity_; friend class MemFs; }; diff --git a/native_client_sdk/src/libraries/nacl_io/osinttypes.h b/native_client_sdk/src/libraries/nacl_io/osinttypes.h index 491922c897d47..2b0b92a5043a2 100644 --- a/native_client_sdk/src/libraries/nacl_io/osinttypes.h +++ b/native_client_sdk/src/libraries/nacl_io/osinttypes.h @@ -34,4 +34,10 @@ #endif +#if !defined(__native_client__) +#define PRIoff "ld" +#else +#define PRIoff "lld" +#endif + #endif /* NACL_IO_OSINTTYPES_H_ */ diff --git a/native_client_sdk/src/libraries/nacl_io/socket/fifo_packet.h b/native_client_sdk/src/libraries/nacl_io/socket/fifo_packet.h index 295465ded9f9d..e8ba0b3c38423 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/fifo_packet.h +++ b/native_client_sdk/src/libraries/nacl_io/socket/fifo_packet.h @@ -8,7 +8,6 @@ #include #include -#include #include "nacl_io/fifo_interface.h" #include "ppapi/c/pp_resource.h" diff --git a/native_client_sdk/src/libraries/nacl_io/socket/packet.cc b/native_client_sdk/src/libraries/nacl_io/socket/packet.cc index f437d0983f023..69a894f64f14a 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/packet.cc +++ b/native_client_sdk/src/libraries/nacl_io/socket/packet.cc @@ -4,6 +4,7 @@ #include "nacl_io/socket/packet.h" +#include #include #include "nacl_io/pepper_interface.h" @@ -17,19 +18,14 @@ Packet::Packet(PepperInterface* ppapi) Packet::~Packet() { if ((NULL != ppapi_) && addr_) ppapi_->ReleaseResource(addr_); - delete[] buffer_; -} - -void Packet::Take(const void* buffer, size_t len, PP_Resource addr) { - addr_ = addr; - len_ = len; - buffer_ = static_cast(const_cast(buffer)); + free(buffer_); } void Packet::Copy(const void* buffer, size_t len, PP_Resource addr) { addr_ = addr; len_ = len; - buffer_ = new char[len]; + buffer_ = (char*)malloc(len); + assert(buffer_); memcpy(buffer_, buffer, len); if (addr && (NULL != ppapi_)) diff --git a/native_client_sdk/src/libraries/nacl_io/socket/packet.h b/native_client_sdk/src/libraries/nacl_io/socket/packet.h index bcae9131a3d87..ce199e65d5e93 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/packet.h +++ b/native_client_sdk/src/libraries/nacl_io/socket/packet.h @@ -14,8 +14,7 @@ namespace nacl_io { class PepperInterface; -// NOTE: The Packet class always owns the buffer and address, either by 'Copy' -// or by 'Take' ownership. +// NOTE: The Packet class always owns the buffer and address. class Packet { public: explicit Packet(PepperInterface* ppapi); @@ -24,9 +23,6 @@ class Packet { // Copy the buffer, and address reference void Copy(const void* buffer, size_t len, PP_Resource addr); - // Take ownership the buffer, and address reference - void Take(const void* buffer, size_t len, PP_Resource addr); - char* buffer() { return buffer_; } PP_Resource addr() { return addr_; } size_t len() { return len_; } diff --git a/native_client_sdk/src/libraries/nacl_io/socket/tcp_node.cc b/native_client_sdk/src/libraries/nacl_io/socket/tcp_node.cc index df7c8670b812c..2066c712cd22b 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/tcp_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/socket/tcp_node.cc @@ -30,7 +30,9 @@ class TcpWork : public StreamFs::Work { emitter_(emitter), data_(NULL) {} - ~TcpWork() { delete[] data_; } + ~TcpWork() { + free(data_); + } TCPSocketInterface* TCPInterface() { return filesystem()->ppapi()->GetTCPSocketInterface(); @@ -63,7 +65,10 @@ class TcpSendWork : public TcpWork { if (capped_len == 0) return false; - data_ = new char[capped_len]; + data_ = (char*)malloc(capped_len); + assert(data_); + if (data_ == NULL) + return false; emitter_->ReadOut_Locked(data_, capped_len); int err = TCPInterface()->Write(node_->socket_resource(), @@ -126,7 +131,10 @@ class TcpRecvWork : public TcpWork { if (capped_len == 0) return false; - data_ = new char[capped_len]; + data_ = (char*)malloc(capped_len); + assert(data_); + if (data_ == NULL) + return false; int err = TCPInterface()->Read(stream->socket_resource(), data_, capped_len, @@ -480,6 +488,12 @@ Error TcpNode::Connect(const HandleAttr& attr, return err; } + // Make sure the connection succeeded. + if (last_errno_ != 0) { + ConnectFailed_Locked(); + return last_errno_; + } + ConnectDone_Locked(); return 0; } diff --git a/native_client_sdk/src/libraries/nacl_io/socket/udp_node.cc b/native_client_sdk/src/libraries/nacl_io/socket/udp_node.cc index 816f8e9a83249..9b0bfc8f1801f 100644 --- a/native_client_sdk/src/libraries/nacl_io/socket/udp_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/socket/udp_node.cc @@ -96,11 +96,8 @@ class UdpRecvWork : public UdpWork { public: explicit UdpRecvWork(const ScopedUdpEventEmitter& emitter) : UdpWork(emitter) { - data_ = new char[kMaxPacketSize]; } - ~UdpRecvWork() { delete[] data_; } - virtual bool Start(int32_t val) { AUTO_LOCK(emitter_->GetLock()); UdpNode* stream = static_cast(emitter_->stream()); @@ -146,7 +143,7 @@ class UdpRecvWork : public UdpWork { } private: - char* data_; + char data_[kMaxPacketSize]; PP_Resource addr_; }; diff --git a/native_client_sdk/src/test_all.py b/native_client_sdk/src/test_all.py index 9f468b413cdf1..6f2113a79e7d3 100755 --- a/native_client_sdk/src/test_all.py +++ b/native_client_sdk/src/test_all.py @@ -30,6 +30,7 @@ TOOLCHAIN_OUT = os.path.join(build_paths.OUT_DIR, 'sdk_tests', 'toolchain') TEST_MODULES = [ + 'build_version_test', 'create_html_test', 'create_nmf_test', 'easy_template_test', diff --git a/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc b/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc index 850fde92bc4c1..b9e2adcb114f4 100644 --- a/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc +++ b/native_client_sdk/src/tests/nacl_io_socket_test/socket_test.cc @@ -392,6 +392,16 @@ TEST_F(SocketTestWithServer, TCPConnectNonBlock) { // And SO_ERROR should be 0. } +TEST_F(SocketTestTCP, TCPConnectFails) { + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + + // 10 is an unassigned well-known port, nothing should be bound to it. + IP4ToSockAddr(LOCAL_HOST, 10, &addr); + ASSERT_EQ(-1, ki_connect(sock1_, (sockaddr*) &addr, addrlen)); + ASSERT_EQ(ECONNREFUSED, errno); +} + TEST_F(SocketTest, Getsockopt) { sock1_ = ki_socket(AF_INET, SOCK_STREAM, 0); EXPECT_GT(sock1_, -1); diff --git a/native_client_sdk/src/tools/getos.py b/native_client_sdk/src/tools/getos.py index c4e31c3188834..e71c68f3f992d 100755 --- a/native_client_sdk/src/tools/getos.py +++ b/native_client_sdk/src/tools/getos.py @@ -68,6 +68,7 @@ def GetSDKVersion(): version = None revision = None + commit_position = None for line in open(readme): if ':' in line: name, value = line.split(':', 1) @@ -75,8 +76,10 @@ def GetSDKVersion(): version = value.strip() if name == "Chrome Revision": revision = value.strip() + if name == "Chrome Commit Position": + commit_position = value.strip() - if revision == None or version == None: + if revision is None or version is None or commit_position is None: raise Error("error parsing SDK README: %s" % readme) try: @@ -85,7 +88,7 @@ def GetSDKVersion(): except ValueError: raise Error("error parsing SDK README: %s" % readme) - return (version, revision) + return (version, revision, commit_position) def GetSystemArch(platform): @@ -205,6 +208,8 @@ def main(args): help='Print major version of the NaCl SDK.') parser.add_option('--sdk-revision', action='store_true', help='Print revision number of the NaCl SDK.') + parser.add_option('--sdk-commit-position', action='store_true', + help='Print commit position of the NaCl SDK.') parser.add_option('--check-version', help='Check that the SDK version is at least as great as the ' 'version passed in.') @@ -230,6 +235,8 @@ def main(args): out = GetSDKVersion()[0] elif options.sdk_revision: out = GetSDKVersion()[1] + elif options.sdk_commit_position: + out = GetSDKVersion()[2] elif options.check_version: required_version = ParseVersion(options.check_version) version = GetSDKVersion() diff --git a/native_client_sdk/src/tools/tests/getos_test.py b/native_client_sdk/src/tools/tests/getos_test.py index 498c52ac3622e..acdd68f314d39 100755 --- a/native_client_sdk/src/tools/tests/getos_test.py +++ b/native_client_sdk/src/tools/tests/getos_test.py @@ -117,10 +117,11 @@ def tearDown(self): def testGetSDKVersion(self): """correctly parses README to find SDK version.""" - expected_version = (16, 196) + expected_version = (16, 196, 'f00baacabba6e-refs/heads/master@{#100}') with open(os.path.join(self.tempdir, 'README'), 'w') as out: out.write('Version: %s\n' % expected_version[0]) out.write('Chrome Revision: %s\n' % expected_version[1]) + out.write('Chrome Commit Position: %s\n' % expected_version[2]) version = getos.GetSDKVersion() self.assertEqual(version, expected_version) diff --git a/net/BUILD.gn b/net/BUILD.gn index 5609408e443ca..d9b2d395114fe 100644 --- a/net/BUILD.gn +++ b/net/BUILD.gn @@ -688,7 +688,7 @@ source_set("test_support") { deps = [ "//base", "//base/test:test_support", - "//crypto:platform", + "//crypto", "//net", "//net/tools/tld_cleanup", "//testing/gmock", @@ -720,7 +720,9 @@ source_set("test_support") { ] } - if (!use_nss_certs) { + if (use_nss_certs) { + deps += ["//crypto:platform" ] + } else { sources -= [ "test/cert_test_util_nss.cc", ] @@ -753,6 +755,7 @@ source_set("balsa") { deps = [ ":net", "//base", + "//url", ] } @@ -846,7 +849,10 @@ if (!is_ios && !is_android) { ] if (is_linux) { - configs += [ "//build/config/linux:gconf" ] + configs += [ + "//build/config/linux:gconf", + "//build/config/linux:glib", + ] deps += [ "//build/config/linux:gio" ] } } diff --git a/net/PRESUBMIT.py b/net/PRESUBMIT.py index 3b830b7aea6f9..e898d990c9bb5 100644 --- a/net/PRESUBMIT.py +++ b/net/PRESUBMIT.py @@ -17,7 +17,7 @@ def GetPreferredTryMasters(project, change): 'mac_chromium_rel_swarming': set(['defaulttests']), }, 'tryserver.chromium.win': { - 'win_chromium_rel': set(['defaulttests']), + 'win_chromium_rel_swarming': set(['defaulttests']), } } # Changes that touch NSS files will likely need a corresponding OpenSSL edit. diff --git a/net/android/java/src/org/chromium/net/ProxyChangeListener.java b/net/android/java/src/org/chromium/net/ProxyChangeListener.java index 7b573968b38cf..63bfb30514cae 100644 --- a/net/android/java/src/org/chromium/net/ProxyChangeListener.java +++ b/net/android/java/src/org/chromium/net/ProxyChangeListener.java @@ -99,15 +99,19 @@ private ProxyConfig extractNewProxy(Intent intent) { try { final String GET_HOST_NAME = "getHost"; final String GET_PORT_NAME = "getPort"; - Object props = intent.getExtras().get("proxy"); - if (props == null) { - return null; - } String className; + String proxyInfo; if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { className = "android.net.ProxyProperties"; + proxyInfo = "proxy"; } else { className = "android.net.ProxyInfo"; + proxyInfo = "android.intent.extra.PROXY_INFO"; + } + + Object props = intent.getExtras().get(proxyInfo); + if (props == null) { + return null; } Class cls = Class.forName(className); diff --git a/net/base/load_flags_list.h b/net/base/load_flags_list.h index f491baf9ea69f..72792eb9aaba4 100644 --- a/net/base/load_flags_list.h +++ b/net/base/load_flags_list.h @@ -41,90 +41,87 @@ LOAD_FLAG(DISABLE_INTERCEPT, 1 << 6) // If present, upload progress messages should be provided to initiator. LOAD_FLAG(ENABLE_UPLOAD_PROGRESS, 1 << 7) -// If present, collect load timing for the request. -LOAD_FLAG(ENABLE_LOAD_TIMING, 1 << 8) - // If present, ignores certificate mismatches with the domain name. // (The default behavior is to trigger an OnSSLCertificateError callback.) -LOAD_FLAG(IGNORE_CERT_COMMON_NAME_INVALID, 1 << 9) +LOAD_FLAG(IGNORE_CERT_COMMON_NAME_INVALID, 1 << 8) // If present, ignores certificate expiration dates // (The default behavior is to trigger an OnSSLCertificateError callback). -LOAD_FLAG(IGNORE_CERT_DATE_INVALID, 1 << 10) +LOAD_FLAG(IGNORE_CERT_DATE_INVALID, 1 << 9) // If present, trusts all certificate authorities // (The default behavior is to trigger an OnSSLCertificateError callback). -LOAD_FLAG(IGNORE_CERT_AUTHORITY_INVALID, 1 << 11) +LOAD_FLAG(IGNORE_CERT_AUTHORITY_INVALID, 1 << 10) // If present, causes certificate revocation checks to be skipped on secure // connections. -LOAD_FLAG(DISABLE_CERT_REVOCATION_CHECKING, 1 << 12) +LOAD_FLAG(DISABLE_CERT_REVOCATION_CHECKING, 1 << 11) // If present, ignores wrong key usage of the certificate // (The default behavior is to trigger an OnSSLCertificateError callback). -LOAD_FLAG(IGNORE_CERT_WRONG_USAGE, 1 << 13) +LOAD_FLAG(IGNORE_CERT_WRONG_USAGE, 1 << 12) // This load will not make any changes to cookies, including storing new // cookies or updating existing ones. -LOAD_FLAG(DO_NOT_SAVE_COOKIES, 1 << 14) +LOAD_FLAG(DO_NOT_SAVE_COOKIES, 1 << 13) // Do not resolve proxies. This override is used when downloading PAC files // to avoid having a circular dependency. -LOAD_FLAG(BYPASS_PROXY, 1 << 15) +LOAD_FLAG(BYPASS_PROXY, 1 << 14) // Indicate this request is for a download, as opposed to viewing. -LOAD_FLAG(IS_DOWNLOAD, 1 << 16) +LOAD_FLAG(IS_DOWNLOAD, 1 << 15) // Requires EV certificate verification. -LOAD_FLAG(VERIFY_EV_CERT, 1 << 17) +LOAD_FLAG(VERIFY_EV_CERT, 1 << 16) // This load will not send any cookies. -LOAD_FLAG(DO_NOT_SEND_COOKIES, 1 << 18) +LOAD_FLAG(DO_NOT_SEND_COOKIES, 1 << 17) // This load will not send authentication data (user name/password) // to the server (as opposed to the proxy). -LOAD_FLAG(DO_NOT_SEND_AUTH_DATA, 1 << 19) +LOAD_FLAG(DO_NOT_SEND_AUTH_DATA, 1 << 18) // This should only be used for testing (set by HttpNetworkTransaction). -LOAD_FLAG(IGNORE_ALL_CERT_ERRORS, 1 << 20) +LOAD_FLAG(IGNORE_ALL_CERT_ERRORS, 1 << 19) // Indicate that this is a top level frame, so that we don't assume it is a // subresource and speculatively pre-connect or pre-resolve when a referring // page is loaded. -LOAD_FLAG(MAIN_FRAME, 1 << 21) +LOAD_FLAG(MAIN_FRAME, 1 << 20) // Indicate that this is a sub frame, and hence it might have subresources that // should be speculatively resolved, or even speculatively preconnected. -LOAD_FLAG(SUB_FRAME, 1 << 22) +LOAD_FLAG(SUB_FRAME, 1 << 21) // If present, intercept actual request/response headers from network stack // and report them to renderer. This includes cookies, so the flag is only // respected if renderer has CanReadRawCookies capability in the security // policy. -LOAD_FLAG(REPORT_RAW_HEADERS, 1 << 23) +LOAD_FLAG(REPORT_RAW_HEADERS, 1 << 22) // Indicates that this load was motivated by the rel=prefetch feature, // and is (in theory) not intended for the current frame. -LOAD_FLAG(PREFETCH, 1 << 24) +LOAD_FLAG(PREFETCH, 1 << 23) // Indicates that this is a load that ignores limits and should complete // immediately. -LOAD_FLAG(IGNORE_LIMITS, 1 << 25) +LOAD_FLAG(IGNORE_LIMITS, 1 << 24) // Suppress login prompts for this request. Cached credentials or // default credentials may still be used for authentication. -LOAD_FLAG(DO_NOT_PROMPT_FOR_LOGIN, 1 << 26) +LOAD_FLAG(DO_NOT_PROMPT_FOR_LOGIN, 1 << 25) // Indicates that the operation is somewhat likely to be due to an // explicit user action. This can be used as a hint to treat the // request with higher priority. -LOAD_FLAG(MAYBE_USER_GESTURE, 1 << 27) +LOAD_FLAG(MAYBE_USER_GESTURE, 1 << 26) // Indicates that the username:password portion of the URL should not // be honored, but that other forms of authority may be used. -LOAD_FLAG(DO_NOT_USE_EMBEDDED_IDENTITY, 1 << 28) +LOAD_FLAG(DO_NOT_USE_EMBEDDED_IDENTITY, 1 << 27) // Send request directly to the origin if the effective proxy is the data // reduction proxy. // TODO(rcs): Remove this flag as soon as http://crbug.com/339237 is resolved. -LOAD_FLAG(BYPASS_DATA_REDUCTION_PROXY, 1 << 29) +LOAD_FLAG(BYPASS_DATA_REDUCTION_PROXY, 1 << 28) diff --git a/net/base/mime_util.cc b/net/base/mime_util.cc index 7dd51a2945d45..5e9be15a04342 100644 --- a/net/base/mime_util.cc +++ b/net/base/mime_util.cc @@ -471,6 +471,8 @@ static bool IsCodecSupportedOnAndroid(MimeUtil::Codec codec) { case MimeUtil::MPEG4_AAC_LC: case MimeUtil::MPEG4_AAC_SBRv1: case MimeUtil::H264_BASELINE: + case MimeUtil::H264_MAIN: + case MimeUtil::H264_HIGH: case MimeUtil::VP8: case MimeUtil::VORBIS: return true; @@ -481,11 +483,6 @@ static bool IsCodecSupportedOnAndroid(MimeUtil::Codec codec) { // MPEG-2 variants of AAC are not supported on Android. return false; - case MimeUtil::H264_MAIN: - case MimeUtil::H264_HIGH: - // H.264 Main and High profiles are not supported on Android. - return false; - case MimeUtil::VP9: // VP9 is supported only in KitKat+ (API Level 19). return base::android::BuildInfo::GetInstance()->sdk_int() >= 19; diff --git a/net/base/net_string_util_icu_alternatives_android.cc b/net/base/net_string_util_icu_alternatives_android.cc index ca0630328f5d8..ca0e7f0315db7 100644 --- a/net/base/net_string_util_icu_alternatives_android.cc +++ b/net/base/net_string_util_icu_alternatives_android.cc @@ -20,12 +20,13 @@ namespace { ScopedJavaLocalRef ConvertToJstring(const std::string& text, const char* charset) { JNIEnv* env = base::android::AttachCurrentThread(); - jobject java_byte_buffer = - env->NewDirectByteBuffer(const_cast(text.data()), text.length()); + ScopedJavaLocalRef java_byte_buffer( + env, + env->NewDirectByteBuffer(const_cast(text.data()), text.length())); base::android::ScopedJavaLocalRef java_charset = base::android::ConvertUTF8ToJavaString(env, base::StringPiece(charset)); ScopedJavaLocalRef java_result = - android::Java_NetStringUtil_convertToUnicode(env, java_byte_buffer, + android::Java_NetStringUtil_convertToUnicode(env, java_byte_buffer.obj(), java_charset.obj()); return java_result; } @@ -36,13 +37,14 @@ ScopedJavaLocalRef ConvertToJstring(const std::string& text, ScopedJavaLocalRef ConvertToNormalizedJstring( const std::string& text, const char* charset) { JNIEnv* env = base::android::AttachCurrentThread(); - jobject java_byte_buffer = - env->NewDirectByteBuffer(const_cast(text.data()), text.length()); + ScopedJavaLocalRef java_byte_buffer( + env, + env->NewDirectByteBuffer(const_cast(text.data()), text.length())); base::android::ScopedJavaLocalRef java_charset = base::android::ConvertUTF8ToJavaString(env, base::StringPiece(charset)); ScopedJavaLocalRef java_result = android::Java_NetStringUtil_convertToUnicodeAndNormalize( - env, java_byte_buffer, java_charset.obj()); + env, java_byte_buffer.obj(), java_charset.obj()); return java_result; } @@ -51,13 +53,14 @@ ScopedJavaLocalRef ConvertToNormalizedJstring( ScopedJavaLocalRef ConvertToJstringWithSubstitutions( const std::string& text, const char* charset) { JNIEnv* env = base::android::AttachCurrentThread(); - jobject java_byte_buffer = - env->NewDirectByteBuffer(const_cast(text.data()), text.length()); + ScopedJavaLocalRef java_byte_buffer( + env, + env->NewDirectByteBuffer(const_cast(text.data()), text.length())); base::android::ScopedJavaLocalRef java_charset = base::android::ConvertUTF8ToJavaString(env, base::StringPiece(charset)); ScopedJavaLocalRef java_result = android::Java_NetStringUtil_convertToUnicodeWithSubstitutions( - env, java_byte_buffer, java_charset.obj()); + env, java_byte_buffer.obj(), java_charset.obj()); return java_result; } diff --git a/net/base/net_util.cc b/net/base/net_util.cc index 941dca9ab53ba..1b65c6b10e4bb 100644 --- a/net/base/net_util.cc +++ b/net/base/net_util.cc @@ -799,6 +799,28 @@ int ConvertAddressFamily(AddressFamily address_family) { return AF_UNSPEC; } +bool ParseURLHostnameToNumber(const std::string& hostname, + IPAddressNumber* ip_number) { + // |hostname| is an already canoncalized hostname, conforming to RFC 3986. + // For an IP address, this is defined in Section 3.2.2 of RFC 3986, with + // the canonical form for IPv6 addresses defined in Section 4 of RFC 5952. + url::Component host_comp(0, hostname.size()); + + // If it has a bracket, try parsing it as an IPv6 address. + if (hostname[0] == '[') { + ip_number->resize(16); // 128 bits. + return url::IPv6AddressToNumber( + hostname.data(), host_comp, &(*ip_number)[0]); + } + + // Otherwise, try IPv4. + ip_number->resize(4); // 32 bits. + int num_components; + url::CanonHostInfo::Family family = url::IPv4AddressToNumber( + hostname.data(), host_comp, &(*ip_number)[0], &num_components); + return family == url::CanonHostInfo::IPV4; +} + bool ParseIPLiteralToNumber(const std::string& ip_literal, IPAddressNumber* ip_number) { // |ip_literal| could be either a IPv4 or an IPv6 literal. If it contains diff --git a/net/base/net_util.h b/net/base/net_util.h index 364d41ebd50dc..c74dfd2ea4848 100644 --- a/net/base/net_util.h +++ b/net/base/net_util.h @@ -372,6 +372,11 @@ NET_EXPORT_PRIVATE AddressFamily GetAddressFamily( // Maps the given AddressFamily to either AF_INET, AF_INET6 or AF_UNSPEC. NET_EXPORT_PRIVATE int ConvertAddressFamily(AddressFamily address_family); +// Parses a URL-safe IP literal (see RFC 3986, Sec 3.2.2) to its numeric value. +// Returns true on success, and fills |ip_number| with the numeric value +NET_EXPORT bool ParseURLHostnameToNumber(const std::string& hostname, + IPAddressNumber* ip_number); + // Parses an IP address literal (either IPv4 or IPv6) to its numeric value. // Returns true on success and fills |ip_number| with the numeric value. NET_EXPORT bool ParseIPLiteralToNumber(const std::string& ip_literal, diff --git a/net/base/net_util_unittest.cc b/net/base/net_util_unittest.cc index 674a7259121f0..8306db53c2b86 100644 --- a/net/base/net_util_unittest.cc +++ b/net/base/net_util_unittest.cc @@ -524,6 +524,31 @@ TEST(NetUtilTest, ConvertIPv4NumberToIPv6Number) { EXPECT_EQ("::ffff:c0a8:1", IPAddressToString(ipv6_number)); } +TEST(NetUtilTest, ParseURLHostnameToNumber_FailParse) { + IPAddressNumber number; + + EXPECT_FALSE(ParseURLHostnameToNumber("bad value", &number)); + EXPECT_FALSE(ParseURLHostnameToNumber("bad:value", &number)); + EXPECT_FALSE(ParseURLHostnameToNumber(std::string(), &number)); + EXPECT_FALSE(ParseURLHostnameToNumber("192.168.0.1:30", &number)); + EXPECT_FALSE(ParseURLHostnameToNumber(" 192.168.0.1 ", &number)); + EXPECT_FALSE(ParseURLHostnameToNumber("::1", &number)); +} + +TEST(NetUtilTest, ParseURLHostnameToNumber_IPv4) { + IPAddressNumber number; + EXPECT_TRUE(ParseURLHostnameToNumber("192.168.0.1", &number)); + EXPECT_EQ("192,168,0,1", DumpIPNumber(number)); + EXPECT_EQ("192.168.0.1", IPAddressToString(number)); +} + +TEST(NetUtilTest, ParseURLHostnameToNumber_IPv6) { + IPAddressNumber number; + EXPECT_TRUE(ParseURLHostnameToNumber("[1:abcd::3:4:ff]", &number)); + EXPECT_EQ("0,1,171,205,0,0,0,0,0,0,0,3,0,4,0,255", DumpIPNumber(number)); + EXPECT_EQ("1:abcd::3:4:ff", IPAddressToString(number)); +} + TEST(NetUtilTest, IsIPv4Mapped) { IPAddressNumber ipv4_number; EXPECT_TRUE(ParseIPLiteralToNumber("192.168.0.1", &ipv4_number)); diff --git a/net/base/network_delegate.cc b/net/base/network_delegate.cc index 448f217641ecf..76d94abd2df94 100644 --- a/net/base/network_delegate.cc +++ b/net/base/network_delegate.cc @@ -21,11 +21,21 @@ int NetworkDelegate::NotifyBeforeURLRequest( return OnBeforeURLRequest(request, callback, new_url); } -void NetworkDelegate::NotifyResolveProxy(const GURL& url, int load_flags, - ProxyInfo* result) { +void NetworkDelegate::NotifyResolveProxy( + const GURL& url, + int load_flags, + const ProxyService& proxy_service, + ProxyInfo* result) { DCHECK(CalledOnValidThread()); DCHECK(result); - OnResolveProxy(url, load_flags, result); + OnResolveProxy(url, load_flags, proxy_service, result); +} + +void NetworkDelegate::NotifyProxyFallback( + const ProxyServer& bad_proxy, + int net_error) { + DCHECK(CalledOnValidThread()); + OnProxyFallback(bad_proxy, net_error); } int NetworkDelegate::NotifyBeforeSendHeaders( @@ -162,8 +172,15 @@ int NetworkDelegate::OnBeforeURLRequest(URLRequest* request, return OK; } -void NetworkDelegate::OnResolveProxy(const GURL& url, int load_flags, - ProxyInfo* result) { +void NetworkDelegate::OnResolveProxy( + const GURL& url, + int load_flags, + const ProxyService& proxy_service, + ProxyInfo* result) { +} + +void NetworkDelegate::OnProxyFallback(const ProxyServer& bad_proxy, + int net_error) { } int NetworkDelegate::OnBeforeSendHeaders(URLRequest* request, diff --git a/net/base/network_delegate.h b/net/base/network_delegate.h index aa8fa9a3c4f86..a290d16604eef 100644 --- a/net/base/network_delegate.h +++ b/net/base/network_delegate.h @@ -36,6 +36,8 @@ class CookieOptions; class HttpRequestHeaders; class HttpResponseHeaders; class ProxyInfo; +class ProxyServer; +class ProxyService; class SocketStream; class URLRequest; @@ -61,8 +63,12 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe { int NotifyBeforeURLRequest(URLRequest* request, const CompletionCallback& callback, GURL* new_url); - void NotifyResolveProxy(const GURL& url, int load_flags, + void NotifyResolveProxy(const GURL& url, + int load_flags, + const ProxyService& proxy_service, ProxyInfo* result); + void NotifyProxyFallback(const ProxyServer& bad_proxy, + int net_error); int NotifyBeforeSendHeaders(URLRequest* request, const CompletionCallback& callback, HttpRequestHeaders* headers); @@ -128,8 +134,16 @@ class NET_EXPORT NetworkDelegate : public base::NonThreadSafe { // may override the decision by modifying the ProxyInfo |result|. virtual void OnResolveProxy(const GURL& url, int load_flags, + const ProxyService& proxy_service, ProxyInfo* result); + // Called when use of |bad_proxy| fails due to |net_error|. |net_error| is + // the network error encountered, if any, and OK if the fallback was + // for a reason other than a network error (e.g. the proxy service was + // explicitly directed to skip a proxy). + virtual void OnProxyFallback(const ProxyServer& bad_proxy, + int net_error); + // Called right before the HTTP headers are sent. Allows the delegate to // read/write |headers| before they get sent out. |callback| and |headers| are // valid only until OnCompleted or OnURLRequestDestroyed is called for this diff --git a/net/base/openssl_private_key_store_memory.cc b/net/base/openssl_private_key_store_memory.cc index 0913e460bd2ab..4d6a287c2a7b7 100644 --- a/net/base/openssl_private_key_store_memory.cc +++ b/net/base/openssl_private_key_store_memory.cc @@ -36,7 +36,7 @@ class MemoryKeyPairStore { } bool StoreKeyPair(EVP_PKEY* pkey) { - CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY); + EVP_PKEY_dup(pkey); base::AutoLock lock(lock_); keys_.push_back(pkey); return true; diff --git a/net/base/registry_controlled_domains/OWNERS b/net/base/registry_controlled_domains/OWNERS index 9f8b179960287..2ba05a6168c48 100644 --- a/net/base/registry_controlled_domains/OWNERS +++ b/net/base/registry_controlled_domains/OWNERS @@ -1,2 +1,3 @@ pam@chromium.org pkasting@chromium.org +rsleevi@chromium.org diff --git a/net/base/registry_controlled_domains/effective_tld_names.dat b/net/base/registry_controlled_domains/effective_tld_names.dat index 4a8664322b8e5..19881bf00090d 100644 --- a/net/base/registry_controlled_domains/effective_tld_names.dat +++ b/net/base/registry_controlled_domains/effective_tld_names.dat @@ -7992,6 +7992,171 @@ click // cern : 2014-06-05 European Organization for Nuclear Research ("CERN") cern +// healthcare : 2014-06-13 Silver Glen, LLC +healthcare + +// xn--30rr7y : 2014-06-13 Excellent First Limited +xn--30rr7y + +// band : 2014-06-13 Auburn Hollow, LLC +band + +// xn--9et52u : 2014-06-13 RISE VICTORY LIMITED +xn--9et52u + +// world : 2014-06-13 Bitter Fields, LLC +world + +// latrobe : 2014-06-16 La Trobe University +latrobe + +// bible : 2014-06-19 American Bible Society +bible + +// java : 2014-06-19 Oracle Corporation +java + +// sky : 2014-06-19 Sky IP International Ltd, a company incorporated in England and Wales, operating via its registered Swiss branch +sky + +// oracle : 2014-06-19 Oracle Corporation +oracle + +// pharmacy : 2014-06-19 National Association of Boards of Pharmacy +pharmacy + +// dvag : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +dvag + +// xn--vermgensberater-ctb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +xn--vermgensberater-ctb + +// xn--vermgensberatung-pwb : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +xn--vermgensberatung-pwb + +// montblanc : 2014-06-23 Richemont DNS Inc. +montblanc + +// iwc : 2014-06-23 Richemont DNS Inc. +iwc + +// cartier : 2014-06-23 Richemont DNS Inc. +cartier + +// pohl : 2014-06-23 Deutsche Vermögensberatung Aktiengesellschaft DVAG +pohl + +// diet : 2014-06-26 Uniregistry, Corp. +diet + +// cba : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA +cba + +// netbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA +netbank + +// pictet : 2014-06-26 Pictet Europe S.A. +pictet + +// help : 2014-06-26 Uniregistry, Corp. +help + +// pizza : 2014-06-26 Foggy Moon, LLC +pizza + +// garden : 2014-06-26 Top Level Domain Holdings Limited +garden + +// commbank : 2014-06-26 COMMONWEALTH BANK OF AUSTRALIA +commbank + +// gifts : 2014-07-03 Goose Sky, LLC +gifts + +// fashion : 2014-07-03 Top Level Domain Holdings Limited +fashion + +// tui : 2014-07-03 TUI AG +tui + +// iinet : 2014-07-03 Connect West Pty. Ltd. +iinet + +// restaurant : 2014-07-03 Snow Avenue, LLC +restaurant + +// alsace : 2014-07-02 REGION D ALSACE +alsace + +// poker : 2014-07-03 Afilias Domains No. 5 Limited +poker + +// allfinanz : 2014-07-03 Allfinanz Deutsche Vermögensberatung Aktiengesellschaft +allfinanz + +// sarl : 2014-07-03 Delta Orchard, LLC +sarl + +// taipei : 2014-07-10 Taipei City Government +taipei + +// immo : 2014-07-10 Auburn Bloom, LLC +immo + +// hermes : 2014-07-10 HERMES INTERNATIONAL +hermes + +// rip : 2014-07-10 United TLD Holdco Ltd. +rip + +// gbiz : 2014-07-17 Charleston Road Registry Inc. +gbiz + +// bloomberg : 2014-07-17 Bloomberg IP Holdings LLC +bloomberg + +// sew : 2014-07-17 SEW-EURODRIVE GmbH & Co KG +sew + +// prof : 2014-07-24 Charleston Road Registry Inc. +prof + +// gle : 2014-07-24 Charleston Road Registry Inc. +gle + +// amsterdam : 2014-07-24 Gemeente Amsterdam +amsterdam + +// aquarelle : 2014-07-24 Aquarelle.com +aquarelle + +// nexus : 2014-07-24 Charleston Road Registry Inc. +nexus + +// flsmidth : 2014-07-24 FLSmidth A/S +flsmidth + +// bnl : 2014-07-24 Banca Nazionale del Lavoro +bnl + +// bcn : 2014-07-24 Municipi de Barcelona +bcn + +// chrome : 2014-07-24 Charleston Road Registry Inc. +chrome + +// google : 2014-07-24 Charleston Road Registry Inc. +google + +// barcelona : 2014-07-24 Municipi de Barcelona +barcelona + +// cal : 2014-07-24 Charleston Road Registry Inc. +cal + +// abbott : 2014-07-24 Abbott Laboratories, Inc. +abbott + // ===END ICANN DOMAINS=== // ===BEGIN PRIVATE DOMAINS=== diff --git a/net/base/registry_controlled_domains/effective_tld_names.gperf b/net/base/registry_controlled_domains/effective_tld_names.gperf index 6bee195507e33..f131a98733dc6 100644 --- a/net/base/registry_controlled_domains/effective_tld_names.gperf +++ b/net/base/registry_controlled_domains/effective_tld_names.gperf @@ -31,6 +31,7 @@ aa.no, 0 aarborte.no, 0 ab.ca, 0 abashiri.hokkaido.jp, 0 +abbott, 0 abeno.osaka.jp, 0 abiko.chiba.jp, 0 abira.hokkaido.jp, 0 @@ -169,6 +170,8 @@ alaska.museum, 0 alessandria.it, 0 alesund.no, 0 algard.no, 0 +allfinanz, 0 +alsace, 0 alstahaug.no, 0 alta.no, 0 altai.ru, 0 @@ -192,6 +195,7 @@ americanart.museum, 0 ami.ibaraki.jp, 0 amli.no, 0 amot.no, 0 +amsterdam, 0 amsterdam.museum, 0 amur.ru, 0 amursk.ru, 0 @@ -236,6 +240,7 @@ ap.it, 0 appspot.com, 4 aq, 0 aq.it, 0 +aquarelle, 0 aquarium.museum, 0 aquila.it, 0 ar, 0 @@ -410,10 +415,12 @@ balsan.it, 0 balsfjord.no, 0 baltimore.museum, 0 bamble.no, 0 +band, 0 bandai.fukushima.jp, 0 bando.ibaraki.jp, 0 bar, 0 bar.pro, 0 +barcelona, 0 barcelona.museum, 0 bardu.no, 0 bargains, 0 @@ -437,6 +444,7 @@ bauhaus, 0 bayern, 0 bb, 0 bc.ca, 0 +bcn, 0 bd, 2 bd.se, 0 be, 0 @@ -475,6 +483,7 @@ bi.it, 0 bialowieza.pl, 0 bialystok.pl, 0 bibai.hokkaido.jp, 0 +bible, 0 bible.museum, 0 bid, 0 biei.hokkaido.jp, 0 @@ -564,11 +573,13 @@ blogspot.sg, 4 blogspot.sk, 4 blogspot.td, 4 blogspot.tw, 4 +bloomberg, 0 blue, 0 bm, 0 bmd.br, 0 bn, 2 bn.it, 0 +bnl, 0 bnpparibas, 0 bo, 0 bo.it, 0 @@ -662,6 +673,7 @@ cab, 0 cadaques.museum, 0 cagliari.it, 0 cahcesuolo.no, 0 +cal, 0 cal.it, 0 calabria.it, 0 california.museum, 0 @@ -690,6 +702,7 @@ cargo.aero, 0 carrara-massa.it, 0 carraramassa.it, 0 carrier.museum, 0 +cartier, 0 cartoonart.museum, 0 casa, 0 casadelamoneda.museum, 0 @@ -704,6 +717,7 @@ catanzaro.it, 0 catering, 0 catering.aero, 0 cb.it, 0 +cba, 0 cbg.ru, 0 cc, 0 cc.ak.us, 0 @@ -831,6 +845,7 @@ choshi.chiba.jp, 0 choyo.kumamoto.jp, 0 christiansburg.museum, 0 christmas, 0 +chrome, 0 chtr.k12.ma.us, 0 chukotka.ru, 0 chungbuk.kr, 0 @@ -1086,6 +1101,7 @@ com.vi, 0 com.vn, 0 com.vu, 0 com.ws, 0 +commbank, 0 communication.museum, 0 communications.museum, 0 community, 0 @@ -1221,6 +1237,7 @@ detroit.museum, 0 dgca.aero, 0 diamonds, 0 dielddanuorri.no, 0 +diet, 0 digital, 0 dinosaur.museum, 0 direct, 0 @@ -1271,6 +1288,7 @@ drobak.no, 0 dudinka.ru, 0 durban, 0 durham.museum, 0 +dvag, 0 dvrdns.org, 4 dyn-o-saur.com, 4 dynalias.com, 4 @@ -1552,6 +1570,7 @@ farmequipment.museum, 0 farmers.museum, 0 farmstead.museum, 0 farsund.no, 0 +fashion, 0 fauske.no, 0 fc.it, 0 fe.it, 0 @@ -1618,6 +1637,7 @@ florence.it, 0 florida.museum, 0 florist, 0 floro.no, 0 +flsmidth, 0 fly, 0 fm, 0 fm.br, 0 @@ -1816,6 +1836,7 @@ gamo.shiga.jp, 0 gamvik.no, 0 gangaviika.no, 0 gangwon.kr, 0 +garden, 0 garden.museum, 0 gateway.museum, 0 gaular.no, 0 @@ -1823,6 +1844,7 @@ gausdal.no, 0 gb, 0 gb.com, 4 gb.net, 4 +gbiz, 0 gc.ca, 0 gd, 0 gd.cn, 0 @@ -1855,6 +1877,7 @@ gi, 0 giehtavuoatna.no, 0 giessen.museum, 0 gift, 0 +gifts, 0 gifu.gifu.jp, 0 gifu.jp, 0 gildeskal.no, 0 @@ -1874,6 +1897,7 @@ gl, 0 glas.museum, 0 glass, 0 glass.museum, 0 +gle, 0 gliding.aero, 0 gliwice.pl, 0 global, 0 @@ -1922,6 +1946,7 @@ gokase.miyazaki.jp, 0 gol.no, 0 gon.pk, 0 gonohe.aomori.jp, 0 +google, 0 googleapis.com, 4 googlecode.com, 4 gop, 0 @@ -2215,10 +2240,12 @@ he.cn, 0 health.museum, 0 health.nz, 0 health.vn, 0 +healthcare, 0 heguri.nara.jp, 0 heimatunduhren.museum, 0 hekinan.aichi.jp, 0 hellas.museum, 0 +help, 0 helsinki.museum, 0 hembygdsforbund.museum, 0 hemne.no, 0 @@ -2228,6 +2255,7 @@ herad.no, 0 here, 0 here-for-more.info, 4 heritage.museum, 0 +hermes, 0 herokuapp.com, 4 herokussl.com, 4 heroy.more-og-romsdal.no, 0 @@ -2430,6 +2458,7 @@ iheya.okinawa.jp, 0 iida.nagano.jp, 0 iide.yamagata.jp, 0 iijima.nagano.jp, 0 +iinet, 0 iitate.fukushima.jp, 0 iiyama.nagano.jp, 0 iizuka.fukuoka.jp, 0 @@ -2458,6 +2487,7 @@ imakane.hokkaido.jp, 0 imari.saga.jp, 0 imb.br, 0 imizu.toyama.jp, 0 +immo, 0 immobilien, 0 imperia.it, 0 in, 0 @@ -2697,6 +2727,7 @@ iwata.shizuoka.jp, 0 iwate.iwate.jp, 0 iwate.jp, 0 iwatsuki.saitama.jp, 0 +iwc, 0 iwi.nz, 0 iyo.ehime.jp, 0 iz.hr, 0 @@ -2716,6 +2747,7 @@ jamal.ru, 0 jamison.museum, 0 jan-mayen.no, 0 jar.ru, 0 +java, 0 jaworzno.pl, 0 je, 0 jefferson.museum, 0 @@ -3249,6 +3281,7 @@ larsson.museum, 0 larvik.no, 0 laspezia.it, 0 latina.it, 0 +latrobe, 0 lavagis.no, 0 lavangen.no, 0 law.pro, 0 @@ -3772,6 +3805,7 @@ mombetsu.hokkaido.jp, 0 monash, 0 money.museum, 0 monmouth.museum, 0 +montblanc, 0 monticello.museum, 0 montreal.museum, 0 monza-brianza.it, 0 @@ -4132,6 +4166,7 @@ net.vi, 0 net.vn, 0 net.vu, 0 net.ws, 0 +netbank, 0 network, 0 neues.museum, 0 neustar, 0 @@ -4143,6 +4178,7 @@ newport.museum, 0 news.hu, 0 newspaper.museum, 0 newyork.museum, 0 +nexus, 0 neyagawa.osaka.jp, 0 nf, 0 nf.ca, 0 @@ -4446,6 +4482,7 @@ or.tz, 0 or.ug, 0 or.us, 0 ora.gunma.jp, 0 +oracle, 0 oregon.museum, 0 oregontrail.museum, 0 orenburg.ru, 0 @@ -4709,6 +4746,7 @@ pg.it, 0 ph, 0 pharmacien.fr, 0 pharmaciens.km, 0 +pharmacy, 0 pharmacy.museum, 0 philadelphia.museum, 0 philadelphiaarea.museum, 0 @@ -4722,6 +4760,7 @@ physio, 0 pi.it, 0 piacenza.it, 0 pics, 0 +pictet, 0 pictures, 0 piedmont.it, 0 piemonte.it, 0 @@ -4734,6 +4773,7 @@ pisa.it, 0 pistoia.it, 0 pisz.pl, 0 pittsburgh.museum, 0 +pizza, 0 pk, 0 pl, 0 pl.ua, 0 @@ -4757,6 +4797,8 @@ podhale.pl, 0 podlasie.pl, 0 podzone.net, 4 podzone.org, 4 +pohl, 0 +poker, 0 pol.dz, 0 pol.ht, 0 police.uk, 0 @@ -4823,6 +4865,7 @@ prochowice.pl, 0 prod, 0 production.aero, 0 productions, 0 +prof, 0 prof.pr, 0 project.museum, 0 properties, 0 @@ -4928,6 +4971,7 @@ research.aero, 0 research.museum, 0 resistance.museum, 0 rest, 0 +restaurant, 0 reviews, 0 rg.it, 0 rhcloud.com, 4 @@ -4946,6 +4990,7 @@ ringerike.no, 0 ringsaker.no, 0 rio, 0 riodejaneiro.museum, 0 +rip, 0 rishiri.hokkaido.jp, 0 rishirifuji.hokkaido.jp, 0 risor.no, 0 @@ -5107,6 +5152,7 @@ sar.it, 0 saratov.ru, 0 sardegna.it, 0 sardinia.it, 0 +sarl, 0 saroma.hokkaido.jp, 0 sarpsborg.no, 0 sarufutsu.hokkaido.jp, 0 @@ -5226,6 +5272,7 @@ settlement.museum, 0 settlers.museum, 0 settsu.osaka.jp, 0 sevastopol.ua, 0 +sew, 0 sex.hu, 0 sex.pl, 0 sexy, 0 @@ -5373,6 +5420,7 @@ sklep.pl, 0 skoczow.pl, 0 skodje.no, 0 skole.museum, 0 +sky, 0 skydiving.aero, 0 sl, 0 slask.pl, 0 @@ -5577,6 +5625,7 @@ taiji.wakayama.jp, 0 taiki.hokkaido.jp, 0 taiki.mie.jp, 0 tainai.niigata.jp, 0 +taipei, 0 taira.toyama.jp, 0 taishi.hyogo.jp, 0 taishi.osaka.jp, 0 @@ -5915,6 +5964,7 @@ tsuwano.shimane.jp, 0 tsuyama.okayama.jp, 0 tt, 0 tt.im, 0 +tui, 0 tula.ru, 0 tur.ar, 0 tur.br, 0 @@ -6257,6 +6307,7 @@ workinggroup.aero, 0 works, 0 works.aero, 0 workshop.museum, 0 +world, 0 worse-than.tv, 4 writesthisblog.com, 4 wroc.pl, 0 @@ -6273,6 +6324,7 @@ x.bg, 0 x.se, 0 xj.cn, 0 xn--1qqw23a, 0 +xn--30rr7y, 0 xn--3bst00m, 0 xn--3ds443g, 0 xn--3e0b707e, 0 @@ -6294,6 +6346,7 @@ xn--80au.xn--90a3ac, 0 xn--90a3ac, 0 xn--90azh.xn--90a3ac, 0 xn--9dbhblg6di.museum, 0 +xn--9et52u, 0 xn--andy-ira.no, 0 xn--aroport-bya.ci, 0 xn--asky-ira.no, 0 @@ -6539,6 +6592,8 @@ xn--unup4y, 0 xn--vads-jra.no, 0 xn--vard-jra.no, 0 xn--vegrshei-c0a.no, 0 +xn--vermgensberater-ctb, 0 +xn--vermgensberatung-pwb, 0 xn--vestvgy-ixa6o.no, 0 xn--vg-yiab.no, 0 xn--vgan-qoa.no, 0 diff --git a/net/base/sdch_manager.cc b/net/base/sdch_manager.cc index 8b7481b50b582..19b1d76fdadb5 100644 --- a/net/base/sdch_manager.cc +++ b/net/base/sdch_manager.cc @@ -22,7 +22,7 @@ namespace net { #if defined(OS_ANDROID) || defined(OS_IOS) // static const size_t SdchManager::kMaxDictionaryCount = 1; -const size_t SdchManager::kMaxDictionarySize = 150 * 1000; +const size_t SdchManager::kMaxDictionarySize = 500 * 1000; #else // static const size_t SdchManager::kMaxDictionaryCount = 20; @@ -234,7 +234,6 @@ SdchManager::~SdchManager() { void SdchManager::ClearData() { blacklisted_domains_.clear(); - exponential_blacklist_count_.clear(); allow_latency_experiment_.clear(); if (fetcher_.get()) fetcher_->Cancel(); @@ -266,51 +265,63 @@ void SdchManager::EnableSecureSchemeSupport(bool enabled) { g_secure_scheme_supported_ = enabled; } -void SdchManager::BlacklistDomain(const GURL& url) { +void SdchManager::BlacklistDomain(const GURL& url, + ProblemCodes blacklist_reason) { SetAllowLatencyExperiment(url, false); - std::string domain(base::StringToLowerASCII(url.host())); - int count = blacklisted_domains_[domain]; - if (count > 0) + BlacklistInfo* blacklist_info = + &blacklisted_domains_[base::StringToLowerASCII(url.host())]; + + if (blacklist_info->count > 0) return; // Domain is already blacklisted. - count = 1 + 2 * exponential_blacklist_count_[domain]; - if (count > 0) - exponential_blacklist_count_[domain] = count; - else - count = INT_MAX; + if (blacklist_info->exponential_count > (INT_MAX - 1) / 2) { + blacklist_info->exponential_count = INT_MAX; + } else { + blacklist_info->exponential_count = + blacklist_info->exponential_count * 2 + 1; + } - blacklisted_domains_[domain] = count; + blacklist_info->count = blacklist_info->exponential_count; + blacklist_info->reason = blacklist_reason; } -void SdchManager::BlacklistDomainForever(const GURL& url) { +void SdchManager::BlacklistDomainForever(const GURL& url, + ProblemCodes blacklist_reason) { SetAllowLatencyExperiment(url, false); - std::string domain(base::StringToLowerASCII(url.host())); - exponential_blacklist_count_[domain] = INT_MAX; - blacklisted_domains_[domain] = INT_MAX; + BlacklistInfo* blacklist_info = + &blacklisted_domains_[base::StringToLowerASCII(url.host())]; + blacklist_info->count = INT_MAX; + blacklist_info->exponential_count = INT_MAX; + blacklist_info->reason = blacklist_reason; } void SdchManager::ClearBlacklistings() { blacklisted_domains_.clear(); - exponential_blacklist_count_.clear(); } void SdchManager::ClearDomainBlacklisting(const std::string& domain) { - blacklisted_domains_.erase(base::StringToLowerASCII(domain)); + BlacklistInfo* blacklist_info = &blacklisted_domains_[ + base::StringToLowerASCII(domain)]; + blacklist_info->count = 0; + blacklist_info->reason = MIN_PROBLEM_CODE; } int SdchManager::BlackListDomainCount(const std::string& domain) { - if (blacklisted_domains_.end() == blacklisted_domains_.find(domain)) + std::string domain_lower(base::StringToLowerASCII(domain)); + + if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) return 0; - return blacklisted_domains_[base::StringToLowerASCII(domain)]; + return blacklisted_domains_[domain_lower].count; } int SdchManager::BlacklistDomainExponential(const std::string& domain) { - if (exponential_blacklist_count_.end() == - exponential_blacklist_count_.find(domain)) + std::string domain_lower(base::StringToLowerASCII(domain)); + + if (blacklisted_domains_.end() == blacklisted_domains_.find(domain_lower)) return 0; - return exponential_blacklist_count_[base::StringToLowerASCII(domain)]; + return blacklisted_domains_[domain_lower].exponential_count; } bool SdchManager::IsInSupportedDomain(const GURL& url) { @@ -324,17 +335,23 @@ bool SdchManager::IsInSupportedDomain(const GURL& url) { if (blacklisted_domains_.empty()) return true; - std::string domain(base::StringToLowerASCII(url.host())); - DomainCounter::iterator it = blacklisted_domains_.find(domain); - if (blacklisted_domains_.end() == it) + DomainBlacklistInfo::iterator it = + blacklisted_domains_.find(base::StringToLowerASCII(url.host())); + if (blacklisted_domains_.end() == it || it->second.count == 0) return true; - int count = it->second - 1; - if (count > 0) - blacklisted_domains_[domain] = count; - else - blacklisted_domains_.erase(domain); + UMA_HISTOGRAM_ENUMERATION("Sdch3.BlacklistReason", it->second.reason, + MAX_PROBLEM_CODE); SdchErrorRecovery(DOMAIN_BLACKLIST_INCLUDES_TARGET); + + int count = it->second.count - 1; + if (count > 0) { + it->second.count = count; + } else { + it->second.count = 0; + it->second.reason = MIN_PROBLEM_CODE; + } + return false; } diff --git a/net/base/sdch_manager.h b/net/base/sdch_manager.h index 6f2ea5af6de0a..67ec167af9441 100644 --- a/net/base/sdch_manager.h +++ b/net/base/sdch_manager.h @@ -265,11 +265,11 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) { // Used when filter errors are found from a given domain, but it is plausible // that the cause is temporary (such as application startup, where cached // entries are used, but a dictionary is not yet loaded). - void BlacklistDomain(const GURL& url); + void BlacklistDomain(const GURL& url, ProblemCodes blacklist_reason); // Used when SEVERE filter errors are found from a given domain, to prevent // further use of SDCH on that domain. - void BlacklistDomainForever(const GURL& url); + void BlacklistDomainForever(const GURL& url, ProblemCodes blacklist_reason); // Unit test only, this function resets enabling of sdch, and clears the // blacklist. @@ -337,7 +337,18 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) { void SetAllowLatencyExperiment(const GURL& url, bool enable); private: - typedef std::map DomainCounter; + struct BlacklistInfo { + BlacklistInfo() + : count(0), + exponential_count(0), + reason(MIN_PROBLEM_CODE) {} + + int count; // # of times to refuse SDCH advertisement. + int exponential_count; // Current exponential backoff ratchet. + ProblemCodes reason; // Why domain was blacklisted. + + }; + typedef std::map DomainBlacklistInfo; typedef std::set ExperimentSet; // A map of dictionaries info indexed by the hash that the server provides. @@ -358,13 +369,8 @@ class NET_EXPORT SdchManager : public NON_EXPORTED_BASE(base::NonThreadSafe) { // An instance that can fetch a dictionary given a URL. scoped_ptr fetcher_; - // List domains where decode failures have required disabling sdch, along with - // count of how many additonal uses should be blacklisted. - DomainCounter blacklisted_domains_; - - // Support exponential backoff in number of domain accesses before - // blacklisting expires. - DomainCounter exponential_blacklist_count_; + // List domains where decode failures have required disabling sdch. + DomainBlacklistInfo blacklisted_domains_; // List of hostnames for which a latency experiment is allowed (because a // round trip test has recently passed). diff --git a/net/base/sdch_manager_unittest.cc b/net/base/sdch_manager_unittest.cc index 26893bd1b94f2..a46bf808f4eb8 100644 --- a/net/base/sdch_manager_unittest.cc +++ b/net/base/sdch_manager_unittest.cc @@ -65,11 +65,11 @@ TEST_F(SdchManagerTest, DomainBlacklisting) { GURL test_url("http://www.test.com"); GURL google_url("http://www.google.com"); - sdch_manager()->BlacklistDomain(test_url); + sdch_manager()->BlacklistDomain(test_url, SdchManager::MIN_PROBLEM_CODE); EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(test_url)); EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(google_url)); - sdch_manager()->BlacklistDomain(google_url); + sdch_manager()->BlacklistDomain(google_url, SdchManager::MIN_PROBLEM_CODE); EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(google_url)); } @@ -79,7 +79,7 @@ TEST_F(SdchManagerTest, DomainBlacklistingCaseSensitivity) { EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(test_url)); EXPECT_TRUE(sdch_manager()->IsInSupportedDomain(test2_url)); - sdch_manager()->BlacklistDomain(test_url); + sdch_manager()->BlacklistDomain(test_url, SdchManager::MIN_PROBLEM_CODE); EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(test2_url)); } @@ -98,7 +98,7 @@ TEST_F(SdchManagerTest, BlacklistingSingleBlacklist) { std::string domain(gurl.host()); sdch_manager()->ClearBlacklistings(); - sdch_manager()->BlacklistDomain(gurl); + sdch_manager()->BlacklistDomain(gurl, SdchManager::MIN_PROBLEM_CODE); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), 1); EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), 1); @@ -115,7 +115,7 @@ TEST_F(SdchManagerTest, BlacklistingExponential) { int exponential = 1; for (int i = 1; i < 100; ++i) { - sdch_manager()->BlacklistDomain(gurl); + sdch_manager()->BlacklistDomain(gurl, SdchManager::MIN_PROBLEM_CODE); EXPECT_EQ(sdch_manager()->BlacklistDomainExponential(domain), exponential); EXPECT_EQ(sdch_manager()->BlackListDomainCount(domain), exponential); @@ -483,7 +483,8 @@ TEST_F(SdchManagerTest, ClearDictionaryData) { &dictionary); EXPECT_TRUE(dictionary); - sdch_manager()->BlacklistDomain(GURL(blacklist_url)); + sdch_manager()->BlacklistDomain(GURL(blacklist_url), + SdchManager::MIN_PROBLEM_CODE); EXPECT_FALSE(sdch_manager()->IsInSupportedDomain(blacklist_url)); sdch_manager()->ClearData(); diff --git a/net/cert/ev_root_ca_metadata.cc b/net/cert/ev_root_ca_metadata.cc index 04f4bda3c9645..74d88a8f50302 100644 --- a/net/cert/ev_root_ca_metadata.cc +++ b/net/cert/ev_root_ca_metadata.cc @@ -143,6 +143,12 @@ static const EVMetadata ev_root_ca_metadata[] = { 0x2c, 0x50, 0xb6, 0x56, 0x3b, 0x8e, 0x2d, 0x93, 0xc3, 0x11 } }, {"1.3.6.1.4.1.6449.1.2.1.5.1", ""}, }, + // COMODO RSA Certification Authority + // https://comodorsacertificationauthority-ev.comodoca.com/ + { { { 0xaf, 0xe5, 0xd2, 0x44, 0xa8, 0xd1, 0x19, 0x42, 0x30, 0xff, + 0x47, 0x9f, 0xe2, 0xf8, 0x97, 0xbb, 0xcd, 0x7a, 0x8c, 0xb4 } }, + {"1.3.6.1.4.1.6449.1.2.1.5.1", ""}, + }, // Cybertrust Global Root // https://evup.cybertrust.ne.jp/ctj-ev-upgrader/evseal.gif { { { 0x5f, 0x43, 0xe5, 0xb1, 0xbf, 0xf8, 0x78, 0x8c, 0xac, 0x1c, @@ -365,6 +371,18 @@ static const EVMetadata ev_root_ca_metadata[] = { 0x74, 0x70, 0x19, 0x9d, 0x2a, 0xbe, 0x11, 0xe3, 0x81, 0xd1 } }, {"1.3.6.1.4.1.7879.13.24.1", "" }, }, + // USERTrust ECC Certification Authority + // https://usertrustecccertificationauthority-ev.comodoca.com/ + { { { 0xd1, 0xcb, 0xca, 0x5d, 0xb2, 0xd5, 0x2a, 0x7f, 0x69, 0x3b, + 0x67, 0x4d, 0xe5, 0xf0, 0x5a, 0x1d, 0x0c, 0x95, 0x7d, 0xf0 } }, + {"1.3.6.1.4.1.6449.1.2.1.5.1", ""}, + }, + // USERTrust RSA Certification Authority + // https://usertrustrsacertificationauthority-ev.comodoca.com/ + { { { 0x2b, 0x8f, 0x1b, 0x57, 0x33, 0x0d, 0xbb, 0xa2, 0xd0, 0x7a, + 0x6c, 0x51, 0xf7, 0x0e, 0xe9, 0x0d, 0xda, 0xb9, 0xad, 0x8e } }, + {"1.3.6.1.4.1.6449.1.2.1.5.1", ""}, + }, // UTN - DATACorp SGC { { { 0x58, 0x11, 0x9f, 0x0e, 0x12, 0x82, 0x87, 0xea, 0x50, 0xfd, 0xd9, 0x87, 0x45, 0x6f, 0x4f, 0x78, 0xdc, 0xfa, 0xd6, 0xd4 } }, diff --git a/net/cert/multi_threaded_cert_verifier.h b/net/cert/multi_threaded_cert_verifier.h index 8f00ebe301f93..cdd3235d69d1f 100644 --- a/net/cert/multi_threaded_cert_verifier.h +++ b/net/cert/multi_threaded_cert_verifier.h @@ -96,7 +96,7 @@ class NET_EXPORT_PRIVATE MultiThreadedCertVerifier }; // CachedResult contains the result of a certificate verification. - struct CachedResult { + struct NET_EXPORT_PRIVATE CachedResult { CachedResult(); ~CachedResult(); diff --git a/net/cert/x509_certificate_openssl.cc b/net/cert/x509_certificate_openssl.cc index 504f3aea5f33f..fdd0e48d8551e 100644 --- a/net/cert/x509_certificate_openssl.cc +++ b/net/cert/x509_certificate_openssl.cc @@ -241,13 +241,7 @@ void sk_X509_NAME_free_all(STACK_OF(X509_NAME)* sk) { X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( OSCertHandle cert_handle) { DCHECK(cert_handle); - // Using X509_dup causes the entire certificate to be reparsed. This - // conversion, besides being non-trivial, drops any associated - // application-specific data set by X509_set_ex_data. Using CRYPTO_add - // just bumps up the ref-count for the cert, without causing any allocations - // or deallocations. - CRYPTO_add(&cert_handle->references, 1, CRYPTO_LOCK_X509); - return cert_handle; + return X509_up_ref(cert_handle); } // static diff --git a/net/disk_cache/entry_unittest.cc b/net/disk_cache/entry_unittest.cc index 359f7250845df..a94f6fbe6b027 100644 --- a/net/disk_cache/entry_unittest.cc +++ b/net/disk_cache/entry_unittest.cc @@ -3179,6 +3179,38 @@ TEST_F(DiskCacheEntryTest, SimpleCacheDoomCreateDoom) { // This test passes if it doesn't crash. } +TEST_F(DiskCacheEntryTest, SimpleCacheDoomCloseCreateCloseOpen) { + // Test sequence: Create, Doom, Close, Create, Close, Open. + SetSimpleCacheMode(); + InitCache(); + + disk_cache::Entry* null = NULL; + + const char key[] = "this is a key"; + + disk_cache::Entry* entry1 = NULL; + ASSERT_EQ(net::OK, CreateEntry(key, &entry1)); + ScopedEntryPtr entry1_closer(entry1); + EXPECT_NE(null, entry1); + + entry1->Doom(); + entry1_closer.reset(); + entry1 = NULL; + + disk_cache::Entry* entry2 = NULL; + ASSERT_EQ(net::OK, CreateEntry(key, &entry2)); + ScopedEntryPtr entry2_closer(entry2); + EXPECT_NE(null, entry2); + + entry2_closer.reset(); + entry2 = NULL; + + disk_cache::Entry* entry3 = NULL; + ASSERT_EQ(net::OK, OpenEntry(key, &entry3)); + ScopedEntryPtr entry3_closer(entry3); + EXPECT_NE(null, entry3); +} + // Checks that an optimistic Create would fail later on a racing Open. TEST_F(DiskCacheEntryTest, SimpleCacheOptimisticCreateFailsOnOpen) { SetSimpleCacheMode(); diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc index 5e8c3f2ffbfe3..a762620eab15b 100644 --- a/net/disk_cache/simple/simple_backend_impl.cc +++ b/net/disk_cache/simple/simple_backend_impl.cc @@ -197,6 +197,34 @@ void RecordIndexLoad(net::CacheType cache_type, } // namespace +class SimpleBackendImpl::ActiveEntryProxy + : public SimpleEntryImpl::ActiveEntryProxy { + public: + virtual ~ActiveEntryProxy() { + if (backend_) { + DCHECK_EQ(1U, backend_->active_entries_.count(entry_hash_)); + backend_->active_entries_.erase(entry_hash_); + } + } + + static scoped_ptr Create( + int64 entry_hash, + SimpleBackendImpl* backend) { + scoped_ptr + proxy(new ActiveEntryProxy(entry_hash, backend)); + return proxy.Pass(); + } + + private: + ActiveEntryProxy(uint64 entry_hash, + SimpleBackendImpl* backend) + : entry_hash_(entry_hash), + backend_(backend->AsWeakPtr()) {} + + uint64 entry_hash_; + base::WeakPtr backend_; +}; + SimpleBackendImpl::SimpleBackendImpl(const FilePath& path, int max_bytes, net::CacheType cache_type, @@ -250,10 +278,6 @@ int SimpleBackendImpl::GetMaxFileSize() const { return index_->max_size() / kMaxFileRatio; } -void SimpleBackendImpl::OnDeactivated(const SimpleEntryImpl* entry) { - active_entries_.erase(entry->entry_hash()); -} - void SimpleBackendImpl::OnDoomStart(uint64 entry_hash) { // TODO(ttuttle): Revert to DCHECK once http://crbug.com/317138 is fixed. CHECK_EQ(0u, entries_pending_doom_.count(entry_hash)); @@ -511,18 +535,17 @@ scoped_refptr SimpleBackendImpl::CreateOrFindActiveEntry( const std::string& key) { DCHECK_EQ(entry_hash, simple_util::GetEntryHashKey(key)); std::pair insert_result = - active_entries_.insert(std::make_pair(entry_hash, - base::WeakPtr())); + active_entries_.insert(EntryMap::value_type(entry_hash, NULL)); EntryMap::iterator& it = insert_result.first; - if (insert_result.second) - DCHECK(!it->second.get()); - if (!it->second.get()) { - SimpleEntryImpl* entry = new SimpleEntryImpl( - cache_type_, path_, entry_hash, entry_operations_mode_, this, net_log_); + const bool did_insert = insert_result.second; + if (did_insert) { + SimpleEntryImpl* entry = it->second = + new SimpleEntryImpl(cache_type_, path_, entry_hash, + entry_operations_mode_,this, net_log_); entry->SetKey(key); - it->second = entry->AsWeakPtr(); + entry->SetActiveEntryProxy(ActiveEntryProxy::Create(entry_hash, this)); } - DCHECK(it->second.get()); + DCHECK(it->second); // It's possible, but unlikely, that we have an entry hash collision with a // currently active entry. if (key != it->second->key()) { @@ -530,7 +553,7 @@ scoped_refptr SimpleBackendImpl::CreateOrFindActiveEntry( DCHECK_EQ(0U, active_entries_.count(entry_hash)); return CreateOrFindActiveEntry(entry_hash, key); } - return make_scoped_refptr(it->second.get()); + return make_scoped_refptr(it->second); } int SimpleBackendImpl::OpenEntryFromHash(uint64 entry_hash, @@ -639,19 +662,19 @@ void SimpleBackendImpl::OnEntryOpenedFromHash( } DCHECK(*entry); std::pair insert_result = - active_entries_.insert(std::make_pair(hash, - base::WeakPtr())); + active_entries_.insert(EntryMap::value_type(hash, simple_entry)); EntryMap::iterator& it = insert_result.first; const bool did_insert = insert_result.second; if (did_insert) { - // There is no active entry corresponding to this hash. The entry created - // is put in the map of active entries and returned to the caller. - it->second = simple_entry->AsWeakPtr(); - callback.Run(error_code); + // There was no active entry corresponding to this hash. We've already put + // the entry opened from hash in the |active_entries_|. We now provide the + // proxy object to the entry. + it->second->SetActiveEntryProxy(ActiveEntryProxy::Create(hash, this)); + callback.Run(net::OK); } else { - // The entry was made active with the key while the creation from hash - // occurred. The entry created from hash needs to be closed, and the one - // coming from the key returned to the caller. + // The entry was made active while we waiting for the open from hash to + // finish. The entry created from hash needs to be closed, and the one + // in |active_entries_| can be returned to the caller. simple_entry->Close(); it->second->OpenEntry(entry, callback); } diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h index eb14de8841a30..996cfd2da8002 100644 --- a/net/disk_cache/simple/simple_backend_impl.h +++ b/net/disk_cache/simple/simple_backend_impl.h @@ -64,10 +64,6 @@ class NET_EXPORT_PRIVATE SimpleBackendImpl : public Backend, // Returns the maximum file size permitted in this backend. int GetMaxFileSize() const; - // Removes |entry| from the |active_entries_| set, forcing future Open/Create - // operations to construct a new object. - void OnDeactivated(const SimpleEntryImpl* entry); - // Flush our SequencedWorkerPool. static void FlushWorkerPoolForTesting(); @@ -107,11 +103,14 @@ class NET_EXPORT_PRIVATE SimpleBackendImpl : public Backend, virtual void OnExternalCacheHit(const std::string& key) OVERRIDE; private: - typedef base::hash_map > EntryMap; + typedef base::hash_map EntryMap; typedef base::Callback InitializeIndexCallback; + class ActiveEntryProxy; + friend class ActiveEntryProxy; + // Return value of InitCacheStructureOnDisk(). struct DiskStatResult { base::Time cache_dir_mtime; diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc index cc2a1ea032da1..b0ff38328742e 100644 --- a/net/disk_cache/simple/simple_entry_impl.cc +++ b/net/disk_cache/simple/simple_entry_impl.cc @@ -160,6 +160,8 @@ class SimpleEntryImpl::ScopedOperationRunner { SimpleEntryImpl* const entry_; }; +SimpleEntryImpl::ActiveEntryProxy::~ActiveEntryProxy() {} + SimpleEntryImpl::SimpleEntryImpl(net::CacheType cache_type, const FilePath& path, const uint64 entry_hash, @@ -195,6 +197,12 @@ SimpleEntryImpl::SimpleEntryImpl(net::CacheType cache_type, CreateNetLogSimpleEntryConstructionCallback(this)); } +void SimpleEntryImpl::SetActiveEntryProxy( + scoped_ptr active_entry_proxy) { + DCHECK(!active_entry_proxy_); + active_entry_proxy_.reset(active_entry_proxy.release()); +} + int SimpleEntryImpl::OpenEntry(Entry** out_entry, const CompletionCallback& callback) { DCHECK(backend_.get()); @@ -525,7 +533,6 @@ SimpleEntryImpl::~SimpleEntryImpl() { DCHECK_EQ(0U, pending_operations_.size()); DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_FAILURE); DCHECK(!synchronous_entry_); - RemoveSelfFromBackend(); net_log_.EndEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY); } @@ -567,18 +574,12 @@ void SimpleEntryImpl::ReturnEntryToCaller(Entry** out_entry) { *out_entry = this; } -void SimpleEntryImpl::RemoveSelfFromBackend() { - if (!backend_.get()) - return; - backend_->OnDeactivated(this); -} - void SimpleEntryImpl::MarkAsDoomed() { doomed_ = true; if (!backend_.get()) return; backend_->index()->Remove(entry_hash_); - RemoveSelfFromBackend(); + active_entry_proxy_.reset(); } void SimpleEntryImpl::RunNextOperationIfNeeded() { @@ -1297,6 +1298,7 @@ void SimpleEntryImpl::DoomOperationComplete( State state_to_restore, int result) { state_ = state_to_restore; + net_log_.AddEvent(net::NetLog::TYPE_SIMPLE_CACHE_ENTRY_DOOM_END); if (!callback.is_null()) callback.Run(result); RunNextOperationIfNeeded(); diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h index 2d78d8bfe0a04..2dfb757e19a52 100644 --- a/net/disk_cache/simple/simple_entry_impl.h +++ b/net/disk_cache/simple/simple_entry_impl.h @@ -11,7 +11,6 @@ #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h" #include "net/base/cache_type.h" #include "net/base/net_export.h" @@ -40,8 +39,7 @@ struct SimpleEntryCreationResults; // disk cache. It proxies for the SimpleSynchronousEntry, which performs IO // on the worker thread. class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, - public base::RefCounted, - public base::SupportsWeakPtr { + public base::RefCounted { friend class base::RefCounted; public: enum OperationsMode { @@ -49,6 +47,14 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, OPTIMISTIC_OPERATIONS, }; + // The Backend provides an |ActiveEntryProxy| instance to this entry when it + // is active, meaning it's the canonical entry for this |entry_hash_|. The + // entry can make itself inactive by deleting its proxy. + class ActiveEntryProxy { + public: + virtual ~ActiveEntryProxy() = 0; + }; + SimpleEntryImpl(net::CacheType cache_type, const base::FilePath& path, uint64 entry_hash, @@ -56,6 +62,9 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, SimpleBackendImpl* backend, net::NetLog* net_log); + void SetActiveEntryProxy( + scoped_ptr active_entry_proxy); + // Adds another reader/writer to this entry, if possible, returning |this| to // |entry|. int OpenEntry(Entry** entry, const CompletionCallback& callback); @@ -150,10 +159,6 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, // count. void ReturnEntryToCaller(Entry** out_entry); - // Ensures that |this| is no longer referenced by our |backend_|, this - // guarantees that this entry cannot have OpenEntry/CreateEntry called again. - void RemoveSelfFromBackend(); - // An error occured, and the SimpleSynchronousEntry should have Doomed // us at this point. We need to remove |this| from the Backend and the // index. @@ -297,6 +302,8 @@ class NET_EXPORT_PRIVATE SimpleEntryImpl : public Entry, int length, int stream_index); + scoped_ptr active_entry_proxy_; + // All nonstatic SimpleEntryImpl methods should always be called on the IO // thread, in all cases. |io_thread_checker_| documents and enforces this. base::ThreadChecker io_thread_checker_; diff --git a/net/filter/sdch_filter.cc b/net/filter/sdch_filter.cc index 2ef5ad51d7c4d..57492ea1316e0 100644 --- a/net/filter/sdch_filter.cc +++ b/net/filter/sdch_filter.cc @@ -55,7 +55,7 @@ SdchFilter::~SdchFilter() { // Note this will "wear off" quickly enough, and is just meant to assure // in some rare case that the user is not stuck. url_request_context_->sdch_manager()->BlacklistDomain( - url_); + url_, SdchManager::INCOMPLETE_SDCH_CONTENT); UMA_HISTOGRAM_COUNTS("Sdch3.PartialBytesIn", static_cast(filter_context_.GetByteReadCount())); UMA_HISTOGRAM_COUNTS("Sdch3.PartialVcdiffIn", source_bytes_); @@ -218,7 +218,8 @@ Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer, SdchManager::SdchErrorRecovery(SdchManager::PASSING_THROUGH_NON_SDCH); decoding_status_ = PASS_THROUGH; // ... but further back-off on advertising SDCH support. - url_request_context_->sdch_manager()->BlacklistDomain(url_); + url_request_context_->sdch_manager()->BlacklistDomain( + url_, SdchManager::PASSING_THROUGH_NON_SDCH); } if (decoding_status_ == PASS_THROUGH) { @@ -228,13 +229,13 @@ Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer, if (std::string::npos == mime_type_.find("text/html")) { // Since we can't do a meta-refresh (along with an exponential // backoff), we'll just make sure this NEVER happens again. - url_request_context_->sdch_manager()->BlacklistDomainForever(url_); - if (filter_context_.IsCachedContent()) - SdchManager::SdchErrorRecovery( - SdchManager::CACHED_META_REFRESH_UNSUPPORTED); - else - SdchManager::SdchErrorRecovery( - SdchManager::META_REFRESH_UNSUPPORTED); + SdchManager::ProblemCodes problem = + (filter_context_.IsCachedContent() ? + SdchManager::CACHED_META_REFRESH_UNSUPPORTED : + SdchManager::META_REFRESH_UNSUPPORTED); + url_request_context_->sdch_manager()->BlacklistDomainForever( + url_, problem); + SdchManager::SdchErrorRecovery(problem); return FILTER_ERROR; } // HTML content means we can issue a meta-refresh, and get the content @@ -247,7 +248,8 @@ Filter::FilterStatus SdchFilter::ReadFilteredData(char* dest_buffer, } else { // Since it wasn't in the cache, we definately need at least some // period of blacklisting to get the correct content. - url_request_context_->sdch_manager()->BlacklistDomain(url_); + url_request_context_->sdch_manager()->BlacklistDomain( + url_, SdchManager::META_REFRESH_RECOVERY); SdchManager::SdchErrorRecovery(SdchManager::META_REFRESH_RECOVERY); } decoding_status_ = META_REFRESH_RECOVERY; diff --git a/net/http/disk_based_cert_cache.cc b/net/http/disk_based_cert_cache.cc index fc98b064c0213..d7d3d2f723758 100644 --- a/net/http/disk_based_cert_cache.cc +++ b/net/http/disk_based_cert_cache.cc @@ -81,10 +81,10 @@ class DiskBasedCertCache::WriteWorker { private: enum State { - STATE_CREATE, - STATE_CREATE_COMPLETE, STATE_OPEN, STATE_OPEN_COMPLETE, + STATE_CREATE, + STATE_CREATE_COMPLETE, STATE_WRITE, STATE_WRITE_COMPLETE, STATE_NONE @@ -93,10 +93,10 @@ class DiskBasedCertCache::WriteWorker { void OnIOComplete(int rv); int DoLoop(int rv); - int DoCreate(); - int DoCreateComplete(int rv); int DoOpen(); int DoOpenComplete(int rv); + int DoCreate(); + int DoCreateComplete(int rv); int DoWrite(); int DoWriteComplete(int rv); @@ -146,7 +146,8 @@ DiskBasedCertCache::WriteWorker::~WriteWorker() { void DiskBasedCertCache::WriteWorker::Start() { DCHECK_EQ(STATE_NONE, next_state_); - next_state_ = STATE_CREATE; + + next_state_ = STATE_OPEN; int rv = DoLoop(OK); if (rv == ERR_IO_PENDING) @@ -183,18 +184,18 @@ int DiskBasedCertCache::WriteWorker::DoLoop(int rv) { State state = next_state_; next_state_ = STATE_NONE; switch (state) { - case STATE_CREATE: - rv = DoCreate(); - break; - case STATE_CREATE_COMPLETE: - rv = DoCreateComplete(rv); - break; case STATE_OPEN: rv = DoOpen(); break; case STATE_OPEN_COMPLETE: rv = DoOpenComplete(rv); break; + case STATE_CREATE: + rv = DoCreate(); + break; + case STATE_CREATE_COMPLETE: + rv = DoCreateComplete(rv); + break; case STATE_WRITE: rv = DoWrite(); break; @@ -210,18 +211,15 @@ int DiskBasedCertCache::WriteWorker::DoLoop(int rv) { return rv; } -int DiskBasedCertCache::WriteWorker::DoCreate() { - next_state_ = STATE_CREATE_COMPLETE; - - return backend_->CreateEntry(key_, &entry_, io_callback_); +int DiskBasedCertCache::WriteWorker::DoOpen() { + next_state_ = STATE_OPEN_COMPLETE; + return backend_->OpenEntry(key_, &entry_, io_callback_); } -int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) { - // An error here usually signifies that the entry already exists. - // If this occurs, it is necessary to instead open the previously - // existing entry. +int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) { + // The entry doesn't exist yet, so we should create it. if (rv < 0) { - next_state_ = STATE_OPEN; + next_state_ = STATE_CREATE; return OK; } @@ -229,12 +227,12 @@ int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) { return OK; } -int DiskBasedCertCache::WriteWorker::DoOpen() { - next_state_ = STATE_OPEN_COMPLETE; - return backend_->OpenEntry(key_, &entry_, io_callback_); +int DiskBasedCertCache::WriteWorker::DoCreate() { + next_state_ = STATE_CREATE_COMPLETE; + return backend_->CreateEntry(key_, &entry_, io_callback_); } -int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) { +int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) { if (rv < 0) return rv; diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc index d1c26285529da..286a526a3edb2 100644 --- a/net/http/http_cache_transaction.cc +++ b/net/http/http_cache_transaction.cc @@ -52,7 +52,7 @@ using base::TimeTicks; namespace { // TODO(ricea): Move this to HttpResponseHeaders once it is standardised. -static const char kFreshnessHeader[] = "Chromium-Resource-Freshness"; +static const char kFreshnessHeader[] = "Resource-Freshness"; // Stores data relevant to the statistics of writing and reading entire // certificate chains using DiskBasedCertCache. |num_pending_ops| is the number diff --git a/net/http/http_cache_unittest.cc b/net/http/http_cache_unittest.cc index bbd604416de26..4987c37b025a6 100644 --- a/net/http/http_cache_unittest.cc +++ b/net/http/http_cache_unittest.cc @@ -6730,14 +6730,12 @@ static void CheckResourceFreshnessHeader(const net::HttpRequestInfo* request, std::string* response_headers, std::string* response_data) { std::string value; - EXPECT_TRUE( - request->extra_headers.GetHeader("Chromium-Resource-Freshness", &value)); + EXPECT_TRUE(request->extra_headers.GetHeader("Resource-Freshness", &value)); EXPECT_EQ("max-age=3600,stale-while-revalidate=7200,age=10801", value); } -// Verify that the Chromium-Resource-Freshness header is sent on a revalidation -// if the stale-while-revalidate directive was on the response. -// TODO(ricea): Rename this test when a final name for the header is decided. +// Verify that the Resource-Freshness header is sent on a revalidation if the +// stale-while-revalidate directive was on the response. TEST(HttpCache, ResourceFreshnessHeaderSent) { MockHttpCache cache; @@ -6753,8 +6751,7 @@ TEST(HttpCache, ResourceFreshnessHeaderSent) { EXPECT_EQ(1, cache.network_layer()->transaction_count()); - // Send the request again and check that Chromium-Resource-Freshness header is - // added. + // Send the request again and check that Resource-Freshness header is added. stale_while_revalidate_transaction.handler = CheckResourceFreshnessHeader; RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction); @@ -6766,10 +6763,10 @@ static void CheckResourceFreshnessAbsent(const net::HttpRequestInfo* request, std::string* response_status, std::string* response_headers, std::string* response_data) { - EXPECT_FALSE(request->extra_headers.HasHeader("Chromium-Resource-Freshness")); + EXPECT_FALSE(request->extra_headers.HasHeader("Resource-Freshness")); } -// Verify that the Chromium-Resource-Freshness header is not sent when +// Verify that the Resource-Freshness header is not sent when // stale-while-revalidate is 0. TEST(HttpCache, ResourceFreshnessHeaderNotSent) { MockHttpCache cache; @@ -6786,8 +6783,7 @@ TEST(HttpCache, ResourceFreshnessHeaderNotSent) { EXPECT_EQ(1, cache.network_layer()->transaction_count()); - // Send the request again and check that Chromium-Resource-Freshness header is - // absent. + // Send the request again and check that Resource-Freshness header is absent. stale_while_revalidate_transaction.handler = CheckResourceFreshnessAbsent; RunTransactionTest(cache.http_cache(), stale_while_revalidate_transaction); diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc index b48d660dc74eb..faffc6c623477 100644 --- a/net/http/http_network_session.cc +++ b/net/http/http_network_session.cc @@ -20,6 +20,7 @@ #include "net/quic/crypto/quic_random.h" #include "net/quic/quic_clock.h" #include "net/quic/quic_crypto_client_stream_factory.h" +#include "net/quic/quic_protocol.h" #include "net/quic/quic_stream_factory.h" #include "net/socket/client_socket_factory.h" #include "net/socket/client_socket_pool_manager_impl.h" @@ -87,7 +88,6 @@ HttpNetworkSession::Params::Params() enable_websocket_over_spdy(false), enable_quic(false), enable_quic_port_selection(true), - enable_quic_pacing(false), enable_quic_time_based_loss_detection(false), quic_clock(NULL), quic_random(NULL), @@ -129,12 +129,12 @@ HttpNetworkSession::HttpNetworkSession(const Params& params) params.quic_user_agent_id, params.quic_supported_versions, params.enable_quic_port_selection, - params.enable_quic_pacing, params.enable_quic_time_based_loss_detection, params.quic_connection_options), spdy_session_pool_(params.host_resolver, params.ssl_config_service, params.http_server_properties, + params.transport_security_state, params.force_spdy_single_domain, params.enable_spdy_compression, params.enable_spdy_ping_based_connection_checking, @@ -252,7 +252,7 @@ base::Value* HttpNetworkSession::QuicInfoToValue() const { dict->SetBoolean("enable_quic_port_selection", params_.enable_quic_port_selection); dict->SetBoolean("enable_quic_pacing", - params_.enable_quic_pacing); + ContainsQuicTag(params_.quic_connection_options, kPACE)); dict->SetBoolean("enable_quic_time_based_loss_detection", params_.enable_quic_time_based_loss_detection); dict->SetString("origin_to_force_quic_on", diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h index 21a692dc28995..f62232a22a639 100644 --- a/net/http/http_network_session.h +++ b/net/http/http_network_session.h @@ -110,7 +110,6 @@ class NET_EXPORT HttpNetworkSession bool enable_quic; bool enable_quic_port_selection; - bool enable_quic_pacing; bool enable_quic_time_based_loss_detection; HostPortPair origin_to_force_quic_on; QuicClock* quic_clock; // Will be owned by QuicStreamFactory. diff --git a/net/http/http_response_body_drainer_unittest.cc b/net/http/http_response_body_drainer_unittest.cc index 42efa57f919e1..5aa0bff98f728 100644 --- a/net/http/http_response_body_drainer_unittest.cc +++ b/net/http/http_response_body_drainer_unittest.cc @@ -16,6 +16,7 @@ #include "net/http/http_network_session.h" #include "net/http/http_server_properties_impl.h" #include "net/http/http_stream.h" +#include "net/http/transport_security_state.h" #include "net/proxy/proxy_service.h" #include "net/ssl/ssl_config_service_defaults.h" #include "testing/gtest/include/gtest/gtest.h" @@ -210,6 +211,7 @@ class HttpResponseBodyDrainerTest : public testing::Test { : proxy_service_(ProxyService::CreateDirect()), ssl_config_service_(new SSLConfigServiceDefaults), http_server_properties_(new HttpServerPropertiesImpl()), + transport_security_state_(new TransportSecurityState()), session_(CreateNetworkSession()), mock_stream_(new MockHttpStream(&result_waiter_)), drainer_(new HttpResponseBodyDrainer(mock_stream_)) {} @@ -221,12 +223,14 @@ class HttpResponseBodyDrainerTest : public testing::Test { params.proxy_service = proxy_service_.get(); params.ssl_config_service = ssl_config_service_.get(); params.http_server_properties = http_server_properties_->GetWeakPtr(); + params.transport_security_state = transport_security_state_.get(); return new HttpNetworkSession(params); } scoped_ptr proxy_service_; scoped_refptr ssl_config_service_; scoped_ptr http_server_properties_; + scoped_ptr transport_security_state_; const scoped_refptr session_; CloseResultWaiter result_waiter_; MockHttpStream* const mock_stream_; // Owned by |drainer_|. diff --git a/net/http/http_response_headers.cc b/net/http/http_response_headers.cc index 698366968b16f..3a9f7e04fe0c4 100644 --- a/net/http/http_response_headers.cc +++ b/net/http/http_response_headers.cc @@ -1355,9 +1355,12 @@ base::Value* HttpResponseHeaders::NetLogCallback( std::string value; while (EnumerateHeaderLines(&iterator, &name, &value)) { std::string log_value = ElideHeaderValueForNetLog(log_level, name, value); + std::string escaped_name = EscapeNonASCII(name); + std::string escaped_value = EscapeNonASCII(log_value); headers->Append( new base::StringValue( - base::StringPrintf("%s: %s", name.c_str(), log_value.c_str()))); + base::StringPrintf("%s: %s", escaped_name.c_str(), + escaped_value.c_str()))); } dict->Set("headers", headers); return dict; diff --git a/net/http/http_security_headers_unittest.cc b/net/http/http_security_headers_unittest.cc index ce919ff81f394..240e76d10affd 100644 --- a/net/http/http_security_headers_unittest.cc +++ b/net/http/http_security_headers_unittest.cc @@ -506,6 +506,7 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { // docs.google.com has preloaded pins. const bool sni_enabled = true; std::string domain = "docs.google.com"; + state.enable_static_pins_ = true; EXPECT_TRUE( state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); @@ -554,8 +555,9 @@ TEST_F(HttpSecurityHeadersTest, UpdateDynamicPKPOnly) { HashValueVector hashes; hashes.push_back(good_hash); std::string failure_log; - EXPECT_TRUE( - state.CheckPublicKeyPins(domain, sni_enabled, hashes, &failure_log)); + const bool is_issued_by_known_root = true; + EXPECT_TRUE(state.CheckPublicKeyPins( + domain, sni_enabled, is_issued_by_known_root, hashes, &failure_log)); TransportSecurityState::DomainState new_dynamic_domain_state; EXPECT_TRUE(state.GetDynamicDomainState(domain, &new_dynamic_domain_state)); @@ -585,6 +587,7 @@ TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) { // docs.google.com has preloaded pins. const bool sni_enabled = true; std::string domain = "docs.google.com"; + state.enable_static_pins_ = true; ASSERT_TRUE( state.GetStaticDomainState(domain, sni_enabled, &static_domain_state)); EXPECT_GT(static_domain_state.pkp.spki_hashes.size(), 1UL); @@ -648,8 +651,13 @@ TEST_F(HttpSecurityHeadersTest, MAYBE_UpdateDynamicPKPMaxAge0) { // Damage the hashes to cause a pin validation failure. new_static_domain_state2.pkp.spki_hashes[0].data()[0] ^= 0x80; new_static_domain_state2.pkp.spki_hashes[1].data()[0] ^= 0x80; - EXPECT_FALSE(state.CheckPublicKeyPins( - domain, true, new_static_domain_state2.pkp.spki_hashes, &failure_log)); + const bool is_issued_by_known_root = true; + EXPECT_FALSE( + state.CheckPublicKeyPins(domain, + true, + is_issued_by_known_root, + new_static_domain_state2.pkp.spki_hashes, + &failure_log)); EXPECT_NE(0UL, failure_log.length()); } #undef MAYBE_UpdateDynamicPKPMaxAge0 @@ -663,6 +671,7 @@ TEST_F(HttpSecurityHeadersTest, NoClobberPins) { // accounts.google.com has preloaded pins. std::string domain = "accounts.google.com"; + state.enable_static_pins_ = true; // Retrieve the DomainState as it is by default, including its known good // pins. @@ -680,8 +689,12 @@ TEST_F(HttpSecurityHeadersTest, NoClobberPins) { EXPECT_TRUE(state.AddHSTSHeader(domain, "includesubdomains; max-age=10000")); EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); std::string failure_log; - EXPECT_TRUE(state.CheckPublicKeyPins( - domain, sni_enabled, saved_hashes, &failure_log)); + const bool is_issued_by_known_root = true; + EXPECT_TRUE(state.CheckPublicKeyPins(domain, + sni_enabled, + is_issued_by_known_root, + saved_hashes, + &failure_log)); // Add an HPKP header, which should only update the dynamic state. HashValue good_hash = GetTestHashValue(1, HASH_VALUE_SHA1); @@ -701,8 +714,11 @@ TEST_F(HttpSecurityHeadersTest, NoClobberPins) { EXPECT_TRUE(state.ShouldUpgradeToSSL(domain, sni_enabled)); // The dynamic pins, which do not match |saved_hashes|, should take // precedence over the static pins and cause the check to fail. - EXPECT_FALSE(state.CheckPublicKeyPins( - domain, sni_enabled, saved_hashes, &failure_log)); + EXPECT_FALSE(state.CheckPublicKeyPins(domain, + sni_enabled, + is_issued_by_known_root, + saved_hashes, + &failure_log)); } }; // namespace net diff --git a/net/http/http_stream_factory_impl_job.cc b/net/http/http_stream_factory_impl_job.cc index cad818be76dd9..53a377b262031 100644 --- a/net/http/http_stream_factory_impl_job.cc +++ b/net/http/http_stream_factory_impl_job.cc @@ -1137,7 +1137,8 @@ int HttpStreamFactoryImpl::Job::DoCreateStreamComplete(int result) { if (result < 0) return result; - session_->proxy_service()->ReportSuccess(proxy_info_); + session_->proxy_service()->ReportSuccess(proxy_info_, + session_->network_delegate()); next_state_ = STATE_NONE; return OK; } diff --git a/net/http/transport_security_state.cc b/net/http/transport_security_state.cc index 0b209b356e834..79ee302887ce0 100644 --- a/net/http/transport_security_state.cc +++ b/net/http/transport_security_state.cc @@ -84,7 +84,12 @@ bool AddHash(const char* sha1_hash, } // namespace TransportSecurityState::TransportSecurityState() - : delegate_(NULL) { + : delegate_(NULL), enable_static_pins_(true) { +// Static pinning is only enabled for official builds to make sure that +// others don't end up with pins that cannot be easily updated. +#if !defined(OFFICIAL_BUILD) || defined(OS_ANDROID) || defined(OS_IOS) + enable_static_pins_ = false; +#endif DCHECK(CalledOnValidThread()); } @@ -118,21 +123,30 @@ bool TransportSecurityState::ShouldUpgradeToSSL(const std::string& host, return false; } -bool TransportSecurityState::CheckPublicKeyPins(const std::string& host, - bool sni_enabled, - const HashValueVector& hashes, - std::string* failure_log) { - DomainState dynamic_state; - if (GetDynamicDomainState(host, &dynamic_state)) - return dynamic_state.CheckPublicKeyPins(hashes, failure_log); +bool TransportSecurityState::CheckPublicKeyPins( + const std::string& host, + bool sni_available, + bool is_issued_by_known_root, + const HashValueVector& public_key_hashes, + std::string* pinning_failure_log) { + // Perform pin validation if, and only if, all these conditions obtain: + // + // * the server's certificate chain chains up to a known root (i.e. not a + // user-installed trust anchor); and + // * the server actually has public key pins. + if (!is_issued_by_known_root || !HasPublicKeyPins(host, sni_available)) { + return true; + } - DomainState static_state; - if (GetStaticDomainState(host, sni_enabled, &static_state) && - static_state.CheckPublicKeyPins(hashes, failure_log)) { - return true; + bool pins_are_valid = CheckPublicKeyPinsImpl( + host, sni_available, public_key_hashes, pinning_failure_log); + if (!pins_are_valid) { + LOG(ERROR) << *pinning_failure_log; + ReportUMAOnPinFailure(host); } - return false; + UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", pins_are_valid); + return pins_are_valid; } bool TransportSecurityState::HasPublicKeyPins(const std::string& host, @@ -527,6 +541,8 @@ enum SecondLevelDomainName { DOMAIN_GOOGLETAGSERVICES_COM, DOMAIN_DROPBOX_COM, + DOMAIN_YOUTUBE_NOCOOKIE_COM, + DOMAIN_2MDN_NET, // Boundary value for UMA_HISTOGRAM_ENUMERATION: DOMAIN_NUM_EVENTS @@ -549,9 +565,13 @@ struct HSTSPreload { SecondLevelDomainName second_level_domain_name; }; -static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, - const std::string& canonicalized_host, size_t i, - TransportSecurityState::DomainState* out, bool* ret) { +static bool HasPreload(const struct HSTSPreload* entries, + size_t num_entries, + const std::string& canonicalized_host, + size_t i, + bool enable_static_pins, + TransportSecurityState::DomainState* out, + bool* ret) { for (size_t j = 0; j < num_entries; j++) { if (entries[j].length == canonicalized_host.size() - i && memcmp(entries[j].dns_name, &canonicalized_host[i], @@ -561,26 +581,29 @@ static bool HasPreload(const struct HSTSPreload* entries, size_t num_entries, } else { out->sts.include_subdomains = entries[j].include_subdomains; out->sts.last_observed = base::GetBuildTime(); - out->pkp.include_subdomains = entries[j].include_subdomains; - out->pkp.last_observed = base::GetBuildTime(); *ret = true; out->sts.upgrade_mode = TransportSecurityState::DomainState::MODE_FORCE_HTTPS; if (!entries[j].https_required) out->sts.upgrade_mode = TransportSecurityState::DomainState::MODE_DEFAULT; - if (entries[j].pins.required_hashes) { - const char* const* sha1_hash = entries[j].pins.required_hashes; - while (*sha1_hash) { - AddHash(*sha1_hash, &out->pkp.spki_hashes); - sha1_hash++; + + if (enable_static_pins) { + out->pkp.include_subdomains = entries[j].include_subdomains; + out->pkp.last_observed = base::GetBuildTime(); + if (entries[j].pins.required_hashes) { + const char* const* sha1_hash = entries[j].pins.required_hashes; + while (*sha1_hash) { + AddHash(*sha1_hash, &out->pkp.spki_hashes); + sha1_hash++; + } } - } - if (entries[j].pins.excluded_hashes) { - const char* const* sha1_hash = entries[j].pins.excluded_hashes; - while (*sha1_hash) { - AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); - sha1_hash++; + if (entries[j].pins.excluded_hashes) { + const char* const* sha1_hash = entries[j].pins.excluded_hashes; + while (*sha1_hash) { + AddHash(*sha1_hash, &out->pkp.bad_spki_hashes); + sha1_hash++; + } } } } @@ -763,6 +786,24 @@ bool TransportSecurityState::IsBuildTimely() { return (base::Time::Now() - build_time).InDays() < 70 /* 10 weeks */; } +bool TransportSecurityState::CheckPublicKeyPinsImpl( + const std::string& host, + bool sni_enabled, + const HashValueVector& hashes, + std::string* failure_log) { + DomainState dynamic_state; + if (GetDynamicDomainState(host, &dynamic_state)) + return dynamic_state.CheckPublicKeyPins(hashes, failure_log); + + DomainState static_state; + if (GetStaticDomainState(host, sni_enabled, &static_state)) + return static_state.CheckPublicKeyPins(hashes, failure_log); + + // HasPublicKeyPins should have returned true in order for this method + // to have been called, so if we fall through to here, it's an error. + return false; +} + bool TransportSecurityState::GetStaticDomainState(const std::string& host, bool sni_enabled, DomainState* out) const { @@ -781,15 +822,22 @@ bool TransportSecurityState::GetStaticDomainState(const std::string& host, canonicalized_host.size() - i); out->domain = DNSDomainToString(host_sub_chunk); bool ret; - if (is_build_timely && - HasPreload(kPreloadedSTS, kNumPreloadedSTS, canonicalized_host, i, out, - &ret)) { + if (is_build_timely && HasPreload(kPreloadedSTS, + kNumPreloadedSTS, + canonicalized_host, + i, + enable_static_pins_, + out, + &ret)) { return ret; } - if (sni_enabled && - is_build_timely && - HasPreload(kPreloadedSNISTS, kNumPreloadedSNISTS, canonicalized_host, i, - out, &ret)) { + if (sni_enabled && is_build_timely && HasPreload(kPreloadedSNISTS, + kNumPreloadedSNISTS, + canonicalized_host, + i, + enable_static_pins_, + out, + &ret)) { return ret; } } diff --git a/net/http/transport_security_state.h b/net/http/transport_security_state.h index 3645937914550..4d49da1b228fe 100644 --- a/net/http/transport_security_state.h +++ b/net/http/transport_security_state.h @@ -163,6 +163,7 @@ class NET_EXPORT TransportSecurityState bool ShouldUpgradeToSSL(const std::string& host, bool sni_enabled); bool CheckPublicKeyPins(const std::string& host, bool sni_enabled, + bool is_issued_by_known_root, const HashValueVector& hashes, std::string* failure_log); bool HasPublicKeyPins(const std::string& host, bool sni_enabled); @@ -267,6 +268,14 @@ class NET_EXPORT TransportSecurityState // The maximum number of seconds for which we'll cache an HSTS request. static const long int kMaxHSTSAgeSecs; + private: + friend class TransportSecurityStateTest; + FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, UpdateDynamicPKPOnly); + FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, UpdateDynamicPKPMaxAge0); + FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, NoClobberPins); + + typedef std::map DomainStateMap; + // Send an UMA report on pin validation failure, if the host is in a // statically-defined list of domains. // @@ -282,12 +291,11 @@ class NET_EXPORT TransportSecurityState // information) is timely. static bool IsBuildTimely(); - private: - friend class TransportSecurityStateTest; - FRIEND_TEST_ALL_PREFIXES(HttpSecurityHeadersTest, - UpdateDynamicPKPOnly); - - typedef std::map DomainStateMap; + // Helper method for actually checking pins. + bool CheckPublicKeyPinsImpl(const std::string& host, + bool sni_enabled, + const HashValueVector& hashes, + std::string* failure_log); // If a Delegate is present, notify it that the internal state has // changed. @@ -309,6 +317,9 @@ class NET_EXPORT TransportSecurityState Delegate* delegate_; + // True if static pins should be used. + bool enable_static_pins_; + DISALLOW_COPY_AND_ASSIGN(TransportSecurityState); }; diff --git a/net/http/transport_security_state_static.h b/net/http/transport_security_state_static.h index 91636efb4983a..fd8c2ed1a070f 100644 --- a/net/http/transport_security_state_static.h +++ b/net/http/transport_security_state_static.h @@ -439,648 +439,848 @@ static const char* const kDropboxAcceptableCerts[] = { } static const struct HSTSPreload kPreloadedSTS[] = { - {25, true, "\013pinningtest\007appspot\003com", false, kTestPins, DOMAIN_APPSPOT_COM }, - {12, true, "\006google\003com", false, kGooglePins, DOMAIN_GOOGLE_COM }, - {19, true, "\006wallet\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {21, true, "\010checkout\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {19, true, "\006chrome\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, true, "\004docs\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {20, true, "\007domains\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {18, true, "\005sites\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {25, true, "\014spreadsheets\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {22, true, "\011appengine\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {22, true, "\011encrypted\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {21, true, "\010accounts\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {21, true, "\010profiles\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, true, "\004mail\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {23, true, "\012talkgadget\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, true, "\004talk\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {29, true, "\020hostedtalkgadget\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, true, "\004plus\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {25, true, "\004plus\007sandbox\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {19, true, "\006script\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {20, true, "\007history\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {21, true, "\010security\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, true, "\004goto\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {18, true, "\005cloud\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {18, true, "\005glass\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {18, true, "\005admin\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, false, "\004play\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {20, true, "\006market\007android\003com", true, kGooglePins, DOMAIN_ANDROID_COM }, - {26, true, "\003ssl\020google-analytics\003com", true, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM }, - {18, true, "\005drive\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {16, true, "\012googleplex\003com", true, kGooglePins, DOMAIN_GOOGLEPLEX_COM }, - {19, true, "\006groups\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {17, true, "\004apis\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {32, true, "\022chromiumcodereview\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {38, true, "\030chrome-devtools-frontend\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {24, true, "\012codereview\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {25, true, "\012codereview\010chromium\003org", true, kGooglePins, DOMAIN_CHROMIUM_ORG }, - {17, true, "\004code\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {16, true, "\012googlecode\003com", false, kGooglePins, DOMAIN_GOOGLECODE_COM }, - {15, true, "\002dl\006google\003com", true, kGooglePins, DOMAIN_GOOGLE_COM }, - {26, true, "\011translate\012googleapis\003com", true, kGooglePins, DOMAIN_GOOGLEAPIS_COM }, - {24, true, "\012webfilings\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {35, true, "\025webfilings-mirror-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {27, true, "\015webfilings-eu\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {34, true, "\024webfilings-eu-mirror\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {24, true, "\012wf-demo-eu\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {25, true, "\013wf-demo-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {24, true, "\012wf-pentest\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {26, true, "\014wf-trial-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {25, true, "\013xbrlsuccess\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {25, true, "\013w-spotlight\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {29, true, "\017wf-training-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {30, true, "\020wf-bigsky-master\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {27, true, "\015wf-staging-hr\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {32, true, "\022wf-training-master\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {28, true, "\016wf-dogfood-hrd\007appspot\003com", true, kGooglePins, DOMAIN_APPSPOT_COM }, - {23, true, "\005chart\004apis\006google\003com", false, kGooglePins, DOMAIN_GOOGLE_COM }, - {11, true, "\005ytimg\003com", false, kGooglePins, DOMAIN_YTIMG_COM }, - {23, true, "\021googleusercontent\003com", false, kGooglePins, DOMAIN_GOOGLEUSERCONTENT_COM }, - {13, true, "\007youtube\003com", false, kGooglePins, DOMAIN_YOUTUBE_COM }, - {16, true, "\012googleapis\003com", false, kGooglePins, DOMAIN_GOOGLEAPIS_COM }, - {22, true, "\020googleadservices\003com", false, kGooglePins, DOMAIN_GOOGLEADSERVICES_COM }, - {13, true, "\007appspot\003com", false, kGooglePins, DOMAIN_APPSPOT_COM }, - {23, true, "\021googlesyndication\003com", false, kGooglePins, DOMAIN_GOOGLESYNDICATION_COM }, - {17, true, "\013doubleclick\003net", false, kGooglePins, DOMAIN_DOUBLECLICK_NET }, - {13, true, "\007gstatic\003com", false, kGooglePins, DOMAIN_GSTATIC_COM }, - {10, true, "\005youtu\002be", false, kGooglePins, DOMAIN_YOUTU_BE }, - {13, true, "\007android\003com", false, kGooglePins, DOMAIN_ANDROID_COM }, - {20, true, "\016googlecommerce\003com", false, kGooglePins, DOMAIN_GOOGLECOMMERCE_COM }, - {12, true, "\006urchin\003com", false, kGooglePins, DOMAIN_URCHIN_COM }, - {8, true, "\003goo\002gl", false, kGooglePins, DOMAIN_GOO_GL }, - {6, true, "\001g\002co", false, kGooglePins, DOMAIN_G_CO }, - {22, true, "\020googletagmanager\003com", false, kGooglePins, DOMAIN_GOOGLETAGMANAGER_COM }, - {23, true, "\021googletagservices\003com", false, kGooglePins, DOMAIN_GOOGLETAGSERVICES_COM }, - {11, true, "\006google\002ac", false, kGooglePins, DOMAIN_GOOGLE_AC }, - {11, true, "\006google\002ad", false, kGooglePins, DOMAIN_GOOGLE_AD }, - {11, true, "\006google\002ae", false, kGooglePins, DOMAIN_GOOGLE_AE }, - {11, true, "\006google\002af", false, kGooglePins, DOMAIN_GOOGLE_AF }, - {11, true, "\006google\002ag", false, kGooglePins, DOMAIN_GOOGLE_AG }, - {11, true, "\006google\002am", false, kGooglePins, DOMAIN_GOOGLE_AM }, - {11, true, "\006google\002as", false, kGooglePins, DOMAIN_GOOGLE_AS }, - {11, true, "\006google\002at", false, kGooglePins, DOMAIN_GOOGLE_AT }, - {11, true, "\006google\002az", false, kGooglePins, DOMAIN_GOOGLE_AZ }, - {11, true, "\006google\002ba", false, kGooglePins, DOMAIN_GOOGLE_BA }, - {11, true, "\006google\002be", false, kGooglePins, DOMAIN_GOOGLE_BE }, - {11, true, "\006google\002bf", false, kGooglePins, DOMAIN_GOOGLE_BF }, - {11, true, "\006google\002bg", false, kGooglePins, DOMAIN_GOOGLE_BG }, - {11, true, "\006google\002bi", false, kGooglePins, DOMAIN_GOOGLE_BI }, - {11, true, "\006google\002bj", false, kGooglePins, DOMAIN_GOOGLE_BJ }, - {11, true, "\006google\002bs", false, kGooglePins, DOMAIN_GOOGLE_BS }, - {11, true, "\006google\002by", false, kGooglePins, DOMAIN_GOOGLE_BY }, - {11, true, "\006google\002ca", false, kGooglePins, DOMAIN_GOOGLE_CA }, - {12, true, "\006google\003cat", false, kGooglePins, DOMAIN_GOOGLE_CAT }, - {11, true, "\006google\002cc", false, kGooglePins, DOMAIN_GOOGLE_CC }, - {11, true, "\006google\002cd", false, kGooglePins, DOMAIN_GOOGLE_CD }, - {11, true, "\006google\002cf", false, kGooglePins, DOMAIN_GOOGLE_CF }, - {11, true, "\006google\002cg", false, kGooglePins, DOMAIN_GOOGLE_CG }, - {11, true, "\006google\002ch", false, kGooglePins, DOMAIN_GOOGLE_CH }, - {11, true, "\006google\002ci", false, kGooglePins, DOMAIN_GOOGLE_CI }, - {11, true, "\006google\002cl", false, kGooglePins, DOMAIN_GOOGLE_CL }, - {11, true, "\006google\002cm", false, kGooglePins, DOMAIN_GOOGLE_CM }, - {11, true, "\006google\002cn", false, kGooglePins, DOMAIN_GOOGLE_CN }, - {14, true, "\006google\002co\002ao", false, kGooglePins, DOMAIN_CO_AO }, - {14, true, "\006google\002co\002bw", false, kGooglePins, DOMAIN_CO_BW }, - {14, true, "\006google\002co\002ck", false, kGooglePins, DOMAIN_CO_CK }, - {14, true, "\006google\002co\002cr", false, kGooglePins, DOMAIN_CO_CR }, - {14, true, "\006google\002co\002hu", false, kGooglePins, DOMAIN_CO_HU }, - {14, true, "\006google\002co\002id", false, kGooglePins, DOMAIN_CO_ID }, - {14, true, "\006google\002co\002il", false, kGooglePins, DOMAIN_CO_IL }, - {14, true, "\006google\002co\002im", false, kGooglePins, DOMAIN_CO_IM }, - {14, true, "\006google\002co\002in", false, kGooglePins, DOMAIN_CO_IN }, - {14, true, "\006google\002co\002je", false, kGooglePins, DOMAIN_CO_JE }, - {14, true, "\006google\002co\002jp", false, kGooglePins, DOMAIN_CO_JP }, - {14, true, "\006google\002co\002ke", false, kGooglePins, DOMAIN_CO_KE }, - {14, true, "\006google\002co\002kr", false, kGooglePins, DOMAIN_CO_KR }, - {14, true, "\006google\002co\002ls", false, kGooglePins, DOMAIN_CO_LS }, - {14, true, "\006google\002co\002ma", false, kGooglePins, DOMAIN_CO_MA }, - {14, true, "\006google\002co\002mz", false, kGooglePins, DOMAIN_CO_MZ }, - {14, true, "\006google\002co\002nz", false, kGooglePins, DOMAIN_CO_NZ }, - {14, true, "\006google\002co\002th", false, kGooglePins, DOMAIN_CO_TH }, - {14, true, "\006google\002co\002tz", false, kGooglePins, DOMAIN_CO_TZ }, - {14, true, "\006google\002co\002ug", false, kGooglePins, DOMAIN_CO_UG }, - {14, true, "\006google\002co\002uk", false, kGooglePins, DOMAIN_CO_UK }, - {14, true, "\006google\002co\002uz", false, kGooglePins, DOMAIN_CO_UZ }, - {14, true, "\006google\002co\002ve", false, kGooglePins, DOMAIN_CO_VE }, - {14, true, "\006google\002co\002vi", false, kGooglePins, DOMAIN_CO_VI }, - {14, true, "\006google\002co\002za", false, kGooglePins, DOMAIN_CO_ZA }, - {14, true, "\006google\002co\002zm", false, kGooglePins, DOMAIN_CO_ZM }, - {14, true, "\006google\002co\002zw", false, kGooglePins, DOMAIN_CO_ZW }, - {15, true, "\006google\003com\002af", false, kGooglePins, DOMAIN_COM_AF }, - {15, true, "\006google\003com\002ag", false, kGooglePins, DOMAIN_COM_AG }, - {15, true, "\006google\003com\002ai", false, kGooglePins, DOMAIN_COM_AI }, - {15, true, "\006google\003com\002ar", false, kGooglePins, DOMAIN_COM_AR }, - {15, true, "\006google\003com\002au", false, kGooglePins, DOMAIN_COM_AU }, - {15, true, "\006google\003com\002bd", false, kGooglePins, DOMAIN_COM_BD }, - {15, true, "\006google\003com\002bh", false, kGooglePins, DOMAIN_COM_BH }, - {15, true, "\006google\003com\002bn", false, kGooglePins, DOMAIN_COM_BN }, - {15, true, "\006google\003com\002bo", false, kGooglePins, DOMAIN_COM_BO }, - {15, true, "\006google\003com\002br", false, kGooglePins, DOMAIN_COM_BR }, - {15, true, "\006google\003com\002by", false, kGooglePins, DOMAIN_COM_BY }, - {15, true, "\006google\003com\002bz", false, kGooglePins, DOMAIN_COM_BZ }, - {15, true, "\006google\003com\002cn", false, kGooglePins, DOMAIN_COM_CN }, - {15, true, "\006google\003com\002co", false, kGooglePins, DOMAIN_COM_CO }, - {15, true, "\006google\003com\002cu", false, kGooglePins, DOMAIN_COM_CU }, - {15, true, "\006google\003com\002cy", false, kGooglePins, DOMAIN_COM_CY }, - {15, true, "\006google\003com\002do", false, kGooglePins, DOMAIN_COM_DO }, - {15, true, "\006google\003com\002ec", false, kGooglePins, DOMAIN_COM_EC }, - {15, true, "\006google\003com\002eg", false, kGooglePins, DOMAIN_COM_EG }, - {15, true, "\006google\003com\002et", false, kGooglePins, DOMAIN_COM_ET }, - {15, true, "\006google\003com\002fj", false, kGooglePins, DOMAIN_COM_FJ }, - {15, true, "\006google\003com\002ge", false, kGooglePins, DOMAIN_COM_GE }, - {15, true, "\006google\003com\002gh", false, kGooglePins, DOMAIN_COM_GH }, - {15, true, "\006google\003com\002gi", false, kGooglePins, DOMAIN_COM_GI }, - {15, true, "\006google\003com\002gr", false, kGooglePins, DOMAIN_COM_GR }, - {15, true, "\006google\003com\002gt", false, kGooglePins, DOMAIN_COM_GT }, - {15, true, "\006google\003com\002hk", false, kGooglePins, DOMAIN_COM_HK }, - {15, true, "\006google\003com\002iq", false, kGooglePins, DOMAIN_COM_IQ }, - {15, true, "\006google\003com\002jm", false, kGooglePins, DOMAIN_COM_JM }, - {15, true, "\006google\003com\002jo", false, kGooglePins, DOMAIN_COM_JO }, - {15, true, "\006google\003com\002kh", false, kGooglePins, DOMAIN_COM_KH }, - {15, true, "\006google\003com\002kw", false, kGooglePins, DOMAIN_COM_KW }, - {15, true, "\006google\003com\002lb", false, kGooglePins, DOMAIN_COM_LB }, - {15, true, "\006google\003com\002ly", false, kGooglePins, DOMAIN_COM_LY }, - {15, true, "\006google\003com\002mt", false, kGooglePins, DOMAIN_COM_MT }, - {15, true, "\006google\003com\002mx", false, kGooglePins, DOMAIN_COM_MX }, - {15, true, "\006google\003com\002my", false, kGooglePins, DOMAIN_COM_MY }, - {15, true, "\006google\003com\002na", false, kGooglePins, DOMAIN_COM_NA }, - {15, true, "\006google\003com\002nf", false, kGooglePins, DOMAIN_COM_NF }, - {15, true, "\006google\003com\002ng", false, kGooglePins, DOMAIN_COM_NG }, - {15, true, "\006google\003com\002ni", false, kGooglePins, DOMAIN_COM_NI }, - {15, true, "\006google\003com\002np", false, kGooglePins, DOMAIN_COM_NP }, - {15, true, "\006google\003com\002nr", false, kGooglePins, DOMAIN_COM_NR }, - {15, true, "\006google\003com\002om", false, kGooglePins, DOMAIN_COM_OM }, - {15, true, "\006google\003com\002pa", false, kGooglePins, DOMAIN_COM_PA }, - {15, true, "\006google\003com\002pe", false, kGooglePins, DOMAIN_COM_PE }, - {15, true, "\006google\003com\002ph", false, kGooglePins, DOMAIN_COM_PH }, - {15, true, "\006google\003com\002pk", false, kGooglePins, DOMAIN_COM_PK }, - {15, true, "\006google\003com\002pl", false, kGooglePins, DOMAIN_COM_PL }, - {15, true, "\006google\003com\002pr", false, kGooglePins, DOMAIN_COM_PR }, - {15, true, "\006google\003com\002py", false, kGooglePins, DOMAIN_COM_PY }, - {15, true, "\006google\003com\002qa", false, kGooglePins, DOMAIN_COM_QA }, - {15, true, "\006google\003com\002ru", false, kGooglePins, DOMAIN_COM_RU }, - {15, true, "\006google\003com\002sa", false, kGooglePins, DOMAIN_COM_SA }, - {15, true, "\006google\003com\002sb", false, kGooglePins, DOMAIN_COM_SB }, - {15, true, "\006google\003com\002sg", false, kGooglePins, DOMAIN_COM_SG }, - {15, true, "\006google\003com\002sl", false, kGooglePins, DOMAIN_COM_SL }, - {15, true, "\006google\003com\002sv", false, kGooglePins, DOMAIN_COM_SV }, - {15, true, "\006google\003com\002tj", false, kGooglePins, DOMAIN_COM_TJ }, - {15, true, "\006google\003com\002tn", false, kGooglePins, DOMAIN_COM_TN }, - {15, true, "\006google\003com\002tr", false, kGooglePins, DOMAIN_COM_TR }, - {15, true, "\006google\003com\002tw", false, kGooglePins, DOMAIN_COM_TW }, - {15, true, "\006google\003com\002ua", false, kGooglePins, DOMAIN_COM_UA }, - {15, true, "\006google\003com\002uy", false, kGooglePins, DOMAIN_COM_UY }, - {15, true, "\006google\003com\002vc", false, kGooglePins, DOMAIN_COM_VC }, - {15, true, "\006google\003com\002ve", false, kGooglePins, DOMAIN_COM_VE }, - {15, true, "\006google\003com\002vn", false, kGooglePins, DOMAIN_COM_VN }, - {11, true, "\006google\002cv", false, kGooglePins, DOMAIN_GOOGLE_CV }, - {11, true, "\006google\002cz", false, kGooglePins, DOMAIN_GOOGLE_CZ }, - {11, true, "\006google\002de", false, kGooglePins, DOMAIN_GOOGLE_DE }, - {11, true, "\006google\002dj", false, kGooglePins, DOMAIN_GOOGLE_DJ }, - {11, true, "\006google\002dk", false, kGooglePins, DOMAIN_GOOGLE_DK }, - {11, true, "\006google\002dm", false, kGooglePins, DOMAIN_GOOGLE_DM }, - {11, true, "\006google\002dz", false, kGooglePins, DOMAIN_GOOGLE_DZ }, - {11, true, "\006google\002ee", false, kGooglePins, DOMAIN_GOOGLE_EE }, - {11, true, "\006google\002es", false, kGooglePins, DOMAIN_GOOGLE_ES }, - {11, true, "\006google\002fi", false, kGooglePins, DOMAIN_GOOGLE_FI }, - {11, true, "\006google\002fm", false, kGooglePins, DOMAIN_GOOGLE_FM }, - {11, true, "\006google\002fr", false, kGooglePins, DOMAIN_GOOGLE_FR }, - {11, true, "\006google\002ga", false, kGooglePins, DOMAIN_GOOGLE_GA }, - {11, true, "\006google\002ge", false, kGooglePins, DOMAIN_GOOGLE_GE }, - {11, true, "\006google\002gg", false, kGooglePins, DOMAIN_GOOGLE_GG }, - {11, true, "\006google\002gl", false, kGooglePins, DOMAIN_GOOGLE_GL }, - {11, true, "\006google\002gm", false, kGooglePins, DOMAIN_GOOGLE_GM }, - {11, true, "\006google\002gp", false, kGooglePins, DOMAIN_GOOGLE_GP }, - {11, true, "\006google\002gr", false, kGooglePins, DOMAIN_GOOGLE_GR }, - {11, true, "\006google\002gy", false, kGooglePins, DOMAIN_GOOGLE_GY }, - {11, true, "\006google\002hk", false, kGooglePins, DOMAIN_GOOGLE_HK }, - {11, true, "\006google\002hn", false, kGooglePins, DOMAIN_GOOGLE_HN }, - {11, true, "\006google\002hr", false, kGooglePins, DOMAIN_GOOGLE_HR }, - {11, true, "\006google\002ht", false, kGooglePins, DOMAIN_GOOGLE_HT }, - {11, true, "\006google\002hu", false, kGooglePins, DOMAIN_GOOGLE_HU }, - {11, true, "\006google\002ie", false, kGooglePins, DOMAIN_GOOGLE_IE }, - {11, true, "\006google\002im", false, kGooglePins, DOMAIN_GOOGLE_IM }, - {13, true, "\006google\004info", false, kGooglePins, DOMAIN_GOOGLE_INFO }, - {11, true, "\006google\002iq", false, kGooglePins, DOMAIN_GOOGLE_IQ }, - {11, true, "\006google\002is", false, kGooglePins, DOMAIN_GOOGLE_IS }, - {11, true, "\006google\002it", false, kGooglePins, DOMAIN_GOOGLE_IT }, - {14, true, "\006google\002it\002ao", false, kGooglePins, DOMAIN_IT_AO }, - {11, true, "\006google\002je", false, kGooglePins, DOMAIN_GOOGLE_JE }, - {11, true, "\006google\002jo", false, kGooglePins, DOMAIN_GOOGLE_JO }, - {13, true, "\006google\004jobs", false, kGooglePins, DOMAIN_GOOGLE_JOBS }, - {11, true, "\006google\002jp", false, kGooglePins, DOMAIN_GOOGLE_JP }, - {11, true, "\006google\002kg", false, kGooglePins, DOMAIN_GOOGLE_KG }, - {11, true, "\006google\002ki", false, kGooglePins, DOMAIN_GOOGLE_KI }, - {11, true, "\006google\002kz", false, kGooglePins, DOMAIN_GOOGLE_KZ }, - {11, true, "\006google\002la", false, kGooglePins, DOMAIN_GOOGLE_LA }, - {11, true, "\006google\002li", false, kGooglePins, DOMAIN_GOOGLE_LI }, - {11, true, "\006google\002lk", false, kGooglePins, DOMAIN_GOOGLE_LK }, - {11, true, "\006google\002lt", false, kGooglePins, DOMAIN_GOOGLE_LT }, - {11, true, "\006google\002lu", false, kGooglePins, DOMAIN_GOOGLE_LU }, - {11, true, "\006google\002lv", false, kGooglePins, DOMAIN_GOOGLE_LV }, - {11, true, "\006google\002md", false, kGooglePins, DOMAIN_GOOGLE_MD }, - {11, true, "\006google\002me", false, kGooglePins, DOMAIN_GOOGLE_ME }, - {11, true, "\006google\002mg", false, kGooglePins, DOMAIN_GOOGLE_MG }, - {11, true, "\006google\002mk", false, kGooglePins, DOMAIN_GOOGLE_MK }, - {11, true, "\006google\002ml", false, kGooglePins, DOMAIN_GOOGLE_ML }, - {11, true, "\006google\002mn", false, kGooglePins, DOMAIN_GOOGLE_MN }, - {11, true, "\006google\002ms", false, kGooglePins, DOMAIN_GOOGLE_MS }, - {11, true, "\006google\002mu", false, kGooglePins, DOMAIN_GOOGLE_MU }, - {11, true, "\006google\002mv", false, kGooglePins, DOMAIN_GOOGLE_MV }, - {11, true, "\006google\002mw", false, kGooglePins, DOMAIN_GOOGLE_MW }, - {11, true, "\006google\002ne", false, kGooglePins, DOMAIN_GOOGLE_NE }, - {14, true, "\006google\002ne\002jp", false, kGooglePins, DOMAIN_NE_JP }, - {12, true, "\006google\003net", false, kGooglePins, DOMAIN_GOOGLE_NET }, - {11, true, "\006google\002nl", false, kGooglePins, DOMAIN_GOOGLE_NL }, - {11, true, "\006google\002no", false, kGooglePins, DOMAIN_GOOGLE_NO }, - {11, true, "\006google\002nr", false, kGooglePins, DOMAIN_GOOGLE_NR }, - {11, true, "\006google\002nu", false, kGooglePins, DOMAIN_GOOGLE_NU }, - {15, true, "\006google\003off\002ai", false, kGooglePins, DOMAIN_OFF_AI }, - {11, true, "\006google\002pk", false, kGooglePins, DOMAIN_GOOGLE_PK }, - {11, true, "\006google\002pl", false, kGooglePins, DOMAIN_GOOGLE_PL }, - {11, true, "\006google\002pn", false, kGooglePins, DOMAIN_GOOGLE_PN }, - {11, true, "\006google\002ps", false, kGooglePins, DOMAIN_GOOGLE_PS }, - {11, true, "\006google\002pt", false, kGooglePins, DOMAIN_GOOGLE_PT }, - {11, true, "\006google\002ro", false, kGooglePins, DOMAIN_GOOGLE_RO }, - {11, true, "\006google\002rs", false, kGooglePins, DOMAIN_GOOGLE_RS }, - {11, true, "\006google\002ru", false, kGooglePins, DOMAIN_GOOGLE_RU }, - {11, true, "\006google\002rw", false, kGooglePins, DOMAIN_GOOGLE_RW }, - {11, true, "\006google\002sc", false, kGooglePins, DOMAIN_GOOGLE_SC }, - {11, true, "\006google\002se", false, kGooglePins, DOMAIN_GOOGLE_SE }, - {11, true, "\006google\002sh", false, kGooglePins, DOMAIN_GOOGLE_SH }, - {11, true, "\006google\002si", false, kGooglePins, DOMAIN_GOOGLE_SI }, - {11, true, "\006google\002sk", false, kGooglePins, DOMAIN_GOOGLE_SK }, - {11, true, "\006google\002sm", false, kGooglePins, DOMAIN_GOOGLE_SM }, - {11, true, "\006google\002sn", false, kGooglePins, DOMAIN_GOOGLE_SN }, - {11, true, "\006google\002so", false, kGooglePins, DOMAIN_GOOGLE_SO }, - {11, true, "\006google\002st", false, kGooglePins, DOMAIN_GOOGLE_ST }, - {11, true, "\006google\002td", false, kGooglePins, DOMAIN_GOOGLE_TD }, - {11, true, "\006google\002tg", false, kGooglePins, DOMAIN_GOOGLE_TG }, - {11, true, "\006google\002tk", false, kGooglePins, DOMAIN_GOOGLE_TK }, - {11, true, "\006google\002tl", false, kGooglePins, DOMAIN_GOOGLE_TL }, - {11, true, "\006google\002tm", false, kGooglePins, DOMAIN_GOOGLE_TM }, - {11, true, "\006google\002tn", false, kGooglePins, DOMAIN_GOOGLE_TN }, - {11, true, "\006google\002to", false, kGooglePins, DOMAIN_GOOGLE_TO }, - {11, true, "\006google\002tt", false, kGooglePins, DOMAIN_GOOGLE_TT }, - {11, true, "\006google\002us", false, kGooglePins, DOMAIN_GOOGLE_US }, - {11, true, "\006google\002uz", false, kGooglePins, DOMAIN_GOOGLE_UZ }, - {11, true, "\006google\002vg", false, kGooglePins, DOMAIN_GOOGLE_VG }, - {11, true, "\006google\002vu", false, kGooglePins, DOMAIN_GOOGLE_VU }, - {11, true, "\006google\002ws", false, kGooglePins, DOMAIN_GOOGLE_WS }, - {23, true, "\005learn\013doubleclick\003net", false, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\003www\006paypal\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006paypal\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\003www\006elanex\003biz", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006jottit\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\015sunshinepress\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, false, "\003www\013noisebridge\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, false, "\004neg9\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006riseup\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, false, "\006factor\002cc", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\007members\010mayfirst\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\007support\010mayfirst\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\002id\010mayfirst\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, true, "\005lists\010mayfirst\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\007webmail\010mayfirst\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, true, "\011roundcube\010mayfirst\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {28, false, "\016aladdinschools\007appspot\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\011ottospora\002nl", true, kNoPins, DOMAIN_NOT_PINNED }, - {25, false, "\003www\017paycheckrecords\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\010lastpass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\003www\010lastpass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010keyerror\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\010entropia\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\003www\010entropia\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\005romab\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\012logentries\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\003www\012logentries\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006stripe\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {27, true, "\025cloudsecurityalliance\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\005login\004sapo\002pt", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\015mattmccutchen\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006betnet\002fr", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\010uprotect\002it", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\010squareup\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006square\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\004cert\002se", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006crypto\002is", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, true, "\005simon\007butcher\004name", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\004linx\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007dropcam\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\003www\007dropcam\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {30, true, "\010ebanking\014indovinabank\003com\002vn", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007epoxate\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\012torproject\003org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, - {21, true, "\004blog\012torproject\003org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, - {22, true, "\005check\012torproject\003org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, - {20, true, "\003www\012torproject\003org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, - {21, true, "\004dist\012torproject\003org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, - {22, true, "\003www\014moneybookers\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\013ledgerscope\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, false, "\003www\013ledgerscope\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003app\007recurly\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003api\007recurly\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007greplin\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\003www\007greplin\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {27, true, "\006luneta\016nearbuysystems\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006ubertt\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\004pixi\002me", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010grepular\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\012mydigipass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\003www\012mydigipass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {26, false, "\011developer\012mydigipass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {30, false, "\003www\011developer\012mydigipass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, false, "\007sandbox\012mydigipass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {28, false, "\003www\007sandbox\012mydigipass\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006crypto\003cat", true, kCryptoCatPins, DOMAIN_CRYPTO_CAT }, - {25, true, "\014bigshinylock\006minazo\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005crate\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007twitter\003com", true, kTwitterComPins, DOMAIN_TWITTER_COM }, - {17, true, "\003www\007twitter\003com", true, kTwitterComPins, DOMAIN_TWITTER_COM }, - {17, true, "\003api\007twitter\003com", false, kTwitterCDNPins, DOMAIN_TWITTER_COM }, - {19, true, "\005oauth\007twitter\003com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, - {20, true, "\006mobile\007twitter\003com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, - {17, true, "\003dev\007twitter\003com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, - {22, true, "\010business\007twitter\003com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, - {22, true, "\010platform\007twitter\003com", false, kTwitterCDNPins, DOMAIN_TWITTER_COM }, - {11, true, "\005twimg\003com", false, kTwitterCDNPins, DOMAIN_TWIMG_COM }, - {22, true, "\020braintreegateway\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, false, "\021braintreepayments\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {27, false, "\003www\021braintreepayments\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, false, "\022emailprivacytester\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007tor2web\003org", false, kTor2webPins, DOMAIN_TOR2WEB_ORG }, - {25, true, "\010business\007medbank\003com\002mt", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\005arivo\003com\002br", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\003www\013apollo-auto\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\003www\005cueup\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, false, "\005jitsi\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\003www\005jitsi\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\010download\005jitsi\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {8, true, "\003sol\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\010irccloud\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\003www\010irccloud\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\005alpha\010irccloud\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006passwd\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011browserid\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\005login\007persona\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007neonisi\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003www\007neonisi\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\005shops\007neonisi\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\014piratenlogin\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011howrandom\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\010intercom\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\003api\010intercom\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\003www\010intercom\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\010fatzebra\003com\002au", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\007csawctf\004poly\003edu", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, false, "\003www\014makeyourlaws\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\003iop\006intuit\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\010surfeasy\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\003www\010surfeasy\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011packagist\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\003www\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011mylookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, false, "\003www\011mylookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\002dm\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\010business\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\004blog\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003faq\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\010platform\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\005email\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003app\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003api\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, true, "\011keymaster\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, true, "\011discovery\007lookout\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\014mobilethreat\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {25, true, "\023mobilethreatnetwork\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011itriskltd\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\012stocktrade\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\011openshift\006redhat\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\014therapynotes\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, false, "\003www\014therapynotes\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\003wiz\003biz", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\002my\006onlime\002ch", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\007webmail\006onlime\002ch", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\003crm\006onlime\002ch", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\003www\003gov\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\014silentcircle\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\014silentcircle\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\015serverdensity\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\002my\010alfresco\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\007webmail\010gigahost\002dk", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007paymill\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\007paymill\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\012gocardless\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\005espra\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005zoo24\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\004mega\002co\002nz", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\003api\004mega\002co\002nz", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007lockify\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\010writeapp\002me", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\010bugzilla\007mozilla\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {30, true, "\007members\020nearlyfreespeech\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, false, "\003ssl\011panoramio\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007kiwiirc\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003pay\010gigahost\002dk", true, kNoPins, DOMAIN_NOT_PINNED }, - {27, true, "\015controlcenter\010gigahost\002dk", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006simple\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\003www\006simple\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\002fj\006simple\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\003api\006simple\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\004bank\006simple\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\005bassh\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\004sah3\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, false, "\003grc\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\003www\003grc\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\003www\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\007manager\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\004blog\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\007library\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\005forum\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\001p\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\005paste\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, false, "\010pastebin\006linode\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\017inertianetworks\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\010carezone\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011conformal\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\012cyphertite\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\010logotype\002se", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\004bccx\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011launchkey\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\010carlolly\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\003www\013cyveillance\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\004blog\013cyveillance\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006whonix\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006shodan\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\015rapidresearch\002me", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010surkatty\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\017securityheaders\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005haste\002ch", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\007mudcrab\002us", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\010mediacru\002sh", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\010lolicore\002ch", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\007cloudns\003com\002au", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\005oplop\007appspot\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006bcrook\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\004wiki\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, false, "\004lumi\002do", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\020appseccalifornia\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\013crowdcurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\013saturngames\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, true, "\021strongest-privacy\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {25, true, "\011ecosystem\011atlassian\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\002id\011atlassian\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011bitbucket\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\007cupcake\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\007cupcake\002is", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\004tent\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006cybozu\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\013davidlyness\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006medium\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\007liberty\007lavabit\003com", true, kLavabitPins, DOMAIN_LAVABIT_COM }, - {16, true, "\012getlantern\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011kinsights\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\007simbolo\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, false, "\003www\007simbolo\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\012zenpayroll\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\003www\012zenpayroll\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\003get\012zenpayroll\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, false, "\006errors\012zenpayroll\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, false, "\006manage\012zenpayroll\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\016gernert-server\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\010skydrive\004live\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, true, "\011lifeguard\005aecom\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\004data\003qld\003gov\002au", true, kNoPins, DOMAIN_NOT_PINNED }, - {25, false, "\014publications\003qld\003gov\002au", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\002go\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\005login\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\002my\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\007payroll\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\002in\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\003api\004xero\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\003eff\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\004mail\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\010passport\006yandex\002ru", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, false, "\010passport\006yandex\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\010passport\006yandex\002ua", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\010passport\006yandex\002by", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\010passport\006yandex\002kz", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, false, "\010passport\006yandex\003com\002tr", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006mnsure\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\010getcloak\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\003www\010getcloak\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, true, "\020matteomarescotti\004name", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\003www\011heliosnet\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007opsmate\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003www\007opsmate\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007f-droid\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\003www\010evernote\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\003app\010yinxiang\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011neilwynne\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, false, "\016calyxinstitute\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, false, "\003www\016calyxinstitute\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011blacklane\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\012boxcryptor\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, false, "\004aclu\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\003www\004aclu\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007prodpad\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007mailbox\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006roddis\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\003www\006roddis\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005fiken\002no", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010fairbill\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\005nexth\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005nexth\002us", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005nexth\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006souyar\003net", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006souyar\002de", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006souyar\002us", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, false, "\003www\007banking\002co\002at", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, false, "\003mbp\007banking\002co\002at", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007feedbin\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, true, "\004heha\002co", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\013passwordbox\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\004pypi\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\003www\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\004docs\006python\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\013encircleapp\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\010onedrive\004live\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010onedrive\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, true, "\016keepersecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011keeperapp\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\006donmez\002ws", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, false, "\010activiti\010alfresco\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011cloudcert\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010seifried\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, false, "\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\003www\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, false, "\006static\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\005stage\005wepay\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011vmoagents\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007adsfund\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {9, false, "\004pult\002co", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\014dillonkorman\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\006edmodo\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, false, "\003www\013eternalgoth\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\003app\007manilla\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\012harvestapp\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\007anycoin\002me", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, true, "\010noexpect\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\006airbnb\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, true, "\003www\006airbnb\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, false, "\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {14, false, "\003www\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, false, "\006mobile\004usaa\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, true, "\007subrosa\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, false, "\011detectify\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, true, "\005crbug\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, true, "\016manageprojects\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {21, false, "\017tinfoilsecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {25, false, "\003www\017tinfoilsecurity\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {11, false, "\006imouto\002my", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\010vocaloid\002my", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\006sakaki\005anime\002my", true, kNoPins, DOMAIN_NOT_PINNED }, - {18, true, "\007reviews\005anime\002my", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\004miku\007hatsune\002my", true, kNoPins, DOMAIN_NOT_PINNED }, - {19, true, "\012webcollect\003org\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, false, "\003www\016capitainetrain\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {22, true, "\010accounts\007firefox\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {6, true, "\001z\002ai", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, true, "\007wildbee\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, true, "\006portal\005tirol\002gv\002at", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007dropbox\003com", true, kDropboxPins, DOMAIN_DROPBOX_COM }, - {17, true, "\003www\007dropbox\003com", true, kDropboxPins, DOMAIN_DROPBOX_COM }, - {18, true, "\012code-poets\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {13, false, "\007jackyyf\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {10, true, "\005flynn\002io", true, kNoPins, DOMAIN_NOT_PINNED }, - {15, true, "\011hackerone\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {28, true, "\026hackerone-user-content\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, false, "\017gamesdepartment\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {27, true, "\003www\017gamesdepartment\002co\002uk", true, kNoPins, DOMAIN_NOT_PINNED }, - {16, false, "\012schokokeks\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {20, true, "\003www\012schokokeks\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {23, true, "\006config\012schokokeks\003org", true, kNoPins, DOMAIN_NOT_PINNED }, - {24, true, "\007webmail\012schokokeks\003org", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\013" "pinningtest" "\007" "appspot" "\003" "com", false, kTestPins, DOMAIN_APPSPOT_COM }, + {12, true, "\006" "google" "\003" "com", false, kGooglePins, DOMAIN_GOOGLE_COM }, + {19, true, "\006" "wallet" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {21, true, "\010" "checkout" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {19, true, "\006" "chrome" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, true, "\004" "docs" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {20, true, "\007" "domains" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {18, true, "\005" "sites" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {25, true, "\014" "spreadsheets" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {22, true, "\011" "appengine" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {22, true, "\011" "encrypted" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {21, true, "\010" "accounts" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {21, true, "\010" "profiles" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, true, "\004" "mail" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {23, true, "\012" "talkgadget" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, true, "\004" "talk" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {29, true, "\020" "hostedtalkgadget" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, true, "\004" "plus" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {25, true, "\004" "plus" "\007" "sandbox" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {19, true, "\006" "script" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {20, true, "\007" "history" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {21, true, "\010" "security" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, true, "\004" "goto" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {18, true, "\005" "cloud" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {18, true, "\005" "glass" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {18, true, "\005" "admin" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, false, "\004" "play" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {20, true, "\006" "market" "\007" "android" "\003" "com", true, kGooglePins, DOMAIN_ANDROID_COM }, + {26, true, "\003" "ssl" "\020" "google-analytics" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM }, + {18, true, "\005" "drive" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {16, true, "\012" "googleplex" "\003" "com", true, kGooglePins, DOMAIN_GOOGLEPLEX_COM }, + {19, true, "\006" "groups" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {17, true, "\004" "apis" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {32, true, "\022" "chromiumcodereview" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {38, true, "\030" "chrome-devtools-frontend" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {24, true, "\012" "codereview" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\012" "codereview" "\010" "chromium" "\003" "org", true, kGooglePins, DOMAIN_CHROMIUM_ORG }, + {17, true, "\004" "code" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {16, true, "\012" "googlecode" "\003" "com", false, kGooglePins, DOMAIN_GOOGLECODE_COM }, + {15, true, "\002" "dl" "\006" "google" "\003" "com", true, kGooglePins, DOMAIN_GOOGLE_COM }, + {26, true, "\011" "translate" "\012" "googleapis" "\003" "com", true, kGooglePins, DOMAIN_GOOGLEAPIS_COM }, + {24, true, "\012" "webfilings" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {35, true, "\025" "webfilings-mirror-hrd" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {27, true, "\015" "webfilings-eu" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {34, true, "\024" "webfilings-eu-mirror" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {24, true, "\012" "wf-demo-eu" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\013" "wf-demo-hrd" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {24, true, "\012" "wf-pentest" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {26, true, "\014" "wf-trial-hrd" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\013" "xbrlsuccess" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {25, true, "\013" "w-spotlight" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {29, true, "\017" "wf-training-hrd" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {30, true, "\020" "wf-bigsky-master" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {27, true, "\015" "wf-staging-hr" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {32, true, "\022" "wf-training-master" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {28, true, "\016" "wf-dogfood-hrd" "\007" "appspot" "\003" "com", true, kGooglePins, DOMAIN_APPSPOT_COM }, + {23, true, "\005" "chart" "\004" "apis" "\006" "google" "\003" "com", false, kGooglePins, DOMAIN_GOOGLE_COM }, + {11, true, "\005" "ytimg" "\003" "com", false, kGooglePins, DOMAIN_YTIMG_COM }, + {23, true, "\021" "googleusercontent" "\003" "com", false, kGooglePins, DOMAIN_GOOGLEUSERCONTENT_COM }, + {13, true, "\007" "youtube" "\003" "com", false, kGooglePins, DOMAIN_YOUTUBE_COM }, + {22, true, "\020" "youtube-nocookie" "\003" "com", false, kGooglePins, DOMAIN_YOUTUBE_NOCOOKIE_COM }, + {16, true, "\012" "googleapis" "\003" "com", false, kGooglePins, DOMAIN_GOOGLEAPIS_COM }, + {22, true, "\020" "googleadservices" "\003" "com", false, kGooglePins, DOMAIN_GOOGLEADSERVICES_COM }, + {13, true, "\007" "appspot" "\003" "com", false, kGooglePins, DOMAIN_APPSPOT_COM }, + {23, true, "\021" "googlesyndication" "\003" "com", false, kGooglePins, DOMAIN_GOOGLESYNDICATION_COM }, + {17, true, "\013" "doubleclick" "\003" "net", false, kGooglePins, DOMAIN_DOUBLECLICK_NET }, + {10, true, "\004" "2mdn" "\003" "net", false, kGooglePins, DOMAIN_2MDN_NET }, + {13, true, "\007" "gstatic" "\003" "com", false, kGooglePins, DOMAIN_GSTATIC_COM }, + {10, true, "\005" "youtu" "\002" "be", false, kGooglePins, DOMAIN_YOUTU_BE }, + {13, true, "\007" "android" "\003" "com", false, kGooglePins, DOMAIN_ANDROID_COM }, + {20, true, "\016" "googlecommerce" "\003" "com", false, kGooglePins, DOMAIN_GOOGLECOMMERCE_COM }, + {12, true, "\006" "urchin" "\003" "com", false, kGooglePins, DOMAIN_URCHIN_COM }, + {8, true, "\003" "goo" "\002" "gl", false, kGooglePins, DOMAIN_GOO_GL }, + {6, true, "\001" "g" "\002" "co", false, kGooglePins, DOMAIN_G_CO }, + {22, true, "\020" "googletagmanager" "\003" "com", false, kGooglePins, DOMAIN_GOOGLETAGMANAGER_COM }, + {23, true, "\021" "googletagservices" "\003" "com", false, kGooglePins, DOMAIN_GOOGLETAGSERVICES_COM }, + {11, true, "\006" "google" "\002" "ac", false, kGooglePins, DOMAIN_GOOGLE_AC }, + {11, true, "\006" "google" "\002" "ad", false, kGooglePins, DOMAIN_GOOGLE_AD }, + {11, true, "\006" "google" "\002" "ae", false, kGooglePins, DOMAIN_GOOGLE_AE }, + {11, true, "\006" "google" "\002" "af", false, kGooglePins, DOMAIN_GOOGLE_AF }, + {11, true, "\006" "google" "\002" "ag", false, kGooglePins, DOMAIN_GOOGLE_AG }, + {11, true, "\006" "google" "\002" "am", false, kGooglePins, DOMAIN_GOOGLE_AM }, + {11, true, "\006" "google" "\002" "as", false, kGooglePins, DOMAIN_GOOGLE_AS }, + {11, true, "\006" "google" "\002" "at", false, kGooglePins, DOMAIN_GOOGLE_AT }, + {11, true, "\006" "google" "\002" "az", false, kGooglePins, DOMAIN_GOOGLE_AZ }, + {11, true, "\006" "google" "\002" "ba", false, kGooglePins, DOMAIN_GOOGLE_BA }, + {11, true, "\006" "google" "\002" "be", false, kGooglePins, DOMAIN_GOOGLE_BE }, + {11, true, "\006" "google" "\002" "bf", false, kGooglePins, DOMAIN_GOOGLE_BF }, + {11, true, "\006" "google" "\002" "bg", false, kGooglePins, DOMAIN_GOOGLE_BG }, + {11, true, "\006" "google" "\002" "bi", false, kGooglePins, DOMAIN_GOOGLE_BI }, + {11, true, "\006" "google" "\002" "bj", false, kGooglePins, DOMAIN_GOOGLE_BJ }, + {11, true, "\006" "google" "\002" "bs", false, kGooglePins, DOMAIN_GOOGLE_BS }, + {11, true, "\006" "google" "\002" "by", false, kGooglePins, DOMAIN_GOOGLE_BY }, + {11, true, "\006" "google" "\002" "ca", false, kGooglePins, DOMAIN_GOOGLE_CA }, + {12, true, "\006" "google" "\003" "cat", false, kGooglePins, DOMAIN_GOOGLE_CAT }, + {11, true, "\006" "google" "\002" "cc", false, kGooglePins, DOMAIN_GOOGLE_CC }, + {11, true, "\006" "google" "\002" "cd", false, kGooglePins, DOMAIN_GOOGLE_CD }, + {11, true, "\006" "google" "\002" "cf", false, kGooglePins, DOMAIN_GOOGLE_CF }, + {11, true, "\006" "google" "\002" "cg", false, kGooglePins, DOMAIN_GOOGLE_CG }, + {11, true, "\006" "google" "\002" "ch", false, kGooglePins, DOMAIN_GOOGLE_CH }, + {11, true, "\006" "google" "\002" "ci", false, kGooglePins, DOMAIN_GOOGLE_CI }, + {11, true, "\006" "google" "\002" "cl", false, kGooglePins, DOMAIN_GOOGLE_CL }, + {11, true, "\006" "google" "\002" "cm", false, kGooglePins, DOMAIN_GOOGLE_CM }, + {11, true, "\006" "google" "\002" "cn", false, kGooglePins, DOMAIN_GOOGLE_CN }, + {14, true, "\006" "google" "\002" "co" "\002" "ao", false, kGooglePins, DOMAIN_CO_AO }, + {14, true, "\006" "google" "\002" "co" "\002" "bw", false, kGooglePins, DOMAIN_CO_BW }, + {14, true, "\006" "google" "\002" "co" "\002" "ck", false, kGooglePins, DOMAIN_CO_CK }, + {14, true, "\006" "google" "\002" "co" "\002" "cr", false, kGooglePins, DOMAIN_CO_CR }, + {14, true, "\006" "google" "\002" "co" "\002" "hu", false, kGooglePins, DOMAIN_CO_HU }, + {14, true, "\006" "google" "\002" "co" "\002" "id", false, kGooglePins, DOMAIN_CO_ID }, + {14, true, "\006" "google" "\002" "co" "\002" "il", false, kGooglePins, DOMAIN_CO_IL }, + {14, true, "\006" "google" "\002" "co" "\002" "im", false, kGooglePins, DOMAIN_CO_IM }, + {14, true, "\006" "google" "\002" "co" "\002" "in", false, kGooglePins, DOMAIN_CO_IN }, + {14, true, "\006" "google" "\002" "co" "\002" "je", false, kGooglePins, DOMAIN_CO_JE }, + {14, true, "\006" "google" "\002" "co" "\002" "jp", false, kGooglePins, DOMAIN_CO_JP }, + {14, true, "\006" "google" "\002" "co" "\002" "ke", false, kGooglePins, DOMAIN_CO_KE }, + {14, true, "\006" "google" "\002" "co" "\002" "kr", false, kGooglePins, DOMAIN_CO_KR }, + {14, true, "\006" "google" "\002" "co" "\002" "ls", false, kGooglePins, DOMAIN_CO_LS }, + {14, true, "\006" "google" "\002" "co" "\002" "ma", false, kGooglePins, DOMAIN_CO_MA }, + {14, true, "\006" "google" "\002" "co" "\002" "mz", false, kGooglePins, DOMAIN_CO_MZ }, + {14, true, "\006" "google" "\002" "co" "\002" "nz", false, kGooglePins, DOMAIN_CO_NZ }, + {14, true, "\006" "google" "\002" "co" "\002" "th", false, kGooglePins, DOMAIN_CO_TH }, + {14, true, "\006" "google" "\002" "co" "\002" "tz", false, kGooglePins, DOMAIN_CO_TZ }, + {14, true, "\006" "google" "\002" "co" "\002" "ug", false, kGooglePins, DOMAIN_CO_UG }, + {14, true, "\006" "google" "\002" "co" "\002" "uk", false, kGooglePins, DOMAIN_CO_UK }, + {14, true, "\006" "google" "\002" "co" "\002" "uz", false, kGooglePins, DOMAIN_CO_UZ }, + {14, true, "\006" "google" "\002" "co" "\002" "ve", false, kGooglePins, DOMAIN_CO_VE }, + {14, true, "\006" "google" "\002" "co" "\002" "vi", false, kGooglePins, DOMAIN_CO_VI }, + {14, true, "\006" "google" "\002" "co" "\002" "za", false, kGooglePins, DOMAIN_CO_ZA }, + {14, true, "\006" "google" "\002" "co" "\002" "zm", false, kGooglePins, DOMAIN_CO_ZM }, + {14, true, "\006" "google" "\002" "co" "\002" "zw", false, kGooglePins, DOMAIN_CO_ZW }, + {15, true, "\006" "google" "\003" "com" "\002" "af", false, kGooglePins, DOMAIN_COM_AF }, + {15, true, "\006" "google" "\003" "com" "\002" "ag", false, kGooglePins, DOMAIN_COM_AG }, + {15, true, "\006" "google" "\003" "com" "\002" "ai", false, kGooglePins, DOMAIN_COM_AI }, + {15, true, "\006" "google" "\003" "com" "\002" "ar", false, kGooglePins, DOMAIN_COM_AR }, + {15, true, "\006" "google" "\003" "com" "\002" "au", false, kGooglePins, DOMAIN_COM_AU }, + {15, true, "\006" "google" "\003" "com" "\002" "bd", false, kGooglePins, DOMAIN_COM_BD }, + {15, true, "\006" "google" "\003" "com" "\002" "bh", false, kGooglePins, DOMAIN_COM_BH }, + {15, true, "\006" "google" "\003" "com" "\002" "bn", false, kGooglePins, DOMAIN_COM_BN }, + {15, true, "\006" "google" "\003" "com" "\002" "bo", false, kGooglePins, DOMAIN_COM_BO }, + {15, true, "\006" "google" "\003" "com" "\002" "br", false, kGooglePins, DOMAIN_COM_BR }, + {15, true, "\006" "google" "\003" "com" "\002" "by", false, kGooglePins, DOMAIN_COM_BY }, + {15, true, "\006" "google" "\003" "com" "\002" "bz", false, kGooglePins, DOMAIN_COM_BZ }, + {15, true, "\006" "google" "\003" "com" "\002" "cn", false, kGooglePins, DOMAIN_COM_CN }, + {15, true, "\006" "google" "\003" "com" "\002" "co", false, kGooglePins, DOMAIN_COM_CO }, + {15, true, "\006" "google" "\003" "com" "\002" "cu", false, kGooglePins, DOMAIN_COM_CU }, + {15, true, "\006" "google" "\003" "com" "\002" "cy", false, kGooglePins, DOMAIN_COM_CY }, + {15, true, "\006" "google" "\003" "com" "\002" "do", false, kGooglePins, DOMAIN_COM_DO }, + {15, true, "\006" "google" "\003" "com" "\002" "ec", false, kGooglePins, DOMAIN_COM_EC }, + {15, true, "\006" "google" "\003" "com" "\002" "eg", false, kGooglePins, DOMAIN_COM_EG }, + {15, true, "\006" "google" "\003" "com" "\002" "et", false, kGooglePins, DOMAIN_COM_ET }, + {15, true, "\006" "google" "\003" "com" "\002" "fj", false, kGooglePins, DOMAIN_COM_FJ }, + {15, true, "\006" "google" "\003" "com" "\002" "ge", false, kGooglePins, DOMAIN_COM_GE }, + {15, true, "\006" "google" "\003" "com" "\002" "gh", false, kGooglePins, DOMAIN_COM_GH }, + {15, true, "\006" "google" "\003" "com" "\002" "gi", false, kGooglePins, DOMAIN_COM_GI }, + {15, true, "\006" "google" "\003" "com" "\002" "gr", false, kGooglePins, DOMAIN_COM_GR }, + {15, true, "\006" "google" "\003" "com" "\002" "gt", false, kGooglePins, DOMAIN_COM_GT }, + {15, true, "\006" "google" "\003" "com" "\002" "hk", false, kGooglePins, DOMAIN_COM_HK }, + {15, true, "\006" "google" "\003" "com" "\002" "iq", false, kGooglePins, DOMAIN_COM_IQ }, + {15, true, "\006" "google" "\003" "com" "\002" "jm", false, kGooglePins, DOMAIN_COM_JM }, + {15, true, "\006" "google" "\003" "com" "\002" "jo", false, kGooglePins, DOMAIN_COM_JO }, + {15, true, "\006" "google" "\003" "com" "\002" "kh", false, kGooglePins, DOMAIN_COM_KH }, + {15, true, "\006" "google" "\003" "com" "\002" "kw", false, kGooglePins, DOMAIN_COM_KW }, + {15, true, "\006" "google" "\003" "com" "\002" "lb", false, kGooglePins, DOMAIN_COM_LB }, + {15, true, "\006" "google" "\003" "com" "\002" "ly", false, kGooglePins, DOMAIN_COM_LY }, + {15, true, "\006" "google" "\003" "com" "\002" "mt", false, kGooglePins, DOMAIN_COM_MT }, + {15, true, "\006" "google" "\003" "com" "\002" "mx", false, kGooglePins, DOMAIN_COM_MX }, + {15, true, "\006" "google" "\003" "com" "\002" "my", false, kGooglePins, DOMAIN_COM_MY }, + {15, true, "\006" "google" "\003" "com" "\002" "na", false, kGooglePins, DOMAIN_COM_NA }, + {15, true, "\006" "google" "\003" "com" "\002" "nf", false, kGooglePins, DOMAIN_COM_NF }, + {15, true, "\006" "google" "\003" "com" "\002" "ng", false, kGooglePins, DOMAIN_COM_NG }, + {15, true, "\006" "google" "\003" "com" "\002" "ni", false, kGooglePins, DOMAIN_COM_NI }, + {15, true, "\006" "google" "\003" "com" "\002" "np", false, kGooglePins, DOMAIN_COM_NP }, + {15, true, "\006" "google" "\003" "com" "\002" "nr", false, kGooglePins, DOMAIN_COM_NR }, + {15, true, "\006" "google" "\003" "com" "\002" "om", false, kGooglePins, DOMAIN_COM_OM }, + {15, true, "\006" "google" "\003" "com" "\002" "pa", false, kGooglePins, DOMAIN_COM_PA }, + {15, true, "\006" "google" "\003" "com" "\002" "pe", false, kGooglePins, DOMAIN_COM_PE }, + {15, true, "\006" "google" "\003" "com" "\002" "ph", false, kGooglePins, DOMAIN_COM_PH }, + {15, true, "\006" "google" "\003" "com" "\002" "pk", false, kGooglePins, DOMAIN_COM_PK }, + {15, true, "\006" "google" "\003" "com" "\002" "pl", false, kGooglePins, DOMAIN_COM_PL }, + {15, true, "\006" "google" "\003" "com" "\002" "pr", false, kGooglePins, DOMAIN_COM_PR }, + {15, true, "\006" "google" "\003" "com" "\002" "py", false, kGooglePins, DOMAIN_COM_PY }, + {15, true, "\006" "google" "\003" "com" "\002" "qa", false, kGooglePins, DOMAIN_COM_QA }, + {15, true, "\006" "google" "\003" "com" "\002" "ru", false, kGooglePins, DOMAIN_COM_RU }, + {15, true, "\006" "google" "\003" "com" "\002" "sa", false, kGooglePins, DOMAIN_COM_SA }, + {15, true, "\006" "google" "\003" "com" "\002" "sb", false, kGooglePins, DOMAIN_COM_SB }, + {15, true, "\006" "google" "\003" "com" "\002" "sg", false, kGooglePins, DOMAIN_COM_SG }, + {15, true, "\006" "google" "\003" "com" "\002" "sl", false, kGooglePins, DOMAIN_COM_SL }, + {15, true, "\006" "google" "\003" "com" "\002" "sv", false, kGooglePins, DOMAIN_COM_SV }, + {15, true, "\006" "google" "\003" "com" "\002" "tj", false, kGooglePins, DOMAIN_COM_TJ }, + {15, true, "\006" "google" "\003" "com" "\002" "tn", false, kGooglePins, DOMAIN_COM_TN }, + {15, true, "\006" "google" "\003" "com" "\002" "tr", false, kGooglePins, DOMAIN_COM_TR }, + {15, true, "\006" "google" "\003" "com" "\002" "tw", false, kGooglePins, DOMAIN_COM_TW }, + {15, true, "\006" "google" "\003" "com" "\002" "ua", false, kGooglePins, DOMAIN_COM_UA }, + {15, true, "\006" "google" "\003" "com" "\002" "uy", false, kGooglePins, DOMAIN_COM_UY }, + {15, true, "\006" "google" "\003" "com" "\002" "vc", false, kGooglePins, DOMAIN_COM_VC }, + {15, true, "\006" "google" "\003" "com" "\002" "ve", false, kGooglePins, DOMAIN_COM_VE }, + {15, true, "\006" "google" "\003" "com" "\002" "vn", false, kGooglePins, DOMAIN_COM_VN }, + {11, true, "\006" "google" "\002" "cv", false, kGooglePins, DOMAIN_GOOGLE_CV }, + {11, true, "\006" "google" "\002" "cz", false, kGooglePins, DOMAIN_GOOGLE_CZ }, + {11, true, "\006" "google" "\002" "de", false, kGooglePins, DOMAIN_GOOGLE_DE }, + {11, true, "\006" "google" "\002" "dj", false, kGooglePins, DOMAIN_GOOGLE_DJ }, + {11, true, "\006" "google" "\002" "dk", false, kGooglePins, DOMAIN_GOOGLE_DK }, + {11, true, "\006" "google" "\002" "dm", false, kGooglePins, DOMAIN_GOOGLE_DM }, + {11, true, "\006" "google" "\002" "dz", false, kGooglePins, DOMAIN_GOOGLE_DZ }, + {11, true, "\006" "google" "\002" "ee", false, kGooglePins, DOMAIN_GOOGLE_EE }, + {11, true, "\006" "google" "\002" "es", false, kGooglePins, DOMAIN_GOOGLE_ES }, + {11, true, "\006" "google" "\002" "fi", false, kGooglePins, DOMAIN_GOOGLE_FI }, + {11, true, "\006" "google" "\002" "fm", false, kGooglePins, DOMAIN_GOOGLE_FM }, + {11, true, "\006" "google" "\002" "fr", false, kGooglePins, DOMAIN_GOOGLE_FR }, + {11, true, "\006" "google" "\002" "ga", false, kGooglePins, DOMAIN_GOOGLE_GA }, + {11, true, "\006" "google" "\002" "ge", false, kGooglePins, DOMAIN_GOOGLE_GE }, + {11, true, "\006" "google" "\002" "gg", false, kGooglePins, DOMAIN_GOOGLE_GG }, + {11, true, "\006" "google" "\002" "gl", false, kGooglePins, DOMAIN_GOOGLE_GL }, + {11, true, "\006" "google" "\002" "gm", false, kGooglePins, DOMAIN_GOOGLE_GM }, + {11, true, "\006" "google" "\002" "gp", false, kGooglePins, DOMAIN_GOOGLE_GP }, + {11, true, "\006" "google" "\002" "gr", false, kGooglePins, DOMAIN_GOOGLE_GR }, + {11, true, "\006" "google" "\002" "gy", false, kGooglePins, DOMAIN_GOOGLE_GY }, + {11, true, "\006" "google" "\002" "hk", false, kGooglePins, DOMAIN_GOOGLE_HK }, + {11, true, "\006" "google" "\002" "hn", false, kGooglePins, DOMAIN_GOOGLE_HN }, + {11, true, "\006" "google" "\002" "hr", false, kGooglePins, DOMAIN_GOOGLE_HR }, + {11, true, "\006" "google" "\002" "ht", false, kGooglePins, DOMAIN_GOOGLE_HT }, + {11, true, "\006" "google" "\002" "hu", false, kGooglePins, DOMAIN_GOOGLE_HU }, + {11, true, "\006" "google" "\002" "ie", false, kGooglePins, DOMAIN_GOOGLE_IE }, + {11, true, "\006" "google" "\002" "im", false, kGooglePins, DOMAIN_GOOGLE_IM }, + {13, true, "\006" "google" "\004" "info", false, kGooglePins, DOMAIN_GOOGLE_INFO }, + {11, true, "\006" "google" "\002" "iq", false, kGooglePins, DOMAIN_GOOGLE_IQ }, + {11, true, "\006" "google" "\002" "is", false, kGooglePins, DOMAIN_GOOGLE_IS }, + {11, true, "\006" "google" "\002" "it", false, kGooglePins, DOMAIN_GOOGLE_IT }, + {14, true, "\006" "google" "\002" "it" "\002" "ao", false, kGooglePins, DOMAIN_IT_AO }, + {11, true, "\006" "google" "\002" "je", false, kGooglePins, DOMAIN_GOOGLE_JE }, + {11, true, "\006" "google" "\002" "jo", false, kGooglePins, DOMAIN_GOOGLE_JO }, + {13, true, "\006" "google" "\004" "jobs", false, kGooglePins, DOMAIN_GOOGLE_JOBS }, + {11, true, "\006" "google" "\002" "jp", false, kGooglePins, DOMAIN_GOOGLE_JP }, + {11, true, "\006" "google" "\002" "kg", false, kGooglePins, DOMAIN_GOOGLE_KG }, + {11, true, "\006" "google" "\002" "ki", false, kGooglePins, DOMAIN_GOOGLE_KI }, + {11, true, "\006" "google" "\002" "kz", false, kGooglePins, DOMAIN_GOOGLE_KZ }, + {11, true, "\006" "google" "\002" "la", false, kGooglePins, DOMAIN_GOOGLE_LA }, + {11, true, "\006" "google" "\002" "li", false, kGooglePins, DOMAIN_GOOGLE_LI }, + {11, true, "\006" "google" "\002" "lk", false, kGooglePins, DOMAIN_GOOGLE_LK }, + {11, true, "\006" "google" "\002" "lt", false, kGooglePins, DOMAIN_GOOGLE_LT }, + {11, true, "\006" "google" "\002" "lu", false, kGooglePins, DOMAIN_GOOGLE_LU }, + {11, true, "\006" "google" "\002" "lv", false, kGooglePins, DOMAIN_GOOGLE_LV }, + {11, true, "\006" "google" "\002" "md", false, kGooglePins, DOMAIN_GOOGLE_MD }, + {11, true, "\006" "google" "\002" "me", false, kGooglePins, DOMAIN_GOOGLE_ME }, + {11, true, "\006" "google" "\002" "mg", false, kGooglePins, DOMAIN_GOOGLE_MG }, + {11, true, "\006" "google" "\002" "mk", false, kGooglePins, DOMAIN_GOOGLE_MK }, + {11, true, "\006" "google" "\002" "ml", false, kGooglePins, DOMAIN_GOOGLE_ML }, + {11, true, "\006" "google" "\002" "mn", false, kGooglePins, DOMAIN_GOOGLE_MN }, + {11, true, "\006" "google" "\002" "ms", false, kGooglePins, DOMAIN_GOOGLE_MS }, + {11, true, "\006" "google" "\002" "mu", false, kGooglePins, DOMAIN_GOOGLE_MU }, + {11, true, "\006" "google" "\002" "mv", false, kGooglePins, DOMAIN_GOOGLE_MV }, + {11, true, "\006" "google" "\002" "mw", false, kGooglePins, DOMAIN_GOOGLE_MW }, + {11, true, "\006" "google" "\002" "ne", false, kGooglePins, DOMAIN_GOOGLE_NE }, + {14, true, "\006" "google" "\002" "ne" "\002" "jp", false, kGooglePins, DOMAIN_NE_JP }, + {12, true, "\006" "google" "\003" "net", false, kGooglePins, DOMAIN_GOOGLE_NET }, + {11, true, "\006" "google" "\002" "nl", false, kGooglePins, DOMAIN_GOOGLE_NL }, + {11, true, "\006" "google" "\002" "no", false, kGooglePins, DOMAIN_GOOGLE_NO }, + {11, true, "\006" "google" "\002" "nr", false, kGooglePins, DOMAIN_GOOGLE_NR }, + {11, true, "\006" "google" "\002" "nu", false, kGooglePins, DOMAIN_GOOGLE_NU }, + {15, true, "\006" "google" "\003" "off" "\002" "ai", false, kGooglePins, DOMAIN_OFF_AI }, + {11, true, "\006" "google" "\002" "pk", false, kGooglePins, DOMAIN_GOOGLE_PK }, + {11, true, "\006" "google" "\002" "pl", false, kGooglePins, DOMAIN_GOOGLE_PL }, + {11, true, "\006" "google" "\002" "pn", false, kGooglePins, DOMAIN_GOOGLE_PN }, + {11, true, "\006" "google" "\002" "ps", false, kGooglePins, DOMAIN_GOOGLE_PS }, + {11, true, "\006" "google" "\002" "pt", false, kGooglePins, DOMAIN_GOOGLE_PT }, + {11, true, "\006" "google" "\002" "ro", false, kGooglePins, DOMAIN_GOOGLE_RO }, + {11, true, "\006" "google" "\002" "rs", false, kGooglePins, DOMAIN_GOOGLE_RS }, + {11, true, "\006" "google" "\002" "ru", false, kGooglePins, DOMAIN_GOOGLE_RU }, + {11, true, "\006" "google" "\002" "rw", false, kGooglePins, DOMAIN_GOOGLE_RW }, + {11, true, "\006" "google" "\002" "sc", false, kGooglePins, DOMAIN_GOOGLE_SC }, + {11, true, "\006" "google" "\002" "se", false, kGooglePins, DOMAIN_GOOGLE_SE }, + {11, true, "\006" "google" "\002" "sh", false, kGooglePins, DOMAIN_GOOGLE_SH }, + {11, true, "\006" "google" "\002" "si", false, kGooglePins, DOMAIN_GOOGLE_SI }, + {11, true, "\006" "google" "\002" "sk", false, kGooglePins, DOMAIN_GOOGLE_SK }, + {11, true, "\006" "google" "\002" "sm", false, kGooglePins, DOMAIN_GOOGLE_SM }, + {11, true, "\006" "google" "\002" "sn", false, kGooglePins, DOMAIN_GOOGLE_SN }, + {11, true, "\006" "google" "\002" "so", false, kGooglePins, DOMAIN_GOOGLE_SO }, + {11, true, "\006" "google" "\002" "st", false, kGooglePins, DOMAIN_GOOGLE_ST }, + {11, true, "\006" "google" "\002" "td", false, kGooglePins, DOMAIN_GOOGLE_TD }, + {11, true, "\006" "google" "\002" "tg", false, kGooglePins, DOMAIN_GOOGLE_TG }, + {11, true, "\006" "google" "\002" "tk", false, kGooglePins, DOMAIN_GOOGLE_TK }, + {11, true, "\006" "google" "\002" "tl", false, kGooglePins, DOMAIN_GOOGLE_TL }, + {11, true, "\006" "google" "\002" "tm", false, kGooglePins, DOMAIN_GOOGLE_TM }, + {11, true, "\006" "google" "\002" "tn", false, kGooglePins, DOMAIN_GOOGLE_TN }, + {11, true, "\006" "google" "\002" "to", false, kGooglePins, DOMAIN_GOOGLE_TO }, + {11, true, "\006" "google" "\002" "tt", false, kGooglePins, DOMAIN_GOOGLE_TT }, + {11, true, "\006" "google" "\002" "us", false, kGooglePins, DOMAIN_GOOGLE_US }, + {11, true, "\006" "google" "\002" "uz", false, kGooglePins, DOMAIN_GOOGLE_UZ }, + {11, true, "\006" "google" "\002" "vg", false, kGooglePins, DOMAIN_GOOGLE_VG }, + {11, true, "\006" "google" "\002" "vu", false, kGooglePins, DOMAIN_GOOGLE_VU }, + {11, true, "\006" "google" "\002" "ws", false, kGooglePins, DOMAIN_GOOGLE_WS }, + {23, true, "\005" "learn" "\013" "doubleclick" "\003" "net", false, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003" "www" "\006" "paypal" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "paypal" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003" "www" "\006" "elanex" "\003" "biz", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "jottit" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "sunshinepress" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\003" "www" "\013" "noisebridge" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, false, "\004" "neg9" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "riseup" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\006" "factor" "\002" "cc", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\007" "members" "\010" "mayfirst" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\007" "support" "\010" "mayfirst" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\002" "id" "\010" "mayfirst" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\005" "lists" "\010" "mayfirst" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\007" "webmail" "\010" "mayfirst" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, true, "\011" "roundcube" "\010" "mayfirst" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, false, "\016" "aladdinschools" "\007" "appspot" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "ottospora" "\002" "nl", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, false, "\003" "www" "\017" "paycheckrecords" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010" "lastpass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003" "www" "\010" "lastpass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "keyerror" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\010" "entropia" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\003" "www" "\010" "entropia" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "romab" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\012" "logentries" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\003" "www" "\012" "logentries" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "stripe" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {27, true, "\025" "cloudsecurityalliance" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\005" "login" "\004" "sapo" "\002" "pt", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "mattmccutchen" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "betnet" "\002" "fr", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "uprotect" "\002" "it", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010" "squareup" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "square" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "cert" "\002" "se", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "crypto" "\002" "is", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\005" "simon" "\007" "butcher" "\004" "name", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\004" "linx" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "dropcam" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\003" "www" "\007" "dropcam" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, true, "\010" "ebanking" "\014" "indovinabank" "\003" "com" "\002" "vn", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "epoxate" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\012" "torproject" "\003" "org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, + {21, true, "\004" "blog" "\012" "torproject" "\003" "org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, + {22, true, "\005" "check" "\012" "torproject" "\003" "org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, + {20, true, "\003" "www" "\012" "torproject" "\003" "org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, + {21, true, "\004" "dist" "\012" "torproject" "\003" "org", true, kTorPins, DOMAIN_TORPROJECT_ORG }, + {22, true, "\003" "www" "\014" "moneybookers" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\013" "ledgerscope" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\003" "www" "\013" "ledgerscope" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "app" "\007" "recurly" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "api" "\007" "recurly" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "greplin" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\003" "www" "\007" "greplin" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {27, true, "\006" "luneta" "\016" "nearbuysystems" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "ubertt" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "pixi" "\002" "me", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "grepular" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\012" "mydigipass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\003" "www" "\012" "mydigipass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {26, false, "\011" "developer" "\012" "mydigipass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, false, "\003" "www" "\011" "developer" "\012" "mydigipass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\007" "sandbox" "\012" "mydigipass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, false, "\003" "www" "\007" "sandbox" "\012" "mydigipass" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "crypto" "\003" "cat", true, kCryptoCatPins, DOMAIN_CRYPTO_CAT }, + {25, true, "\014" "bigshinylock" "\006" "minazo" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "crate" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "twitter" "\003" "com", true, kTwitterComPins, DOMAIN_TWITTER_COM }, + {17, true, "\003" "www" "\007" "twitter" "\003" "com", true, kTwitterComPins, DOMAIN_TWITTER_COM }, + {17, true, "\003" "api" "\007" "twitter" "\003" "com", false, kTwitterCDNPins, DOMAIN_TWITTER_COM }, + {19, true, "\005" "oauth" "\007" "twitter" "\003" "com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, + {20, true, "\006" "mobile" "\007" "twitter" "\003" "com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, + {17, true, "\003" "dev" "\007" "twitter" "\003" "com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, + {22, true, "\010" "business" "\007" "twitter" "\003" "com", false, kTwitterComPins, DOMAIN_TWITTER_COM }, + {22, true, "\010" "platform" "\007" "twitter" "\003" "com", false, kTwitterCDNPins, DOMAIN_TWITTER_COM }, + {11, true, "\005" "twimg" "\003" "com", false, kTwitterCDNPins, DOMAIN_TWIMG_COM }, + {22, true, "\020" "braintreegateway" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\021" "braintreepayments" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {27, false, "\003" "www" "\021" "braintreepayments" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\022" "emailprivacytester" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "tor2web" "\003" "org", false, kTor2webPins, DOMAIN_TOR2WEB_ORG }, + {25, true, "\010" "business" "\007" "medbank" "\003" "com" "\002" "mt", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\005" "arivo" "\003" "com" "\002" "br", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\003" "www" "\013" "apollo-auto" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\003" "www" "\005" "cueup" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\005" "jitsi" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\003" "www" "\005" "jitsi" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010" "download" "\005" "jitsi" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {8, true, "\003" "sol" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010" "irccloud" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003" "www" "\010" "irccloud" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\005" "alpha" "\010" "irccloud" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "passwd" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "browserid" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\005" "login" "\007" "persona" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "neonisi" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "www" "\007" "neonisi" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\005" "shops" "\007" "neonisi" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "piratenlogin" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "howrandom" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\010" "intercom" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\003" "api" "\010" "intercom" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\003" "www" "\010" "intercom" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\010" "fatzebra" "\003" "com" "\002" "au", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\007" "csawctf" "\004" "poly" "\003" "edu", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "makeyourlaws" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, false, "\003" "www" "\014" "makeyourlaws" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\003" "iop" "\006" "intuit" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010" "surfeasy" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003" "www" "\010" "surfeasy" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "packagist" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\003" "www" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "mylookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003" "www" "\011" "mylookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\002" "dm" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\010" "business" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\004" "blog" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "faq" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\010" "platform" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\005" "email" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "app" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "api" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\011" "keymaster" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\011" "discovery" "\007" "lookout" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "mobilethreat" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\023" "mobilethreatnetwork" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "itriskltd" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "stocktrade" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\011" "openshift" "\006" "redhat" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\014" "therapynotes" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, false, "\003" "www" "\014" "therapynotes" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\003" "wiz" "\003" "biz", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\002" "my" "\006" "onlime" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\007" "webmail" "\006" "onlime" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\003" "crm" "\006" "onlime" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\003" "www" "\003" "gov" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "silentcircle" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "silentcircle" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\015" "serverdensity" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\002" "my" "\010" "alfresco" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\007" "webmail" "\010" "gigahost" "\002" "dk", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "paymill" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "paymill" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "gocardless" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "espra" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "zoo24" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\004" "mega" "\002" "co" "\002" "nz", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\003" "api" "\004" "mega" "\002" "co" "\002" "nz", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "lockify" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\010" "writeapp" "\002" "me", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\010" "bugzilla" "\007" "mozilla" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, true, "\007" "members" "\020" "nearlyfreespeech" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003" "ssl" "\011" "panoramio" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "kiwiirc" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "pay" "\010" "gigahost" "\002" "dk", true, kNoPins, DOMAIN_NOT_PINNED }, + {27, true, "\015" "controlcenter" "\010" "gigahost" "\002" "dk", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "simple" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003" "www" "\006" "simple" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\002" "fj" "\006" "simple" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003" "api" "\006" "simple" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004" "bank" "\006" "simple" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "bassh" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\004" "sah3" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, false, "\003" "grc" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\003" "www" "\003" "grc" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003" "www" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\007" "manager" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\004" "blog" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\007" "library" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\005" "forum" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\001" "p" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\005" "paste" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\010" "pastebin" "\006" "linode" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\017" "inertianetworks" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010" "carezone" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "conformal" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "cyphertite" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "logotype" "\002" "se", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\004" "bccx" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "launchkey" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\010" "carlolly" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\003" "www" "\013" "cyveillance" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\004" "blog" "\013" "cyveillance" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "whonix" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "shodan" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\015" "rapidresearch" "\002" "me", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "surkatty" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\017" "securityheaders" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "haste" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "mudcrab" "\002" "us", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "mediacru" "\002" "sh", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "lolicore" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\007" "cloudns" "\003" "com" "\002" "au", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\005" "oplop" "\007" "appspot" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "bcrook" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004" "wiki" "\006" "python" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, false, "\004" "lumi" "\002" "do", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\020" "appseccalifornia" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "crowdcurity" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\013" "saturngames" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\021" "strongest-privacy" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\011" "ecosystem" "\011" "atlassian" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\002" "id" "\011" "atlassian" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "bitbucket" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "cupcake" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "cupcake" "\002" "is", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "tent" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "cybozu" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "davidlyness" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "medium" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\007" "liberty" "\007" "lavabit" "\003" "com", true, kLavabitPins, DOMAIN_LAVABIT_COM }, + {16, true, "\012" "getlantern" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "kinsights" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\007" "simbolo" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003" "www" "\007" "simbolo" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\012" "zenpayroll" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\003" "www" "\012" "zenpayroll" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\003" "get" "\012" "zenpayroll" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\006" "errors" "\012" "zenpayroll" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\006" "manage" "\012" "zenpayroll" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\016" "gernert-server" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\010" "skydrive" "\004" "live" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\011" "lifeguard" "\005" "aecom" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\004" "data" "\003" "qld" "\003" "gov" "\002" "au", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, false, "\014" "publications" "\003" "qld" "\003" "gov" "\002" "au", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\002" "go" "\004" "xero" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\005" "login" "\004" "xero" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\002" "my" "\004" "xero" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\007" "payroll" "\004" "xero" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\002" "in" "\004" "xero" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\003" "api" "\004" "xero" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\003" "eff" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "mail" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010" "passport" "\006" "yandex" "\002" "ru", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\010" "passport" "\006" "yandex" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010" "passport" "\006" "yandex" "\002" "ua", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010" "passport" "\006" "yandex" "\002" "by", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\010" "passport" "\006" "yandex" "\002" "kz", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\010" "passport" "\006" "yandex" "\003" "com" "\002" "tr", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "mnsure" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\010" "getcloak" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003" "www" "\010" "getcloak" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\020" "matteomarescotti" "\004" "name", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\003" "www" "\011" "heliosnet" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "opsmate" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "www" "\007" "opsmate" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "f-droid" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003" "www" "\010" "evernote" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\003" "app" "\010" "yinxiang" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "neilwynne" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, false, "\016" "calyxinstitute" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\003" "www" "\016" "calyxinstitute" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "blacklane" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "boxcryptor" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, false, "\004" "aclu" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\003" "www" "\004" "aclu" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "prodpad" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "mailbox" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "roddis" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\003" "www" "\006" "roddis" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "fiken" "\002" "no", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "fairbill" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "nexth" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "nexth" "\002" "us", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "nexth" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "souyar" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "souyar" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "souyar" "\002" "us", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003" "www" "\007" "banking" "\002" "co" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, false, "\003" "mbp" "\007" "banking" "\002" "co" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "feedbin" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "heha" "\002" "co", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "passwordbox" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "python" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004" "pypi" "\006" "python" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\003" "www" "\006" "python" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004" "docs" "\006" "python" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "encircleapp" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\010" "onedrive" "\004" "live" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "onedrive" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016" "keepersecurity" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "keeperapp" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "donmez" "\002" "ws", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\010" "activiti" "\010" "alfresco" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "cloudcert" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "seifried" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\005" "wepay" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\003" "www" "\005" "wepay" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, false, "\006" "static" "\005" "wepay" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\005" "stage" "\005" "wepay" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "vmoagents" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "adsfund" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, false, "\004" "pult" "\002" "co", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "dillonkorman" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "edmodo" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\003" "www" "\013" "eternalgoth" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\003" "app" "\007" "manilla" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "harvestapp" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "anycoin" "\002" "me", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "noexpect" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\006" "airbnb" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\003" "www" "\006" "airbnb" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, false, "\004" "usaa" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, false, "\003" "www" "\004" "usaa" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, false, "\006" "mobile" "\004" "usaa" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "subrosa" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, false, "\011" "detectify" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "crbug" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016" "manageprojects" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, false, "\017" "tinfoilsecurity" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, false, "\003" "www" "\017" "tinfoilsecurity" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\006" "imouto" "\002" "my", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "vocaloid" "\002" "my", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\006" "sakaki" "\005" "anime" "\002" "my", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\007" "reviews" "\005" "anime" "\002" "my", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\004" "miku" "\007" "hatsune" "\002" "my", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\012" "webcollect" "\003" "org" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, false, "\003" "www" "\016" "capitainetrain" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\010" "accounts" "\007" "firefox" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {6, true, "\001" "z" "\002" "ai", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "wildbee" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\006" "portal" "\005" "tirol" "\002" "gv" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "dropbox" "\003" "com", true, kDropboxPins, DOMAIN_DROPBOX_COM }, + {17, true, "\003" "www" "\007" "dropbox" "\003" "com", true, kDropboxPins, DOMAIN_DROPBOX_COM }, + {18, true, "\012" "code-poets" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, false, "\007" "jackyyf" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "flynn" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "hackerone" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, true, "\026" "hackerone-user-content" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, false, "\017" "gamesdepartment" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {27, true, "\003" "www" "\017" "gamesdepartment" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, false, "\012" "schokokeks" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\003" "www" "\012" "schokokeks" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\006" "config" "\012" "schokokeks" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, true, "\007" "webmail" "\012" "schokokeks" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {8, true, "\003" "mwe" "\002" "st", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "ub3rk1tten" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "addvocate" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "alexsexton" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "azprep" "\002" "us", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "beneathvt" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "cloudup" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016" "cryptopartyatx" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "cybershambles" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {7, true, "\002" "ed" "\002" "gs", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\017" "forewordreviews" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\020" "giacomopelagatti" "\002" "it", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "helichat" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\022" "hostinginnederland" "\002" "nl", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "isitchristmas" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "konklone" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "koop-bremen" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "kura" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {34, true, "\035" "markusueberallassetmanagement" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "mikewest" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "miskatonic" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "optimus" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "oversight" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\007" "picksin" "\004" "club", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, true, "\026" "pressfreedomfoundation" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\020" "projektzentrisch" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "rippleunion" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "robteix" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {8, true, "\003" "s-c" "\002" "se", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\017" "security-carpet" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "sherbers" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "tittelbach" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "tomfisher" "\002" "eu", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "wunderlist" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "zotero" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "adamkostecki" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "archlinux" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\017" "auf-feindgebiet" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "baruch" "\002" "me", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "bedeta" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "benjamins" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "bl4ckb0x" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "bl4ckb0x" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\010" "bl4ckb0x" "\004" "info", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "bl4ckb0x" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "bl4ckb0x" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\020" "blocksatz-medien" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\017" "conrad-kostecki" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "cube" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "datenkeks" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "derhil" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\024" "energy-drink-magazin" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, true, "\031" "ferienhaus-polchow-ruegen" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "fischer-its" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "freeshell" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "greensolid" "\003" "biz", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "hasilocke" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\015" "hausverbrauch" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "helpium" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "hex2013" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "honeytracks" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "ihrlotto" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "jonas-keidel" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "jonaswitmer" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "k-dev" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "kraken" "\002" "io", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\014" "lagerauftrag" "\004" "info", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "lavalite" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "loenshotel" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "loftboard" "\002" "eu", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "mondwandler" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\021" "mountainroseherbs" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "movlib" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\017" "musicgamegalaxy" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "mynigma" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\012" "nachsenden" "\004" "info", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "netzbit" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {8, true, "\003" "pdf" "\002" "yt", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016" "pierre-schmitz" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\015" "promecon-gmbh" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "prowhisky" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "pubkey" "\002" "is", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "qetesh" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "riccy" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "scrambl" "\002" "is", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "tageau" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "ukrainians" "\002" "ch", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\007" "viennan" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\020" "winhistory-forum" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "y-o-w" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "explodie" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "accelerated" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {8, true, "\003" "aie" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "baer" "\002" "im", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, true, "\027" "bayrisch-fuer-anfaenger" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "beastowner" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "beastowner" "\002" "li", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\023" "best-wedding-quotes" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "bitfactory" "\002" "ws", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "bohramt" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, true, "\030" "buddhistische-weisheiten" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "cartouche24" "\002" "eu", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "cartucce24" "\002" "it", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\016" "celltek-server" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\017" "clapping-rhymes" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {26, true, "\025" "die-besten-weisheiten" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "edyou" "\002" "eu", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "eurotramp" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "forodeespanol" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {23, true, "\022" "gemeinfreie-lieder" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "getdigitized" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "globuli-info" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "guphi" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, true, "\027" "guthabenkarten-billiger" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "haufschild" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {31, true, "\032" "hoerbuecher-und-hoerspiele" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\004" "iban" "\002" "is", true, kNoPins, DOMAIN_NOT_PINNED }, + {29, true, "\026" "irische-segenswuensche" "\004" "info", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "it-schwerin" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\021" "janus-engineering" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "jfreitag" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "julian-kipka" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "kardize24" "\002" "pl", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "kernel-error" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, true, "\027" "kinderbuecher-kostenlos" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\006" "kitsta" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "klatschreime" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, true, "\023" "kleidertauschpartys" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "koordinate" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\017" "lasst-uns-beten" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "lb-toner" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\024" "mandala-ausmalbilder" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\015" "mathiasbynens" "\002" "be", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\005" "klaxn" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\004" "mig5" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "netzpolitik" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {9, true, "\003" "npw" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "otakuworld" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "pajonzeck" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "rad-route" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "raiseyourflag" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "redports" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016" "reserve-online" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\015" "riesenmagnete" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "rosenkeller" "\003" "org", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\017" "salaervergleich" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "schwarzer" "\002" "it", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "secuvera" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "siammedia" "\002" "co", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\014" "simplystudio" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, true, "\031" "sprueche-zum-valentinstag" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {26, true, "\023" "sprueche-zur-geburt" "\004" "info", true, kNoPins, DOMAIN_NOT_PINNED }, + {26, true, "\025" "sprueche-zur-hochzeit" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {30, true, "\031" "sprueche-zur-konfirmation" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\012" "studydrive" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "supplies24" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "supplies24" "\002" "es", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\016" "tatort-fanpage" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "tektoria" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\017" "texte-zur-taufe" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "tinte24" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "tintenfix" "\003" "net", true, kNoPins, DOMAIN_NOT_PINNED }, + {28, true, "\027" "tipps-fuer-den-haushalt" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "toner24" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\007" "toner24" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "toner24" "\002" "es", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "toner24" "\002" "fr", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "toner24" "\002" "it", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "toner24" "\002" "nl", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "toner24" "\002" "pl", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "tonerdepot" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {13, true, "\010" "tonerjet" "\002" "at", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\010" "tonerjet" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "tonerklick" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "tonerkurier" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\011" "tonermaus" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\014" "tonermonster" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "tonex" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {10, true, "\005" "tonex" "\002" "nl", true, kNoPins, DOMAIN_NOT_PINNED }, + {18, true, "\013" "trauertexte" "\004" "info", true, kNoPins, DOMAIN_NOT_PINNED }, + {21, true, "\020" "unterfrankenclan" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\012" "webandmore" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {26, true, "\025" "welches-kinderfahrrad" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "apadvantage" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {22, true, "\021" "apn-einstellungen" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {19, true, "\015" "barcodeberlin" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\010" "certible" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {20, true, "\016" "data-abundance" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, true, "\007" "dedimax" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "hostix" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\011" "janoberst" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {14, true, "\006" "jelmer" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "jelmer" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, + {16, true, "\013" "munich-rage" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, true, "\006" "posteo" "\002" "de", true, kNoPins, DOMAIN_NOT_PINNED }, + {25, true, "\024" "stationary-traveller" "\002" "eu", true, kNoPins, DOMAIN_NOT_PINNED }, + {24, true, "\022" "thepaymentscompany" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {15, true, "\007" "xps2pdf" "\002" "co" "\002" "uk", true, kNoPins, DOMAIN_NOT_PINNED }, }; static const size_t kNumPreloadedSTS = ARRAYSIZE_UNSAFE(kPreloadedSTS); static const struct HSTSPreload kPreloadedSNISTS[] = { - {11, false, "\005gmail\003com", true, kGooglePins, DOMAIN_GMAIL_COM }, - {16, false, "\012googlemail\003com", true, kGooglePins, DOMAIN_GOOGLEMAIL_COM }, - {15, false, "\003www\005gmail\003com", true, kGooglePins, DOMAIN_GMAIL_COM }, - {20, false, "\003www\012googlemail\003com", true, kGooglePins, DOMAIN_GOOGLEMAIL_COM }, - {22, true, "\020google-analytics\003com", false, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM }, - {18, true, "\014googlegroups\003com", false, kGooglePins, DOMAIN_GOOGLEGROUPS_COM }, - {13, true, "\007mykolab\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {17, true, "\013semenkovich\003com", true, kNoPins, DOMAIN_NOT_PINNED }, - {8, false, "\003rme\002li", true, kNoPins, DOMAIN_NOT_PINNED }, - {12, false, "\003www\003rme\002li", true, kNoPins, DOMAIN_NOT_PINNED }, + {11, false, "\005" "gmail" "\003" "com", true, kGooglePins, DOMAIN_GMAIL_COM }, + {16, false, "\012" "googlemail" "\003" "com", true, kGooglePins, DOMAIN_GOOGLEMAIL_COM }, + {15, false, "\003" "www" "\005" "gmail" "\003" "com", true, kGooglePins, DOMAIN_GMAIL_COM }, + {20, false, "\003" "www" "\012" "googlemail" "\003" "com", true, kGooglePins, DOMAIN_GOOGLEMAIL_COM }, + {22, true, "\020" "google-analytics" "\003" "com", false, kGooglePins, DOMAIN_GOOGLE_ANALYTICS_COM }, + {18, true, "\014" "googlegroups" "\003" "com", false, kGooglePins, DOMAIN_GOOGLEGROUPS_COM }, + {13, true, "\007" "mykolab" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {17, true, "\013" "semenkovich" "\003" "com", true, kNoPins, DOMAIN_NOT_PINNED }, + {8, false, "\003" "rme" "\002" "li", true, kNoPins, DOMAIN_NOT_PINNED }, + {12, false, "\003" "www" "\003" "rme" "\002" "li", true, kNoPins, DOMAIN_NOT_PINNED }, }; static const size_t kNumPreloadedSNISTS = ARRAYSIZE_UNSAFE(kPreloadedSNISTS); diff --git a/net/http/transport_security_state_static.json b/net/http/transport_security_state_static.json index b700448a57895..65513847e651d 100644 --- a/net/http/transport_security_state_static.json +++ b/net/http/transport_security_state_static.json @@ -257,11 +257,13 @@ { "name": "ytimg.com", "include_subdomains": true, "pins": "google" }, { "name": "googleusercontent.com", "include_subdomains": true, "pins": "google" }, { "name": "youtube.com", "include_subdomains": true, "pins": "google" }, + { "name": "youtube-nocookie.com", "include_subdomains": true, "pins": "google" }, { "name": "googleapis.com", "include_subdomains": true, "pins": "google" }, { "name": "googleadservices.com", "include_subdomains": true, "pins": "google" }, { "name": "appspot.com", "include_subdomains": true, "pins": "google" }, { "name": "googlesyndication.com", "include_subdomains": true, "pins": "google" }, { "name": "doubleclick.net", "include_subdomains": true, "pins": "google" }, + { "name": "2mdn.net", "include_subdomains": true, "pins": "google" }, { "name": "gstatic.com", "include_subdomains": true, "pins": "google" }, { "name": "youtu.be", "include_subdomains": true, "pins": "google" }, { "name": "android.com", "include_subdomains": true, "pins": "google" }, @@ -829,6 +831,204 @@ { "name": "www.schokokeks.org", "include_subdomains": true, "mode": "force-https" }, { "name": "config.schokokeks.org", "include_subdomains": true, "mode": "force-https" }, { "name": "webmail.schokokeks.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "mwe.st", "include_subdomains": true, "mode": "force-https" }, + { "name": "ub3rk1tten.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "addvocate.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "alexsexton.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "azprep.us", "include_subdomains": true, "mode": "force-https" }, + { "name": "beneathvt.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "cloudup.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "cryptopartyatx.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "cybershambles.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ed.gs", "include_subdomains": true, "mode": "force-https" }, + { "name": "forewordreviews.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "giacomopelagatti.it", "include_subdomains": true, "mode": "force-https" }, + { "name": "helichat.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "hostinginnederland.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "isitchristmas.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "konklone.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "koop-bremen.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "kura.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "markusueberallassetmanagement.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "mikewest.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "miskatonic.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "optimus.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "oversight.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "picksin.club", "include_subdomains": true, "mode": "force-https" }, + { "name": "pressfreedomfoundation.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "projektzentrisch.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "rippleunion.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "robteix.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "s-c.se", "include_subdomains": true, "mode": "force-https" }, + { "name": "security-carpet.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "sherbers.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tittelbach.at", "include_subdomains": true, "mode": "force-https" }, + { "name": "tomfisher.eu", "include_subdomains": true, "mode": "force-https" }, + { "name": "wunderlist.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "zotero.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "adamkostecki.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "archlinux.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "auf-feindgebiet.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "baruch.me", "include_subdomains": true, "mode": "force-https" }, + { "name": "bedeta.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "benjamins.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "bl4ckb0x.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "bl4ckb0x.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "bl4ckb0x.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "bl4ckb0x.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "bl4ckb0x.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "blocksatz-medien.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "conrad-kostecki.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "cube.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "datenkeks.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "derhil.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "energy-drink-magazin.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "ferienhaus-polchow-ruegen.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "fischer-its.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "freeshell.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "greensolid.biz", "include_subdomains": true, "mode": "force-https" }, + { "name": "hasilocke.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "hausverbrauch.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "helpium.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "hex2013.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "honeytracks.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ihrlotto.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "jonas-keidel.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "jonaswitmer.ch", "include_subdomains": true, "mode": "force-https" }, + { "name": "k-dev.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "kraken.io", "include_subdomains": true, "mode": "force-https" }, + { "name": "lagerauftrag.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "lavalite.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "loenshotel.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "loftboard.eu", "include_subdomains": true, "mode": "force-https" }, + { "name": "mondwandler.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "mountainroseherbs.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "movlib.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "musicgamegalaxy.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "mynigma.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "nachsenden.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "netzbit.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "pdf.yt", "include_subdomains": true, "mode": "force-https" }, + { "name": "pierre-schmitz.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "promecon-gmbh.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "prowhisky.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "pubkey.is", "include_subdomains": true, "mode": "force-https" }, + { "name": "qetesh.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "riccy.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "scrambl.is", "include_subdomains": true, "mode": "force-https" }, + { "name": "tageau.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "ukrainians.ch", "include_subdomains": true, "mode": "force-https" }, + { "name": "viennan.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "winhistory-forum.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "y-o-w.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "explodie.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "accelerated.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "aie.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "baer.im", "include_subdomains": true, "mode": "force-https" }, + { "name": "bayrisch-fuer-anfaenger.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "beastowner.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "beastowner.li", "include_subdomains": true, "mode": "force-https" }, + { "name": "best-wedding-quotes.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "bitfactory.ws", "include_subdomains": true, "mode": "force-https" }, + { "name": "bohramt.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "buddhistische-weisheiten.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "cartouche24.eu", "include_subdomains": true, "mode": "force-https" }, + { "name": "cartucce24.it", "include_subdomains": true, "mode": "force-https" }, + { "name": "celltek-server.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "clapping-rhymes.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "die-besten-weisheiten.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "edyou.eu", "include_subdomains": true, "mode": "force-https" }, + { "name": "eurotramp.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "forodeespanol.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "gemeinfreie-lieder.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "getdigitized.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "globuli-info.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "guphi.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "guthabenkarten-billiger.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "haufschild.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "hoerbuecher-und-hoerspiele.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "iban.is", "include_subdomains": true, "mode": "force-https" }, + { "name": "irische-segenswuensche.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "it-schwerin.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "janus-engineering.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "jfreitag.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "julian-kipka.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "kardize24.pl", "include_subdomains": true, "mode": "force-https" }, + { "name": "kernel-error.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "kinderbuecher-kostenlos.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "kitsta.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "klatschreime.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "kleidertauschpartys.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "koordinate.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "lasst-uns-beten.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "lb-toner.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "mandala-ausmalbilder.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "mathiasbynens.be", "include_subdomains": true, "mode": "force-https" }, + { "name": "klaxn.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "mig5.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "netzpolitik.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "npw.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "otakuworld.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "pajonzeck.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "rad-route.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "raiseyourflag.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "redports.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "reserve-online.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "riesenmagnete.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "rosenkeller.org", "include_subdomains": true, "mode": "force-https" }, + { "name": "salaervergleich.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "schwarzer.it", "include_subdomains": true, "mode": "force-https" }, + { "name": "secuvera.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "siammedia.co", "include_subdomains": true, "mode": "force-https" }, + { "name": "simplystudio.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "sprueche-zum-valentinstag.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "sprueche-zur-geburt.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "sprueche-zur-hochzeit.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "sprueche-zur-konfirmation.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "studydrive.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "supplies24.at", "include_subdomains": true, "mode": "force-https" }, + { "name": "supplies24.es", "include_subdomains": true, "mode": "force-https" }, + { "name": "tatort-fanpage.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tektoria.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "texte-zur-taufe.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tinte24.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tintenfix.net", "include_subdomains": true, "mode": "force-https" }, + { "name": "tipps-fuer-den-haushalt.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.at", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.co.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.es", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.fr", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.it", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "toner24.pl", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonerdepot.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonerjet.at", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonerjet.co.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonerklick.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonerkurier.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonermaus.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonermonster.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonex.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "tonex.nl", "include_subdomains": true, "mode": "force-https" }, + { "name": "trauertexte.info", "include_subdomains": true, "mode": "force-https" }, + { "name": "unterfrankenclan.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "webandmore.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "welches-kinderfahrrad.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "apadvantage.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "apn-einstellungen.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "barcodeberlin.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "certible.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "data-abundance.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "dedimax.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "hostix.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "janoberst.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "jelmer.co.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "jelmer.uk", "include_subdomains": true, "mode": "force-https" }, + { "name": "munich-rage.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "posteo.de", "include_subdomains": true, "mode": "force-https" }, + { "name": "stationary-traveller.eu", "include_subdomains": true, "mode": "force-https" }, + { "name": "thepaymentscompany.com", "include_subdomains": true, "mode": "force-https" }, + { "name": "xps2pdf.co.uk", "include_subdomains": true, "mode": "force-https" }, // Entries that are only valid if the client supports SNI. { "name": "gmail.com", "mode": "force-https", "pins": "google", "snionly": true }, diff --git a/net/http/transport_security_state_unittest.cc b/net/http/transport_security_state_unittest.cc index 476ae94bd681c..dfbc7539e1a09 100644 --- a/net/http/transport_security_state_unittest.cc +++ b/net/http/transport_security_state_unittest.cc @@ -37,6 +37,7 @@ namespace net { class TransportSecurityStateTest : public testing::Test { + public: virtual void SetUp() { #if defined(USE_OPENSSL) crypto::EnsureOpenSSLInit(); @@ -45,6 +46,14 @@ class TransportSecurityStateTest : public testing::Test { #endif } + static void DisableStaticPins(TransportSecurityState* state) { + state->enable_static_pins_ = false; + } + + static void EnableStaticPins(TransportSecurityState* state) { + state->enable_static_pins_ = true; + } + protected: bool GetStaticDomainState(TransportSecurityState* state, const std::string& host, @@ -162,6 +171,27 @@ TEST_F(TransportSecurityStateTest, DeleteDynamicDataForHost) { EXPECT_FALSE(state.GetDynamicDomainState("yahoo.com", &domain_state)); } +TEST_F(TransportSecurityStateTest, EnableStaticPins) { + TransportSecurityState state; + TransportSecurityState::DomainState domain_state; + + EnableStaticPins(&state); + + EXPECT_TRUE( + state.GetStaticDomainState("chrome.google.com", true, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); +} + +TEST_F(TransportSecurityStateTest, DisableStaticPins) { + TransportSecurityState state; + TransportSecurityState::DomainState domain_state; + + DisableStaticPins(&state); + EXPECT_TRUE( + state.GetStaticDomainState("chrome.google.com", true, &domain_state)); + EXPECT_TRUE(domain_state.pkp.spki_hashes.empty()); +} + TEST_F(TransportSecurityStateTest, IsPreloaded) { const std::string paypal = "paypal.com"; const std::string www_paypal = "www.paypal.com"; @@ -177,7 +207,6 @@ TEST_F(TransportSecurityStateTest, IsPreloaded) { EXPECT_TRUE(GetStaticDomainState(&state, paypal, true, &domain_state)); EXPECT_TRUE(GetStaticDomainState(&state, www_paypal, true, &domain_state)); EXPECT_FALSE(domain_state.sts.include_subdomains); - EXPECT_FALSE(domain_state.pkp.include_subdomains); EXPECT_FALSE(GetStaticDomainState(&state, a_www_paypal, true, &domain_state)); EXPECT_FALSE(GetStaticDomainState(&state, abc_paypal, true, &domain_state)); EXPECT_FALSE(GetStaticDomainState(&state, example, true, &domain_state)); @@ -214,6 +243,7 @@ static bool HasStaticState(const char* hostname) { static bool HasStaticPublicKeyPins(const char* hostname, bool sni_enabled) { TransportSecurityState state; + TransportSecurityStateTest::EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; if (!state.GetStaticDomainState(hostname, sni_enabled, &domain_state)) return false; @@ -227,6 +257,7 @@ static bool HasStaticPublicKeyPins(const char* hostname) { static bool OnlyPinningInStaticState(const char* hostname) { TransportSecurityState state; + TransportSecurityStateTest::EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; if (!state.GetStaticDomainState(hostname, true /* SNI ok */, &domain_state)) return false; @@ -285,24 +316,6 @@ TEST_F(TransportSecurityStateTest, Preloaded) { EXPECT_FALSE(HasStaticState("m.gmail.com")); EXPECT_FALSE(HasStaticState("m.googlemail.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.google.com")); - EXPECT_TRUE(OnlyPinningInStaticState("foo.google.com")); - EXPECT_TRUE(OnlyPinningInStaticState("google.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.youtube.com")); - EXPECT_TRUE(OnlyPinningInStaticState("youtube.com")); - EXPECT_TRUE(OnlyPinningInStaticState("i.ytimg.com")); - EXPECT_TRUE(OnlyPinningInStaticState("ytimg.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googleusercontent.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.googleusercontent.com")); - EXPECT_TRUE(OnlyPinningInStaticState("www.google-analytics.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googleapis.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googleadservices.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googlecode.com")); - EXPECT_TRUE(OnlyPinningInStaticState("appspot.com")); - EXPECT_TRUE(OnlyPinningInStaticState("googlesyndication.com")); - EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net")); - EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com")); - // Tests for domains that don't work without SNI. EXPECT_FALSE(state.GetStaticDomainState("gmail.com", false, &domain_state)); EXPECT_FALSE( @@ -407,28 +420,12 @@ TEST_F(TransportSecurityStateTest, Preloaded) { EXPECT_TRUE(StaticShouldRedirect("www.dropcam.com")); EXPECT_FALSE(HasStaticState("foo.dropcam.com")); - EXPECT_TRUE( - state.GetStaticDomainState("torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("www.torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("check.torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); - EXPECT_TRUE( - state.GetStaticDomainState("blog.torproject.org", false, &domain_state)); - EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); EXPECT_TRUE(StaticShouldRedirect("ebanking.indovinabank.com.vn")); EXPECT_TRUE(StaticShouldRedirect("foo.ebanking.indovinabank.com.vn")); EXPECT_TRUE(StaticShouldRedirect("epoxate.com")); EXPECT_FALSE(HasStaticState("foo.epoxate.com")); - EXPECT_TRUE(HasStaticPublicKeyPins("torproject.org")); - EXPECT_TRUE(HasStaticPublicKeyPins("www.torproject.org")); - EXPECT_TRUE(HasStaticPublicKeyPins("check.torproject.org")); - EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org")); EXPECT_FALSE(HasStaticState("foo.torproject.org")); EXPECT_TRUE(StaticShouldRedirect("www.moneybookers.com")); @@ -478,6 +475,57 @@ TEST_F(TransportSecurityStateTest, Preloaded) { EXPECT_TRUE(StaticShouldRedirect("crate.io")); EXPECT_TRUE(StaticShouldRedirect("foo.crate.io")); +} + +TEST_F(TransportSecurityStateTest, PreloadedPins) { + TransportSecurityState state; + EnableStaticPins(&state); + TransportSecurityState::DomainState domain_state; + + // We do more extensive checks for the first domain. + EXPECT_TRUE( + state.GetStaticDomainState("www.paypal.com", true, &domain_state)); + EXPECT_EQ(domain_state.sts.upgrade_mode, + TransportSecurityState::DomainState::MODE_FORCE_HTTPS); + EXPECT_FALSE(domain_state.sts.include_subdomains); + EXPECT_FALSE(domain_state.pkp.include_subdomains); + + EXPECT_TRUE(OnlyPinningInStaticState("www.google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("foo.google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("google.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.youtube.com")); + EXPECT_TRUE(OnlyPinningInStaticState("youtube.com")); + EXPECT_TRUE(OnlyPinningInStaticState("i.ytimg.com")); + EXPECT_TRUE(OnlyPinningInStaticState("ytimg.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleusercontent.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.googleusercontent.com")); + EXPECT_TRUE(OnlyPinningInStaticState("www.google-analytics.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleapis.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googleadservices.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googlecode.com")); + EXPECT_TRUE(OnlyPinningInStaticState("appspot.com")); + EXPECT_TRUE(OnlyPinningInStaticState("googlesyndication.com")); + EXPECT_TRUE(OnlyPinningInStaticState("doubleclick.net")); + EXPECT_TRUE(OnlyPinningInStaticState("googlegroups.com")); + + EXPECT_TRUE(HasStaticPublicKeyPins("torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("www.torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("check.torproject.org")); + EXPECT_TRUE(HasStaticPublicKeyPins("blog.torproject.org")); + EXPECT_FALSE(HasStaticState("foo.torproject.org")); + + EXPECT_TRUE( + state.GetStaticDomainState("torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("www.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("check.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); + EXPECT_TRUE( + state.GetStaticDomainState("blog.torproject.org", false, &domain_state)); + EXPECT_FALSE(domain_state.pkp.spki_hashes.empty()); EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); } @@ -495,6 +543,7 @@ TEST_F(TransportSecurityStateTest, LongNames) { TEST_F(TransportSecurityStateTest, BuiltinCertPins) { TransportSecurityState state; + EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; EXPECT_TRUE( @@ -534,7 +583,6 @@ TEST_F(TransportSecurityStateTest, BuiltinCertPins) { EXPECT_TRUE(HasStaticPublicKeyPins("ssl.google-analytics.com")); EXPECT_TRUE(HasStaticPublicKeyPins("www.googleplex.com")); - // Disabled in order to help track down pinning failures --agl EXPECT_TRUE(HasStaticPublicKeyPins("twitter.com")); EXPECT_FALSE(HasStaticPublicKeyPins("foo.twitter.com")); EXPECT_TRUE(HasStaticPublicKeyPins("www.twitter.com")); @@ -585,6 +633,8 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { } TransportSecurityState state; + EnableStaticPins(&state); + TransportSecurityState::DomainState domain_state; EXPECT_TRUE( state.GetStaticDomainState("blog.torproject.org", true, &domain_state)); @@ -597,6 +647,7 @@ TEST_F(TransportSecurityStateTest, PinValidationWithoutRejectedCerts) { TEST_F(TransportSecurityStateTest, OptionalHSTSCertPins) { TransportSecurityState state; + EnableStaticPins(&state); TransportSecurityState::DomainState domain_state; EXPECT_FALSE(StaticShouldRedirect("www.google-analytics.com")); diff --git a/net/net.gypi b/net/net.gypi index bb9f016adf6be..b85f57bea9ac1 100644 --- a/net/net.gypi +++ b/net/net.gypi @@ -759,6 +759,8 @@ 'quic/congestion_control/tcp_receiver.h', 'quic/congestion_control/time_loss_algorithm.cc', 'quic/congestion_control/time_loss_algorithm.h', + 'quic/congestion_control/timestamp_receiver.cc', + 'quic/congestion_control/timestamp_receiver.h', 'quic/crypto/aead_base_decrypter.h', 'quic/crypto/aead_base_decrypter_nss.cc', 'quic/crypto/aead_base_decrypter_openssl.cc', @@ -1120,6 +1122,8 @@ 'url_request/ftp_protocol_handler.cc', 'url_request/ftp_protocol_handler.h', 'url_request/http_user_agent_settings.h', + 'url_request/redirect_info.cc', + 'url_request/redirect_info.h', 'url_request/static_http_user_agent_settings.cc', 'url_request/static_http_user_agent_settings.h', 'url_request/url_fetcher.cc', @@ -1436,6 +1440,7 @@ 'quic/congestion_control/tcp_loss_algorithm_test.cc', 'quic/congestion_control/tcp_receiver_test.cc', 'quic/congestion_control/time_loss_algorithm_test.cc', + 'quic/congestion_control/timestamp_receiver_test.cc', 'quic/crypto/aes_128_gcm_12_decrypter_test.cc', 'quic/crypto/aes_128_gcm_12_encrypter_test.cc', 'quic/crypto/cert_compressor_test.cc', diff --git a/net/ocsp/nss_ocsp.cc b/net/ocsp/nss_ocsp.cc index 33d1933f348eb..7c719487b49d4 100644 --- a/net/ocsp/nss_ocsp.cc +++ b/net/ocsp/nss_ocsp.cc @@ -37,6 +37,7 @@ #include "net/base/upload_data_stream.h" #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "url/gurl.h" @@ -283,12 +284,12 @@ class OCSPRequestSession } virtual void OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE { DCHECK_EQ(request, request_); DCHECK_EQ(base::MessageLoopForIO::current(), io_loop_); - if (!new_url.SchemeIs("http")) { + if (!redirect_info.new_url.SchemeIs("http")) { // Prevent redirects to non-HTTP schemes, including HTTPS. This matches // the initial check in OCSPServerSession::CreateRequest(). CancelURLRequest(); diff --git a/net/proxy/proxy_info.cc b/net/proxy/proxy_info.cc index b6fcc86767f14..96ae3ce92ed2a 100644 --- a/net/proxy/proxy_info.cc +++ b/net/proxy/proxy_info.cc @@ -63,8 +63,8 @@ std::string ProxyInfo::ToPacString() const { return proxy_list_.ToPacString(); } -bool ProxyInfo::Fallback(const BoundNetLog& net_log) { - return proxy_list_.Fallback(&proxy_retry_info_, net_log); +bool ProxyInfo::Fallback(int net_error, const BoundNetLog& net_log) { + return proxy_list_.Fallback(&proxy_retry_info_, net_error, net_log); } void ProxyInfo::DeprioritizeBadProxies( diff --git a/net/proxy/proxy_info.h b/net/proxy/proxy_info.h index 336cb3a58881c..1e4fdedc11009 100644 --- a/net/proxy/proxy_info.h +++ b/net/proxy/proxy_info.h @@ -115,9 +115,12 @@ class NET_EXPORT ProxyInfo { // See description in ProxyList::ToPacString(). std::string ToPacString() const; - // Marks the current proxy as bad. Returns true if there is another proxy + // Marks the current proxy as bad. |net_error| should contain the network + // error encountered when this proxy was tried, if any. If this fallback + // is not because of a network error, then |OK| should be passed in (eg. for + // reasons such as local policy). Returns true if there is another proxy is // available to try in proxy list_. - bool Fallback(const BoundNetLog& net_log); + bool Fallback(int net_error, const BoundNetLog& net_log); // De-prioritizes the proxies that we have cached as not working, by moving // them to the end of the proxy list. diff --git a/net/proxy/proxy_info_unittest.cc b/net/proxy/proxy_info_unittest.cc index 377cff3438a5a..165283d55c6d8 100644 --- a/net/proxy/proxy_info_unittest.cc +++ b/net/proxy/proxy_info_unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "net/base/net_errors.h" #include "net/proxy/proxy_info.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,7 +33,7 @@ TEST(ProxyInfoTest, ProxyInfoIsDirectOnly) { EXPECT_FALSE(info.is_direct()); EXPECT_FALSE(info.is_direct_only()); // After falling back to direct, we shouldn't consider it DIRECT only. - EXPECT_TRUE(info.Fallback(BoundNetLog())); + EXPECT_TRUE(info.Fallback(ERR_PROXY_CONNECTION_FAILED, BoundNetLog())); EXPECT_TRUE(info.is_direct()); EXPECT_FALSE(info.is_direct_only()); } diff --git a/net/proxy/proxy_list.cc b/net/proxy/proxy_list.cc index 2adea29fba2cf..4dc46267d05f3 100644 --- a/net/proxy/proxy_list.cc +++ b/net/proxy/proxy_list.cc @@ -146,6 +146,7 @@ base::ListValue* ProxyList::ToValue() const { } bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info, + int net_error, const BoundNetLog& net_log) { // TODO(eroman): It would be good if instead of removing failed proxies @@ -171,6 +172,7 @@ bool ProxyList::Fallback(ProxyRetryInfoMap* proxy_retry_info, TimeDelta::FromMinutes(5), true, ProxyServer(), + net_error, net_log); // Remove this proxy from our list. @@ -182,6 +184,7 @@ void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info, base::TimeDelta retry_delay, bool try_while_bad, const ProxyServer& proxy_to_retry, + int net_error, const BoundNetLog& net_log) const { // Mark this proxy as bad. std::string proxy_key = proxy_to_retry.ToURI(); @@ -195,6 +198,7 @@ void ProxyList::AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info, retry_info.current_delay = retry_delay; retry_info.bad_until = TimeTicks().Now() + retry_info.current_delay; retry_info.try_while_bad = try_while_bad; + retry_info.net_error = net_error; (*proxy_retry_info)[proxy_key] = retry_info; } net_log.AddEvent(NetLog::TYPE_PROXY_LIST_FALLBACK, @@ -206,6 +210,7 @@ void ProxyList::UpdateRetryInfoOnFallback( base::TimeDelta retry_delay, bool reconsider, const ProxyServer& another_proxy_to_bypass, + int net_error, const BoundNetLog& net_log) const { DCHECK(retry_delay != base::TimeDelta()); @@ -215,14 +220,22 @@ void ProxyList::UpdateRetryInfoOnFallback( } if (!proxies_[0].is_direct()) { - AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider, proxies_[0], + AddProxyToRetryList(proxy_retry_info, + retry_delay, + reconsider, + proxies_[0], + net_error, net_log); // If an additional proxy to bypass is specified, add it to the retry map // as well. if (another_proxy_to_bypass.is_valid()) { - AddProxyToRetryList(proxy_retry_info, retry_delay, reconsider, - another_proxy_to_bypass, net_log); + AddProxyToRetryList(proxy_retry_info, + retry_delay, + reconsider, + another_proxy_to_bypass, + net_error, + net_log); } } } diff --git a/net/proxy/proxy_list.h b/net/proxy/proxy_list.h index ac9635a977acb..d57743e2df5ab 100644 --- a/net/proxy/proxy_list.h +++ b/net/proxy/proxy_list.h @@ -78,10 +78,14 @@ class NET_EXPORT_PRIVATE ProxyList { // Returns a serialized value for the list. The caller takes ownership of it. base::ListValue* ToValue() const; - // Marks the current proxy server as bad and deletes it from the list. The - // list of known bad proxies is given by proxy_retry_info. Returns true if - // there is another server available in the list. + // Marks the current proxy server as bad and deletes it from the list. The + // list of known bad proxies is given by |proxy_retry_info|. |net_error| + // should contain the network error encountered when this proxy was tried, if + // any. If this fallback is not because of a network error, then |OK| should + // be passed in (eg. for reasons such as local policy). Returns true if there + // is another server available in the list. bool Fallback(ProxyRetryInfoMap* proxy_retry_info, + int net_error, const BoundNetLog& net_log); // Updates |proxy_retry_info| to indicate that the first proxy in the list @@ -90,22 +94,28 @@ class NET_EXPORT_PRIVATE ProxyList { // retry after |retry_delay| if positive, and will use the default proxy retry // duration otherwise. It may reconsider the proxy beforehand if |reconsider| // is true. Additionally updates |proxy_retry_info| with - // |another_proxy_to_bypass| if non-empty. + // |another_proxy_to_bypass| if non-empty. |net_error| should contain the + // network error countered when this proxy was tried, or OK if the proxy retry + // info is being updated for a non-network related reason (e.g. local policy). void UpdateRetryInfoOnFallback( ProxyRetryInfoMap* proxy_retry_info, base::TimeDelta retry_delay, bool reconsider, const ProxyServer& another_proxy_to_bypass, + int net_error, const BoundNetLog& net_log) const; private: // Updates |proxy_retry_info| to indicate that the |proxy_to_retry| in // |proxies_| is bad for |retry_delay|, but may be reconsidered earlier if - // |try_while_bad| is true. + // |try_while_bad| is true. |net_error| should contain the network error + // countered when this proxy was tried, or OK if the proxy retry info is + // being updated for a non-network related reason (e.g. local policy). void AddProxyToRetryList(ProxyRetryInfoMap* proxy_retry_info, base::TimeDelta retry_delay, bool try_while_bad, const ProxyServer& proxy_to_retry, + int net_error, const BoundNetLog& net_log) const; // List of proxies. diff --git a/net/proxy/proxy_list_unittest.cc b/net/proxy/proxy_list_unittest.cc index 9ccf9c4e4f795..23b823f916ea1 100644 --- a/net/proxy/proxy_list_unittest.cc +++ b/net/proxy/proxy_list_unittest.cc @@ -4,6 +4,7 @@ #include "net/proxy/proxy_list.h" +#include "net/base/net_errors.h" #include "net/base/net_log.h" #include "net/proxy/proxy_retry_info.h" #include "net/proxy/proxy_server.h" @@ -172,13 +173,38 @@ TEST(ProxyListTest, UpdateRetryInfoOnFallback) { ProxyList list; ProxyRetryInfoMap retry_info_map; BoundNetLog net_log; + ProxyServer proxy_server( + ProxyServer::FromURI("foopy1:80", ProxyServer::SCHEME_HTTP)); list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80"); list.UpdateRetryInfoOnFallback(&retry_info_map, base::TimeDelta::FromSeconds(60), true, - ProxyServer(), + proxy_server, + ERR_PROXY_CONNECTION_FAILED, + net_log); + EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80")); + EXPECT_EQ(ERR_PROXY_CONNECTION_FAILED, + retry_info_map[proxy_server.ToURI()].net_error); + EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80")); + EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80")); + } + // Retrying should put the first proxy on the retry list, even if there + // was no network error. + { + ProxyList list; + ProxyRetryInfoMap retry_info_map; + BoundNetLog net_log; + ProxyServer proxy_server( + ProxyServer::FromURI("foopy1:80", ProxyServer::SCHEME_HTTP)); + list.SetFromPacString("PROXY foopy1:80;PROXY foopy2:80;PROXY foopy3:80"); + list.UpdateRetryInfoOnFallback(&retry_info_map, + base::TimeDelta::FromSeconds(60), + true, + proxy_server, + OK, net_log); EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80")); + EXPECT_EQ(OK, retry_info_map[proxy_server.ToURI()].net_error); EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80")); EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80")); } @@ -195,8 +221,11 @@ TEST(ProxyListTest, UpdateRetryInfoOnFallback) { base::TimeDelta::FromSeconds(60), true, proxy_server, + ERR_NAME_RESOLUTION_FAILED, net_log); EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy1:80")); + EXPECT_EQ(ERR_NAME_RESOLUTION_FAILED, + retry_info_map[proxy_server.ToURI()].net_error); EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80")); EXPECT_TRUE(retry_info_map.end() != retry_info_map.find("foopy3:80")); } @@ -213,6 +242,7 @@ TEST(ProxyListTest, UpdateRetryInfoOnFallback) { base::TimeDelta::FromSeconds(60), true, proxy_server, + OK, net_log); EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy2:80")); EXPECT_TRUE(retry_info_map.end() == retry_info_map.find("foopy3:80")); diff --git a/net/proxy/proxy_retry_info.h b/net/proxy/proxy_retry_info.h index 0d073b9c2cda6..a36fcd9ccb80c 100644 --- a/net/proxy/proxy_retry_info.h +++ b/net/proxy/proxy_retry_info.h @@ -24,6 +24,11 @@ struct ProxyRetryInfo { // True if this proxy should be considered even if still bad. bool try_while_bad; + + // The network error received when this proxy failed, or |OK| if the proxy + // was added to the retry list for a non-network related reason. (e.g. local + // policy). + int net_error; }; // Map of proxy servers with the associated RetryInfo structures. diff --git a/net/proxy/proxy_server.cc b/net/proxy/proxy_server.cc index b0997e0a0fa8e..09c2538e559b5 100644 --- a/net/proxy/proxy_server.cc +++ b/net/proxy/proxy_server.cc @@ -219,27 +219,6 @@ ProxyServer::Scheme ProxyServer::GetSchemeFromURI(const std::string& scheme) { return GetSchemeFromURIInternal(scheme.begin(), scheme.end()); } -// TODO(bengr): Use |scheme_| to indicate that this is the data reduction proxy. -#if defined(SPDY_PROXY_AUTH_ORIGIN) -bool ProxyServer::isDataReductionProxy() const { - bool dev_host = false; -#if defined (DATA_REDUCTION_DEV_HOST) - dev_host = host_port_pair_.Equals( - HostPortPair::FromURL(GURL(DATA_REDUCTION_DEV_HOST))); -#endif - return dev_host || host_port_pair_.Equals( - HostPortPair::FromURL(GURL(SPDY_PROXY_AUTH_ORIGIN))); -} - -bool ProxyServer::isDataReductionProxyFallback() const { -#if defined(DATA_REDUCTION_FALLBACK_HOST) - return host_port_pair_.Equals( - HostPortPair::FromURL(GURL(DATA_REDUCTION_FALLBACK_HOST))); -#endif // defined(DATA_REDUCTION_FALLBACK_HOST) - return false; -} -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) - // static ProxyServer ProxyServer::FromSchemeHostAndPort( Scheme scheme, diff --git a/net/proxy/proxy_server.h b/net/proxy/proxy_server.h index 27b8b049e5091..1ff65367285ae 100644 --- a/net/proxy/proxy_server.h +++ b/net/proxy/proxy_server.h @@ -154,14 +154,6 @@ class NET_EXPORT ProxyServer { return host_port_pair_ < other.host_port_pair_; } -#if defined(SPDY_PROXY_AUTH_ORIGIN) - // Returns true if this proxy server is the data reduction proxy or its - // fallback, respectively, as configured in gyp. These functions will return - // false for data reduction proxy servers specified on the command line. - bool isDataReductionProxy() const; - bool isDataReductionProxyFallback() const; -#endif // defined(SPDY_PROXY_AUTH_ORIGIN) - private: // Creates a ProxyServer given a scheme, and host/port string. If parsing the // host/port string fails, the returned instance will be invalid. diff --git a/net/proxy/proxy_service.cc b/net/proxy/proxy_service.cc index a1d634a75843d..3b93bdefbd369 100644 --- a/net/proxy/proxy_service.cc +++ b/net/proxy/proxy_service.cc @@ -13,8 +13,6 @@ #include "base/memory/weak_ptr.h" #include "base/message_loop/message_loop.h" #include "base/message_loop/message_loop_proxy.h" -#include "base/metrics/histogram.h" -#include "base/metrics/sparse_histogram.h" #include "base/strings/string_util.h" #include "base/thread_task_runner_handle.h" #include "base/values.h" @@ -1197,6 +1195,7 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url, // want to re-run ResolveProxy in two cases: 1) we have a new config, or 2) a // direct connection failed and we never tried the current config. + DCHECK(result); bool re_resolve = result->config_id_ != config_.id(); if (re_resolve) { @@ -1207,23 +1206,12 @@ int ProxyService::ReconsiderProxyAfterError(const GURL& url, network_delegate, net_log); } -#if defined(SPDY_PROXY_AUTH_ORIGIN) - if (result->proxy_server().isDataReductionProxy()) { - RecordDataReductionProxyBypassInfo( - true, false, result->proxy_server(), NETWORK_ERROR); - RecordDataReductionProxyBypassOnNetworkError( - true, result->proxy_server(), net_error); - } else if (result->proxy_server().isDataReductionProxyFallback()) { - RecordDataReductionProxyBypassInfo( - false, false, result->proxy_server(), NETWORK_ERROR); - RecordDataReductionProxyBypassOnNetworkError( - false, result->proxy_server(), net_error); - } -#endif + DCHECK(!result->is_empty()); + ProxyServer bad_proxy = result->proxy_server(); // We don't have new proxy settings to try, try to fallback to the next proxy // in the list. - bool did_fallback = result->Fallback(net_log); + bool did_fallback = result->Fallback(net_error, net_log); // Return synchronous failure if there is nothing left to fall-back to. // TODO(eroman): This is a yucky API, clean it up. @@ -1235,9 +1223,11 @@ bool ProxyService::MarkProxiesAsBadUntil( base::TimeDelta retry_delay, const ProxyServer& another_bad_proxy, const BoundNetLog& net_log) { - result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_, retry_delay, + result.proxy_list_.UpdateRetryInfoOnFallback(&proxy_retry_info_, + retry_delay, false, another_bad_proxy, + OK, net_log); if (another_bad_proxy.is_valid()) return result.proxy_list_.size() > 2; @@ -1245,7 +1235,8 @@ bool ProxyService::MarkProxiesAsBadUntil( return result.proxy_list_.size() > 1; } -void ProxyService::ReportSuccess(const ProxyInfo& result) { +void ProxyService::ReportSuccess(const ProxyInfo& result, + NetworkDelegate* network_delegate) { DCHECK(CalledOnValidThread()); const ProxyRetryInfoMap& new_retry_info = result.proxy_retry_info(); @@ -1255,8 +1246,16 @@ void ProxyService::ReportSuccess(const ProxyInfo& result) { for (ProxyRetryInfoMap::const_iterator iter = new_retry_info.begin(); iter != new_retry_info.end(); ++iter) { ProxyRetryInfoMap::iterator existing = proxy_retry_info_.find(iter->first); - if (existing == proxy_retry_info_.end()) + if (existing == proxy_retry_info_.end()) { proxy_retry_info_[iter->first] = iter->second; + if (network_delegate) { + const ProxyServer& bad_proxy = + ProxyServer::FromURI(iter->first, ProxyServer::SCHEME_HTTP); + const ProxyRetryInfo& proxy_retry_info = iter->second; + network_delegate->NotifyProxyFallback(bad_proxy, + proxy_retry_info.net_error); + } + } else if (existing->second.bad_until < iter->second.bad_until) existing->second.bad_until = iter->second.bad_until; } @@ -1305,7 +1304,7 @@ int ProxyService::DidFinishResolvingProxy(const GURL& url, // Allow the network delegate to interpose on the resolution decision, // possibly modifying the ProxyInfo. if (network_delegate) - network_delegate->NotifyResolveProxy(url, load_flags, result); + network_delegate->NotifyResolveProxy(url, load_flags, *this, result); // When logging all events is enabled, dump the proxy list. if (net_log.IsLogging()) { @@ -1332,7 +1331,7 @@ int ProxyService::DidFinishResolvingProxy(const GURL& url, // Allow the network delegate to interpose on the resolution decision, // possibly modifying the ProxyInfo. if (network_delegate) - network_delegate->NotifyResolveProxy(url, load_flags, result); + network_delegate->NotifyResolveProxy(url, load_flags, *this, result); } else { result_code = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED; } @@ -1459,53 +1458,6 @@ scoped_ptr return scoped_ptr(new DefaultPollPolicy()); } -void ProxyService::RecordDataReductionProxyBypassInfo( - bool is_primary, - bool bypass_all, - const ProxyServer& proxy_server, - DataReductionProxyBypassType bypass_type) const { - // Only record UMA if the proxy isn't already on the retry list. - if (proxy_retry_info_.find(proxy_server.ToURI()) != proxy_retry_info_.end()) - return; - - if (bypass_all) { - if (is_primary) { - UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BlockTypePrimary", - bypass_type, BYPASS_EVENT_TYPE_MAX); - } else { - UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BlockTypeFallback", - bypass_type, BYPASS_EVENT_TYPE_MAX); - } - } else { - if (is_primary) { - UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BypassTypePrimary", - bypass_type, BYPASS_EVENT_TYPE_MAX); - } else { - UMA_HISTOGRAM_ENUMERATION("DataReductionProxy.BypassTypeFallback", - bypass_type, BYPASS_EVENT_TYPE_MAX); - } - } -} - -void ProxyService::RecordDataReductionProxyBypassOnNetworkError( - bool is_primary, - const ProxyServer& proxy_server, - int net_error) { - // Only record UMA if the proxy isn't already on the retry list. - if (proxy_retry_info_.find(proxy_server.ToURI()) != proxy_retry_info_.end()) - return; - - if (is_primary) { - UMA_HISTOGRAM_SPARSE_SLOWLY( - "DataReductionProxy.BypassOnNetworkErrorPrimary", - std::abs(net_error)); - return; - } - UMA_HISTOGRAM_SPARSE_SLOWLY( - "DataReductionProxy.BypassOnNetworkErrorFallback", - std::abs(net_error)); -} - void ProxyService::OnProxyConfigChanged( const ProxyConfig& config, ProxyConfigService::ConfigAvailability availability) { diff --git a/net/proxy/proxy_service.h b/net/proxy/proxy_service.h index baca9da875a8a..69aaf46c13b08 100644 --- a/net/proxy/proxy_service.h +++ b/net/proxy/proxy_service.h @@ -160,7 +160,10 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, // and will use the default proxy retry duration otherwise. Proxies marked as // bad will not be retried until |retry_delay| has passed. Returns true if // there will be at least one proxy remaining in the list after fallback and - // false otherwise. + // false otherwise. This method should be used to add proxies to the bad + // proxy list only for reasons other than a network error. If a proxy needs + // to be added to the bad proxy list because a network error was encountered + // when trying to connect to it, use |ReconsiderProxyAfterError|. bool MarkProxiesAsBadUntil(const ProxyInfo& results, base::TimeDelta retry_delay, const ProxyServer& another_bad_proxy, @@ -168,8 +171,10 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, // Called to report that the last proxy connection succeeded. If |proxy_info| // has a non empty proxy_retry_info map, the proxies that have been tried (and - // failed) for this request will be marked as bad. - void ReportSuccess(const ProxyInfo& proxy_info); + // failed) for this request will be marked as bad. |network_delegate| will + // be notified of any proxy fallbacks. + void ReportSuccess(const ProxyInfo& proxy_info, + NetworkDelegate* network_delegate); // Call this method with a non-null |pac_request| to cancel the PAC request. void CancelPacRequest(PacRequest* pac_request); @@ -273,65 +278,6 @@ class NET_EXPORT ProxyService : public NetworkChangeNotifier::IPAddressObserver, bool quick_check_enabled() const { return quick_check_enabled_; } - // Values of the UMA DataReductionProxy.BypassType{Primary|Fallback} - // and DataReductionProxy.BlockType{Primary|Fallback} histograms. - // This enum must remain synchronized with the enum of the same - // name in metrics/histograms/histograms.xml. - enum DataReductionProxyBypassType { - // Bypass due to explicit instruction for the current request. - // Not yet supported. - CURRENT_BYPASS = 0, - - // Bypass the proxy for less than one minute. - SHORT_BYPASS = 1, - - // Bypass the proxy for one to five minutes. - MEDIUM_BYPASS = 2, - - // Bypass the proxy for more than five minutes. - LONG_BYPASS = 3, - - // Bypass due to a 4xx missing via header. - MISSING_VIA_HEADER_4XX = 4, - - // Bypass due to other missing via header, excluding 4xx errors. - MISSING_VIA_HEADER_OTHER = 5, - - // Bypass due to 407 response from proxy without a challenge. - MALFORMED_407 = 6, - - // Bypass due to a 500 internal server error - STATUS_500_HTTP_INTERNAL_SERVER_ERROR = 7, - - // Bypass because the request URI was too long. - STATUS_502_HTTP_BAD_GATEWAY = 8, - - // Bypass due to a 503 response. - STATUS_503_HTTP_SERVICE_UNAVAILABLE = 9, - - // Bypass due to any network error. - NETWORK_ERROR = 10, - - // This must always be last. - BYPASS_EVENT_TYPE_MAX = 11 - }; - - // Records a |DataReductionProxyBypassEventType| or - // |DataReductionProxyBlockEventType| for either the data reduction - // proxy (|is_primary| is true) or the data reduction proxy fallback. - void RecordDataReductionProxyBypassInfo( - bool is_primary, - bool bypass_all, - const ProxyServer& proxy_server, - DataReductionProxyBypassType block_type) const; - - // Records a net error code that resulted in bypassing the data reduction - // proxy (|is_primary| is true) or the data reduction proxy fallback. - void RecordDataReductionProxyBypassOnNetworkError( - bool is_primary, - const ProxyServer& proxy_server, - int net_error); - private: FRIEND_TEST_ALL_PREFIXES(ProxyServiceTest, UpdateConfigAfterFailedAutodetect); FRIEND_TEST_ALL_PREFIXES(ProxyServiceTest, UpdateConfigFromPACToDirect); diff --git a/net/proxy/proxy_service_unittest.cc b/net/proxy/proxy_service_unittest.cc index 69ab4ce050ffd..a222710bd216b 100644 --- a/net/proxy/proxy_service_unittest.cc +++ b/net/proxy/proxy_service_unittest.cc @@ -161,12 +161,16 @@ class TestResolveProxyNetworkDelegate : public NetworkDelegate { TestResolveProxyNetworkDelegate() : on_resolve_proxy_called_(false), add_proxy_(false), - remove_proxy_(false) { + remove_proxy_(false), + proxy_service_(NULL) { } - virtual void OnResolveProxy( - const GURL& url, int load_flags, ProxyInfo* result) OVERRIDE { + virtual void OnResolveProxy(const GURL& url, + int load_flags, + const ProxyService& proxy_service, + ProxyInfo* result) OVERRIDE { on_resolve_proxy_called_ = true; + proxy_service_ = &proxy_service; DCHECK(!add_proxy_ || !remove_proxy_); if (add_proxy_) { result->UseNamedProxy("delegate_proxy.com"); @@ -187,10 +191,48 @@ class TestResolveProxyNetworkDelegate : public NetworkDelegate { remove_proxy_ = remove_proxy; } + const ProxyService* proxy_service() const { + return proxy_service_; + } + private: bool on_resolve_proxy_called_; bool add_proxy_; bool remove_proxy_; + const ProxyService* proxy_service_; +}; + +// A test network delegate that exercises the OnProxyFallback callback. +class TestProxyFallbackNetworkDelegate : public NetworkDelegate { + public: + TestProxyFallbackNetworkDelegate() + : on_proxy_fallback_called_(false), + proxy_fallback_net_error_(OK) { + } + + virtual void OnProxyFallback(const ProxyServer& proxy_server, + int net_error) OVERRIDE { + proxy_server_ = proxy_server; + proxy_fallback_net_error_ = net_error; + on_proxy_fallback_called_ = true; + } + + bool on_proxy_fallback_called() const { + return on_proxy_fallback_called_; + } + + const ProxyServer& proxy_server() const { + return proxy_server_; + } + + int proxy_fallback_net_error() const { + return proxy_fallback_net_error_; + } + + private: + bool on_proxy_fallback_called_; + ProxyServer proxy_server_; + int proxy_fallback_net_error_; }; } // namespace @@ -257,6 +299,7 @@ TEST_F(ProxyServiceTest, OnResolveProxyCallbackAddProxy) { url, net::LOAD_NORMAL, &info, callback.callback(), NULL, &delegate, log.bound()); EXPECT_TRUE(delegate.on_resolve_proxy_called()); + EXPECT_EQ(&service, delegate.proxy_service()); // Verify that the NetworkDelegate's behavior is stateless across // invocations of ResolveProxy. Start by having the callback add a proxy @@ -462,10 +505,12 @@ TEST_F(ProxyServiceTest, PAC_FailoverWithoutDirect) { // Now, imagine that connecting to foopy:8080 fails: there is nothing // left to fallback to, since our proxy list was NOT terminated by // DIRECT. + NetworkDelegate network_delegate; TestCompletionCallback callback2; + ProxyServer expected_proxy_server = info.proxy_server(); rv = service.ReconsiderProxyAfterError( url, net::LOAD_NORMAL, net::ERR_PROXY_CONNECTION_FAILED, - &info, callback2.callback(), NULL, NULL, BoundNetLog()); + &info, callback2.callback(), NULL, &network_delegate, BoundNetLog()); // ReconsiderProxyAfterError returns error indicating nothing left. EXPECT_EQ(ERR_FAILED, rv); EXPECT_TRUE(info.is_empty()); @@ -571,30 +616,34 @@ TEST_F(ProxyServiceTest, PAC_FailoverAfterDirect) { EXPECT_EQ("foobar:10", info.proxy_server().ToURI()); // Fallback 2. + NetworkDelegate network_delegate; + ProxyServer expected_proxy_server3 = info.proxy_server(); TestCompletionCallback callback3; rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL, net::ERR_PROXY_CONNECTION_FAILED, &info, callback3.callback(), NULL, - NULL, BoundNetLog()); + &network_delegate, BoundNetLog()); EXPECT_EQ(OK, rv); EXPECT_TRUE(info.is_direct()); // Fallback 3. + ProxyServer expected_proxy_server4 = info.proxy_server(); TestCompletionCallback callback4; rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL, net::ERR_PROXY_CONNECTION_FAILED, &info, callback4.callback(), NULL, - NULL, BoundNetLog()); + &network_delegate, BoundNetLog()); EXPECT_EQ(OK, rv); EXPECT_FALSE(info.is_direct()); EXPECT_EQ("foobar:20", info.proxy_server().ToURI()); // Fallback 4 -- Nothing to fall back to! + ProxyServer expected_proxy_server5 = info.proxy_server(); TestCompletionCallback callback5; rv = service.ReconsiderProxyAfterError(url, net::LOAD_NORMAL, net::ERR_PROXY_CONNECTION_FAILED, &info, callback5.callback(), NULL, - NULL, BoundNetLog()); + &network_delegate, BoundNetLog()); EXPECT_EQ(ERR_FAILED, rv); EXPECT_TRUE(info.is_empty()); } @@ -911,7 +960,11 @@ TEST_F(ProxyServiceTest, ProxyFallback) { EXPECT_EQ("foopy2:9090", info.proxy_server().ToURI()); // Report back that the second proxy worked. This will globally mark the // first proxy as bad. - service.ReportSuccess(info); + TestProxyFallbackNetworkDelegate test_delegate; + service.ReportSuccess(info, &test_delegate); + EXPECT_EQ("foopy1:8080", test_delegate.proxy_server().ToURI()); + EXPECT_EQ(net::ERR_PROXY_CONNECTION_FAILED, + test_delegate.proxy_fallback_net_error()); TestCompletionCallback callback3; rv = service.ResolveProxy( diff --git a/net/quic/congestion_control/hybrid_slow_start_test.cc b/net/quic/congestion_control/hybrid_slow_start_test.cc index 91c5d9f2d6529..bea5840ab793f 100644 --- a/net/quic/congestion_control/hybrid_slow_start_test.cc +++ b/net/quic/congestion_control/hybrid_slow_start_test.cc @@ -53,7 +53,7 @@ TEST_F(HybridSlowStartTest, Simple) { // TODO(ianswett): Add tests which more realistically invoke the methods, // simulating how actual acks arrive and packets are sent. TEST_F(HybridSlowStartTest, AckTrain) { - // At a typical RTT 60 ms, assuming that the inter arrival is 1 ms, + // At a typical RTT 60 ms, assuming that the inter arrival timestamp is 1 ms, // we expect to be able to send a burst of 30 packet before we trigger the // ack train detection. const int kMaxLoopCount = 5; diff --git a/net/quic/congestion_control/receive_algorithm_interface.cc b/net/quic/congestion_control/receive_algorithm_interface.cc index 8d1a39ce745f2..4f608580c7f99 100644 --- a/net/quic/congestion_control/receive_algorithm_interface.cc +++ b/net/quic/congestion_control/receive_algorithm_interface.cc @@ -5,6 +5,7 @@ #include "net/quic/congestion_control/receive_algorithm_interface.h" #include "net/quic/congestion_control/tcp_receiver.h" +#include "net/quic/congestion_control/timestamp_receiver.h" namespace net { @@ -14,9 +15,8 @@ ReceiveAlgorithmInterface* ReceiveAlgorithmInterface::Create( switch (type) { case kTCP: return new TcpReceiver(); - case kInterArrival: - LOG(DFATAL) << "InterArrivalSendAlgorithm no longer supported."; - return NULL; + case kTimestamp: + return new TimestampReceiver(); } return NULL; } diff --git a/net/quic/congestion_control/send_algorithm_simulator.cc b/net/quic/congestion_control/send_algorithm_simulator.cc index 36aa32163d935..33f6ba009f489 100644 --- a/net/quic/congestion_control/send_algorithm_simulator.cc +++ b/net/quic/congestion_control/send_algorithm_simulator.cc @@ -48,7 +48,8 @@ SendAlgorithmSimulator::SendAlgorithmSimulator( loss_correlation_(0), bandwidth_(bandwidth), rtt_(rtt), - buffer_size_(1000000) { + buffer_size_(1000000), + delayed_ack_timer_(QuicTime::Delta::FromMilliseconds(100)) { uint32 seed = base::RandInt(0, std::numeric_limits::max()); DVLOG(1) << "Seeding SendAlgorithmSimulator with " << seed; simple_random_.set_seed(seed); @@ -92,7 +93,8 @@ void SendAlgorithmSimulator::TransferBytes(QuicByteCount max_bytes, clock_->AdvanceTime(QuicTime::Delta::FromMilliseconds(100)); SendDataNow(&pending_transfers_.front()); } else if (ack_event.time_delta < send_event.time_delta) { - DVLOG(1) << "Handling ack, advancing time:" + DVLOG(1) << "Handling ack of largest observed:" + << ack_event.transfer->sender->next_acked << ", advancing time:" << ack_event.time_delta.ToMicroseconds() << "us"; // Ack data all the data up to ack time and lose any missing sequence // numbers. @@ -165,57 +167,78 @@ QuicTime::Delta SendAlgorithmSimulator::FindNextAcked(Transfer* transfer) { lose_next_ack_ = reverse_loss_rate_ * kuint64max > simple_random_.RandUint64(); } - bool two_acks_remaining = lose_next_ack_; - sender->next_acked = sender->last_acked; - bool packets_lost = false; + + QuicPacketSequenceNumber next_acked = sender->last_acked; + QuicTime::Delta next_ack_delay = + FindNextAck(transfer, sender->last_acked, &next_acked); + if (lose_next_ack_) { + next_ack_delay = FindNextAck(transfer, next_acked, &next_acked); + } + sender->next_acked = next_acked; + return next_ack_delay; +} + +QuicTime::Delta SendAlgorithmSimulator::FindNextAck( + const Transfer* transfer, + QuicPacketSequenceNumber last_acked, + QuicPacketSequenceNumber* next_acked) const { + *next_acked = last_acked; + QuicTime::Delta ack_delay = QuicTime::Delta::Infinite(); // Remove any packets that are simulated as lost. for (list::const_iterator it = sent_packets_.begin(); it != sent_packets_.end(); ++it) { if (transfer != it->transfer) { continue; } - + // Skip over any packets less than or equal to last_acked. + if (it->sequence_number <= last_acked) { + continue; + } // Lost packets don't trigger an ack. - if (it->ack_time == QuicTime::Zero()) { - packets_lost = true; + if (it->lost) { continue; } - // Buffer dropped packets are skipped automatically, but still end up - // being lost and cause acks to be sent immediately. - if (sender->next_acked < it->sequence_number - 1) { - packets_lost = true; + DCHECK_LT(*next_acked, it->sequence_number); + // Consider a delayed ack for the current next_acked. + if (ack_delay < it->ack_time.Subtract(clock_->Now())) { + break; } - DCHECK_LT(sender->next_acked, it->sequence_number); - sender->next_acked = it->sequence_number; - if (packets_lost || (sender->next_acked - sender->last_acked) % 2 == 0) { - if (two_acks_remaining) { - two_acks_remaining = false; - } else { - break; - } + *next_acked = it->sequence_number; + ack_delay = it->ack_time.Subtract(clock_->Now()); + if (HasRecentLostPackets(transfer, *next_acked) || + (*next_acked - last_acked) >= 2) { + break; } - } - // If the connection has no packets to be acked, return Infinite. - if (sender->next_acked == sender->last_acked) { - return QuicTime::Delta::Infinite(); + ack_delay = ack_delay.Add(delayed_ack_timer_); } - QuicTime::Delta ack_time = QuicTime::Delta::Infinite(); - for (list::const_iterator it = sent_packets_.begin(); - it != sent_packets_.end(); ++it) { - if (transfer == it->transfer && sender->next_acked == it->sequence_number) { - ack_time = it->ack_time.Subtract(clock_->Now()); - } - } - // If only one packet is acked, simulate a delayed ack. - if (!ack_time.IsInfinite() && transfer->bytes_in_flight == kPacketSize) { - ack_time = ack_time.Add(QuicTime::Delta::FromMilliseconds(100)); - } DVLOG(1) << "FindNextAcked found next_acked_:" << transfer->sender->next_acked << " last_acked:" << transfer->sender->last_acked - << " ack_time(ms):" << ack_time.ToMilliseconds(); - return ack_time; + << " ack_delay(ms):" << ack_delay.ToMilliseconds(); + return ack_delay; +} + +bool SendAlgorithmSimulator::HasRecentLostPackets( + const Transfer* transfer, QuicPacketSequenceNumber next_acked) const { + QuicPacketSequenceNumber last_packet = transfer->sender->last_acked; + for (list::const_iterator it = sent_packets_.begin(); + it != sent_packets_.end() && it->sequence_number < next_acked; ++it) { + if (transfer != it->transfer) { + continue; + } + // Lost packets don't trigger an ack. + if (it->lost) { + return true; + } + // Buffer dropped packets are skipped automatically, but still end up + // being lost and cause acks to be sent immediately. + if (it->sequence_number > last_packet + 1) { + return true; + } + last_packet = it->sequence_number; + } + return false; } void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { @@ -223,9 +246,13 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { DCHECK_LT(sender->last_acked, sender->next_acked); SendAlgorithmInterface::CongestionMap acked_packets; SendAlgorithmInterface::CongestionMap lost_packets; + DVLOG(1) << "Acking packets from:" << sender->last_acked + << " to " << sender->next_acked + << " bytes_in_flight:" << transfer->bytes_in_flight + << " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms"; // Some entries may be missing from the sent_packets_ array, if they were // dropped due to buffer overruns. - SentPacket largest_observed(0, QuicTime::Zero(), QuicTime::Zero(), NULL); + SentPacket largest_observed; list::iterator it = sent_packets_.begin(); while (sender->last_acked < sender->next_acked) { ++sender->last_acked; @@ -244,10 +271,10 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { lost_packets[sender->last_acked] = info; continue; } - if (it->ack_time.IsInitialized()) { - acked_packets[sender->last_acked] = info; - } else { + if (it->lost) { lost_packets[sender->last_acked] = info; + } else { + acked_packets[sender->last_acked] = info; } // This packet has been acked or lost, remove it from sent_packets_. largest_observed = *it; @@ -258,10 +285,12 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { DVLOG(1) << "Updating RTT from send_time:" << largest_observed.send_time.ToDebuggingValue() << " to ack_time:" << largest_observed.ack_time.ToDebuggingValue(); - sender->rtt_stats->UpdateRtt( - largest_observed.ack_time.Subtract(largest_observed.send_time), - QuicTime::Delta::Zero(), - clock_->Now()); + QuicTime::Delta measured_rtt = + largest_observed.ack_time.Subtract(largest_observed.send_time); + DCHECK_GE(measured_rtt.ToMicroseconds(), rtt_.ToMicroseconds()); + sender->rtt_stats->UpdateRtt(measured_rtt, + QuicTime::Delta::Zero(), + clock_->Now()); sender->send_algorithm->OnCongestionEvent( true, transfer->bytes_in_flight, acked_packets, lost_packets); DCHECK_LE(kPacketSize * (acked_packets.size() + lost_packets.size()), @@ -281,6 +310,8 @@ void SendAlgorithmSimulator::HandlePendingAck(Transfer* transfer) { sender->last_transfer_bandwidth = QuicBandwidth::FromBytesAndTimeDelta(transfer->num_bytes, transfer_time); + DCHECK_GE(bandwidth_.ToBitsPerSecond(), + sender->last_transfer_bandwidth.ToBitsPerSecond()); for (vector::iterator it = pending_transfers_.begin(); it != pending_transfers_.end(); ++it) { if (transfer == &(*it)) { @@ -295,7 +326,8 @@ void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) { Sender* sender = transfer->sender; ++sender->last_sent; DVLOG(1) << "Sending packet:" << sender->last_sent - << " bytes_in_flight:" << transfer->bytes_in_flight; + << " bytes_in_flight:" << transfer->bytes_in_flight + << " Now():" << (clock_->Now().ToDebuggingValue() / 1000) << "ms"; sender->send_algorithm->OnPacketSent( clock_->Now(), transfer->bytes_in_flight, sender->last_sent, kPacketSize, HAS_RETRANSMITTABLE_DATA); @@ -306,29 +338,25 @@ void SendAlgorithmSimulator::SendDataNow(Transfer* transfer) { bool packet_lost = forward_loss_rate_ * kuint64max > simple_random_.RandUint64(); // Handle correlated loss. - if (!sent_packets_.empty() && - !sent_packets_.back().ack_time.IsInitialized() && + if (!sent_packets_.empty() && sent_packets_.back().lost && loss_correlation_ * kuint64max > simple_random_.RandUint64()) { packet_lost = true; } DVLOG(1) << "losing packet:" << sender->last_sent << " due to random loss."; - QuicTime ack_time = clock_->Now().Add(rtt_); // If the number of bytes in flight are less than the bdp, there's // no buffering delay. Bytes lost from the buffer are not counted. QuicByteCount bdp = bandwidth_.ToBytesPerPeriod(rtt_); - if ((sent_packets_.size() + 1) * kPacketSize > bdp) { - QuicByteCount qsize = (sent_packets_.size() + 1) * kPacketSize - bdp; - ack_time = ack_time.Add(bandwidth_.TransferTime(qsize)); - DVLOG(1) << "Increasing transfer time:" - << bandwidth_.TransferTime(qsize).ToMilliseconds() - << "ms due to qsize:" << qsize; + QuicTime ack_time = clock_->Now().Add(rtt_); + if (kPacketSize > bdp) { + ack_time = ack_time.Add(bandwidth_.TransferTime(kPacketSize - bdp)); } - // If the packet is lost, give it an ack time of Zero. + QuicTime queue_ack_time = sent_packets_.empty() ? QuicTime::Zero() : + sent_packets_.back().ack_time.Add(bandwidth_.TransferTime(kPacketSize)); + ack_time = QuicTime::Max(ack_time, queue_ack_time); sent_packets_.push_back(SentPacket( - sender->last_sent, clock_->Now(), - packet_lost ? QuicTime::Zero() : ack_time, transfer)); + sender->last_sent, clock_->Now(), ack_time, packet_lost, transfer)); } else { DVLOG(1) << "losing packet:" << sender->last_sent << " because the buffer was full."; diff --git a/net/quic/congestion_control/send_algorithm_simulator.h b/net/quic/congestion_control/send_algorithm_simulator.h index 4ecb83338fdb0..934b27514a62e 100644 --- a/net/quic/congestion_control/send_algorithm_simulator.h +++ b/net/quic/congestion_control/send_algorithm_simulator.h @@ -89,18 +89,27 @@ class SendAlgorithmSimulator { }; struct SentPacket { + SentPacket() + : sequence_number(0), + send_time(QuicTime::Zero()), + ack_time(QuicTime::Zero()), + lost(false), + transfer(NULL) {} SentPacket(QuicPacketSequenceNumber sequence_number, QuicTime send_time, QuicTime ack_time, + bool lost, Transfer* transfer) : sequence_number(sequence_number), send_time(send_time), ack_time(ack_time), + lost(lost), transfer(transfer) {} QuicPacketSequenceNumber sequence_number; QuicTime send_time; QuicTime ack_time; + bool lost; Transfer* transfer; }; @@ -133,6 +142,10 @@ class SendAlgorithmSimulator { buffer_size_ = buffer_size_bytes; } + void set_delayed_ack_timer(QuicTime::Delta delayed_ack_timer) { + delayed_ack_timer_ = delayed_ack_timer; + } + // Advance the time by |delta| without sending anything. void AdvanceTime(QuicTime::Delta delta); @@ -173,6 +186,18 @@ class SendAlgorithmSimulator { // Sets the next acked. QuicTime::Delta FindNextAcked(Transfer* transfer); + // Sets the |next_acked| packet for the |transfer| starting at the specified + // |last_acked|. Returns QuicTime::Delta::Infinite and doesn't set + // |next_acked| if there is no ack after |last_acked|. + QuicTime::Delta FindNextAck(const Transfer* transfer, + QuicPacketSequenceNumber last_acked, + QuicPacketSequenceNumber* next_acked) const; + + // Returns true if any of the packets |transfer| is waiting for less than + // next_acked have been lost. + bool HasRecentLostPackets(const Transfer* transfer, + QuicPacketSequenceNumber next_acked) const; + // Process all the acks that should have arrived by the current time, and // lose any packets that are missing. Returns the number of bytes acked. void HandlePendingAck(Transfer* transfer); @@ -196,6 +221,7 @@ class SendAlgorithmSimulator { QuicBandwidth bandwidth_; QuicTime::Delta rtt_; size_t buffer_size_; // In bytes. + QuicTime::Delta delayed_ack_timer_; DISALLOW_COPY_AND_ASSIGN(SendAlgorithmSimulator); }; diff --git a/net/quic/congestion_control/tcp_cubic_sender.cc b/net/quic/congestion_control/tcp_cubic_sender.cc index dd7e7aee5c31f..d967704667888 100644 --- a/net/quic/congestion_control/tcp_cubic_sender.cc +++ b/net/quic/congestion_control/tcp_cubic_sender.cc @@ -8,6 +8,7 @@ #include "base/metrics/histogram.h" #include "net/quic/congestion_control/rtt_stats.h" +#include "net/quic/crypto/crypto_protocol.h" using std::max; using std::min; @@ -20,7 +21,6 @@ namespace { // fast retransmission. The cwnd after a timeout is still 1. const QuicTcpCongestionWindow kMinimumCongestionWindow = 2; const QuicByteCount kMaxSegmentSize = kDefaultTCPMSS; -const QuicByteCount kDefaultReceiveWindow = 64000; const int64 kInitialCongestionWindow = 10; const int kMaxBurstLength = 3; }; // namespace @@ -37,7 +37,7 @@ TcpCubicSender::TcpCubicSender( stats_(stats), reno_(reno), congestion_window_count_(0), - receive_window_(kDefaultReceiveWindow), + receive_window_(kDefaultSocketReceiveBuffer), prr_out_(0), prr_delivered_(0), ack_count_since_loss_(0), @@ -58,17 +58,31 @@ TcpCubicSender::~TcpCubicSender() { } void TcpCubicSender::SetFromConfig(const QuicConfig& config, bool is_server) { - if (is_server && config.HasReceivedInitialCongestionWindow()) { - // Set the initial window size. - congestion_window_ = min(kMaxInitialWindow, - config.ReceivedInitialCongestionWindow()); + if (is_server) { + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kIW10)) { + // Initial window experiment. Ignore the initial congestion + // window suggested by the client and use the default ICWND of + // 10 instead. + congestion_window_ = kInitialCongestionWindow; + } else if (config.HasReceivedInitialCongestionWindow()) { + // Set the initial window size. + congestion_window_ = min(kMaxInitialWindow, + config.ReceivedInitialCongestionWindow()); + } + } + if (config.HasReceivedSocketReceiveBuffer()) { + // Set the initial socket receive buffer size in bytes. + receive_window_ = config.ReceivedSocketReceiveBuffer(); } } void TcpCubicSender::OnIncomingQuicCongestionFeedbackFrame( const QuicCongestionFeedbackFrame& feedback, QuicTime feedback_receive_time) { - receive_window_ = feedback.tcp.receive_window; + if (feedback.type == kTCP) { + receive_window_ = feedback.tcp.receive_window; + } } void TcpCubicSender::OnCongestionEvent( diff --git a/net/quic/congestion_control/tcp_cubic_sender_test.cc b/net/quic/congestion_control/tcp_cubic_sender_test.cc index ef2e1c0a2cf0f..cc52da6c7d244 100644 --- a/net/quic/congestion_control/tcp_cubic_sender_test.cc +++ b/net/quic/congestion_control/tcp_cubic_sender_test.cc @@ -9,6 +9,7 @@ #include "net/quic/congestion_control/rtt_stats.h" #include "net/quic/congestion_control/tcp_cubic_sender.h" #include "net/quic/congestion_control/tcp_receiver.h" +#include "net/quic/crypto/crypto_protocol.h" #include "net/quic/quic_utils.h" #include "net/quic/test_tools/mock_clock.h" #include "net/quic/test_tools/quic_config_peer.h" @@ -130,16 +131,12 @@ class TcpCubicSenderTest : public ::testing::Test { }; TEST_F(TcpCubicSenderTest, SimpleSender) { - QuicCongestionFeedbackFrame feedback; // At startup make sure we are at the default. EXPECT_EQ(kDefaultWindowTCP, sender_->GetCongestionWindow()); // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA).IsZero()); - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, @@ -157,14 +154,10 @@ TEST_F(TcpCubicSenderTest, SimpleSender) { TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) { // Send exactly 10 packets and ensure the CWND ends at 14 packets. const int kNumberOfAcks = 5; - QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA).IsZero()); - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, @@ -183,14 +176,10 @@ TEST_F(TcpCubicSenderTest, ApplicationLimitedSlowStart) { TEST_F(TcpCubicSenderTest, ExponentialSlowStart) { const int kNumberOfAcks = 20; - QuicCongestionFeedbackFrame feedback; // At startup make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, HAS_RETRANSMITTABLE_DATA).IsZero()); - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Make sure we can send. EXPECT_TRUE(sender_->TimeUntilSend(clock_.Now(), 0, @@ -216,11 +205,6 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { // Since we start at 10 packet first round will be 5 second round 10 etc // Hence we should pass 30 at 65 = 5 + 10 + 20 + 30 const int kNumberOfAcks = 65; - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); @@ -262,12 +246,6 @@ TEST_F(TcpCubicSenderTest, SlowStartAckTrain) { } TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - const int kNumberOfAcks = 10; for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. @@ -319,12 +297,6 @@ TEST_F(TcpCubicSenderTest, SlowStartPacketLoss) { TEST_F(TcpCubicSenderTest, SlowStartPacketLossPRR) { // Test based on the first example in RFC6937. - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example. const int kNumberOfAcks = 5; for (int i = 0; i < kNumberOfAcks; ++i) { @@ -375,12 +347,6 @@ TEST_F(TcpCubicSenderTest, SlowStartBurstPacketLossPRR) { // Test based on the second example in RFC6937, though we also implement // forward acknowledgements, so the first two incoming acks will trigger // PRR immediately. - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - // Ack 10 packets in 5 acks to raise the CWND to 20, as in the example. const int kNumberOfAcks = 5; for (int i = 0; i < kNumberOfAcks; ++i) { @@ -506,11 +472,6 @@ TEST_F(TcpCubicSenderTest, SlowStartMaxSendWindow) { sender_.reset( new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP)); - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - for (int i = 0; i < kNumberOfAcks; ++i) { // Send our full send window. SendAvailableSendWindow(); @@ -527,11 +488,6 @@ TEST_F(TcpCubicSenderTest, TcpRenoMaxCongestionWindow) { sender_.reset( new TcpCubicSenderPeer(&clock_, true, kMaxCongestionWindowTCP)); - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - SendAvailableSendWindow(); AckNPackets(2); // Make sure we fall out of slow start. @@ -556,11 +512,6 @@ TEST_F(TcpCubicSenderTest, TcpCubicMaxCongestionWindow) { sender_.reset( new TcpCubicSenderPeer(&clock_, false, kMaxCongestionWindowTCP)); - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); - SendAvailableSendWindow(); AckNPackets(2); // Make sure we fall out of slow start. @@ -612,14 +563,17 @@ TEST_F(TcpCubicSenderTest, ConfigureMaxInitialWindow) { sender_->SetFromConfig(config, true); EXPECT_EQ(2 * congestion_window, sender_->congestion_window()); + + // Verify that kCOPT: kIW10 forces the congestion window to the + // default of 10 regardless of ReceivedInitialWindow. + QuicTagVector options; + options.push_back(kIW10); + QuicConfigPeer::SetReceivedConnectionOptions(&config, options); + sender_->SetFromConfig(config, true); + EXPECT_EQ(congestion_window, sender_->congestion_window()); } TEST_F(TcpCubicSenderTest, CongestionAvoidanceAtEndOfRecovery) { - // Make sure that we fall out of slow start when we encounter a packet loss. - QuicCongestionFeedbackFrame feedback; - // Get default QuicCongestionFeedbackFrame from receiver. - ASSERT_TRUE(receiver_->GenerateCongestionFeedback(&feedback)); - sender_->OnIncomingQuicCongestionFeedbackFrame(feedback, clock_.Now()); // Ack 10 packets in 5 acks to raise the CWND to 20. const int kNumberOfAcks = 5; for (int i = 0; i < kNumberOfAcks; ++i) { diff --git a/net/quic/congestion_control/timestamp_receiver.cc b/net/quic/congestion_control/timestamp_receiver.cc new file mode 100644 index 0000000000000..ad4407c1ad237 --- /dev/null +++ b/net/quic/congestion_control/timestamp_receiver.cc @@ -0,0 +1,43 @@ +// 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 "net/quic/congestion_control/timestamp_receiver.h" + +#include + +#include "base/basictypes.h" + +namespace net { + +TimestampReceiver::TimestampReceiver() { +} + +TimestampReceiver::~TimestampReceiver() { +} + +bool TimestampReceiver::GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) { + if (received_packet_times_.size() <= 1) { + // Don't waste resources by sending a feedback frame for only one packet. + return false; + } + feedback->type = kTimestamp; + + // Copy our current receive set to our feedback message, we will not resend + // this data if it is lost. + feedback->timestamp.received_packet_times = received_packet_times_; + + // Prepare for the next set of arriving packets by clearing our current set. + received_packet_times_.clear(); + return true; +} + +void TimestampReceiver::RecordIncomingPacket( + QuicByteCount /*bytes*/, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp) { + received_packet_times_.insert(std::make_pair(sequence_number, timestamp)); +} + +} // namespace net diff --git a/net/quic/congestion_control/timestamp_receiver.h b/net/quic/congestion_control/timestamp_receiver.h new file mode 100644 index 0000000000000..3eb2a7c461e65 --- /dev/null +++ b/net/quic/congestion_control/timestamp_receiver.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_CONGESTION_CONTROL_TIMESTAMP_RECEIVER_H_ +#define NET_QUIC_CONGESTION_CONTROL_TIMESTAMP_RECEIVER_H_ + +#include "base/basictypes.h" +#include "net/quic/congestion_control/receive_algorithm_interface.h" +#include "net/quic/quic_clock.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE TimestampReceiver : public ReceiveAlgorithmInterface { + public: + TimestampReceiver(); + virtual ~TimestampReceiver(); + + virtual bool GenerateCongestionFeedback( + QuicCongestionFeedbackFrame* feedback) OVERRIDE; + + virtual void RecordIncomingPacket(QuicByteCount bytes, + QuicPacketSequenceNumber sequence_number, + QuicTime timestamp) OVERRIDE; + + private: + // The set of received packets since the last feedback was sent, along with + // their arrival times. + TimeMap received_packet_times_; + + DISALLOW_COPY_AND_ASSIGN(TimestampReceiver); +}; + +} // namespace net + +#endif // NET_QUIC_CONGESTION_CONTROL_TIMESTAMP_RECEIVER_H_ diff --git a/net/quic/congestion_control/timestamp_receiver_test.cc b/net/quic/congestion_control/timestamp_receiver_test.cc new file mode 100644 index 0000000000000..2c477ea887b53 --- /dev/null +++ b/net/quic/congestion_control/timestamp_receiver_test.cc @@ -0,0 +1,55 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "net/quic/congestion_control/timestamp_receiver.h" +#include "net/quic/test_tools/mock_clock.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { +namespace test { + +class TimestampReceiverTest : public ::testing::Test { + protected: + TimestampReceiver receiver_; + MockClock clock_; +}; + +TEST_F(TimestampReceiverTest, SimpleReceiver) { + QuicTime start = clock_.ApproximateNow(); + QuicTime::Delta received_delta = QuicTime::Delta::FromMilliseconds(10); + clock_.AdvanceTime(received_delta); + QuicTime receive_timestamp = clock_.ApproximateNow(); + receiver_.RecordIncomingPacket(1, 1, receive_timestamp); + + QuicCongestionFeedbackFrame feedback; + // Do not generate a congestion feedback frame because it has only one packet. + ASSERT_FALSE(receiver_.GenerateCongestionFeedback(&feedback)); + + clock_.AdvanceTime(received_delta); + receive_timestamp = clock_.ApproximateNow(); + // Packet not received; but rather revived by FEC. + receiver_.RecordIncomingPacket(1, 2, receive_timestamp); + clock_.AdvanceTime(received_delta); + receive_timestamp = clock_.ApproximateNow(); + receiver_.RecordIncomingPacket(1, 3, receive_timestamp); + + ASSERT_TRUE(receiver_.GenerateCongestionFeedback(&feedback)); + + EXPECT_EQ(kTimestamp, feedback.type); + EXPECT_EQ(3u, feedback.timestamp.received_packet_times.size()); + TimeMap::iterator it = feedback.timestamp.received_packet_times.begin(); + EXPECT_EQ(1u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(10), it->second.Subtract(start)); + it = feedback.timestamp.received_packet_times.begin(); + ++it; + EXPECT_EQ(2u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(20), it->second.Subtract(start)); + ++it; + EXPECT_EQ(3u, it->first); + EXPECT_EQ(QuicTime::Delta::FromMilliseconds(30), it->second.Subtract(start)); +} + +} // namespace test +} // namespace net diff --git a/net/quic/crypto/crypto_handshake.cc b/net/quic/crypto/crypto_handshake.cc index 408b76bdecff8..5a928b6e0cdc1 100644 --- a/net/quic/crypto/crypto_handshake.cc +++ b/net/quic/crypto/crypto_handshake.cc @@ -13,7 +13,8 @@ namespace net { QuicCryptoNegotiatedParameters::QuicCryptoNegotiatedParameters() : key_exchange(0), - aead(0) { + aead(0), + x509_ecdsa_supported(false) { } QuicCryptoNegotiatedParameters::~QuicCryptoNegotiatedParameters() {} diff --git a/net/quic/crypto/crypto_handshake.h b/net/quic/crypto/crypto_handshake.h index cc25570af6bd4..bfd2f347fd854 100644 --- a/net/quic/crypto/crypto_handshake.h +++ b/net/quic/crypto/crypto_handshake.h @@ -124,6 +124,13 @@ struct NET_EXPORT_PRIVATE QuicCryptoNegotiatedParameters { // bytes of x coordinate, followed by 32 bytes of y coordinate. Both values // are big-endian and the pair is a P-256 public key. std::string channel_id; + + // Used when generating proof signature when sending server config updates. + bool x509_ecdsa_supported; + + // Used to generate cert chain when sending server config updates. + std::string client_common_set_hashes; + std::string client_cached_cert_hashes; }; // QuicCryptoConfig contains common configuration between clients and servers. diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h index 577913789dc7f..0a9c579d1d909 100644 --- a/net/quic/crypto/crypto_protocol.h +++ b/net/quic/crypto/crypto_protocol.h @@ -44,14 +44,18 @@ const QuicTag kNULL = TAG('N', 'U', 'L', 'N'); // null algorithm const QuicTag kAESG = TAG('A', 'E', 'S', 'G'); // AES128 + GCM-12 const QuicTag kCC12 = TAG('C', 'C', '1', '2'); // ChaCha20 + Poly1305 +// Socket receive buffer +const QuicTag kSRBF = TAG('S', 'R', 'B', 'F'); // Socket receive buffer + // Congestion control feedback types const QuicTag kQBIC = TAG('Q', 'B', 'I', 'C'); // TCP cubic -const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic -const QuicTag kINAR = TAG('I', 'N', 'A', 'R'); // Inter arrival +const QuicTag kTSTP = TAG('T', 'S', 'T', 'P'); // Timestamp // Congestion control options const QuicTag kTBBR = TAG('T', 'B', 'B', 'R'); // Reduced Buffer Bloat TCP const QuicTag kRENO = TAG('R', 'E', 'N', 'O'); // Reno Congestion Control +const QuicTag kIW10 = TAG('I', 'W', '1', '0'); // Force ICWND to 10 +const QuicTag kPACE = TAG('P', 'A', 'C', 'E'); // Paced TCP cubic // Loss detection algorithm types const QuicTag kNACK = TAG('N', 'A', 'C', 'K'); // TCP style nack counting diff --git a/net/quic/crypto/crypto_utils_test.cc b/net/quic/crypto/crypto_utils_test.cc index 37ac583314a92..91830f6260591 100644 --- a/net/quic/crypto/crypto_utils_test.cc +++ b/net/quic/crypto/crypto_utils_test.cc @@ -93,7 +93,7 @@ TEST(CryptoUtilsTest, TestExportKeyingMaterial) { }, }; - for (size_t i = 0; i < arraysize(test_vector); i++) { + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_vector); i++) { // Decode the test vector. string subkey_secret; string label; diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc index 90e03fee6b275..9ed83db515a16 100644 --- a/net/quic/crypto/proof_verifier_chromium.cc +++ b/net/quic/crypto/proof_verifier_chromium.cc @@ -236,58 +236,20 @@ int ProofVerifierChromium::Job::DoVerifyCert(int result) { int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { verifier_.reset(); -#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS) - // TODO(wtc): The following code was copied from ssl_client_socket_nss.cc. - // Convert it to a new function that can be called by both files. These - // variables simulate the arguments to the new function. const CertVerifyResult& cert_verify_result = verify_details_->cert_verify_result; - bool sni_available = true; - const std::string& host = hostname_; - TransportSecurityState* transport_security_state = transport_security_state_; - std::string* pinning_failure_log = &verify_details_->pinning_failure_log; - - // Take care of any mandates for public key pinning. - // - // Pinning is only enabled for official builds to make sure that others don't - // end up with pins that cannot be easily updated. - // - // TODO(agl): We might have an issue here where a request for foo.example.com - // merges into a SPDY connection to www.example.com, and gets a different - // certificate. - - // Perform pin validation if, and only if, all these conditions obtain: - // - // * a TransportSecurityState object is available; - // * the server's certificate chain is valid (or suffers from only a minor - // error); - // * the server's certificate chain chains up to a known root (i.e. not a - // user-installed trust anchor); and - // * the build is recent (very old builds should fail open so that users - // have some chance to recover). - // const CertStatus cert_status = cert_verify_result.cert_status; - if (transport_security_state && + if (transport_security_state_ && (result == OK || (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && - cert_verify_result.is_issued_by_known_root && - TransportSecurityState::IsBuildTimely()) { - if (transport_security_state->HasPublicKeyPins(host, sni_available)) { - if (!transport_security_state->CheckPublicKeyPins( - host, - sni_available, - cert_verify_result.public_key_hashes, - pinning_failure_log)) { - LOG(ERROR) << *pinning_failure_log; - result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false); - TransportSecurityState::ReportUMAOnPinFailure(host); - } else { - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true); - } - } + !transport_security_state_->CheckPublicKeyPins( + hostname_, + true, /* sni_available */ + cert_verify_result.is_issued_by_known_root, + cert_verify_result.public_key_hashes, + &verify_details_->pinning_failure_log)) { + result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; } -#endif if (result != OK) { std::string error_string = ErrorToString(result); diff --git a/net/quic/crypto/quic_crypto_client_config.cc b/net/quic/crypto/quic_crypto_client_config.cc index 18c0d541de5c2..9b64e1c80a6aa 100644 --- a/net/quic/crypto/quic_crypto_client_config.cc +++ b/net/quic/crypto/quic_crypto_client_config.cc @@ -612,7 +612,12 @@ QuicErrorCode QuicCryptoClientConfig::CacheNewServerConfig( cached->SetProof(certs, proof); } else { - cached->ClearProof(); + if (proof_verifier() != NULL) { + // Secure QUIC: clear existing proof as we have been sent a new SCFG + // without matching proof/certs. + cached->ClearProof(); + } + if (has_proof && !has_cert) { *error_details = "Certificate missing"; return QUIC_INVALID_CRYPTO_MESSAGE_PARAMETER; diff --git a/net/quic/crypto/quic_crypto_server_config.cc b/net/quic/crypto/quic_crypto_server_config.cc index ec88594c949c6..bfd3ed34982a9 100644 --- a/net/quic/crypto/quic_crypto_server_config.cc +++ b/net/quic/crypto/quic_crypto_server_config.cc @@ -603,7 +603,7 @@ QuicErrorCode QuicCryptoServerConfig::ProcessClientHello( !info.client_nonce_well_formed || !info.unique || !requested_config.get()) { - BuildRejection(*primary_config, client_hello, info, rand, out); + BuildRejection(*primary_config, client_hello, info, rand, params, out); return QUIC_NO_ERROR; } @@ -1039,11 +1039,50 @@ void QuicCryptoServerConfig::EvaluateClientHello( helper.StartedAsyncCallback(); } +bool QuicCryptoServerConfig::BuildServerConfigUpdateMessage( + const IPEndPoint& client_ip, + const QuicClock* clock, + QuicRandom* rand, + const QuicCryptoNegotiatedParameters& params, + CryptoHandshakeMessage* out) const { + base::AutoLock locked(configs_lock_); + out->set_tag(kSCUP); + out->SetStringPiece(kSCFG, primary_config_->serialized); + out->SetStringPiece(kSourceAddressTokenTag, + NewSourceAddressToken(*primary_config_, + client_ip, + rand, + clock->WallNow())); + + if (proof_source_ == NULL) { + // Insecure QUIC, can send SCFG without proof. + return true; + } + + const vector* certs; + string signature; + if (!proof_source_->GetProof(params.sni, primary_config_->serialized, + params.x509_ecdsa_supported, &certs, + &signature)) { + DVLOG(1) << "Server: failed to get proof."; + return false; + } + + const string compressed = CertCompressor::CompressChain( + *certs, params.client_common_set_hashes, params.client_cached_cert_hashes, + primary_config_->common_cert_sets); + + out->SetStringPiece(kCertificateTag, compressed); + out->SetStringPiece(kPROF, signature); + return true; +} + void QuicCryptoServerConfig::BuildRejection( const Config& config, const CryptoHandshakeMessage& client_hello, const ClientHelloInfo& info, QuicRandom* rand, + QuicCryptoNegotiatedParameters *params, CryptoHandshakeMessage* out) const { out->set_tag(kREJ); out->SetStringPiece(kSCFG, config.serialized); @@ -1074,12 +1113,12 @@ void QuicCryptoServerConfig::BuildRejection( return; } - bool x509_supported = false, x509_ecdsa_supported = false; + bool x509_supported = false; for (size_t i = 0; i < num_their_proof_demands; i++) { switch (their_proof_demands[i]) { case kX509: x509_supported = true; - x509_ecdsa_supported = true; + params->x509_ecdsa_supported = true; break; case kX59R: x509_supported = true; @@ -1094,18 +1133,24 @@ void QuicCryptoServerConfig::BuildRejection( const vector* certs; string signature; if (!proof_source_->GetProof(info.sni.as_string(), config.serialized, - x509_ecdsa_supported, &certs, &signature)) { + params->x509_ecdsa_supported, &certs, + &signature)) { return; } - StringPiece their_common_set_hashes; - StringPiece their_cached_cert_hashes; - client_hello.GetStringPiece(kCCS, &their_common_set_hashes); - client_hello.GetStringPiece(kCCRT, &their_cached_cert_hashes); + StringPiece client_common_set_hashes; + if (client_hello.GetStringPiece(kCCS, &client_common_set_hashes)) { + params->client_common_set_hashes = client_common_set_hashes.as_string(); + } + + StringPiece client_cached_cert_hashes; + if (client_hello.GetStringPiece(kCCRT, &client_cached_cert_hashes)) { + params->client_cached_cert_hashes = client_cached_cert_hashes.as_string(); + } const string compressed = CertCompressor::CompressChain( - *certs, their_common_set_hashes, their_cached_cert_hashes, - config.common_cert_sets); + *certs, params->client_common_set_hashes, + params->client_cached_cert_hashes, config.common_cert_sets); // kREJOverheadBytes is a very rough estimate of how much of a REJ // message is taken up by things other than the certificates. diff --git a/net/quic/crypto/quic_crypto_server_config.h b/net/quic/crypto/quic_crypto_server_config.h index 8760aced42734..9ec0612ea38d1 100644 --- a/net/quic/crypto/quic_crypto_server_config.h +++ b/net/quic/crypto/quic_crypto_server_config.h @@ -16,6 +16,7 @@ #include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/quic/crypto/crypto_handshake.h" +#include "net/quic/crypto/crypto_handshake_message.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/crypto_secret_boxer.h" #include "net/quic/quic_time.h" @@ -210,6 +211,13 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { CryptoHandshakeMessage* out, std::string* error_details) const; + bool BuildServerConfigUpdateMessage( + const IPEndPoint& client_ip, + const QuicClock* clock, + QuicRandom* rand, + const QuicCryptoNegotiatedParameters& params, + CryptoHandshakeMessage* out) const; + // SetProofSource installs |proof_source| as the ProofSource for handshakes. // This object takes ownership of |proof_source|. void SetProofSource(ProofSource* proof_source); @@ -371,6 +379,7 @@ class NET_EXPORT_PRIVATE QuicCryptoServerConfig { const CryptoHandshakeMessage& client_hello, const ClientHelloInfo& info, QuicRandom* rand, + QuicCryptoNegotiatedParameters *params, CryptoHandshakeMessage* out) const; // ParseConfigProtobuf parses the given config protobuf and returns a diff --git a/net/quic/crypto/source_address_token.h b/net/quic/crypto/source_address_token.h index c719fef0412a3..a41a7265b7f79 100644 --- a/net/quic/crypto/source_address_token.h +++ b/net/quic/crypto/source_address_token.h @@ -42,6 +42,23 @@ class CachedNetworkParameters { bandwidth_estimate_bytes_per_second_ = bandwidth_estimate_bytes_per_second; } + int32 max_bandwidth_estimate_bytes_per_second() const { + return max_bandwidth_estimate_bytes_per_second_; + } + void set_max_bandwidth_estimate_bytes_per_second( + int32 max_bandwidth_estimate_bytes_per_second) { + max_bandwidth_estimate_bytes_per_second_ = + max_bandwidth_estimate_bytes_per_second; + } + + int64 max_bandwidth_timestamp_seconds() const { + return max_bandwidth_timestamp_seconds_; + } + void set_max_bandwidth_timestamp_seconds( + int64 max_bandwidth_timestamp_seconds) { + max_bandwidth_timestamp_seconds_ = max_bandwidth_timestamp_seconds; + } + int32 min_rtt_ms() const { return min_rtt_ms_; } @@ -66,6 +83,11 @@ class CachedNetworkParameters { // The server can supply a bandwidth estimate (in bytes/s) which it may re-use // on receipt of a source-address token with this field set. int32 bandwidth_estimate_bytes_per_second_; + // The maximum bandwidth seen by the client, not necessarily the latest. + int32 max_bandwidth_estimate_bytes_per_second_; + // Timestamp (seconds since UNIX epoch) that indicates when the max bandwidth + // was seen by the server. + int64 max_bandwidth_timestamp_seconds_; // The min RTT seen on a previous connection can be used by the server to // inform initial connection parameters for new connections. int32 min_rtt_ms_; diff --git a/net/quic/quic_client_session.cc b/net/quic/quic_client_session.cc index c6699f189bace..b1367249baa52 100644 --- a/net/quic/quic_client_session.cc +++ b/net/quic/quic_client_session.cc @@ -13,6 +13,7 @@ #include "base/values.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" +#include "net/http/transport_security_state.h" #include "net/quic/crypto/proof_verifier_chromium.h" #include "net/quic/crypto/quic_server_info.h" #include "net/quic/quic_connection_helper.h" @@ -20,6 +21,7 @@ #include "net/quic/quic_default_packet_writer.h" #include "net/quic/quic_server_id.h" #include "net/quic/quic_stream_factory.h" +#include "net/spdy/spdy_session.h" #include "net/ssl/channel_id_service.h" #include "net/ssl/ssl_connection_status_flags.h" #include "net/ssl/ssl_info.h" @@ -138,6 +140,7 @@ QuicClientSession::QuicClientSession( scoped_ptr writer, QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, + TransportSecurityState* transport_security_state, scoped_ptr server_info, const QuicServerId& server_id, const QuicConfig& config, @@ -151,6 +154,7 @@ QuicClientSession::QuicClientSession( socket_(socket.Pass()), writer_(writer.Pass()), read_buffer_(new IOBufferWithSize(kMaxPacketSize)), + transport_security_state_(transport_security_state), server_info_(server_info.Pass()), read_pending_(false), num_total_streams_(0), @@ -489,28 +493,8 @@ bool QuicClientSession::CanPool(const std::string& hostname) const { return true; } - // Disable pooling for secure sessions. - // TODO(rch): re-enable this. - return false; -#if 0 - bool unused = false; - // Pooling is prohibited if the server cert is not valid for the new domain, - // and for connections on which client certs were sent. It is also prohibited - // when channel ID was sent if the hosts are from different eTLDs+1. - if (!ssl_info.cert->VerifyNameMatch(hostname, &unused)) - return false; - - if (ssl_info.client_cert_sent) - return false; - - if (ssl_info.channel_id_sent && - ChannelIDService::GetDomainForHost(hostname) != - ChannelIDService::GetDomainForHost(server_host_port_.host())) { - return false; - } - - return true; -#endif + return SpdySession::CanPool(transport_security_state_, ssl_info, + server_host_port_.host(), hostname); } QuicDataStream* QuicClientSession::CreateIncomingDataStream( diff --git a/net/quic/quic_client_session.h b/net/quic/quic_client_session.h index 6112e1eb492eb..9808cac4259b6 100644 --- a/net/quic/quic_client_session.h +++ b/net/quic/quic_client_session.h @@ -35,6 +35,7 @@ class QuicServerId; class QuicServerInfo; class QuicStreamFactory; class SSLInfo; +class TransportSecurityState; namespace test { class QuicClientSessionPeer; @@ -95,6 +96,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase { scoped_ptr writer, QuicStreamFactory* stream_factory, QuicCryptoClientStreamFactory* crypto_client_stream_factory, + TransportSecurityState* transport_security_state, scoped_ptr server_info, const QuicServerId& server_id, const QuicConfig& config, @@ -226,6 +228,7 @@ class NET_EXPORT_PRIVATE QuicClientSession : public QuicClientSessionBase { scoped_ptr socket_; scoped_ptr writer_; scoped_refptr read_buffer_; + TransportSecurityState* transport_security_state_; scoped_ptr server_info_; scoped_ptr cert_verify_result_; std::string pinning_failure_log_; diff --git a/net/quic/quic_client_session_test.cc b/net/quic/quic_client_session_test.cc index ae1aea6e9a671..1e13e89ae4992 100644 --- a/net/quic/quic_client_session_test.cc +++ b/net/quic/quic_client_session_test.cc @@ -6,12 +6,14 @@ #include +#include "base/base64.h" #include "base/files/file_path.h" #include "base/rand_util.h" #include "net/base/capturing_net_log.h" #include "net/base/test_completion_callback.h" #include "net/base/test_data_directory.h" #include "net/cert/cert_verify_result.h" +#include "net/http/transport_security_state.h" #include "net/quic/crypto/aes_128_gcm_12_encrypter.h" #include "net/quic/crypto/crypto_protocol.h" #include "net/quic/crypto/proof_verifier_chromium.h" @@ -24,6 +26,7 @@ #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/simple_quic_framer.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_test_utils.h" #include "net/test/cert_test_util.h" #include "net/udp/datagram_client_socket.h" @@ -73,6 +76,7 @@ class QuicClientSessionTest : public ::testing::TestWithParam { connection_( new PacketSavingConnection(false, SupportedVersions(GetParam()))), session_(connection_, GetSocket().Pass(), writer_.Pass(), NULL, NULL, + &transport_security_state_, make_scoped_ptr((QuicServerInfo*)NULL), QuicServerId(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED), @@ -108,6 +112,7 @@ class QuicClientSessionTest : public ::testing::TestWithParam { CapturingNetLog net_log_; MockClientSocketFactory socket_factory_; StaticSocketDataProvider socket_data_; + TransportSecurityState transport_security_state_; QuicClientSession session_; MockClock clock_; MockRandom random_; @@ -172,18 +177,15 @@ TEST_P(QuicClientSessionTest, GoAwayReceived) { EXPECT_EQ(NULL, session_.CreateOutgoingDataStream()); } -// TODO(rch): re-enable this. -TEST_P(QuicClientSessionTest, DISABLED_CanPool) { +TEST_P(QuicClientSessionTest, CanPool) { // Load a cert that is valid for: // www.example.org // mail.example.org // www.example.com - base::FilePath certs_dir = GetTestCertsDirectory(); - CertVerifyResult result; ProofVerifyDetailsChromium details; details.cert_verify_result.verified_cert = - ImportCertFromFile(certs_dir, "spdy_pooling.pem"); + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); ASSERT_TRUE(details.cert_verify_result.verified_cert); session_.OnProofVerifyDetailsAvailable(details); @@ -196,18 +198,15 @@ TEST_P(QuicClientSessionTest, DISABLED_CanPool) { EXPECT_FALSE(session_.CanPool("mail.google.com")); } -// TODO(rch): re-enable this. -TEST_P(QuicClientSessionTest, DISABLED_ConnectionPooledWithTlsChannelId) { +TEST_P(QuicClientSessionTest, ConnectionPooledWithTlsChannelId) { // Load a cert that is valid for: // www.example.org // mail.example.org // www.example.com - base::FilePath certs_dir = GetTestCertsDirectory(); - CertVerifyResult result; ProofVerifyDetailsChromium details; details.cert_verify_result.verified_cert = - ImportCertFromFile(certs_dir, "spdy_pooling.pem"); + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); ASSERT_TRUE(details.cert_verify_result.verified_cert); session_.OnProofVerifyDetailsAvailable(details); @@ -220,6 +219,51 @@ TEST_P(QuicClientSessionTest, DISABLED_ConnectionPooledWithTlsChannelId) { EXPECT_FALSE(session_.CanPool("mail.google.com")); } +TEST_P(QuicClientSessionTest, ConnectionNotPooledWithDifferentPin) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + uint8 bad_pin = 3; + AddPin(&transport_security_state_, "mail.example.org", primary_pin, + backup_pin); + + ProofVerifyDetailsChromium details; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + details.cert_verify_result.is_issued_by_known_root = true; + details.cert_verify_result.public_key_hashes.push_back( + GetTestHashValue(bad_pin)); + + ASSERT_TRUE(details.cert_verify_result.verified_cert); + + session_.OnProofVerifyDetailsAvailable(details); + CompleteCryptoHandshake(); + QuicClientSessionPeer::SetChannelIDSent(&session_, true); + + EXPECT_FALSE(session_.CanPool("mail.example.org")); +} + +TEST_P(QuicClientSessionTest, ConnectionPooledWithMatchingPin) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + AddPin(&transport_security_state_, "mail.example.org", primary_pin, + backup_pin); + + ProofVerifyDetailsChromium details; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + details.cert_verify_result.is_issued_by_known_root = true; + details.cert_verify_result.public_key_hashes.push_back( + GetTestHashValue(primary_pin)); + + ASSERT_TRUE(details.cert_verify_result.verified_cert); + + session_.OnProofVerifyDetailsAvailable(details); + CompleteCryptoHandshake(); + QuicClientSessionPeer::SetChannelIDSent(&session_, true); + + EXPECT_TRUE(session_.CanPool("mail.example.org")); +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_config.cc b/net/quic/quic_config.cc index 90579d3a03f3b..4542a04f7477d 100644 --- a/net/quic/quic_config.cc +++ b/net/quic/quic_config.cc @@ -443,7 +443,8 @@ QuicConfig::QuicConfig() initial_stream_flow_control_window_bytes_(kSFCW, PRESENCE_OPTIONAL), // TODO(rjshade): Make this PRESENCE_REQUIRED when retiring // QUIC_VERSION_19. - initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL) { + initial_session_flow_control_window_bytes_(kCFCW, PRESENCE_OPTIONAL), + socket_receive_buffer_(kSRBF, PRESENCE_OPTIONAL) { } QuicConfig::~QuicConfig() {} @@ -617,6 +618,22 @@ uint32 QuicConfig::ReceivedInitialSessionFlowControlWindowBytes() const { return initial_session_flow_control_window_bytes_.GetReceivedValue(); } +void QuicConfig::SetSocketReceiveBufferToSend(uint32 tcp_receive_window) { + socket_receive_buffer_.SetSendValue(tcp_receive_window); +} + +uint32 QuicConfig::GetSocketReceiveBufferToSend() const { + return socket_receive_buffer_.GetSendValue(); +} + +bool QuicConfig::HasReceivedSocketReceiveBuffer() const { + return socket_receive_buffer_.HasReceivedValue(); +} + +uint32 QuicConfig::ReceivedSocketReceiveBuffer() const { + return socket_receive_buffer_.GetReceivedValue(); +} + bool QuicConfig::negotiated() { // TODO(ianswett): Add the negotiated parameters once and iterate over all // of them in negotiated, ToHandshakeMessage, ProcessClientHello, and @@ -629,9 +646,6 @@ bool QuicConfig::negotiated() { void QuicConfig::SetDefaults() { QuicTagVector congestion_feedback; - if (FLAGS_enable_quic_pacing) { - congestion_feedback.push_back(kPACE); - } congestion_feedback.push_back(kQBIC); congestion_feedback_.set(congestion_feedback, kQBIC); idle_connection_state_lifetime_seconds_.set(kDefaultTimeoutSecs, @@ -648,15 +662,6 @@ void QuicConfig::SetDefaults() { SetInitialSessionFlowControlWindowToSend(kDefaultFlowControlSendWindow); } -void QuicConfig::EnablePacing(bool enable_pacing) { - QuicTagVector congestion_feedback; - if (enable_pacing) { - congestion_feedback.push_back(kPACE); - } - congestion_feedback.push_back(kQBIC); - congestion_feedback_.set(congestion_feedback, kQBIC); -} - void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { congestion_feedback_.ToHandshakeMessage(out); idle_connection_state_lifetime_seconds_.ToHandshakeMessage(out); @@ -668,6 +673,7 @@ void QuicConfig::ToHandshakeMessage(CryptoHandshakeMessage* out) const { initial_flow_control_window_bytes_.ToHandshakeMessage(out); initial_stream_flow_control_window_bytes_.ToHandshakeMessage(out); initial_session_flow_control_window_bytes_.ToHandshakeMessage(out); + socket_receive_buffer_.ToHandshakeMessage(out); connection_options_.ToHandshakeMessage(out); } @@ -714,6 +720,10 @@ QuicErrorCode QuicConfig::ProcessPeerHello( error = initial_session_flow_control_window_bytes_.ProcessPeerHello( peer_hello, hello_type, error_details); } + if (error == QUIC_NO_ERROR) { + error = socket_receive_buffer_.ProcessPeerHello( + peer_hello, hello_type, error_details); + } if (error == QUIC_NO_ERROR) { error = loss_detection_.ProcessPeerHello( peer_hello, hello_type, error_details); diff --git a/net/quic/quic_config.h b/net/quic/quic_config.h index 376b2b05896f6..02c2b0326d565 100644 --- a/net/quic/quic_config.h +++ b/net/quic/quic_config.h @@ -340,14 +340,20 @@ class NET_EXPORT_PRIVATE QuicConfig { uint32 ReceivedInitialSessionFlowControlWindowBytes() const; + // Sets socket receive buffer to transmit to the peer. + void SetSocketReceiveBufferToSend(uint32 window_bytes); + + uint32 GetSocketReceiveBufferToSend() const; + + bool HasReceivedSocketReceiveBuffer() const; + + uint32 ReceivedSocketReceiveBuffer() const; + bool negotiated(); // SetDefaults sets the members to sensible, default values. void SetDefaults(); - // Enabled pacing. - void EnablePacing(bool enable_pacing); - // ToHandshakeMessage serialises the settings in this object as a series of // tags /value pairs and adds them to |out|. void ToHandshakeMessage(CryptoHandshakeMessage* out) const; @@ -389,6 +395,9 @@ class NET_EXPORT_PRIVATE QuicConfig { QuicFixedUint32 initial_stream_flow_control_window_bytes_; // Initial session flow control receive window in bytes. QuicFixedUint32 initial_session_flow_control_window_bytes_; + + // Socket receive buffer in bytes. + QuicFixedUint32 socket_receive_buffer_; }; } // namespace net diff --git a/net/quic/quic_config_test.cc b/net/quic/quic_config_test.cc index 5241fed7e6f25..5c302b0d8065e 100644 --- a/net/quic/quic_config_test.cc +++ b/net/quic/quic_config_test.cc @@ -42,6 +42,7 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) { config_.set_idle_connection_state_lifetime(QuicTime::Delta::FromSeconds(5), QuicTime::Delta::FromSeconds(2)); config_.set_max_streams_per_connection(4, 2); + config_.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer); CryptoHandshakeMessage msg; config_.ToHandshakeMessage(&msg); @@ -66,6 +67,10 @@ TEST_F(QuicConfigTest, ToHandshakeMessage) { EXPECT_EQ(QUIC_NO_ERROR, error); EXPECT_EQ(kInitialSessionFlowControlWindowForTest, value); + error = msg.GetUint32(kSRBF, &value); + EXPECT_EQ(QUIC_NO_ERROR, error); + EXPECT_EQ(kDefaultSocketReceiveBuffer, value); + const QuicTag* out; size_t out_len; error = msg.GetTaglist(kCGST, &out, &out_len); @@ -83,15 +88,14 @@ TEST_F(QuicConfigTest, ToHandshakeMessageWithPacing) { const QuicTag* out; size_t out_len; EXPECT_EQ(QUIC_NO_ERROR, msg.GetTaglist(kCGST, &out, &out_len)); - EXPECT_EQ(2u, out_len); - EXPECT_EQ(kPACE, out[0]); - EXPECT_EQ(kQBIC, out[1]); + EXPECT_EQ(1u, out_len); + EXPECT_EQ(kQBIC, out[0]); } TEST_F(QuicConfigTest, ProcessClientHello) { QuicConfig client_config; QuicTagVector cgst; - cgst.push_back(kINAR); + cgst.push_back(kTSTP); cgst.push_back(kQBIC); client_config.set_congestion_feedback(cgst, kQBIC); client_config.set_idle_connection_state_lifetime( @@ -107,6 +111,7 @@ TEST_F(QuicConfigTest, ProcessClientHello) { 2 * kInitialStreamFlowControlWindowForTest); client_config.SetInitialSessionFlowControlWindowToSend( 2 * kInitialSessionFlowControlWindowForTest); + client_config.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer); QuicTagVector copt; copt.push_back(kTBBR); copt.push_back(kFHDR); @@ -137,6 +142,8 @@ TEST_F(QuicConfigTest, ProcessClientHello) { 2 * kInitialStreamFlowControlWindowForTest); EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedSocketReceiveBuffer(), + kDefaultSocketReceiveBuffer); } TEST_F(QuicConfigTest, ProcessServerHello) { @@ -159,6 +166,7 @@ TEST_F(QuicConfigTest, ProcessServerHello) { 2 * kInitialStreamFlowControlWindowForTest); server_config.SetInitialSessionFlowControlWindowToSend( 2 * kInitialSessionFlowControlWindowForTest); + server_config.SetSocketReceiveBufferToSend(kDefaultSocketReceiveBuffer); CryptoHandshakeMessage msg; server_config.ToHandshakeMessage(&msg); string error_details; @@ -183,6 +191,8 @@ TEST_F(QuicConfigTest, ProcessServerHello) { 2 * kInitialStreamFlowControlWindowForTest); EXPECT_EQ(config_.ReceivedInitialSessionFlowControlWindowBytes(), 2 * kInitialSessionFlowControlWindowForTest); + EXPECT_EQ(config_.ReceivedSocketReceiveBuffer(), + kDefaultSocketReceiveBuffer); } TEST_F(QuicConfigTest, MissingOptionalValuesInCHLO) { @@ -261,7 +271,7 @@ TEST_F(QuicConfigTest, MultipleNegotiatedValuesInVectorTag) { QuicConfig server_config; QuicTagVector cgst; cgst.push_back(kQBIC); - cgst.push_back(kINAR); + cgst.push_back(kTSTP); server_config.set_congestion_feedback(cgst, kQBIC); CryptoHandshakeMessage msg; @@ -276,8 +286,8 @@ TEST_F(QuicConfigTest, NoOverLapInCGST) { QuicConfig server_config; server_config.SetDefaults(); QuicTagVector cgst; - cgst.push_back(kINAR); - server_config.set_congestion_feedback(cgst, kINAR); + cgst.push_back(kTSTP); + server_config.set_congestion_feedback(cgst, kTSTP); CryptoHandshakeMessage msg; string error_details; diff --git a/net/quic/quic_connection.cc b/net/quic/quic_connection.cc index 9594fdd6982ba..a4305f62c71bf 100644 --- a/net/quic/quic_connection.cc +++ b/net/quic/quic_connection.cc @@ -237,10 +237,13 @@ QuicConnection::QuicConnection(QuicConnectionId connection_id, peer_port_changed_(false), self_ip_changed_(false), self_port_changed_(false) { +#if 0 + // TODO(rtenneti): Should we enable this code in chromium? if (!is_server_) { // Pacing will be enabled if the client negotiates it. sent_packet_manager_.MaybeEnablePacing(); } +#endif DVLOG(1) << ENDPOINT << "Created connection with connection_id: " << connection_id; timeout_alarm_->Set(clock_->ApproximateNow().Add(idle_network_timeout_)); @@ -1179,17 +1182,15 @@ void QuicConnection::WriteIfNotBlocked() { } bool QuicConnection::ProcessValidatedPacket() { - if ((!FLAGS_quic_allow_port_migration && peer_port_changed_) || - peer_ip_changed_ || self_ip_changed_ || self_port_changed_) { + if (peer_ip_changed_ || self_ip_changed_ || self_port_changed_) { SendConnectionCloseWithDetails( QUIC_ERROR_MIGRATING_ADDRESS, "Neither IP address migration, nor self port migration are supported."); return false; } - // Port migration is supported, do it now if port has changed. - if (FLAGS_quic_allow_port_migration && - peer_port_changed_) { + // Peer port migration is supported, do it now if port has changed. + if (peer_port_changed_) { DVLOG(1) << ENDPOINT << "Peer's port changed from " << peer_address_.port() << " to " << migrating_peer_port_ << ", migrating connection."; @@ -1542,6 +1543,10 @@ bool QuicConnection::OnSerializedPacket( NOT_RETRANSMISSION); } +void QuicConnection::OnHandshakeComplete() { + sent_packet_manager_.SetHandshakeConfirmed(); +} + bool QuicConnection::SendOrQueuePacket(EncryptionLevel level, const SerializedPacket& packet, TransmissionType transmission_type) { @@ -1695,7 +1700,10 @@ void QuicConnection::MaybeProcessUndecryptablePackets() { // new keys installed and hence any undecryptable packets will // never be able to be decrypted. if (encryption_level_ == ENCRYPTION_FORWARD_SECURE) { - if (debug_visitor_ != NULL) { + if (debug_visitor_.get() != NULL) { + // TODO(rtenneti): perhaps more efficient to pass the number of + // undecryptable packets as the argument to OnUndecryptablePacket so that + // we just need to call OnUndecryptablePacket once? for (size_t i = 0; i < undecryptable_packets_.size(); ++i) { debug_visitor_->OnUndecryptablePacket(); } @@ -1796,6 +1804,9 @@ void QuicConnection::CloseConnection(QuicErrorCode error, bool from_peer) { return; } connected_ = false; + if (debug_visitor_.get() != NULL) { + debug_visitor_->OnConnectionClosed(error, from_peer); + } visitor_->OnConnectionClosed(error, from_peer); // Cancel the alarms so they don't trigger any action now that the // connection is closed. diff --git a/net/quic/quic_connection.h b/net/quic/quic_connection.h index a98458bb0de5e..4d57208caef00 100644 --- a/net/quic/quic_connection.h +++ b/net/quic/quic_connection.h @@ -137,7 +137,7 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor const IPEndPoint& peer_address, const QuicEncryptedPacket& packet) {} - // Called when a packet is recived with a connection id that does not + // Called when a packet is received with a connection id that does not // match the ID of this connection. virtual void OnIncorrectConnectionId( QuicConnectionId connection_id) {} @@ -198,6 +198,9 @@ class NET_EXPORT_PRIVATE QuicConnectionDebugVisitor // in the revival of a packet via FEC. virtual void OnRevivedPacket(const QuicPacketHeader& revived_header, base::StringPiece payload) {} + + // Called when the connection is closed. + virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) {} }; class NET_EXPORT_PRIVATE QuicConnectionHelperInterface { @@ -363,10 +366,16 @@ class NET_EXPORT_PRIVATE QuicConnection virtual QuicStopWaitingFrame* CreateStopWaitingFrame() OVERRIDE; virtual bool OnSerializedPacket(const SerializedPacket& packet) OVERRIDE; + // Called by the crypto stream when the handshake completes. In the server's + // case this is when the SHLO has been ACKed. Clients call this on receipt of + // the SHLO. + void OnHandshakeComplete(); + // Accessors void set_visitor(QuicConnectionVisitorInterface* visitor) { visitor_ = visitor; } + // This method takes ownership of |debug_visitor|. void set_debug_visitor(QuicConnectionDebugVisitor* debug_visitor) { debug_visitor_.reset(debug_visitor); packet_generator_.set_debug_delegate(debug_visitor); diff --git a/net/quic/quic_connection_logger.cc b/net/quic/quic_connection_logger.cc index 8f2163bc6a663..6f7757e12d4e2 100644 --- a/net/quic/quic_connection_logger.cc +++ b/net/quic/quic_connection_logger.cc @@ -122,13 +122,13 @@ base::Value* NetLogQuicCongestionFeedbackFrameCallback( NetLog::LogLevel /* log_level */) { base::DictionaryValue* dict = new base::DictionaryValue(); switch (frame->type) { - case kInterArrival: { - dict->SetString("type", "InterArrival"); + case kTimestamp: { + dict->SetString("type", "Timestamp"); base::ListValue* received = new base::ListValue(); dict->Set("received_packets", received); for (TimeMap::const_iterator it = - frame->inter_arrival.received_packet_times.begin(); - it != frame->inter_arrival.received_packet_times.end(); ++it) { + frame->timestamp.received_packet_times.begin(); + it != frame->timestamp.received_packet_times.end(); ++it) { string value = base::Uint64ToString(it->first) + "@" + base::Uint64ToString(it->second.ToDebuggingValue()); received->AppendString(value); diff --git a/net/quic/quic_connection_logger.h b/net/quic/quic_connection_logger.h index d020a61c707ff..bbb87e0bbef4f 100644 --- a/net/quic/quic_connection_logger.h +++ b/net/quic/quic_connection_logger.h @@ -62,12 +62,12 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger const QuicVersionNegotiationPacket& packet) OVERRIDE; virtual void OnRevivedPacket(const QuicPacketHeader& revived_header, base::StringPiece payload) OVERRIDE; + virtual void OnConnectionClosed(QuicErrorCode error, bool from_peer) OVERRIDE; void OnCryptoHandshakeMessageReceived( const CryptoHandshakeMessage& message); void OnCryptoHandshakeMessageSent( const CryptoHandshakeMessage& message); - void OnConnectionClosed(QuicErrorCode error, bool from_peer); void OnSuccessfulVersionNegotiation(const QuicVersion& version); void UpdateReceivedFrameCounts(QuicStreamId stream_id, int num_frames_received, diff --git a/net/quic/quic_connection_test.cc b/net/quic/quic_connection_test.cc index 7faafe7a844ba..13f97bf436c9a 100644 --- a/net/quic/quic_connection_test.cc +++ b/net/quic/quic_connection_test.cc @@ -57,7 +57,6 @@ const bool kEntropyFlag = true; const QuicPacketEntropyHash kTestEntropyHash = 76; const int kDefaultRetransmissionTimeMs = 500; -const int kMinRetransmissionTimeMs = 200; class TestReceiveAlgorithm : public ReceiveAlgorithmInterface { public: @@ -885,7 +884,7 @@ class QuicConnectionTest : public ::testing::TestWithParam { } QuicTime::Delta DefaultDelayedAckTime() { - return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2); + return QuicTime::Delta::FromMilliseconds(kMaxDelayedAckTime); } // Initialize a frame acknowledging all packets up to largest_observed. @@ -3962,7 +3961,7 @@ TEST_P(QuicConnectionTest, Pacing) { writer_.get(), true, version()); TestConnection client(connection_id_, IPEndPoint(), helper_.get(), writer_.get(), false, version()); - EXPECT_TRUE(client.sent_packet_manager().using_pacing()); + EXPECT_FALSE(client.sent_packet_manager().using_pacing()); EXPECT_FALSE(server.sent_packet_manager().using_pacing()); } diff --git a/net/quic/quic_crypto_client_stream.cc b/net/quic/quic_crypto_client_stream.cc index d28860aeeaa7f..40e4d45901c53 100644 --- a/net/quic/quic_crypto_client_stream.cc +++ b/net/quic/quic_crypto_client_stream.cc @@ -464,6 +464,7 @@ void QuicCryptoClientStream::DoHandshakeLoop( handshake_confirmed_ = true; session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + session()->connection()->OnHandshakeComplete(); return; } case STATE_IDLE: diff --git a/net/quic/quic_crypto_client_stream_test.cc b/net/quic/quic_crypto_client_stream_test.cc index 344dfe1c54a74..47b4ed6765beb 100644 --- a/net/quic/quic_crypto_client_stream_test.cc +++ b/net/quic/quic_crypto_client_stream_test.cc @@ -93,8 +93,7 @@ TEST_F(QuicCryptoClientStreamTest, NegotiatedParameters) { CompleteCryptoHandshake(); const QuicConfig* config = session_->config(); - EXPECT_EQ(FLAGS_enable_quic_pacing ? kPACE : kQBIC, - config->congestion_feedback()); + EXPECT_EQ(kQBIC, config->congestion_feedback()); EXPECT_EQ(kDefaultTimeoutSecs, config->idle_connection_state_lifetime().ToSeconds()); EXPECT_EQ(kDefaultMaxStreamsPerConnection, diff --git a/net/quic/quic_crypto_server_stream.cc b/net/quic/quic_crypto_server_stream.cc index 7bd2c034d93b2..df8f0d8dc0e08 100644 --- a/net/quic/quic_crypto_server_stream.cc +++ b/net/quic/quic_crypto_server_stream.cc @@ -15,13 +15,23 @@ namespace net { +void ServerHelloNotifier::OnAckNotification( + int num_original_packets, + int num_original_bytes, + int num_retransmitted_packets, + int num_retransmitted_bytes, + QuicTime::Delta delta_largest_observed) { + server_stream_->OnServerHelloAcked(); +} + QuicCryptoServerStream::QuicCryptoServerStream( const QuicCryptoServerConfig& crypto_config, QuicSession* session) : QuicCryptoStream(session), crypto_config_(crypto_config), validate_client_hello_cb_(NULL), - num_handshake_messages_(0) { + num_handshake_messages_(0), + num_server_config_update_messages_sent_(0) { } QuicCryptoServerStream::~QuicCryptoServerStream() { @@ -116,7 +126,16 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( session()->connection()->SetDecrypter( crypto_negotiated_params_.initial_crypters.decrypter.release(), ENCRYPTION_INITIAL); - SendHandshakeMessage(reply); + + // We want to be notified when the SHLO is ACKed so that we can disable + // HANDSHAKE_MODE in the sent packet manager. + if (session()->connection()->version() <= QUIC_VERSION_21) { + SendHandshakeMessage(reply); + } else { + scoped_refptr server_hello_notifier( + new ServerHelloNotifier(this)); + SendHandshakeMessage(reply, server_hello_notifier.get()); + } session()->connection()->SetEncrypter( ENCRYPTION_FORWARD_SECURE, @@ -130,6 +149,37 @@ void QuicCryptoServerStream::FinishProcessingHandshakeMessage( encryption_established_ = true; handshake_confirmed_ = true; session()->OnCryptoHandshakeEvent(QuicSession::HANDSHAKE_CONFIRMED); + + // Now that the handshake is complete, send an updated server config and + // source-address token to the client. + SendServerConfigUpdate(); +} + +void QuicCryptoServerStream::SendServerConfigUpdate() { + if (session()->connection()->version() <= QUIC_VERSION_21) { + return; + } + + CryptoHandshakeMessage server_config_update_message; + if (!crypto_config_.BuildServerConfigUpdateMessage( + session()->connection()->peer_address(), + session()->connection()->clock(), + session()->connection()->random_generator(), + crypto_negotiated_params_, &server_config_update_message)) { + DVLOG(1) << "Server: Failed to build server config update (SCUP)!"; + return; + } + + DVLOG(1) << "Server: Sending server config update: " + << server_config_update_message.DebugString(); + const QuicData& data = server_config_update_message.GetSerialized(); + WriteOrBufferData(string(data.data(), data.length()), false, NULL); + + ++num_server_config_update_messages_sent_; +} + +void QuicCryptoServerStream::OnServerHelloAcked() { + session()->connection()->OnHandshakeComplete(); } bool QuicCryptoServerStream::GetBase64SHA256ClientChannelID( diff --git a/net/quic/quic_crypto_server_stream.h b/net/quic/quic_crypto_server_stream.h index ee4013120e4f8..9cb242bea802b 100644 --- a/net/quic/quic_crypto_server_stream.h +++ b/net/quic/quic_crypto_server_stream.h @@ -16,12 +16,37 @@ namespace net { class CryptoHandshakeMessage; class QuicCryptoServerConfig; +class QuicCryptoServerStream; class QuicSession; namespace test { class CryptoTestUtils; } // namespace test +// Receives a notification when the server hello (SHLO) has been ACKed by the +// peer. At this point we disable HANDSHAKE_MODE in the sent packet manager. +class NET_EXPORT_PRIVATE ServerHelloNotifier : public + QuicAckNotifier::DelegateInterface { + public: + explicit ServerHelloNotifier(QuicCryptoServerStream* stream) + : server_stream_(stream) {} + + // QuicAckNotifier::DelegateInterface implementation + virtual void OnAckNotification( + int num_original_packets, + int num_original_bytes, + int num_retransmitted_packets, + int num_retransmitted_bytes, + QuicTime::Delta delta_largest_observed) OVERRIDE; + + private: + virtual ~ServerHelloNotifier() {} + + QuicCryptoServerStream* server_stream_; + + DISALLOW_COPY_AND_ASSIGN(ServerHelloNotifier); +}; + class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { public: QuicCryptoServerStream(const QuicCryptoServerConfig& crypto_config, @@ -44,6 +69,17 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { uint8 num_handshake_messages() const { return num_handshake_messages_; } + int num_server_config_update_messages_sent() const { + return num_server_config_update_messages_sent_; + } + + // Sends the latest server config and source-address token to the client. + void SendServerConfigUpdate(); + + // Called by the ServerHello AckNotifier once the SHLO has been ACKed by the + // client. + void OnServerHelloAcked(); + protected: virtual QuicErrorCode ProcessClientHello( const CryptoHandshakeMessage& message, @@ -90,8 +126,12 @@ class NET_EXPORT_PRIVATE QuicCryptoServerStream : public QuicCryptoStream { // handshake message is being validated. ValidateCallback* validate_client_hello_cb_; + // Number of handshake messages received by this stream. uint8 num_handshake_messages_; + // Number of server config update (SCUP) messages sent by this stream. + int num_server_config_update_messages_sent_; + DISALLOW_COPY_AND_ASSIGN(QuicCryptoServerStream); }; diff --git a/net/quic/quic_crypto_server_stream_test.cc b/net/quic/quic_crypto_server_stream_test.cc index 8e8da000acce3..c2e8bf85f36b0 100644 --- a/net/quic/quic_crypto_server_stream_test.cc +++ b/net/quic/quic_crypto_server_stream_test.cc @@ -131,6 +131,7 @@ TEST_P(QuicCryptoServerStreamTest, ConnectedAfterCHLO) { EXPECT_EQ(2, CompleteCryptoHandshake()); EXPECT_TRUE(stream_.encryption_established()); EXPECT_TRUE(stream_.handshake_confirmed()); + EXPECT_EQ(1, stream_.num_server_config_update_messages_sent()); } TEST_P(QuicCryptoServerStreamTest, ZeroRTT) { @@ -223,6 +224,7 @@ TEST_P(QuicCryptoServerStreamTest, ZeroRTT) { } EXPECT_EQ(1, client->num_sent_client_hellos()); + EXPECT_EQ(1, server->num_server_config_update_messages_sent()); } TEST_P(QuicCryptoServerStreamTest, MessageAfterHandshake) { diff --git a/net/quic/quic_crypto_stream.cc b/net/quic/quic_crypto_stream.cc index 25467cb3cb645..8aa8d3effbd85 100644 --- a/net/quic/quic_crypto_stream.cc +++ b/net/quic/quic_crypto_stream.cc @@ -18,13 +18,12 @@ using base::StringPiece; namespace net { -#define ENDPOINT (is_server_ ? "Server: " : " Client: ") +#define ENDPOINT (session()->is_server() ? "Server: " : " Client: ") QuicCryptoStream::QuicCryptoStream(QuicSession* session) : ReliableQuicStream(kCryptoStreamId, session), encryption_established_(false), - handshake_confirmed_(false), - is_server_(session->is_server()) { + handshake_confirmed_(false) { crypto_framer_.set_visitor(this); if (version() <= QUIC_VERSION_20) { // Prior to QUIC_VERSION_21 the crypto stream is not subject to any flow @@ -61,11 +60,17 @@ QuicPriority QuicCryptoStream::EffectivePriority() const { void QuicCryptoStream::SendHandshakeMessage( const CryptoHandshakeMessage& message) { + SendHandshakeMessage(message, NULL); +} + +void QuicCryptoStream::SendHandshakeMessage( + const CryptoHandshakeMessage& message, + QuicAckNotifier::DelegateInterface* delegate) { DVLOG(1) << ENDPOINT << "Sending " << message.DebugString(); session()->OnCryptoHandshakeMessageSent(message); const QuicData& data = message.GetSerialized(); // TODO(wtc): check the return value. - WriteOrBufferData(string(data.data(), data.length()), false, NULL); + WriteOrBufferData(string(data.data(), data.length()), false, delegate); } bool QuicCryptoStream::ExportKeyingMaterial( diff --git a/net/quic/quic_crypto_stream.h b/net/quic/quic_crypto_stream.h index 131e096f31025..dc8b227debb79 100644 --- a/net/quic/quic_crypto_stream.h +++ b/net/quic/quic_crypto_stream.h @@ -45,6 +45,10 @@ class NET_EXPORT_PRIVATE QuicCryptoStream // Sends |message| to the peer. // TODO(wtc): return a success/failure status. void SendHandshakeMessage(const CryptoHandshakeMessage& message); + // As above, but registers |delegate| for notification when |message| has been + // ACKed by the peer. + void SendHandshakeMessage(const CryptoHandshakeMessage& message, + QuicAckNotifier::DelegateInterface* delegate); // Performs key extraction to derive a new secret of |result_len| bytes // dependent on |label|, |context|, and the stream's negotiated subkey secret. @@ -69,8 +73,6 @@ class NET_EXPORT_PRIVATE QuicCryptoStream private: CryptoFramer crypto_framer_; - bool is_server_; - DISALLOW_COPY_AND_ASSIGN(QuicCryptoStream); }; diff --git a/net/quic/quic_crypto_stream_test.cc b/net/quic/quic_crypto_stream_test.cc index b21d5c0108bee..76649128dadc8 100644 --- a/net/quic/quic_crypto_stream_test.cc +++ b/net/quic/quic_crypto_stream_test.cc @@ -10,7 +10,6 @@ #include "base/memory/scoped_ptr.h" #include "net/quic/crypto/crypto_handshake.h" #include "net/quic/crypto/crypto_protocol.h" -#include "net/quic/quic_flags.h" #include "net/quic/test_tools/crypto_test_utils.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/quic/test_tools/reliable_quic_stream_peer.h" @@ -104,8 +103,6 @@ TEST_F(QuicCryptoStreamTest, ProcessBadData) { } TEST_F(QuicCryptoStreamTest, NoConnectionLevelFlowControl) { - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); if (connection_->version() <= QUIC_VERSION_20) { EXPECT_FALSE(stream_.flow_controller()->IsEnabled()); } else { diff --git a/net/quic/quic_data_stream_test.cc b/net/quic/quic_data_stream_test.cc index b36053c6e7548..26dcd6cb90890 100644 --- a/net/quic/quic_data_stream_test.cc +++ b/net/quic/quic_data_stream_test.cc @@ -6,7 +6,6 @@ #include "net/quic/quic_ack_notifier.h" #include "net/quic/quic_connection.h" -#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" #include "net/quic/quic_write_blocked_list.h" #include "net/quic/spdy_utils.h" @@ -418,9 +417,6 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlWindowUpdate) { if (GetParam() < QUIC_VERSION_19) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); - Initialize(kShouldProcessData); // Set a small flow control limit for streams and connection. @@ -505,8 +501,6 @@ TEST_P(QuicDataStreamTest, ConnectionFlowControlViolation) { if (GetParam() < QUIC_VERSION_19) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Stream should not process data, so that data gets buffered in the // sequencer, triggering flow control limits. diff --git a/net/quic/quic_dispatcher.cc b/net/quic/quic_dispatcher.cc index 5c6836aa3db4f..802f00b8dfa06 100644 --- a/net/quic/quic_dispatcher.cc +++ b/net/quic/quic_dispatcher.cc @@ -164,7 +164,6 @@ QuicDispatcher::QuicDispatcher(const QuicConfig& config, delete_sessions_alarm_( helper_->CreateAlarm(new DeleteSessionsAlarm(this))), supported_versions_(supported_versions), - supported_versions_no_connection_flow_control_(supported_versions), current_packet_(NULL), framer_(supported_versions, /*unused*/ QuicTime::Zero(), true), framer_visitor_(new QuicFramerVisitor(this)) { @@ -180,19 +179,6 @@ void QuicDispatcher::Initialize(QuicServerPacketWriter* writer) { DCHECK(writer_ == NULL); writer_.reset(writer); time_wait_list_manager_.reset(CreateQuicTimeWaitListManager()); - - // Remove all versions > QUIC_VERSION_18 from the - // supported_versions_no_connection_flow_control_ vector. - QuicVersionVector::iterator connection_it = - find(supported_versions_no_connection_flow_control_.begin(), - supported_versions_no_connection_flow_control_.end(), - QUIC_VERSION_19); - if (connection_it != supported_versions_no_connection_flow_control_.end()) { - supported_versions_no_connection_flow_control_.erase( - supported_versions_no_connection_flow_control_.begin(), - connection_it + 1); - } - CHECK(!supported_versions_no_connection_flow_control_.empty()); } void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address, @@ -375,27 +361,14 @@ QuicConnection* QuicDispatcher::CreateQuicConnection( const IPEndPoint& client_address, QuicPerConnectionPacketWriter* writer) { QuicConnection* connection; - if (FLAGS_enable_quic_connection_flow_control_2) { - DVLOG(1) << "Creating QuicDispatcher with all versions."; - connection = new QuicConnection(connection_id, - client_address, - helper_, - writer, - false /* owns_writer */, - true /* is_server */, - supported_versions_); - } else { - DVLOG(1) << "Connection flow control disabled, creating QuicDispatcher " - << "WITHOUT version 19 or higher."; - connection = new QuicConnection( - connection_id, - client_address, - helper_, - writer, - false /* owns_writer */, - true /* is_server */, - supported_versions_no_connection_flow_control_); - } + connection = new QuicConnection( + connection_id, + client_address, + helper_, + writer, + false /* owns_writer */, + true /* is_server */, + supported_versions_); writer->set_connection(connection); return connection; } diff --git a/net/quic/quic_dispatcher.h b/net/quic/quic_dispatcher.h index e7a5e95013391..2687e8b52c306 100644 --- a/net/quic/quic_dispatcher.h +++ b/net/quic/quic_dispatcher.h @@ -70,7 +70,7 @@ class QuicDispatcher : public QuicBlockedWriterInterface, virtual ~QuicDispatcher(); - // Takes ownership of the packet writer + // Takes ownership of the packet writer. virtual void Initialize(QuicServerPacketWriter* writer); // Process the incoming packet by creating a new session, passing it to @@ -137,11 +137,6 @@ class QuicDispatcher : public QuicBlockedWriterInterface, return supported_versions_; } - const QuicVersionVector& supported_versions_no_connection_flow_control() - const { - return supported_versions_no_connection_flow_control_; - } - const IPEndPoint& current_server_address() { return current_server_address_; } @@ -206,14 +201,6 @@ class QuicDispatcher : public QuicBlockedWriterInterface, // skipped as necessary). const QuicVersionVector supported_versions_; - // Versions which do not support *connection* flow control (introduced in - // QUIC_VERSION_19). - // This is used to construct new QuicConnections when connection flow control - // is disabled via flag. - // TODO(rjshade): Remove this when - // FLAGS_enable_quic_connection_flow_control_2 is removed. - QuicVersionVector supported_versions_no_connection_flow_control_; - // Information about the packet currently being handled. IPEndPoint current_client_address_; IPEndPoint current_server_address_; diff --git a/net/quic/quic_flags.cc b/net/quic/quic_flags.cc index 58980b343188f..affa27129c3be 100644 --- a/net/quic/quic_flags.cc +++ b/net/quic/quic_flags.cc @@ -19,20 +19,11 @@ bool FLAGS_track_retransmission_history = false; // request pacing for the server to enable it. bool FLAGS_enable_quic_pacing = true; -// Do not remove this flag until b/11792453 is marked as Fixed. -// If true, turns on connection level flow control in QUIC. -// If this is disabled, all in flight QUIC connections talking QUIC_VERSION_19 -// or higher will timeout. New connections will be fine. -bool FLAGS_enable_quic_connection_flow_control_2 = true; - bool FLAGS_quic_allow_oversized_packets_for_test = false; // When true, the use time based loss detection instead of nack. bool FLAGS_quic_use_time_loss_detection = false; -// If true, allow peer port migration of established QUIC connections. -bool FLAGS_quic_allow_port_migration = true; - // If true, it will return as soon as an error is detected while validating // CHLO. bool FLAGS_use_early_return_when_verifying_chlo = true; @@ -44,3 +35,6 @@ bool FLAGS_send_quic_crypto_reject_reason = false; // packets, to reduce latency of data delivery to the application. The client // must also request FEC protection for the server to use FEC. bool FLAGS_enable_quic_fec = false; + +// If true, a QUIC connection with too many unfinished streams will be closed. +bool FLAGS_close_quic_connection_unfinished_streams = false; diff --git a/net/quic/quic_flags.h b/net/quic/quic_flags.h index f9654af0ed41d..c68b822413519 100644 --- a/net/quic/quic_flags.h +++ b/net/quic/quic_flags.h @@ -9,12 +9,11 @@ NET_EXPORT_PRIVATE extern bool FLAGS_track_retransmission_history; NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_pacing; -NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_connection_flow_control_2; NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_oversized_packets_for_test; NET_EXPORT_PRIVATE extern bool FLAGS_quic_use_time_loss_detection; -NET_EXPORT_PRIVATE extern bool FLAGS_quic_allow_port_migration; NET_EXPORT_PRIVATE extern bool FLAGS_use_early_return_when_verifying_chlo; NET_EXPORT_PRIVATE extern bool FLAGS_send_quic_crypto_reject_reason; NET_EXPORT_PRIVATE extern bool FLAGS_enable_quic_fec; +NET_EXPORT_PRIVATE extern bool FLAGS_close_quic_connection_unfinished_streams; #endif // NET_QUIC_QUIC_FLAGS_H_ diff --git a/net/quic/quic_flow_controller.cc b/net/quic/quic_flow_controller.cc index 9d0903172cf86..855d16530cfc5 100644 --- a/net/quic/quic_flow_controller.cc +++ b/net/quic/quic_flow_controller.cc @@ -179,12 +179,7 @@ void QuicFlowController::Disable() { } bool QuicFlowController::IsEnabled() const { - bool connection_flow_control_enabled = - (id_ == kConnectionLevelId && - FLAGS_enable_quic_connection_flow_control_2); - bool stream_flow_control_enabled = (id_ != kConnectionLevelId); - return (connection_flow_control_enabled || stream_flow_control_enabled) && - is_enabled_; + return is_enabled_; } bool QuicFlowController::IsBlocked() const { diff --git a/net/quic/quic_framer.cc b/net/quic/quic_framer.cc index 2ef5c7fbdf1d8..fe705015c1ed9 100644 --- a/net/quic/quic_framer.cc +++ b/net/quic/quic_framer.cc @@ -975,8 +975,8 @@ QuicFramer::AckFrameInfo QuicFramer::GetAckFrameInfo( *iter == (last_missing + 1)) { ++cur_range_length; } else { - ack_info.nack_ranges[last_missing - cur_range_length] - = cur_range_length; + ack_info.nack_ranges[last_missing - cur_range_length] = + cur_range_length; cur_range_length = 0; } ack_info.max_delta = max(ack_info.max_delta, *iter - last_missing); @@ -1417,59 +1417,12 @@ bool QuicFramer::ProcessQuicCongestionFeedbackFrame( static_cast(feedback_type); switch (frame->type) { - case kInterArrival: { - CongestionFeedbackMessageInterArrival* inter_arrival = - &frame->inter_arrival; - uint8 num_received_packets; - if (!reader_->ReadBytes(&num_received_packets, 1)) { - set_detailed_error("Unable to read num received packets."); - return false; - } - - if (num_received_packets > 0u) { - uint64 smallest_received; - if (!ProcessPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, - &smallest_received)) { - set_detailed_error("Unable to read smallest received."); - return false; - } - - uint64 time_received_us; - if (!reader_->ReadUInt64(&time_received_us)) { - set_detailed_error("Unable to read time received."); - return false; - } - QuicTime time_received = creation_time_.Add( - QuicTime::Delta::FromMicroseconds(time_received_us)); - - inter_arrival->received_packet_times.insert( - make_pair(smallest_received, time_received)); - - for (uint8 i = 0; i < num_received_packets - 1; ++i) { - uint16 sequence_delta; - if (!reader_->ReadUInt16(&sequence_delta)) { - set_detailed_error( - "Unable to read sequence delta in received packets."); - return false; - } - - int32 time_delta_us; - if (!reader_->ReadBytes(&time_delta_us, sizeof(time_delta_us))) { - set_detailed_error( - "Unable to read time delta in received packets."); - return false; - } - QuicPacketSequenceNumber packet = smallest_received + sequence_delta; - inter_arrival->received_packet_times.insert( - make_pair(packet, time_received.Add( - QuicTime::Delta::FromMicroseconds(time_delta_us)))); - } - } - break; + case kTimestamp: { + set_detailed_error("Timestamp feedback not supported."); + return false; } case kTCP: { CongestionFeedbackMessageTCP* tcp = &frame->tcp; - // TODO(ianswett): Remove receive window, since it's constant. uint16 receive_window = 0; if (!reader_->ReadUInt16(&receive_window)) { set_detailed_error("Unable to read receive window."); @@ -1788,17 +1741,8 @@ size_t QuicFramer::ComputeFrameLength( len += 1; // Congestion feedback type. switch (congestion_feedback.type) { - case kInterArrival: { - const CongestionFeedbackMessageInterArrival& inter_arrival = - congestion_feedback.inter_arrival; - len += 1; // Number received packets. - if (inter_arrival.received_packet_times.size() > 0) { - len += PACKET_6BYTE_SEQUENCE_NUMBER; // Smallest received. - len += 8; // Time. - // 2 bytes per sequence number delta plus 4 bytes per delta time. - len += PACKET_6BYTE_SEQUENCE_NUMBER * - (inter_arrival.received_packet_times.size() - 1); - } + case kTimestamp: { + set_detailed_error("Timestamp feedback not supported."); break; } case kTCP: @@ -2094,55 +2038,9 @@ bool QuicFramer::AppendCongestionFeedbackFrame( } switch (frame.type) { - case kInterArrival: { - const CongestionFeedbackMessageInterArrival& inter_arrival = - frame.inter_arrival; - DCHECK_GE(numeric_limits::max(), - inter_arrival.received_packet_times.size()); - if (inter_arrival.received_packet_times.size() > - numeric_limits::max()) { - return false; - } - // TODO(ianswett): Make num_received_packets a varint. - uint8 num_received_packets = - inter_arrival.received_packet_times.size(); - if (!writer->WriteBytes(&num_received_packets, 1)) { - return false; - } - if (num_received_packets > 0) { - TimeMap::const_iterator it = - inter_arrival.received_packet_times.begin(); - - QuicPacketSequenceNumber lowest_sequence = it->first; - if (!AppendPacketSequenceNumber(PACKET_6BYTE_SEQUENCE_NUMBER, - lowest_sequence, writer)) { - return false; - } - - QuicTime lowest_time = it->second; - if (!writer->WriteUInt64( - lowest_time.Subtract(creation_time_).ToMicroseconds())) { - return false; - } - - for (++it; it != inter_arrival.received_packet_times.end(); ++it) { - QuicPacketSequenceNumber sequence_delta = it->first - lowest_sequence; - DCHECK_GE(numeric_limits::max(), sequence_delta); - if (sequence_delta > numeric_limits::max()) { - return false; - } - if (!writer->WriteUInt16(static_cast(sequence_delta))) { - return false; - } - - int32 time_delta_us = - it->second.Subtract(lowest_time).ToMicroseconds(); - if (!writer->WriteBytes(&time_delta_us, sizeof(time_delta_us))) { - return false; - } - } - } - break; + case kTimestamp: { + // Timestamp feedback not supported. + return false; } case kTCP: { const CongestionFeedbackMessageTCP& tcp = frame.tcp; diff --git a/net/quic/quic_framer.h b/net/quic/quic_framer.h index 1349f9360c6c0..a958786c1d7b6 100644 --- a/net/quic/quic_framer.h +++ b/net/quic/quic_framer.h @@ -524,7 +524,7 @@ class NET_EXPORT_PRIVATE QuicFramer { bool is_server_; // If false, skip validation that the public flags are set to legal values. bool validate_flags_; - // The time this frames was created. Time written to the wire will be + // The time this framer was created. Time written to the wire will be // written as a delta from this value. QuicTime creation_time_; diff --git a/net/quic/quic_framer_test.cc b/net/quic/quic_framer_test.cc index f8d1c5be27ec7..d14e673f85e1f 100644 --- a/net/quic/quic_framer_test.cc +++ b/net/quic/quic_framer_test.cc @@ -46,7 +46,7 @@ const size_t kVersionOffset = kConnectionIdOffset + PACKET_8BYTE_CONNECTION_ID; // Size in bytes of the stream frame fields for an arbitrary StreamID and // offset and the last frame in a packet. -size_t GetMinStreamFrameSize(QuicVersion version) { +size_t GetMinStreamFrameSize() { return kQuicFrameTypeSize + kQuicMaxStreamIdSize + kQuicMaxStreamOffsetSize; } @@ -461,8 +461,7 @@ class QuicFramerTest : public ::testing::TestWithParam { size_t stream_id_size, bool include_version) { // Now test framing boundaries - for (size_t i = kQuicFrameTypeSize; - i < GetMinStreamFrameSize(framer_.version()); ++i) { + for (size_t i = kQuicFrameTypeSize; i < GetMinStreamFrameSize(); ++i) { string expected_error; if (i < kQuicFrameTypeSize + stream_id_size) { expected_error = "Unable to read stream_id."; @@ -2045,96 +2044,6 @@ TEST_P(QuicFramerTest, CongestionFeedbackFrameTCP) { } } -TEST_P(QuicFramerTest, CongestionFeedbackFrameInterArrival) { - unsigned char packet[] = { - // public flags (8 byte connection_id) - 0x3C, - // connection_id - 0x10, 0x32, 0x54, 0x76, - 0x98, 0xBA, 0xDC, 0xFE, - // packet sequence number - 0xBC, 0x9A, 0x78, 0x56, - 0x34, 0x12, - // private flags - 0x00, - - // frame type (congestion feedback frame) - 0x20, - // congestion feedback type (inter arrival) - 0x01, - // num received packets - 0x03, - // lowest sequence number - 0xBA, 0x9A, 0x78, 0x56, - 0x34, 0x12, - // receive time - 0x87, 0x96, 0xA5, 0xB4, - 0xC3, 0xD2, 0xE1, 0x07, - // sequence delta - 0x01, 0x00, - // time delta - 0x01, 0x00, 0x00, 0x00, - // sequence delta (skip one packet) - 0x03, 0x00, - // time delta - 0x02, 0x00, 0x00, 0x00, - }; - - QuicEncryptedPacket encrypted(AsChars(packet), arraysize(packet), false); - EXPECT_TRUE(framer_.ProcessPacket(encrypted)); - - EXPECT_EQ(QUIC_NO_ERROR, framer_.error()); - ASSERT_TRUE(visitor_.header_.get()); - EXPECT_TRUE(CheckDecryption(encrypted, !kIncludeVersion)); - - EXPECT_EQ(0u, visitor_.stream_frames_.size()); - ASSERT_EQ(1u, visitor_.congestion_feedback_frames_.size()); - const QuicCongestionFeedbackFrame& frame = - *visitor_.congestion_feedback_frames_[0]; - ASSERT_EQ(kInterArrival, frame.type); - ASSERT_EQ(3u, frame.inter_arrival.received_packet_times.size()); - TimeMap::const_iterator iter = - frame.inter_arrival.received_packet_times.begin(); - EXPECT_EQ(GG_UINT64_C(0x0123456789ABA), iter->first); - EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59687), - iter->second.Subtract(start_).ToMicroseconds()); - ++iter; - EXPECT_EQ(GG_UINT64_C(0x0123456789ABB), iter->first); - EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59688), - iter->second.Subtract(start_).ToMicroseconds()); - ++iter; - EXPECT_EQ(GG_UINT64_C(0x0123456789ABD), iter->first); - EXPECT_EQ(GG_INT64_C(0x07E1D2C3B4A59689), - iter->second.Subtract(start_).ToMicroseconds()); - - // Now test framing boundaries - for (size_t i = kQuicFrameTypeSize; i < 29; ++i) { - string expected_error; - if (i < 2) { - expected_error = "Unable to read congestion feedback type."; - } else if (i < 3) { - expected_error = "Unable to read num received packets."; - } else if (i < 9) { - expected_error = "Unable to read smallest received."; - } else if (i < 17) { - expected_error = "Unable to read time received."; - } else if (i < 19) { - expected_error = "Unable to read sequence delta in received packets."; - } else if (i < 23) { - expected_error = "Unable to read time delta in received packets."; - } else if (i < 25) { - expected_error = "Unable to read sequence delta in received packets."; - } else if (i < 29) { - expected_error = "Unable to read time delta in received packets."; - } - CheckProcessingFails( - packet, - i + GetPacketHeaderSize(PACKET_8BYTE_CONNECTION_ID, !kIncludeVersion, - PACKET_6BYTE_SEQUENCE_NUMBER, NOT_IN_FEC_GROUP), - expected_error, QUIC_INVALID_CONGESTION_FEEDBACK_DATA); - } -} - TEST_P(QuicFramerTest, CongestionFeedbackFrameInvalidFeedback) { unsigned char packet[] = { // public flags (8 byte connection_id) @@ -3429,75 +3338,6 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketTCP) { AsChars(packet), arraysize(packet)); } -TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInterArrival) { - QuicPacketHeader header; - header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); - header.public_header.reset_flag = false; - header.public_header.version_flag = false; - header.fec_flag = false; - header.entropy_flag = false; - header.packet_sequence_number = GG_UINT64_C(0x123456789ABC); - header.fec_group = 0; - - QuicCongestionFeedbackFrame frame; - frame.type = kInterArrival; - frame.inter_arrival.received_packet_times.insert( - make_pair(GG_UINT64_C(0x0123456789ABA), - start_.Add(QuicTime::Delta::FromMicroseconds( - GG_UINT64_C(0x07E1D2C3B4A59687))))); - frame.inter_arrival.received_packet_times.insert( - make_pair(GG_UINT64_C(0x0123456789ABB), - start_.Add(QuicTime::Delta::FromMicroseconds( - GG_UINT64_C(0x07E1D2C3B4A59688))))); - frame.inter_arrival.received_packet_times.insert( - make_pair(GG_UINT64_C(0x0123456789ABD), - start_.Add(QuicTime::Delta::FromMicroseconds( - GG_UINT64_C(0x07E1D2C3B4A59689))))); - QuicFrames frames; - frames.push_back(QuicFrame(&frame)); - - unsigned char packet[] = { - // public flags (8 byte connection_id) - 0x3C, - // connection_id - 0x10, 0x32, 0x54, 0x76, - 0x98, 0xBA, 0xDC, 0xFE, - // packet sequence number - 0xBC, 0x9A, 0x78, 0x56, - 0x34, 0x12, - // private flags - 0x00, - - // frame type (congestion feedback frame) - 0x20, - // congestion feedback type (inter arrival) - 0x01, - // num received packets - 0x03, - // lowest sequence number - 0xBA, 0x9A, 0x78, 0x56, - 0x34, 0x12, - // receive time - 0x87, 0x96, 0xA5, 0xB4, - 0xC3, 0xD2, 0xE1, 0x07, - // sequence delta - 0x01, 0x00, - // time delta - 0x01, 0x00, 0x00, 0x00, - // sequence delta (skip one packet) - 0x03, 0x00, - // time delta - 0x02, 0x00, 0x00, 0x00, - }; - - scoped_ptr data(BuildDataPacket(header, frames)); - ASSERT_TRUE(data != NULL); - - test::CompareCharArraysWithHexError("constructed packet", - data->data(), data->length(), - AsChars(packet), arraysize(packet)); -} - TEST_P(QuicFramerTest, BuildStopWaitingPacket) { QuicPacketHeader header; header.public_header.connection_id = GG_UINT64_C(0xFEDCBA9876543210); @@ -3556,7 +3396,7 @@ TEST_P(QuicFramerTest, BuildCongestionFeedbackFramePacketInvalidFeedback) { QuicCongestionFeedbackFrame congestion_feedback_frame; congestion_feedback_frame.type = - static_cast(kInterArrival + 1); + static_cast(kTimestamp + 1); QuicFrames frames; frames.push_back(QuicFrame(&congestion_feedback_frame)); diff --git a/net/quic/quic_headers_stream_test.cc b/net/quic/quic_headers_stream_test.cc index 94f90c6392b72..f2265cec9a00f 100644 --- a/net/quic/quic_headers_stream_test.cc +++ b/net/quic/quic_headers_stream_test.cc @@ -4,7 +4,6 @@ #include "net/quic/quic_headers_stream.h" -#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" #include "net/quic/spdy_utils.h" #include "net/quic/test_tools/quic_connection_peer.h" @@ -326,8 +325,6 @@ TEST_P(QuicHeadersStreamTest, ProcessSpdyWindowUpdateFrame) { } TEST_P(QuicHeadersStreamTest, NoConnectionLevelFlowControl) { - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); if (connection_->version() <= QUIC_VERSION_20) { EXPECT_FALSE(headers_stream_->flow_controller()->IsEnabled()); } else { diff --git a/net/quic/quic_http_stream.cc b/net/quic/quic_http_stream.cc index 36eac6aee8ae1..3ddfa06261607 100644 --- a/net/quic/quic_http_stream.cc +++ b/net/quic/quic_http_stream.cc @@ -102,12 +102,15 @@ void QuicHttpStream::OnStreamReady(int rv) { int QuicHttpStream::SendRequest(const HttpRequestHeaders& request_headers, HttpResponseInfo* response, const CompletionCallback& callback) { - CHECK(stream_); CHECK(!request_body_stream_); CHECK(!response_info_); CHECK(!callback.is_null()); CHECK(response); + if (!stream_) { + return ERR_CONNECTION_CLOSED; + } + QuicPriority priority = ConvertRequestPriorityToQuicPriority(priority_); stream_->set_priority(priority); // Store the serialized request headers. @@ -215,6 +218,8 @@ void QuicHttpStream::Close(bool not_reusable) { stream_->SetDelegate(NULL); stream_->Reset(QUIC_STREAM_CANCELLED); stream_ = NULL; + response_status_ = was_handshake_confirmed_ ? + ERR_CONNECTION_CLOSED : ERR_QUIC_HANDSHAKE_FAILED; } } @@ -352,6 +357,7 @@ void QuicHttpStream::OnCryptoHandshakeConfirmed() { } void QuicHttpStream::OnSessionClosed(int error) { + Close(false); session_error_ = error; session_.reset(); } diff --git a/net/quic/quic_http_stream_test.cc b/net/quic/quic_http_stream_test.cc index 5e46258f2cbce..96ce8d3599bef 100644 --- a/net/quic/quic_http_stream_test.cc +++ b/net/quic/quic_http_stream_test.cc @@ -11,6 +11,7 @@ #include "net/base/upload_bytes_element_reader.h" #include "net/base/upload_data_stream.h" #include "net/http/http_response_headers.h" +#include "net/http/transport_security_state.h" #include "net/quic/congestion_control/receive_algorithm_interface.h" #include "net/quic/congestion_control/send_algorithm_interface.h" #include "net/quic/crypto/crypto_protocol.h" @@ -214,6 +215,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam { scoped_ptr(socket), writer_.Pass(), NULL, &crypto_client_stream_factory_, + &transport_security_state_, make_scoped_ptr((QuicServerInfo*)NULL), QuicServerId(kServerHostname, kServerPort, false, PRIVACY_MODE_DISABLED), @@ -299,6 +301,7 @@ class QuicHttpStreamTest : public ::testing::TestWithParam { testing::StrictMock visitor_; scoped_ptr stream_; scoped_ptr writer_; + TransportSecurityState transport_security_state_; scoped_ptr session_; QuicCryptoClientConfig crypto_config_; TestCompletionCallback callback_; @@ -424,6 +427,44 @@ TEST_P(QuicHttpStreamTest, GetRequestLargeResponse) { EXPECT_TRUE(AtEof()); } +// Regression test for http://crbug.com/409101 +TEST_P(QuicHttpStreamTest, SessionClosedBeforeSendRequest) { + SetRequest("GET", "/", DEFAULT_PRIORITY); + Initialize(); + + request_.method = "GET"; + request_.url = GURL("http://www.google.com/"); + + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, + net_log_, callback_.callback())); + + session_->connection()->CloseConnection(QUIC_NO_ERROR, true); + + EXPECT_EQ(ERR_CONNECTION_CLOSED, + stream_->SendRequest(headers_, &response_, + callback_.callback())); +} + +// Regression test for http://crbug.com/409871 +TEST_P(QuicHttpStreamTest, SessionClosedBeforeReadResponseHeaders) { + SetRequest("GET", "/", DEFAULT_PRIORITY); + AddWrite(ConstructRequestHeadersPacket(1, kFin)); + Initialize(); + + request_.method = "GET"; + request_.url = GURL("http://www.google.com/"); + + EXPECT_EQ(OK, stream_->InitializeStream(&request_, DEFAULT_PRIORITY, + net_log_, callback_.callback())); + + EXPECT_EQ(OK, stream_->SendRequest(headers_, &response_, + callback_.callback())); + + session_->connection()->CloseConnection(QUIC_NO_ERROR, true); + + EXPECT_NE(OK, stream_->ReadResponseHeaders(callback_.callback())); +} + TEST_P(QuicHttpStreamTest, SendPostRequest) { SetRequest("POST", "/", DEFAULT_PRIORITY); AddWrite(ConstructRequestHeadersPacket(1, !kFin)); diff --git a/net/quic/quic_protocol.cc b/net/quic/quic_protocol.cc index a1975022bb09d..419820a8c1cee 100644 --- a/net/quic/quic_protocol.cc +++ b/net/quic/quic_protocol.cc @@ -169,6 +169,8 @@ QuicTag QuicVersionToQuicTag(const QuicVersion version) { return MakeQuicTag('Q', '0', '2', '0'); case QUIC_VERSION_21: return MakeQuicTag('Q', '0', '2', '1'); + case QUIC_VERSION_22: + return MakeQuicTag('Q', '0', '2', '2'); default: // This shold be an ERROR because we should never attempt to convert an // invalid QuicVersion to be written to the wire. @@ -200,6 +202,7 @@ string QuicVersionToString(const QuicVersion version) { RETURN_STRING_LITERAL(QUIC_VERSION_19); RETURN_STRING_LITERAL(QUIC_VERSION_20); RETURN_STRING_LITERAL(QUIC_VERSION_21); + RETURN_STRING_LITERAL(QUIC_VERSION_22); default: return "QUIC_VERSION_UNSUPPORTED"; } @@ -271,11 +274,10 @@ CongestionFeedbackMessageTCP::CongestionFeedbackMessageTCP() : receive_window(0) { } -CongestionFeedbackMessageInterArrival::CongestionFeedbackMessageInterArrival() { +CongestionFeedbackMessageTimestamp::CongestionFeedbackMessageTimestamp() { } -CongestionFeedbackMessageInterArrival:: - ~CongestionFeedbackMessageInterArrival() {} +CongestionFeedbackMessageTimestamp::~CongestionFeedbackMessageTimestamp() {} QuicCongestionFeedbackFrame::QuicCongestionFeedbackFrame() : type(kTCP) {} @@ -502,13 +504,12 @@ ostream& operator<<(ostream& os, const QuicCongestionFeedbackFrame& congestion_frame) { os << "type: " << congestion_frame.type; switch (congestion_frame.type) { - case kInterArrival: { - const CongestionFeedbackMessageInterArrival& inter_arrival = - congestion_frame.inter_arrival; + case kTimestamp: { + const CongestionFeedbackMessageTimestamp& timestamp = + congestion_frame.timestamp; os << " received packets: [ "; - for (TimeMap::const_iterator it = - inter_arrival.received_packet_times.begin(); - it != inter_arrival.received_packet_times.end(); ++it) { + for (TimeMap::const_iterator it = timestamp.received_packet_times.begin(); + it != timestamp.received_packet_times.end(); ++it) { os << it->first << "@" << it->second.ToDebuggingValue() << " "; } os << "]"; diff --git a/net/quic/quic_protocol.h b/net/quic/quic_protocol.h index ec94e4c7e57cb..6b29331e3b6a4 100644 --- a/net/quic/quic_protocol.h +++ b/net/quic/quic_protocol.h @@ -69,6 +69,9 @@ const uint32 kDefaultFlowControlSendWindow = 16 * 1024; // 16 KB // algorithms. const size_t kMaxTcpCongestionWindow = 200; +// Size of the socket receive buffer in bytes. +const QuicByteCount kDefaultSocketReceiveBuffer = 256 * 1024; + // Don't allow a client to suggest an RTT longer than 15 seconds. const uint32 kMaxInitialRoundTripTimeUs = 15 * kNumMicrosPerSecond; @@ -101,6 +104,9 @@ const QuicStreamId kCryptoStreamId = 1; // Reserved ID for the headers stream. const QuicStreamId kHeadersStreamId = 3; +// Maximum delayed ack time, in ms. +const int kMaxDelayedAckTime = 25; + // This is the default network timeout a for connection till the crypto // handshake succeeds and the negotiated timeout from the handshake is received. const int64 kDefaultInitialTimeoutSecs = 120; // 2 mins. @@ -278,6 +284,7 @@ enum QuicVersion { QUIC_VERSION_19 = 19, // Connection level flow control. QUIC_VERSION_20 = 20, // Independent stream/connection flow control windows. QUIC_VERSION_21 = 21, // Headers/crypto streams are flow controlled. + QUIC_VERSION_22 = 22, // Send Server Config Update messages on crypto stream. }; // This vector contains QUIC versions which we currently support. @@ -287,7 +294,8 @@ enum QuicVersion { // // IMPORTANT: if you are adding to this list, follow the instructions at // http://sites/quic/adding-and-removing-versions -static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_21, +static const QuicVersion kSupportedQuicVersions[] = {QUIC_VERSION_22, + QUIC_VERSION_21, QUIC_VERSION_20, QUIC_VERSION_19, QUIC_VERSION_18, @@ -440,6 +448,8 @@ enum QuicErrorCode { QUIC_INVALID_PRIORITY = 49, // Too many streams already open. QUIC_TOO_MANY_OPEN_STREAMS = 18, + // The peer must send a FIN/RST for each stream, and has not been doing so. + QUIC_TOO_MANY_UNFINISHED_STREAMS = 66, // Received public reset for this connection. QUIC_PUBLIC_RESET = 19, // Invalid protocol version. @@ -530,7 +540,7 @@ enum QuicErrorCode { QUIC_VERSION_NEGOTIATION_MISMATCH = 55, // No error. Used as bound while iterating. - QUIC_LAST_ERROR = 66, + QUIC_LAST_ERROR = 67, }; struct NET_EXPORT_PRIVATE QuicPacketPublicHeader { @@ -700,7 +710,7 @@ void NET_EXPORT_PRIVATE InsertMissingPacketsBetween( // compatibility. enum CongestionFeedbackType { kTCP, // Used to mimic TCP. - kInterArrival, // Use additional inter arrival information. + kTimestamp, // Use additional inter arrival timestamp information. }; // Defines for all types of congestion control algorithms that can be used in @@ -724,9 +734,9 @@ struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTCP { QuicByteCount receive_window; }; -struct NET_EXPORT_PRIVATE CongestionFeedbackMessageInterArrival { - CongestionFeedbackMessageInterArrival(); - ~CongestionFeedbackMessageInterArrival(); +struct NET_EXPORT_PRIVATE CongestionFeedbackMessageTimestamp { + CongestionFeedbackMessageTimestamp(); + ~CongestionFeedbackMessageTimestamp(); // The set of received packets since the last feedback was sent, along with // their arrival times. @@ -741,10 +751,10 @@ struct NET_EXPORT_PRIVATE QuicCongestionFeedbackFrame { std::ostream& os, const QuicCongestionFeedbackFrame& c); CongestionFeedbackType type; - // This should really be a union, but since the inter arrival struct + // This should really be a union, but since the timestamp struct // is non-trivial, C++ prohibits it. CongestionFeedbackMessageTCP tcp; - CongestionFeedbackMessageInterArrival inter_arrival; + CongestionFeedbackMessageTimestamp timestamp; }; struct NET_EXPORT_PRIVATE QuicRstStreamFrame { diff --git a/net/quic/quic_received_packet_manager.h b/net/quic/quic_received_packet_manager.h index b63d292e85d1f..94cfbc6d38e1f 100644 --- a/net/quic/quic_received_packet_manager.h +++ b/net/quic/quic_received_packet_manager.h @@ -114,7 +114,7 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager : // Checks if we're still waiting for the packet with |sequence_number|. bool IsAwaitingPacket(QuicPacketSequenceNumber sequence_number); - // Update the |received_info| for an outgoing ack. + // Update the |ack_frame| for an outgoing ack. void UpdateReceivedPacketInfo(QuicAckFrame* ack_frame, QuicTime approximate_now); @@ -131,7 +131,7 @@ class NET_EXPORT_PRIVATE QuicReceivedPacketManager : virtual QuicPacketEntropyHash EntropyHash( QuicPacketSequenceNumber sequence_number) const OVERRIDE; - // Updates internal state based on |received_info|. + // Updates internal state based on |ack_frame|. void UpdatePacketInformationReceivedByPeer(const QuicAckFrame& ack_frame); // Updates internal state based on |stop_waiting|. void UpdatePacketInformationSentByPeer( diff --git a/net/quic/quic_sent_packet_manager.cc b/net/quic/quic_sent_packet_manager.cc index f233d8ba7bd32..292afa635fcb6 100644 --- a/net/quic/quic_sent_packet_manager.cc +++ b/net/quic/quic_sent_packet_manager.cc @@ -49,8 +49,6 @@ static const size_t kNumMinRttSamplesAfterQuiescence = 2; // Number of unpaced packets to send after quiescence. static const size_t kInitialUnpacedBurst = 10; -// Use a 1 minute window for Recent Min RTT with BBR. - bool HasCryptoHandshake(const TransmissionInfo& transmission_info) { if (transmission_info.retransmittable_frames == NULL) { return false; @@ -87,7 +85,8 @@ QuicSentPacketManager::QuicSentPacketManager( consecutive_crypto_retransmission_count_(0), pending_tlp_transmission_(false), max_tail_loss_probes_(kDefaultMaxTailLossProbes), - using_pacing_(false) { + using_pacing_(false), + handshake_confirmed_(false) { } QuicSentPacketManager::~QuicSentPacketManager() { @@ -114,9 +113,8 @@ void QuicSentPacketManager::SetFromConfig(const QuicConfig& config) { send_algorithm_.reset( SendAlgorithmInterface::Create(clock_, &rtt_stats_, kReno, stats_)); } - if (config.congestion_feedback() == kPACE || - (config.HasReceivedConnectionOptions() && - ContainsQuicTag(config.ReceivedConnectionOptions(), kPACE))) { + if (config.HasReceivedConnectionOptions() && + ContainsQuicTag(config.ReceivedConnectionOptions(), kPACE)) { MaybeEnablePacing(); } // TODO(ianswett): Remove the "HasReceivedLossDetection" branch once @@ -606,7 +604,9 @@ bool QuicSentPacketManager::MaybeRetransmitTailLossProbe() { if (!it->second.in_flight || frames == NULL) { continue; } - DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake()); + if (!handshake_confirmed_) { + DCHECK_NE(IS_HANDSHAKE, frames->HasCryptoHandshake()); + } MarkForRetransmission(sequence_number, TLP_RETRANSMISSION); return true; } @@ -652,7 +652,7 @@ void QuicSentPacketManager::RetransmitAllPackets() { QuicSentPacketManager::RetransmissionTimeoutMode QuicSentPacketManager::GetRetransmissionMode() const { DCHECK(unacked_packets_.HasInFlightPackets()); - if (unacked_packets_.HasPendingCryptoPackets()) { + if (!handshake_confirmed_ && unacked_packets_.HasPendingCryptoPackets()) { return HANDSHAKE_MODE; } if (loss_algorithm_->GetLossTimeout() != QuicTime::Zero()) { @@ -737,6 +737,8 @@ QuicTime::Delta QuicSentPacketManager::TimeUntilSend( now, unacked_packets_.bytes_in_flight(), retransmittable); } +// Uses a 25ms delayed ack timer. Also helps with better signaling +// in low-bandwidth (< ~384 kbps), where an ack is sent per packet. // Ensures that the Delayed Ack timer is always set to a value lesser // than the retransmission timer's minimum value (MinRTO). We want the // delayed ack to get back to the QUIC peer before the sender's @@ -750,7 +752,8 @@ QuicTime::Delta QuicSentPacketManager::TimeUntilSend( // any benefits, but if the delayed ack becomes a significant source // of (likely, tail) latency, then consider such a mechanism. const QuicTime::Delta QuicSentPacketManager::DelayedAckTime() const { - return QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2); + return QuicTime::Delta::FromMilliseconds(min(kMaxDelayedAckTime, + kMinRetransmissionTimeMs/2)); } const QuicTime QuicSentPacketManager::GetRetransmissionTime() const { @@ -802,7 +805,8 @@ const QuicTime::Delta QuicSentPacketManager::GetTailLossProbeDelay() const { QuicTime::Delta srtt = rtt_stats_.SmoothedRtt(); if (!unacked_packets_.HasMultipleInFlightPackets()) { return QuicTime::Delta::Max( - srtt.Multiply(1.5).Add(DelayedAckTime()), srtt.Multiply(2)); + srtt.Multiply(2), srtt.Multiply(1.5) + .Add(QuicTime::Delta::FromMilliseconds(kMinRetransmissionTimeMs/2))); } return QuicTime::Delta::FromMilliseconds( max(kMinTailLossProbeTimeoutMs, diff --git a/net/quic/quic_sent_packet_manager.h b/net/quic/quic_sent_packet_manager.h index 1bedf5afa6777..3ace0143c1c81 100644 --- a/net/quic/quic_sent_packet_manager.h +++ b/net/quic/quic_sent_packet_manager.h @@ -111,6 +111,8 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { virtual void SetFromConfig(const QuicConfig& config); + void SetHandshakeConfirmed() { handshake_confirmed_ = true; } + // Called when a new packet is serialized. If the packet contains // retransmittable data, it will be added to the unacked packet map. void OnSerializedPacket(const SerializedPacket& serialized_packet); @@ -362,6 +364,12 @@ class NET_EXPORT_PRIVATE QuicSentPacketManager { SendAlgorithmInterface::CongestionMap packets_acked_; SendAlgorithmInterface::CongestionMap packets_lost_; + // Set to true after the crypto handshake has successfully completed. After + // this is true we no longer use HANDSHAKE_MODE, and further frames sent on + // the crypto stream (i.e. SCUP messages) are treated like normal + // retransmittable frames. + bool handshake_confirmed_; + DISALLOW_COPY_AND_ASSIGN(QuicSentPacketManager); }; diff --git a/net/quic/quic_session.cc b/net/quic/quic_session.cc index 4c8f50d726ca7..f2277271695e1 100644 --- a/net/quic/quic_session.cc +++ b/net/quic/quic_session.cc @@ -262,8 +262,7 @@ void QuicSession::OnWindowUpdateFrames( DVLOG(1) << ENDPOINT << "Received connection level flow control window update with " "byte offset: " << frames[i].byte_offset; - if (FLAGS_enable_quic_connection_flow_control_2 && - flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) { + if (flow_controller_->UpdateSendWindowOffset(frames[i].byte_offset)) { connection_window_updated = true; } continue; @@ -428,10 +427,15 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, // of the how many bytes the stream's flow controller believes it has // received, for accurate connection level flow control accounting. if (!stream->HasFinalReceivedByteOffset() && - stream->flow_controller()->IsEnabled() && - FLAGS_enable_quic_connection_flow_control_2) { + stream->flow_controller()->IsEnabled()) { locally_closed_streams_highest_offset_[stream_id] = stream->flow_controller()->highest_received_byte_offset(); + if (FLAGS_close_quic_connection_unfinished_streams && + connection()->connected() && + locally_closed_streams_highest_offset_.size() > max_open_streams_) { + // A buggy client may fail to send FIN/RSTs. Don't tolerate this. + connection_->SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS); + } } stream_map_.erase(it); @@ -440,10 +444,6 @@ void QuicSession::CloseStreamInner(QuicStreamId stream_id, void QuicSession::UpdateFlowControlOnFinalReceivedByteOffset( QuicStreamId stream_id, QuicStreamOffset final_byte_offset) { - if (!FLAGS_enable_quic_connection_flow_control_2) { - return; - } - map::iterator it = locally_closed_streams_highest_offset_.find(stream_id); if (it == locally_closed_streams_highest_offset_.end()) { diff --git a/net/quic/quic_session_test.cc b/net/quic/quic_session_test.cc index 83f2447df80ff..b604a7ffee1f1 100644 --- a/net/quic/quic_session_test.cc +++ b/net/quic/quic_session_test.cc @@ -531,8 +531,6 @@ TEST_P(QuicSessionTest, OnCanWriteLimitsNumWritesIfFlowControlBlocked) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Ensure connection level flow control blockage. QuicFlowControllerPeer::SetSendWindowOffset(session_.flow_controller(), 0); EXPECT_TRUE(session_.flow_controller()->IsBlocked()); @@ -711,8 +709,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstOutOfOrder) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Test that when we receive an out of order stream RST we correctly adjust // our connection level flow control receive window. // On close, the stream should mark as consumed all bytes between the highest @@ -741,8 +737,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAndLocalReset) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Test the situation where we receive a FIN on a stream, and before we fully // consume all the data from the sequencer buffer we locally RST the stream. // The bytes between highest consumed byte, and the final byte offset that we @@ -789,8 +783,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingFinAfterRst) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Connection starts with some non-zero highest received byte offset, // due to other active streams. const uint64 kInitialConnectionBytesConsumed = 567; @@ -833,8 +825,6 @@ TEST_P(QuicSessionTest, ConnectionFlowControlAccountingRstAfterRst) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Connection starts with some non-zero highest received byte offset, // due to other active streams. const uint64 kInitialConnectionBytesConsumed = 567; @@ -869,8 +859,6 @@ TEST_P(QuicSessionTest, FlowControlWithInvalidFinalOffset) { if (version() <= QUIC_VERSION_16) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); const uint64 kLargeOffset = kInitialSessionFlowControlWindowForTest + 1; EXPECT_CALL(*connection_, @@ -896,8 +884,6 @@ TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) { return; } - ValueRestore old_flag(&FLAGS_enable_quic_connection_flow_control_2, - true); // Test that after successful version negotiation, flow control is disabled // appropriately at both the connection and stream level. @@ -918,6 +904,35 @@ TEST_P(QuicSessionTest, VersionNegotiationDisablesFlowControl) { EXPECT_FALSE(stream->flow_controller()->IsEnabled()); } +TEST_P(QuicSessionTest, TooManyUnfinishedStreamsCauseConnectionClose) { + if (version() < QUIC_VERSION_18) { + return; + } + // If a buggy/malicious peer creates too many streams that are not ended with + // a FIN or RST then we send a connection close. + ValueRestore old_flag(&FLAGS_close_quic_connection_unfinished_streams, + true); + + EXPECT_CALL(*connection_, + SendConnectionClose(QUIC_TOO_MANY_UNFINISHED_STREAMS)).Times(1); + + const int kMaxStreams = 5; + QuicSessionPeer::SetMaxOpenStreams(&session_, kMaxStreams); + + // Create kMaxStreams + 1 data streams, and close them all without receiving a + // FIN or a RST from the client. + const int kFirstStreamId = kClientDataStreamId1; + const int kFinalStreamId = kClientDataStreamId1 + 2 * kMaxStreams + 1; + for (int i = kFirstStreamId; i < kFinalStreamId; i += 2) { + QuicStreamFrame data1(i, false, 0, MakeIOVector("HT")); + vector frames; + frames.push_back(data1); + session_.OnStreamFrames(frames); + EXPECT_EQ(1u, session_.GetNumOpenStreams()); + session_.CloseStream(i); + } +} + } // namespace } // namespace test } // namespace net diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc index ea30b2e16e087..aa1c0f838f7f2 100644 --- a/net/quic/quic_stream_factory.cc +++ b/net/quic/quic_stream_factory.cc @@ -85,12 +85,10 @@ bool IsEcdsaSupported() { return true; } -QuicConfig InitializeQuicConfig(bool enable_pacing, - bool enable_time_based_loss_detection, +QuicConfig InitializeQuicConfig(bool enable_time_based_loss_detection, const QuicTagVector& connection_options) { QuicConfig config; config.SetDefaults(); - config.EnablePacing(enable_pacing); if (enable_time_based_loss_detection) config.SetLossDetectionToSend(kTIME); config.set_idle_connection_state_lifetime( @@ -462,25 +460,25 @@ QuicStreamFactory::QuicStreamFactory( const std::string& user_agent_id, const QuicVersionVector& supported_versions, bool enable_port_selection, - bool enable_pacing, bool enable_time_based_loss_detection, const QuicTagVector& connection_options) : require_confirmation_(true), host_resolver_(host_resolver), client_socket_factory_(client_socket_factory), http_server_properties_(http_server_properties), + transport_security_state_(transport_security_state), quic_server_info_factory_(NULL), quic_crypto_client_stream_factory_(quic_crypto_client_stream_factory), random_generator_(random_generator), clock_(clock), max_packet_length_(max_packet_length), - config_(InitializeQuicConfig(enable_pacing, - enable_time_based_loss_detection, + config_(InitializeQuicConfig(enable_time_based_loss_detection, connection_options)), supported_versions_(supported_versions), enable_port_selection_(enable_port_selection), port_seed_(random_generator_->RandUint64()), weak_factory_(this) { + DCHECK(transport_security_state_); crypto_config_.SetDefaults(); crypto_config_.set_user_agent_id(user_agent_id); crypto_config_.AddCanonicalSuffix(".c.youtube.com"); @@ -861,8 +859,8 @@ int QuicStreamFactory::CreateSession( *session = new QuicClientSession( connection, socket.Pass(), writer.Pass(), this, - quic_crypto_client_stream_factory_, server_info.Pass(), server_id, - config, &crypto_config_, + quic_crypto_client_stream_factory_, transport_security_state_, + server_info.Pass(), server_id, config, &crypto_config_, base::MessageLoop::current()->message_loop_proxy().get(), net_log.net_log()); (*session)->InitializeSession(); diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h index 574bf6f1c9371..6a0216834b213 100644 --- a/net/quic/quic_stream_factory.h +++ b/net/quic/quic_stream_factory.h @@ -102,7 +102,6 @@ class NET_EXPORT_PRIVATE QuicStreamFactory const std::string& user_agent_id, const QuicVersionVector& supported_versions, bool enable_port_selection, - bool enable_pacing, bool enable_time_based_loss_detection, const QuicTagVector& connection_options); virtual ~QuicStreamFactory(); @@ -238,6 +237,7 @@ class NET_EXPORT_PRIVATE QuicStreamFactory HostResolver* host_resolver_; ClientSocketFactory* client_socket_factory_; base::WeakPtr http_server_properties_; + TransportSecurityState* transport_security_state_; QuicServerInfoFactory* quic_server_info_factory_; QuicCryptoClientStreamFactory* quic_crypto_client_stream_factory_; QuicRandom* random_generator_; diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc index df6613457577e..73feff80fd939 100644 --- a/net/quic/quic_stream_factory_test.cc +++ b/net/quic/quic_stream_factory_test.cc @@ -25,6 +25,7 @@ #include "net/quic/test_tools/quic_test_packet_maker.h" #include "net/quic/test_tools/quic_test_utils.h" #include "net/socket/socket_test_util.h" +#include "net/spdy/spdy_test_utils.h" #include "net/ssl/channel_id_service.h" #include "net/ssl/default_channel_id_store.h" #include "net/test/cert_test_util.h" @@ -100,8 +101,7 @@ class QuicStreamFactoryTest : public ::testing::TestWithParam { channel_id_service_.get(), &transport_security_state_, &crypto_client_stream_factory_, &random_generator_, clock_, kDefaultMaxPacketSize, std::string(), - SupportedVersions(GetParam()), true, true, true, - QuicTagVector()), + SupportedVersions(GetParam()), true, true, QuicTagVector()), host_port_pair_(kDefaultServerHostName, kDefaultServerPort), is_https_(false), privacy_mode_(PRIVACY_MODE_DISABLED) { @@ -360,8 +360,7 @@ TEST_P(QuicStreamFactoryTest, CreateHttpVsHttps) { EXPECT_TRUE(socket_data2.at_write_eof()); } -// TODO(rch): re-enable this. -TEST_P(QuicStreamFactoryTest, DISABLED_Pooling) { +TEST_P(QuicStreamFactoryTest, Pooling) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; @@ -477,8 +476,7 @@ TEST_P(QuicStreamFactoryTest, NoPoolingAfterGoAway) { EXPECT_TRUE(socket_data2.at_write_eof()); } -// TODO(rch): re-enable this. -TEST_P(QuicStreamFactoryTest, DISABLED_HttpsPooling) { +TEST_P(QuicStreamFactoryTest, HttpsPooling) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF }; @@ -499,6 +497,7 @@ TEST_P(QuicStreamFactoryTest, DISABLED_HttpsPooling) { ASSERT_NE(static_cast(NULL), test_cert); ProofVerifyDetailsChromium verify_details; verify_details.cert_verify_result.verified_cert = test_cert; + verify_details.cert_verify_result.is_issued_by_known_root = true; crypto_client_stream_factory_.set_proof_verify_details(&verify_details); host_resolver_.set_synchronous_mode(true); @@ -605,6 +604,146 @@ TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithCertMismatch) { EXPECT_TRUE(socket_data2.at_write_eof()); } +TEST_P(QuicStreamFactoryTest, HttpsPoolingWithMatchingPins) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data); + socket_data.StopAfter(1); + + HostPortPair server1("www.example.org", 443); + HostPortPair server2("mail.example.org", 443); + uint8 primary_pin = 1; + uint8 backup_pin = 2; + test::AddPin(&transport_security_state_, "mail.example.org", primary_pin, + backup_pin); + + // Load a cert that is valid for: + // www.example.org (server1) + // mail.example.org (server2) + base::FilePath certs_dir = GetTestCertsDirectory(); + scoped_refptr test_cert( + ImportCertFromFile(certs_dir, "spdy_pooling.pem")); + ASSERT_NE(static_cast(NULL), test_cert); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = test_cert; + verify_details.cert_verify_result.is_issued_by_known_root = true; + verify_details.cert_verify_result.public_key_hashes.push_back( + test::GetTestHashValue(primary_pin)); + crypto_client_stream_factory_.set_proof_verify_details(&verify_details); + + + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + is_https_ = true; + EXPECT_EQ(OK, + request.Request(server1, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + TestCompletionCallback callback; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, + request2.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + + EXPECT_EQ(QuicStreamFactoryPeer::GetActiveSession( + &factory_, server1, is_https_), + QuicStreamFactoryPeer::GetActiveSession( + &factory_, server2, is_https_)); + + EXPECT_TRUE(socket_data.at_read_eof()); + EXPECT_TRUE(socket_data.at_write_eof()); +} + +TEST_P(QuicStreamFactoryTest, NoHttpsPoolingWithDifferentPins) { + MockRead reads[] = { + MockRead(ASYNC, OK, 0) // EOF + }; + DeterministicSocketData socket_data1(reads, arraysize(reads), NULL, 0); + DeterministicSocketData socket_data2(reads, arraysize(reads), NULL, 0); + socket_factory_.AddSocketDataProvider(&socket_data1); + socket_factory_.AddSocketDataProvider(&socket_data2); + socket_data1.StopAfter(1); + socket_data2.StopAfter(1); + + HostPortPair server1("www.example.org", 443); + HostPortPair server2("mail.example.org", 443); + uint8 primary_pin = 1; + uint8 backup_pin = 2; + uint8 bad_pin = 3; + test::AddPin(&transport_security_state_, "mail.example.org", primary_pin, + backup_pin); + + // Load a cert that is valid for: + // www.example.org (server1) + // mail.example.org (server2) + base::FilePath certs_dir = GetTestCertsDirectory(); + scoped_refptr test_cert( + ImportCertFromFile(certs_dir, "spdy_pooling.pem")); + ASSERT_NE(static_cast(NULL), test_cert); + ProofVerifyDetailsChromium verify_details; + verify_details.cert_verify_result.verified_cert = test_cert; + verify_details.cert_verify_result.is_issued_by_known_root = true; + verify_details.cert_verify_result.public_key_hashes.push_back( + test::GetTestHashValue(bad_pin)); + crypto_client_stream_factory_.set_proof_verify_details(&verify_details); + + + host_resolver_.set_synchronous_mode(true); + host_resolver_.rules()->AddIPLiteralRule(server1.host(), "192.168.0.1", ""); + host_resolver_.rules()->AddIPLiteralRule(server2.host(), "192.168.0.1", ""); + + QuicStreamRequest request(&factory_); + is_https_ = true; + EXPECT_EQ(OK, + request.Request(server1, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr stream = request.ReleaseStream(); + EXPECT_TRUE(stream.get()); + + TestCompletionCallback callback; + QuicStreamRequest request2(&factory_); + EXPECT_EQ(OK, + request2.Request(server2, + is_https_, + privacy_mode_, + "GET", + net_log_, + callback_.callback())); + scoped_ptr stream2 = request2.ReleaseStream(); + EXPECT_TRUE(stream2.get()); + + EXPECT_NE(QuicStreamFactoryPeer::GetActiveSession( + &factory_, server1, is_https_), + QuicStreamFactoryPeer::GetActiveSession( + &factory_, server2, is_https_)); + + EXPECT_TRUE(socket_data1.at_read_eof()); + EXPECT_TRUE(socket_data1.at_write_eof()); + EXPECT_TRUE(socket_data2.at_read_eof()); + EXPECT_TRUE(socket_data2.at_write_eof()); +} + TEST_P(QuicStreamFactoryTest, Goaway) { MockRead reads[] = { MockRead(ASYNC, OK, 0) // EOF diff --git a/net/quic/quic_utils.cc b/net/quic/quic_utils.cc index 87ec18c0d4d77..8956fa21782b7 100644 --- a/net/quic/quic_utils.cc +++ b/net/quic/quic_utils.cc @@ -191,6 +191,7 @@ const char* QuicUtils::ErrorToString(QuicErrorCode error) { RETURN_STRING_LITERAL(QUIC_INVALID_STREAM_ID); RETURN_STRING_LITERAL(QUIC_INVALID_PRIORITY); RETURN_STRING_LITERAL(QUIC_TOO_MANY_OPEN_STREAMS); + RETURN_STRING_LITERAL(QUIC_TOO_MANY_UNFINISHED_STREAMS); RETURN_STRING_LITERAL(QUIC_PUBLIC_RESET); RETURN_STRING_LITERAL(QUIC_INVALID_VERSION); RETURN_STRING_LITERAL(QUIC_INVALID_HEADER_ID); diff --git a/net/quic/reliable_quic_stream.cc b/net/quic/reliable_quic_stream.cc index 212b69868c7a8..dbb5d440c219e 100644 --- a/net/quic/reliable_quic_stream.cc +++ b/net/quic/reliable_quic_stream.cc @@ -234,6 +234,7 @@ void ReliableQuicStream::OnConnectionClosed(QuicErrorCode error, void ReliableQuicStream::OnFinRead() { DCHECK(sequencer_.IsClosed()); + fin_received_ = true; CloseReadSide(); } diff --git a/net/quic/reliable_quic_stream_test.cc b/net/quic/reliable_quic_stream_test.cc index f25011d5d307a..f188783e425c7 100644 --- a/net/quic/reliable_quic_stream_test.cc +++ b/net/quic/reliable_quic_stream_test.cc @@ -6,7 +6,6 @@ #include "net/quic/quic_ack_notifier.h" #include "net/quic/quic_connection.h" -#include "net/quic/quic_flags.h" #include "net/quic/quic_utils.h" #include "net/quic/quic_write_blocked_list.h" #include "net/quic/spdy_utils.h" @@ -471,9 +470,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataWithQuicAckNotifier) { // Set a large flow control send window so this doesn't interfere with test. stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); - if (FLAGS_enable_quic_connection_flow_control_2) { - session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); - } + session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); scoped_refptr proxy_delegate; @@ -526,9 +523,7 @@ TEST_F(ReliableQuicStreamTest, WriteOrBufferDataAckNotificationBeforeFlush) { // Set a large flow control send window so this doesn't interfere with test. stream_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); - if (FLAGS_enable_quic_connection_flow_control_2) { - session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); - } + session_->flow_controller()->UpdateSendWindowOffset(kDataSize + 1); scoped_refptr proxy_delegate; @@ -634,9 +629,6 @@ TEST_F(ReliableQuicStreamTest, WriteAndBufferDataWithAckNotiferOnlyFinRemains) { // as we check for violation and close the connection early. TEST_F(ReliableQuicStreamTest, StreamSequencerNeverSeesPacketsViolatingFlowControl) { - ValueRestore old_connection_flag( - &FLAGS_enable_quic_connection_flow_control_2, true); - Initialize(kShouldProcessData); // Receive a stream frame that violates flow control: the byte offset is diff --git a/net/quic/test_tools/crypto_test_utils.cc b/net/quic/test_tools/crypto_test_utils.cc index 0e3a81517a774..86ff6422460a7 100644 --- a/net/quic/test_tools/crypto_test_utils.cc +++ b/net/quic/test_tools/crypto_test_utils.cc @@ -62,9 +62,9 @@ class CryptoFramerVisitor : public CryptoFramerVisitorInterface { }; // MovePackets parses crypto handshake messages from packet number -// |*inout_packet_index| through to the last packet and has |dest_stream| -// process them. |*inout_packet_index| is updated with an index one greater -// than the last packet processed. +// |*inout_packet_index| through to the last packet (or until a packet fails to +// decrypt) and has |dest_stream| process them. |*inout_packet_index| is updated +// with an index one greater than the last packet processed. void MovePackets(PacketSavingConnection* source_conn, size_t *inout_packet_index, QuicCryptoStream* dest_stream, @@ -84,7 +84,12 @@ void MovePackets(PacketSavingConnection* source_conn, size_t index = *inout_packet_index; for (; index < source_conn->encrypted_packets_.size(); index++) { - ASSERT_TRUE(framer.ProcessPacket(*source_conn->encrypted_packets_[index])); + if (!framer.ProcessPacket(*source_conn->encrypted_packets_[index])) { + // The framer will be unable to decrypt forward-secure packets sent after + // the handshake is complete. Don't treat them as handshake packets. + break; + } + for (vector::const_iterator i = framer.stream_frames().begin(); i != framer.stream_frames().end(); ++i) { diff --git a/net/quic/test_tools/mock_crypto_client_stream.cc b/net/quic/test_tools/mock_crypto_client_stream.cc index a4b3ab0289d6d..4d4057a4b0195 100644 --- a/net/quic/test_tools/mock_crypto_client_stream.cc +++ b/net/quic/test_tools/mock_crypto_client_stream.cc @@ -81,7 +81,7 @@ void MockCryptoClientStream::SendOnCryptoHandshakeEvent( void MockCryptoClientStream::SetConfigNegotiated() { ASSERT_FALSE(session()->config()->negotiated()); QuicTagVector cgst; - cgst.push_back(kINAR); + cgst.push_back(kTSTP); cgst.push_back(kQBIC); session()->config()->set_congestion_feedback(cgst, kQBIC); session()->config()->set_idle_connection_state_lifetime( diff --git a/net/quic/test_tools/quic_session_peer.cc b/net/quic/test_tools/quic_session_peer.cc index f8024fe0ae2c8..14897b26fcbdd 100644 --- a/net/quic/test_tools/quic_session_peer.cc +++ b/net/quic/test_tools/quic_session_peer.cc @@ -50,5 +50,11 @@ QuicDataStream* QuicSessionPeer::GetIncomingDataStream( return session->GetIncomingDataStream(stream_id); } +// static +map& +QuicSessionPeer::GetLocallyClosedStreamsHighestOffset(QuicSession* session) { + return session->locally_closed_streams_highest_offset_; +} + } // namespace test } // namespace net diff --git a/net/quic/test_tools/quic_session_peer.h b/net/quic/test_tools/quic_session_peer.h index 02146caf1adb8..e69f6ba9c64a8 100644 --- a/net/quic/test_tools/quic_session_peer.h +++ b/net/quic/test_tools/quic_session_peer.h @@ -28,6 +28,8 @@ class QuicSessionPeer { static QuicWriteBlockedList* GetWriteBlockedStreams(QuicSession* session); static QuicDataStream* GetIncomingDataStream(QuicSession* session, QuicStreamId stream_id); + static std::map& + GetLocallyClosedStreamsHighestOffset(QuicSession* session); private: DISALLOW_COPY_AND_ASSIGN(QuicSessionPeer); diff --git a/net/quic/test_tools/quic_test_utils.h b/net/quic/test_tools/quic_test_utils.h index 350bc37915152..ce6877ef505c9 100644 --- a/net/quic/test_tools/quic_test_utils.h +++ b/net/quic/test_tools/quic_test_utils.h @@ -90,8 +90,8 @@ QuicConfig DefaultQuicConfig(); // Returns a version vector consisting of |version|. QuicVersionVector SupportedVersions(QuicVersion version); -// Testing convenience method to construct a QuicAckFrame with all packets -// from least_unacked to largest_observed acked. +// Testing convenience method to construct a QuicAckFrame with entropy_hash set +// to 0 and largest_observed from peer set to |largest_observed|. QuicAckFrame MakeAckFrame(QuicPacketSequenceNumber largest_observed); // Testing convenience method to construct a QuicAckFrame with |num_nack_ranges| diff --git a/net/socket/socket_test_util.cc b/net/socket/socket_test_util.cc index acdf5670b4d23..3498c13428d26 100644 --- a/net/socket/socket_test_util.cc +++ b/net/socket/socket_test_util.cc @@ -279,7 +279,7 @@ SSLSocketDataProvider::SSLSocketDataProvider(IoMode mode, int result) cert_request_info(NULL), channel_id_sent(false), connection_status(0), - should_block_on_connect(false), + should_pause_on_connect(false), is_in_session_cache(false) { SSLConnectionStatusSetVersion(SSL_CONNECTION_VERSION_TLS1_2, &connection_status); @@ -1349,7 +1349,7 @@ int MockSSLClientSocket::Write(IOBuffer* buf, int buf_len, } int MockSSLClientSocket::Connect(const CompletionCallback& callback) { - next_connect_state_ = STATE_TRANSPORT_CONNECT; + next_connect_state_ = STATE_SSL_CONNECT; reached_connect_ = true; int rv = DoConnectLoop(OK); if (rv == ERR_IO_PENDING) @@ -1460,7 +1460,7 @@ void MockSSLClientSocket::OnConnectComplete(const MockConnect& data) { } void MockSSLClientSocket::RestartPausedConnect() { - DCHECK(data_->should_block_on_connect); + DCHECK(data_->should_pause_on_connect); DCHECK_EQ(next_connect_state_, STATE_SSL_CONNECT_COMPLETE); OnIOComplete(data_->connect.result); } @@ -1479,12 +1479,6 @@ int MockSSLClientSocket::DoConnectLoop(int result) { ConnectState state = next_connect_state_; next_connect_state_ = STATE_NONE; switch (state) { - case STATE_TRANSPORT_CONNECT: - rv = DoTransportConnect(); - break; - case STATE_TRANSPORT_CONNECT_COMPLETE: - rv = DoTransportConnectComplete(rv); - break; case STATE_SSL_CONNECT: rv = DoSSLConnect(); break; @@ -1501,22 +1495,10 @@ int MockSSLClientSocket::DoConnectLoop(int result) { return rv; } -int MockSSLClientSocket::DoTransportConnect() { - next_connect_state_ = STATE_TRANSPORT_CONNECT_COMPLETE; - return transport_->socket()->Connect( - base::Bind(&MockSSLClientSocket::OnIOComplete, base::Unretained(this))); -} - -int MockSSLClientSocket::DoTransportConnectComplete(int result) { - if (result == OK) - next_connect_state_ = STATE_SSL_CONNECT; - return result; -} - int MockSSLClientSocket::DoSSLConnect() { next_connect_state_ = STATE_SSL_CONNECT_COMPLETE; - if (data_->should_block_on_connect) + if (data_->should_pause_on_connect) return ERR_IO_PENDING; if (data_->connect.mode == ASYNC) { diff --git a/net/socket/socket_test_util.h b/net/socket/socket_test_util.h index 8f04626640471..00905cd6dbb60 100644 --- a/net/socket/socket_test_util.h +++ b/net/socket/socket_test_util.h @@ -334,8 +334,8 @@ struct SSLSocketDataProvider { bool channel_id_sent; ChannelIDService* channel_id_service; int connection_status; - // Indicates that the socket should block in the Connect method. - bool should_block_on_connect; + // Indicates that the socket should pause in the Connect method. + bool should_pause_on_connect; // Whether or not the Socket should behave like there is a pre-existing // session to resume. Whether or not such a session is reported as // resumed is controlled by |connection_status|. @@ -990,8 +990,6 @@ class MockSSLClientSocket : public MockClientSocket, public AsyncSocket { private: enum ConnectState { STATE_NONE, - STATE_TRANSPORT_CONNECT, - STATE_TRANSPORT_CONNECT_COMPLETE, STATE_SSL_CONNECT, STATE_SSL_CONNECT_COMPLETE, }; @@ -1001,8 +999,6 @@ class MockSSLClientSocket : public MockClientSocket, public AsyncSocket { // Runs the state transistion loop. int DoConnectLoop(int result); - int DoTransportConnect(); - int DoTransportConnectComplete(int result); int DoSSLConnect(); int DoSSLConnectComplete(int result); diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc index 0d3c53d891e98..92af627b7f871 100644 --- a/net/socket/ssl_client_socket_nss.cc +++ b/net/socket/ssl_client_socket_nss.cc @@ -3427,53 +3427,20 @@ int SSLClientSocketNSS::DoVerifyCertComplete(int result) { if (result == OK) LogConnectionTypeMetrics(); -#if defined(OFFICIAL_BUILD) && !defined(OS_ANDROID) && !defined(OS_IOS) - // Take care of any mandates for public key pinning. - // - // Pinning is only enabled for official builds to make sure that others don't - // end up with pins that cannot be easily updated. - // - // TODO(agl): We might have an issue here where a request for foo.example.com - // merges into a SPDY connection to www.example.com, and gets a different - // certificate. - - // Perform pin validation if, and only if, all these conditions obtain: - // - // * a TransportSecurityState object is available; - // * the server's certificate chain is valid (or suffers from only a minor - // error); - // * the server's certificate chain chains up to a known root (i.e. not a - // user-installed trust anchor); and - // * the build is recent (very old builds should fail open so that users - // have some chance to recover). - // + bool sni_available = ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || + ssl_config_.version_fallback; const CertStatus cert_status = server_cert_verify_result_.cert_status; if (transport_security_state_ && (result == OK || (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && - server_cert_verify_result_.is_issued_by_known_root && - TransportSecurityState::IsBuildTimely()) { - bool sni_available = - ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || - ssl_config_.version_fallback; - const std::string& host = host_and_port_.host(); - - if (transport_security_state_->HasPublicKeyPins(host, sni_available)) { - if (!transport_security_state_->CheckPublicKeyPins( - host, - sni_available, - server_cert_verify_result_.public_key_hashes, - &pinning_failure_log_)) { - LOG(ERROR) << pinning_failure_log_; - result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", false); - TransportSecurityState::ReportUMAOnPinFailure(host); - } else { - UMA_HISTOGRAM_BOOLEAN("Net.PublicKeyPinSuccess", true); - } - } + !transport_security_state_->CheckPublicKeyPins( + host_and_port_.host(), + sni_available, + server_cert_verify_result_.is_issued_by_known_root, + server_cert_verify_result_.public_key_hashes, + &pinning_failure_log_)) { + result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; } -#endif if (result == OK) { // Only check Certificate Transparency if there were no other errors with diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h index 0af196f15f231..d9757f0c43cc7 100644 --- a/net/socket/ssl_client_socket_nss.h +++ b/net/socket/ssl_client_socket_nss.h @@ -204,7 +204,7 @@ class SSLClientSocketNSS : public SSLClientSocket { TransportSecurityState* transport_security_state_; // pinning_failure_log contains a message produced by - // TransportSecurityState::DomainState::CheckPublicKeyPins in the event of a + // TransportSecurityState::CheckPublicKeyPins in the event of a // pinning failure. It is a (somewhat) human-readable string. std::string pinning_failure_log_; diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc index 4d108fc92782f..09f36c8dfa645 100644 --- a/net/socket/ssl_client_socket_openssl.cc +++ b/net/socket/ssl_client_socket_openssl.cc @@ -23,6 +23,7 @@ #include "net/cert/cert_verifier.h" #include "net/cert/single_request_cert_verifier.h" #include "net/cert/x509_certificate_net_log_param.h" +#include "net/http/transport_security_state.h" #include "net/socket/ssl_error_params.h" #include "net/socket/ssl_session_cache_openssl.h" #include "net/ssl/openssl_ssl_util.h" @@ -57,7 +58,13 @@ const int kNoPendingReadResult = 1; // the server supports NPN, choosing "http/1.1" is the best answer. const char kDefaultSupportedNPNProtocol[] = "http/1.1"; +void FreeX509Stack(STACK_OF(X509)* ptr) { + sk_X509_pop_free(ptr, X509_free); +} + typedef crypto::ScopedOpenSSL::Type ScopedX509; +typedef crypto::ScopedOpenSSL::Type + ScopedX509Stack; #if OPENSSL_VERSION_NUMBER < 0x1000103fL // This method doesn't seem to have made it into the OpenSSL headers. @@ -95,10 +102,6 @@ int GetNetSSLVersion(SSL* ssl) { } } -void FreeX509Stack(STACK_OF(X509) * ptr) { - sk_X509_pop_free(ptr, X509_free); -} - ScopedX509 OSCertHandleToOpenSSL( X509Certificate::OSCertHandle os_handle) { #if defined(USE_OPENSSL_CERTS) @@ -112,6 +115,18 @@ ScopedX509 OSCertHandleToOpenSSL( #endif // defined(USE_OPENSSL_CERTS) } +ScopedX509Stack OSCertHandlesToOpenSSL( + const X509Certificate::OSCertHandles& os_handles) { + ScopedX509Stack stack(sk_X509_new_null()); + for (size_t i = 0; i < os_handles.size(); i++) { + ScopedX509 x509 = OSCertHandleToOpenSSL(os_handles[i]); + if (!x509) + return ScopedX509Stack(); + sk_X509_push(stack.get(), x509.release()); + } + return stack.Pass(); +} + } // namespace class SSLClientSocketOpenSSL::SSLContext { @@ -142,7 +157,7 @@ class SSLClientSocketOpenSSL::SSLContext { ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method())); session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig); SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), CertVerifyCallback, NULL); - SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback); + SSL_CTX_set_cert_cb(ssl_ctx_.get(), ClientCertRequestCallback, NULL); SSL_CTX_set_verify(ssl_ctx_.get(), SSL_VERIFY_PEER, NULL); // TODO(kristianm): Only select this if ssl_config_.next_proto is not empty. // It would be better if the callback were not a global setting, @@ -160,10 +175,10 @@ class SSLClientSocketOpenSSL::SSLContext { static SSLSessionCacheOpenSSL::Config kDefaultSessionCacheConfig; - static int ClientCertCallback(SSL* ssl, X509** x509, EVP_PKEY** pkey) { + static int ClientCertRequestCallback(SSL* ssl, void* arg) { SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl); - CHECK(socket); - return socket->ClientCertRequestCallback(ssl, x509, pkey); + DCHECK(socket); + return socket->ClientCertRequestCallback(ssl); } static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) { @@ -226,9 +241,6 @@ class SSLClientSocketOpenSSL::PeerCertificateChain { bool IsValid() { return os_chain_.get() && openssl_chain_.get(); } private: - typedef crypto::ScopedOpenSSL::Type - ScopedX509Stack; - ScopedX509Stack openssl_chain_; scoped_refptr os_chain_; @@ -243,12 +255,8 @@ SSLClientSocketOpenSSL::PeerCertificateChain::operator=( // os_chain_ is reference counted by scoped_refptr; os_chain_ = other.os_chain_; - // Must increase the reference count manually for sk_X509_dup - openssl_chain_.reset(sk_X509_dup(other.openssl_chain_.get())); - for (size_t i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) { - X509* x = sk_X509_value(openssl_chain_.get(), i); - CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); - } + openssl_chain_.reset(X509_chain_up_ref(other.openssl_chain_.get())); + return *this; } @@ -270,15 +278,7 @@ void SSLClientSocketOpenSSL::PeerCertificateChain::Reset( os_chain_ = X509Certificate::CreateFromHandle(sk_X509_value(chain, 0), intermediates); - // sk_X509_dup does not increase reference count on the certs in the stack. - openssl_chain_.reset(sk_X509_dup(chain)); - - std::vector der_chain; - for (size_t i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) { - X509* x = sk_X509_value(openssl_chain_.get(), i); - // Increase the reference count for the certs in openssl_chain_. - CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); - } + openssl_chain_.reset(X509_chain_up_ref(chain)); } #else // !defined(USE_OPENSSL_CERTS) void SSLClientSocketOpenSSL::PeerCertificateChain::Reset( @@ -289,16 +289,12 @@ void SSLClientSocketOpenSSL::PeerCertificateChain::Reset( if (!chain) return; - // sk_X509_dup does not increase reference count on the certs in the stack. - openssl_chain_.reset(sk_X509_dup(chain)); + openssl_chain_.reset(X509_chain_up_ref(chain)); std::vector der_chain; for (size_t i = 0; i < sk_X509_num(openssl_chain_.get()); ++i) { X509* x = sk_X509_value(openssl_chain_.get(), i); - // Increase the reference count for the certs in openssl_chain_. - CRYPTO_add(&x->references, 1, CRYPTO_LOCK_X509); - unsigned char* cert_data = NULL; int cert_data_length = i2d_X509(x, &cert_data); if (cert_data_length && cert_data) @@ -348,7 +344,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( transport_read_error_(OK), transport_write_error_(OK), server_cert_chain_(new PeerCertificateChain(NULL)), - completed_handshake_(false), + completed_connect_(false), was_ever_used_(false), client_auth_cert_needed_(false), cert_verifier_(context.cert_verifier), @@ -363,8 +359,9 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL( next_handshake_state_(STATE_NONE), npn_status_(kNextProtoUnsupported), channel_id_xtn_negotiated_(false), - ran_handshake_finished_callback_(false), + handshake_succeeded_(false), marked_session_as_good_(false), + transport_security_state_(context.transport_security_state), net_log_(transport_->socket()->NetLog()) { } @@ -428,6 +425,10 @@ int SSLClientSocketOpenSSL::GetTLSUniqueChannelBinding(std::string* out) { } int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) { + // It is an error to create an SSLClientSocket whose context has no + // TransportSecurityState. + DCHECK(transport_security_state_); + net_log_.BeginEvent(NetLog::TYPE_SSL_CONNECT); // Set up new ssl object. @@ -493,7 +494,7 @@ void SSLClientSocketOpenSSL::Disconnect() { transport_write_error_ = OK; server_cert_verify_result_.Reset(); - completed_handshake_ = false; + completed_connect_ = false; cert_authorities_.clear(); cert_key_types_.clear(); @@ -508,7 +509,7 @@ void SSLClientSocketOpenSSL::Disconnect() { bool SSLClientSocketOpenSSL::IsConnected() const { // If the handshake has not yet completed. - if (!completed_handshake_) + if (!completed_connect_) return false; // If an asynchronous operation is still pending. if (user_read_buf_.get() || user_write_buf_.get()) @@ -519,7 +520,7 @@ bool SSLClientSocketOpenSSL::IsConnected() const { bool SSLClientSocketOpenSSL::IsConnectedAndIdle() const { // If the handshake has not yet completed. - if (!completed_handshake_) + if (!completed_connect_) return false; // If an asynchronous operation is still pending. if (user_read_buf_.get() || user_write_buf_.get()) @@ -588,6 +589,7 @@ bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) { ssl_info->client_cert_sent = ssl_config_.send_client_cert && ssl_config_.client_cert.get(); ssl_info->channel_id_sent = WasChannelIDSent(); + ssl_info->pinning_failure_log = pinning_failure_log_; RecordChannelIDSupport(channel_id_service_, channel_id_xtn_negotiated_, @@ -679,18 +681,6 @@ int SSLClientSocketOpenSSL::SetSendBufferSize(int32 size) { return transport_->socket()->SetSendBufferSize(size); } -// static -void SSLClientSocketOpenSSL::InfoCallback(const SSL* ssl, - int result, - int /*unused*/) { - SSLClientSocketOpenSSL* ssl_socket = - SSLContext::GetInstance()->GetClientSocketFromSSL(ssl); - if (result == SSL_CB_HANDSHAKE_DONE) { - ssl_socket->ran_handshake_finished_callback_ = true; - ssl_socket->CheckIfHandshakeFinished(); - } -} - int SSLClientSocketOpenSSL::Init() { DCHECK(!ssl_); DCHECK(!transport_bio_); @@ -719,7 +709,7 @@ int SSLClientSocketOpenSSL::Init() { DCHECK(transport_bio_); // Install a callback on OpenSSL's end to plumb transport errors through. - BIO_set_callback(ssl_bio, &SSLClientSocketOpenSSL::BIOCallback); + BIO_set_callback(ssl_bio, BIOCallback); BIO_set_callback_arg(ssl_bio, reinterpret_cast(this)); SSL_set_bio(ssl_, ssl_bio, ssl_bio); @@ -1035,6 +1025,21 @@ int SSLClientSocketOpenSSL::DoVerifyCert(int result) { int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) { verifier_.reset(); + bool sni_available = ssl_config_.version_max >= SSL_PROTOCOL_VERSION_TLS1 || + ssl_config_.version_fallback; + const CertStatus cert_status = server_cert_verify_result_.cert_status; + if (transport_security_state_ && + (result == OK || + (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && + !transport_security_state_->CheckPublicKeyPins( + host_and_port_.host(), + sni_available, + server_cert_verify_result_.is_issued_by_known_root, + server_cert_verify_result_.public_key_hashes, + &pinning_failure_log_)) { + result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; + } + if (result == OK) { // TODO(joth): Work out if we need to remember the intermediate CA certs // when the server sends them to us, and do so here. @@ -1046,7 +1051,8 @@ int SSLClientSocketOpenSSL::DoVerifyCertComplete(int result) { << " (" << result << ")"; } - completed_handshake_ = true; + completed_connect_ = true; + // Exit DoHandshakeLoop and return the result to the caller to Connect. DCHECK_EQ(STATE_NONE, next_handshake_state_); return result; @@ -1404,13 +1410,12 @@ int SSLClientSocketOpenSSL::TransportReadComplete(int result) { return result; } -int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, - X509** x509, - EVP_PKEY** pkey) { +int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl) { DVLOG(3) << "OpenSSL ClientCertRequestCallback called"; DCHECK(ssl == ssl_); - DCHECK(*x509 == NULL); - DCHECK(*pkey == NULL); + + // Clear any currently configured certificates. + SSL_certs_clear(ssl_); #if defined(OS_IOS) // TODO(droger): Support client auth on iOS. See http://crbug.com/145954). @@ -1444,7 +1449,6 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, // Second pass: a client certificate should have been selected. if (ssl_config_.client_cert.get()) { - // TODO(davidben): Configure OpenSSL to also send the intermediates. ScopedX509 leaf_x509 = OSCertHandleToOpenSSL(ssl_config_.client_cert->os_cert_handle()); if (!leaf_x509) { @@ -1453,6 +1457,14 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, return -1; } + ScopedX509Stack chain = OSCertHandlesToOpenSSL( + ssl_config_.client_cert->GetIntermediateCertificates()); + if (!chain) { + LOG(WARNING) << "Failed to import intermediate certificates"; + OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_BAD_FORMAT); + return -1; + } + // TODO(davidben): With Linux client auth support, this should be // conditioned on OS_ANDROID and then, with https://crbug.com/394131, // removed altogether. OpenSSLClientKeyStore is mostly an artifact of the @@ -1473,20 +1485,22 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl, return -1; } - // TODO(joth): (copied from NSS) We should wait for server certificate - // verification before sending our credentials. See http://crbug.com/13934 - *x509 = leaf_x509.release(); - *pkey = privkey.release(); + if (!SSL_use_certificate(ssl_, leaf_x509.get()) || + !SSL_use_PrivateKey(ssl_, privkey.get()) || + !SSL_set1_chain(ssl_, chain.get())) { + LOG(WARNING) << "Failed to set client certificate"; + return -1; + } return 1; } #endif // defined(OS_IOS) // Send no client certificate. - return 0; + return 1; } int SSLClientSocketOpenSSL::CertVerifyCallback(X509_STORE_CTX* store_ctx) { - if (!completed_handshake_) { + if (!completed_connect_) { // If the first handshake hasn't completed then we accept any certificates // because we verify after the handshake. return 1; @@ -1586,19 +1600,6 @@ long SSLClientSocketOpenSSL::MaybeReplayTransportError( return retvalue; } -// Determines if the session for |ssl_| is in the cache, and calls the -// handshake completion callback if that is the case. -// -// CheckIfHandshakeFinished is called twice per connection: once after -// MarkSSLSessionAsGood, when the certificate has been verified, and -// once via an OpenSSL callback when the handshake has completed. On the -// second call, when the certificate has been verified and the handshake -// has completed, the connection's handshake completion callback is run. -void SSLClientSocketOpenSSL::CheckIfHandshakeFinished() { - if (ran_handshake_finished_callback_ && marked_session_as_good_) - OnHandshakeCompletion(); -} - // static long SSLClientSocketOpenSSL::BIOCallback( BIO *bio, @@ -1612,6 +1613,32 @@ long SSLClientSocketOpenSSL::BIOCallback( bio, cmd, argp, argi, argl, retvalue); } +// static +void SSLClientSocketOpenSSL::InfoCallback(const SSL* ssl, + int type, + int /*val*/) { + if (type == SSL_CB_HANDSHAKE_DONE) { + SSLClientSocketOpenSSL* ssl_socket = + SSLContext::GetInstance()->GetClientSocketFromSSL(ssl); + ssl_socket->handshake_succeeded_ = true; + ssl_socket->CheckIfHandshakeFinished(); + } +} + +// Determines if both the handshake and certificate verification have completed +// successfully, and calls the handshake completion callback if that is the +// case. +// +// CheckIfHandshakeFinished is called twice per connection: once after +// MarkSSLSessionAsGood, when the certificate has been verified, and +// once via an OpenSSL callback when the handshake has completed. On the +// second call, when the certificate has been verified and the handshake +// has completed, the connection's handshake completion callback is run. +void SSLClientSocketOpenSSL::CheckIfHandshakeFinished() { + if (handshake_succeeded_ && marked_session_as_good_) + OnHandshakeCompletion(); +} + scoped_refptr SSLClientSocketOpenSSL::GetUnverifiedServerCertificateChain() const { return server_cert_; diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h index 3d2194b304af4..0bf8eafe3a8bc 100644 --- a/net/socket/ssl_client_socket_openssl.h +++ b/net/socket/ssl_client_socket_openssl.h @@ -106,10 +106,6 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { friend class SSLClientSocket; friend class SSLContext; - // Callback that is run by OpenSSL to obtain information about the - // state of the SSL handshake. - static void InfoCallback(const SSL* ssl, int result, int unused); - int Init(); void DoReadCallback(int result); void DoWriteCallback(int result); @@ -146,7 +142,7 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { // Callback from the SSL layer that indicates the remote server is requesting // a certificate for this client. - int ClientCertRequestCallback(SSL* ssl, X509** x509, EVP_PKEY** pkey); + int ClientCertRequestCallback(SSL* ssl); // CertVerifyCallback is called to verify the server's certificates. We do // verification after the handshake so this function only enforces that the @@ -172,6 +168,10 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { const char *argp, int argi, long argl, long retvalue); + // Callback that is used to obtain information about the state of the SSL + // handshake. + static void InfoCallback(const SSL* ssl, int type, int val); + void CheckIfHandshakeFinished(); bool transport_send_busy_; @@ -211,11 +211,11 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { // error writing to the transport socket. A value of OK indicates no error. int transport_write_error_; - // Set when handshake finishes. + // Set when Connect finishes. scoped_ptr server_cert_chain_; scoped_refptr server_cert_; CertVerifyResult server_cert_verify_result_; - bool completed_handshake_; + bool completed_connect_; // Set when Read() or Write() successfully reads or writes data to or from the // network. @@ -275,12 +275,20 @@ class SSLClientSocketOpenSSL : public SSLClientSocket { // True if channel ID extension was negotiated. bool channel_id_xtn_negotiated_; // True if InfoCallback has been run with result = SSL_CB_HANDSHAKE_DONE. - bool ran_handshake_finished_callback_; + bool handshake_succeeded_; // True if MarkSSLSessionAsGood has been called for this socket's - // connection's SSL session. + // SSL session. bool marked_session_as_good_; // The request handle for |channel_id_service_|. ChannelIDService::RequestHandle channel_id_request_handle_; + + TransportSecurityState* transport_security_state_; + + // pinning_failure_log contains a message produced by + // TransportSecurityState::CheckPublicKeyPins in the event of a + // pinning failure. It is a (somewhat) human-readable string. + std::string pinning_failure_log_; + BoundNetLog net_log_; }; diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h index 4c026b02b868f..cfc7c0dfbabd3 100644 --- a/net/socket/ssl_client_socket_pool.h +++ b/net/socket/ssl_client_socket_pool.h @@ -134,7 +134,7 @@ class SSLConnectJobMessenger { // Adds |socket| to the list of sockets waiting to Connect(). When // the messenger has determined that it's an appropriate time for |socket| - // to connect, it will asynchronously invoke |callback|. + // to connect, it will invoke |callback|. // // Note: It is an error to call AddPendingSocket() without having first // called MonitorConnectionResult() and configuring a socket that WILL diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc index 55b94bf2c6bf2..f2792a7dc50b0 100644 --- a/net/socket/ssl_client_socket_pool_unittest.cc +++ b/net/socket/ssl_client_socket_pool_unittest.cc @@ -22,6 +22,7 @@ #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_server_properties_impl.h" +#include "net/http/transport_security_state.h" #include "net/proxy/proxy_service.h" #include "net/socket/client_socket_handle.h" #include "net/socket/client_socket_pool_histograms.h" @@ -79,7 +80,8 @@ class SSLClientSocketPoolTest public ::testing::WithParamInterface { protected: SSLClientSocketPoolTest() - : proxy_service_(ProxyService::CreateDirect()), + : transport_security_state_(new TransportSecurityState), + proxy_service_(ProxyService::CreateDirect()), ssl_config_service_(new SSLConfigServiceDefaults), http_auth_handler_factory_( HttpAuthHandlerFactory::CreateDefault(&host_resolver_)), @@ -331,13 +333,13 @@ TEST_P(SSLClientSocketPoolTest, SocketsConnectWithoutFlag) { SSLSocketDataProvider ssl(ASYNC, OK); ssl.is_in_session_cache = false; - ssl.should_block_on_connect = true; + ssl.should_pause_on_connect = true; SSLSocketDataProvider ssl2(ASYNC, OK); ssl2.is_in_session_cache = false; - ssl2.should_block_on_connect = true; + ssl2.should_pause_on_connect = true; SSLSocketDataProvider ssl3(ASYNC, OK); ssl3.is_in_session_cache = false; - ssl3.should_block_on_connect = true; + ssl3.should_pause_on_connect = true; socket_factory_.AddSSLSocketDataProvider(&ssl); socket_factory_.AddSSLSocketDataProvider(&ssl2); socket_factory_.AddSSLSocketDataProvider(&ssl3); @@ -404,7 +406,7 @@ TEST_P(SSLClientSocketPoolTest, DeletedSSLConnectJob) { SSLSocketDataProvider ssl(ASYNC, OK); ssl.is_in_session_cache = false; - ssl.should_block_on_connect = true; + ssl.should_pause_on_connect = true; SSLSocketDataProvider ssl2(ASYNC, OK); ssl2.is_in_session_cache = false; SSLSocketDataProvider ssl3(ASYNC, OK); @@ -469,7 +471,7 @@ TEST_P(SSLClientSocketPoolTest, DeletedSocketAfterFail) { SSLSocketDataProvider ssl(ASYNC, ERR_SSL_PROTOCOL_ERROR); ssl.is_in_session_cache = false; - ssl.should_block_on_connect = true; + ssl.should_pause_on_connect = true; SSLSocketDataProvider ssl2(ASYNC, OK); ssl2.is_in_session_cache = false; SSLSocketDataProvider ssl3(ASYNC, OK); @@ -542,10 +544,10 @@ TEST_P(SSLClientSocketPoolTest, SimultaneousConnectJobsFail) { socket_factory_.AddSocketDataProvider(&data5); SSLSocketDataProvider ssl(ASYNC, ERR_SSL_PROTOCOL_ERROR); ssl.is_in_session_cache = false; - ssl.should_block_on_connect = true; + ssl.should_pause_on_connect = true; SSLSocketDataProvider ssl2(ASYNC, OK); ssl2.is_in_session_cache = false; - ssl2.should_block_on_connect = true; + ssl2.should_pause_on_connect = true; SSLSocketDataProvider ssl3(ASYNC, OK); ssl3.is_in_session_cache = false; SSLSocketDataProvider ssl4(ASYNC, OK); @@ -632,7 +634,7 @@ TEST_P(SSLClientSocketPoolTest, SimultaneousConnectJobsSuccess) { SSLSocketDataProvider ssl(ASYNC, OK); ssl.is_in_session_cache = false; - ssl.should_block_on_connect = true; + ssl.should_pause_on_connect = true; SSLSocketDataProvider ssl2(ASYNC, OK); ssl2.is_in_session_cache = false; SSLSocketDataProvider ssl3(ASYNC, OK); @@ -1257,8 +1259,7 @@ TEST_P(SSLClientSocketPoolTest, NeedProxyAuth) { EXPECT_FALSE(tunnel_handle->socket()->IsConnected()); } -// TODO(rch): re-enable this. -TEST_P(SSLClientSocketPoolTest, DISABLED_IPPooling) { +TEST_P(SSLClientSocketPoolTest, IPPooling) { const int kTestPort = 80; struct TestHosts { std::string name; diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc index 6789d67ab3a7e..c36e5819665c6 100644 --- a/net/socket/ssl_client_socket_unittest.cc +++ b/net/socket/ssl_client_socket_unittest.cc @@ -358,6 +358,9 @@ class FakeBlockingStreamSocket : public WrappedStreamSocket { // Waits for the blocked Write() call to be scheduled. void WaitForWrite(); + // Returns the wrapped stream socket. + StreamSocket* transport() { return transport_.get(); } + private: // Handles completion from the underlying transport read. void OnReadCompleted(int result); @@ -796,6 +799,11 @@ class SSLClientSocketCertRequestInfoTest : public SSLClientSocketTest { }; class SSLClientSocketFalseStartTest : public SSLClientSocketTest { + public: + SSLClientSocketFalseStartTest() + : monitor_handshake_callback_(false), + fail_handshake_after_false_start_(false) {} + protected: // Creates an SSLClientSocket with |client_config| attached to a // FakeBlockingStreamSocket, returning both in |*out_raw_transport| and @@ -817,8 +825,11 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest { scoped_ptr* out_sock) { CHECK(test_server()); - scoped_ptr real_transport( - new TCPClientSocket(addr(), NULL, NetLog::Source())); + scoped_ptr real_transport(scoped_ptr( + new TCPClientSocket(addr(), NULL, NetLog::Source()))); + real_transport.reset( + new SynchronousErrorStreamSocket(real_transport.Pass())); + scoped_ptr transport( new FakeBlockingStreamSocket(real_transport.Pass())); int rv = callback->GetResult(transport->Connect(callback->callback())); @@ -830,6 +841,12 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest { test_server()->host_port_pair(), client_config); + if (monitor_handshake_callback_) { + sock->SetHandshakeCompletionCallback( + base::Bind(&SSLClientSocketTest::RecordCompletedHandshake, + base::Unretained(this))); + } + // Connect. Stop before the client processes the first server leg // (ServerHello, etc.) raw_transport->BlockReadResult(); @@ -845,6 +862,12 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest { raw_transport->UnblockReadResult(); raw_transport->WaitForWrite(); + if (fail_handshake_after_false_start_) { + SynchronousErrorStreamSocket* error_socket = + static_cast( + raw_transport->transport()); + error_socket->SetNextReadError(ERR_CONNECTION_RESET); + } // And, finally, release that and block the next server leg // (ChangeCipherSpec, Finished). raw_transport->BlockReadResult(); @@ -862,6 +885,7 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest { TestCompletionCallback callback; FakeBlockingStreamSocket* raw_transport = NULL; scoped_ptr sock; + ASSERT_NO_FATAL_FAILURE(CreateAndConnectUntilServerFinishedReceived( client_config, &callback, &raw_transport, &sock)); @@ -896,7 +920,10 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest { // After releasing reads, the connection proceeds. raw_transport->UnblockReadResult(); rv = callback.GetResult(rv); - EXPECT_LT(0, rv); + if (fail_handshake_after_false_start_) + EXPECT_EQ(ERR_CONNECTION_RESET, rv); + else + EXPECT_LT(0, rv); } else { // False Start is not enabled, so the handshake will not complete because // the server second leg is blocked. @@ -904,6 +931,13 @@ class SSLClientSocketFalseStartTest : public SSLClientSocketTest { EXPECT_FALSE(callback.have_result()); } } + + // Indicates that the socket's handshake completion callback should + // be monitored. + bool monitor_handshake_callback_; + // Indicates that this test's handshake should fail after the client + // "finished" message is sent. + bool fail_handshake_after_false_start_; }; class SSLClientSocketChannelIDTest : public SSLClientSocketTest { @@ -2694,7 +2728,6 @@ TEST_F(SSLClientSocketTest, HandshakeCallbackIsRun_WithFailure) { // Tests that the completion callback is run when an SSL connection // completes successfully. TEST_F(SSLClientSocketTest, HandshakeCallbackIsRun_WithSuccess) { - SpawnedTestServer::SSLOptions ssl_options; SpawnedTestServer test_server(SpawnedTestServer::TYPE_HTTPS, SpawnedTestServer::kLocalhost, base::FilePath()); @@ -2728,8 +2761,8 @@ TEST_F(SSLClientSocketTest, HandshakeCallbackIsRun_WithSuccess) { EXPECT_TRUE(ran_handshake_completion_callback_); } -// Tests that the completion callback is run with connections -// that do not cache their session. +// Tests that the completion callback is run with a server that doesn't cache +// sessions. TEST_F(SSLClientSocketTest, HandshakeCallbackIsRun_WithDisabledSessionCache) { SpawnedTestServer::SSLOptions ssl_options; ssl_options.disable_session_cache = true; @@ -2764,6 +2797,35 @@ TEST_F(SSLClientSocketTest, HandshakeCallbackIsRun_WithDisabledSessionCache) { EXPECT_TRUE(sock->IsConnected()); EXPECT_TRUE(ran_handshake_completion_callback_); } + +TEST_F(SSLClientSocketFalseStartTest, + HandshakeCallbackIsRun_WithFalseStartFailure) { + // False Start requires NPN and a forward-secret cipher suite. + SpawnedTestServer::SSLOptions server_options; + server_options.key_exchanges = + SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA; + server_options.enable_npn = true; + SSLConfig client_config; + client_config.next_protos.push_back("http/1.1"); + monitor_handshake_callback_ = true; + fail_handshake_after_false_start_ = true; + ASSERT_NO_FATAL_FAILURE(TestFalseStart(server_options, client_config, true)); + ASSERT_TRUE(ran_handshake_completion_callback_); +} + +TEST_F(SSLClientSocketFalseStartTest, + HandshakeCallbackIsRun_WithFalseStartSuccess) { + // False Start requires NPN and a forward-secret cipher suite. + SpawnedTestServer::SSLOptions server_options; + server_options.key_exchanges = + SpawnedTestServer::SSLOptions::KEY_EXCHANGE_DHE_RSA; + server_options.enable_npn = true; + SSLConfig client_config; + client_config.next_protos.push_back("http/1.1"); + monitor_handshake_callback_ = true; + ASSERT_NO_FATAL_FAILURE(TestFalseStart(server_options, client_config, true)); + ASSERT_TRUE(ran_handshake_completion_callback_); +} #endif // defined(USE_OPENSSL) TEST_F(SSLClientSocketFalseStartTest, FalseStartEnabled) { diff --git a/net/socket/ssl_session_cache_openssl.cc b/net/socket/ssl_session_cache_openssl.cc index 2c19c60dd0b25..8aaa9a08660ca 100644 --- a/net/socket/ssl_session_cache_openssl.cc +++ b/net/socket/ssl_session_cache_openssl.cc @@ -10,7 +10,6 @@ #include #include -#include "base/callback.h" #include "base/containers/hash_tables.h" #include "base/lazy_instance.h" #include "base/logging.h" diff --git a/net/socket/ssl_session_cache_openssl.h b/net/socket/ssl_session_cache_openssl.h index 8c2ed4fed0342..abf5eab78cbe9 100644 --- a/net/socket/ssl_session_cache_openssl.h +++ b/net/socket/ssl_session_cache_openssl.h @@ -8,7 +8,6 @@ #include #include "base/basictypes.h" -#include "base/callback_forward.h" #include "net/base/net_export.h" // Avoid including OpenSSL headers here. diff --git a/net/socket/unix_domain_client_socket_posix_unittest.cc b/net/socket/unix_domain_client_socket_posix_unittest.cc index d79112249291d..d22aaf6ba53f8 100644 --- a/net/socket/unix_domain_client_socket_posix_unittest.cc +++ b/net/socket/unix_domain_client_socket_posix_unittest.cc @@ -21,10 +21,14 @@ namespace { const char kSocketFilename[] = "socket_for_testing"; -bool UserCanConnectCallback(bool allow_user, uid_t uid, gid_t gid) { +bool UserCanConnectCallback( + bool allow_user, const UnixDomainServerSocket::Credentials& credentials) { // Here peers are running in same process. - EXPECT_EQ(getuid(), uid); - EXPECT_EQ(getgid(), gid); +#if defined(OS_LINUX) || defined(OS_ANDROID) + EXPECT_EQ(getpid(), credentials.process_id); +#endif + EXPECT_EQ(getuid(), credentials.user_id); + EXPECT_EQ(getgid(), credentials.group_id); return allow_user; } diff --git a/net/socket/unix_domain_listen_socket_posix.cc b/net/socket/unix_domain_listen_socket_posix.cc index 7bab4ffcb7fe0..dc7c19c966386 100644 --- a/net/socket/unix_domain_listen_socket_posix.cc +++ b/net/socket/unix_domain_listen_socket_posix.cc @@ -113,10 +113,9 @@ void UnixDomainListenSocket::Accept() { SocketDescriptor conn = StreamListenSocket::AcceptSocket(); if (conn == kInvalidSocket) return; - uid_t user_id; - gid_t group_id; - if (!UnixDomainServerSocket::GetPeerIds(conn, &user_id, &group_id) || - !auth_callback_.Run(user_id, group_id)) { + UnixDomainServerSocket::Credentials credentials; + if (!UnixDomainServerSocket::GetPeerCredentials(conn, &credentials) || + !auth_callback_.Run(credentials)) { if (IGNORE_EINTR(close(conn)) < 0) LOG(ERROR) << "close() error"; return; diff --git a/net/socket/unix_domain_listen_socket_posix_unittest.cc b/net/socket/unix_domain_listen_socket_posix_unittest.cc index 5fe501730ca15..6fcbfd5885b65 100644 --- a/net/socket/unix_domain_listen_socket_posix_unittest.cc +++ b/net/socket/unix_domain_listen_socket_posix_unittest.cc @@ -138,7 +138,7 @@ class TestListenSocketDelegate : public StreamListenSocket::Delegate { bool UserCanConnectCallback( bool allow_user, const scoped_refptr& event_manager, - uid_t, gid_t) { + const UnixDomainServerSocket::Credentials&) { event_manager->Notify( allow_user ? EVENT_AUTH_GRANTED : EVENT_AUTH_DENIED); return allow_user; diff --git a/net/socket/unix_domain_server_socket_posix.cc b/net/socket/unix_domain_server_socket_posix.cc index 8f2f2d6de2f01..a81f1ce033af0 100644 --- a/net/socket/unix_domain_server_socket_posix.cc +++ b/net/socket/unix_domain_server_socket_posix.cc @@ -28,19 +28,20 @@ UnixDomainServerSocket::~UnixDomainServerSocket() { } // static -bool UnixDomainServerSocket::GetPeerIds(SocketDescriptor socket, - uid_t* user_id, - gid_t* group_id) { +bool UnixDomainServerSocket::GetPeerCredentials(SocketDescriptor socket, + Credentials* credentials) { #if defined(OS_LINUX) || defined(OS_ANDROID) struct ucred user_cred; socklen_t len = sizeof(user_cred); if (getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &user_cred, &len) < 0) return false; - *user_id = user_cred.uid; - *group_id = user_cred.gid; + credentials->process_id = user_cred.pid; + credentials->user_id = user_cred.uid; + credentials->group_id = user_cred.gid; return true; #else - return getpeereid(socket, user_id, group_id) == 0; + return getpeereid( + socket, &credentials->user_id, &credentials->group_id) == 0; #endif } @@ -130,10 +131,9 @@ bool UnixDomainServerSocket::AuthenticateAndGetStreamSocket( scoped_ptr* socket) { DCHECK(accept_socket_); - uid_t user_id; - gid_t group_id; - if (!GetPeerIds(accept_socket_->socket_fd(), &user_id, &group_id) || - !auth_callback_.Run(user_id, group_id)) { + Credentials credentials; + if (!GetPeerCredentials(accept_socket_->socket_fd(), &credentials) || + !auth_callback_.Run(credentials)) { accept_socket_.reset(); return false; } diff --git a/net/socket/unix_domain_server_socket_posix.h b/net/socket/unix_domain_server_socket_posix.h index 06fb8d32c47bc..85c743d2b8fcc 100644 --- a/net/socket/unix_domain_server_socket_posix.h +++ b/net/socket/unix_domain_server_socket_posix.h @@ -25,20 +25,30 @@ class SocketLibevent; // Linux and Android. class NET_EXPORT UnixDomainServerSocket : public ServerSocket { public: + // Credentials of a peer process connected to the socket. + struct NET_EXPORT Credentials { +#if defined(OS_LINUX) || defined(OS_ANDROID) + // Linux/Android API provides more information about the connected peer + // than Windows/OS X. It's useful for permission-based authorization on + // Android. + pid_t process_id; +#endif + uid_t user_id; + gid_t group_id; + }; + // Callback that returns whether the already connected client, identified by - // its process |user_id| and |group_id|, is allowed to keep the connection - // open. Note that the socket is closed immediately in case the callback - // returns false. - typedef base::Callback AuthCallback; + // its credentials, is allowed to keep the connection open. Note that + // the socket is closed immediately in case the callback returns false. + typedef base::Callback AuthCallback; UnixDomainServerSocket(const AuthCallback& auth_callack, bool use_abstract_namespace); virtual ~UnixDomainServerSocket(); - // Gets UID and GID of peer to check permissions. - static bool GetPeerIds(SocketDescriptor socket_fd, - uid_t* user_id, - gid_t* group_id); + // Gets credentials of peer to check permissions. + static bool GetPeerCredentials(SocketDescriptor socket_fd, + Credentials* credentials); // ServerSocket implementation. virtual int Listen(const IPEndPoint& address, int backlog) OVERRIDE; diff --git a/net/socket/unix_domain_server_socket_posix_unittest.cc b/net/socket/unix_domain_server_socket_posix_unittest.cc index 07209f6605657..d8a52812c4e40 100644 --- a/net/socket/unix_domain_server_socket_posix_unittest.cc +++ b/net/socket/unix_domain_server_socket_posix_unittest.cc @@ -24,10 +24,14 @@ namespace { const char kSocketFilename[] = "socket_for_testing"; const char kInvalidSocketPath[] = "/invalid/path"; -bool UserCanConnectCallback(bool allow_user, uid_t uid, gid_t gid) { +bool UserCanConnectCallback(bool allow_user, + const UnixDomainServerSocket::Credentials& credentials) { // Here peers are running in same process. - EXPECT_EQ(getuid(), uid); - EXPECT_EQ(getgid(), gid); +#if defined(OS_LINUX) || defined(OS_ANDROID) + EXPECT_EQ(getpid(), credentials.process_id); +#endif + EXPECT_EQ(getuid(), credentials.user_id); + EXPECT_EQ(getgid(), credentials.group_id); return allow_user; } diff --git a/net/spdy/spdy_session.cc b/net/spdy/spdy_session.cc index 64e2c04b2cab2..51c6ee7919db1 100644 --- a/net/spdy/spdy_session.cc +++ b/net/spdy/spdy_session.cc @@ -29,10 +29,12 @@ #include "net/base/net_log.h" #include "net/base/net_util.h" #include "net/cert/asn1_util.h" +#include "net/cert/cert_verify_result.h" #include "net/http/http_log_util.h" #include "net/http/http_network_session.h" #include "net/http/http_server_properties.h" #include "net/http/http_util.h" +#include "net/http/transport_security_state.h" #include "net/spdy/spdy_buffer_producer.h" #include "net/spdy/spdy_frame_builder.h" #include "net/spdy/spdy_http_utils.h" @@ -529,9 +531,47 @@ SpdySession::PushedStreamInfo::PushedStreamInfo( SpdySession::PushedStreamInfo::~PushedStreamInfo() {} +// static +bool SpdySession::CanPool(TransportSecurityState* transport_security_state, + const SSLInfo& ssl_info, + const std::string& old_hostname, + const std::string& new_hostname) { + // Pooling is prohibited if the server cert is not valid for the new domain, + // and for connections on which client certs were sent. It is also prohibited + // when channel ID was sent if the hosts are from different eTLDs+1. + if (IsCertStatusError(ssl_info.cert_status)) + return false; + + if (ssl_info.client_cert_sent) + return false; + + if (ssl_info.channel_id_sent && + ChannelIDService::GetDomainForHost(new_hostname) != + ChannelIDService::GetDomainForHost(old_hostname)) { + return false; + } + + bool unused = false; + if (!ssl_info.cert->VerifyNameMatch(new_hostname, &unused)) + return false; + + std::string pinning_failure_log; + if (!transport_security_state->CheckPublicKeyPins( + new_hostname, + true, /* sni_available */ + ssl_info.is_issued_by_known_root, + ssl_info.public_key_hashes, + &pinning_failure_log)) { + return false; + } + + return true; +} + SpdySession::SpdySession( const SpdySessionKey& spdy_session_key, const base::WeakPtr& http_server_properties, + TransportSecurityState* transport_security_state, bool verify_domain_authentication, bool enable_sending_initial_data, bool enable_compression, @@ -547,6 +587,7 @@ SpdySession::SpdySession( spdy_session_key_(spdy_session_key), pool_(NULL), http_server_properties_(http_server_properties), + transport_security_state_(transport_security_state), read_buffer_(new IOBuffer(kReadBufferSize)), stream_hi_water_mark_(kFirstStreamId), num_pushed_streams_(0u), @@ -714,18 +755,8 @@ bool SpdySession::VerifyDomainAuthentication(const std::string& domain) { if (!GetSSLInfo(&ssl_info, &was_npn_negotiated, &protocol_negotiated)) return true; // This is not a secure session, so all domains are okay. - // Disable pooling for secure sessions. - // TODO(rch): re-enable this. - return false; -#if 0 - bool unused = false; - return - !ssl_info.client_cert_sent && - (!ssl_info.channel_id_sent || - (ChannelIDService::GetDomainForHost(domain) == - ChannelIDService::GetDomainForHost(host_port_pair().host()))) && - ssl_info.cert->VerifyNameMatch(domain, &unused); -#endif + return CanPool(transport_security_state_, ssl_info, + host_port_pair().host(), domain); } int SpdySession::GetPushStream( diff --git a/net/spdy/spdy_session.h b/net/spdy/spdy_session.h index 4037e03601a41..d2da8637d5922 100644 --- a/net/spdy/spdy_session.h +++ b/net/spdy/spdy_session.h @@ -70,6 +70,7 @@ class BoundNetLog; struct LoadTimingInfo; class SpdyStream; class SSLInfo; +class TransportSecurityState; // NOTE: There's an enum of the same name (also with numeric suffixes) // in histograms.xml. Be sure to add new values there also. @@ -222,6 +223,13 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, FLOW_CONTROL_STREAM_AND_SESSION }; + // Returns true if |hostname| can be pooled into an existing connection + // associated with |ssl_info|. + static bool CanPool(TransportSecurityState* transport_security_state, + const SSLInfo& ssl_info, + const std::string& old_hostname, + const std::string& new_hostname); + // Create a new SpdySession. // |spdy_session_key| is the host/port that this session connects to, privacy // and proxy configuration settings that it's using. @@ -229,6 +237,7 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, // network events to. SpdySession(const SpdySessionKey& spdy_session_key, const base::WeakPtr& http_server_properties, + TransportSecurityState* transport_security_state, bool verify_domain_authentication, bool enable_sending_initial_data, bool enable_compression, @@ -963,6 +972,8 @@ class NET_EXPORT SpdySession : public BufferedSpdyFramerVisitorInterface, SpdySessionPool* pool_; const base::WeakPtr http_server_properties_; + TransportSecurityState* transport_security_state_; + // The socket handle for this session. scoped_ptr connection_; diff --git a/net/spdy/spdy_session_pool.cc b/net/spdy/spdy_session_pool.cc index 189c9453cadbe..d8397855d94f2 100644 --- a/net/spdy/spdy_session_pool.cc +++ b/net/spdy/spdy_session_pool.cc @@ -31,6 +31,7 @@ SpdySessionPool::SpdySessionPool( HostResolver* resolver, SSLConfigService* ssl_config_service, const base::WeakPtr& http_server_properties, + TransportSecurityState* transport_security_state, bool force_single_domain, bool enable_compression, bool enable_ping_based_connection_checking, @@ -41,6 +42,7 @@ SpdySessionPool::SpdySessionPool( SpdySessionPool::TimeFunc time_func, const std::string& trusted_spdy_proxy) : http_server_properties_(http_server_properties), + transport_security_state_(transport_security_state), ssl_config_service_(ssl_config_service), resolver_(resolver), verify_domain_authentication_(true), @@ -98,6 +100,7 @@ base::WeakPtr SpdySessionPool::CreateAvailableSessionFromSocket( scoped_ptr new_session( new SpdySession(key, http_server_properties_, + transport_security_state_, verify_domain_authentication_, enable_sending_initial_data_, enable_compression_, diff --git a/net/spdy/spdy_session_pool.h b/net/spdy/spdy_session_pool.h index 0fad5d2d46b38..2fbb03033be3b 100644 --- a/net/spdy/spdy_session_pool.h +++ b/net/spdy/spdy_session_pool.h @@ -34,6 +34,7 @@ class ClientSocketHandle; class HostResolver; class HttpServerProperties; class SpdySession; +class TransportSecurityState; // This is a very simple pool for open SpdySessions. class NET_EXPORT SpdySessionPool @@ -50,6 +51,7 @@ class NET_EXPORT SpdySessionPool HostResolver* host_resolver, SSLConfigService* ssl_config_service, const base::WeakPtr& http_server_properties, + TransportSecurityState* transport_security_state, bool force_single_domain, bool enable_compression, bool enable_ping_based_connection_checking, @@ -191,6 +193,8 @@ class NET_EXPORT SpdySessionPool const base::WeakPtr http_server_properties_; + TransportSecurityState* transport_security_state_; + // The set of all sessions. This is a superset of the sessions in // |available_sessions_|. // diff --git a/net/spdy/spdy_session_unittest.cc b/net/spdy/spdy_session_unittest.cc index 1fa5f2e92c4b0..8194fe7d09788 100644 --- a/net/spdy/spdy_session_unittest.cc +++ b/net/spdy/spdy_session_unittest.cc @@ -4,6 +4,7 @@ #include "net/spdy/spdy_session.h" +#include "base/base64.h" #include "base/bind.h" #include "base/callback.h" #include "base/memory/scoped_ptr.h" @@ -2375,7 +2376,7 @@ TEST_P(SpdySessionTest, CloseActivatedStreamThatClosesSession) { EXPECT_TRUE(session == NULL); } -TEST_P(SpdySessionTest, DISABLED_VerifyDomainAuthentication) { +TEST_P(SpdySessionTest, VerifyDomainAuthentication) { session_deps_.host_resolver->set_synchronous_mode(true); MockConnect connect_data(SYNCHRONOUS, OK); @@ -2417,8 +2418,7 @@ TEST_P(SpdySessionTest, DISABLED_VerifyDomainAuthentication) { EXPECT_FALSE(session->VerifyDomainAuthentication("mail.google.com")); } -// TODO(rch): re-enable this. -TEST_P(SpdySessionTest, DISABLED_ConnectionPooledWithTlsChannelId) { +TEST_P(SpdySessionTest, ConnectionPooledWithTlsChannelId) { session_deps_.host_resolver->set_synchronous_mode(true); MockConnect connect_data(SYNCHRONOUS, OK); @@ -5001,4 +5001,108 @@ TEST(MapNetErrorToGoAwayStatus, MapsValue) { CHECK_EQ(GOAWAY_PROTOCOL_ERROR, MapNetErrorToGoAwayStatus(ERR_UNEXPECTED)); } +TEST(CanPoolTest, CanPool) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "www.example.org")); + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.com")); + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.google.com")); +} + +TEST(CanPoolTest, CanNotPoolWithCertErrors) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.cert_status = CERT_STATUS_REVOKED; + + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + +TEST(CanPoolTest, CanNotPoolWithClientCerts) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.client_cert_sent = true; + + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + +TEST(CanPoolTest, CanNotPoolAcrossETLDsWithChannelID) { + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + TransportSecurityState tss; + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.channel_id_sent = true; + + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "www.example.com")); +} + +TEST(CanPoolTest, CanNotPoolWithBadPins) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + uint8 bad_pin = 3; + TransportSecurityState tss; + test::AddPin(&tss, "mail.example.org", primary_pin, backup_pin); + + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.is_issued_by_known_root = true; + ssl_info.public_key_hashes.push_back(test::GetTestHashValue(bad_pin)); + + EXPECT_FALSE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + +TEST(CanPoolTest, CanPoolWithAcceptablePins) { + uint8 primary_pin = 1; + uint8 backup_pin = 2; + TransportSecurityState tss; + test::AddPin(&tss, "mail.example.org", primary_pin, backup_pin); + + SSLInfo ssl_info; + ssl_info.cert = ImportCertFromFile(GetTestCertsDirectory(), + "spdy_pooling.pem"); + ssl_info.is_issued_by_known_root = true; + ssl_info.public_key_hashes.push_back(test::GetTestHashValue(primary_pin)); + + EXPECT_TRUE(SpdySession::CanPool( + &tss, ssl_info, "www.example.org", "mail.example.org")); +} + } // namespace net diff --git a/net/spdy/spdy_test_utils.cc b/net/spdy/spdy_test_utils.cc index e33a08bc1c33f..7627abe03c8c0 100644 --- a/net/spdy/spdy_test_utils.cc +++ b/net/spdy/spdy_test_utils.cc @@ -7,10 +7,13 @@ #include #include +#include "base/base64.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/string_number_conversions.h" #include "base/sys_byteorder.h" +#include "net/http/transport_security_state.h" +#include "net/ssl/ssl_info.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { @@ -139,6 +142,36 @@ std::string a2b_hex(const char* hex_data) { return result; } +HashValue GetTestHashValue(uint8_t label) { + HashValue hash_value(HASH_VALUE_SHA256); + memset(hash_value.data(), label, hash_value.size()); + return hash_value; +} + +std::string GetTestPin(uint8_t label) { + HashValue hash_value = GetTestHashValue(label); + std::string base64; + base::Base64Encode(base::StringPiece( + reinterpret_cast(hash_value.data()), hash_value.size()), &base64); + + return std::string("pin-sha256=\"") + base64 + "\""; +} + +void AddPin(TransportSecurityState* state, + const std::string& host, + uint8_t primary_label, + uint8_t backup_label) { + std::string primary_pin = GetTestPin(primary_label); + std::string backup_pin = GetTestPin(backup_label); + std::string header = "max-age = 10000; " + primary_pin + "; " + backup_pin; + + // Construct a fake SSLInfo that will pass AddHPKPHeader's checks. + SSLInfo ssl_info; + ssl_info.is_issued_by_known_root = true; + ssl_info.public_key_hashes.push_back(GetTestHashValue(primary_label)); + EXPECT_TRUE(state->AddHPKPHeader(host, header, ssl_info)); +} + } // namespace test } // namespace net diff --git a/net/spdy/spdy_test_utils.h b/net/spdy/spdy_test_utils.h index 439311e2c16a9..14bc8efbb4cf1 100644 --- a/net/spdy/spdy_test_utils.h +++ b/net/spdy/spdy_test_utils.h @@ -5,12 +5,17 @@ #ifndef NET_SPDY_TEST_UTILS_H_ #define NET_SPDY_TEST_UTILS_H_ +#include + #include #include "net/spdy/spdy_protocol.h" namespace net { +class HashValue; +class TransportSecurityState; + namespace test { std::string HexDumpWithMarks(const unsigned char* data, int length, @@ -33,6 +38,19 @@ void SetFrameLength(SpdyFrame* frame, std::string a2b_hex(const char* hex_data); +// Returns a SHA1 HashValue in which each byte has the value |label|. +HashValue GetTestHashValue(uint8_t label); + +// Returns SHA1 pinning header for the of the base64 encoding of +// GetTestHashValue(|label|). +std::string GetTestPin(uint8_t label); + +// Adds a pin for |host| to |state|. +void AddPin(TransportSecurityState* state, + const std::string& host, + uint8_t primary_label, + uint8_t backup_label); + } // namespace test } // namespace net diff --git a/net/ssl/openssl_client_key_store.cc b/net/ssl/openssl_client_key_store.cc index ef38da6e12770..b0b5eb3d4753d 100644 --- a/net/ssl/openssl_client_key_store.cc +++ b/net/ssl/openssl_client_key_store.cc @@ -15,17 +15,6 @@ namespace net { namespace { -// Increment the reference count of a given EVP_PKEY. This function -// is similar to EVP_PKEY_dup which is not available from the OpenSSL -// version used by Chromium at the moment. Its name is distinct to -// avoid compiler warnings about ambiguous function calls at caller -// sites. -EVP_PKEY* CopyEVP_PKEY(EVP_PKEY* key) { - if (key) - CRYPTO_add(&key->references, 1, CRYPTO_LOCK_EVP_PKEY); - return key; -} - // Return the EVP_PKEY holding the public key of a given certificate. // |cert| is a certificate. // Returns a scoped EVP_PKEY for it. @@ -48,35 +37,35 @@ OpenSSLClientKeyStore::~OpenSSLClientKeyStore() { } OpenSSLClientKeyStore::KeyPair::KeyPair(EVP_PKEY* pub_key, - EVP_PKEY* priv_key) { - public_key = CopyEVP_PKEY(pub_key); - private_key = CopyEVP_PKEY(priv_key); + EVP_PKEY* priv_key) + : public_key(EVP_PKEY_dup(pub_key)), + private_key(EVP_PKEY_dup(priv_key)) { } OpenSSLClientKeyStore::KeyPair::~KeyPair() { - EVP_PKEY_free(public_key); - EVP_PKEY_free(private_key); } -OpenSSLClientKeyStore::KeyPair::KeyPair(const KeyPair& other) { - public_key = CopyEVP_PKEY(other.public_key); - private_key = CopyEVP_PKEY(other.private_key); +OpenSSLClientKeyStore::KeyPair::KeyPair(const KeyPair& other) + : public_key(EVP_PKEY_dup(other.public_key.get())), + private_key(EVP_PKEY_dup(other.private_key.get())) { } void OpenSSLClientKeyStore::KeyPair::operator=(const KeyPair& other) { - EVP_PKEY* old_public_key = public_key; - EVP_PKEY* old_private_key = private_key; - public_key = CopyEVP_PKEY(other.public_key); - private_key = CopyEVP_PKEY(other.private_key); - EVP_PKEY_free(old_private_key); - EVP_PKEY_free(old_public_key); + // Use a temporary ScopedEVP_PKEY because scoped_ptr does not allow resetting + // to the current value, even though it's safe here. + crypto::ScopedEVP_PKEY public_key_tmp(EVP_PKEY_dup(other.public_key.get())); + crypto::ScopedEVP_PKEY private_key_tmp(EVP_PKEY_dup(other.private_key.get())); + public_key.reset(); + public_key = public_key_tmp.Pass(); + private_key.reset(); + private_key = private_key_tmp.Pass(); } int OpenSSLClientKeyStore::FindKeyPairIndex(EVP_PKEY* public_key) { if (!public_key) return -1; for (size_t n = 0; n < pairs_.size(); ++n) { - if (EVP_PKEY_cmp(pairs_[n].public_key, public_key) == 1) + if (EVP_PKEY_cmp(pairs_[n].public_key.get(), public_key) == 1) return static_cast(n); } return -1; @@ -120,7 +109,7 @@ crypto::ScopedEVP_PKEY OpenSSLClientKeyStore::FetchClientCertPrivateKey( if (index < 0) return crypto::ScopedEVP_PKEY(); - return crypto::ScopedEVP_PKEY(CopyEVP_PKEY(pairs_[index].private_key)); + return crypto::ScopedEVP_PKEY(EVP_PKEY_dup(pairs_[index].private_key.get())); } void OpenSSLClientKeyStore::Flush() { diff --git a/net/ssl/openssl_client_key_store.h b/net/ssl/openssl_client_key_store.h index 1cf2ceda1891e..0cbb23a73f667 100644 --- a/net/ssl/openssl_client_key_store.h +++ b/net/ssl/openssl_client_key_store.h @@ -76,8 +76,8 @@ class NET_EXPORT OpenSSLClientKeyStore { void operator=(const KeyPair& other); ~KeyPair(); - EVP_PKEY* public_key; - EVP_PKEY* private_key; + crypto::ScopedEVP_PKEY public_key; + crypto::ScopedEVP_PKEY private_key; private: KeyPair(); // intentionally not implemented. diff --git a/net/test/spawned_test_server/base_test_server.h b/net/test/spawned_test_server/base_test_server.h index fb71222499c97..45ea1cecffa4a 100644 --- a/net/test/spawned_test_server/base_test_server.h +++ b/net/test/spawned_test_server/base_test_server.h @@ -205,7 +205,8 @@ class BaseTestServer { bool enable_npn; // Whether to disable TLS session caching. When session caching is - // disabled, the server will generate a new Session ID for every connection. + // disabled, the server will use an empty session ID in the + // ServerHello. bool disable_session_cache; }; diff --git a/net/tools/quic/end_to_end_test.cc b/net/tools/quic/end_to_end_test.cc index 19985a80faab0..0c86d033e6875 100644 --- a/net/tools/quic/end_to_end_test.cc +++ b/net/tools/quic/end_to_end_test.cc @@ -197,9 +197,6 @@ class EndToEndTest : public ::testing::TestWithParam { FLAGS_enable_quic_pacing = GetParam().use_pacing; FLAGS_enable_quic_fec = GetParam().use_fec; - if (negotiated_version_ >= QUIC_VERSION_19) { - FLAGS_enable_quic_connection_flow_control_2 = true; - } VLOG(1) << "Using Configuration: " << GetParam(); client_config_.SetDefaults(); @@ -295,6 +292,11 @@ class EndToEndTest : public ::testing::TestWithParam { bool Initialize() { QuicTagVector copt; + if (GetParam().use_pacing) { + copt.push_back(kPACE); + } + server_config_.SetConnectionOptionsToSend(copt); + // TODO(nimia): Consider setting the congestion control algorithm for the // client as well according to the test parameter. copt.push_back(GetParam().congestion_control_tag); @@ -1165,8 +1167,6 @@ TEST_P(EndToEndTest, ConnectionMigrationClientPortChanged) { // Tests that the client's port can change during an established QUIC // connection, and that doing so does not result in the connection being // closed by the server. - FLAGS_quic_allow_port_migration = true; - ASSERT_TRUE(Initialize()); EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); @@ -1366,6 +1366,27 @@ TEST_P(EndToEndTest, HeadersAndCryptoStreamsNoConnectionFlowControl) { server_thread_->Resume(); } +TEST_P(EndToEndTest, RequestWithNoBodyWillNeverSendStreamFrameWithFIN) { + // Regression test for b/16010251. + // A stream created on receipt of a simple request with no body will never get + // a stream frame with a FIN. Verify that we don't keep track of the stream in + // the locally closed streams map: it will never be removed if so. + ASSERT_TRUE(Initialize()); + + // Send a simple headers only request, and receive response. + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ(200u, client_->response_headers()->parsed_response_code()); + + // Now verify that the server is not waiting for a final FIN or RST. + server_thread_->Pause(); + QuicDispatcher* dispatcher = + QuicServerPeer::GetDispatcher(server_thread_->server()); + QuicSession* session = dispatcher->session_map().begin()->second; + EXPECT_EQ(0u, QuicSessionPeer::GetLocallyClosedStreamsHighestOffset( + session).size()); + server_thread_->Resume(); +} + } // namespace } // namespace test } // namespace tools diff --git a/net/tools/quic/quic_dispatcher.cc b/net/tools/quic/quic_dispatcher.cc index 9980674fb24a0..cf3a46b3bf0a3 100644 --- a/net/tools/quic/quic_dispatcher.cc +++ b/net/tools/quic/quic_dispatcher.cc @@ -169,7 +169,6 @@ QuicDispatcher::QuicDispatcher(const QuicConfig& config, epoll_server_(epoll_server), helper_(new QuicEpollConnectionHelper(epoll_server_)), supported_versions_(supported_versions), - supported_versions_no_connection_flow_control_(supported_versions), current_packet_(NULL), framer_(supported_versions, /*unused*/ QuicTime::Zero(), true), framer_visitor_(new QuicFramerVisitor(this)) { @@ -185,18 +184,6 @@ void QuicDispatcher::Initialize(int fd) { DCHECK(writer_ == NULL); writer_.reset(CreateWriter(fd)); time_wait_list_manager_.reset(CreateQuicTimeWaitListManager()); - - // Remove all versions > QUIC_VERSION_18 from the - // supported_versions_no_connection_flow_control_ vector. - QuicVersionVector::iterator connection_it = find( - supported_versions_no_connection_flow_control_.begin(), - supported_versions_no_connection_flow_control_.end(), QUIC_VERSION_19); - if (connection_it != supported_versions_no_connection_flow_control_.end()) { - supported_versions_no_connection_flow_control_.erase( - supported_versions_no_connection_flow_control_.begin(), - connection_it + 1); - } - CHECK(!supported_versions_no_connection_flow_control_.empty()); } void QuicDispatcher::ProcessPacket(const IPEndPoint& server_address, @@ -376,26 +363,13 @@ QuicConnection* QuicDispatcher::CreateQuicConnection( QuicConnectionId connection_id, const IPEndPoint& server_address, const IPEndPoint& client_address) { - if (FLAGS_enable_quic_connection_flow_control_2) { - DLOG(INFO) << "Creating QuicDispatcher with all versions."; - return new QuicConnection(connection_id, - client_address, - helper_.get(), - writer_.get(), - false /* owns_writer */, - true /* is_server */, - supported_versions_); - } - - DLOG(INFO) << "Connection flow control disabled, creating QuicDispatcher " - << "WITHOUT version 19 or higher."; return new QuicConnection(connection_id, client_address, helper_.get(), writer_.get(), false /* owns_writer */, true /* is_server */, - supported_versions_no_connection_flow_control_); + supported_versions_); } QuicTimeWaitListManager* QuicDispatcher::CreateQuicTimeWaitListManager() { diff --git a/net/tools/quic/quic_dispatcher.h b/net/tools/quic/quic_dispatcher.h index 088de311af3a2..8d7b03b1e14b2 100644 --- a/net/tools/quic/quic_dispatcher.h +++ b/net/tools/quic/quic_dispatcher.h @@ -144,11 +144,6 @@ class QuicDispatcher : public QuicServerSessionVisitor, return supported_versions_; } - const QuicVersionVector& supported_versions_no_connection_flow_control() - const { - return supported_versions_no_connection_flow_control_; - } - const IPEndPoint& current_server_address() { return current_server_address_; } @@ -215,14 +210,6 @@ class QuicDispatcher : public QuicServerSessionVisitor, // skipped as necessary). const QuicVersionVector supported_versions_; - // Versions which do not support *connection* flow control (introduced in - // QUIC_VERSION_19). - // This is used to construct new QuicConnections when connection flow control - // is disabled via flag. - // TODO(rjshade): Remove this when - // FLAGS_enable_quic_connection_flow_control_2 is removed. - QuicVersionVector supported_versions_no_connection_flow_control_; - // Information about the packet currently being handled. IPEndPoint current_client_address_; IPEndPoint current_server_address_; diff --git a/net/udp/udp_socket_unittest.cc b/net/udp/udp_socket_unittest.cc index 20e38ee4c118f..100692cac92dc 100644 --- a/net/udp/udp_socket_unittest.cc +++ b/net/udp/udp_socket_unittest.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 "net/udp/udp_socket.h" + #include "net/udp/udp_client_socket.h" #include "net/udp/udp_server_socket.h" @@ -585,6 +587,155 @@ TEST_F(UDPSocketTest, MulticastOptions) { socket.Close(); } +// Checking that DSCP bits are set correctly is difficult, +// but let's check that the code doesn't crash at least. +TEST_F(UDPSocketTest, SetDSCP) { + // Setup the server to listen. + IPEndPoint bind_address; + UDPSocket client(DatagramSocket::DEFAULT_BIND, + RandIntCallback(), + NULL, + NetLog::Source()); + // We need a real IP, but we won't actually send anything to it. + CreateUDPAddress("8.8.8.8", 9999, &bind_address); + int rv = client.Connect(bind_address); + if (rv != OK) { + // Let's try localhost then.. + CreateUDPAddress("127.0.0.1", 9999, &bind_address); + rv = client.Connect(bind_address); + } + EXPECT_EQ(OK, rv); + + client.SetDiffServCodePoint(DSCP_NO_CHANGE); + client.SetDiffServCodePoint(DSCP_AF41); + client.SetDiffServCodePoint(DSCP_DEFAULT); + client.SetDiffServCodePoint(DSCP_CS2); + client.SetDiffServCodePoint(DSCP_NO_CHANGE); + client.SetDiffServCodePoint(DSCP_DEFAULT); + client.Close(); +} + } // namespace +#if defined(OS_WIN) + +namespace { + +const HANDLE kFakeHandle = (HANDLE)19; +const QOS_FLOWID kFakeFlowId = (QOS_FLOWID)27; + +BOOL WINAPI FakeQOSCreateHandleFAIL(PQOS_VERSION version, PHANDLE handle) { + EXPECT_EQ(0, version->MinorVersion); + EXPECT_EQ(1, version->MajorVersion); + SetLastError(ERROR_OPEN_FAILED); + return false; +} + +BOOL WINAPI FakeQOSCreateHandle(PQOS_VERSION version, PHANDLE handle) { + EXPECT_EQ(0, version->MinorVersion); + EXPECT_EQ(1, version->MajorVersion); + *handle = kFakeHandle; + return true; +} + +BOOL WINAPI FakeQOSCloseHandle(HANDLE handle) { + EXPECT_EQ(kFakeHandle, handle); + return true; +} + +QOS_TRAFFIC_TYPE g_expected_traffic_type; + +BOOL WINAPI FakeQOSAddSocketToFlow(HANDLE handle, + SOCKET socket, + PSOCKADDR addr, + QOS_TRAFFIC_TYPE traffic_type, + DWORD flags, + PQOS_FLOWID flow_id) { + EXPECT_EQ(kFakeHandle, handle); + EXPECT_EQ(NULL, addr); + EXPECT_EQ(QOS_NON_ADAPTIVE_FLOW, flags); + EXPECT_EQ(0, *flow_id); + *flow_id = kFakeFlowId; + return true; +} + +BOOL WINAPI FakeQOSRemoveSocketFromFlow(HANDLE handle, + SOCKET socket, + QOS_FLOWID flowid, + DWORD reserved) { + EXPECT_EQ(kFakeHandle, handle); + EXPECT_EQ(NULL, socket); + EXPECT_EQ(kFakeFlowId, flowid); + EXPECT_EQ(0, reserved); + return true; +} + +DWORD g_expected_dscp; + +BOOL WINAPI FakeQOSSetFlow(HANDLE handle, + QOS_FLOWID flow_id, + QOS_SET_FLOW op, + ULONG size, + PVOID data, + DWORD reserved, + LPOVERLAPPED overlapped) { + EXPECT_EQ(kFakeHandle, handle); + EXPECT_EQ(QOSSetOutgoingDSCPValue, op); + EXPECT_EQ(sizeof(DWORD), size); + EXPECT_EQ(g_expected_dscp, *reinterpret_cast(data)); + EXPECT_EQ(kFakeFlowId, flow_id); + EXPECT_EQ(0, reserved); + EXPECT_EQ(NULL, overlapped); + return true; +} + +} // namespace + +// Mock out the Qwave functions and make sure they are +// called correctly. Must be in net namespace for friendship +// reasons. +TEST_F(UDPSocketTest, SetDSCPFake) { + // Setup the server to listen. + IPEndPoint bind_address; + // We need a real IP, but we won't actually send anything to it. + CreateUDPAddress("8.8.8.8", 9999, &bind_address); + UDPSocket client(DatagramSocket::DEFAULT_BIND, + RandIntCallback(), + NULL, + NetLog::Source()); + int rv = client.SetDiffServCodePoint(DSCP_AF41); + EXPECT_EQ(ERR_SOCKET_NOT_CONNECTED, rv); + rv = client.Connect(bind_address); + EXPECT_EQ(OK, rv); + + QwaveAPI& qos(QwaveAPI::Get()); + qos.create_handle_func_ = FakeQOSCreateHandleFAIL; + qos.close_handle_func_ = FakeQOSCloseHandle; + qos.add_socket_to_flow_func_ = FakeQOSAddSocketToFlow; + qos.remove_socket_from_flow_func_ = FakeQOSRemoveSocketFromFlow; + qos.set_flow_func_ = FakeQOSSetFlow; + qos.qwave_supported_ = true; + + EXPECT_EQ(OK, client.SetDiffServCodePoint(DSCP_NO_CHANGE)); + EXPECT_EQ(ERROR_NOT_SUPPORTED, client.SetDiffServCodePoint(DSCP_AF41)); + qos.create_handle_func_ = FakeQOSCreateHandle; + g_expected_dscp = DSCP_AF41; + g_expected_traffic_type = QOSTrafficTypeAudioVideo; + EXPECT_EQ(OK, client.SetDiffServCodePoint(DSCP_AF41)); + g_expected_dscp = DSCP_DEFAULT; + g_expected_traffic_type = QOSTrafficTypeBestEffort; + EXPECT_EQ(OK, client.SetDiffServCodePoint(DSCP_DEFAULT)); + g_expected_dscp = DSCP_CS2; + g_expected_traffic_type = QOSTrafficTypeExcellentEffort; + EXPECT_EQ(OK, client.SetDiffServCodePoint(DSCP_CS2)); + g_expected_dscp = DSCP_CS3; + g_expected_traffic_type = QOSTrafficTypeExcellentEffort; + EXPECT_EQ(OK, client.SetDiffServCodePoint(DSCP_NO_CHANGE)); + g_expected_dscp = DSCP_DEFAULT; + g_expected_traffic_type = QOSTrafficTypeBestEffort; + EXPECT_EQ(OK, client.SetDiffServCodePoint(DSCP_DEFAULT)); + client.Close(); +} +#endif + } // namespace net diff --git a/net/udp/udp_socket_win.cc b/net/udp/udp_socket_win.cc index 4f742b93f03c1..f4af9b34eda43 100644 --- a/net/udp/udp_socket_win.cc +++ b/net/udp/udp_socket_win.cc @@ -7,6 +7,7 @@ #include #include "base/callback.h" +#include "base/lazy_instance.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" #include "base/metrics/histogram.h" @@ -156,6 +157,82 @@ void UDPSocketWin::Core::WriteDelegate::OnObjectSignaled(HANDLE object) { core_->Release(); } +//----------------------------------------------------------------------------- + +QwaveAPI::QwaveAPI() : qwave_supported_(false) { + HMODULE qwave = LoadLibrary(L"qwave.dll"); + if (!qwave) + return; + create_handle_func_ = + (CreateHandleFn)GetProcAddress(qwave, "QOSCreateHandle"); + close_handle_func_ = + (CloseHandleFn)GetProcAddress(qwave, "QOSCloseHandle"); + add_socket_to_flow_func_ = + (AddSocketToFlowFn)GetProcAddress(qwave, "QOSAddSocketToFlow"); + remove_socket_from_flow_func_ = + (RemoveSocketFromFlowFn)GetProcAddress(qwave, "QOSRemoveSocketFromFlow"); + set_flow_func_ = (SetFlowFn)GetProcAddress(qwave, "QOSSetFlow"); + + if (create_handle_func_ && close_handle_func_ && + add_socket_to_flow_func_ && remove_socket_from_flow_func_ && + set_flow_func_) { + qwave_supported_ = true; + } +} + +QwaveAPI& QwaveAPI::Get() { + static base::LazyInstance::Leaky lazy_qwave = + LAZY_INSTANCE_INITIALIZER; + return lazy_qwave.Get(); +} + +bool QwaveAPI::qwave_supported() const { + return qwave_supported_; +} +BOOL QwaveAPI::CreateHandle(PQOS_VERSION version, PHANDLE handle) { + return create_handle_func_(version, handle); +} +BOOL QwaveAPI::CloseHandle(HANDLE handle) { + return close_handle_func_(handle); +} + +BOOL QwaveAPI::AddSocketToFlow(HANDLE handle, + SOCKET socket, + PSOCKADDR addr, + QOS_TRAFFIC_TYPE traffic_type, + DWORD flags, + PQOS_FLOWID flow_id) { + return add_socket_to_flow_func_(handle, + socket, + addr, + traffic_type, + flags, + flow_id); +} + +BOOL QwaveAPI::RemoveSocketFromFlow(HANDLE handle, + SOCKET socket, + QOS_FLOWID flow_id, + DWORD reserved) { + return remove_socket_from_flow_func_(handle, socket, flow_id, reserved); +} + +BOOL QwaveAPI::SetFlow(HANDLE handle, + QOS_FLOWID flow_id, + QOS_SET_FLOW op, + ULONG size, + PVOID data, + DWORD reserved, + LPOVERLAPPED overlapped) { + return set_flow_func_(handle, + flow_id, + op, + size, + data, + reserved, + overlapped); +} + //----------------------------------------------------------------------------- @@ -171,7 +248,9 @@ UDPSocketWin::UDPSocketWin(DatagramSocket::BindType bind_type, bind_type_(bind_type), rand_int_cb_(rand_int_cb), recv_from_address_(NULL), - net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_UDP_SOCKET)) { + net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_UDP_SOCKET)), + qos_handle_(NULL), + qos_flow_id_(0) { EnsureWinsockInit(); net_log_.BeginEvent(NetLog::TYPE_SOCKET_ALIVE, source.ToEventParametersCallback()); @@ -190,6 +269,10 @@ void UDPSocketWin::Close() { if (!is_connected()) return; + if (qos_handle_) { + QwaveAPI::Get().CloseHandle(qos_handle_); + } + // Zero out any pending read/write callback state. read_callback_.Reset(); recv_from_address_ = NULL; @@ -831,10 +914,97 @@ int UDPSocketWin::SetMulticastLoopbackMode(bool loopback) { return OK; } -// TODO(hubbe): Implement differentiated services for windows. -// Note: setsockopt(IP_TOS) does not work on windows XP and later. int UDPSocketWin::SetDiffServCodePoint(DiffServCodePoint dscp) { - return ERR_NOT_IMPLEMENTED; + if (dscp == DSCP_NO_CHANGE) { + return OK; + } + + if (!is_connected()) + return ERR_SOCKET_NOT_CONNECTED; + + QwaveAPI& qos(QwaveAPI::Get()); + + if (!qos.qwave_supported()) + return ERROR_NOT_SUPPORTED; + + if (qos_handle_ == NULL) { + QOS_VERSION version; + version.MajorVersion = 1; + version.MinorVersion = 0; + qos.CreateHandle(&version, &qos_handle_); + if (qos_handle_ == NULL) + return ERROR_NOT_SUPPORTED; + } + + QOS_TRAFFIC_TYPE traffic_type = QOSTrafficTypeBestEffort; + switch (dscp) { + case DSCP_CS0: + traffic_type = QOSTrafficTypeBestEffort; + break; + case DSCP_CS1: + traffic_type = QOSTrafficTypeBackground; + break; + case DSCP_AF11: + case DSCP_AF12: + case DSCP_AF13: + case DSCP_CS2: + case DSCP_AF21: + case DSCP_AF22: + case DSCP_AF23: + case DSCP_CS3: + case DSCP_AF31: + case DSCP_AF32: + case DSCP_AF33: + case DSCP_CS4: + traffic_type = QOSTrafficTypeExcellentEffort; + break; + case DSCP_AF41: + case DSCP_AF42: + case DSCP_AF43: + case DSCP_CS5: + traffic_type = QOSTrafficTypeAudioVideo; + break; + case DSCP_EF: + case DSCP_CS6: + traffic_type = QOSTrafficTypeVoice; + break; + case DSCP_CS7: + traffic_type = QOSTrafficTypeControl; + break; + case DSCP_NO_CHANGE: + NOTREACHED(); + break; + } + if (qos_flow_id_ != 0) { + qos.RemoveSocketFromFlow(qos_handle_, NULL, qos_flow_id_, 0); + qos_flow_id_ = 0; + } + if (!qos.AddSocketToFlow(qos_handle_, + socket_, + NULL, + traffic_type, + QOS_NON_ADAPTIVE_FLOW, + &qos_flow_id_)) { + DWORD err = GetLastError(); + if (err == ERROR_DEVICE_REINITIALIZATION_NEEDED) { + qos.CloseHandle(qos_handle_); + qos_flow_id_ = 0; + qos_handle_ = 0; + } + return MapSystemError(err); + } + // This requires admin rights, and may fail, if so we ignore it + // as AddSocketToFlow should still do *approximately* the right thing. + DWORD buf = dscp; + qos.SetFlow(qos_handle_, + qos_flow_id_, + QOSSetOutgoingDSCPValue, + sizeof(buf), + &buf, + 0, + NULL); + + return OK; } void UDPSocketWin::DetachFromThread() { diff --git a/net/udp/udp_socket_win.h b/net/udp/udp_socket_win.h index 907919e23520f..bc97d5683655a 100644 --- a/net/udp/udp_socket_win.h +++ b/net/udp/udp_socket_win.h @@ -5,6 +5,7 @@ #ifndef NET_UDP_UDP_SOCKET_WIN_H_ #define NET_UDP_UDP_SOCKET_WIN_H_ +#include #include #include "base/memory/ref_counted.h" @@ -257,9 +258,70 @@ class NET_EXPORT UDPSocketWin : NON_EXPORTED_BASE(public base::NonThreadSafe) { BoundNetLog net_log_; + // QWAVE data. Used to set DSCP bits on outgoing packets. + HANDLE qos_handle_; + QOS_FLOWID qos_flow_id_; + DISALLOW_COPY_AND_ASSIGN(UDPSocketWin); }; +//----------------------------------------------------------------------------- + +// QWAVE (Quality Windows Audio/Video Experience) is the latest windows +// library for setting packet priorities (and other things). Unfortunately, +// Microsoft has decided that setting the DSCP bits with setsockopt() no +// longer works, so we have to use this API instead. +// This class is meant to be used as a singleton. It exposes a few dynamically +// loaded functions and a bool called "qwave_supported". +class NET_EXPORT QwaveAPI { + typedef BOOL (WINAPI *CreateHandleFn)(PQOS_VERSION, PHANDLE); + typedef BOOL (WINAPI *CloseHandleFn)(HANDLE); + typedef BOOL (WINAPI *AddSocketToFlowFn)( + HANDLE, SOCKET, PSOCKADDR, QOS_TRAFFIC_TYPE, DWORD, PQOS_FLOWID); + typedef BOOL (WINAPI *RemoveSocketFromFlowFn)( + HANDLE, SOCKET, QOS_FLOWID, DWORD); + typedef BOOL (WINAPI *SetFlowFn)( + HANDLE, QOS_FLOWID, QOS_SET_FLOW, ULONG, PVOID, DWORD, LPOVERLAPPED); + + public: + QwaveAPI(); + + static QwaveAPI& Get(); + + bool qwave_supported() const; + BOOL CreateHandle(PQOS_VERSION version, PHANDLE handle); + BOOL CloseHandle(HANDLE handle); + BOOL AddSocketToFlow(HANDLE handle, + SOCKET socket, + PSOCKADDR addr, + QOS_TRAFFIC_TYPE traffic_type, + DWORD flags, + PQOS_FLOWID flow_id); + BOOL RemoveSocketFromFlow(HANDLE handle, + SOCKET socket, + QOS_FLOWID flow_id, + DWORD reserved); + BOOL SetFlow(HANDLE handle, + QOS_FLOWID flow_id, + QOS_SET_FLOW op, + ULONG size, + PVOID data, + DWORD reserved, + LPOVERLAPPED overlapped); + + private: + bool qwave_supported_; + CreateHandleFn create_handle_func_; + CloseHandleFn close_handle_func_; + AddSocketToFlowFn add_socket_to_flow_func_; + RemoveSocketFromFlowFn remove_socket_from_flow_func_; + SetFlowFn set_flow_func_; + + FRIEND_TEST_ALL_PREFIXES(UDPSocketTest, SetDSCPFake); + DISALLOW_COPY_AND_ASSIGN(QwaveAPI); +}; + + } // namespace net #endif // NET_UDP_UDP_SOCKET_WIN_H_ diff --git a/net/url_request/redirect_info.cc b/net/url_request/redirect_info.cc new file mode 100644 index 0000000000000..16b517a994bba --- /dev/null +++ b/net/url_request/redirect_info.cc @@ -0,0 +1,13 @@ +// 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 "net/url_request/redirect_info.h" + +namespace net { + +RedirectInfo::RedirectInfo() : status_code(-1) {} + +RedirectInfo::~RedirectInfo() {} + +} // namespace net diff --git a/net/url_request/redirect_info.h b/net/url_request/redirect_info.h new file mode 100644 index 0000000000000..b1ae6369eb3cd --- /dev/null +++ b/net/url_request/redirect_info.h @@ -0,0 +1,43 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_URL_REQUEST_REDIRECT_INFO_H_ +#define NET_URL_REQUEST_REDIRECT_INFO_H_ + +#include + +#include "net/base/net_export.h" +#include "url/gurl.h" + +namespace net { + +// RedirectInfo captures information about a redirect and any fields in a +// request that change. This struct must be kept in sync with +// content/common/resource_messages.h. +struct NET_EXPORT RedirectInfo { + RedirectInfo(); + ~RedirectInfo(); + + // The status code for the redirect response. This is almost redundant with + // the response headers, but some URLRequestJobs emit redirects without + // headers. + int status_code; + + // The new request method. Depending on the response code, the request method + // may change. + std::string new_method; + + // The new request URL. + GURL new_url; + + // The new first-party URL for cookies. + GURL new_first_party_for_cookies; + + // The new HTTP referrer header. + std::string new_referrer; +}; + +} // namespace net + +#endif // NET_URL_REQUEST_REDIRECT_INFO_H_ diff --git a/net/url_request/url_fetcher_core.cc b/net/url_request/url_fetcher_core.cc index eb45ecf52e375..e8da0e1ee992c 100644 --- a/net/url_request/url_fetcher_core.cc +++ b/net/url_request/url_fetcher_core.cc @@ -20,6 +20,7 @@ #include "net/base/upload_data_stream.h" #include "net/base/upload_file_element_reader.h" #include "net/http/http_response_headers.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_fetcher_delegate.h" #include "net/url_request/url_fetcher_response_writer.h" #include "net/url_request/url_request_context.h" @@ -371,13 +372,13 @@ bool URLFetcherCore::GetResponseAsFilePath(bool take_ownership, } void URLFetcherCore::OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) { DCHECK_EQ(request, request_.get()); DCHECK(network_task_runner_->BelongsToCurrentThread()); if (stop_on_redirect_) { stopped_on_redirect_ = true; - url_ = new_url; + url_ = redirect_info.new_url; response_code_ = request_->GetResponseCode(); was_fetched_via_proxy_ = request_->was_fetched_via_proxy(); request->Cancel(); diff --git a/net/url_request/url_fetcher_core.h b/net/url_request/url_fetcher_core.h index abf42b5248cea..bddeaf4c88b88 100644 --- a/net/url_request/url_fetcher_core.h +++ b/net/url_request/url_fetcher_core.h @@ -123,7 +123,7 @@ class URLFetcherCore // Overridden from URLRequest::Delegate: virtual void OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE; virtual void OnResponseStarted(URLRequest* request) OVERRIDE; virtual void OnReadCompleted(URLRequest* request, diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc index 5466be1b58943..6b33aaac575d7 100644 --- a/net/url_request/url_request.cc +++ b/net/url_request/url_request.cc @@ -30,6 +30,7 @@ #include "net/http/http_response_headers.h" #include "net/http/http_util.h" #include "net/ssl/ssl_cert_request_info.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_error_job.h" #include "net/url_request/url_request_job.h" @@ -169,7 +170,7 @@ URLRequestJob* URLRequest::Interceptor::MaybeInterceptResponse( // URLRequest::Delegate void URLRequest::Delegate::OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) { } @@ -259,6 +260,7 @@ void URLRequest::Init(const GURL& url, url_chain_.push_back(url); method_ = "GET"; referrer_policy_ = CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE; + first_party_url_policy_ = NEVER_CHANGE_FIRST_PARTY_URL; load_flags_ = LOAD_NORMAL; delegate_ = delegate; is_pending_ = false; @@ -563,9 +565,16 @@ bool URLRequest::IsHandledURL(const GURL& url) { void URLRequest::set_first_party_for_cookies( const GURL& first_party_for_cookies) { + DCHECK(!is_pending_); first_party_for_cookies_ = first_party_for_cookies; } +void URLRequest::set_first_party_url_policy( + FirstPartyURLPolicy first_party_url_policy) { + DCHECK(!is_pending_); + first_party_url_policy_ = first_party_url_policy; +} + void URLRequest::set_method(const std::string& method) { DCHECK(!is_pending_); method_ = method; @@ -822,18 +831,19 @@ void URLRequest::StopCaching() { job_->StopCaching(); } -void URLRequest::NotifyReceivedRedirect(const GURL& location, +void URLRequest::NotifyReceivedRedirect(const RedirectInfo& redirect_info, bool* defer_redirect) { is_redirecting_ = true; + // TODO(davidben): Pass the full RedirectInfo down to MaybeInterceptRedirect? URLRequestJob* job = URLRequestJobManager::GetInstance()->MaybeInterceptRedirect( - this, network_delegate_, location); + this, network_delegate_, redirect_info.new_url); if (job) { RestartWithJob(job); } else if (delegate_) { OnCallToDelegate(); - delegate_->OnReceivedRedirect(this, location, defer_redirect); + delegate_->OnReceivedRedirect(this, redirect_info, defer_redirect); // |this| may be have been destroyed here. } } @@ -956,27 +966,29 @@ void URLRequest::OrphanJob() { job_ = NULL; } -int URLRequest::Redirect(const GURL& location, int http_status_code) { +int URLRequest::Redirect(const RedirectInfo& redirect_info) { // Matches call in NotifyReceivedRedirect. OnCallToDelegateComplete(); if (net_log_.IsLogging()) { net_log_.AddEvent( NetLog::TYPE_URL_REQUEST_REDIRECTED, - NetLog::StringCallback("location", &location.possibly_invalid_spec())); + NetLog::StringCallback("location", + &redirect_info.new_url.possibly_invalid_spec())); } + // TODO(davidben): Pass the full RedirectInfo to the NetworkDelegate. if (network_delegate_) - network_delegate_->NotifyBeforeRedirect(this, location); + network_delegate_->NotifyBeforeRedirect(this, redirect_info.new_url); if (redirect_limit_ <= 0) { DVLOG(1) << "disallowing redirect: exceeds limit"; return ERR_TOO_MANY_REDIRECTS; } - if (!location.is_valid()) + if (!redirect_info.new_url.is_valid()) return ERR_INVALID_URL; - if (!job_->IsSafeRedirect(location)) { + if (!job_->IsSafeRedirect(redirect_info.new_url)) { DVLOG(1) << "disallowing redirect: unsafe protocol"; return ERR_UNSAFE_REDIRECT; } @@ -985,8 +997,8 @@ int URLRequest::Redirect(const GURL& location, int http_status_code) { final_upload_progress_ = job_->GetUploadProgress(); PrepareToRestart(); - std::string new_method(ComputeMethodForRedirect(method_, http_status_code)); - if (new_method != method_) { + if (redirect_info.new_method != method_) { + // TODO(davidben): This logic still needs to be replicated at the consumers. if (method_ == "POST") { // If being switched from POST, must remove headers that were specific to // the POST and don't have meaning in other methods. For example the @@ -996,17 +1008,13 @@ int URLRequest::Redirect(const GURL& location, int http_status_code) { StripPostSpecificHeaders(&extra_request_headers_); } upload_data_stream_.reset(); - method_.swap(new_method); + method_ = redirect_info.new_method; } - // Suppress the referrer if we're redirecting out of https. - if (referrer_policy_ == - CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE && - GURL(referrer_).SchemeIsSecure() && !location.SchemeIsSecure()) { - referrer_.clear(); - } + referrer_ = redirect_info.new_referrer; + first_party_for_cookies_ = redirect_info.new_first_party_for_cookies; - url_chain_.push_back(location); + url_chain_.push_back(redirect_info.new_url); --redirect_limit_; Start(); diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h index 0357c6ae80323..b4f2079526129 100644 --- a/net/url_request/url_request.h +++ b/net/url_request/url_request.h @@ -51,6 +51,7 @@ class CookieOptions; class HostPortPair; class IOBuffer; struct LoadTimingInfo; +struct RedirectInfo; class SSLCertRequestInfo; class SSLInfo; class UploadDataStream; @@ -108,6 +109,15 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), NEVER_CLEAR_REFERRER, }; + // First-party URL redirect policy: During server redirects, the first-party + // URL for cookies normally doesn't change. However, if the request is a + // top-level first-party request, the first-party URL should be updated to the + // URL on every redirect. + enum FirstPartyURLPolicy { + NEVER_CHANGE_FIRST_PARTY_URL, + UPDATE_FIRST_PARTY_URL_ON_REDIRECT, + }; + // This class handles network interception. Use with // (Un)RegisterRequestInterceptor. class NET_EXPORT Interceptor { @@ -185,10 +195,10 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), // class NET_EXPORT Delegate { public: - // Called upon a server-initiated redirect. The delegate may call the - // request's Cancel method to prevent the redirect from being followed. - // Since there may be multiple chained redirects, there may also be more - // than one redirect call. + // Called upon receiving a redirect. The delegate may call the request's + // Cancel method to prevent the redirect from being followed. Since there + // may be multiple chained redirects, there may also be more than one + // redirect call. // // When this function is called, the request will still contain the // original URL, the destination of the redirect is provided in 'new_url'. @@ -202,7 +212,7 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), // need to set it if they are happy with the default behavior of not // deferring redirect. virtual void OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect); // Called when we receive an authentication failure. The delegate should @@ -324,10 +334,17 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), const GURL& first_party_for_cookies() const { return first_party_for_cookies_; } - // This method may be called before Start() or FollowDeferredRedirect() is - // called. + // This method may only be called before Start(). void set_first_party_for_cookies(const GURL& first_party_for_cookies); + // The first-party URL policy to apply when updating the first party URL + // during redirects. The first-party URL policy may only be changed before + // Start() is called. + FirstPartyURLPolicy first_party_url_policy() const { + return first_party_url_policy_; + } + void set_first_party_url_policy(FirstPartyURLPolicy first_party_url_policy); + // The request method, as an uppercase string. "GET" is the default value. // The request method may only be changed before Start() is called and // should only be assigned an uppercase value. @@ -350,6 +367,7 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), // The referrer policy to apply when updating the referrer during redirects. // The referrer policy may only be changed before Start() is called. + ReferrerPolicy referrer_policy() const { return referrer_policy_; } void set_referrer_policy(ReferrerPolicy referrer_policy); // Sets the delegate of the request. This value may be changed at any time, @@ -682,10 +700,11 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), // Allow the URLRequestJob to redirect this request. Returns OK if // successful, otherwise an error code is returned. - int Redirect(const GURL& location, int http_status_code); + int Redirect(const RedirectInfo& redirect_info); // Called by URLRequestJob to allow interception when a redirect occurs. - void NotifyReceivedRedirect(const GURL& location, bool* defer_redirect); + void NotifyReceivedRedirect(const RedirectInfo& redirect_info, + bool* defer_redirect); // Called by URLRequestHttpJob (note, only HTTP(S) jobs will call this) to // allow deferral of network initialization. @@ -788,6 +807,7 @@ class NET_EXPORT URLRequest : NON_EXPORTED_BASE(public base::NonThreadSafe), std::string method_; // "GET", "POST", etc. Should be all uppercase. std::string referrer_; ReferrerPolicy referrer_policy_; + FirstPartyURLPolicy first_party_url_policy_; HttpRequestHeaders extra_request_headers_; int load_flags_; // Flags indicating the request type for the load; // expected values are LOAD_* enums above. diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h index 4b09534775cb4..6fca1d6cbcd7e 100644 --- a/net/url_request/url_request_context_builder.h +++ b/net/url_request/url_request_context_builder.h @@ -168,7 +168,7 @@ class NET_EXPORT URLRequestContextBuilder { URLRequestContext* Build(); private: - struct SchemeFactory { + struct NET_EXPORT SchemeFactory { SchemeFactory(const std::string& scheme, net::HttpAuthHandlerFactory* factory); ~SchemeFactory(); diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc index a66a3f553f9e1..239f06c4bb0c1 100644 --- a/net/url_request/url_request_job.cc +++ b/net/url_request/url_request_job.cc @@ -33,7 +33,6 @@ URLRequestJob::URLRequestJob(URLRequest* request, filtered_read_buffer_len_(0), has_handled_response_(false), expected_content_size_(-1), - deferred_redirect_status_code_(-1), network_delegate_(network_delegate), weak_factory_(this) { base::PowerMonitor* power_monitor = base::PowerMonitor::Get(); @@ -203,22 +202,18 @@ void URLRequestJob::ContinueDespiteLastError() { } void URLRequestJob::FollowDeferredRedirect() { - DCHECK(deferred_redirect_status_code_ != -1); + DCHECK_NE(-1, deferred_redirect_info_.status_code); - // NOTE: deferred_redirect_url_ may be invalid, and attempting to redirect to - // such an URL will fail inside FollowRedirect. The DCHECK above asserts - // that we called OnReceivedRedirect. + // NOTE: deferred_redirect_info_ may be invalid, and attempting to follow it + // will fail inside FollowRedirect. The DCHECK above asserts that we called + // OnReceivedRedirect. // It is also possible that FollowRedirect will drop the last reference to // this job, so we need to reset our members before calling it. - GURL redirect_url = deferred_redirect_url_; - int redirect_status_code = deferred_redirect_status_code_; - - deferred_redirect_url_ = GURL(); - deferred_redirect_status_code_ = -1; - - FollowRedirect(redirect_url, redirect_status_code); + RedirectInfo redirect_info = deferred_redirect_info_; + deferred_redirect_info_ = RedirectInfo(); + FollowRedirect(redirect_info); } void URLRequestJob::ResumeNetworkStart() { @@ -335,22 +330,11 @@ void URLRequestJob::NotifyHeadersComplete() { // so it does not treat being stopped as an error. DoneReadingRedirectResponse(); - const GURL& url = request_->url(); - - // Move the reference fragment of the old location to the new one if the - // new one has none. This duplicates mozilla's behavior. - if (url.is_valid() && url.has_ref() && !new_location.has_ref() && - CopyFragmentOnRedirect(new_location)) { - GURL::Replacements replacements; - // Reference the |ref| directly out of the original URL to avoid a - // malloc. - replacements.SetRef(url.spec().data(), - url.parsed_for_possibly_invalid_spec().ref); - new_location = new_location.ReplaceComponents(replacements); - } + RedirectInfo redirect_info = + ComputeRedirectInfo(new_location, http_status_code); bool defer_redirect = false; - request_->NotifyReceivedRedirect(new_location, &defer_redirect); + request_->NotifyReceivedRedirect(redirect_info, &defer_redirect); // Ensure that the request wasn't detached or destroyed in // NotifyReceivedRedirect @@ -360,10 +344,9 @@ void URLRequestJob::NotifyHeadersComplete() { // If we were not cancelled, then maybe follow the redirect. if (request_->status().is_success()) { if (defer_redirect) { - deferred_redirect_url_ = new_location; - deferred_redirect_status_code_ = http_status_code; + deferred_redirect_info_ = redirect_info; } else { - FollowRedirect(new_location, http_status_code); + FollowRedirect(redirect_info); } return; } @@ -726,8 +709,8 @@ bool URLRequestJob::ReadRawDataHelper(IOBuffer* buf, int buf_size, return rv; } -void URLRequestJob::FollowRedirect(const GURL& location, int http_status_code) { - int rv = request_->Redirect(location, http_status_code); +void URLRequestJob::FollowRedirect(const RedirectInfo& redirect_info) { + int rv = request_->Redirect(redirect_info); if (rv != OK) NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, rv)); } @@ -770,4 +753,52 @@ bool URLRequestJob::FilterHasData() { void URLRequestJob::UpdatePacketReadTimes() { } +RedirectInfo URLRequestJob::ComputeRedirectInfo(const GURL& location, + int http_status_code) { + const GURL& url = request_->url(); + + RedirectInfo redirect_info; + + redirect_info.status_code = http_status_code; + + // The request method may change, depending on the status code. + redirect_info.new_method = URLRequest::ComputeMethodForRedirect( + request_->method(), http_status_code); + + // Move the reference fragment of the old location to the new one if the + // new one has none. This duplicates mozilla's behavior. + if (url.is_valid() && url.has_ref() && !location.has_ref() && + CopyFragmentOnRedirect(location)) { + GURL::Replacements replacements; + // Reference the |ref| directly out of the original URL to avoid a + // malloc. + replacements.SetRef(url.spec().data(), + url.parsed_for_possibly_invalid_spec().ref); + redirect_info.new_url = location.ReplaceComponents(replacements); + } else { + redirect_info.new_url = location; + } + + // Update the first-party URL if appropriate. + if (request_->first_party_url_policy() == + URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT) { + redirect_info.new_first_party_for_cookies = redirect_info.new_url; + } else { + redirect_info.new_first_party_for_cookies = + request_->first_party_for_cookies(); + } + + // Suppress the referrer if we're redirecting out of https. + if (request_->referrer_policy() == + URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE && + GURL(request_->referrer()).SchemeIsSecure() && + !redirect_info.new_url.SchemeIsSecure()) { + redirect_info.new_referrer.clear(); + } else { + redirect_info.new_referrer = request_->referrer(); + } + + return redirect_info; +} + } // namespace net diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h index 2942e79d616c5..2dcd67af878b0 100644 --- a/net/url_request/url_request_job.h +++ b/net/url_request/url_request_job.h @@ -19,6 +19,7 @@ #include "net/base/request_priority.h" #include "net/base/upload_progress.h" #include "net/cookies/canonical_cookie.h" +#include "net/url_request/redirect_info.h" #include "url/gurl.h" namespace net { @@ -364,7 +365,7 @@ class NET_EXPORT URLRequestJob // Called in response to a redirect that was not canceled to follow the // redirect. The current job will be replaced with a new job loading the // given redirect destination. - void FollowRedirect(const GURL& location, int http_status_code); + void FollowRedirect(const RedirectInfo& redirect_info); // Called after every raw read. If |bytes_read| is > 0, this indicates // a successful read of |bytes_read| unfiltered bytes. If |bytes_read| @@ -384,6 +385,10 @@ class NET_EXPORT URLRequestJob // The default implementation does nothing. virtual void UpdatePacketReadTimes(); + // Computes a new RedirectInfo based on receiving a redirect response of + // |location| and |http_status_code|. + RedirectInfo ComputeRedirectInfo(const GURL& location, int http_status_code); + // Indicates that the job is done producing data, either it has completed // all the data or an error has been encountered. Set exclusively by // NotifyDone so that it is kept in sync with the request. @@ -419,8 +424,7 @@ class NET_EXPORT URLRequestJob int64 expected_content_size_; // Set when a redirect is deferred. - GURL deferred_redirect_url_; - int deferred_redirect_status_code_; + RedirectInfo deferred_redirect_info_; // The network delegate to use with this request, if any. NetworkDelegate* network_delegate_; diff --git a/net/url_request/url_request_test_util.cc b/net/url_request/url_request_test_util.cc index 03c189b8e1190..3800fec8b76b1 100644 --- a/net/url_request/url_request_test_util.cc +++ b/net/url_request/url_request_test_util.cc @@ -195,7 +195,7 @@ void TestDelegate::ClearFullRequestHeaders() { } void TestDelegate::OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) { EXPECT_TRUE(request->is_redirecting()); @@ -366,7 +366,7 @@ void TestNetworkDelegate::InitRequestStatesIfNew(int request_id) { int TestNetworkDelegate::OnBeforeURLRequest( URLRequest* request, const CompletionCallback& callback, - GURL* new_url ) { + GURL* new_url) { int req_id = request->identifier(); InitRequestStatesIfNew(req_id); event_order_[req_id] += "OnBeforeURLRequest\n"; diff --git a/net/url_request/url_request_test_util.h b/net/url_request/url_request_test_util.h index 28613ce94fe6b..3e3a11400a749 100644 --- a/net/url_request/url_request_test_util.h +++ b/net/url_request/url_request_test_util.h @@ -175,7 +175,8 @@ class TestDelegate : public URLRequest::Delegate { void ClearFullRequestHeaders(); // URLRequest::Delegate: - virtual void OnReceivedRedirect(URLRequest* request, const GURL& new_url, + virtual void OnReceivedRedirect(URLRequest* request, + const RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE; virtual void OnBeforeNetworkStart(URLRequest* request, bool* defer) OVERRIDE; virtual void OnAuthRequired(URLRequest* request, diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc index a66924ef862ff..2a1de2c99a1bc 100644 --- a/net/url_request/url_request_unittest.cc +++ b/net/url_request/url_request_unittest.cc @@ -104,10 +104,10 @@ const base::string16 kUser(ASCIIToUTF16("user")); // Tests load timing information in the case a fresh connection was used, with // no proxy. -void TestLoadTimingNotReused(const net::LoadTimingInfo& load_timing_info, +void TestLoadTimingNotReused(const LoadTimingInfo& load_timing_info, int connect_timing_flags) { EXPECT_FALSE(load_timing_info.socket_reused); - EXPECT_NE(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); @@ -127,10 +127,10 @@ void TestLoadTimingNotReused(const net::LoadTimingInfo& load_timing_info, // Same as above, but with proxy times. void TestLoadTimingNotReusedWithProxy( - const net::LoadTimingInfo& load_timing_info, + const LoadTimingInfo& load_timing_info, int connect_timing_flags) { EXPECT_FALSE(load_timing_info.socket_reused); - EXPECT_NE(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); @@ -151,9 +151,9 @@ void TestLoadTimingNotReusedWithProxy( // Same as above, but with a reused socket and proxy times. void TestLoadTimingReusedWithProxy( - const net::LoadTimingInfo& load_timing_info) { + const LoadTimingInfo& load_timing_info) { EXPECT_TRUE(load_timing_info.socket_reused); - EXPECT_NE(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + EXPECT_NE(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); @@ -193,9 +193,9 @@ void FillBuffer(char* buffer, size_t len) { #if !defined(OS_IOS) void TestLoadTimingCacheHitNoNetwork( - const net::LoadTimingInfo& load_timing_info) { + const LoadTimingInfo& load_timing_info) { EXPECT_FALSE(load_timing_info.socket_reused); - EXPECT_EQ(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + EXPECT_EQ(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); EXPECT_FALSE(load_timing_info.request_start_time.is_null()); EXPECT_FALSE(load_timing_info.request_start.is_null()); @@ -212,9 +212,9 @@ void TestLoadTimingCacheHitNoNetwork( // Tests load timing in the case that there is no HTTP response. This can be // used to test in the case of errors or non-HTTP requests. void TestLoadTimingNoHttpResponse( - const net::LoadTimingInfo& load_timing_info) { + const LoadTimingInfo& load_timing_info) { EXPECT_FALSE(load_timing_info.socket_reused); - EXPECT_EQ(net::NetLog::Source::kInvalidId, load_timing_info.socket_log_id); + EXPECT_EQ(NetLog::Source::kInvalidId, load_timing_info.socket_log_id); // Only the request times should be non-null. EXPECT_FALSE(load_timing_info.request_start_time.is_null()); @@ -305,7 +305,7 @@ class BlockingNetworkDelegate : public TestNetworkDelegate { }; // Behavior during blocked stages. During other stages, just - // returns net::OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION. + // returns OK or NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION. enum BlockMode { SYNCHRONOUS, // No callback, returns specified return values. AUTO_CALLBACK, // |this| posts a task to run the callback using the @@ -771,7 +771,7 @@ TEST_F(URLRequestTest, FileTestFullSpecifiedRange) { HttpRequestHeaders headers; headers.SetHeader( HttpRequestHeaders::kRange, - net::HttpByteRange::Bounded( + HttpByteRange::Bounded( first_byte_position, last_byte_position).GetHeaderValue()); r.SetExtraRequestHeaders(headers); r.Start(); @@ -814,7 +814,7 @@ TEST_F(URLRequestTest, FileTestHalfSpecifiedRange) { HttpRequestHeaders headers; headers.SetHeader(HttpRequestHeaders::kRange, - net::HttpByteRange::RightUnbounded( + HttpByteRange::RightUnbounded( first_byte_position).GetHeaderValue()); r.SetExtraRequestHeaders(headers); r.Start(); @@ -869,7 +869,7 @@ TEST_F(URLRequestTest, AllowFileURLs) { ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir.path(), &test_file)); std::string test_data("monkey"); base::WriteFile(test_file, test_data.data(), test_data.size()); - GURL test_file_url = net::FilePathToFileURL(test_file); + GURL test_file_url = FilePathToFileURL(test_file); { TestDelegate d; @@ -1873,7 +1873,7 @@ TEST_F(URLRequestTest, NetworkDelegateProxyError) { EXPECT_EQ(1, network_delegate.completed_requests()); } -// Make sure that net::NetworkDelegate::NotifyCompleted is called if +// Make sure that NetworkDelegate::NotifyCompleted is called if // content is empty. TEST_F(URLRequestTest, RequestCompletionForEmptyResponse) { TestDelegate d; @@ -2431,12 +2431,12 @@ class FixedDateNetworkDelegate : public TestNetworkDelegate { : fixed_date_(fixed_date) {} virtual ~FixedDateNetworkDelegate() {} - // net::NetworkDelegate implementation + // NetworkDelegate implementation virtual int OnHeadersReceived( - net::URLRequest* request, - const net::CompletionCallback& callback, - const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* override_response_headers, + URLRequest* request, + const CompletionCallback& callback, + const HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) OVERRIDE; private: @@ -2446,13 +2446,13 @@ class FixedDateNetworkDelegate : public TestNetworkDelegate { }; int FixedDateNetworkDelegate::OnHeadersReceived( - net::URLRequest* request, - const net::CompletionCallback& callback, - const net::HttpResponseHeaders* original_response_headers, - scoped_refptr* override_response_headers, + URLRequest* request, + const CompletionCallback& callback, + const HttpResponseHeaders* original_response_headers, + scoped_refptr* override_response_headers, GURL* allowed_unsafe_redirect_url) { - net::HttpResponseHeaders* new_response_headers = - new net::HttpResponseHeaders(original_response_headers->raw_headers()); + HttpResponseHeaders* new_response_headers = + new HttpResponseHeaders(original_response_headers->raw_headers()); new_response_headers->RemoveHeader("Date"); new_response_headers->AddHeader("Date: " + fixed_date_); @@ -3124,7 +3124,7 @@ TEST_F(URLRequestTestHTTP, NetworkDelegateRedirectRequestOnHeadersReceived) { EXPECT_TRUE( network_delegate.last_observed_proxy().Equals( test_server_.host_port_pair())); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(redirect_url, r.url()); EXPECT_EQ(original_url, r.original_url()); EXPECT_EQ(2U, r.url_chain().size()); @@ -4123,7 +4123,7 @@ class AsyncLoggingUrlRequestDelegate : public TestDelegate { // URLRequest::Delegate implementation: void virtual OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE { *defer_redirect = true; AsyncDelegateLogger::Run( @@ -4133,7 +4133,7 @@ class AsyncLoggingUrlRequestDelegate : public TestDelegate { LOAD_STATE_WAITING_FOR_DELEGATE, base::Bind( &AsyncLoggingUrlRequestDelegate::OnReceivedRedirectLoggingComplete, - base::Unretained(this), request, new_url)); + base::Unretained(this), request, redirect_info)); } virtual void OnResponseStarted(URLRequest* request) OVERRIDE { @@ -4161,9 +4161,9 @@ class AsyncLoggingUrlRequestDelegate : public TestDelegate { private: void OnReceivedRedirectLoggingComplete(URLRequest* request, - const GURL& new_url) { + const RedirectInfo& redirect_info) { bool defer_redirect = false; - TestDelegate::OnReceivedRedirect(request, new_url, &defer_redirect); + TestDelegate::OnReceivedRedirect(request, redirect_info, &defer_redirect); // FollowDeferredRedirect should not be called after cancellation. if (cancel_stage_ == CANCEL_ON_RECEIVED_REDIRECT) return; @@ -4621,10 +4621,10 @@ const char kExtraHeader[] = "Allow-Snafu"; const char kExtraValue[] = "fubar"; class RedirectWithAdditionalHeadersDelegate : public TestDelegate { - virtual void OnReceivedRedirect(net::URLRequest* request, - const GURL& new_url, + virtual void OnReceivedRedirect(URLRequest* request, + const RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE { - TestDelegate::OnReceivedRedirect(request, new_url, defer_redirect); + TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); request->SetExtraRequestHeaderByName(kExtraHeader, kExtraValue, false); } }; @@ -4657,10 +4657,10 @@ namespace { const char kExtraHeaderToRemove[] = "To-Be-Removed"; class RedirectWithHeaderRemovalDelegate : public TestDelegate { - virtual void OnReceivedRedirect(net::URLRequest* request, - const GURL& new_url, - bool* defer_redirect) OVERRIDE { - TestDelegate::OnReceivedRedirect(request, new_url, defer_redirect); + virtual void OnReceivedRedirect(URLRequest* request, + const RedirectInfo& redirect_info, + bool* defer_redirect) OVERRIDE { + TestDelegate::OnReceivedRedirect(request, redirect_info, defer_redirect); request->RemoveRequestHeaderByName(kExtraHeaderToRemove); } }; @@ -5388,7 +5388,7 @@ TEST_F(URLRequestTestHTTP, UnsafeRedirectToWhitelistedUnsafeURL) { EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); EXPECT_EQ(2U, r.url_chain().size()); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(unsafe_url, r.url()); EXPECT_EQ("this-is-considered-an-unsafe-url", d.data_received()); } @@ -5441,7 +5441,7 @@ TEST_F(URLRequestTestHTTP, UnsafeRedirectWithDifferentReferenceFragment) { EXPECT_EQ(2U, r.url_chain().size()); EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(original_url, r.original_url()); EXPECT_EQ(expected_url, r.url()); } @@ -5469,7 +5469,7 @@ TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragmentAndUnrelatedUnsafeUrl) { EXPECT_EQ(2U, r.url_chain().size()); EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(original_url, r.original_url()); EXPECT_EQ(expected_redirect_url, r.url()); } @@ -5496,7 +5496,7 @@ TEST_F(URLRequestTestHTTP, RedirectWithReferenceFragment) { EXPECT_EQ(2U, r.url_chain().size()); EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(original_url, r.original_url()); EXPECT_EQ(redirect_url, r.url()); } @@ -5522,7 +5522,7 @@ TEST_F(URLRequestTestHTTP, RedirectJobWithReferenceFragment) { base::RunLoop().Run(); EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(original_url, r.original_url()); EXPECT_EQ(redirect_url, r.url()); } @@ -6151,12 +6151,57 @@ TEST_F(URLRequestTestHTTP, Redirect302PreserveReferenceFragment) { EXPECT_EQ(2U, r.url_chain().size()); EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); - EXPECT_EQ(net::OK, r.status().error()); + EXPECT_EQ(OK, r.status().error()); EXPECT_EQ(original_url, r.original_url()); EXPECT_EQ(expected_url, r.url()); } } +TEST_F(URLRequestTestHTTP, RedirectPreserveFirstPartyURL) { + ASSERT_TRUE(test_server_.Start()); + + GURL url(test_server_.GetURL("files/redirect302-to-echo")); + GURL first_party_url("http://example.com"); + + TestDelegate d; + { + URLRequest r(url, DEFAULT_PRIORITY, &d, &default_context_); + r.set_first_party_for_cookies(first_party_url); + + r.Start(); + base::RunLoop().Run(); + + EXPECT_EQ(2U, r.url_chain().size()); + EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); + EXPECT_EQ(OK, r.status().error()); + EXPECT_EQ(first_party_url, r.first_party_for_cookies()); + } +} + +TEST_F(URLRequestTestHTTP, RedirectUpdateFirstPartyURL) { + ASSERT_TRUE(test_server_.Start()); + + GURL url(test_server_.GetURL("files/redirect302-to-echo")); + GURL original_first_party_url("http://example.com"); + GURL expected_first_party_url(test_server_.GetURL("echo")); + + TestDelegate d; + { + URLRequest r(url, DEFAULT_PRIORITY, &d, &default_context_); + r.set_first_party_for_cookies(original_first_party_url); + r.set_first_party_url_policy( + URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT); + + r.Start(); + base::RunLoop().Run(); + + EXPECT_EQ(2U, r.url_chain().size()); + EXPECT_EQ(URLRequestStatus::SUCCESS, r.status().status()); + EXPECT_EQ(OK, r.status().error()); + EXPECT_EQ(expected_first_party_url, r.first_party_for_cookies()); + } +} + TEST_F(URLRequestTestHTTP, InterceptPost302RedirectGet) { ASSERT_TRUE(test_server_.Start()); @@ -6970,9 +7015,9 @@ TEST_F(HTTPSRequestTest, SSLSessionCacheShardTest) { params.http_server_properties = default_context_.http_server_properties(); params.ssl_session_cache_shard = "alternate"; - scoped_ptr cache(new net::HttpCache( - new net::HttpNetworkSession(params), - net::HttpCache::DefaultBackend::InMemory(0))); + scoped_ptr cache(new HttpCache( + new HttpNetworkSession(params), + HttpCache::DefaultBackend::InMemory(0))); default_context_.set_http_transaction_factory(cache.get()); @@ -7178,7 +7223,7 @@ TEST_F(HTTPSFallbackTest, SSLv3NoFallbackReset) { class HTTPSSessionTest : public testing::Test { public: HTTPSSessionTest() : default_context_(true) { - cert_verifier_.set_default_result(net::OK); + cert_verifier_.set_default_result(OK); default_context_.set_network_delegate(&default_network_delegate_); default_context_.set_cert_verifier(&cert_verifier_); @@ -7206,7 +7251,7 @@ TEST_F(HTTPSSessionTest, DontResumeSessionsForInvalidCertificates) { SSLClientSocket::ClearSessionCache(); // Simulate the certificate being expired and attempt a connection. - cert_verifier_.set_default_result(net::ERR_CERT_DATE_INVALID); + cert_verifier_.set_default_result(ERR_CERT_DATE_INVALID); { TestDelegate d; URLRequest r(test_server.GetURL("ssl-session-cache"), @@ -7227,7 +7272,7 @@ TEST_F(HTTPSSessionTest, DontResumeSessionsForInvalidCertificates) { // Now change the certificate to be acceptable (so that the response is // loaded), and ensure that no session id is presented to the peer. - cert_verifier_.set_default_result(net::OK); + cert_verifier_.set_default_result(OK); { TestDelegate d; URLRequest r(test_server.GetURL("ssl-session-cache"), @@ -7334,7 +7379,7 @@ class HTTPSOCSPTest : public HTTPSRequestTest { SetupContext(&context_); context_.Init(); - scoped_refptr root_cert = + scoped_refptr root_cert = ImportCertFromFile(GetTestCertsDirectory(), "ocsp-test-root.pem"); CHECK_NE(static_cast(NULL), root_cert); test_root_.reset(new ScopedTestRoot(root_cert.get())); diff --git a/net/websockets/websocket_inflater.h b/net/websockets/websocket_inflater.h index e57317f53343b..fdfa7624fef42 100644 --- a/net/websockets/websocket_inflater.h +++ b/net/websockets/websocket_inflater.h @@ -66,7 +66,7 @@ class NET_EXPORT_PRIVATE WebSocketInflater { private: // Ring buffer with fixed capacity. - class OutputBuffer { + class NET_EXPORT_PRIVATE OutputBuffer { public: explicit OutputBuffer(size_t capacity); ~OutputBuffer(); diff --git a/net/websockets/websocket_stream.cc b/net/websockets/websocket_stream.cc index b6a235991979d..c9513d758b663 100644 --- a/net/websockets/websocket_stream.cc +++ b/net/websockets/websocket_stream.cc @@ -12,6 +12,7 @@ #include "net/http/http_request_headers.h" #include "net/http/http_response_headers.h" #include "net/http/http_status_code.h" +#include "net/url_request/redirect_info.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" #include "net/websockets/websocket_errors.h" @@ -46,7 +47,7 @@ class Delegate : public URLRequest::Delegate { // Implementation of URLRequest::Delegate methods. virtual void OnReceivedRedirect(URLRequest* request, - const GURL& new_url, + const RedirectInfo& redirect_info, bool* defer_redirect) OVERRIDE { // HTTP status codes returned by HttpStreamParser are filtered by // WebSocketBasicHandshakeStream, and only 101, 401 and 407 are permitted diff --git a/pdf/instance.cc b/pdf/instance.cc index d779089f0067f..cda0c588a665b 100644 --- a/pdf/instance.cc +++ b/pdf/instance.cc @@ -307,6 +307,10 @@ Instance::Instance(PP_Instance instance) } Instance::~Instance() { + if (timer_pending_) { + timer_factory_.CancelAll(); + timer_pending_ = false; + } // The engine may try to access this instance during its destruction. // Make sure this happens early while the instance is still intact. engine_.reset(); @@ -1495,6 +1499,12 @@ void Instance::DocumentLoadComplete(int page_count) { DCHECK(document_load_state_ == LOAD_STATE_LOADING); document_load_state_ = LOAD_STATE_COMPLETE; UserMetricsRecordAction("PDF.LoadSuccess"); + + if (did_call_start_loading_) { + pp::PDF::DidStopLoading(this); + did_call_start_loading_ = false; + } + if (on_load_callback_.is_string()) ExecuteScript(on_load_callback_); // Note: If we are in print preview mode on_load_callback_ might call @@ -1511,11 +1521,6 @@ void Instance::DocumentLoadComplete(int page_count) { if (!pp::PDF::IsAvailable()) return; - if (did_call_start_loading_) { - pp::PDF::DidStopLoading(this); - did_call_start_loading_ = false; - } - int content_restrictions = CONTENT_RESTRICTION_CUT | CONTENT_RESTRICTION_PASTE; if (!engine_->HasPermission(PDFEngine::PERMISSION_COPY)) diff --git a/pdf/pdfium/pdfium_engine.cc b/pdf/pdfium/pdfium_engine.cc index e8c164b03ac0d..606180a4ac989 100644 --- a/pdf/pdfium/pdfium_engine.cc +++ b/pdf/pdfium/pdfium_engine.cc @@ -1964,6 +1964,8 @@ std::string PDFiumEngine::GetPageAsJSON(int index) { if (index < 0 || static_cast(index) > pages_.size() - 1) return "{}"; + // TODO(thestig) Remove after debugging http://crbug.com/402035 + CHECK(pages_[index]); scoped_ptr node( pages_[index]->GetAccessibleContentAsValue(current_rotation_)); std::string page_json; @@ -2021,6 +2023,8 @@ void PDFiumEngine::AppendBlankPages(int num_pages) { page_rect.height() * kPointsPerInch / kPixelsPerInch; FPDFPage_New(doc_, i, width_in_points, height_in_points); pages_.push_back(new PDFiumPage(this, i, page_rect, true)); + // TODO(thestig) Remove after debugging http://crbug.com/402035 + CHECK(pages_.back()); } CalculateVisiblePages(); @@ -2188,6 +2192,8 @@ void PDFiumEngine::LoadPageInfo(bool reload) { pages_[i]->set_rect(page_rect); } else { pages_.push_back(new PDFiumPage(this, i, page_rect, doc_complete)); + // TODO(thestig) Remove after debugging http://crbug.com/402035 + CHECK(pages_.back()); } } diff --git a/ppapi/PRESUBMIT.py b/ppapi/PRESUBMIT.py index ddbd31475c66b..78457c43c5e95 100644 --- a/ppapi/PRESUBMIT.py +++ b/ppapi/PRESUBMIT.py @@ -147,7 +147,9 @@ def CheckUpdatedNaClSDK(input_api, output_api): return RunCmdAndCheck(cmd, 'PPAPI Interface modified without updating NaCl SDK.\n' '(note that some dev interfaces should not be added ' - 'the NaCl SDK; when in doubt, ask a ppapi OWNER.)', + 'the NaCl SDK; when in doubt, ask a ppapi OWNER.\n' + 'To ignore a file, add it to IGNORED_FILES in ' + 'native_client_sdk/src/build_tools/verify_ppapi.py)', output_api, warning=True) diff --git a/ppapi/api/dev/pp_video_dev.idl b/ppapi/api/dev/pp_video_dev.idl index 97bd4ce1051cc..1c70342effcf9 100644 --- a/ppapi/api/dev/pp_video_dev.idl +++ b/ppapi/api/dev/pp_video_dev.idl @@ -31,7 +31,7 @@ enum PP_VideoDecoder_Profile { PP_VIDEODECODER_H264PROFILE_SCALABLEHIGH = 9, PP_VIDEODECODER_H264PROFILE_STEREOHIGH = 10, PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH = 11, - PP_VIDEODECODER_VP8PROFILE_MAIN = 12 + PP_VIDEODECODER_VP8PROFILE_ANY = 12 }; /** diff --git a/ppapi/api/pp_codecs.idl b/ppapi/api/pp_codecs.idl index 2558671c5ac9c..423db9f7f43ea 100644 --- a/ppapi/api/pp_codecs.idl +++ b/ppapi/api/pp_codecs.idl @@ -18,9 +18,9 @@ enum PP_VideoProfile { PP_VIDEOPROFILE_H264SCALABLEHIGH = 8, PP_VIDEOPROFILE_H264STEREOHIGH = 9, PP_VIDEOPROFILE_H264MULTIVIEWHIGH = 10, - PP_VIDEOPROFILE_VP8MAIN = 11, - PP_VIDEOPROFILE_VP9MAIN = 12, - PP_VIDEOPROFILE_MAX = PP_VIDEOPROFILE_VP9MAIN + PP_VIDEOPROFILE_VP8_ANY = 11, + PP_VIDEOPROFILE_VP9_ANY = 12, + PP_VIDEOPROFILE_MAX = PP_VIDEOPROFILE_VP9_ANY }; /** diff --git a/ppapi/api/ppb_compositor_layer.idl b/ppapi/api/ppb_compositor_layer.idl index a457ccc2a6352..ed0967d4fa42a 100644 --- a/ppapi/api/ppb_compositor_layer.idl +++ b/ppapi/api/ppb_compositor_layer.idl @@ -6,7 +6,8 @@ [generate_thunk] label Chrome { - [channel=dev] M37 = 0.1 + [channel=dev] M37 = 0.1, + [channel=dev] M38 = 0.2 }; /** @@ -107,6 +108,34 @@ interface PPB_CompositorLayer { [in] PP_Size size, [in] PP_CompletionCallback cc); + /** + * Sets the texture of a texture layer. If the layer is uninitialized, + * it will initialize the layer first, and then set its texture. + * The source rect will be set to ((0, 0), (1, 1)). If the layer has been + * initialized to another kind of layer, the layer will not be changed, + * and PP_ERROR_BADARGUMENT will be returned. + * + * param[in] layer A PP_Resource corresponding to a compositor + * layer resource. + * param[in] context A PP_Resource corresponding to a graphics + * 3d resource which owns the GL texture. + * param[in] target GL texture target (GL_TEXTURE_2D, etc). + * param[in] texture A GL texture object id. + * param[in] size A PP_Size for the size of the layer before + * transform. + * param[in] cc A PP_CompletionCallback to be called when + * the texture is released by Chromium compositor. + * + * @return An int32_t containing a result code from pp_errors.h. + */ + [version = 0.2] + int32_t SetTexture([in] PP_Resource layer, + [in] PP_Resource context, + [in] uint32_t target, + [in] uint32_t texture, + [in] PP_Size size, + [in] PP_CompletionCallback cc); + /** * Sets the image of an image layer. If the layer is uninitialized, * it will initialize the layer first, and then set its image. diff --git a/ppapi/api/private/ppb_testing_private.idl b/ppapi/api/private/ppb_testing_private.idl index 98ddfc84bba39..0ede2de2ea089 100644 --- a/ppapi/api/private/ppb_testing_private.idl +++ b/ppapi/api/private/ppb_testing_private.idl @@ -131,4 +131,9 @@ interface PPB_Testing_Private { */ void SetMinimumArrayBufferSizeForShmem([in] PP_Instance instance, [in] uint32_t threshold); + + /** + * Run the V8 garbage collector for tests. + */ + void RunV8GC([in] PP_Instance instance); }; diff --git a/ppapi/c/dev/pp_video_dev.h b/ppapi/c/dev/pp_video_dev.h index eeb149819c990..75ee21a219dd4 100644 --- a/ppapi/c/dev/pp_video_dev.h +++ b/ppapi/c/dev/pp_video_dev.h @@ -45,7 +45,7 @@ typedef enum { PP_VIDEODECODER_H264PROFILE_SCALABLEHIGH = 9, PP_VIDEODECODER_H264PROFILE_STEREOHIGH = 10, PP_VIDEODECODER_H264PROFILE_MULTIVIEWHIGH = 11, - PP_VIDEODECODER_VP8PROFILE_MAIN = 12 + PP_VIDEODECODER_VP8PROFILE_ANY = 12 } PP_VideoDecoder_Profile; PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_VideoDecoder_Profile, 4); /** diff --git a/ppapi/c/pp_codecs.h b/ppapi/c/pp_codecs.h index 5c6af36a4634e..5e5bce7f74408 100644 --- a/ppapi/c/pp_codecs.h +++ b/ppapi/c/pp_codecs.h @@ -34,9 +34,9 @@ typedef enum { PP_VIDEOPROFILE_H264SCALABLEHIGH = 8, PP_VIDEOPROFILE_H264STEREOHIGH = 9, PP_VIDEOPROFILE_H264MULTIVIEWHIGH = 10, - PP_VIDEOPROFILE_VP8MAIN = 11, - PP_VIDEOPROFILE_VP9MAIN = 12, - PP_VIDEOPROFILE_MAX = PP_VIDEOPROFILE_VP9MAIN + PP_VIDEOPROFILE_VP8_ANY = 11, + PP_VIDEOPROFILE_VP9_ANY = 12, + PP_VIDEOPROFILE_MAX = PP_VIDEOPROFILE_VP9_ANY } PP_VideoProfile; /** * @} diff --git a/ppapi/c/pp_macros.h b/ppapi/c/pp_macros.h index 31d7297fd4334..ea355fa292e43 100644 --- a/ppapi/c/pp_macros.h +++ b/ppapi/c/pp_macros.h @@ -3,13 +3,13 @@ * found in the LICENSE file. */ -/* From pp_macros.idl modified Tue May 20 17:13:23 2014. */ +/* From pp_macros.idl modified Wed Jun 11 11:38:24 2014. */ #ifndef PPAPI_C_PP_MACROS_H_ #define PPAPI_C_PP_MACROS_H_ -#define PPAPI_RELEASE 37 +#define PPAPI_RELEASE 38 /** * @file diff --git a/ppapi/c/ppb_compositor_layer.h b/ppapi/c/ppb_compositor_layer.h index 8fcb1ab60f9d8..25d2ebc30e75a 100644 --- a/ppapi/c/ppb_compositor_layer.h +++ b/ppapi/c/ppb_compositor_layer.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From ppb_compositor_layer.idl modified Wed Jun 4 11:17:54 2014. */ +/* From ppb_compositor_layer.idl modified Thu Aug 14 18:06:33 2014. */ #ifndef PPAPI_C_PPB_COMPOSITOR_LAYER_H_ #define PPAPI_C_PPB_COMPOSITOR_LAYER_H_ @@ -18,6 +18,7 @@ #include "ppapi/c/pp_stdint.h" #define PPB_COMPOSITORLAYER_INTERFACE_0_1 "PPB_CompositorLayer;0.1" /* dev */ +#define PPB_COMPOSITORLAYER_INTERFACE_0_2 "PPB_CompositorLayer;0.2" /* dev */ /** * @file */ @@ -65,7 +66,7 @@ typedef enum { * Defines the PPB_CompositorLayer interface. It is used by * PPB_Compositor. */ -struct PPB_CompositorLayer_0_1 { /* dev */ +struct PPB_CompositorLayer_0_2 { /* dev */ /** * Determines if a resource is a compositor layer resource. * @@ -114,6 +115,7 @@ struct PPB_CompositorLayer_0_1 { /* dev */ * layer resource. * param[in] context A PP_Resource corresponding to a graphics * 3d resource which owns the GL texture. + * param[in] target GL texture target (GL_TEXTURE_2D, etc). * param[in] texture A GL texture object id. * param[in] size A PP_Size for the size of the layer before * transform. @@ -124,6 +126,7 @@ struct PPB_CompositorLayer_0_1 { /* dev */ */ int32_t (*SetTexture)(PP_Resource layer, PP_Resource context, + uint32_t target, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback cc); @@ -228,6 +231,31 @@ struct PPB_CompositorLayer_0_1 { /* dev */ */ int32_t (*SetPremultipliedAlpha)(PP_Resource layer, PP_Bool premult); }; + +struct PPB_CompositorLayer_0_1 { /* dev */ + PP_Bool (*IsCompositorLayer)(PP_Resource resource); + int32_t (*SetColor)(PP_Resource layer, + float red, + float green, + float blue, + float alpha, + const struct PP_Size* size); + int32_t (*SetTexture)(PP_Resource layer, + PP_Resource context, + uint32_t texture, + const struct PP_Size* size, + struct PP_CompletionCallback cc); + int32_t (*SetImage)(PP_Resource layer, + PP_Resource image_data, + const struct PP_Size* size, + struct PP_CompletionCallback cc); + int32_t (*SetClipRect)(PP_Resource layer, const struct PP_Rect* rect); + int32_t (*SetTransform)(PP_Resource layer, const float matrix[16]); + int32_t (*SetOpacity)(PP_Resource layer, float opacity); + int32_t (*SetBlendMode)(PP_Resource layer, PP_BlendMode mode); + int32_t (*SetSourceRect)(PP_Resource layer, const struct PP_FloatRect* rect); + int32_t (*SetPremultipliedAlpha)(PP_Resource layer, PP_Bool premult); +}; /** * @} */ diff --git a/ppapi/c/private/ppb_testing_private.h b/ppapi/c/private/ppb_testing_private.h index 889807dfc9cd3..3d35ec7ea58e1 100644 --- a/ppapi/c/private/ppb_testing_private.h +++ b/ppapi/c/private/ppb_testing_private.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From private/ppb_testing_private.idl modified Mon Nov 18 14:42:33 2013. */ +/* From private/ppb_testing_private.idl modified Mon Jul 28 15:12:12 2014. */ #ifndef PPAPI_C_PRIVATE_PPB_TESTING_PRIVATE_H_ #define PPAPI_C_PRIVATE_PPB_TESTING_PRIVATE_H_ @@ -140,6 +140,10 @@ struct PPB_Testing_Private_1_0 { */ void (*SetMinimumArrayBufferSizeForShmem)(PP_Instance instance, uint32_t threshold); + /** + * Run the V8 garbage collector for tests. + */ + void (*RunV8GC)(PP_Instance instance); }; typedef struct PPB_Testing_Private_1_0 PPB_Testing_Private; diff --git a/ppapi/cpp/compositor_layer.cc b/ppapi/cpp/compositor_layer.cc index cbe823a3ac017..d15a6731ba5b1 100644 --- a/ppapi/cpp/compositor_layer.cc +++ b/ppapi/cpp/compositor_layer.cc @@ -17,6 +17,10 @@ template <> const char* interface_name() { return PPB_COMPOSITORLAYER_INTERFACE_0_1; } +template <> const char* interface_name() { + return PPB_COMPOSITORLAYER_INTERFACE_0_2; +} + } // namespace CompositorLayer::CompositorLayer() { @@ -43,6 +47,10 @@ int32_t CompositorLayer::SetColor(float red, float blue, float alpha, const Size& size) { + if (has_interface()) { + return get_interface()->SetColor( + pp_resource(), red, green, blue, alpha, &size.pp_size()); + } if (has_interface()) { return get_interface()->SetColor( pp_resource(), red, green, blue, alpha, &size.pp_size()); @@ -51,10 +59,18 @@ int32_t CompositorLayer::SetColor(float red, } int32_t CompositorLayer::SetTexture(const Graphics3D& context, + uint32_t target, uint32_t texture, const Size& size, const CompletionCallback& cc) { + if (has_interface()) { + return get_interface()->SetTexture( + pp_resource(), context.pp_resource(), target, texture, &size.pp_size(), + cc.pp_completion_callback()); + } if (has_interface()) { + if (target != 0x0DE1) // 0x0DE1 GL_TEXTURE_2D + return cc.MayForce(PP_ERROR_NOTSUPPORTED); return get_interface()->SetTexture( pp_resource(), context.pp_resource(), texture, &size.pp_size(), cc.pp_completion_callback()); @@ -64,6 +80,11 @@ int32_t CompositorLayer::SetTexture(const Graphics3D& context, int32_t CompositorLayer::SetImage(const ImageData& image, const CompletionCallback& cc) { + if (has_interface()) { + return get_interface()->SetImage( + pp_resource(), image.pp_resource(), NULL, + cc.pp_completion_callback()); + } if (has_interface()) { return get_interface()->SetImage( pp_resource(), image.pp_resource(), NULL, @@ -75,6 +96,11 @@ int32_t CompositorLayer::SetImage(const ImageData& image, int32_t CompositorLayer::SetImage(const ImageData& image, const Size& size, const CompletionCallback& cc) { + if (has_interface()) { + return get_interface()->SetImage( + pp_resource(), image.pp_resource(), &size.pp_size(), + cc.pp_completion_callback()); + } if (has_interface()) { return get_interface()->SetImage( pp_resource(), image.pp_resource(), &size.pp_size(), @@ -84,6 +110,10 @@ int32_t CompositorLayer::SetImage(const ImageData& image, } int32_t CompositorLayer::SetClipRect(const Rect& rect) { + if (has_interface()) { + return get_interface()->SetClipRect( + pp_resource(), &rect.pp_rect()); + } if (has_interface()) { return get_interface()->SetClipRect( pp_resource(), &rect.pp_rect()); @@ -92,6 +122,10 @@ int32_t CompositorLayer::SetClipRect(const Rect& rect) { } int32_t CompositorLayer::SetTransform(const float matrix[16]) { + if (has_interface()) { + return get_interface()->SetTransform( + pp_resource(), matrix); + } if (has_interface()) { return get_interface()->SetTransform( pp_resource(), matrix); @@ -100,6 +134,10 @@ int32_t CompositorLayer::SetTransform(const float matrix[16]) { } int32_t CompositorLayer::SetOpacity(float opacity) { + if (has_interface()) { + return get_interface()->SetOpacity( + pp_resource(), opacity); + } if (has_interface()) { return get_interface()->SetOpacity( pp_resource(), opacity); @@ -108,6 +146,10 @@ int32_t CompositorLayer::SetOpacity(float opacity) { } int32_t CompositorLayer::SetBlendMode(PP_BlendMode mode) { + if (has_interface()) { + return get_interface()->SetBlendMode( + pp_resource(), mode); + } if (has_interface()) { return get_interface()->SetBlendMode( pp_resource(), mode); @@ -116,6 +158,10 @@ int32_t CompositorLayer::SetBlendMode(PP_BlendMode mode) { } int32_t CompositorLayer::SetSourceRect(const FloatRect& rect) { + if (has_interface()) { + return get_interface()->SetSourceRect( + pp_resource(), &rect.pp_float_rect()); + } if (has_interface()) { return get_interface()->SetSourceRect( pp_resource(), &rect.pp_float_rect()); @@ -124,6 +170,10 @@ int32_t CompositorLayer::SetSourceRect(const FloatRect& rect) { } int32_t CompositorLayer::SetPremultipliedAlpha(bool premult) { + if (has_interface()) { + return get_interface()->SetPremultipliedAlpha( + pp_resource(), PP_FromBool(premult)); + } if (has_interface()) { return get_interface()->SetPremultipliedAlpha( pp_resource(), PP_FromBool(premult)); @@ -132,6 +182,10 @@ int32_t CompositorLayer::SetPremultipliedAlpha(bool premult) { } bool CompositorLayer::IsCompositorLayer(const Resource& resource) { + if (has_interface()) { + return PP_ToBool(get_interface()-> + IsCompositorLayer(resource.pp_resource())); + } if (has_interface()) { return PP_ToBool(get_interface()-> IsCompositorLayer(resource.pp_resource())); diff --git a/ppapi/cpp/compositor_layer.h b/ppapi/cpp/compositor_layer.h index f431bd8d5ae81..9e65e87964d6e 100644 --- a/ppapi/cpp/compositor_layer.h +++ b/ppapi/cpp/compositor_layer.h @@ -70,6 +70,7 @@ class CompositorLayer : public Resource { /// /// param[in] context A Graphics3D corresponding to a graphics 3d /// resource which owns the GL texture. + /// param[in] target GL texture target (GL_TEXTURE_2D, etc). /// param[in] texture A GL texture object id. /// param[in] size A Size for the size of the layer before /// transform. @@ -78,6 +79,7 @@ class CompositorLayer : public Resource { /// /// @return An int32_t containing a result code from pp_errors.h. int32_t SetTexture(const Graphics3D& context, + uint32_t target, uint32_t texture, const Size& size, const CompletionCallback& cc); diff --git a/ppapi/examples/compositor/compositor.cc b/ppapi/examples/compositor/compositor.cc index e7e3f1ffb0cc4..d13e48a7c039b 100644 --- a/ppapi/examples/compositor/compositor.cc +++ b/ppapi/examples/compositor/compositor.cc @@ -351,7 +351,9 @@ void DemoInstance::PrepareLayers(int32_t frame) { cube_->Draw(); rv = stable_texture_layer_.SetTexture( *context_, - texture, pp::Size(600, 600), + GL_TEXTURE_2D, + texture, + pp::Size(600, 600), callback_factory_.NewCallback(&DemoInstance::OnTextureReleased, texture)); assert(rv == PP_OK_COMPLETIONPENDING); @@ -397,7 +399,11 @@ void DemoInstance::PrepareLayers(int32_t frame) { GLuint texture = PrepareFramebuffer(); cube_->UpdateForTimeDelta(0.02f); cube_->Draw(); - rv = texture_layer_.SetTexture(*context_, texture, pp::Size(400, 400), + rv = texture_layer_.SetTexture( + *context_, + GL_TEXTURE_2D, + texture, + pp::Size(400, 400), callback_factory_.NewCallback(&DemoInstance::OnTextureReleased, texture)); assert(rv == PP_OK_COMPLETIONPENDING); diff --git a/ppapi/examples/video_decode/video_decode.cc b/ppapi/examples/video_decode/video_decode.cc index 56748e037d6ba..ab2cead44be74 100644 --- a/ppapi/examples/video_decode/video_decode.cc +++ b/ppapi/examples/video_decode/video_decode.cc @@ -247,7 +247,7 @@ Decoder::Decoder(MyInstance* instance, pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE)); #if defined USE_VP8_TESTDATA_INSTEAD_OF_H264 - const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8MAIN; + const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_VP8_ANY; #else const PP_VideoProfile kBitstreamProfile = PP_VIDEOPROFILE_H264MAIN; #endif diff --git a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc index 578153073126d..36499ae4aa511 100644 --- a/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc +++ b/ppapi/native_client/src/trusted/plugin/pnacl_coordinator.cc @@ -275,29 +275,6 @@ void PnaclCoordinator::NexeReadDidOpen(int32_t pp_error) { } void PnaclCoordinator::OpenBitcodeStream() { - // The component updater's resource throttles + OnDemand update/install - // should block the URL request until the compiler is present. Now we - // can load the resources (e.g. llc and ld nexes). - resources_.reset(new PnaclResources(plugin_)); - CHECK(resources_ != NULL); - - // The first step of loading resources: read the resource info file. - if (!resources_->ReadResourceInfo()) { - ExitWithError(); - return; - } - - // Second step of loading resources: call StartLoad to load pnacl-llc - // and pnacl-ld, based on the filenames found in the resource info file. - if (!resources_->StartLoad()) { - ReportNonPpapiError( - PP_NACL_ERROR_PNACL_RESOURCE_FETCH, - nacl::string("The Portable Native Client (pnacl) component is not " - "installed. Please consult chrome://components for more " - "information.")); - return; - } - // Even though we haven't started downloading, create the translation // thread object immediately. This ensures that any pieces of the file // that get downloaded before the compilation thread is accepting @@ -333,6 +310,32 @@ void PnaclCoordinator::BitcodeStreamCacheHit(PP_FileHandle handle) { void PnaclCoordinator::BitcodeStreamCacheMiss(int64_t expected_pexe_size, PP_FileHandle nexe_handle) { + // IMPORTANT: Make sure that PnaclResources::StartLoad() is only + // called after you receive a response to a request for a .pexe file. + // + // The component updater's resource throttles + OnDemand update/install + // should block the URL request until the compiler is present. Now we + // can load the resources (e.g. llc and ld nexes). + resources_.reset(new PnaclResources(plugin_)); + CHECK(resources_ != NULL); + + // The first step of loading resources: read the resource info file. + if (!resources_->ReadResourceInfo()) { + ExitWithError(); + return; + } + + // Second step of loading resources: call StartLoad to load pnacl-llc + // and pnacl-ld, based on the filenames found in the resource info file. + if (!resources_->StartLoad()) { + ReportNonPpapiError( + PP_NACL_ERROR_PNACL_RESOURCE_FETCH, + nacl::string("The Portable Native Client (pnacl) component is not " + "installed. Please consult chrome://components for more " + "information.")); + return; + } + expected_pexe_size_ = expected_pexe_size; for (int i = 0; i < split_module_count_; i++) { diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c index c5e36d42fb823..245b3bc18fed2 100644 --- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c +++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c @@ -97,6 +97,7 @@ static int mystrcmp(const char* s1, const char *s2) { static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Compositor_0_1; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_1; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_2; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Console_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Core_1_0; static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileIO_1_0; @@ -296,6 +297,60 @@ static int32_t Pnacl_M37_PPB_CompositorLayer_SetPremultipliedAlpha(PP_Resource l /* End wrapper methods for PPB_CompositorLayer_0_1 */ +/* Begin wrapper methods for PPB_CompositorLayer_0_2 */ + +static PP_Bool Pnacl_M38_PPB_CompositorLayer_IsCompositorLayer(PP_Resource resource) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->IsCompositorLayer(resource); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetColor(PP_Resource layer, float red, float green, float blue, float alpha, const struct PP_Size* size) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetColor(layer, red, green, blue, alpha, size); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetTexture(PP_Resource layer, PP_Resource context, uint32_t target, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback* cc) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetTexture(layer, context, target, texture, size, *cc); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetImage(PP_Resource layer, PP_Resource image_data, const struct PP_Size* size, struct PP_CompletionCallback* cc) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetImage(layer, image_data, size, *cc); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetClipRect(PP_Resource layer, const struct PP_Rect* rect) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetClipRect(layer, rect); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetTransform(PP_Resource layer, const float matrix[16]) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetTransform(layer, matrix); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetOpacity(PP_Resource layer, float opacity) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetOpacity(layer, opacity); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetBlendMode(PP_Resource layer, PP_BlendMode mode) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetBlendMode(layer, mode); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetSourceRect(PP_Resource layer, const struct PP_FloatRect* rect) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetSourceRect(layer, rect); +} + +static int32_t Pnacl_M38_PPB_CompositorLayer_SetPremultipliedAlpha(PP_Resource layer, PP_Bool premult) { + const struct PPB_CompositorLayer_0_2 *iface = Pnacl_WrapperInfo_PPB_CompositorLayer_0_2.real_iface; + return iface->SetPremultipliedAlpha(layer, premult); +} + +/* End wrapper methods for PPB_CompositorLayer_0_2 */ + /* Begin wrapper methods for PPB_Console_1_0 */ static void Pnacl_M25_PPB_Console_Log(PP_Instance instance, PP_LogLevel level, struct PP_Var* value) { @@ -3989,6 +4044,11 @@ static void Pnacl_M33_PPB_Testing_Private_SetMinimumArrayBufferSizeForShmem(PP_I iface->SetMinimumArrayBufferSizeForShmem(instance, threshold); } +static void Pnacl_M33_PPB_Testing_Private_RunV8GC(PP_Instance instance) { + const struct PPB_Testing_Private_1_0 *iface = Pnacl_WrapperInfo_PPB_Testing_Private_1_0.real_iface; + iface->RunV8GC(instance); +} + /* End wrapper methods for PPB_Testing_Private_1_0 */ /* Begin wrapper methods for PPB_UDPSocket_Private_0_2 */ @@ -4364,6 +4424,19 @@ static const struct PPB_CompositorLayer_0_1 Pnacl_Wrappers_PPB_CompositorLayer_0 .SetPremultipliedAlpha = (int32_t (*)(PP_Resource layer, PP_Bool premult))&Pnacl_M37_PPB_CompositorLayer_SetPremultipliedAlpha }; +static const struct PPB_CompositorLayer_0_2 Pnacl_Wrappers_PPB_CompositorLayer_0_2 = { + .IsCompositorLayer = (PP_Bool (*)(PP_Resource resource))&Pnacl_M38_PPB_CompositorLayer_IsCompositorLayer, + .SetColor = (int32_t (*)(PP_Resource layer, float red, float green, float blue, float alpha, const struct PP_Size* size))&Pnacl_M38_PPB_CompositorLayer_SetColor, + .SetTexture = (int32_t (*)(PP_Resource layer, PP_Resource context, uint32_t target, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback cc))&Pnacl_M38_PPB_CompositorLayer_SetTexture, + .SetImage = (int32_t (*)(PP_Resource layer, PP_Resource image_data, const struct PP_Size* size, struct PP_CompletionCallback cc))&Pnacl_M38_PPB_CompositorLayer_SetImage, + .SetClipRect = (int32_t (*)(PP_Resource layer, const struct PP_Rect* rect))&Pnacl_M38_PPB_CompositorLayer_SetClipRect, + .SetTransform = (int32_t (*)(PP_Resource layer, const float matrix[16]))&Pnacl_M38_PPB_CompositorLayer_SetTransform, + .SetOpacity = (int32_t (*)(PP_Resource layer, float opacity))&Pnacl_M38_PPB_CompositorLayer_SetOpacity, + .SetBlendMode = (int32_t (*)(PP_Resource layer, PP_BlendMode mode))&Pnacl_M38_PPB_CompositorLayer_SetBlendMode, + .SetSourceRect = (int32_t (*)(PP_Resource layer, const struct PP_FloatRect* rect))&Pnacl_M38_PPB_CompositorLayer_SetSourceRect, + .SetPremultipliedAlpha = (int32_t (*)(PP_Resource layer, PP_Bool premult))&Pnacl_M38_PPB_CompositorLayer_SetPremultipliedAlpha +}; + static const struct PPB_Console_1_0 Pnacl_Wrappers_PPB_Console_1_0 = { .Log = (void (*)(PP_Instance instance, PP_LogLevel level, struct PP_Var value))&Pnacl_M25_PPB_Console_Log, .LogWithSource = (void (*)(PP_Instance instance, PP_LogLevel level, struct PP_Var source, struct PP_Var value))&Pnacl_M25_PPB_Console_LogWithSource @@ -5391,7 +5464,8 @@ static const struct PPB_Testing_Private_1_0 Pnacl_Wrappers_PPB_Testing_Private_1 .SimulateInputEvent = (void (*)(PP_Instance instance, PP_Resource input_event))&Pnacl_M33_PPB_Testing_Private_SimulateInputEvent, .GetDocumentURL = (struct PP_Var (*)(PP_Instance instance, struct PP_URLComponents_Dev* components))&Pnacl_M33_PPB_Testing_Private_GetDocumentURL, .GetLiveVars = (uint32_t (*)(struct PP_Var live_vars[], uint32_t array_size))&Pnacl_M33_PPB_Testing_Private_GetLiveVars, - .SetMinimumArrayBufferSizeForShmem = (void (*)(PP_Instance instance, uint32_t threshold))&Pnacl_M33_PPB_Testing_Private_SetMinimumArrayBufferSizeForShmem + .SetMinimumArrayBufferSizeForShmem = (void (*)(PP_Instance instance, uint32_t threshold))&Pnacl_M33_PPB_Testing_Private_SetMinimumArrayBufferSizeForShmem, + .RunV8GC = (void (*)(PP_Instance instance))&Pnacl_M33_PPB_Testing_Private_RunV8GC }; static const struct PPB_UDPSocket_Private_0_2 Pnacl_Wrappers_PPB_UDPSocket_Private_0_2 = { @@ -5497,6 +5571,12 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_1 = { .real_iface = NULL }; +static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_CompositorLayer_0_2 = { + .iface_macro = PPB_COMPOSITORLAYER_INTERFACE_0_2, + .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_CompositorLayer_0_2, + .real_iface = NULL +}; + static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_Console_1_0 = { .iface_macro = PPB_CONSOLE_INTERFACE_1_0, .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_Console_1_0, @@ -6124,6 +6204,7 @@ static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPP_Instance_Private_0_1 = { static struct __PnaclWrapperInfo *s_ppb_wrappers[] = { &Pnacl_WrapperInfo_PPB_Compositor_0_1, &Pnacl_WrapperInfo_PPB_CompositorLayer_0_1, + &Pnacl_WrapperInfo_PPB_CompositorLayer_0_2, &Pnacl_WrapperInfo_PPB_Console_1_0, &Pnacl_WrapperInfo_PPB_Core_1_0, &Pnacl_WrapperInfo_PPB_FileIO_1_0, diff --git a/ppapi/proxy/compositor_layer_resource.cc b/ppapi/proxy/compositor_layer_resource.cc index 17bc7b2fe4228..09a63b1805355 100644 --- a/ppapi/proxy/compositor_layer_resource.cc +++ b/ppapi/proxy/compositor_layer_resource.cc @@ -5,6 +5,7 @@ #include "ppapi/proxy/compositor_layer_resource.h" #include "base/logging.h" +#include "gpu/GLES2/gl2extchromium.h" #include "gpu/command_buffer/client/gles2_implementation.h" #include "gpu/command_buffer/common/mailbox.h" #include "ppapi/proxy/compositor_resource.h" @@ -121,8 +122,17 @@ int32_t CompositorLayerResource::SetColor(float red, return PP_OK; } +int32_t CompositorLayerResource::SetTexture0_1( + PP_Resource context, + uint32_t texture, + const PP_Size* size, + const scoped_refptr& release_callback) { + return SetTexture(context, GL_TEXTURE_2D, texture, size, release_callback); +} + int32_t CompositorLayerResource::SetTexture( PP_Resource context, + uint32_t target, uint32_t texture, const PP_Size* size, const scoped_refptr& release_callback) { @@ -135,6 +145,12 @@ int32_t CompositorLayerResource::SetTexture( if (enter.failed()) return PP_ERROR_BADRESOURCE; + if (target != GL_TEXTURE_2D && + target != GL_TEXTURE_EXTERNAL_OES && + target != GL_TEXTURE_RECTANGLE_ARB) { + return PP_ERROR_BADARGUMENT; + } + if (!size || size->width <= 0 || size->height <= 0) return PP_ERROR_BADARGUMENT; @@ -147,7 +163,7 @@ int32_t CompositorLayerResource::SetTexture( gl->GenMailboxCHROMIUM( reinterpret_cast(data_.texture->mailbox.name)); gl->ProduceTextureDirectCHROMIUM( - texture, GL_TEXTURE_2D, + texture, target, reinterpret_cast(data_.texture->mailbox.name)); // Set the source size to (1, 1). It will be used to verify the source_rect @@ -156,6 +172,7 @@ int32_t CompositorLayerResource::SetTexture( data_.common.size = *size; data_.common.resource_id = compositor_->GenerateResourceId(); + data_.texture->target = target; data_.texture->sync_point = gl->InsertSyncPointCHROMIUM(); data_.texture->source_rect.point = PP_MakeFloatPoint(0.0f, 0.0f); data_.texture->source_rect.size = source_size_; diff --git a/ppapi/proxy/compositor_layer_resource.h b/ppapi/proxy/compositor_layer_resource.h index a31d8fe7c8acc..09f5d81cee577 100644 --- a/ppapi/proxy/compositor_layer_resource.h +++ b/ppapi/proxy/compositor_layer_resource.h @@ -53,8 +53,14 @@ class PPAPI_PROXY_EXPORT CompositorLayerResource float blue, float alpha, const PP_Size* size) OVERRIDE; + virtual int32_t SetTexture0_1( + PP_Resource context, + uint32_t texture, + const PP_Size* size, + const scoped_refptr& callback) OVERRIDE; virtual int32_t SetTexture( PP_Resource context, + uint32_t target, uint32_t texture, const PP_Size* size, const scoped_refptr& callback) OVERRIDE; diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h index 2090d36a883f6..7747abf370f0d 100644 --- a/ppapi/proxy/ppapi_messages.h +++ b/ppapi/proxy/ppapi_messages.h @@ -264,6 +264,7 @@ IPC_STRUCT_TRAITS_END() IPC_STRUCT_TRAITS_BEGIN(ppapi::CompositorLayerData::TextureLayer) IPC_STRUCT_TRAITS_MEMBER(mailbox) + IPC_STRUCT_TRAITS_MEMBER(target) IPC_STRUCT_TRAITS_MEMBER(sync_point) IPC_STRUCT_TRAITS_MEMBER(source_rect) IPC_STRUCT_TRAITS_MEMBER(premult_alpha) diff --git a/ppapi/proxy/ppb_testing_proxy.cc b/ppapi/proxy/ppb_testing_proxy.cc index 0b29795f124f5..0dbab0be0635c 100644 --- a/ppapi/proxy/ppb_testing_proxy.cc +++ b/ppapi/proxy/ppb_testing_proxy.cc @@ -127,6 +127,11 @@ void SetMinimumArrayBufferSizeForShmem(PP_Instance instance, API_ID_PPB_TESTING, threshold)); } +void RunV8GC(PP_Instance instance) { + // TODO(raymes): Implement this if we need it. + NOTIMPLEMENTED(); +} + const PPB_Testing_Private testing_interface = { &ReadImageData, &RunMessageLoop, @@ -136,7 +141,8 @@ const PPB_Testing_Private testing_interface = { &SimulateInputEvent, &GetDocumentURL, &GetLiveVars, - &SetMinimumArrayBufferSizeForShmem + &SetMinimumArrayBufferSizeForShmem, + &RunV8GC }; } // namespace diff --git a/ppapi/proxy/ppp_messaging_proxy_perftest.cc b/ppapi/proxy/ppp_messaging_proxy_perftest.cc index 666609a51b36d..c526514594978 100644 --- a/ppapi/proxy/ppp_messaging_proxy_perftest.cc +++ b/ppapi/proxy/ppp_messaging_proxy_perftest.cc @@ -6,9 +6,11 @@ #include "base/strings/string_number_conversions.h" #include "base/test/perf_time_logger.h" #include "ppapi/c/ppp_messaging.h" +#include "ppapi/proxy/ppapi_messages.h" #include "ppapi/proxy/ppapi_proxy_test.h" #include "ppapi/proxy/serialized_var.h" #include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/var.h" #include "ppapi/shared_impl/var_tracker.h" @@ -19,6 +21,7 @@ namespace { base::WaitableEvent handle_message_called(false, false); void HandleMessage(PP_Instance /* instance */, PP_Var message_data) { + ppapi::ProxyAutoLock lock; StringVar* string_var = StringVar::FromPPVar(message_data); DCHECK(string_var); // Retrieve the string to make sure the proxy can't "optimize away" sending @@ -49,10 +52,6 @@ class PppMessagingPerfTest : public TwoWayTest { // Tests the performance of sending strings through the proxy. TEST_F(PppMessagingPerfTest, StringPerformance) { - // Grab the host-side proxy of ppp_messaging. - const PPP_Messaging* ppp_messaging = static_cast( - host().host_dispatcher()->GetProxiedInterface( - PPP_MESSAGING_INTERFACE)); const PP_Instance kTestInstance = pp_instance(); int seed = 123; int string_count = 1000; @@ -77,7 +76,13 @@ TEST_F(PppMessagingPerfTest, StringPerformance) { for (int i = 0; i < string_count; ++i) { const std::string test_string(rand() % max_string_size, 'a'); PP_Var host_string = StringVar::StringToPPVar(test_string); - ppp_messaging->HandleMessage(kTestInstance, host_string); + // We don't have a host-side PPP_Messaging interface; just send the message + // directly like the proxy does. + host().host_dispatcher()->Send(new PpapiMsg_PPPMessaging_HandleMessage( + ppapi::API_ID_PPP_MESSAGING, + kTestInstance, + ppapi::proxy::SerializedVarSendInput(host().host_dispatcher(), + host_string))); handle_message_called.Wait(); PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(host_string); } diff --git a/ppapi/shared_impl/compositor_layer_data.h b/ppapi/shared_impl/compositor_layer_data.h index b51fcd787a96b..8cf096ea49b69 100644 --- a/ppapi/shared_impl/compositor_layer_data.h +++ b/ppapi/shared_impl/compositor_layer_data.h @@ -78,11 +78,13 @@ struct PPAPI_SHARED_EXPORT CompositorLayerData { struct TextureLayer { TextureLayer() - : sync_point(0), + : target(0), + sync_point(0), source_rect(PP_MakeFloatRectFromXYWH(0.0f, 0.0f, 1.0f, 1.0f)), premult_alpha(true) {} gpu::Mailbox mailbox; + uint32_t target; uint32_t sync_point; PP_FloatRect source_rect; bool premult_alpha; diff --git a/ppapi/shared_impl/scoped_pp_var.cc b/ppapi/shared_impl/scoped_pp_var.cc index f612aa1a2ac76..95748392fce2c 100644 --- a/ppapi/shared_impl/scoped_pp_var.cc +++ b/ppapi/shared_impl/scoped_pp_var.cc @@ -4,8 +4,10 @@ #include "ppapi/shared_impl/scoped_pp_var.h" +#include "ppapi/c/dev/ppb_memory_dev.h" #include "ppapi/shared_impl/ppapi_globals.h" #include "ppapi/shared_impl/var_tracker.h" +#include "ppapi/thunk/thunk.h" namespace ppapi { @@ -46,4 +48,45 @@ PP_Var ScopedPPVar::Release() { return result; } +ScopedPPVarArray::ScopedPPVarArray(const PassPPBMemoryAllocatedArray&, + PP_Var* array, + size_t size) + : array_(array), + size_(size) {} + +ScopedPPVarArray::ScopedPPVarArray(size_t size) + : size_(size) { + if (size > 0) { + array_ = static_cast( + thunk::GetPPB_Memory_Dev_0_1_Thunk()->MemAlloc( + static_cast(sizeof(PP_Var) * size))); + } + for (size_t i = 0; i < size_; ++i) + array_[i] = PP_MakeUndefined(); +} + +ScopedPPVarArray::~ScopedPPVarArray() { + for (size_t i = 0; i < size_; ++i) + CallRelease(array_[i]); + if (size_ > 0) + thunk::GetPPB_Memory_Dev_0_1_Thunk()->MemFree(array_); + +} + +PP_Var* ScopedPPVarArray::Release(const PassPPBMemoryAllocatedArray&, + size_t* size) { + PP_Var* result = array_; + *size = size_; + array_ = NULL; + size_ = 0; + return result; +} + +void ScopedPPVarArray::Set(size_t index, PP_Var var) { + DCHECK(index < size_); + CallAddRef(var); + CallRelease(array_[index]); + array_[index] = var; +} + } // namespace ppapi diff --git a/ppapi/shared_impl/scoped_pp_var.h b/ppapi/shared_impl/scoped_pp_var.h index 564401d5d4be8..44d0ff115817b 100644 --- a/ppapi/shared_impl/scoped_pp_var.h +++ b/ppapi/shared_impl/scoped_pp_var.h @@ -5,6 +5,9 @@ #ifndef PPAPI_SHARED_IMPL_SCOPED_PP_VAR_H_ #define PPAPI_SHARED_IMPL_SCOPED_PP_VAR_H_ +#include + +#include "base/macros.h" #include "ppapi/c/pp_var.h" #include "ppapi/shared_impl/ppapi_shared_export.h" @@ -42,6 +45,41 @@ class PPAPI_SHARED_EXPORT ScopedPPVar { PP_Var var_; }; +// An array of PP_Vars which will be deallocated and have their references +// decremented when they go out of scope. +class ScopedPPVarArray { + public: + struct PassPPBMemoryAllocatedArray {}; + + // Assumes responsibility for one ref of each of the vars in the array as + // well as the array memory allocated by PPB_Memory_Dev. + // TODO(raymes): Add compatibility for arrays allocated with C++ "new". + ScopedPPVarArray(const PassPPBMemoryAllocatedArray&, + PP_Var* array, + size_t size); + + explicit ScopedPPVarArray(size_t size); + ~ScopedPPVarArray(); + + // Passes ownership of the vars and the underlying array memory to the caller. + // Note that the memory has been allocated with PPB_Memory_Dev. + PP_Var* Release(const PassPPBMemoryAllocatedArray&, size_t* size); + + PP_Var* get() { return array_; } + size_t size() { return size_; } + + // Adds a ref to |var|. The refcount of the existing var will be decremented. + void Set(size_t index, PP_Var var); + const PP_Var& operator[](size_t index) { return array_[index]; } + + private: + // TODO(raymes): Consider supporting copy/assign. + DISALLOW_COPY_AND_ASSIGN(ScopedPPVarArray); + + PP_Var* array_; + size_t size_; +}; + } // namespace ppapi #endif // PPAPI_SHARED_IMPL_SCOPED_PP_VAR_H_ diff --git a/ppapi/shared_impl/var.cc b/ppapi/shared_impl/var.cc index 30201823ec13f..195a5544dfe05 100644 --- a/ppapi/shared_impl/var.cc +++ b/ppapi/shared_impl/var.cc @@ -82,6 +82,8 @@ ArrayBufferVar* Var::AsArrayBufferVar() { return NULL; } NPObjectVar* Var::AsNPObjectVar() { return NULL; } +V8ObjectVar* Var::AsV8ObjectVar() { return NULL; } + ProxyObjectVar* Var::AsProxyObjectVar() { return NULL; } ArrayVar* Var::AsArrayVar() { return NULL; } diff --git a/ppapi/shared_impl/var.h b/ppapi/shared_impl/var.h index d5a7a55829786..fcca41a9f95c8 100644 --- a/ppapi/shared_impl/var.h +++ b/ppapi/shared_impl/var.h @@ -23,6 +23,7 @@ class NPObjectVar; class ProxyObjectVar; class ResourceVar; class StringVar; +class V8ObjectVar; class VarTracker; // Var ------------------------------------------------------------------------- @@ -36,6 +37,7 @@ class PPAPI_SHARED_EXPORT Var : public base::RefCounted { virtual StringVar* AsStringVar(); virtual ArrayBufferVar* AsArrayBufferVar(); virtual NPObjectVar* AsNPObjectVar(); + virtual V8ObjectVar* AsV8ObjectVar(); virtual ProxyObjectVar* AsProxyObjectVar(); virtual ArrayVar* AsArrayVar(); virtual DictionaryVar* AsDictionaryVar(); diff --git a/ppapi/tests/test_case.h b/ppapi/tests/test_case.h index 948d2b73bb5c8..90283bd9f4e0b 100644 --- a/ppapi/tests/test_case.h +++ b/ppapi/tests/test_case.h @@ -63,6 +63,7 @@ class TestCase { // Returns the scriptable test object for the current test, if any. // Internally, this uses CreateTestObject which each test overrides. pp::VarPrivate GetTestObject(); + void ResetTestObject() { test_object_ = pp::VarPrivate(); } #endif // A function that is invoked whenever HandleMessage is called on the diff --git a/ppapi/tests/test_compositor.cc b/ppapi/tests/test_compositor.cc index e844e8dd4c957..ba2b5eda3f2ee 100644 --- a/ppapi/tests/test_compositor.cc +++ b/ppapi/tests/test_compositor.cc @@ -122,7 +122,8 @@ std::string TestCompositor::TestBindUnbind() { TestCompletionCallback texture_release_callback(instance_->pp_instance(), PP_REQUIRED); ASSERT_EQ(PP_OK_COMPLETIONPENDING, - texture_layer.SetTexture(graphics_3d, texture, pp::Size(100, 100), + texture_layer.SetTexture(graphics_3d, GL_TEXTURE_2D, texture, + pp::Size(100, 100), texture_release_callback.GetCallback())); pp::ImageData image; @@ -201,7 +202,8 @@ std::string TestCompositor::TestReleaseInternal(bool bind) { TestCompletionCallback texture_release_callback(instance_->pp_instance(), PP_REQUIRED); ASSERT_EQ(PP_OK_COMPLETIONPENDING, - texture_layer.SetTexture(graphics_3d, texture, pp::Size(100, 100), + texture_layer.SetTexture(graphics_3d, GL_TEXTURE_2D, texture, + pp::Size(100, 100), texture_release_callback.GetCallback())); pp::ImageData image; @@ -265,7 +267,8 @@ std::string TestCompositor::TestReleaseWithoutCommitInternal(bool bind) { TestCompletionCallback texture_release_callback(instance_->pp_instance(), PP_REQUIRED); ASSERT_EQ(PP_OK_COMPLETIONPENDING, - texture_layer.SetTexture(graphics_3d, texture, pp::Size(100, 100), + texture_layer.SetTexture(graphics_3d, GL_TEXTURE_2D, texture, + pp::Size(100, 100), texture_release_callback.GetCallback())); pp::ImageData image; @@ -347,7 +350,8 @@ std::string TestCompositor::TestGeneralInternal(bool bind) { TestCompletionCallback texture_release_callback(instance_->pp_instance(), PP_REQUIRED); ASSERT_EQ(PP_OK_COMPLETIONPENDING, - texture_layer.SetTexture(graphics_3d, texture, pp::Size(100, 100), + texture_layer.SetTexture(graphics_3d, texture, GL_TEXTURE_2D, + pp::Size(100, 100), texture_release_callback.GetCallback())); pp::ImageData image; diff --git a/ppapi/tests/test_instance_deprecated.cc b/ppapi/tests/test_instance_deprecated.cc index 1fd99e15b0f98..6746858e2a9ed 100644 --- a/ppapi/tests/test_instance_deprecated.cc +++ b/ppapi/tests/test_instance_deprecated.cc @@ -5,7 +5,6 @@ #include "ppapi/tests/test_instance_deprecated.h" #include -#include #include "ppapi/c/ppb_var.h" #include "ppapi/cpp/module.h" @@ -126,6 +125,12 @@ bool TestInstance::Init() { } TestInstance::~TestInstance() { + ResetTestObject(); + // When running tests in process, some post conditions check that teardown + // happened successfully. We need to run the garbage collector to ensure that + // vars get released. + if (testing_interface_->IsOutOfProcess() == PP_FALSE) + testing_interface_->RunV8GC(instance_->pp_instance()); // Save the fact that we were destroyed in sessionStorage. This tests that // we can ExecuteScript at instance destruction without crashing. It also // allows us to check that ExecuteScript will run and succeed in certain @@ -218,8 +223,17 @@ class ObjectWithChildren : public pp::deprecated::ScriptableObject { }; std::string TestInstance::TestRecursiveObjects() { - // These should be deleted when we exit scope, so should not leak. - pp::VarPrivate not_leaked(instance(), new ObjectWithChildren(this, 50)); + const int kNumChildren = 20; + { + // These should be deleted when we exit scope, so should not leak. + pp::VarPrivate not_leaked(instance(), new ObjectWithChildren(this, + kNumChildren)); + } + // We need to run the GC multiple times until all of the vars are released. + // Each GC invocation will result in releasing a var, which will result in its + // children not having any references, allowing them also to be collected. + for (int i = 0; i < kNumChildren; ++i) + testing_interface_->RunV8GC(instance_->pp_instance()); // Leak some, but tell TestCase to ignore the leaks. This test is run and then // reloaded (see ppapi_uitest.cc). If these aren't cleaned up when the first @@ -229,7 +243,8 @@ std::string TestInstance::TestRecursiveObjects() { // destructor is not run. pp::VarPrivate leaked( instance(), - new ObjectWithChildren(this, 50, ObjectWithChildren::IgnoreLeaks())); + new ObjectWithChildren(this, kNumChildren, + ObjectWithChildren::IgnoreLeaks())); // Now leak a reference to the root object. This should force the root and // all its descendents to stay in the tracker. LeakReferenceAndIgnore(leaked); diff --git a/ppapi/tests/test_media_stream_audio_track.cc b/ppapi/tests/test_media_stream_audio_track.cc index 05cb0bf3ad7e3..020cf0819be9e 100644 --- a/ppapi/tests/test_media_stream_audio_track.cc +++ b/ppapi/tests/test_media_stream_audio_track.cc @@ -76,6 +76,7 @@ void TestMediaStreamAudioTrack::RunTests(const std::string& filter) { RUN_TEST(Create, filter); RUN_TEST(GetBuffer, filter); RUN_TEST(Configure, filter); + RUN_TEST(ConfigureClose, filter); } void TestMediaStreamAudioTrack::HandleMessage(const pp::Var& message) { @@ -204,6 +205,17 @@ std::string TestMediaStreamAudioTrack::TestConfigure() { ASSERT_FALSE(audio_track_.HasEnded()); ASSERT_FALSE(audio_track_.GetId().empty()); + // Perform a |Configure()| with no attributes. This ends up making an IPC + // call, but the host implementation has a fast-path when there are no changes + // to the configuration. This test is intended to hit that fast-path and make + // sure it works correctly. + { + int32_t attrib_list[] = { + PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE, + }; + ASSERT_SUBTEST_SUCCESS(CheckConfigure(attrib_list, PP_OK)); + } + // Configure number of buffers. struct { int32_t buffers; @@ -293,3 +305,33 @@ std::string TestMediaStreamAudioTrack::TestConfigure() { audio_track_ = pp::MediaStreamAudioTrack(); PASS(); } + +std::string TestMediaStreamAudioTrack::TestConfigureClose() { + // Create a track. + instance_->EvalScript(kJSCode); + event_.Wait(); + event_.Reset(); + + ASSERT_FALSE(audio_track_.is_null()); + ASSERT_FALSE(audio_track_.HasEnded()); + ASSERT_FALSE(audio_track_.GetId().empty()); + + // Configure the audio track and close it immediately. The Configure() call + // should complete. + int32_t attrib_list[] = { + PP_MEDIASTREAMAUDIOTRACK_ATTRIB_BUFFERS, 10, + PP_MEDIASTREAMAUDIOTRACK_ATTRIB_NONE, + }; + TestCompletionCallback cc_configure(instance_->pp_instance(), false); + int32_t result = audio_track_.Configure(attrib_list, + cc_configure.GetCallback()); + ASSERT_EQ(PP_OK_COMPLETIONPENDING, result); + audio_track_.Close(); + cc_configure.WaitForResult(result); + result = cc_configure.result(); + // Unfortunately, we can't control whether the configure succeeds or is + // aborted. + ASSERT_TRUE(result == PP_OK || result == PP_ERROR_ABORTED); + + PASS(); +} diff --git a/ppapi/tests/test_media_stream_audio_track.h b/ppapi/tests/test_media_stream_audio_track.h index 88148e8b3ce69..ee6864c2c77f8 100644 --- a/ppapi/tests/test_media_stream_audio_track.h +++ b/ppapi/tests/test_media_stream_audio_track.h @@ -31,6 +31,7 @@ class TestMediaStreamAudioTrack : public TestCase { std::string TestCreate(); std::string TestGetBuffer(); std::string TestConfigure(); + std::string TestConfigureClose(); pp::MediaStreamAudioTrack audio_track_; diff --git a/ppapi/tests/test_var_deprecated.cc b/ppapi/tests/test_var_deprecated.cc index dbfa83e21d61a..ee79868f21865 100644 --- a/ppapi/tests/test_var_deprecated.cc +++ b/ppapi/tests/test_var_deprecated.cc @@ -318,7 +318,6 @@ std::string TestVarDeprecated::TestHasPropertyAndMethod() { // Check exception and return false on invalid property name. ASSERT_FALSE(window.HasProperty(3.14159, &exception)); ASSERT_FALSE(exception.is_undefined()); - exception = pp::Var(); exception = pp::Var(); ASSERT_FALSE(window.HasMethod(3.14159, &exception)); diff --git a/ppapi/tests/test_video_decoder.cc b/ppapi/tests/test_video_decoder.cc index e801e05b9067c..f5d5bc0ed31bb 100644 --- a/ppapi/tests/test_video_decoder.cc +++ b/ppapi/tests/test_video_decoder.cc @@ -38,7 +38,7 @@ std::string TestVideoDecoder::TestCreate() { TestCompletionCallback callback(instance_->pp_instance(), callback_type()); pp::Graphics3D null_graphics_3d; callback.WaitForResult(video_decoder.Initialize(null_graphics_3d, - PP_VIDEOPROFILE_VP8MAIN, + PP_VIDEOPROFILE_VP8_ANY, kAllowSoftwareFallback, callback.GetCallback())); ASSERT_EQ(PP_ERROR_BADRESOURCE, callback.result()); @@ -60,7 +60,7 @@ std::string TestVideoDecoder::TestCreate() { pp::VideoDecoder video_decoder(instance_); TestCompletionCallback callback(instance_->pp_instance(), callback_type()); callback.WaitForResult(video_decoder.Initialize(graphics_3d_, - PP_VIDEOPROFILE_VP8MAIN, + PP_VIDEOPROFILE_VP8_ANY, kAllowSoftwareFallback, callback.GetCallback())); ASSERT_EQ(PP_OK, callback.result()); diff --git a/ppapi/thunk/ppb_compositor_layer_api.h b/ppapi/thunk/ppb_compositor_layer_api.h index e59f6325c4222..23635f39402f8 100644 --- a/ppapi/thunk/ppb_compositor_layer_api.h +++ b/ppapi/thunk/ppb_compositor_layer_api.h @@ -20,8 +20,14 @@ class PPAPI_THUNK_EXPORT PPB_CompositorLayer_API { float blue, float alpha, const PP_Size* size) = 0; + virtual int32_t SetTexture0_1( + PP_Resource context, + uint32_t texture, + const PP_Size* size, + const scoped_refptr& callback) = 0; virtual int32_t SetTexture( PP_Resource context, + uint32_t target, uint32_t texture, const PP_Size* size, const scoped_refptr& callback) = 0; diff --git a/ppapi/thunk/ppb_compositor_layer_thunk.cc b/ppapi/thunk/ppb_compositor_layer_thunk.cc index 4923feab600af..f0a98df596895 100644 --- a/ppapi/thunk/ppb_compositor_layer_thunk.cc +++ b/ppapi/thunk/ppb_compositor_layer_thunk.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. -// From ppb_compositor_layer.idl modified Wed Jun 4 11:17:54 2014. +// From ppb_compositor_layer.idl modified Thu Aug 14 18:06:33 2014. #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_errors.h" @@ -36,8 +36,24 @@ int32_t SetColor(PP_Resource layer, return enter.object()->SetColor(red, green, blue, alpha, size); } +int32_t SetTexture_0_1(PP_Resource layer, + PP_Resource context, + uint32_t texture, + const struct PP_Size* size, + struct PP_CompletionCallback cc) { + VLOG(4) << "PPB_CompositorLayer::SetTexture()"; + EnterResource enter(layer, cc, true); + if (enter.failed()) + return enter.retval(); + return enter.SetResult(enter.object()->SetTexture0_1(context, + texture, + size, + enter.callback())); +} + int32_t SetTexture(PP_Resource layer, PP_Resource context, + uint32_t target, uint32_t texture, const struct PP_Size* size, struct PP_CompletionCallback cc) { @@ -46,6 +62,7 @@ int32_t SetTexture(PP_Resource layer, if (enter.failed()) return enter.retval(); return enter.SetResult(enter.object()->SetTexture(context, + target, texture, size, enter.callback())); @@ -113,6 +130,19 @@ int32_t SetPremultipliedAlpha(PP_Resource layer, PP_Bool premult) { } const PPB_CompositorLayer_0_1 g_ppb_compositorlayer_thunk_0_1 = { + &IsCompositorLayer, + &SetColor, + &SetTexture_0_1, + &SetImage, + &SetClipRect, + &SetTransform, + &SetOpacity, + &SetBlendMode, + &SetSourceRect, + &SetPremultipliedAlpha +}; + +const PPB_CompositorLayer_0_2 g_ppb_compositorlayer_thunk_0_2 = { &IsCompositorLayer, &SetColor, &SetTexture, @@ -132,5 +162,10 @@ PPAPI_THUNK_EXPORT const PPB_CompositorLayer_0_1* return &g_ppb_compositorlayer_thunk_0_1; } +PPAPI_THUNK_EXPORT const PPB_CompositorLayer_0_2* + GetPPB_CompositorLayer_0_2_Thunk() { + return &g_ppb_compositorlayer_thunk_0_2; +} + } // namespace thunk } // namespace ppapi diff --git a/printing/emf_win.cc b/printing/emf_win.cc index d9472f9cc3fcf..b6c9b4fc6d1c3 100644 --- a/printing/emf_win.cc +++ b/printing/emf_win.cc @@ -169,9 +169,14 @@ Emf::Emf() : emf_(NULL), hdc_(NULL), page_count_(0) { } Emf::~Emf() { + Close(); +} + +void Emf::Close() { DCHECK(!hdc_); if (emf_) DeleteEnhMetaFile(emf_); + emf_ = NULL; } bool Emf::InitToFile(const base::FilePath& metafile_path) { diff --git a/printing/emf_win.h b/printing/emf_win.h index fa274091f20f4..e31e20475aae4 100644 --- a/printing/emf_win.h +++ b/printing/emf_win.h @@ -42,6 +42,9 @@ class PRINTING_EXPORT Emf : public Metafile { Emf(); virtual ~Emf(); + // Closes metafile. + void Close(); + // Generates a new metafile that will record every GDI command, and will // be saved to |metafile_path|. virtual bool InitToFile(const base::FilePath& metafile_path); diff --git a/remoting/android/cast/AndroidManifest.xml.jinja2 b/remoting/android/cast/AndroidManifest.xml.jinja2 new file mode 100644 index 0000000000000..f1ed533c83ddf --- /dev/null +++ b/remoting/android/cast/AndroidManifest.xml.jinja2 @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/remoting/android/cast/src/org/chromium/chromoting/CastExtensionHandler.java b/remoting/android/cast/src/org/chromium/chromoting/CastExtensionHandler.java new file mode 100644 index 0000000000000..053ee6345fad2 --- /dev/null +++ b/remoting/android/cast/src/org/chromium/chromoting/CastExtensionHandler.java @@ -0,0 +1,477 @@ +// 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.chromoting; + +import android.app.Activity; +import android.content.Context; +import android.os.Bundle; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.app.MediaRouteActionProvider; +import android.support.v7.media.MediaRouteSelector; +import android.support.v7.media.MediaRouter; +import android.support.v7.media.MediaRouter.RouteInfo; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.widget.Toast; + +import com.google.android.gms.cast.Cast; +import com.google.android.gms.cast.Cast.Listener; +import com.google.android.gms.cast.CastDevice; +import com.google.android.gms.cast.CastMediaControlIntent; +import com.google.android.gms.cast.CastStatusCodes; +import com.google.android.gms.common.ConnectionResult; +import com.google.android.gms.common.api.GoogleApiClient; +import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks; +import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener; +import com.google.android.gms.common.api.ResultCallback; +import com.google.android.gms.common.api.Status; + +import org.chromium.chromoting.jni.JniInterface; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * A handler that interacts with the Cast Extension of the Chromoting host using extension messages. + * It uses the Cast Android Sender API to start our registered Cast Receiver App on a nearby Cast + * device, if the user chooses to do so. + */ +public class CastExtensionHandler implements ClientExtension, ActivityLifecycleListener { + + /** Extension messages of this type will be handled by the CastExtensionHandler. */ + public static final String EXTENSION_MSG_TYPE = "cast_message"; + + /** Tag used for logging. */ + private static final String TAG = "CastExtensionHandler"; + + /** Application Id of the Cast Receiver App that will be run on the Cast device. */ + private static final String RECEIVER_APP_ID = "8A1211E3"; + + /** + * Custom namespace that will be used to communicate with the Cast device. + * TODO(aiguha): Use com.google.chromeremotedesktop for official builds. + */ + private static final String CHROMOTOCAST_NAMESPACE = "urn:x-cast:com.chromoting.cast.all"; + + /** Context that wil be used to initialize the MediaRouter and the GoogleApiClient. */ + private Context mContext = null; + + /** True if the application has been launched on the Cast device. */ + private boolean mApplicationStarted; + + /** True if the client is temporarily in a disconnected state. */ + private boolean mWaitingForReconnect; + + /** Object that allows routing of media to external devices including Google Cast devices. */ + private MediaRouter mMediaRouter; + + /** Describes the capabilities of routes that the application might want to use. */ + private MediaRouteSelector mMediaRouteSelector; + + /** Cast device selected by the user. */ + private CastDevice mSelectedDevice; + + /** Object to receive callbacks about media routing changes. */ + private MediaRouter.Callback mMediaRouterCallback; + + /** Listener for events related to the connected Cast device.*/ + private Listener mCastClientListener; + + /** Object that handles Google Play Services integration. */ + private GoogleApiClient mApiClient; + + /** Callback objects for connection changes with Google Play Services. */ + private ConnectionCallbacks mConnectionCallbacks; + private OnConnectionFailedListener mConnectionFailedListener; + + /** Channel for receiving messages from the Cast device. */ + private ChromotocastChannel mChromotocastChannel; + + /** Current session ID, if there is one. */ + private String mSessionId; + + /** Queue of messages that are yet to be delivered to the Receiver App. */ + private List mChromotocastMessageQueue; + + /** Current status of the application, if any. */ + private String mApplicationStatus; + + /** + * A callback class for receiving events about media routing. + */ + private class CustomMediaRouterCallback extends MediaRouter.Callback { + @Override + public void onRouteSelected(MediaRouter router, RouteInfo info) { + mSelectedDevice = CastDevice.getFromBundle(info.getExtras()); + connectApiClient(); + } + + @Override + public void onRouteUnselected(MediaRouter router, RouteInfo info) { + tearDown(); + mSelectedDevice = null; + } + } + + /** + * A callback class for receiving the result of launching an application on the user-selected + * Google Cast device. + */ + private class ApplicationConnectionResultCallback implements + ResultCallback { + @Override + public void onResult(Cast.ApplicationConnectionResult result) { + Status status = result.getStatus(); + if (!status.isSuccess()) { + tearDown(); + return; + } + + mSessionId = result.getSessionId(); + mApplicationStatus = result.getApplicationStatus(); + mApplicationStarted = result.getWasLaunched(); + mChromotocastChannel = new ChromotocastChannel(); + + try { + Cast.CastApi.setMessageReceivedCallbacks(mApiClient, + mChromotocastChannel.getNamespace(), mChromotocastChannel); + sendPendingMessagesToCastDevice(); + } catch (IOException e) { + showToast(R.string.connection_to_cast_failed, Toast.LENGTH_SHORT); + tearDown(); + } catch (IllegalStateException e) { + showToast(R.string.connection_to_cast_failed, Toast.LENGTH_SHORT); + tearDown(); + } + } + } + + /** + * A callback class for receiving events about client connections and disconnections from + * Google Play Services. + */ + private class ConnectionCallbacks implements GoogleApiClient.ConnectionCallbacks { + @Override + public void onConnected(Bundle connectionHint) { + if (mWaitingForReconnect) { + mWaitingForReconnect = false; + reconnectChannels(); + return; + } + Cast.CastApi.launchApplication(mApiClient, RECEIVER_APP_ID, false).setResultCallback( + new ApplicationConnectionResultCallback()); + } + + @Override + public void onConnectionSuspended(int cause) { + mWaitingForReconnect = true; + } + } + + /** + * A listener for failures to connect with Google Play Services. + */ + private class ConnectionFailedListener implements GoogleApiClient.OnConnectionFailedListener { + @Override + public void onConnectionFailed(ConnectionResult result) { + Log.e(TAG, String.format("Google Play Service connection failed: %s", result)); + + tearDown(); + } + + } + + /** + * A channel for communication with the Cast device on the CHROMOTOCAST_NAMESPACE. + */ + private class ChromotocastChannel implements Cast.MessageReceivedCallback { + + /** + * Returns the namespace associated with this channel. + */ + public String getNamespace() { + return CHROMOTOCAST_NAMESPACE; + } + + @Override + public void onMessageReceived(CastDevice castDevice, String namespace, String message) { + if (namespace.equals(CHROMOTOCAST_NAMESPACE)) { + sendMessageToHost(message); + } + } + } + + /** + * A listener for changes when connected to a Google Cast device. + */ + private class CastClientListener extends Cast.Listener { + @Override + public void onApplicationStatusChanged() { + try { + if (mApiClient != null) { + mApplicationStatus = Cast.CastApi.getApplicationStatus(mApiClient); + } + } catch (IllegalStateException e) { + showToast(R.string.connection_to_cast_failed, Toast.LENGTH_SHORT); + tearDown(); + } + } + + @Override + public void onVolumeChanged() {} // Changes in volume do not affect us. + + @Override + public void onApplicationDisconnected(int errorCode) { + if (errorCode != CastStatusCodes.SUCCESS) { + Log.e(TAG, String.format("Application disconnected with: %d", errorCode)); + } + tearDown(); + } + } + + /** + * Constructs a CastExtensionHandler with an empty message queue. + */ + public CastExtensionHandler() { + mChromotocastMessageQueue = new ArrayList(); + } + + // + // ClientExtension implementation. + // + + @Override + public String getCapability() { + return Capabilities.CAST_CAPABILITY; + } + + @Override + public boolean onExtensionMessage(String type, String data) { + if (type.equals(EXTENSION_MSG_TYPE)) { + mChromotocastMessageQueue.add(data); + if (mApplicationStarted) { + sendPendingMessagesToCastDevice(); + } + return true; + } + return false; + } + + @Override + public ActivityLifecycleListener onActivityAcceptingListener(Activity activity) { + return this; + } + + // + // ActivityLifecycleListener implementation. + // + + /** Initializes the MediaRouter and related objects using the provided activity Context. */ + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + if (activity == null) { + return; + } + mContext = activity; + mMediaRouter = MediaRouter.getInstance(activity); + mMediaRouteSelector = new MediaRouteSelector.Builder() + .addControlCategory(CastMediaControlIntent.categoryForCast(RECEIVER_APP_ID)) + .build(); + mMediaRouterCallback = new CustomMediaRouterCallback(); + } + + @Override + public void onActivityDestroyed(Activity activity) { + tearDown(); + } + + @Override + public void onActivityPaused(Activity activity) { + removeMediaRouterCallback(); + } + + @Override + public void onActivityResumed(Activity activity) { + addMediaRouterCallback(); + } + + @Override + public void onActivitySaveInstanceState (Activity activity, Bundle outState) {} + + @Override + public void onActivityStarted(Activity activity) { + addMediaRouterCallback(); + } + + @Override + public void onActivityStopped(Activity activity) { + removeMediaRouterCallback(); + } + + @Override + public boolean onActivityCreatedOptionsMenu(Activity activity, Menu menu) { + // Find the cast button in the menu. + MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item); + if (mediaRouteMenuItem == null) { + return false; + } + + // Setup a MediaRouteActionProvider using the button. + MediaRouteActionProvider mediaRouteActionProvider = + (MediaRouteActionProvider) MenuItemCompat.getActionProvider(mediaRouteMenuItem); + mediaRouteActionProvider.setRouteSelector(mMediaRouteSelector); + + return true; + } + + @Override + public boolean onActivityOptionsItemSelected(Activity activity, MenuItem item) { + if (item.getItemId() == R.id.actionbar_disconnect) { + removeMediaRouterCallback(); + showToast(R.string.connection_to_cast_closed, Toast.LENGTH_SHORT); + tearDown(); + return true; + } + return false; + } + + // + // Extension Message Handling logic + // + + /** Sends a message to the Chromoting host. */ + private void sendMessageToHost(String data) { + JniInterface.sendExtensionMessage(EXTENSION_MSG_TYPE, data); + } + + /** Sends any messages in the message queue to the Cast device. */ + private void sendPendingMessagesToCastDevice() { + for (String msg : mChromotocastMessageQueue) { + sendMessageToCastDevice(msg); + } + mChromotocastMessageQueue.clear(); + } + + // + // Cast Sender API logic + // + + /** + * Initializes and connects to Google Play Services. + */ + private void connectApiClient() { + if (mContext == null) { + return; + } + mCastClientListener = new CastClientListener(); + mConnectionCallbacks = new ConnectionCallbacks(); + mConnectionFailedListener = new ConnectionFailedListener(); + + Cast.CastOptions.Builder apiOptionsBuilder = Cast.CastOptions + .builder(mSelectedDevice, mCastClientListener) + .setVerboseLoggingEnabled(true); + + mApiClient = new GoogleApiClient.Builder(mContext) + .addApi(Cast.API, apiOptionsBuilder.build()) + .addConnectionCallbacks(mConnectionCallbacks) + .addOnConnectionFailedListener(mConnectionFailedListener) + .build(); + mApiClient.connect(); + } + + /** + * Adds the callback object to the MediaRouter. Called when the owning activity starts/resumes. + */ + private void addMediaRouterCallback() { + if (mMediaRouter != null && mMediaRouteSelector != null && mMediaRouterCallback != null) { + mMediaRouter.addCallback(mMediaRouteSelector, mMediaRouterCallback, + MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN); + } + } + + /** + * Removes the callback object from the MediaRouter. Called when the owning activity + * stops/pauses. + */ + private void removeMediaRouterCallback() { + if (mMediaRouter != null && mMediaRouterCallback != null) { + mMediaRouter.removeCallback(mMediaRouterCallback); + } + } + + /** + * Sends a message to the Cast device on the CHROMOTOCAST_NAMESPACE. + */ + private void sendMessageToCastDevice(String message) { + if (mApiClient == null || mChromotocastChannel == null) { + return; + } + Cast.CastApi.sendMessage(mApiClient, mChromotocastChannel.getNamespace(), message) + .setResultCallback(new ResultCallback() { + @Override + public void onResult(Status result) { + if (!result.isSuccess()) { + Log.e(TAG, "Failed to send message to cast device."); + } + } + }); + + } + + /** + * Restablishes the chromotocast message channel, so we can continue communicating with the + * Google Cast device. This must be called when resuming a connection. + */ + private void reconnectChannels() { + if (mApiClient == null && mChromotocastChannel == null) { + return; + } + try { + Cast.CastApi.setMessageReceivedCallbacks( + mApiClient,mChromotocastChannel.getNamespace(),mChromotocastChannel); + sendPendingMessagesToCastDevice(); + } catch (IOException e) { + showToast(R.string.connection_to_cast_failed, Toast.LENGTH_SHORT); + } catch (IllegalStateException e) { + showToast(R.string.connection_to_cast_failed, Toast.LENGTH_SHORT); + } + } + + /** + * Stops the running application on the Google Cast device and performs the required tearDown + * sequence. + */ + private void tearDown() { + if (mApiClient != null && mApplicationStarted && mApiClient.isConnected()) { + Cast.CastApi.stopApplication(mApiClient, mSessionId); + if (mChromotocastChannel != null) { + try { + Cast.CastApi.removeMessageReceivedCallbacks( + mApiClient, mChromotocastChannel.getNamespace()); + } catch (IOException e) { + Log.e(TAG, "Failed to remove chromotocast channel."); + } + } + mApiClient.disconnect(); + } + mChromotocastChannel = null; + mApplicationStarted = false; + mApiClient = null; + mSelectedDevice = null; + mWaitingForReconnect = false; + mSessionId = null; + } + + /** + * Makes a toast using the given message and duration. + */ + private void showToast(int messageId, int duration) { + if (mContext != null) { + Toast.makeText(mContext, mContext.getString(messageId), duration).show(); + } + } +} diff --git a/remoting/android/java/AndroidManifest.xml.jinja2 b/remoting/android/java/AndroidManifest.xml.jinja2 index 22795747b9136..836533c88afc4 100644 --- a/remoting/android/java/AndroidManifest.xml.jinja2 +++ b/remoting/android/java/AndroidManifest.xml.jinja2 @@ -34,7 +34,8 @@ + android:windowSoftInputMode="adjustResize" + android:theme="@style/Theme.AppCompat"/> diff --git a/remoting/android/java/res/menu/desktop_actionbar.xml b/remoting/android/java/res/menu/desktop_actionbar.xml index 3b4cc64ba02e9..6596f986c5f39 100644 --- a/remoting/android/java/res/menu/desktop_actionbar.xml +++ b/remoting/android/java/res/menu/desktop_actionbar.xml @@ -6,26 +6,31 @@ --> - + + + app:showAsAction="ifRoom"/> + app:showAsAction="ifRoom"/> + app:showAsAction="withText"/> + app:showAsAction="withText"/> + app:showAsAction="never"/> diff --git a/remoting/android/java/src/org/chromium/chromoting/ActivityLifecycleListener.java b/remoting/android/java/src/org/chromium/chromoting/ActivityLifecycleListener.java new file mode 100644 index 0000000000000..bec5b7b05aca2 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/ActivityLifecycleListener.java @@ -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. + +package org.chromium.chromoting; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + + +/** + * Interface to listen to receive events of an activity's lifecycle and options menu. This interface + * is similar to Application.ActivityLifecycleCallbacks, but is inherently different. This interface + * is intended to act as a listener for a specific Activity. The other is intended as a generic + * listener to be registered at the Application level, for all Activities' lifecycles. + */ +public interface ActivityLifecycleListener { + + public void onActivityCreated(Activity activity, Bundle savedInstanceState); + + public boolean onActivityCreatedOptionsMenu(Activity activity, Menu menu); + + public void onActivityDestroyed(Activity activity); + + public boolean onActivityOptionsItemSelected(Activity activity, MenuItem item); + + public void onActivityPaused(Activity activity); + + public void onActivityResumed(Activity activity); + + public void onActivitySaveInstanceState(Activity activity, Bundle outState); + + public void onActivityStarted(Activity activity); + + public void onActivityStopped(Activity activity); +} diff --git a/remoting/android/java/src/org/chromium/chromoting/Capabilities.java b/remoting/android/java/src/org/chromium/chromoting/Capabilities.java new file mode 100644 index 0000000000000..cff3d9e89cb11 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/Capabilities.java @@ -0,0 +1,15 @@ +// 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.chromoting; + +/** + * The list of all capabilities that might be supported by the Chromoting host + * and client. As more capabilities are supported on the android client, + * they can be enumerated here. This list must be kept synced with the + * Chromoting host. + */ +public class Capabilities { + public static final String CAST_CAPABILITY = "casting"; +} diff --git a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java new file mode 100644 index 0000000000000..932b38563a70a --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java @@ -0,0 +1,157 @@ +// 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.chromoting; + +import android.app.Activity; +import android.text.TextUtils; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * A manager for the capabilities of the Android client. Based on the negotiated set of + * capabilities, it creates the associated ClientExtensions, and enables their communication with + * the Chromoting host by dispatching extension messages appropriately. + * + * The CapabilityManager mirrors how the Chromoting host handles extension messages. For each + * incoming extension message, runs through a list of HostExtensionSession objects, giving each one + * a chance to handle the message. + * + * The CapabilityManager is a singleton class so we can manage client extensions on an application + * level. The singleton object may be used from multiple Activities, thus allowing it to support + * different capabilities at different stages of the application. + */ +public class CapabilityManager { + + /** Lazily-initialized singleton object that can be used from different Activities. */ + private static CapabilityManager sInstance; + + /** Protects access to |sInstance|. */ + private static final Object sInstanceLock = new Object(); + + /** List of all capabilities that are supported by the application. */ + private List mLocalCapabilities; + + /** List of negotiated capabilities received from the host. */ + private List mNegotiatedCapabilities; + + /** List of extensions to the client based on capabilities negotiated with the host. */ + private List mClientExtensions; + + private CapabilityManager() { + mLocalCapabilities = new ArrayList(); + mClientExtensions = new ArrayList(); + + mLocalCapabilities.add(Capabilities.CAST_CAPABILITY); + } + + /** + * Returns the singleton object. Thread-safe. + */ + public static CapabilityManager getInstance() { + synchronized (sInstanceLock) { + if (sInstance == null) { + sInstance = new CapabilityManager(); + } + return sInstance; + } + } + + /** + * Returns a space-separated list (required by host) of the capabilities supported by + * this client. + */ + public String getLocalCapabilities() { + return TextUtils.join(" ", mLocalCapabilities); + } + + /** + * Returns the ActivityLifecycleListener associated with the specified capability, if + * |capability| is enabled and such a listener exists. + * + * Activities that call this method agree to appropriately notify the listener of lifecycle + * events., thus supporting |capability|. This allows extensions like the CastExtensionHandler + * to hook into an existing activity's lifecycle. + */ + public ActivityLifecycleListener onActivityAcceptingListener( + Activity activity, String capability) { + + ActivityLifecycleListener listener; + + if (isCapabilityEnabled(capability)) { + for (ClientExtension ext : mClientExtensions) { + if (ext.getCapability().equals(capability)) { + listener = ext.onActivityAcceptingListener(activity); + if (listener != null) + return listener; + } + } + } + + return new DummyActivityLifecycleListener(); + } + + /** + * Receives the capabilities negotiated between client and host and creates the appropriate + * extension handlers. + * + * Currently only the CAST_CAPABILITY exists, so that is the only extension constructed. + */ + public void setNegotiatedCapabilities(String capabilities) { + mNegotiatedCapabilities = Arrays.asList(capabilities.split(" ")); + mClientExtensions.clear(); + if (isCapabilityEnabled(Capabilities.CAST_CAPABILITY)) { + mClientExtensions.add(maybeCreateCastExtensionHandler()); + } + } + + /** + * Passes the deconstructed extension message to each ClientExtension in turn until the message + * is handled or none remain. Returns true if the message was handled. + */ + public boolean onExtensionMessage(String type, String data) { + if (type == null || type.isEmpty()) { + return false; + } + + for (ClientExtension ext : mClientExtensions) { + if (ext.onExtensionMessage(type, data)) { + return true; + } + } + return false; + } + + /** + * Return true if the capability is enabled for this connection with the host. + */ + private boolean isCapabilityEnabled(String capability) { + return (mNegotiatedCapabilities != null && mNegotiatedCapabilities.contains(capability)); + } + + /** + * Tries to reflectively instantiate a CastExtensionHandler object. + * + * Note: The ONLY reason this is done is that by default, the regular android application + * will be built, without this experimental extension. + */ + private ClientExtension maybeCreateCastExtensionHandler() { + try { + Class cls = Class.forName("org.chromium.chromoting.CastExtensionHandler"); + return (ClientExtension) cls.newInstance(); + } catch (ClassNotFoundException e) { + Log.w("CapabilityManager", "Failed to create CastExtensionHandler."); + return new DummyClientExtension(); + } catch (InstantiationException e) { + Log.w("CapabilityManager", "Failed to create CastExtensionHandler."); + return new DummyClientExtension(); + } catch (IllegalAccessException e) { + Log.w("CapabilityManager", "Failed to create CastExtensionHandler."); + return new DummyClientExtension(); + } + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java index 1928f3c715913..f0901bc2d8e46 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java +++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java @@ -474,7 +474,7 @@ public void onTokenFetched(String code, String accessToken) { // authenticate itself with the host using spake. String sharedSecret = accessToken; - JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret); + JniInterface.onThirdPartyTokenFetched(token, sharedSecret); } }; return new ThirdPartyTokenFetcher(this, host.getTokenUrlPatterns(), callback); diff --git a/remoting/android/java/src/org/chromium/chromoting/ClientExtension.java b/remoting/android/java/src/org/chromium/chromoting/ClientExtension.java new file mode 100644 index 0000000000000..070dcc71bfc40 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/ClientExtension.java @@ -0,0 +1,32 @@ +// 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.chromoting; + +import android.app.Activity; + +/** + * Interface to extend the Android client's functionality by providing a way to communicate with + * the Chromoting host. + */ +public interface ClientExtension { + + /** Returns the capability supported by this extension, or an empty string. */ + public String getCapability(); + + /** + * Called when the client receives an extension message from the host through JniInterface. It + * returns true if the message was handled appropriately, and false otherwise. + */ + public boolean onExtensionMessage(String type, String data); + + /** + * Called when an activity offers to accept an ActivityListener for its lifecycle events. + * This gives Extensions the option to hook into an existing Activity, get notified about + * changes in its state and modify its behavior. Returns the extension's activity listener, + * or null. + */ + public ActivityLifecycleListener onActivityAcceptingListener(Activity activity); + +} diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java index f40469f836037..f6b4d2605d06e 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java +++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java @@ -5,10 +5,10 @@ package org.chromium.chromoting; import android.annotation.SuppressLint; -import android.app.Activity; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; +import android.support.v7.app.ActionBarActivity; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.Menu; @@ -25,7 +25,7 @@ /** * A simple screen that does nothing except display a DesktopView and notify it of rotations. */ -public class Desktop extends Activity implements View.OnSystemUiVisibilityChangeListener { +public class Desktop extends ActionBarActivity implements View.OnSystemUiVisibilityChangeListener { /** Web page to be displayed in the Help screen when launched from this activity. */ private static final String HELP_URL = "http://support.google.com/chrome/?p=mobile_crd_connecthost"; @@ -39,6 +39,9 @@ public class Desktop extends Activity implements View.OnSystemUiVisibilityChange /** Set of pressed keys for which we've sent TextEvent. */ private Set mPressedTextKeys = new TreeSet(); + private ActivityLifecycleListener mActivityLifecycleListener; + + /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { @@ -53,6 +56,36 @@ public void onCreate(Bundle savedInstanceState) { View decorView = getWindow().getDecorView(); decorView.setOnSystemUiVisibilityChangeListener(this); + + mActivityLifecycleListener = CapabilityManager.getInstance() + .onActivityAcceptingListener(this, Capabilities.CAST_CAPABILITY); + mActivityLifecycleListener.onActivityCreated(this, savedInstanceState); + } + + @Override + protected void onStart() { + super.onStart(); + mActivityLifecycleListener.onActivityStarted(this); + } + + @Override + protected void onPause() { + if (isFinishing()) { + mActivityLifecycleListener.onActivityPaused(this); + } + super.onPause(); + } + + @Override + public void onResume() { + super.onResume(); + mActivityLifecycleListener.onActivityResumed(this); + } + + @Override + protected void onStop() { + mActivityLifecycleListener.onActivityStopped(this); + super.onStop(); } /** Called when the activity is finally finished. */ @@ -73,6 +106,9 @@ public void onConfigurationChanged(Configuration newConfig) { @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.desktop_actionbar, menu); + + mActivityLifecycleListener.onActivityCreatedOptionsMenu(this, menu); + return super.onCreateOptionsMenu(menu); } @@ -144,6 +180,9 @@ public void onOverlayButtonPressed(View view) { @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); + + mActivityLifecycleListener.onActivityOptionsItemSelected(this, item); + if (id == R.id.actionbar_keyboard) { ((InputMethodManager)getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(0, 0); return true; diff --git a/remoting/android/java/src/org/chromium/chromoting/DummyActivityLifecycleListener.java b/remoting/android/java/src/org/chromium/chromoting/DummyActivityLifecycleListener.java new file mode 100644 index 0000000000000..76b8c6fa5247f --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/DummyActivityLifecycleListener.java @@ -0,0 +1,48 @@ +// 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.chromoting; + +import android.app.Activity; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; + +/** + * A dummy implementation of ActivityListener that will be passed to any activity requesting + * a capability that is not currently enabled. + */ +public class DummyActivityLifecycleListener implements ActivityLifecycleListener { + + @Override + public void onActivityCreated (Activity activity, Bundle savedInstanceState) {} + + @Override + public void onActivityDestroyed (Activity activity) {} + + @Override + public void onActivityPaused (Activity activity) {} + + @Override + public void onActivityResumed (Activity activity) {} + + @Override + public void onActivitySaveInstanceState (Activity activity, Bundle outState) {} + + @Override + public void onActivityStarted (Activity activity) {} + + @Override + public void onActivityStopped (Activity activity) {} + + @Override + public boolean onActivityCreatedOptionsMenu(Activity activity, Menu menu) { + return false; + } + + @Override + public boolean onActivityOptionsItemSelected(Activity activity, MenuItem item) { + return false; + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/DummyClientExtension.java b/remoting/android/java/src/org/chromium/chromoting/DummyClientExtension.java new file mode 100644 index 0000000000000..e1dc6771b8f00 --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/DummyClientExtension.java @@ -0,0 +1,28 @@ +// 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.chromoting; + +import android.app.Activity; + +/** + * A dummy implementation of ClientExtension. + */ +public class DummyClientExtension implements ClientExtension { + + @Override + public String getCapability() { + return ""; + } + + @Override + public boolean onExtensionMessage(String type, String data) { + return false; + } + + @Override + public ActivityLifecycleListener onActivityAcceptingListener(Activity activity) { + return null; + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/HelpActivity.java b/remoting/android/java/src/org/chromium/chromoting/HelpActivity.java index faaa7b26306d2..7870335dd9348 100644 --- a/remoting/android/java/src/org/chromium/chromoting/HelpActivity.java +++ b/remoting/android/java/src/org/chromium/chromoting/HelpActivity.java @@ -48,7 +48,7 @@ public class HelpActivity extends Activity { * This global variable is used for passing the screenshot from the originating Activity to the * HelpActivity. There seems to be no better way of doing this. */ - private static Bitmap mScreenshot; + private static Bitmap sScreenshot; /** Constant used to send the feedback parcel to the system feedback service. */ private static final int SEND_FEEDBACK_INFO = Binder.FIRST_CALL_TRANSACTION; @@ -78,8 +78,8 @@ private void sendFeedback() { public void onServiceConnected(ComponentName name, IBinder service) { try { Parcel parcel = Parcel.obtain(); - if (mScreenshot != null) { - mScreenshot.writeToParcel(parcel, 0); + if (sScreenshot != null) { + sScreenshot.writeToParcel(parcel, 0); } service.transact(SEND_FEEDBACK_INFO, parcel, null, 0); parcel.recycle(); @@ -98,7 +98,7 @@ public void onServiceDisconnected(ComponentName name) {} /** Launches the Help activity. */ public static void launch(Activity activity, String helpUrl) { View rootView = activity.getWindow().getDecorView().getRootView(); - mScreenshot = UiUtils.generateScaledScreenshot(rootView, MAX_FEEDBACK_SCREENSHOT_DIMENSION, + sScreenshot = UiUtils.generateScaledScreenshot(rootView, MAX_FEEDBACK_SCREENSHOT_DIMENSION, Bitmap.Config.ARGB_8888); Intent intent = new Intent(activity, HelpActivity.class); diff --git a/remoting/android/java/src/org/chromium/chromoting/SecureRandomInitializer.java b/remoting/android/java/src/org/chromium/chromoting/SecureRandomInitializer.java new file mode 100644 index 0000000000000..cf731ed31054d --- /dev/null +++ b/remoting/android/java/src/org/chromium/chromoting/SecureRandomInitializer.java @@ -0,0 +1,41 @@ +// 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.chromoting; + +import java.io.FileInputStream; +import java.io.IOException; +import java.security.SecureRandom; + +/** + * This class contains code to initialize a SecureRandom generator securely on Android platforms + * <= 4.3. See + * {@link http://android-developers.blogspot.com/2013/08/some-securerandom-thoughts.html}. + */ +public class SecureRandomInitializer { + private static final int NUM_RANDOM_BYTES = 16; + + /** + * Safely initializes the random number generator, by seeding it with data from /dev/urandom. + */ + public static void initialize(SecureRandom generator) throws IOException { + FileInputStream fis = null; + try { + fis = new FileInputStream("/dev/urandom"); + byte[] bytes = new byte[NUM_RANDOM_BYTES]; + if (bytes.length != fis.read(bytes)) { + throw new IOException("Failed to get enough random data."); + } + generator.setSeed(bytes); + } finally { + try { + if (fis != null) { + fis.close(); + } + } catch (IOException e) { + // Ignore exception closing the device. + } + } + } +} diff --git a/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java b/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java index ac195cf617acb..7d8dee5de8d72 100644 --- a/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java +++ b/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java @@ -4,6 +4,7 @@ package org.chromium.chromoting; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ComponentName; @@ -14,6 +15,7 @@ import android.util.Base64; import android.util.Log; +import java.io.IOException; import java.security.SecureRandom; import java.util.ArrayList; @@ -39,7 +41,19 @@ public interface Callback { private static final String RESPONSE_TYPE = "code token"; /** This is used to securely generate an opaque 128 bit for the |mState| variable. */ - private static SecureRandom sSecureRandom = new SecureRandom(); + @SuppressLint("TrulyRandom") + private static SecureRandom sSecureRandom; + + // TODO(lambroslambrou): Refactor this class to only initialize a PRNG when ThirdPartyAuth is + // actually used. + static { + sSecureRandom = new SecureRandom(); + try { + SecureRandomInitializer.initialize(sSecureRandom); + } catch (IOException e) { + throw new RuntimeException("Failed to initialize PRNG: " + e); + } + } /** This is used to launch the third party login page in the browser. */ private Activity mContext; diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java index f5ce54ad8299a..9304ee34165e6 100644 --- a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java +++ b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java @@ -18,9 +18,11 @@ import android.view.View; import android.widget.CheckBox; import android.widget.TextView; +import android.widget.Toast; import org.chromium.base.CalledByNative; import org.chromium.base.JNINamespace; +import org.chromium.chromoting.CapabilityManager; import org.chromium.chromoting.Chromoting; import org.chromium.chromoting.R; @@ -142,6 +144,9 @@ public static Error fromValue(int value) { /** Bitmap holding the cursor shape. Accessed on the graphics thread. */ private static Bitmap sCursorBitmap = null; + /** Capability Manager through which capabilities and extensions are handled. */ + private static CapabilityManager sCapabilityManager = CapabilityManager.getInstance(); + /** * To be called once from the main Activity. Any subsequent calls will update the application * context, but not reload the library. This is useful e.g. when the activity is closed and the @@ -176,21 +181,34 @@ public static void connectToHost(String username, String authToken, sConnectionListener = listener; SharedPreferences prefs = sContext.getPreferences(Activity.MODE_PRIVATE); nativeConnect(username, authToken, hostJid, hostId, hostPubkey, - prefs.getString(hostId + "_id", ""), prefs.getString(hostId + "_secret", "")); + prefs.getString(hostId + "_id", ""), prefs.getString(hostId + "_secret", ""), + sCapabilityManager.getLocalCapabilities()); sConnected = true; } /** Performs the native portion of the connection. */ private static native void nativeConnect(String username, String authToken, String hostJid, - String hostId, String hostPubkey, String pairId, String pairSecret); + String hostId, String hostPubkey, String pairId, String pairSecret, + String capabilities); /** Severs the connection and cleans up. Called on the UI thread. */ public static void disconnectFromHost() { - if (!sConnected) return; + if (!sConnected) { + return; + } sConnectionListener.onConnectionState(ConnectionListener.State.CLOSED, ConnectionListener.Error.OK); + disconnectFromHostWithoutNotification(); + } + + /** Same as disconnectFromHost() but without notifying the ConnectionListener. */ + private static void disconnectFromHostWithoutNotification() { + if (!sConnected) { + return; + } + nativeDisconnect(); sConnectionListener = null; sConnected = false; @@ -204,11 +222,17 @@ public static void disconnectFromHost() { /** Performs the native portion of the cleanup. */ private static native void nativeDisconnect(); - /** Reports whenever the connection status changes. Called on the UI thread. */ + /** Called by native code whenever the connection status changes. Called on the UI thread. */ @CalledByNative - private static void reportConnectionStatus(int state, int error) { - sConnectionListener.onConnectionState(ConnectionListener.State.fromValue(state), - ConnectionListener.Error.fromValue(error)); + private static void onConnectionState(int stateCode, int errorCode) { + ConnectionListener.State state = ConnectionListener.State.fromValue(stateCode); + ConnectionListener.Error error = ConnectionListener.Error.fromValue(errorCode); + sConnectionListener.onConnectionState(state, error); + if (state == ConnectionListener.State.FAILED || state == ConnectionListener.State.CLOSED) { + // Disconnect from the host here, otherwise the next time connectToHost() is called, + // it will try to disconnect, triggering an incorrect status notification. + disconnectFromHostWithoutNotification(); + } } /** Prompts the user to enter a PIN. Called on the UI thread. */ @@ -235,8 +259,13 @@ private static void displayAuthenticationPrompt(boolean pairingSupported) { @Override public void onClick(DialogInterface dialog, int which) { Log.i("jniiface", "User provided a PIN code"); - nativeAuthenticationResponse(String.valueOf(pinTextView.getText()), - pinCheckBox.isChecked(), Build.MODEL); + if (sConnected) { + nativeAuthenticationResponse(String.valueOf(pinTextView.getText()), + pinCheckBox.isChecked(), Build.MODEL); + } else { + String message = sContext.getString(R.string.error_network_error); + Toast.makeText(sContext, message, Toast.LENGTH_LONG).show(); + } } }); @@ -457,5 +486,45 @@ public static void fetchThirdPartyToken(String tokenUrl, String clientId, String /** * Notify the native code to continue authentication with the |token| and the |sharedSecret|. */ - public static native void nativeOnThirdPartyTokenFetched(String token, String sharedSecret); + public static void onThirdPartyTokenFetched(String token, String sharedSecret) { + if (!sConnected) { + return; + } + + nativeOnThirdPartyTokenFetched(token, sharedSecret); + } + + /** Passes authentication data to the native handling code. */ + private static native void nativeOnThirdPartyTokenFetched(String token, String sharedSecret); + + // + // Host and Client Capabilities + // + + /** Set the list of negotiated capabilities between host and client. Called on the UI thread. */ + @CalledByNative + public static void setCapabilities(String capabilities) { + sCapabilityManager.setNegotiatedCapabilities(capabilities); + } + + // + // Extension Message Handling + // + + /** Passes on the deconstructed ExtensionMessage to the app. Called on the UI thread. */ + @CalledByNative + public static void handleExtensionMessage(String type, String data) { + sCapabilityManager.onExtensionMessage(type, data); + } + + /** Sends an extension message to the Chromoting host. Called on the UI thread. */ + public static void sendExtensionMessage(String type, String data) { + if (!sConnected) { + return; + } + + nativeSendExtensionMessage(type, data); + } + + private static native void nativeSendExtensionMessage(String type, String data); } diff --git a/remoting/base/resources_linux.cc b/remoting/base/resources_linux.cc index d7d23ba3886b7..63a53f2b49643 100644 --- a/remoting/base/resources_linux.cc +++ b/remoting/base/resources_linux.cc @@ -31,7 +31,8 @@ bool LoadResources(const std::string& pref_locale) { PathService::Override(ui::DIR_LOCALES, path.AppendASCII(kLocaleResourcesDirName)); - ui::ResourceBundle::InitSharedInstanceLocaleOnly(pref_locale, NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + pref_locale, NULL, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); } return true; diff --git a/remoting/base/resources_mac.cc b/remoting/base/resources_mac.cc index 7ea59b6a8a070..d848e67c04190 100644 --- a/remoting/base/resources_mac.cc +++ b/remoting/base/resources_mac.cc @@ -32,7 +32,8 @@ bool LoadResources(const std::string& pref_locale) { if (pref_locale.empty()) l10n_util::OverrideLocaleWithCocoaLocale(); - ui::ResourceBundle::InitSharedInstanceLocaleOnly(pref_locale, NULL); + ui::ResourceBundle::InitSharedInstanceWithLocale( + pref_locale, NULL, ui::ResourceBundle::DO_NOT_LOAD_COMMON_RESOURCES); } return true; diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc index fb66b6c555fe6..a8d1d27caa480 100644 --- a/remoting/client/jni/chromoting_jni_instance.cc +++ b/remoting/client/jni/chromoting_jni_instance.cc @@ -46,12 +46,14 @@ ChromotingJniInstance::ChromotingJniInstance(ChromotingJniRuntime* jni_runtime, const char* host_id, const char* host_pubkey, const char* pairing_id, - const char* pairing_secret) + const char* pairing_secret, + const char* capabilities) : jni_runtime_(jni_runtime), host_id_(host_id), host_jid_(host_jid), create_pairing_(false), stats_logging_enabled_(false), + capabilities_(capabilities), weak_factory_(this) { DCHECK(jni_runtime_->ui_task_runner()->BelongsToCurrentThread()); @@ -250,6 +252,22 @@ void ChromotingJniInstance::SendTextEvent(const std::string& text) { client_->input_stub()->InjectTextEvent(event); } +void ChromotingJniInstance::SendClientMessage(const std::string& type, + const std::string& data) { + if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { + jni_runtime_->network_task_runner()->PostTask( + FROM_HERE, + base::Bind( + &ChromotingJniInstance::SendClientMessage, this, type, data)); + return; + } + + protocol::ExtensionMessage extension_message; + extension_message.set_type(type); + extension_message.set_data(data); + client_->host_stub()->DeliverClientMessage(extension_message); +} + void ChromotingJniInstance::RecordPaintTime(int64 paint_time_ms) { if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { jni_runtime_->network_task_runner()->PostTask( @@ -280,7 +298,7 @@ void ChromotingJniInstance::OnConnectionState( jni_runtime_->ui_task_runner()->PostTask( FROM_HERE, - base::Bind(&ChromotingJniRuntime::ReportConnectionStatus, + base::Bind(&ChromotingJniRuntime::OnConnectionState, base::Unretained(jni_runtime_), state, error)); @@ -299,7 +317,11 @@ void ChromotingJniInstance::OnRouteChanged( } void ChromotingJniInstance::SetCapabilities(const std::string& capabilities) { - NOTIMPLEMENTED(); + jni_runtime_->ui_task_runner()->PostTask( + FROM_HERE, + base::Bind(&ChromotingJniRuntime::SetCapabilities, + base::Unretained(jni_runtime_), + capabilities)); } void ChromotingJniInstance::SetPairingResponse( @@ -314,7 +336,12 @@ void ChromotingJniInstance::SetPairingResponse( void ChromotingJniInstance::DeliverHostMessage( const protocol::ExtensionMessage& message) { - NOTIMPLEMENTED(); + jni_runtime_->ui_task_runner()->PostTask( + FROM_HERE, + base::Bind(&ChromotingJniRuntime::HandleExtensionMessage, + base::Unretained(jni_runtime_), + message.type(), + message.data())); } protocol::ClipboardStub* ChromotingJniInstance::GetClipboardStub() { @@ -402,7 +429,7 @@ void ChromotingJniInstance::ConnectToHostOnNetworkThread() { network_settings)); client_->Start(signaling_.get(), authenticator_.Pass(), - transport_factory.Pass(), host_jid_, std::string()); + transport_factory.Pass(), host_jid_, capabilities_); } void ChromotingJniInstance::DisconnectFromHostOnNetworkThread() { diff --git a/remoting/client/jni/chromoting_jni_instance.h b/remoting/client/jni/chromoting_jni_instance.h index 8f47ea9300c75..ce572fc097377 100644 --- a/remoting/client/jni/chromoting_jni_instance.h +++ b/remoting/client/jni/chromoting_jni_instance.h @@ -48,7 +48,8 @@ class ChromotingJniInstance const char* host_id, const char* host_pubkey, const char* pairing_id, - const char* pairing_secret); + const char* pairing_secret, + const char* capabilities); // Terminates the current connection (if it hasn't already failed) and cleans // up. Must be called before destruction. @@ -86,6 +87,8 @@ class ChromotingJniInstance void SendTextEvent(const std::string& text); + void SendClientMessage(const std::string& type, const std::string& data); + // Records paint time for statistics logging, if enabled. May be called from // any thread. void RecordPaintTime(int64 paint_time_ms); @@ -180,6 +183,11 @@ class ChromotingJniInstance // the Android log. Used on the network thread. bool stats_logging_enabled_; + // The set of capabilities supported by the client. Accessed on the network + // thread. Once SetCapabilities() is called, this will contain the negotiated + // set of capabilities for this remoting session. + std::string capabilities_; + friend class base::RefCountedThreadSafe; base::WeakPtrFactory weak_factory_; diff --git a/remoting/client/jni/chromoting_jni_runtime.cc b/remoting/client/jni/chromoting_jni_runtime.cc index 60a8128e6b3ad..e44c340f9c402 100644 --- a/remoting/client/jni/chromoting_jni_runtime.cc +++ b/remoting/client/jni/chromoting_jni_runtime.cc @@ -77,7 +77,8 @@ static void Connect(JNIEnv* env, jstring hostId, jstring hostPubkey, jstring pairId, - jstring pairSecret) { + jstring pairSecret, + jstring capabilities) { remoting::ChromotingJniRuntime::GetInstance()->ConnectToHost( ConvertJavaStringToUTF8(env, username).c_str(), ConvertJavaStringToUTF8(env, authToken).c_str(), @@ -85,7 +86,8 @@ static void Connect(JNIEnv* env, ConvertJavaStringToUTF8(env, hostId).c_str(), ConvertJavaStringToUTF8(env, hostPubkey).c_str(), ConvertJavaStringToUTF8(env, pairId).c_str(), - ConvertJavaStringToUTF8(env, pairSecret).c_str()); + ConvertJavaStringToUTF8(env, pairSecret).c_str(), + ConvertJavaStringToUTF8(env, capabilities).c_str()); } static void Disconnect(JNIEnv* env, jclass clazz) { @@ -156,6 +158,15 @@ static void OnThirdPartyTokenFetched(JNIEnv* env, ConvertJavaStringToUTF8(env, shared_secret))); } +static void SendExtensionMessage(JNIEnv* env, + jclass clazz, + jstring type, + jstring data) { + remoting::ChromotingJniRuntime::GetInstance()->session()->SendClientMessage( + ConvertJavaStringToUTF8(env, type), + ConvertJavaStringToUTF8(env, data)); +} + // ChromotingJniRuntime implementation. // static @@ -214,7 +225,8 @@ void ChromotingJniRuntime::ConnectToHost(const char* username, const char* host_id, const char* host_pubkey, const char* pairing_id, - const char* pairing_secret) { + const char* pairing_secret, + const char* capabilities) { DCHECK(ui_task_runner_->BelongsToCurrentThread()); DCHECK(!session_); session_ = new ChromotingJniInstance(this, @@ -224,7 +236,8 @@ void ChromotingJniRuntime::ConnectToHost(const char* username, host_id, host_pubkey, pairing_id, - pairing_secret); + pairing_secret, + capabilities); } void ChromotingJniRuntime::DisconnectFromHost() { @@ -235,13 +248,13 @@ void ChromotingJniRuntime::DisconnectFromHost() { } } -void ChromotingJniRuntime::ReportConnectionStatus( +void ChromotingJniRuntime::OnConnectionState( protocol::ConnectionToHost::State state, protocol::ErrorCode error) { DCHECK(ui_task_runner_->BelongsToCurrentThread()); JNIEnv* env = base::android::AttachCurrentThread(); - Java_JniInterface_reportConnectionStatus(env, state, error); + Java_JniInterface_onConnectionState(env, state, error); } void ChromotingJniRuntime::DisplayAuthenticationPrompt(bool pairing_supported) { @@ -281,6 +294,27 @@ void ChromotingJniRuntime::FetchThirdPartyToken(const GURL& token_url, env, j_url.obj(), j_client_id.obj(), j_scope.obj()); } +void ChromotingJniRuntime::SetCapabilities(const std::string& capabilities) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + JNIEnv* env = base::android::AttachCurrentThread(); + + ScopedJavaLocalRef j_cap = + ConvertUTF8ToJavaString(env, capabilities); + + Java_JniInterface_setCapabilities(env, j_cap.obj()); +} + +void ChromotingJniRuntime::HandleExtensionMessage(const std::string& type, + const std::string& message) { + DCHECK(ui_task_runner_->BelongsToCurrentThread()); + JNIEnv* env = base::android::AttachCurrentThread(); + + ScopedJavaLocalRef j_type = ConvertUTF8ToJavaString(env, type); + ScopedJavaLocalRef j_message = ConvertUTF8ToJavaString(env, message); + + Java_JniInterface_handleExtensionMessage(env, j_type.obj(), j_message.obj()); +} + base::android::ScopedJavaLocalRef ChromotingJniRuntime::NewBitmap( webrtc::DesktopSize size) { JNIEnv* env = base::android::AttachCurrentThread(); diff --git a/remoting/client/jni/chromoting_jni_runtime.h b/remoting/client/jni/chromoting_jni_runtime.h index a5ca339a9f022..47689667a065d 100644 --- a/remoting/client/jni/chromoting_jni_runtime.h +++ b/remoting/client/jni/chromoting_jni_runtime.h @@ -57,7 +57,8 @@ class ChromotingJniRuntime { const char* host_id, const char* host_pubkey, const char* pairing_id, - const char* pairing_secret); + const char* pairing_secret, + const char* capabilities); // Terminates any ongoing connection attempt and cleans up by nullifying // |session_|. This is a no-op unless |session| is currently non-null. @@ -70,9 +71,9 @@ class ChromotingJniRuntime { return session_; } - // Notifies the user of the current connection status. Call on UI thread. - void ReportConnectionStatus(protocol::ConnectionToHost::State state, - protocol::ErrorCode error); + // Notifies Java code of the current connection status. Call on UI thread. + void OnConnectionState(protocol::ConnectionToHost::State state, + protocol::ErrorCode error); // Pops up a dialog box asking the user to enter a PIN. Call on UI thread. void DisplayAuthenticationPrompt(bool pairing_supported); @@ -88,6 +89,14 @@ class ChromotingJniRuntime { const std::string& client_id, const std::string& scope); + // Pass on the set of negotiated capabilities to the client. + void SetCapabilities(const std::string& capabilities); + + // Passes on the deconstructed ExtensionMessage to the client to handle + // appropriately. + void HandleExtensionMessage(const std::string& type, + const std::string& message); + // Creates a new Bitmap object to store a video frame. base::android::ScopedJavaLocalRef NewBitmap( webrtc::DesktopSize size); diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index 7e76fc19b2e00..f0f9959d46446 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -215,6 +215,7 @@ ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) input_tracker_(&mouse_input_filter_), key_mapper_(&input_tracker_), input_handler_(this), + text_input_controller_(this), use_async_pin_dialog_(false), use_media_source_rendering_(false), delegate_large_cursors_(false), @@ -237,9 +238,13 @@ ChromotingInstance::ChromotingInstance(PP_Instance pp_instance) mount("", "/usr", "memfs", 0, ""); #endif + // Register for mouse, wheel and keyboard events. RequestInputEvents(PP_INPUTEVENT_CLASS_MOUSE | PP_INPUTEVENT_CLASS_WHEEL); RequestFilteringInputEvents(PP_INPUTEVENT_CLASS_KEYBOARD); + // Disable the client-side IME in Chrome. + text_input_controller_.SetTextInputType(PP_TEXTINPUT_TYPE_NONE); + // Resister this instance to handle debug log messsages. RegisterLoggingInstance(); diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index 325b0d03cbfe5..36bba6aa38c09 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -15,6 +15,7 @@ #include "ppapi/c/pp_rect.h" #include "ppapi/c/pp_resource.h" #include "ppapi/cpp/instance.h" +#include "ppapi/cpp/text_input_controller.h" #include "ppapi/cpp/var.h" #include "remoting/client/client_context.h" #include "remoting/client/client_user_interface.h" @@ -276,6 +277,9 @@ class ChromotingInstance : scoped_ptr normalizing_input_filter_; PepperInputHandler input_handler_; + // Used to control text input settings, such as whether to show the IME. + pp::TextInputController text_input_controller_; + // PIN Fetcher. bool use_async_pin_dialog_; protocol::SecretFetchedCallback secret_fetched_callback_; diff --git a/remoting/client/plugin/pepper_address_resolver.cc b/remoting/client/plugin/pepper_address_resolver.cc new file mode 100644 index 0000000000000..b39e4d7532ae4 --- /dev/null +++ b/remoting/client/plugin/pepper_address_resolver.cc @@ -0,0 +1,84 @@ +// 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 "remoting/client/plugin/pepper_address_resolver.h" + +#include "base/logging.h" +#include "ppapi/cpp/net_address.h" +#include "remoting/client/plugin/pepper_util.h" + +namespace remoting { + +PepperAddressResolver::PepperAddressResolver(const pp::InstanceHandle& instance) + : resolver_(instance), + error_(0), + callback_factory_(this) { +} + +PepperAddressResolver::~PepperAddressResolver() { +} + +void PepperAddressResolver::Start(const rtc::SocketAddress& address) { + DCHECK(!address.hostname().empty()); + + PP_HostResolver_Hint hint; + hint.flags = 0; + hint.family = PP_NETADDRESS_FAMILY_UNSPECIFIED; + pp::CompletionCallback callback = + callback_factory_.NewCallback(&PepperAddressResolver::OnResolved); + int result = resolver_.Resolve( + address.hostname().c_str(), address.port(), hint, callback); + DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); +} + +bool PepperAddressResolver::GetResolvedAddress(int family, + rtc::SocketAddress* addr) const { + rtc::SocketAddress result; + switch (family) { + case AF_INET: + result = ipv4_address_; + break; + case AF_INET6: + result = ipv6_address_; + break; + } + + if (result.IsNil()) + return false; + + *addr = result; + return true; +} + +int PepperAddressResolver::GetError() const { + return error_; +} + +void PepperAddressResolver::Destroy(bool wait) { + delete this; +} + +void PepperAddressResolver::OnResolved(int32_t result) { + if (result < 0) { + error_ = EAI_FAIL; + SignalDone(this); + return; + } + + int count = resolver_.GetNetAddressCount(); + for (int i = 0; i < count; ++i) { + pp::NetAddress address = resolver_.GetNetAddress(i); + if (address.GetFamily() == PP_NETADDRESS_FAMILY_IPV4 && + ipv4_address_.IsNil()) { + PpNetAddressToSocketAddress(address, &ipv4_address_); + } else if (address.GetFamily() == PP_NETADDRESS_FAMILY_IPV6 && + ipv6_address_.IsNil()) { + PpNetAddressToSocketAddress(address, &ipv6_address_); + } + } + + SignalDone(this); +} + +} // namespace remoting diff --git a/remoting/client/plugin/pepper_address_resolver.h b/remoting/client/plugin/pepper_address_resolver.h new file mode 100644 index 0000000000000..37012ea51e05c --- /dev/null +++ b/remoting/client/plugin/pepper_address_resolver.h @@ -0,0 +1,45 @@ +// 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 REMOTING_CLIENT_PLUGIN_PEPPER_ADDRESS_RESOLVER_H_ +#define REMOTING_CLIENT_PLUGIN_PEPPER_ADDRESS_RESOLVER_H_ + +#include "ppapi/cpp/host_resolver.h" +#include "ppapi/utility/completion_callback_factory.h" +#include "third_party/webrtc/base/asyncresolverinterface.h" + +namespace remoting { + +// rtc::AsyncResolverInterface implementation that uses Pepper to resolve +// addresses. +class PepperAddressResolver : public rtc::AsyncResolverInterface { + public: + explicit PepperAddressResolver(const pp::InstanceHandle& instance); + virtual ~PepperAddressResolver(); + + // rtc::AsyncResolverInterface. + virtual void Start(const rtc::SocketAddress& addr) OVERRIDE; + virtual bool GetResolvedAddress(int family, + rtc::SocketAddress* addr) const OVERRIDE; + virtual int GetError() const OVERRIDE; + virtual void Destroy(bool wait) OVERRIDE; + + private: + void OnResolved(int32_t result); + + pp::HostResolver resolver_; + + rtc::SocketAddress ipv4_address_; + rtc::SocketAddress ipv6_address_; + + int error_; + + pp::CompletionCallbackFactory callback_factory_; + + DISALLOW_COPY_AND_ASSIGN(PepperAddressResolver); +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_PLUGIN_PEPPER_ADDRESS_RESOLVER_H_ diff --git a/remoting/client/plugin/pepper_packet_socket_factory.cc b/remoting/client/plugin/pepper_packet_socket_factory.cc index 4909f892a21a6..01a20a333b839 100644 --- a/remoting/client/plugin/pepper_packet_socket_factory.cc +++ b/remoting/client/plugin/pepper_packet_socket_factory.cc @@ -11,9 +11,11 @@ #include "ppapi/cpp/net_address.h" #include "ppapi/cpp/udp_socket.h" #include "ppapi/utility/completion_callback_factory.h" +#include "remoting/client/plugin/pepper_address_resolver.h" #include "remoting/client/plugin/pepper_util.h" #include "remoting/protocol/socket_util.h" #include "third_party/webrtc/base/asyncpacketsocket.h" +#include "third_party/webrtc/base/nethelpers.h" namespace remoting { @@ -437,8 +439,7 @@ rtc::AsyncPacketSocket* PepperPacketSocketFactory::CreateClientTcpSocket( rtc::AsyncResolverInterface* PepperPacketSocketFactory::CreateAsyncResolver() { - NOTREACHED(); - return NULL; + return new PepperAddressResolver(pp_instance_); } } // namespace remoting diff --git a/remoting/client/plugin/pepper_port_allocator.cc b/remoting/client/plugin/pepper_port_allocator.cc index 5cceee1b9a2aa..db47a62cbffc4 100644 --- a/remoting/client/plugin/pepper_port_allocator.cc +++ b/remoting/client/plugin/pepper_port_allocator.cc @@ -8,8 +8,6 @@ #include "base/strings/string_number_conversions.h" #include "net/base/net_util.h" #include "ppapi/c/pp_errors.h" -#include "ppapi/cpp/host_resolver.h" -#include "ppapi/cpp/net_address.h" #include "ppapi/cpp/url_loader.h" #include "ppapi/cpp/url_request_info.h" #include "ppapi/cpp/url_response_info.h" @@ -47,18 +45,13 @@ class PepperPortAllocatorSession virtual void SendSessionRequest(const std::string& host, int port) OVERRIDE; private: - void ResolveStunServerAddress(); - void OnStunAddressResolved(int32_t result); - void OnUrlOpened(int32_t result); void ReadResponseBody(); void OnResponseBodyRead(int32_t result); pp::InstanceHandle instance_; - pp::HostResolver stun_address_resolver_; - rtc::SocketAddress stun_address_; - int stun_port_; + cricket::ServerAddresses stun_hosts_; scoped_ptr relay_url_loader_; std::vector relay_response_body_; @@ -89,13 +82,9 @@ PepperPortAllocatorSession::PepperPortAllocatorSession( relay_token, std::string()), instance_(instance), - stun_address_resolver_(instance_), - stun_port_(0), + stun_hosts_(stun_hosts.begin(), stun_hosts.end()), relay_response_received_(false), callback_factory_(this) { - if (stun_hosts.size() > 0) { - stun_address_ = stun_hosts[0]; - } } PepperPortAllocatorSession::~PepperPortAllocatorSession() { @@ -103,16 +92,6 @@ PepperPortAllocatorSession::~PepperPortAllocatorSession() { void PepperPortAllocatorSession::ConfigReady( cricket::PortConfiguration* config) { - if (config->stun_address.IsUnresolved()) { - // Make sure that the address that we pass to ConfigReady() is - // always resolved. - if (stun_address_.IsUnresolved()) { - config->stun_address.Clear(); - } else { - config->stun_address = stun_address_; - } - } - // Filter out non-UDP relay ports, so that we don't try using TCP. for (cricket::PortConfiguration::RelayList::iterator relay = config->relays.begin(); relay != config->relays.end(); ++relay) { @@ -129,80 +108,14 @@ void PepperPortAllocatorSession::ConfigReady( } void PepperPortAllocatorSession::GetPortConfigurations() { - // Add an empty configuration synchronously, so a local connection - // can be started immediately. + // Add a configuration without relay response first so local and STUN + // candidates can be allocated without waiting for the relay response. ConfigReady(new cricket::PortConfiguration( - rtc::SocketAddress(), std::string(), std::string())); + stun_hosts_, std::string(), std::string())); - ResolveStunServerAddress(); TryCreateRelaySession(); } -void PepperPortAllocatorSession::ResolveStunServerAddress() { - if (stun_address_.IsNil()) { - return; - } - - if (!stun_address_.IsUnresolved()) { - return; - } - - std::string hostname = stun_address_.hostname(); - uint16 port = stun_address_.port(); - - PP_HostResolver_Hint hint; - hint.flags = 0; - hint.family = PP_NETADDRESS_FAMILY_IPV4; - pp::CompletionCallback callback = callback_factory_.NewCallback( - &PepperPortAllocatorSession::OnStunAddressResolved); - int result = stun_address_resolver_.Resolve(hostname.c_str(), - port, - hint, - callback); - DCHECK_EQ(result, PP_OK_COMPLETIONPENDING); -} - -void PepperPortAllocatorSession::OnStunAddressResolved(int32_t result) { - if (result < 0) { - LOG(ERROR) << "Failed to resolve stun address " - << stun_address_.hostname() << ": " << result; - return; - } - - if (!stun_address_resolver_.GetNetAddressCount()) { - LOG(WARNING) << "Received 0 addresses for stun server " - << stun_address_.hostname(); - return; - } - - pp::NetAddress address = stun_address_resolver_.GetNetAddress(0); - if (address.is_null()) { - LOG(ERROR) << "Failed to get address for STUN server " - << stun_address_.hostname(); - return; - } - - PpNetAddressToSocketAddress(address, &stun_address_); - DCHECK(!stun_address_.IsUnresolved()); - - if (relay_response_received_) { - // If we've finished reading the response, then resubmit it to - // HttpPortAllocatorSessionBase. This is necessary because STUN - // and Relay parameters are stored together in PortConfiguration - // and ReceiveSessionResponse() doesn't save relay session - // configuration for the case we resolve STUN address later. This - // method invokes overriden ConfigReady() which then submits - // resolved |stun_address_|. - // - // TODO(sergeyu): Refactor HttpPortAllocatorSessionBase to fix this. - ReceiveSessionResponse(std::string(relay_response_body_.begin(), - relay_response_body_.end())); - } else { - ConfigReady(new cricket::PortConfiguration( - stun_address_, std::string(), std::string())); - } -} - void PepperPortAllocatorSession::SendSessionRequest( const std::string& host, int port) { diff --git a/remoting/host/basic_desktop_environment.cc b/remoting/host/basic_desktop_environment.cc index 011bf5c8abf5b..f821d593d4181 100644 --- a/remoting/host/basic_desktop_environment.cc +++ b/remoting/host/basic_desktop_environment.cc @@ -12,6 +12,8 @@ #include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/input_injector.h" #include "remoting/host/screen_controls.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" namespace remoting { @@ -38,6 +40,14 @@ scoped_ptr BasicDesktopEnvironment::CreateScreenControls() { return scoped_ptr(); } +scoped_ptr +BasicDesktopEnvironment::CreateMouseCursorMonitor() { + return scoped_ptr( + webrtc::MouseCursorMonitor::CreateForScreen( + *desktop_capture_options_, + webrtc::kFullDesktopScreenId)); +} + std::string BasicDesktopEnvironment::GetCapabilities() const { return std::string(); } @@ -50,13 +60,14 @@ scoped_ptr BasicDesktopEnvironment::CreateGnubbyAuthHandler( return scoped_ptr(); } -scoped_ptr +scoped_ptr BasicDesktopEnvironment::CreateVideoCapturer() { DCHECK(caller_task_runner_->BelongsToCurrentThread()); // The basic desktop environment does not use X DAMAGE, since it is // broken on many systems - see http://crbug.com/73423. - return scoped_ptr(webrtc::ScreenCapturer::Create()); + return scoped_ptr( + webrtc::ScreenCapturer::Create(*desktop_capture_options_)); } BasicDesktopEnvironment::BasicDesktopEnvironment( @@ -65,7 +76,10 @@ BasicDesktopEnvironment::BasicDesktopEnvironment( scoped_refptr ui_task_runner) : caller_task_runner_(caller_task_runner), input_task_runner_(input_task_runner), - ui_task_runner_(ui_task_runner) { + ui_task_runner_(ui_task_runner), + desktop_capture_options_( + new webrtc::DesktopCaptureOptions( + webrtc::DesktopCaptureOptions::CreateDefault())) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); } diff --git a/remoting/host/basic_desktop_environment.h b/remoting/host/basic_desktop_environment.h index ab38c49da2370..543ad90c50522 100644 --- a/remoting/host/basic_desktop_environment.h +++ b/remoting/host/basic_desktop_environment.h @@ -13,6 +13,12 @@ #include "base/memory/scoped_ptr.h" #include "remoting/host/desktop_environment.h" +namespace webrtc { + +class DesktopCaptureOptions; + +} // namespace webrtc + namespace remoting { class GnubbyAuthHandler; @@ -27,7 +33,9 @@ class BasicDesktopEnvironment : public DesktopEnvironment { virtual scoped_ptr CreateAudioCapturer() OVERRIDE; virtual scoped_ptr CreateInputInjector() OVERRIDE; virtual scoped_ptr CreateScreenControls() OVERRIDE; - virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateMouseCursorMonitor() + OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; virtual scoped_ptr CreateGnubbyAuthHandler( @@ -53,6 +61,10 @@ class BasicDesktopEnvironment : public DesktopEnvironment { return ui_task_runner_; } + webrtc::DesktopCaptureOptions* desktop_capture_options() { + return desktop_capture_options_.get(); + } + private: // Task runner on which methods of DesktopEnvironment interface should be // called. @@ -64,6 +76,13 @@ class BasicDesktopEnvironment : public DesktopEnvironment { // Used to run UI code. scoped_refptr ui_task_runner_; + // Options shared between |DesktopCapturer| and |MouseCursorMonitor|. It + // might contain expensive resources, thus justifying the sharing. + // Also: it's dynamically allocated to avoid having to bring in + // desktop_capture_options.h which brings in X11 headers which causes hard to + // find build errors. + scoped_ptr desktop_capture_options_; + DISALLOW_COPY_AND_ASSIGN(BasicDesktopEnvironment); }; diff --git a/remoting/host/cast_video_capturer_adapter.cc b/remoting/host/cast_video_capturer_adapter.cc new file mode 100644 index 0000000000000..f781fc68ae7df --- /dev/null +++ b/remoting/host/cast_video_capturer_adapter.cc @@ -0,0 +1,201 @@ +// 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 "remoting/host/cast_video_capturer_adapter.h" + +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" + +namespace remoting { + +// Number of frames to be captured per second. +const int kFramesPerSec = 10; + +CastVideoCapturerAdapter::CastVideoCapturerAdapter( + scoped_ptr capturer) + : screen_capturer_(capturer.Pass()) { + DCHECK(screen_capturer_); + + thread_checker_.DetachFromThread(); + + // Disable video adaptation since we don't intend to use it. + set_enable_video_adapter(false); +} + +CastVideoCapturerAdapter::~CastVideoCapturerAdapter() { + DCHECK(!capture_timer_); +} + +webrtc::SharedMemory* CastVideoCapturerAdapter::CreateSharedMemory( + size_t size) { + return NULL; +} + +void CastVideoCapturerAdapter::OnCaptureCompleted(webrtc::DesktopFrame* frame) { + scoped_ptr owned_frame(frame); + + // Drop the owned_frame if there were no changes. + if (!owned_frame || owned_frame->updated_region().is_empty()) { + owned_frame.reset(); + return; + } + + // Convert the webrtc::DesktopFrame to a cricket::CapturedFrame. + cricket::CapturedFrame captured_frame; + captured_frame.width = owned_frame->size().width(); + captured_frame.height = owned_frame->size().height(); + base::TimeTicks current_time = base::TimeTicks::Now(); + captured_frame.elapsed_time = (current_time - start_time_).InMicroseconds() * + base::Time::kNanosecondsPerMicrosecond; + captured_frame.time_stamp = + current_time.ToInternalValue() * base::Time::kNanosecondsPerMicrosecond; + captured_frame.data = owned_frame->data(); + + // The data_size attribute must be set. If multiple formats are supported, + // this should be set appropriately for each one. + captured_frame.data_size = + (captured_frame.width * webrtc::DesktopFrame::kBytesPerPixel * 8 + 7) / + 8 * captured_frame.height; + captured_frame.fourcc = cricket::FOURCC_ARGB; + + SignalFrameCaptured(this, &captured_frame); +} + +bool CastVideoCapturerAdapter::GetBestCaptureFormat( + const cricket::VideoFormat& desired, + cricket::VideoFormat* best_format) { + DCHECK(thread_checker_.CalledOnValidThread()); + + // For now, just used the desired width and height. + best_format->width = desired.width; + best_format->height = desired.height; + best_format->fourcc = cricket::FOURCC_ARGB; + best_format->interval = FPS_TO_INTERVAL(kFramesPerSec); + return true; +} + +cricket::CaptureState CastVideoCapturerAdapter::Start( + const cricket::VideoFormat& capture_format) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(!capture_timer_); + DCHECK_EQ(capture_format.fourcc, (static_cast(cricket::FOURCC_ARGB))); + + if (!screen_capturer_) { + VLOG(1) << "CastVideoCapturerAdapter failed to start."; + return cricket::CS_FAILED; + } + + // This is required to tell the cricket::VideoCapturer base class what the + // capture format will be. + SetCaptureFormat(&capture_format); + + screen_capturer_->Start(this); + + // Save the Start() time of |screen_capturer_|. This will be used + // to estimate the creation time of the frame source, to set the elapsed_time + // of future CapturedFrames in OnCaptureCompleted(). + start_time_ = base::TimeTicks::Now(); + capture_timer_.reset(new base::RepeatingTimer()); + capture_timer_->Start(FROM_HERE, + base::TimeDelta::FromMicroseconds( + GetCaptureFormat()->interval / + (base::Time::kNanosecondsPerMicrosecond)), + this, + &CastVideoCapturerAdapter::CaptureNextFrame); + + return cricket::CS_RUNNING; +} + +// Similar to the base class implementation with some important differences: +// 1. Does not call either Stop() or Start(), as those would affect the state of +// |screen_capturer_|. +// 2. Does not support unpausing after stopping the capturer. It is unclear +// if that flow needs to be supported. +bool CastVideoCapturerAdapter::Pause(bool pause) { + DCHECK(thread_checker_.CalledOnValidThread()); + + if (pause) { + if (capture_state() == cricket::CS_PAUSED) + return true; + + bool running = capture_state() == cricket::CS_STARTING || + capture_state() == cricket::CS_RUNNING; + + DCHECK_EQ(running, IsRunning()); + + if (!running) { + LOG(ERROR) + << "Cannot pause CastVideoCapturerAdapter."; + return false; + } + + // Stop |capture_timer_| and set capture state to cricket::CS_PAUSED. + capture_timer_->Stop(); + SetCaptureState(cricket::CS_PAUSED); + + VLOG(1) << "CastVideoCapturerAdapter paused."; + + return true; + } else { // Unpausing. + if (capture_state() != cricket::CS_PAUSED || !GetCaptureFormat() || + !capture_timer_) { + LOG(ERROR) << "Cannot unpause CastVideoCapturerAdapter."; + return false; + } + + // Restart |capture_timer_| and set capture state to cricket::CS_RUNNING; + capture_timer_->Start(FROM_HERE, + base::TimeDelta::FromMicroseconds( + GetCaptureFormat()->interval / + (base::Time::kNanosecondsPerMicrosecond)), + this, + &CastVideoCapturerAdapter::CaptureNextFrame); + SetCaptureState(cricket::CS_RUNNING); + + VLOG(1) << "CastVideoCapturerAdapter unpaused."; + } + return true; +} + +void CastVideoCapturerAdapter::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK_NE(capture_state(), cricket::CS_STOPPED); + + capture_timer_.reset(); + + SetCaptureFormat(NULL); + SetCaptureState(cricket::CS_STOPPED); + + VLOG(1) << "CastVideoCapturerAdapter stopped."; +} + + +bool CastVideoCapturerAdapter::IsRunning() { + DCHECK(thread_checker_.CalledOnValidThread()); + + return capture_timer_->IsRunning(); +} + +bool CastVideoCapturerAdapter::IsScreencast() const { + return true; +} + +bool CastVideoCapturerAdapter::GetPreferredFourccs( + std::vector* fourccs) { + DCHECK(thread_checker_.CalledOnValidThread()); + if (!fourccs) + return false; + fourccs->push_back(cricket::FOURCC_ARGB); + return true; +} + +void CastVideoCapturerAdapter::CaptureNextFrame() { + // If we are paused, then don't capture. + if (!IsRunning()) + return; + + screen_capturer_->Capture(webrtc::DesktopRegion()); +} + +} // namespace remoting + diff --git a/remoting/host/cast_video_capturer_adapter.h b/remoting/host/cast_video_capturer_adapter.h new file mode 100644 index 0000000000000..2a549400d8e21 --- /dev/null +++ b/remoting/host/cast_video_capturer_adapter.h @@ -0,0 +1,81 @@ +// 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 REMOTING_HOST_CAST_VIDEO_CAPTURER_ADAPTER_H_ +#define REMOTING_HOST_CAST_VIDEO_CAPTURER_ADAPTER_H_ + +#include + +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread_checker.h" +#include "base/timer/timer.h" +#include "third_party/libjingle/source/talk/media/base/videocapturer.h" +#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace webrtc { +class DesktopFrame; +} // namespace webrtc + +namespace remoting { + +// This class controls the capture of video frames from the desktop and is used +// to construct a VideoSource as part of the webrtc PeerConnection API. +// CastVideoCapturerAdapter acts as an adapter between webrtc::ScreenCapturer +// and the cricket::VideoCapturer interface, which it implements. It is used +// to construct a cricket::VideoSource for a PeerConnection, to capture frames +// of the desktop. As indicated in the base implementation, Start() and Stop() +// should be called on the same thread. +class CastVideoCapturerAdapter : public cricket::VideoCapturer, + public webrtc::DesktopCapturer::Callback { + public: + explicit CastVideoCapturerAdapter( + scoped_ptr capturer); + + virtual ~CastVideoCapturerAdapter(); + + // webrtc::DesktopCapturer::Callback implementation. + virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; + // Converts |frame| to a cricket::CapturedFrame and emits that via + // SignalFrameCaptured for the base::VideoCapturer implementation to process. + virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; + + // cricket::VideoCapturer implementation. + virtual bool GetBestCaptureFormat(const cricket::VideoFormat& desired, + cricket::VideoFormat* best_format) OVERRIDE; + virtual cricket::CaptureState Start( + const cricket::VideoFormat& capture_format) OVERRIDE; + virtual bool Pause(bool pause) OVERRIDE; + virtual void Stop() OVERRIDE; + virtual bool IsRunning() OVERRIDE; + virtual bool IsScreencast() const OVERRIDE; + virtual bool GetPreferredFourccs(std::vector* fourccs) OVERRIDE; + + private: + // Kicks off the next frame capture using |screen_capturer_|. + // The captured frame will be passed to OnCaptureCompleted(). + void CaptureNextFrame(); + + // |thread_checker_| is bound to the peer connection worker thread. + base::ThreadChecker thread_checker_; + + // Used to capture frames. + scoped_ptr screen_capturer_; + + // Used to schedule periodic screen captures. + scoped_ptr > capture_timer_; + + // Used to set the elapsed_time attribute of captured frames. + base::TimeTicks start_time_; + + DISALLOW_COPY_AND_ASSIGN(CastVideoCapturerAdapter); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_CAST_VIDEO_CAPTURER_ADAPTER_H_ + diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h index ab35224aebb10..814a1a3ff8a38 100644 --- a/remoting/host/chromoting_host.h +++ b/remoting/host/chromoting_host.h @@ -49,7 +49,7 @@ class DesktopEnvironmentFactory; // // 2. We listen for incoming connection using libjingle. We will create // a ConnectionToClient object that wraps around linjingle for transport. -// A VideoScheduler is created with an Encoder and a webrtc::ScreenCapturer. +// A VideoScheduler is created with an Encoder and a webrtc::DesktopCapturer. // A ConnectionToClient is added to the ScreenRecorder for transporting // the screen captures. An InputStub is created and registered with the // ConnectionToClient to receive mouse / keyboard events from the remote diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc index b71c3883052c2..2177dca8ddad6 100644 --- a/remoting/host/chromoting_host_unittest.cc +++ b/remoting/host/chromoting_host_unittest.cc @@ -11,7 +11,8 @@ #include "remoting/host/chromoting_host.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/desktop_environment.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_desktop_capturer.h" +#include "remoting/host/fake_mouse_cursor_monitor.h" #include "remoting/host/host_mock_objects.h" #include "remoting/proto/video.pb.h" #include "remoting/protocol/errors.h" @@ -235,7 +236,7 @@ class ChromotingHostTest : public testing::Test { host_->OnSessionRouteChange(get_client(0), channel_name, route); } - // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock + // Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock // DesktopEnvironmentFactory::Create(). DesktopEnvironment* CreateDesktopEnvironment() { MockDesktopEnvironment* desktop_environment = new MockDesktopEnvironment(); @@ -249,6 +250,9 @@ class ChromotingHostTest : public testing::Test { EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) .Times(AtMost(1)) .WillOnce(Invoke(this, &ChromotingHostTest::CreateVideoCapturer)); + EXPECT_CALL(*desktop_environment, CreateMouseCursorMonitorPtr()) + .Times(AtMost(1)) + .WillOnce(Invoke(this, &ChromotingHostTest::CreateMouseCursorMonitor)); EXPECT_CALL(*desktop_environment, GetCapabilities()) .Times(AtMost(1)); EXPECT_CALL(*desktop_environment, SetCapabilities(_)) @@ -265,10 +269,16 @@ class ChromotingHostTest : public testing::Test { return input_injector; } - // Creates a fake webrtc::ScreenCapturer, to mock + // Creates a fake webrtc::DesktopCapturer, to mock // DesktopEnvironment::CreateVideoCapturer(). - webrtc::ScreenCapturer* CreateVideoCapturer() { - return new FakeScreenCapturer(); + webrtc::DesktopCapturer* CreateVideoCapturer() { + return new FakeDesktopCapturer(); + } + + // Creates a MockMouseCursorMonitor, to mock + // DesktopEnvironment::CreateMouseCursorMonitor(). + webrtc::MouseCursorMonitor* CreateMouseCursorMonitor() { + return new FakeMouseCursorMonitor(); } void DisconnectAllClients() { diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h index f28feaa180d76..12c91639097f8 100644 --- a/remoting/host/chromoting_messages.h +++ b/remoting/host/chromoting_messages.h @@ -144,12 +144,6 @@ IPC_MESSAGE_CONTROL3(ChromotingDesktopNetworkMsg_CreateSharedBuffer, IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer, int /* id */) -IPC_STRUCT_TRAITS_BEGIN(webrtc::MouseCursorShape) - IPC_STRUCT_TRAITS_MEMBER(size) - IPC_STRUCT_TRAITS_MEMBER(hotspot) - IPC_STRUCT_TRAITS_MEMBER(data) -IPC_STRUCT_TRAITS_END() - // Serialized webrtc::DesktopFrame. IPC_STRUCT_BEGIN(SerializedDesktopFrame) // ID of the shared memory buffer containing the pixels. @@ -179,8 +173,8 @@ IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_CaptureCompleted, SerializedDesktopFrame /* frame */ ) // Carries a cursor share update from the desktop session agent to the client. -IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_CursorShapeChanged, - webrtc::MouseCursorShape /* cursor_shape */ ) +IPC_MESSAGE_CONTROL1(ChromotingDesktopNetworkMsg_MouseCursor, + webrtc::MouseCursor /* cursor */ ) // Carries a clipboard event from the desktop session agent to the client. // |serialized_event| is a serialized protocol::ClipboardEvent. diff --git a/remoting/host/chromoting_param_traits.cc b/remoting/host/chromoting_param_traits.cc index 8b10a7f764308..8050dc1985937 100644 --- a/remoting/host/chromoting_param_traits.cc +++ b/remoting/host/chromoting_param_traits.cc @@ -5,6 +5,7 @@ #include "remoting/host/chromoting_param_traits.h" #include "base/strings/stringprintf.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" namespace IPC { @@ -87,6 +88,72 @@ void ParamTraits::Log(const webrtc::DesktopRect& p, p.left(), p.top(), p.right(), p.bottom())); } +// static +void ParamTraits::Write( + Message* m, + const webrtc::MouseCursor& p) { + ParamTraits::Write(m, p.image()->size()); + + // Data is serialized in such a way that size is exactly width * height * + // |kBytesPerPixel|. + std::string data; + uint8_t* current_row = p.image()->data(); + for (int y = 0; y < p.image()->size().height(); ++y) { + data.append(current_row, + current_row + p.image()->size().width() * + webrtc::DesktopFrame::kBytesPerPixel); + current_row += p.image()->stride(); + } + m->WriteData(reinterpret_cast(p.image()->data()), data.size()); + + ParamTraits::Write(m, p.hotspot()); +} + +// static +bool ParamTraits::Read( + const Message* m, + PickleIterator* iter, + webrtc::MouseCursor* r) { + webrtc::DesktopSize size; + if (!ParamTraits::Read(m, iter, &size) || + size.width() <= 0 || size.width() > (SHRT_MAX / 2) || + size.height() <= 0 || size.height() > (SHRT_MAX / 2)) { + return false; + } + + const int expected_length = + size.width() * size.height() * webrtc::DesktopFrame::kBytesPerPixel; + + const char* data; + int data_length; + if (!m->ReadData(iter, &data, &data_length) || + data_length != expected_length) { + return false; + } + + webrtc::DesktopVector hotspot; + if (!ParamTraits::Read(m, iter, &hotspot)) + return false; + + webrtc::BasicDesktopFrame* image = new webrtc::BasicDesktopFrame(size); + memcpy(image->data(), data, data_length); + + r->set_image(image); + r->set_hotspot(hotspot); + return true; +} + +// static +void ParamTraits::Log( + const webrtc::MouseCursor& p, + std::string* l) { + l->append(base::StringPrintf( + "webrtc::DesktopRect{image(%d, %d), hotspot(%d, %d)}", + p.image()->size().width(), p.image()->size().height(), + p.hotspot().x(), p.hotspot().y())); +} + + // static void ParamTraits::Write( Message* m, diff --git a/remoting/host/chromoting_param_traits.h b/remoting/host/chromoting_param_traits.h index 7925918d76a93..35ca3bcd74790 100644 --- a/remoting/host/chromoting_param_traits.h +++ b/remoting/host/chromoting_param_traits.h @@ -8,7 +8,9 @@ #include "ipc/ipc_message.h" #include "ipc/ipc_param_traits.h" #include "remoting/host/screen_resolution.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" namespace IPC { @@ -36,6 +38,14 @@ struct ParamTraits { static void Log(const param_type& p, std::string* l); }; +template <> +struct ParamTraits { + typedef webrtc::MouseCursor param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, PickleIterator* iter, param_type* r); + static void Log(const param_type& p, std::string* l); +}; + template <> struct ParamTraits { typedef remoting::ScreenResolution param_type; diff --git a/remoting/host/client_session.cc b/remoting/host/client_session.cc index b5e375e232279..31c3bf145d357 100644 --- a/remoting/host/client_session.cc +++ b/remoting/host/client_session.cc @@ -28,7 +28,7 @@ #include "remoting/protocol/client_stub.h" #include "remoting/protocol/clipboard_thread_proxy.h" #include "remoting/protocol/pairing_registry.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" // Default DPI to assume for old clients that use notifyClientDimensions. const int kDefaultDPI = 96; @@ -453,9 +453,9 @@ void ClientSession::ResetVideoPipeline() { video_scheduler_ = NULL; } - // Create VideoEncoder and ScreenCapturer to match the session's video channel - // configuration. - scoped_ptr video_capturer = + // Create VideoEncoder and DesktopCapturer to match the session's video + // channel configuration. + scoped_ptr video_capturer = extension_manager_->OnCreateVideoCapturer( desktop_environment_->CreateVideoCapturer()); scoped_ptr video_encoder = @@ -472,6 +472,7 @@ void ClientSession::ResetVideoPipeline() { video_encode_task_runner_, network_task_runner_, video_capturer.Pass(), + desktop_environment_->CreateMouseCursorMonitor(), video_encoder.Pass(), connection_->client_stub(), &mouse_clamping_filter_); diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc index d0e1fc6a0e73b..b3dfa45396606 100644 --- a/remoting/host/client_session_unittest.cc +++ b/remoting/host/client_session_unittest.cc @@ -15,8 +15,9 @@ #include "remoting/host/audio_capturer.h" #include "remoting/host/client_session.h" #include "remoting/host/desktop_environment.h" +#include "remoting/host/fake_desktop_capturer.h" #include "remoting/host/fake_host_extension.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_mouse_cursor_monitor.h" #include "remoting/host/host_extension.h" #include "remoting/host/host_extension_session.h" #include "remoting/host/host_mock_objects.h" @@ -134,7 +135,7 @@ class ClientSessionTest : public testing::Test { void StopClientSession(); protected: - // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock + // Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock // DesktopEnvironmentFactory::Create(). DesktopEnvironment* CreateDesktopEnvironment(); @@ -142,9 +143,13 @@ class ClientSessionTest : public testing::Test { // DesktopEnvironment::CreateInputInjector(). InputInjector* CreateInputInjector(); - // Creates a fake webrtc::ScreenCapturer, to mock + // Creates a fake webrtc::DesktopCapturer, to mock // DesktopEnvironment::CreateVideoCapturer(). - webrtc::ScreenCapturer* CreateVideoCapturer(); + webrtc::DesktopCapturer* CreateVideoCapturer(); + + // Creates a MockMouseCursorMonitor, to mock + // DesktopEnvironment::CreateMouseCursorMonitor + webrtc::MouseCursorMonitor* CreateMouseCursorMonitor(); // Notifies the client session that the client connection has been // authenticated and channels have been connected. This effectively enables @@ -279,6 +284,9 @@ DesktopEnvironment* ClientSessionTest::CreateDesktopEnvironment() { .Times(AtMost(1)); EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) .WillRepeatedly(Invoke(this, &ClientSessionTest::CreateVideoCapturer)); + EXPECT_CALL(*desktop_environment, CreateMouseCursorMonitorPtr()) + .WillRepeatedly( + Invoke(this, &ClientSessionTest::CreateMouseCursorMonitor)); EXPECT_CALL(*desktop_environment, GetCapabilities()) .Times(AtMost(1)) .WillOnce(Return(kDefaultTestCapability)); @@ -293,8 +301,12 @@ InputInjector* ClientSessionTest::CreateInputInjector() { return input_injector_.release(); } -webrtc::ScreenCapturer* ClientSessionTest::CreateVideoCapturer() { - return new FakeScreenCapturer(); +webrtc::DesktopCapturer* ClientSessionTest::CreateVideoCapturer() { + return new FakeDesktopCapturer(); +} + +webrtc::MouseCursorMonitor* ClientSessionTest::CreateMouseCursorMonitor() { + return new FakeMouseCursorMonitor(); } void ClientSessionTest::ConnectClientSession() { @@ -584,9 +596,9 @@ TEST_F(ClientSessionTest, ClampMouseEvents) { Expectation connected = authenticated; int input_x[3] = { -999, 100, 999 }; - int expected_x[3] = { 0, 100, FakeScreenCapturer::kWidth - 1 }; + int expected_x[3] = { 0, 100, FakeDesktopCapturer::kWidth - 1 }; int input_y[3] = { -999, 50, 999 }; - int expected_y[3] = { 0, 50, FakeScreenCapturer::kHeight - 1 }; + int expected_y[3] = { 0, 50, FakeDesktopCapturer::kHeight - 1 }; protocol::MouseEvent expected_event; for (int j = 0; j < 3; j++) { diff --git a/remoting/host/desktop_environment.h b/remoting/host/desktop_environment.h index c289891293d39..dbeebb886a91c 100644 --- a/remoting/host/desktop_environment.h +++ b/remoting/host/desktop_environment.h @@ -18,7 +18,8 @@ class SingleThreadTaskRunner; } // namespace base namespace webrtc { -class ScreenCapturer; +class DesktopCapturer; +class MouseCursorMonitor; } // namespace webrtc namespace remoting { @@ -44,7 +45,8 @@ class DesktopEnvironment { virtual scoped_ptr CreateAudioCapturer() = 0; virtual scoped_ptr CreateInputInjector() = 0; virtual scoped_ptr CreateScreenControls() = 0; - virtual scoped_ptr CreateVideoCapturer() = 0; + virtual scoped_ptr CreateVideoCapturer() = 0; + virtual scoped_ptr CreateMouseCursorMonitor() = 0; // Returns the set of all capabilities supported by |this|. virtual std::string GetCapabilities() const = 0; diff --git a/remoting/host/desktop_process_unittest.cc b/remoting/host/desktop_process_unittest.cc index 55e547d096b67..7a188a8b50e2a 100644 --- a/remoting/host/desktop_process_unittest.cc +++ b/remoting/host/desktop_process_unittest.cc @@ -19,7 +19,7 @@ #include "remoting/base/auto_thread_task_runner.h" #include "remoting/host/chromoting_messages.h" #include "remoting/host/desktop_process.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_desktop_capturer.h" #include "remoting/host/host_exit_codes.h" #include "remoting/host/host_mock_objects.h" #include "remoting/host/screen_resolution.h" @@ -105,7 +105,7 @@ class DesktopProcessTest : public testing::Test { void ConnectNetworkChannel(IPC::PlatformFileForTransit desktop_process); void OnDesktopAttached(IPC::PlatformFileForTransit desktop_process); - // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock + // Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock // DesktopEnvironmentFactory::Create(). DesktopEnvironment* CreateDesktopEnvironment(); @@ -113,9 +113,9 @@ class DesktopProcessTest : public testing::Test { // DesktopEnvironment::CreateInputInjector(). InputInjector* CreateInputInjector(); - // Creates a fake webrtc::ScreenCapturer, to mock + // Creates a fake webrtc::DesktopCapturer, to mock // DesktopEnvironment::CreateVideoCapturer(). - webrtc::ScreenCapturer* CreateVideoCapturer(); + webrtc::DesktopCapturer* CreateVideoCapturer(); // Disconnects the daemon-to-desktop channel causing the desktop process to // exit. @@ -218,8 +218,8 @@ InputInjector* DesktopProcessTest::CreateInputInjector() { return input_injector; } -webrtc::ScreenCapturer* DesktopProcessTest::CreateVideoCapturer() { - return new FakeScreenCapturer(); +webrtc::DesktopCapturer* DesktopProcessTest::CreateVideoCapturer() { + return new FakeDesktopCapturer(); } void DesktopProcessTest::DisconnectChannels() { diff --git a/remoting/host/desktop_session_agent.cc b/remoting/host/desktop_session_agent.cc index a7ed484a9da5b..1f75d947e59f2 100644 --- a/remoting/host/desktop_session_agent.cc +++ b/remoting/host/desktop_session_agent.cc @@ -27,6 +27,7 @@ #include "remoting/protocol/input_event_tracker.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" #include "third_party/webrtc/modules/desktop_capture/shared_memory.h" namespace remoting { @@ -298,10 +299,12 @@ void DesktopSessionAgent::OnStartSessionAgent( FROM_HERE, base::Bind(&DesktopSessionAgent::StartAudioCapturer, this)); } - // Start the video capturer. + // Start the video capturer and mouse cursor monitor. video_capturer_ = desktop_environment_->CreateVideoCapturer(); + mouse_cursor_monitor_ = desktop_environment_->CreateMouseCursorMonitor(); video_capture_task_runner_->PostTask( - FROM_HERE, base::Bind(&DesktopSessionAgent::StartVideoCapturer, this)); + FROM_HERE, base::Bind( + &DesktopSessionAgent::StartVideoCapturerAndMouseMonitor, this)); } void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) { @@ -327,14 +330,20 @@ void DesktopSessionAgent::OnCaptureCompleted(webrtc::DesktopFrame* frame) { new ChromotingDesktopNetworkMsg_CaptureCompleted(serialized_frame)); } -void DesktopSessionAgent::OnCursorShapeChanged( - webrtc::MouseCursorShape* cursor_shape) { +void DesktopSessionAgent::OnMouseCursor(webrtc::MouseCursor* cursor) { DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); - scoped_ptr owned_cursor(cursor_shape); + scoped_ptr owned_cursor(cursor); - SendToNetwork(new ChromotingDesktopNetworkMsg_CursorShapeChanged( - *cursor_shape)); + SendToNetwork( + new ChromotingDesktopNetworkMsg_MouseCursor(*owned_cursor)); +} + +void DesktopSessionAgent::OnMouseCursorPosition( + webrtc::MouseCursorMonitor::CursorState state, + const webrtc::DesktopVector& position) { + // We're not subscribing to mouse position changes. + NOTREACHED(); } void DesktopSessionAgent::InjectClipboardEvent( @@ -417,7 +426,8 @@ void DesktopSessionAgent::Stop() { // Stop the video capturer. video_capture_task_runner_->PostTask( - FROM_HERE, base::Bind(&DesktopSessionAgent::StopVideoCapturer, this)); + FROM_HERE, base::Bind( + &DesktopSessionAgent::StopVideoCapturerAndMouseMonitor, this)); } } @@ -429,11 +439,13 @@ void DesktopSessionAgent::OnCaptureFrame() { return; } - // webrtc::ScreenCapturer supports a very few (currently 2) outstanding + mouse_cursor_monitor_->Capture(); + + // webrtc::DesktopCapturer supports a very few (currently 2) outstanding // capture requests. The requests are serialized on // |video_capture_task_runner()| task runner. If the client issues more // requests, pixel data in captured frames will likely be corrupted but - // stability of webrtc::ScreenCapturer will not be affected. + // stability of webrtc::DesktopCapturer will not be affected. video_capturer_->Capture(webrtc::DesktopRegion()); } @@ -545,20 +557,24 @@ void DesktopSessionAgent::StopAudioCapturer() { audio_capturer_.reset(); } -void DesktopSessionAgent::StartVideoCapturer() { +void DesktopSessionAgent::StartVideoCapturerAndMouseMonitor() { DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); if (video_capturer_) { - video_capturer_->SetMouseShapeObserver(this); video_capturer_->Start(this); } + + if (mouse_cursor_monitor_) { + mouse_cursor_monitor_->Init(this, webrtc::MouseCursorMonitor::SHAPE_ONLY); + } } -void DesktopSessionAgent::StopVideoCapturer() { +void DesktopSessionAgent::StopVideoCapturerAndMouseMonitor() { DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); video_capturer_.reset(); last_frame_.reset(); + mouse_cursor_monitor_.reset(); // Video capturer must delete all buffers. DCHECK_EQ(shared_buffers_, 0); diff --git a/remoting/host/desktop_session_agent.h b/remoting/host/desktop_session_agent.h index 2f981948bb8e2..b971b6fff7376 100644 --- a/remoting/host/desktop_session_agent.h +++ b/remoting/host/desktop_session_agent.h @@ -17,8 +17,9 @@ #include "ipc/ipc_platform_file.h" #include "remoting/host/client_session_control.h" #include "remoting/protocol/clipboard_stub.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" namespace IPC { class ChannelProxy; @@ -47,7 +48,7 @@ class DesktopSessionAgent : public base::RefCountedThreadSafe, public IPC::Listener, public webrtc::DesktopCapturer::Callback, - public webrtc::ScreenCapturer::MouseShapeObserver, + public webrtc::MouseCursorMonitor::Callback, public ClientSessionControl { public: class Delegate { @@ -78,9 +79,11 @@ class DesktopSessionAgent virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; - // webrtc::ScreenCapturer::MouseShapeObserver implementation. - virtual void OnCursorShapeChanged( - webrtc::MouseCursorShape* cursor_shape) OVERRIDE; + // webrtc::MouseCursorMonitor::Callback implementation. + virtual void OnMouseCursor(webrtc::MouseCursor* cursor) OVERRIDE; + virtual void OnMouseCursorPosition( + webrtc::MouseCursorMonitor::CursorState state, + const webrtc::DesktopVector& position) OVERRIDE; // Forwards a local clipboard event though the IPC channel to the network // process. @@ -141,11 +144,13 @@ class DesktopSessionAgent // Posted to |audio_capture_task_runner_| to stop the audio capturer. void StopAudioCapturer(); - // Posted to |video_capture_task_runner_| to start the video capturer. - void StartVideoCapturer(); + // Posted to |video_capture_task_runner_| to start the video capturer and the + // mouse cursor monitor. + void StartVideoCapturerAndMouseMonitor(); - // Posted to |video_capture_task_runner_| to stop the video capturer. - void StopVideoCapturer(); + // Posted to |video_capture_task_runner_| to stop the video capturer and the + // mouse cursor monitor. + void StopVideoCapturerAndMouseMonitor(); private: class SharedBuffer; @@ -214,7 +219,10 @@ class DesktopSessionAgent bool started_; // Captures the screen. - scoped_ptr video_capturer_; + scoped_ptr video_capturer_; + + // Captures mouse shapes. + scoped_ptr mouse_cursor_monitor_; // Keep reference to the last frame sent to make sure shared buffer is alive // before it's received. diff --git a/remoting/host/desktop_session_proxy.cc b/remoting/host/desktop_session_proxy.cc index 7904a2db52094..4cda02a53e978 100644 --- a/remoting/host/desktop_session_proxy.cc +++ b/remoting/host/desktop_session_proxy.cc @@ -18,6 +18,7 @@ #include "remoting/host/desktop_session_connector.h" #include "remoting/host/ipc_audio_capturer.h" #include "remoting/host/ipc_input_injector.h" +#include "remoting/host/ipc_mouse_cursor_monitor.h" #include "remoting/host/ipc_screen_controls.h" #include "remoting/host/ipc_video_frame_capturer.h" #include "remoting/proto/audio.pb.h" @@ -25,6 +26,7 @@ #include "remoting/proto/event.pb.h" #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" #include "third_party/webrtc/modules/desktop_capture/shared_memory.h" #if defined(OS_WIN) @@ -137,10 +139,16 @@ scoped_ptr DesktopSessionProxy::CreateScreenControls() { return scoped_ptr(new IpcScreenControls(this)); } -scoped_ptr DesktopSessionProxy::CreateVideoCapturer() { +scoped_ptr DesktopSessionProxy::CreateVideoCapturer() { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - return scoped_ptr(new IpcVideoFrameCapturer(this)); + return scoped_ptr(new IpcVideoFrameCapturer(this)); +} + +scoped_ptr + DesktopSessionProxy::CreateMouseCursorMonitor() { + return scoped_ptr( + new IpcMouseCursorMonitor(this)); } std::string DesktopSessionProxy::GetCapabilities() const { @@ -182,8 +190,8 @@ bool DesktopSessionProxy::OnMessageReceived(const IPC::Message& message) { OnAudioPacket) IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CaptureCompleted, OnCaptureCompleted) - IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CursorShapeChanged, - OnCursorShapeChanged) + IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_MouseCursor, + OnMouseCursor) IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_CreateSharedBuffer, OnCreateSharedBuffer) IPC_MESSAGE_HANDLER(ChromotingDesktopNetworkMsg_ReleaseSharedBuffer, @@ -316,6 +324,13 @@ void DesktopSessionProxy::SetVideoCapturer( video_capturer_ = video_capturer; } +void DesktopSessionProxy::SetMouseCursorMonitor( + const base::WeakPtr& mouse_cursor_monitor) { + DCHECK(video_capture_task_runner_->BelongsToCurrentThread()); + + mouse_cursor_monitor_ = mouse_cursor_monitor; +} + void DesktopSessionProxy::DisconnectSession() { DCHECK(caller_task_runner_->BelongsToCurrentThread()); @@ -502,11 +517,12 @@ void DesktopSessionProxy::OnCaptureCompleted( PostCaptureCompleted(frame.Pass()); } -void DesktopSessionProxy::OnCursorShapeChanged( - const webrtc::MouseCursorShape& cursor_shape) { +void DesktopSessionProxy::OnMouseCursor( + const webrtc::MouseCursor& mouse_cursor) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); - PostCursorShape(scoped_ptr( - new webrtc::MouseCursorShape(cursor_shape))); + scoped_ptr cursor( + webrtc::MouseCursor::CopyOf(mouse_cursor)); + PostMouseCursor(cursor.Pass()); } void DesktopSessionProxy::OnInjectClipboardEvent( @@ -534,14 +550,14 @@ void DesktopSessionProxy::PostCaptureCompleted( base::Passed(&frame))); } -void DesktopSessionProxy::PostCursorShape( - scoped_ptr cursor_shape) { +void DesktopSessionProxy::PostMouseCursor( + scoped_ptr mouse_cursor) { DCHECK(caller_task_runner_->BelongsToCurrentThread()); video_capture_task_runner_->PostTask( FROM_HERE, - base::Bind(&IpcVideoFrameCapturer::OnCursorShapeChanged, video_capturer_, - base::Passed(&cursor_shape))); + base::Bind(&IpcMouseCursorMonitor::OnMouseCursor, mouse_cursor_monitor_, + base::Passed(&mouse_cursor))); } void DesktopSessionProxy::SendToDesktop(IPC::Message* message) { diff --git a/remoting/host/desktop_session_proxy.h b/remoting/host/desktop_session_proxy.h index 1414c611a18e2..941b4e5e0138c 100644 --- a/remoting/host/desktop_session_proxy.h +++ b/remoting/host/desktop_session_proxy.h @@ -31,6 +31,10 @@ class ChannelProxy; class Message; } // namespace IPC +namespace webrtc { +class MouseCursor; +} // namespace webrtc + struct SerializedDesktopFrame; namespace remoting { @@ -41,6 +45,7 @@ class ClientSessionControl; class DesktopSessionConnector; struct DesktopSessionProxyTraits; class IpcAudioCapturer; +class IpcMouseCursorMonitor; class IpcVideoFrameCapturer; class ScreenControls; @@ -76,7 +81,8 @@ class DesktopSessionProxy scoped_ptr CreateAudioCapturer(); scoped_ptr CreateInputInjector(); scoped_ptr CreateScreenControls(); - scoped_ptr CreateVideoCapturer(); + scoped_ptr CreateVideoCapturer(); + scoped_ptr CreateMouseCursorMonitor(); std::string GetCapabilities() const; void SetCapabilities(const std::string& capabilities); @@ -100,7 +106,7 @@ class DesktopSessionProxy // on the |audio_capture_task_runner_| thread. void SetAudioCapturer(const base::WeakPtr& audio_capturer); - // APIs used to implement the webrtc::ScreenCapturer interface. These must be + // APIs used to implement the webrtc::DesktopCapturer interface. These must be // called on the |video_capture_task_runner_| thread. void CaptureFrame(); @@ -109,6 +115,11 @@ class DesktopSessionProxy void SetVideoCapturer( const base::WeakPtr video_capturer); + // Stores |mouse_cursor_monitor| to be used to post mouse cursor changes. + // Called on the |video_capture_task_runner_| thread. + void SetMouseCursorMonitor( + const base::WeakPtr& mouse_cursor_monitor); + // APIs used to implement the InputInjector interface. void InjectClipboardEvent(const protocol::ClipboardEvent& event); void InjectKeyEvent(const protocol::KeyEvent& event); @@ -146,8 +157,8 @@ class DesktopSessionProxy // Handles CaptureCompleted notification from the desktop session agent. void OnCaptureCompleted(const SerializedDesktopFrame& serialized_frame); - // Handles CursorShapeChanged notification from the desktop session agent. - void OnCursorShapeChanged(const webrtc::MouseCursorShape& cursor_shape); + // Handles MouseCursor notification from the desktop session agent. + void OnMouseCursor(const webrtc::MouseCursor& mouse_cursor); // Handles InjectClipboardEvent request from the desktop integration process. void OnInjectClipboardEvent(const std::string& serialized_event); @@ -156,9 +167,9 @@ class DesktopSessionProxy // passing |frame|. void PostCaptureCompleted(scoped_ptr frame); - // Posts OnCursorShapeChanged() to |video_capturer_| on the video thread, - // passing |cursor_shape|. - void PostCursorShape(scoped_ptr cursor_shape); + // Posts OnMouseCursor() to |mouse_cursor_monitor_| on the video thread, + // passing |mouse_cursor|. + void PostMouseCursor(scoped_ptr mouse_cursor); // Sends a message to the desktop session agent. The message is silently // deleted if the channel is broken. @@ -191,6 +202,9 @@ class DesktopSessionProxy // Points to the video capturer receiving captured video frames. base::WeakPtr video_capturer_; + // Points to the mouse cursor monitor receiving mouse cursor changes. + base::WeakPtr mouse_cursor_monitor_; + // IPC channel to the desktop session agent. scoped_ptr desktop_channel_; diff --git a/remoting/host/fake_desktop_capturer.cc b/remoting/host/fake_desktop_capturer.cc new file mode 100644 index 0000000000000..a90935f9b99f7 --- /dev/null +++ b/remoting/host/fake_desktop_capturer.cc @@ -0,0 +1,151 @@ +// 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 "remoting/host/fake_desktop_capturer.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/time/time.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" + +namespace remoting { + +// FakeDesktopCapturer generates a white picture of size kWidth x kHeight +// with a rectangle of size kBoxWidth x kBoxHeight. The rectangle moves kSpeed +// pixels per frame along both axes, and bounces off the sides of the screen. +static const int kWidth = FakeDesktopCapturer::kWidth; +static const int kHeight = FakeDesktopCapturer::kHeight; +static const int kBoxWidth = 140; +static const int kBoxHeight = 140; +static const int kSpeed = 20; + +COMPILE_ASSERT(kBoxWidth < kWidth && kBoxHeight < kHeight, bad_box_size); +COMPILE_ASSERT((kBoxWidth % kSpeed == 0) && (kWidth % kSpeed == 0) && + (kBoxHeight % kSpeed == 0) && (kHeight % kSpeed == 0), + sizes_must_be_multiple_of_kSpeed); + +namespace { + +class DefaultFrameGenerator + : public base::RefCountedThreadSafe { + public: + DefaultFrameGenerator() + : bytes_per_row_(0), + box_pos_x_(0), + box_pos_y_(0), + box_speed_x_(kSpeed), + box_speed_y_(kSpeed), + first_frame_(true) {} + + scoped_ptr GenerateFrame( + webrtc::DesktopCapturer::Callback* callback); + + private: + friend class base::RefCountedThreadSafe; + ~DefaultFrameGenerator() {} + + webrtc::DesktopSize size_; + int bytes_per_row_; + int box_pos_x_; + int box_pos_y_; + int box_speed_x_; + int box_speed_y_; + bool first_frame_; + + DISALLOW_COPY_AND_ASSIGN(DefaultFrameGenerator); +}; + +scoped_ptr DefaultFrameGenerator::GenerateFrame( + webrtc::DesktopCapturer::Callback* callback) { + const int kBytesPerPixel = webrtc::DesktopFrame::kBytesPerPixel; + int buffer_size = kWidth * kHeight * kBytesPerPixel; + webrtc::SharedMemory* shared_memory = + callback->CreateSharedMemory(buffer_size); + scoped_ptr frame; + if (shared_memory) { + frame.reset(new webrtc::SharedMemoryDesktopFrame( + webrtc::DesktopSize(kWidth, kHeight), bytes_per_row_, shared_memory)); + } else { + frame.reset( + new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))); + } + + // Move the box. + bool old_box_pos_x = box_pos_x_; + box_pos_x_ += box_speed_x_; + if (box_pos_x_ + kBoxWidth >= kWidth || box_pos_x_ == 0) + box_speed_x_ = -box_speed_x_; + + bool old_box_pos_y = box_pos_y_; + box_pos_y_ += box_speed_y_; + if (box_pos_y_ + kBoxHeight >= kHeight || box_pos_y_ == 0) + box_speed_y_ = -box_speed_y_; + + memset(frame->data(), 0xff, kHeight * frame->stride()); + + // Draw rectangle with the following colors in its corners: + // cyan....yellow + // .............. + // blue.......red + uint8* row = frame->data() + + (box_pos_y_ * size_.width() + box_pos_x_) * kBytesPerPixel; + for (int y = 0; y < kBoxHeight; ++y) { + for (int x = 0; x < kBoxWidth; ++x) { + int r = x * 255 / kBoxWidth; + int g = y * 255 / kBoxHeight; + int b = 255 - (x * 255 / kBoxWidth); + row[x * kBytesPerPixel] = r; + row[x * kBytesPerPixel + 1] = g; + row[x * kBytesPerPixel + 2] = b; + row[x * kBytesPerPixel + 3] = 0xff; + } + row += frame->stride(); + } + + if (first_frame_) { + frame->mutable_updated_region()->SetRect( + webrtc::DesktopRect::MakeXYWH(0, 0, kWidth, kHeight)); + first_frame_ = false; + } else { + frame->mutable_updated_region()->SetRect(webrtc::DesktopRect::MakeXYWH( + old_box_pos_x, old_box_pos_y, kBoxWidth, kBoxHeight)); + frame->mutable_updated_region()->AddRect(webrtc::DesktopRect::MakeXYWH( + box_pos_x_, box_pos_y_, kBoxWidth, kBoxHeight)); + } + + return frame.Pass(); +} + +} // namespace + +FakeDesktopCapturer::FakeDesktopCapturer() + : callback_(NULL) { + frame_generator_ = base::Bind(&DefaultFrameGenerator::GenerateFrame, + new DefaultFrameGenerator()); +} + +FakeDesktopCapturer::~FakeDesktopCapturer() {} + +void FakeDesktopCapturer::set_frame_generator( + const FrameGenerator& frame_generator) { + DCHECK(!callback_); + frame_generator_ = frame_generator; +} + +void FakeDesktopCapturer::Start(Callback* callback) { + DCHECK(!callback_); + DCHECK(callback); + callback_ = callback; +} + +void FakeDesktopCapturer::Capture(const webrtc::DesktopRegion& region) { + base::Time capture_start_time = base::Time::Now(); + scoped_ptr frame = frame_generator_.Run(callback_); + frame->set_capture_time_ms( + (base::Time::Now() - capture_start_time).InMillisecondsRoundedUp()); + callback_->OnCaptureCompleted(frame.release()); +} + +} // namespace remoting diff --git a/remoting/host/fake_desktop_capturer.h b/remoting/host/fake_desktop_capturer.h new file mode 100644 index 0000000000000..b7dbc74037990 --- /dev/null +++ b/remoting/host/fake_desktop_capturer.h @@ -0,0 +1,50 @@ +// 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 REMOTING_HOST_FAKE_DESKTOP_CAPTURER_H_ +#define REMOTING_HOST_FAKE_DESKTOP_CAPTURER_H_ + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/media_export.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/screen_capture_frame_queue.h" + +namespace remoting { + +// A FakeDesktopCapturer generates artificial image for testing purpose. +// +// FakeDesktopCapturer is double-buffered as required by DesktopCapturer. +class FakeDesktopCapturer : public webrtc::DesktopCapturer { + public: + // By default FakeDesktopCapturer generates frames of size kWidth x kHeight, + // but custom frame generator set using set_frame_generator() may generate + // frames of different size. + static const int kWidth = 800; + static const int kHeight = 600; + + typedef base::Callback( + webrtc::DesktopCapturer::Callback* callback)> FrameGenerator; + + FakeDesktopCapturer(); + virtual ~FakeDesktopCapturer(); + + void set_frame_generator(const FrameGenerator& frame_generator); + + // webrtc::DesktopCapturer interface. + virtual void Start(Callback* callback) OVERRIDE; + virtual void Capture(const webrtc::DesktopRegion& rect) OVERRIDE; + + private: + FrameGenerator frame_generator_; + + Callback* callback_; + + DISALLOW_COPY_AND_ASSIGN(FakeDesktopCapturer); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_FAKE_DESKTOP_CAPTURER_H_ diff --git a/remoting/host/fake_desktop_environment.cc b/remoting/host/fake_desktop_environment.cc index 5e02ec8f54763..f399ae853103e 100644 --- a/remoting/host/fake_desktop_environment.cc +++ b/remoting/host/fake_desktop_environment.cc @@ -5,7 +5,7 @@ #include "remoting/host/fake_desktop_environment.h" #include "remoting/host/audio_capturer.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_desktop_capturer.h" #include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/input_injector.h" @@ -55,12 +55,17 @@ scoped_ptr FakeDesktopEnvironment::CreateScreenControls() { return scoped_ptr(new FakeScreenControls()); } -scoped_ptr +scoped_ptr FakeDesktopEnvironment::CreateVideoCapturer() { - scoped_ptr result(new FakeScreenCapturer()); + scoped_ptr result(new FakeDesktopCapturer()); if (!frame_generator_.is_null()) result->set_frame_generator(frame_generator_); - return result.PassAs(); + return result.PassAs(); +} + +scoped_ptr +FakeDesktopEnvironment::CreateMouseCursorMonitor() { + return scoped_ptr(new FakeMouseCursorMonitor()); } std::string FakeDesktopEnvironment::GetCapabilities() const { diff --git a/remoting/host/fake_desktop_environment.h b/remoting/host/fake_desktop_environment.h index 412702d3d7c61..91fd9bb47f689 100644 --- a/remoting/host/fake_desktop_environment.h +++ b/remoting/host/fake_desktop_environment.h @@ -6,7 +6,8 @@ #define REMOTING_HOST_FAKE_DESKTOP_ENVIRONMENT_H_ #include "remoting/host/desktop_environment.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_desktop_capturer.h" +#include "remoting/host/fake_mouse_cursor_monitor.h" #include "remoting/host/input_injector.h" #include "remoting/host/screen_controls.h" @@ -40,9 +41,10 @@ class FakeDesktopEnvironment : public DesktopEnvironment { FakeDesktopEnvironment(); virtual ~FakeDesktopEnvironment(); - // Sets frame generator to be used for FakeScreenCapturer created by + // Sets frame generator to be used for FakeDesktopCapturer created by // FakeDesktopEnvironment. - void set_frame_generator(FakeScreenCapturer::FrameGenerator frame_generator) { + void set_frame_generator( + FakeDesktopCapturer::FrameGenerator frame_generator) { frame_generator_ = frame_generator; } @@ -50,14 +52,16 @@ class FakeDesktopEnvironment : public DesktopEnvironment { virtual scoped_ptr CreateAudioCapturer() OVERRIDE; virtual scoped_ptr CreateInputInjector() OVERRIDE; virtual scoped_ptr CreateScreenControls() OVERRIDE; - virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateMouseCursorMonitor() + OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; virtual scoped_ptr CreateGnubbyAuthHandler( protocol::ClientStub* client_stub) OVERRIDE; private: - FakeScreenCapturer::FrameGenerator frame_generator_; + FakeDesktopCapturer::FrameGenerator frame_generator_; DISALLOW_COPY_AND_ASSIGN(FakeDesktopEnvironment); }; @@ -67,9 +71,10 @@ class FakeDesktopEnvironmentFactory : public DesktopEnvironmentFactory { FakeDesktopEnvironmentFactory(); virtual ~FakeDesktopEnvironmentFactory(); - // Sets frame generator to be used for FakeScreenCapturer created by + // Sets frame generator to be used for FakeDesktopCapturer created by // FakeDesktopEnvironment. - void set_frame_generator(FakeScreenCapturer::FrameGenerator frame_generator) { + void set_frame_generator( + FakeDesktopCapturer::FrameGenerator frame_generator) { frame_generator_ = frame_generator; } @@ -81,7 +86,7 @@ class FakeDesktopEnvironmentFactory : public DesktopEnvironmentFactory { virtual void SetEnableGnubbyAuth(bool enable) OVERRIDE; private: - FakeScreenCapturer::FrameGenerator frame_generator_; + FakeDesktopCapturer::FrameGenerator frame_generator_; DISALLOW_COPY_AND_ASSIGN(FakeDesktopEnvironmentFactory); }; diff --git a/remoting/host/fake_host_extension.cc b/remoting/host/fake_host_extension.cc index ed93791142ea6..7f5c8c3f12591 100644 --- a/remoting/host/fake_host_extension.cc +++ b/remoting/host/fake_host_extension.cc @@ -10,7 +10,7 @@ #include "remoting/codec/video_encoder.h" #include "remoting/host/host_extension_session.h" #include "remoting/proto/control.pb.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" namespace remoting { @@ -19,8 +19,8 @@ class FakeExtension::Session : public HostExtensionSession { Session(FakeExtension* extension, const std::string& message_type); virtual ~Session() {} - virtual scoped_ptr OnCreateVideoCapturer( - scoped_ptr encoder) OVERRIDE; + virtual scoped_ptr OnCreateVideoCapturer( + scoped_ptr encoder) OVERRIDE; virtual scoped_ptr OnCreateVideoEncoder( scoped_ptr encoder) OVERRIDE; virtual bool ModifiesVideoPipeline() const OVERRIDE; @@ -42,9 +42,9 @@ FakeExtension::Session::Session( message_type_(message_type) { } -scoped_ptr +scoped_ptr FakeExtension::Session::OnCreateVideoCapturer( - scoped_ptr capturer) { + scoped_ptr capturer) { extension_->has_wrapped_video_capturer_ = true; if (extension_->steal_video_capturer_) { capturer.reset(); diff --git a/remoting/host/fake_mouse_cursor_monitor.cc b/remoting/host/fake_mouse_cursor_monitor.cc new file mode 100644 index 0000000000000..5635a488dbb69 --- /dev/null +++ b/remoting/host/fake_mouse_cursor_monitor.cc @@ -0,0 +1,49 @@ +// 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 "remoting/host/fake_mouse_cursor_monitor.h" + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" + +namespace remoting { + +FakeMouseCursorMonitor::FakeMouseCursorMonitor() : callback_(NULL) {} + +FakeMouseCursorMonitor::~FakeMouseCursorMonitor() {} + +void FakeMouseCursorMonitor::Init( + webrtc::MouseCursorMonitor::Callback* callback, + webrtc::MouseCursorMonitor::Mode mode) { + DCHECK(!callback_); + DCHECK(callback); + + // Only shapes supported right now. + CHECK(mode == SHAPE_ONLY); + + callback_ = callback; +} + +void FakeMouseCursorMonitor::Capture() { + DCHECK(callback_); + + const int kWidth = 32; + const int kHeight = 32; + + scoped_ptr desktop_frame( + new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))); + memset(desktop_frame->data(), 0xFF, + webrtc::DesktopFrame::kBytesPerPixel * kWidth * kHeight); + + scoped_ptr mouse_cursor( + new webrtc::MouseCursor(desktop_frame.release(), + webrtc::DesktopVector())); + + callback_->OnMouseCursor(mouse_cursor.release()); +} + +} // namespace remoting diff --git a/remoting/host/fake_mouse_cursor_monitor.h b/remoting/host/fake_mouse_cursor_monitor.h new file mode 100644 index 0000000000000..0b46d727ede1f --- /dev/null +++ b/remoting/host/fake_mouse_cursor_monitor.h @@ -0,0 +1,28 @@ +// 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 REMOTING_HOST_FAKE_MOUSE_CURSOR_MONITOR_H_ +#define REMOTING_HOST_FAKE_MOUSE_CURSOR_MONITOR_H_ + +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" + +namespace remoting { + +class FakeMouseCursorMonitor : public webrtc::MouseCursorMonitor { + public: + FakeMouseCursorMonitor(); + virtual ~FakeMouseCursorMonitor(); + + virtual void Init(Callback* callback, Mode mode) OVERRIDE; + virtual void Capture() OVERRIDE; + + private: + Callback* callback_; + + DISALLOW_COPY_AND_ASSIGN(FakeMouseCursorMonitor); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_FAKE_MOUSE_CURSOR_MONITOR_H_ diff --git a/remoting/host/fake_screen_capturer.cc b/remoting/host/fake_screen_capturer.cc deleted file mode 100644 index 355b7963570fc..0000000000000 --- a/remoting/host/fake_screen_capturer.cc +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "remoting/host/fake_screen_capturer.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/memory/ref_counted.h" -#include "base/time/time.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" - -namespace remoting { - -// FakeScreenCapturer generates a white picture of size kWidth x kHeight -// with a rectangle of size kBoxWidth x kBoxHeight. The rectangle moves kSpeed -// pixels per frame along both axes, and bounces off the sides of the screen. -static const int kWidth = FakeScreenCapturer::kWidth; -static const int kHeight = FakeScreenCapturer::kHeight; -static const int kBoxWidth = 140; -static const int kBoxHeight = 140; -static const int kSpeed = 20; - -COMPILE_ASSERT(kBoxWidth < kWidth && kBoxHeight < kHeight, bad_box_size); -COMPILE_ASSERT((kBoxWidth % kSpeed == 0) && (kWidth % kSpeed == 0) && - (kBoxHeight % kSpeed == 0) && (kHeight % kSpeed == 0), - sizes_must_be_multiple_of_kSpeed); - -namespace { - -class DefaultFrameGenerator - : public base::RefCountedThreadSafe { - public: - DefaultFrameGenerator() - : bytes_per_row_(0), - box_pos_x_(0), - box_pos_y_(0), - box_speed_x_(kSpeed), - box_speed_y_(kSpeed), - first_frame_(true) {} - - scoped_ptr GenerateFrame( - webrtc::ScreenCapturer::Callback* callback); - - private: - friend class base::RefCountedThreadSafe; - ~DefaultFrameGenerator() {} - - webrtc::DesktopSize size_; - int bytes_per_row_; - int box_pos_x_; - int box_pos_y_; - int box_speed_x_; - int box_speed_y_; - bool first_frame_; - - DISALLOW_COPY_AND_ASSIGN(DefaultFrameGenerator); -}; - -scoped_ptr DefaultFrameGenerator::GenerateFrame( - webrtc::ScreenCapturer::Callback* callback) { - const int kBytesPerPixel = webrtc::DesktopFrame::kBytesPerPixel; - int buffer_size = kWidth * kHeight * kBytesPerPixel; - webrtc::SharedMemory* shared_memory = - callback->CreateSharedMemory(buffer_size); - scoped_ptr frame; - if (shared_memory) { - frame.reset(new webrtc::SharedMemoryDesktopFrame( - webrtc::DesktopSize(kWidth, kHeight), bytes_per_row_, shared_memory)); - } else { - frame.reset( - new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))); - } - - // Move the box. - bool old_box_pos_x = box_pos_x_; - box_pos_x_ += box_speed_x_; - if (box_pos_x_ + kBoxWidth >= kWidth || box_pos_x_ == 0) - box_speed_x_ = -box_speed_x_; - - bool old_box_pos_y = box_pos_y_; - box_pos_y_ += box_speed_y_; - if (box_pos_y_ + kBoxHeight >= kHeight || box_pos_y_ == 0) - box_speed_y_ = -box_speed_y_; - - memset(frame->data(), 0xff, kHeight * frame->stride()); - - // Draw rectangle with the following colors in its corners: - // cyan....yellow - // .............. - // blue.......red - uint8* row = frame->data() + - (box_pos_y_ * size_.width() + box_pos_x_) * kBytesPerPixel; - for (int y = 0; y < kBoxHeight; ++y) { - for (int x = 0; x < kBoxWidth; ++x) { - int r = x * 255 / kBoxWidth; - int g = y * 255 / kBoxHeight; - int b = 255 - (x * 255 / kBoxWidth); - row[x * kBytesPerPixel] = r; - row[x * kBytesPerPixel + 1] = g; - row[x * kBytesPerPixel + 2] = b; - row[x * kBytesPerPixel + 3] = 0xff; - } - row += frame->stride(); - } - - if (first_frame_) { - frame->mutable_updated_region()->SetRect( - webrtc::DesktopRect::MakeXYWH(0, 0, kWidth, kHeight)); - first_frame_ = false; - } else { - frame->mutable_updated_region()->SetRect(webrtc::DesktopRect::MakeXYWH( - old_box_pos_x, old_box_pos_y, kBoxWidth, kBoxHeight)); - frame->mutable_updated_region()->AddRect(webrtc::DesktopRect::MakeXYWH( - box_pos_x_, box_pos_y_, kBoxWidth, kBoxHeight)); - } - - return frame.Pass(); -} - -} // namespace - -FakeScreenCapturer::FakeScreenCapturer() - : callback_(NULL), - mouse_shape_observer_(NULL) { - frame_generator_ = base::Bind(&DefaultFrameGenerator::GenerateFrame, - new DefaultFrameGenerator()); -} - -FakeScreenCapturer::~FakeScreenCapturer() {} - -void FakeScreenCapturer::set_frame_generator( - const FrameGenerator& frame_generator) { - DCHECK(!callback_); - frame_generator_ = frame_generator; -} - -void FakeScreenCapturer::Start(Callback* callback) { - DCHECK(!callback_); - DCHECK(callback); - callback_ = callback; -} - -void FakeScreenCapturer::Capture(const webrtc::DesktopRegion& region) { - base::Time capture_start_time = base::Time::Now(); - scoped_ptr frame = frame_generator_.Run(callback_); - frame->set_capture_time_ms( - (base::Time::Now() - capture_start_time).InMillisecondsRoundedUp()); - callback_->OnCaptureCompleted(frame.release()); -} - -void FakeScreenCapturer::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - DCHECK(!mouse_shape_observer_); - DCHECK(mouse_shape_observer); - mouse_shape_observer_ = mouse_shape_observer; -} - -bool FakeScreenCapturer::GetScreenList(ScreenList* screens) { - NOTIMPLEMENTED(); - return false; -} - -bool FakeScreenCapturer::SelectScreen(webrtc::ScreenId id) { - NOTIMPLEMENTED(); - return false; -} - -} // namespace remoting diff --git a/remoting/host/fake_screen_capturer.h b/remoting/host/fake_screen_capturer.h deleted file mode 100644 index 2fd59cc2f95dc..0000000000000 --- a/remoting/host/fake_screen_capturer.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_HOST_FAKE_SCREEN_CAPTURER_H_ -#define REMOTING_HOST_FAKE_SCREEN_CAPTURER_H_ - -#include "base/callback.h" -#include "base/memory/scoped_ptr.h" -#include "media/base/media_export.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capture_frame_queue.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" - -namespace remoting { - -// A FakeScreenCapturer generates artificial image for testing purpose. -// -// FakeScreenCapturer is double-buffered as required by ScreenCapturer. -class FakeScreenCapturer : public webrtc::ScreenCapturer { - public: - // By default FakeScreenCapturer generates frames of size kWidth x kHeight, - // but custom frame generator set using set_frame_generator() may generate - // frames of different size. - static const int kWidth = 800; - static const int kHeight = 600; - - typedef base::Callback( - webrtc::ScreenCapturer::Callback* callback)> FrameGenerator; - - FakeScreenCapturer(); - virtual ~FakeScreenCapturer(); - - void set_frame_generator(const FrameGenerator& frame_generator); - - // webrtc::DesktopCapturer interface. - virtual void Start(Callback* callback) OVERRIDE; - virtual void Capture(const webrtc::DesktopRegion& rect) OVERRIDE; - - // webrtc::ScreenCapturer interface. - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; - virtual bool GetScreenList(ScreenList* screens) OVERRIDE; - virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE; - - private: - FrameGenerator frame_generator_; - - Callback* callback_; - MouseShapeObserver* mouse_shape_observer_; - - DISALLOW_COPY_AND_ASSIGN(FakeScreenCapturer); -}; - -} // namespace remoting - -#endif // REMOTING_HOST_FAKE_SCREEN_CAPTURER_H_ diff --git a/remoting/host/gnubby_auth_handler_posix.cc b/remoting/host/gnubby_auth_handler_posix.cc index f5673d33dd0ef..04afc74347b57 100644 --- a/remoting/host/gnubby_auth_handler_posix.cc +++ b/remoting/host/gnubby_auth_handler_posix.cc @@ -53,10 +53,10 @@ class CompareSocket { // Socket authentication function that only allows connections from callers with // the current uid. -bool MatchUid(uid_t user_id, gid_t) { - bool allowed = user_id == getuid(); +bool MatchUid(const net::UnixDomainServerSocket::Credentials& credentials) { + bool allowed = credentials.user_id == getuid(); if (!allowed) - HOST_LOG << "Refused socket connection from uid " << user_id; + HOST_LOG << "Refused socket connection from uid " << credentials.user_id; return allowed; } diff --git a/remoting/host/host_extension_session.cc b/remoting/host/host_extension_session.cc index 949b3b2ddd6b3..5f548655a38e4 100644 --- a/remoting/host/host_extension_session.cc +++ b/remoting/host/host_extension_session.cc @@ -5,12 +5,12 @@ #include "remoting/host/host_extension_session.h" #include "remoting/codec/video_encoder.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" namespace remoting { -scoped_ptr HostExtensionSession::OnCreateVideoCapturer( - scoped_ptr capturer) { +scoped_ptr HostExtensionSession::OnCreateVideoCapturer( + scoped_ptr capturer) { return capturer.Pass(); } diff --git a/remoting/host/host_extension_session.h b/remoting/host/host_extension_session.h index 1e71180e3a33f..a4a0bd710abd7 100644 --- a/remoting/host/host_extension_session.h +++ b/remoting/host/host_extension_session.h @@ -8,7 +8,7 @@ #include "base/memory/scoped_ptr.h" namespace webrtc { -class ScreenCapturer; +class DesktopCapturer; } namespace remoting { @@ -30,8 +30,8 @@ class HostExtensionSession { // Optional hook functions for HostExtensions which need to wrap or replace // parts of the video, audio, input, etc pipelines. // These are called in response to ResetVideoPipeline(). - virtual scoped_ptr OnCreateVideoCapturer( - scoped_ptr capturer); + virtual scoped_ptr OnCreateVideoCapturer( + scoped_ptr capturer); virtual scoped_ptr OnCreateVideoEncoder( scoped_ptr encoder); virtual bool ModifiesVideoPipeline() const; @@ -48,4 +48,3 @@ class HostExtensionSession { } // namespace remoting #endif // REMOTING_HOST_HOST_EXTENSION_SESSION_H_ - diff --git a/remoting/host/host_extension_session_manager.cc b/remoting/host/host_extension_session_manager.cc index a3446d1c8d7e9..31ed5905f7919 100644 --- a/remoting/host/host_extension_session_manager.cc +++ b/remoting/host/host_extension_session_manager.cc @@ -9,7 +9,7 @@ #include "remoting/host/client_session_control.h" #include "remoting/host/host_extension.h" #include "remoting/host/host_extension_session.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" namespace remoting { HostExtensionSessionManager::HostExtensionSessionManager( @@ -39,9 +39,9 @@ std::string HostExtensionSessionManager::GetCapabilities() { return capabilities; } -scoped_ptr +scoped_ptr HostExtensionSessionManager::OnCreateVideoCapturer( - scoped_ptr capturer) { + scoped_ptr capturer) { for(HostExtensionSessionList::const_iterator it = extension_sessions_.begin(); it != extension_sessions_.end(); ++it) { if ((*it)->ModifiesVideoPipeline()) { diff --git a/remoting/host/host_extension_session_manager.h b/remoting/host/host_extension_session_manager.h index 0c953e9869654..b84d5208509e6 100644 --- a/remoting/host/host_extension_session_manager.h +++ b/remoting/host/host_extension_session_manager.h @@ -12,7 +12,7 @@ #include "base/memory/scoped_vector.h" namespace webrtc { -class ScreenCapturer; +class DesktopCapturer; } namespace remoting { @@ -42,8 +42,8 @@ class HostExtensionSessionManager { // Calls the corresponding hook functions in each extension in turn, to give // them an opportunity to wrap or replace video components. - scoped_ptr OnCreateVideoCapturer( - scoped_ptr capturer); + scoped_ptr OnCreateVideoCapturer( + scoped_ptr capturer); scoped_ptr OnCreateVideoEncoder( scoped_ptr encoder); diff --git a/remoting/host/host_extension_session_manager_unittest.cc b/remoting/host/host_extension_session_manager_unittest.cc index 53df16af3b8ba..f2c875c562ffc 100644 --- a/remoting/host/host_extension_session_manager_unittest.cc +++ b/remoting/host/host_extension_session_manager_unittest.cc @@ -9,7 +9,7 @@ #include "remoting/proto/control.pb.h" #include "remoting/protocol/protocol_mock_objects.h" #include "testing/gtest/include/gtest/gtest.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" namespace remoting { @@ -107,7 +107,8 @@ TEST_F(HostExtensionSessionManagerTest, CanWrapVideoCapturer) { extension3_.set_steal_video_capturer(true); extension_manager.OnNegotiatedCapabilities(&client_stub_, "cap1"); - extension_manager.OnCreateVideoCapturer(scoped_ptr()); + extension_manager.OnCreateVideoCapturer( + scoped_ptr()); EXPECT_FALSE(extension1_.has_wrapped_video_encoder()); EXPECT_TRUE(extension1_.has_wrapped_video_capturer()); @@ -147,7 +148,8 @@ TEST_F(HostExtensionSessionManagerTest, RespectModifiesVideoPipeline) { extension2_.set_steal_video_capturer(true); extension_manager.OnNegotiatedCapabilities(&client_stub_, "cap1"); - extension_manager.OnCreateVideoCapturer(scoped_ptr()); + extension_manager.OnCreateVideoCapturer( + scoped_ptr()); extension_manager.OnCreateVideoEncoder(scoped_ptr()); EXPECT_FALSE(extension1_.has_wrapped_video_encoder()); diff --git a/remoting/host/host_main.cc b/remoting/host/host_main.cc index cd6705a660aa4..403ff64f03a04 100644 --- a/remoting/host/host_main.cc +++ b/remoting/host/host_main.cc @@ -76,7 +76,9 @@ const char kUsageMessage[] = " --host-config= - Specifies the host configuration.\n" " --help, -? - Print this message.\n" " --type - Specifies process type.\n" - " --version - Prints the host version and exits.\n"; + " --version - Prints the host version and exits.\n" + " --window-id= - Specifies a window to remote," + " instead of the whole desktop.\n"; void Usage(const base::FilePath& program_name) { printf(kUsageMessage, program_name.MaybeAsASCII().c_str()); diff --git a/remoting/host/host_mock_objects.cc b/remoting/host/host_mock_objects.cc index dcc2646e9f7f4..775314ce66fc5 100644 --- a/remoting/host/host_mock_objects.cc +++ b/remoting/host/host_mock_objects.cc @@ -14,7 +14,7 @@ #include "remoting/host/input_injector.h" #include "remoting/proto/event.pb.h" #include "remoting/protocol/transport.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" namespace remoting { @@ -34,9 +34,9 @@ scoped_ptr MockDesktopEnvironment::CreateScreenControls() { return scoped_ptr(CreateScreenControlsPtr()); } -scoped_ptr +scoped_ptr MockDesktopEnvironment::CreateVideoCapturer() { - return scoped_ptr(CreateVideoCapturerPtr()); + return scoped_ptr(CreateVideoCapturerPtr()); } scoped_ptr @@ -45,6 +45,11 @@ MockDesktopEnvironment::CreateGnubbyAuthHandler( return scoped_ptr(CreateGnubbyAuthHandlerPtr(client_stub)); } +scoped_ptr +MockDesktopEnvironment::CreateMouseCursorMonitor() { + return scoped_ptr(CreateMouseCursorMonitorPtr()); +} + MockDesktopEnvironmentFactory::MockDesktopEnvironmentFactory() {} MockDesktopEnvironmentFactory::~MockDesktopEnvironmentFactory() {} @@ -79,4 +84,8 @@ MockGnubbyAuthHandler::MockGnubbyAuthHandler() {} MockGnubbyAuthHandler::~MockGnubbyAuthHandler() {} +MockMouseCursorMonitor::MockMouseCursorMonitor() {} + +MockMouseCursorMonitor::~MockMouseCursorMonitor() {} + } // namespace remoting diff --git a/remoting/host/host_mock_objects.h b/remoting/host/host_mock_objects.h index 930828b6583b7..bbddd08625666 100644 --- a/remoting/host/host_mock_objects.h +++ b/remoting/host/host_mock_objects.h @@ -19,6 +19,7 @@ #include "remoting/host/screen_resolution.h" #include "remoting/proto/control.pb.h" #include "testing/gmock/include/gmock/gmock.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" namespace base { class SingleThreadTaskRunner; @@ -34,7 +35,8 @@ class MockDesktopEnvironment : public DesktopEnvironment { MOCK_METHOD0(CreateAudioCapturerPtr, AudioCapturer*()); MOCK_METHOD0(CreateInputInjectorPtr, InputInjector*()); MOCK_METHOD0(CreateScreenControlsPtr, ScreenControls*()); - MOCK_METHOD0(CreateVideoCapturerPtr, webrtc::ScreenCapturer*()); + MOCK_METHOD0(CreateVideoCapturerPtr, webrtc::DesktopCapturer*()); + MOCK_METHOD0(CreateMouseCursorMonitorPtr, webrtc::MouseCursorMonitor*()); MOCK_CONST_METHOD0(GetCapabilities, std::string()); MOCK_METHOD1(SetCapabilities, void(const std::string&)); MOCK_METHOD1(CreateGnubbyAuthHandlerPtr, GnubbyAuthHandler*( @@ -44,9 +46,11 @@ class MockDesktopEnvironment : public DesktopEnvironment { virtual scoped_ptr CreateAudioCapturer() OVERRIDE; virtual scoped_ptr CreateInputInjector() OVERRIDE; virtual scoped_ptr CreateScreenControls() OVERRIDE; - virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateVideoCapturer() OVERRIDE; virtual scoped_ptr CreateGnubbyAuthHandler( protocol::ClientStub* client_stub) OVERRIDE; + virtual scoped_ptr CreateMouseCursorMonitor() + OVERRIDE; }; class MockClientSessionControl : public ClientSessionControl { @@ -147,6 +151,18 @@ class MockGnubbyAuthHandler : public GnubbyAuthHandler { DISALLOW_COPY_AND_ASSIGN(MockGnubbyAuthHandler); }; +class MockMouseCursorMonitor : public webrtc::MouseCursorMonitor { + public: + MockMouseCursorMonitor(); + virtual ~MockMouseCursorMonitor(); + + MOCK_METHOD2(Init, void(Callback* callback, Mode mode)); + MOCK_METHOD0(Capture, void()); + + private: + DISALLOW_COPY_AND_ASSIGN(MockMouseCursorMonitor); +}; + } // namespace remoting #endif // REMOTING_HOST_HOST_MOCK_OBJECTS_H_ diff --git a/remoting/host/ipc_desktop_environment.cc b/remoting/host/ipc_desktop_environment.cc index 33327124c8002..c8d60f32212eb 100644 --- a/remoting/host/ipc_desktop_environment.cc +++ b/remoting/host/ipc_desktop_environment.cc @@ -19,6 +19,7 @@ #include "remoting/host/gnubby_auth_handler.h" #include "remoting/host/input_injector.h" #include "remoting/host/screen_controls.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" namespace remoting { @@ -57,7 +58,12 @@ scoped_ptr IpcDesktopEnvironment::CreateScreenControls() { return desktop_session_proxy_->CreateScreenControls(); } -scoped_ptr +scoped_ptr +IpcDesktopEnvironment::CreateMouseCursorMonitor() { + return desktop_session_proxy_->CreateMouseCursorMonitor(); +} + +scoped_ptr IpcDesktopEnvironment::CreateVideoCapturer() { return desktop_session_proxy_->CreateVideoCapturer(); } diff --git a/remoting/host/ipc_desktop_environment.h b/remoting/host/ipc_desktop_environment.h index bb68df181ba4d..ca3dfcdc5a224 100644 --- a/remoting/host/ipc_desktop_environment.h +++ b/remoting/host/ipc_desktop_environment.h @@ -52,7 +52,9 @@ class IpcDesktopEnvironment : public DesktopEnvironment { virtual scoped_ptr CreateAudioCapturer() OVERRIDE; virtual scoped_ptr CreateInputInjector() OVERRIDE; virtual scoped_ptr CreateScreenControls() OVERRIDE; - virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateMouseCursorMonitor() + OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; virtual scoped_ptr CreateGnubbyAuthHandler( diff --git a/remoting/host/ipc_desktop_environment_unittest.cc b/remoting/host/ipc_desktop_environment_unittest.cc index 38b8cd2fc0cc7..9e13e8f4dec8c 100644 --- a/remoting/host/ipc_desktop_environment_unittest.cc +++ b/remoting/host/ipc_desktop_environment_unittest.cc @@ -24,7 +24,8 @@ #include "remoting/host/desktop_session.h" #include "remoting/host/desktop_session_connector.h" #include "remoting/host/desktop_session_proxy.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_desktop_capturer.h" +#include "remoting/host/fake_mouse_cursor_monitor.h" #include "remoting/host/host_mock_objects.h" #include "remoting/host/ipc_desktop_environment.h" #include "remoting/protocol/protocol_mock_objects.h" @@ -129,7 +130,7 @@ class IpcDesktopEnvironmentTest : public testing::Test { bool virtual_terminal); void DisconnectTerminal(int terminal_id); - // Creates a DesktopEnvironment with a fake webrtc::ScreenCapturer, to mock + // Creates a DesktopEnvironment with a fake webrtc::DesktopCapturer, to mock // DesktopEnvironmentFactory::Create(). DesktopEnvironment* CreateDesktopEnvironment(); @@ -137,9 +138,13 @@ class IpcDesktopEnvironmentTest : public testing::Test { // DesktopEnvironment::CreateInputInjector(). InputInjector* CreateInputInjector(); - // Creates a fake webrtc::ScreenCapturer, to mock + // Creates a fake webrtc::DesktopCapturer, to mock // DesktopEnvironment::CreateVideoCapturer(). - webrtc::ScreenCapturer* CreateVideoCapturer(); + webrtc::DesktopCapturer* CreateVideoCapturer(); + + // Creates a MockMouseCursorMonitor, to mock + // DesktopEnvironment::CreateMouseCursorMonitor + webrtc::MouseCursorMonitor* CreateMouseCursorMonitor(); void DeleteDesktopEnvironment(); @@ -197,7 +202,7 @@ class IpcDesktopEnvironmentTest : public testing::Test { scoped_ptr screen_controls_; // The IPC screen capturer. - scoped_ptr video_capturer_; + scoped_ptr video_capturer_; // Represents the desktop process running in a user session. scoped_ptr desktop_process_; @@ -208,7 +213,7 @@ class IpcDesktopEnvironmentTest : public testing::Test { // The last |terminal_id| passed to ConnectTermina(); int terminal_id_; - webrtc::MockScreenCapturerCallback screen_capturer_callback_; + webrtc::MockScreenCapturerCallback desktop_capturer_callback_; MockClientSessionControl client_session_control_; base::WeakPtrFactory client_session_control_factory_; @@ -324,6 +329,10 @@ DesktopEnvironment* IpcDesktopEnvironmentTest::CreateDesktopEnvironment() { .Times(AtMost(1)) .WillOnce(Invoke( this, &IpcDesktopEnvironmentTest::CreateVideoCapturer)); + EXPECT_CALL(*desktop_environment, CreateMouseCursorMonitorPtr()) + .Times(AtMost(1)) + .WillOnce(Invoke( + this, &IpcDesktopEnvironmentTest::CreateMouseCursorMonitor)); EXPECT_CALL(*desktop_environment, GetCapabilities()) .Times(AtMost(1)); EXPECT_CALL(*desktop_environment, SetCapabilities(_)) @@ -343,8 +352,13 @@ InputInjector* IpcDesktopEnvironmentTest::CreateInputInjector() { return remote_input_injector_; } -webrtc::ScreenCapturer* IpcDesktopEnvironmentTest::CreateVideoCapturer() { - return new FakeScreenCapturer(); +webrtc::DesktopCapturer* IpcDesktopEnvironmentTest::CreateVideoCapturer() { + return new FakeDesktopCapturer(); +} + +webrtc::MouseCursorMonitor* +IpcDesktopEnvironmentTest::CreateMouseCursorMonitor() { + return new FakeMouseCursorMonitor(); } void IpcDesktopEnvironmentTest::DeleteDesktopEnvironment() { @@ -443,13 +457,13 @@ TEST_F(IpcDesktopEnvironmentTest, CaptureFrame) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); // Stop the test when the first frame is captured. - EXPECT_CALL(screen_capturer_callback_, OnCaptureCompleted(_)) + EXPECT_CALL(desktop_capturer_callback_, OnCaptureCompleted(_)) .WillOnce(DoAll( DeleteArg<0>(), InvokeWithoutArgs( @@ -472,7 +486,7 @@ TEST_F(IpcDesktopEnvironmentTest, Reattach) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -505,7 +519,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectClipboardEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -536,7 +550,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectKeyEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -567,7 +581,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectTextEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -597,7 +611,7 @@ TEST_F(IpcDesktopEnvironmentTest, InjectMouseEvent) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); @@ -628,7 +642,7 @@ TEST_F(IpcDesktopEnvironmentTest, SetScreenResolution) { // Start the input injector and screen capturer. input_injector_->Start(clipboard_stub.PassAs()); - video_capturer_->Start(&screen_capturer_callback_); + video_capturer_->Start(&desktop_capturer_callback_); // Run the message loop until the desktop is attached. setup_run_loop_->Run(); diff --git a/remoting/host/ipc_mouse_cursor_monitor.cc b/remoting/host/ipc_mouse_cursor_monitor.cc new file mode 100644 index 0000000000000..8db099eb3519c --- /dev/null +++ b/remoting/host/ipc_mouse_cursor_monitor.cc @@ -0,0 +1,42 @@ +// 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 "remoting/host/ipc_mouse_cursor_monitor.h" + +#include "remoting/host/desktop_session_proxy.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" + +namespace remoting { + +IpcMouseCursorMonitor::IpcMouseCursorMonitor( + scoped_refptr desktop_session_proxy) + : callback_(NULL), + desktop_session_proxy_(desktop_session_proxy), + weak_factory_(this) { +} + +IpcMouseCursorMonitor::~IpcMouseCursorMonitor() {} + +void IpcMouseCursorMonitor::Init(Callback* callback, Mode mode) { + DCHECK(!callback_); + DCHECK(callback); + DCHECK_EQ(webrtc::MouseCursorMonitor::SHAPE_ONLY, mode); + callback_ = callback; + desktop_session_proxy_->SetMouseCursorMonitor(weak_factory_.GetWeakPtr()); +} + +void IpcMouseCursorMonitor::Capture() { + // Ignore. DesktopSessionAgent will capture the cursor at the same time it + // captures a screen frame when |IpcVideoFrameCapturer::Capture()| is called. + // This saves an IPC roundtrip. +} + +void IpcMouseCursorMonitor::OnMouseCursor( + scoped_ptr cursor) { + DCHECK(callback_); + callback_->OnMouseCursor(cursor.release()); +} + +} // namespace remoting + diff --git a/remoting/host/ipc_mouse_cursor_monitor.h b/remoting/host/ipc_mouse_cursor_monitor.h new file mode 100644 index 0000000000000..c9581781ad462 --- /dev/null +++ b/remoting/host/ipc_mouse_cursor_monitor.h @@ -0,0 +1,48 @@ +// 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 REMOTING_HOST_IPC_MOUSE_CURSOR_MONITOR_H_ +#define REMOTING_HOST_IPC_MOUSE_CURSOR_MONITOR_H_ + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" + +namespace remoting { + +class DesktopSessionProxy; + +// Routes webrtc::MouseCursorMonitor calls through the IPC channel to the +// desktop session agent running in the desktop integration process. +class IpcMouseCursorMonitor : public webrtc::MouseCursorMonitor { + public: + explicit IpcMouseCursorMonitor( + scoped_refptr desktop_session_proxy); + virtual ~IpcMouseCursorMonitor(); + + // webrtc::MouseCursorMonitor interface. + virtual void Init(Callback* callback, Mode mode) OVERRIDE; + virtual void Capture() OVERRIDE; + + // Called when the cursor shape has changed. + void OnMouseCursor(scoped_ptr cursor); + + private: + // The callback passed to |webrtc::MouseCursorMonitor::Init()|. + webrtc::MouseCursorMonitor::Callback* callback_; + + // Wraps the IPC channel to the desktop session agent. + scoped_refptr desktop_session_proxy_; + + // Used to cancel tasks pending on the capturer when it is stopped. + base::WeakPtrFactory weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(IpcMouseCursorMonitor); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_IPC_MOUSE_CURSOR_MONITOR_H_ diff --git a/remoting/host/ipc_video_frame_capturer.cc b/remoting/host/ipc_video_frame_capturer.cc index c56c20e1f9fb0..7f39a3dc354e7 100644 --- a/remoting/host/ipc_video_frame_capturer.cc +++ b/remoting/host/ipc_video_frame_capturer.cc @@ -13,7 +13,6 @@ namespace remoting { IpcVideoFrameCapturer::IpcVideoFrameCapturer( scoped_refptr desktop_session_proxy) : callback_(NULL), - mouse_shape_observer_(NULL), desktop_session_proxy_(desktop_session_proxy), capture_pending_(false), weak_factory_(this) { @@ -29,23 +28,6 @@ void IpcVideoFrameCapturer::Start(Callback* callback) { desktop_session_proxy_->SetVideoCapturer(weak_factory_.GetWeakPtr()); } -void IpcVideoFrameCapturer::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - DCHECK(!mouse_shape_observer_); - DCHECK(mouse_shape_observer); - mouse_shape_observer_ = mouse_shape_observer; -} - -bool IpcVideoFrameCapturer::GetScreenList(ScreenList* screens) { - NOTIMPLEMENTED(); - return false; -} - -bool IpcVideoFrameCapturer::SelectScreen(webrtc::ScreenId id) { - NOTIMPLEMENTED(); - return false; -} - void IpcVideoFrameCapturer::Capture(const webrtc::DesktopRegion& region) { DCHECK(!capture_pending_); capture_pending_ = true; @@ -59,10 +41,4 @@ void IpcVideoFrameCapturer::OnCaptureCompleted( callback_->OnCaptureCompleted(frame.release()); } -void IpcVideoFrameCapturer::OnCursorShapeChanged( - scoped_ptr cursor_shape) { - if (mouse_shape_observer_) - mouse_shape_observer_->OnCursorShapeChanged(cursor_shape.release()); -} - } // namespace remoting diff --git a/remoting/host/ipc_video_frame_capturer.h b/remoting/host/ipc_video_frame_capturer.h index 616f0a2280d5d..396bdd5707a39 100644 --- a/remoting/host/ipc_video_frame_capturer.h +++ b/remoting/host/ipc_video_frame_capturer.h @@ -8,23 +8,15 @@ #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" #include "base/memory/scoped_ptr.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" - -namespace IPC { -class Message; -} // namespace IPC - -namespace media { -struct MouseCursorShape; -} // namespace media +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" namespace remoting { class DesktopSessionProxy; -// Routes webrtc::ScreenCapturer calls though the IPC channel to the desktop +// Routes webrtc::DesktopCapturer calls though the IPC channel to the desktop // session agent running in the desktop integration process. -class IpcVideoFrameCapturer : public webrtc::ScreenCapturer { +class IpcVideoFrameCapturer : public webrtc::DesktopCapturer { public: explicit IpcVideoFrameCapturer( scoped_refptr desktop_session_proxy); @@ -34,25 +26,12 @@ class IpcVideoFrameCapturer : public webrtc::ScreenCapturer { virtual void Start(Callback* callback) OVERRIDE; virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE; - // webrtc::ScreenCapturer interface. - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; - - virtual bool GetScreenList(ScreenList* screens) OVERRIDE; - - virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE; - // Called when a video |frame| has been captured. void OnCaptureCompleted(scoped_ptr frame); - // Called when the cursor shape has changed. - void OnCursorShapeChanged(scoped_ptr cursor_shape); - private: - // Points to the callback passed to webrtc::ScreenCapturer::Start(). - webrtc::ScreenCapturer::Callback* callback_; - - MouseShapeObserver* mouse_shape_observer_; + // Points to the callback passed to webrtc::DesktopCapturer::Start(). + webrtc::DesktopCapturer::Callback* callback_; // Wraps the IPC channel to the desktop session agent. scoped_refptr desktop_session_proxy_; diff --git a/remoting/host/me2me_desktop_environment.cc b/remoting/host/me2me_desktop_environment.cc index abf9a99c95307..e6f5fdca93de8 100644 --- a/remoting/host/me2me_desktop_environment.cc +++ b/remoting/host/me2me_desktop_environment.cc @@ -40,16 +40,6 @@ scoped_ptr Me2MeDesktopEnvironment::CreateScreenControls() { new ResizingHostObserver(DesktopResizer::Create())); } -scoped_ptr -Me2MeDesktopEnvironment::CreateVideoCapturer() { - DCHECK(caller_task_runner()->BelongsToCurrentThread()); - webrtc::DesktopCaptureOptions options = - webrtc::DesktopCaptureOptions::CreateDefault(); - options.set_use_update_notifications(true); - return scoped_ptr( - webrtc::ScreenCapturer::Create(options)); -} - std::string Me2MeDesktopEnvironment::GetCapabilities() const { return kRateLimitResizeRequests; } @@ -63,6 +53,7 @@ Me2MeDesktopEnvironment::Me2MeDesktopEnvironment( ui_task_runner), gnubby_auth_enabled_(false) { DCHECK(caller_task_runner->BelongsToCurrentThread()); + desktop_capture_options()->set_use_update_notifications(true); } scoped_ptr Me2MeDesktopEnvironment::CreateGnubbyAuthHandler( diff --git a/remoting/host/me2me_desktop_environment.h b/remoting/host/me2me_desktop_environment.h index 9ee69a96f9ad5..f5f31e6fc25d7 100644 --- a/remoting/host/me2me_desktop_environment.h +++ b/remoting/host/me2me_desktop_environment.h @@ -21,7 +21,6 @@ class Me2MeDesktopEnvironment : public BasicDesktopEnvironment { // DesktopEnvironment interface. virtual scoped_ptr CreateScreenControls() OVERRIDE; - virtual scoped_ptr CreateVideoCapturer() OVERRIDE; virtual std::string GetCapabilities() const OVERRIDE; virtual scoped_ptr CreateGnubbyAuthHandler( protocol::ClientStub* client_stub) OVERRIDE; diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 7ac8d09520354..b0c8ea01c18b1 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -65,6 +65,7 @@ #include "remoting/host/policy_hack/policy_watcher.h" #include "remoting/host/session_manager_factory.h" #include "remoting/host/signaling_connector.h" +#include "remoting/host/single_window_desktop_environment.h" #include "remoting/host/token_validator_factory_impl.h" #include "remoting/host/usage_stats_consent.h" #include "remoting/host/username.h" @@ -133,6 +134,8 @@ const char kFrameRecorderBufferKbName[] = "frame-recorder-buffer-kb"; // from stdin. const char kStdinConfigPath[] = "-"; +const char kWindowIdSwitchName[] = "window-id"; + } // namespace namespace remoting { @@ -309,6 +312,13 @@ class HostProcess ThirdPartyAuthConfig third_party_auth_config_; bool enable_gnubby_auth_; + // Boolean to change flow, where ncessary, if we're + // capturing a window instead of the entire desktop. + bool enable_window_capture_; + + // Used to specify which window to stream, if enabled. + webrtc::WindowId window_id_; + scoped_ptr oauth_token_getter_; scoped_ptr signal_strategy_; scoped_ptr signaling_connector_; @@ -348,6 +358,8 @@ HostProcess::HostProcess(scoped_ptr context, allow_pairing_(true), curtain_required_(false), enable_gnubby_auth_(false), + enable_window_capture_(false), + window_id_(0), #if defined(REMOTING_MULTI_PROCESS) desktop_session_connector_(NULL), #endif // defined(REMOTING_MULTI_PROCESS) @@ -454,6 +466,26 @@ bool HostProcess::InitWithCommandLine(const base::CommandLine* cmd_line) { signal_parent_ = cmd_line->HasSwitch(kSignalParentSwitchName); + enable_window_capture_ = cmd_line->HasSwitch(kWindowIdSwitchName); + if (enable_window_capture_) { + +#if defined(OS_LINUX) || defined(OS_WIN) + LOG(WARNING) << "Window capturing is not fully supported on Linux or " + "Windows."; +#endif // defined(OS_LINUX) || defined(OS_WIN) + + // uint32_t is large enough to hold window IDs on all platforms. + uint32_t window_id; + if (base::StringToUint( + cmd_line->GetSwitchValueASCII(kWindowIdSwitchName), + &window_id)) { + window_id_ = static_cast(window_id); + } else { + LOG(ERROR) << "Window with window id: " << window_id_ + << " not found. Shutting down host."; + return false; + } + } return true; } @@ -684,11 +716,21 @@ void HostProcess::StartOnUiThread() { daemon_channel_.get()); desktop_session_connector_ = desktop_environment_factory; #else // !defined(OS_WIN) - DesktopEnvironmentFactory* desktop_environment_factory = + DesktopEnvironmentFactory* desktop_environment_factory; + if (enable_window_capture_) { + desktop_environment_factory = + new SingleWindowDesktopEnvironmentFactory( + context_->network_task_runner(), + context_->input_task_runner(), + context_->ui_task_runner(), + window_id_); + } else { + desktop_environment_factory = new Me2MeDesktopEnvironmentFactory( context_->network_task_runner(), context_->input_task_runner(), context_->ui_task_runner()); + } #endif // !defined(OS_WIN) desktop_environment_factory_.reset(desktop_environment_factory); diff --git a/remoting/host/shaped_desktop_capturer.cc b/remoting/host/shaped_desktop_capturer.cc new file mode 100644 index 0000000000000..14b3e99bedbd4 --- /dev/null +++ b/remoting/host/shaped_desktop_capturer.cc @@ -0,0 +1,42 @@ +// 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 "remoting/host/shaped_desktop_capturer.h" + +#include "base/logging.h" +#include "remoting/host/desktop_shape_tracker.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" + +namespace remoting { + +ShapedDesktopCapturer::ShapedDesktopCapturer( + scoped_ptr desktop_capturer, + scoped_ptr shape_tracker) + : desktop_capturer_(desktop_capturer.Pass()), + shape_tracker_(shape_tracker.Pass()), + callback_(NULL) { +} + +ShapedDesktopCapturer::~ShapedDesktopCapturer() {} + +void ShapedDesktopCapturer::Start(webrtc::DesktopCapturer::Callback* callback) { + callback_ = callback; + desktop_capturer_->Start(this); +} + +void ShapedDesktopCapturer::Capture(const webrtc::DesktopRegion& region) { + desktop_capturer_->Capture(region); +} + +webrtc::SharedMemory* ShapedDesktopCapturer::CreateSharedMemory(size_t size) { + return callback_->CreateSharedMemory(size); +} + +void ShapedDesktopCapturer::OnCaptureCompleted(webrtc::DesktopFrame* frame) { + shape_tracker_->RefreshDesktopShape(); + frame->set_shape(new webrtc::DesktopRegion(shape_tracker_->desktop_shape())); + callback_->OnCaptureCompleted(frame); +} + +} // namespace remoting diff --git a/remoting/host/shaped_desktop_capturer.h b/remoting/host/shaped_desktop_capturer.h new file mode 100644 index 0000000000000..a49743928aad4 --- /dev/null +++ b/remoting/host/shaped_desktop_capturer.h @@ -0,0 +1,41 @@ +// 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 REMOTING_HOST_SHAPED_DESKTOP_CAPTURER_H_ +#define REMOTING_HOST_SHAPED_DESKTOP_CAPTURER_H_ + +#include "base/memory/scoped_ptr.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" + +namespace remoting { + +class DesktopShapeTracker; + +// Screen capturer that also captures desktop shape. +class ShapedDesktopCapturer : public webrtc::DesktopCapturer, + public webrtc::DesktopCapturer::Callback { + public: + ShapedDesktopCapturer(scoped_ptr screen_capturer, + scoped_ptr shape_tracker); + virtual ~ShapedDesktopCapturer(); + + // webrtc::DesktopCapturer interface. + virtual void Start(webrtc::DesktopCapturer::Callback* callback) OVERRIDE; + virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE; + + private: + // webrtc::DesktopCapturer::Callback interface. + virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; + virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; + + scoped_ptr desktop_capturer_; + scoped_ptr shape_tracker_; + webrtc::DesktopCapturer::Callback* callback_; + + DISALLOW_COPY_AND_ASSIGN(ShapedDesktopCapturer); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SHAPED_DESKTOP_CAPTURER_H_ diff --git a/remoting/host/shaped_desktop_capturer_unittest.cc b/remoting/host/shaped_desktop_capturer_unittest.cc new file mode 100644 index 0000000000000..d0acc3d77bbfe --- /dev/null +++ b/remoting/host/shaped_desktop_capturer_unittest.cc @@ -0,0 +1,70 @@ +// 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 "remoting/host/shaped_desktop_capturer.h" + +#include "remoting/host/desktop_shape_tracker.h" +#include "remoting/host/fake_desktop_capturer.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" + +namespace remoting { + +class FakeDesktopShapeTracker : public DesktopShapeTracker { + public: + FakeDesktopShapeTracker() {} + virtual ~FakeDesktopShapeTracker() {} + + static webrtc::DesktopRegion CreateShape() { + webrtc::DesktopRegion result; + result.AddRect(webrtc::DesktopRect::MakeXYWH(0, 0, 5, 5)); + result.AddRect(webrtc::DesktopRect::MakeXYWH(5, 5, 5, 5)); + return result; + } + + virtual void RefreshDesktopShape() OVERRIDE { + shape_ = CreateShape(); + } + + virtual const webrtc::DesktopRegion& desktop_shape() OVERRIDE { + // desktop_shape() can't be called before RefreshDesktopShape(). + EXPECT_FALSE(shape_.is_empty()); + return shape_; + } + + private: + webrtc::DesktopRegion shape_; +}; + +class ShapedDesktopCapturerTest : public testing::Test, + public webrtc::DesktopCapturer::Callback { + public: + // webrtc::DesktopCapturer::Callback interface + virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE { + return NULL; + } + + virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE { + last_frame_.reset(frame); + } + + scoped_ptr last_frame_; +}; + +// Verify that captured frame have shape. +TEST_F(ShapedDesktopCapturerTest, Basic) { + ShapedDesktopCapturer capturer( + scoped_ptr(new FakeDesktopCapturer()), + scoped_ptr(new FakeDesktopShapeTracker())); + capturer.Start(this); + capturer.Capture(webrtc::DesktopRegion()); + ASSERT_TRUE(last_frame_.get()); + ASSERT_TRUE(last_frame_->shape()); + EXPECT_TRUE( + FakeDesktopShapeTracker::CreateShape().Equals(*last_frame_->shape())); +} + +} // namespace remoting diff --git a/remoting/host/shaped_screen_capturer.cc b/remoting/host/shaped_screen_capturer.cc deleted file mode 100644 index 00e808fadd8c7..0000000000000 --- a/remoting/host/shaped_screen_capturer.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "remoting/host/shaped_screen_capturer.h" - -#include "base/logging.h" -#include "remoting/host/desktop_shape_tracker.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" - -namespace remoting { - -// static -scoped_ptr ShapedScreenCapturer::Create( - webrtc::DesktopCaptureOptions options) { - return scoped_ptr( - new ShapedScreenCapturer(scoped_ptr( - webrtc::ScreenCapturer::Create(options)), - DesktopShapeTracker::Create(options))); -} - -ShapedScreenCapturer::ShapedScreenCapturer( - scoped_ptr screen_capturer, - scoped_ptr shape_tracker) - : screen_capturer_(screen_capturer.Pass()), - shape_tracker_(shape_tracker.Pass()), - callback_(NULL) { -} - -ShapedScreenCapturer::~ShapedScreenCapturer() {} - -void ShapedScreenCapturer::Start(webrtc::ScreenCapturer::Callback* callback) { - callback_ = callback; - screen_capturer_->Start(this); -} - -void ShapedScreenCapturer::Capture(const webrtc::DesktopRegion& region) { - screen_capturer_->Capture(region); -} - -void ShapedScreenCapturer::SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) { - screen_capturer_->SetMouseShapeObserver(mouse_shape_observer); -} - -bool ShapedScreenCapturer::GetScreenList(ScreenList* screens) { - NOTIMPLEMENTED(); - return false; -} - -bool ShapedScreenCapturer::SelectScreen(webrtc::ScreenId id) { - NOTIMPLEMENTED(); - return false; -} - -webrtc::SharedMemory* ShapedScreenCapturer::CreateSharedMemory(size_t size) { - return callback_->CreateSharedMemory(size); -} - -void ShapedScreenCapturer::OnCaptureCompleted(webrtc::DesktopFrame* frame) { - shape_tracker_->RefreshDesktopShape(); - frame->set_shape(new webrtc::DesktopRegion(shape_tracker_->desktop_shape())); - callback_->OnCaptureCompleted(frame); -} - -} // namespace remoting diff --git a/remoting/host/shaped_screen_capturer.h b/remoting/host/shaped_screen_capturer.h deleted file mode 100644 index c343293094751..0000000000000 --- a/remoting/host/shaped_screen_capturer.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_HOST_SHAPED_SCREEN_CAPTURER_H_ -#define REMOTING_HOST_SHAPED_SCREEN_CAPTURER_H_ - -#include "base/memory/scoped_ptr.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" - -namespace remoting { - -class DesktopShapeTracker; - -// Screen capturer that also captures desktop shape. -class ShapedScreenCapturer : public webrtc::ScreenCapturer, - public webrtc::DesktopCapturer::Callback { - public: - static scoped_ptr Create( - webrtc::DesktopCaptureOptions options); - - ShapedScreenCapturer(scoped_ptr screen_capturer, - scoped_ptr shape_tracker); - virtual ~ShapedScreenCapturer(); - - // webrtc::ScreenCapturer interface. - virtual void Start(webrtc::ScreenCapturer::Callback* callback) OVERRIDE; - virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE; - virtual void SetMouseShapeObserver( - MouseShapeObserver* mouse_shape_observer) OVERRIDE; - virtual bool GetScreenList(ScreenList* screens) OVERRIDE; - virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE; - - private: - // webrtc::ScreenCapturer::Callback interface. - virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; - virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; - - scoped_ptr screen_capturer_; - scoped_ptr shape_tracker_; - webrtc::ScreenCapturer::Callback* callback_; - - DISALLOW_COPY_AND_ASSIGN(ShapedScreenCapturer); -}; - -} // namespace remoting - -#endif // REMOTING_HOST_SHAPED_SCREEN_CAPTURER_H_ diff --git a/remoting/host/shaped_screen_capturer_unittest.cc b/remoting/host/shaped_screen_capturer_unittest.cc deleted file mode 100644 index 1be21bf0d3cdc..0000000000000 --- a/remoting/host/shaped_screen_capturer_unittest.cc +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "remoting/host/shaped_screen_capturer.h" - -#include "remoting/host/desktop_shape_tracker.h" -#include "remoting/host/fake_screen_capturer.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" - -namespace remoting { - -class FakeDesktopShapeTracker : public DesktopShapeTracker { - public: - FakeDesktopShapeTracker() {} - virtual ~FakeDesktopShapeTracker() {} - - static webrtc::DesktopRegion CreateShape() { - webrtc::DesktopRegion result; - result.AddRect(webrtc::DesktopRect::MakeXYWH(0, 0, 5, 5)); - result.AddRect(webrtc::DesktopRect::MakeXYWH(5, 5, 5, 5)); - return result; - } - - virtual void RefreshDesktopShape() OVERRIDE { - shape_ = CreateShape(); - } - - virtual const webrtc::DesktopRegion& desktop_shape() OVERRIDE { - // desktop_shape() can't be called before RefreshDesktopShape(). - EXPECT_FALSE(shape_.is_empty()); - return shape_; - } - - private: - webrtc::DesktopRegion shape_; -}; - -class ShapedScreenCapturerTest : public testing::Test, - public webrtc::DesktopCapturer::Callback { - public: - // webrtc::DesktopCapturer::Callback interface - virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE { - return NULL; - } - - virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE { - last_frame_.reset(frame); - } - - scoped_ptr last_frame_; -}; - -// Verify that captured frame have shape. -TEST_F(ShapedScreenCapturerTest, Basic) { - ShapedScreenCapturer capturer( - scoped_ptr(new FakeScreenCapturer()), - scoped_ptr(new FakeDesktopShapeTracker())); - capturer.Start(this); - capturer.Capture(webrtc::DesktopRegion()); - ASSERT_TRUE(last_frame_.get()); - ASSERT_TRUE(last_frame_->shape()); - EXPECT_TRUE( - FakeDesktopShapeTracker::CreateShape().Equals(*last_frame_->shape())); -} - -} // namespace remoting diff --git a/remoting/host/single_window_desktop_environment.cc b/remoting/host/single_window_desktop_environment.cc new file mode 100644 index 0000000000000..7950b8a93d7fd --- /dev/null +++ b/remoting/host/single_window_desktop_environment.cc @@ -0,0 +1,106 @@ +// 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 "remoting/host/single_window_desktop_environment.h" + +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "remoting/host/single_window_input_injector.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h" +#include "third_party/webrtc/modules/desktop_capture/window_capturer.h" + +namespace remoting { + +// Enables capturing and streaming of windows. +class SingleWindowDesktopEnvironment : public BasicDesktopEnvironment { + + public: + virtual ~SingleWindowDesktopEnvironment(); + + // DesktopEnvironment interface. + virtual scoped_ptr CreateVideoCapturer() OVERRIDE; + virtual scoped_ptr CreateInputInjector() OVERRIDE; + + protected: + friend class SingleWindowDesktopEnvironmentFactory; + SingleWindowDesktopEnvironment( + scoped_refptr caller_task_runner, + scoped_refptr input_task_runner, + scoped_refptr ui_task_runner, + webrtc::WindowId window_id); + + private: + webrtc::WindowId window_id_; + + DISALLOW_COPY_AND_ASSIGN(SingleWindowDesktopEnvironment); +}; + +SingleWindowDesktopEnvironment::~SingleWindowDesktopEnvironment() { +} + +scoped_ptr +SingleWindowDesktopEnvironment::CreateVideoCapturer() { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + webrtc::DesktopCaptureOptions options = + webrtc::DesktopCaptureOptions::CreateDefault(); + options.set_use_update_notifications(true); + + scoped_ptrwindow_capturer( + webrtc::WindowCapturer::Create(options)); + window_capturer->SelectWindow(window_id_); + + return window_capturer.PassAs(); +} + +scoped_ptr +SingleWindowDesktopEnvironment::CreateInputInjector() { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + scoped_ptr input_injector( + InputInjector::Create(input_task_runner(), + ui_task_runner())); + return SingleWindowInputInjector::CreateForWindow( + window_id_, input_injector.Pass()).PassAs(); +} + +SingleWindowDesktopEnvironment::SingleWindowDesktopEnvironment( + scoped_refptr caller_task_runner, + scoped_refptr input_task_runner, + scoped_refptr ui_task_runner, + webrtc::WindowId window_id) + : BasicDesktopEnvironment(caller_task_runner, + input_task_runner, + ui_task_runner), + window_id_(window_id) { +} + +SingleWindowDesktopEnvironmentFactory::SingleWindowDesktopEnvironmentFactory( + scoped_refptr caller_task_runner, + scoped_refptr input_task_runner, + scoped_refptr ui_task_runner, + webrtc::WindowId window_id) + : BasicDesktopEnvironmentFactory(caller_task_runner, + input_task_runner, + ui_task_runner), + window_id_(window_id) { +} + +SingleWindowDesktopEnvironmentFactory:: + ~SingleWindowDesktopEnvironmentFactory() { +} + +scoped_ptr SingleWindowDesktopEnvironmentFactory::Create( + base::WeakPtr client_session_control) { + DCHECK(caller_task_runner()->BelongsToCurrentThread()); + + scoped_ptr desktop_environment( + new SingleWindowDesktopEnvironment(caller_task_runner(), + input_task_runner(), + ui_task_runner(), + window_id_)); + return desktop_environment.PassAs(); +} + +} // namespace remoting diff --git a/remoting/host/single_window_desktop_environment.h b/remoting/host/single_window_desktop_environment.h new file mode 100644 index 0000000000000..eee773e7bc00d --- /dev/null +++ b/remoting/host/single_window_desktop_environment.h @@ -0,0 +1,37 @@ +// 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 REMOTING_HOST_SINGLE_WINDOW_DESKTOP_ENVIRONMENT_H_ +#define REMOTING_HOST_SINGLE_WINDOW_DESKTOP_ENVIRONMENT_H_ + +#include "remoting/host/basic_desktop_environment.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" + +namespace remoting { + +// Passed to the ChromotingHost to remote an individual window's contents, +// rather than a whole desktop. +class SingleWindowDesktopEnvironmentFactory + : public BasicDesktopEnvironmentFactory { + public: + SingleWindowDesktopEnvironmentFactory( + scoped_refptr caller_task_runner, + scoped_refptr input_task_runner, + scoped_refptr ui_task_runner, + webrtc::WindowId window_id); + virtual ~SingleWindowDesktopEnvironmentFactory(); + + // DesktopEnvironmentFactory interface. + virtual scoped_ptr Create( + base::WeakPtr client_session_control) OVERRIDE; + + private: + webrtc::WindowId window_id_; + + DISALLOW_COPY_AND_ASSIGN(SingleWindowDesktopEnvironmentFactory); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SINGLE_WINDOW_DESKTOP_ENVIRONMENT_H_ diff --git a/remoting/host/single_window_input_injector.h b/remoting/host/single_window_input_injector.h new file mode 100644 index 0000000000000..6ee92c7503ea2 --- /dev/null +++ b/remoting/host/single_window_input_injector.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_SINGLE_WINDOW_INPUT_INJECTOR_H_ +#define REMOTING_HOST_SINGLE_WINDOW_INPUT_INJECTOR_H_ + +#include "base/memory/scoped_ptr.h" +#include "remoting/host/input_injector.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h" + +namespace remoting { + +// This class is used to wrap an InputInjector. All this class does +// is forward messages to the wrapped InputInjector. When a MouseEvent +// comes in, SingleWindowInputInjector adjusts the MouseEvent's +// coordinates so that they are in respect to the window instead of +// the desktop. Clipboard, Text, and Key events are still global. +class SingleWindowInputInjector : public InputInjector { + public: + // This Create method needs to be passed a full desktop + // InputInjector. + static scoped_ptr CreateForWindow( + webrtc::WindowId window_id, + scoped_ptr input_injector); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_SINGLE_WINDOW_INPUT_INJECTOR_H_ diff --git a/remoting/host/single_window_input_injector_linux.cc b/remoting/host/single_window_input_injector_linux.cc new file mode 100644 index 0000000000000..fd42f66b67655 --- /dev/null +++ b/remoting/host/single_window_input_injector_linux.cc @@ -0,0 +1,15 @@ +// 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 "remoting/host/single_window_input_injector.h" + +namespace remoting { + +scoped_ptr SingleWindowInputInjector::CreateForWindow( + webrtc::WindowId window_id, + scoped_ptr input_injector) { + return scoped_ptr(); +} + +} // namespace remoting diff --git a/remoting/host/single_window_input_injector_mac.cc b/remoting/host/single_window_input_injector_mac.cc new file mode 100644 index 0000000000000..78dc2e2a0737b --- /dev/null +++ b/remoting/host/single_window_input_injector_mac.cc @@ -0,0 +1,166 @@ +// 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 "remoting/host/single_window_input_injector.h" + +#include +#include + +#include "base/mac/foundation_util.h" +#include "base/mac/scoped_cftyperef.h" +#include "remoting/proto/event.pb.h" +#include "third_party/webrtc/modules/desktop_capture/mac/desktop_configuration.h" + +namespace remoting { + +using protocol::ClipboardEvent; +using protocol::KeyEvent; +using protocol::TextEvent; +using protocol::MouseEvent; + +class SingleWindowInputInjectorMac : public SingleWindowInputInjector { + public: + SingleWindowInputInjectorMac( + webrtc::WindowId window_id, + scoped_ptr input_injector); + virtual ~SingleWindowInputInjectorMac(); + + // InputInjector interface. + virtual void Start( + scoped_ptr client_clipboard) OVERRIDE; + virtual void InjectKeyEvent(const KeyEvent& event) OVERRIDE; + virtual void InjectTextEvent(const TextEvent& event) OVERRIDE; + virtual void InjectMouseEvent(const MouseEvent& event) OVERRIDE; + virtual void InjectClipboardEvent(const ClipboardEvent& event) OVERRIDE; + + private: + CGRect FindCGRectOfWindow(); + + CGWindowID window_id_; + scoped_ptr input_injector_; + + DISALLOW_COPY_AND_ASSIGN(SingleWindowInputInjectorMac); +}; + +SingleWindowInputInjectorMac::SingleWindowInputInjectorMac( + webrtc::WindowId window_id, + scoped_ptr input_injector) + : window_id_(static_cast(window_id)), + input_injector_(input_injector.Pass()) { +} + +SingleWindowInputInjectorMac::~SingleWindowInputInjectorMac() { +} + +void SingleWindowInputInjectorMac::Start( + scoped_ptr client_clipboard) { + input_injector_->Start(client_clipboard.Pass()); +} + +void SingleWindowInputInjectorMac::InjectKeyEvent(const KeyEvent& event) { + input_injector_->InjectKeyEvent(event); +} + +void SingleWindowInputInjectorMac::InjectTextEvent(const TextEvent& event) { + input_injector_->InjectTextEvent(event); +} + +void SingleWindowInputInjectorMac::InjectMouseEvent(const MouseEvent& event) { + if (event.has_x() && event.has_y()) { + CGRect window_rect = FindCGRectOfWindow(); + if (CGRectIsNull(window_rect)) { + LOG(ERROR) << "Window rect is null, so forwarding unmodified MouseEvent"; + input_injector_->InjectMouseEvent(event); + return; + } + + webrtc::MacDesktopConfiguration desktop_config = + webrtc::MacDesktopConfiguration::GetCurrent( + webrtc::MacDesktopConfiguration::TopLeftOrigin); + + // Create a vector that has the origin of the window. + webrtc::DesktopVector window_pos(window_rect.origin.x, + window_rect.origin.y); + + // The underlying InputInjector expects coordinates relative to the + // top-left of the top-left-most monitor, so translate the window origin + // to that coordinate scheme. + window_pos.subtract( + webrtc::DesktopVector(desktop_config.pixel_bounds.left(), + desktop_config.pixel_bounds.top())); + + // We must make sure we are taking into account the fact that when we + // find the window on the host it returns its coordinates in Density + // Independent coordinates. We have to convert to Density Dependent + // because InputInjector assumes Density Dependent coordinates in the + // MouseEvent. + window_pos.set(window_pos.x() * desktop_config.dip_to_pixel_scale, + window_pos.y() * desktop_config.dip_to_pixel_scale); + + // Create a new event with coordinates that are in respect to the window. + MouseEvent modified_event(event); + modified_event.set_x(event.x() + window_pos.x()); + modified_event.set_y(event.y() + window_pos.y()); + input_injector_->InjectMouseEvent(modified_event); + } else { + input_injector_->InjectMouseEvent(event); + } +} + +void SingleWindowInputInjectorMac::InjectClipboardEvent( + const ClipboardEvent& event) { + input_injector_->InjectClipboardEvent(event); +} + +// This method finds the rectangle of the window we are streaming using +// |window_id_|. The InputInjector can then use this rectangle +// to translate the input event to coordinates of the window rather +// than the screen. +CGRect SingleWindowInputInjectorMac::FindCGRectOfWindow() { + CGRect rect; + CGWindowID ids[1] = {window_id_}; + base::ScopedCFTypeRef window_id_array( + CFArrayCreate(NULL, reinterpret_cast(&ids), 1, NULL)); + + base::ScopedCFTypeRef window_array( + CGWindowListCreateDescriptionFromArray(window_id_array)); + + if (window_array == NULL || CFArrayGetCount(window_array) == 0) { + // Could not find the window. It might have been closed. + LOG(ERROR) << "Specified window to stream not found for id: " + << window_id_; + return CGRectNull; + } + + // We don't use ScopedCFTypeRef for |window_array| because the + // CFDictionaryRef returned by CFArrayGetValueAtIndex is owned by + // window_array. The same is true of the |bounds|. + CFDictionaryRef window = + base::mac::CFCast( + CFArrayGetValueAtIndex(window_array, 0)); + + if (CFDictionaryContainsKey(window, kCGWindowBounds)) { + CFDictionaryRef bounds = + base::mac::GetValueFromDictionary( + window, kCGWindowBounds); + + if (bounds) { + if (CGRectMakeWithDictionaryRepresentation(bounds, &rect)) { + return rect; + } + } + } + + return CGRectNull; +} + +scoped_ptr SingleWindowInputInjector::CreateForWindow( + webrtc::WindowId window_id, + scoped_ptr input_injector) { + scoped_ptr injector( + new SingleWindowInputInjectorMac(window_id, input_injector.Pass())); + return injector.PassAs(); +} + +} // namespace remoting diff --git a/remoting/host/single_window_input_injector_win.cc b/remoting/host/single_window_input_injector_win.cc new file mode 100644 index 0000000000000..fd42f66b67655 --- /dev/null +++ b/remoting/host/single_window_input_injector_win.cc @@ -0,0 +1,15 @@ +// 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 "remoting/host/single_window_input_injector.h" + +namespace remoting { + +scoped_ptr SingleWindowInputInjector::CreateForWindow( + webrtc::WindowId window_id, + scoped_ptr input_injector) { + return scoped_ptr(); +} + +} // namespace remoting diff --git a/remoting/host/video_frame_recorder_host_extension.cc b/remoting/host/video_frame_recorder_host_extension.cc index 67f9c941eb392..b0b4d47d84bde 100644 --- a/remoting/host/video_frame_recorder_host_extension.cc +++ b/remoting/host/video_frame_recorder_host_extension.cc @@ -50,12 +50,13 @@ class VideoFrameRecorderHostExtensionSession : public HostExtensionSession { private: VideoEncoderVerbatim verbatim_encoder; VideoFrameRecorder video_frame_recorder; + bool first_frame_; DISALLOW_COPY_AND_ASSIGN(VideoFrameRecorderHostExtensionSession); }; VideoFrameRecorderHostExtensionSession::VideoFrameRecorderHostExtensionSession( - int64_t max_content_bytes) { + int64_t max_content_bytes) : first_frame_(false) { video_frame_recorder.SetMaxContentBytes(max_content_bytes); } @@ -93,6 +94,7 @@ bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage( if (type == kStartType) { video_frame_recorder.SetEnableRecording(true); + first_frame_ = true; } else if (type == kStopType) { video_frame_recorder.SetEnableRecording(false); } else if (type == kNextFrameType) { @@ -103,6 +105,15 @@ bool VideoFrameRecorderHostExtensionSession::OnExtensionMessage( base::DictionaryValue reply_message; reply_message.SetString(kType, kNextFrameReplyType); if (frame) { + // If this is the first frame then override the updated region so that + // the encoder will send the whole frame's contents. + if (first_frame_) { + first_frame_ = false; + + frame->mutable_updated_region()->SetRect( + webrtc::DesktopRect::MakeSize(frame->size())); + } + // Encode the frame into a raw ARGB VideoPacket. scoped_ptr encoded_frame( verbatim_encoder.Encode(*frame)); diff --git a/remoting/host/video_scheduler.cc b/remoting/host/video_scheduler.cc index 24181c393a308..b9f1276c4639c 100644 --- a/remoting/host/video_scheduler.cc +++ b/remoting/host/video_scheduler.cc @@ -20,9 +20,10 @@ #include "remoting/protocol/cursor_shape_stub.h" #include "remoting/protocol/message_decoder.h" #include "remoting/protocol/video_stub.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" #include "third_party/webrtc/modules/desktop_capture/mouse_cursor_shape.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" namespace remoting { @@ -30,10 +31,10 @@ namespace remoting { // TODO(hclam): Move this value to CaptureScheduler. static const int kMaxPendingFrames = 2; -// Interval between empty keep-alive frames. These frames are sent only -// when there are no real video frames being sent. To prevent PseudoTCP from -// resetting congestion window this value must be smaller than the minimum -// RTO used in PseudoTCP, which is 250ms. +// Interval between empty keep-alive frames. These frames are sent only when the +// stream is paused or inactive for some other reason (e.g. when blocked on +// capturer). To prevent PseudoTCP from resetting congestion window this value +// must be smaller than the minimum RTO used in PseudoTCP, which is 250ms. static const int kKeepAlivePacketIntervalMs = 200; static bool g_enable_timestamps = false; @@ -47,7 +48,8 @@ VideoScheduler::VideoScheduler( scoped_refptr capture_task_runner, scoped_refptr encode_task_runner, scoped_refptr network_task_runner, - scoped_ptr capturer, + scoped_ptr capturer, + scoped_ptr mouse_cursor_monitor, scoped_ptr encoder, protocol::CursorShapeStub* cursor_stub, protocol::VideoStub* video_stub) @@ -55,6 +57,7 @@ VideoScheduler::VideoScheduler( encode_task_runner_(encode_task_runner), network_task_runner_(network_task_runner), capturer_(capturer.Pass()), + mouse_cursor_monitor_(mouse_cursor_monitor.Pass()), encoder_(encoder.Pass()), cursor_stub_(cursor_stub), video_stub_(video_stub), @@ -65,6 +68,7 @@ VideoScheduler::VideoScheduler( sequence_number_(0) { DCHECK(network_task_runner_->BelongsToCurrentThread()); DCHECK(capturer_); + DCHECK(mouse_cursor_monitor_); DCHECK(encoder_); DCHECK(cursor_stub_); DCHECK(video_stub_); @@ -103,11 +107,10 @@ void VideoScheduler::OnCaptureCompleted(webrtc::DesktopFrame* frame) { } } -void VideoScheduler::OnCursorShapeChanged( - webrtc::MouseCursorShape* cursor_shape) { +void VideoScheduler::OnMouseCursor(webrtc::MouseCursor* cursor) { DCHECK(capture_task_runner_->BelongsToCurrentThread()); - scoped_ptr owned_cursor(cursor_shape); + scoped_ptr owned_cursor(cursor); // Do nothing if the scheduler is being stopped. if (!capturer_) @@ -115,17 +118,33 @@ void VideoScheduler::OnCursorShapeChanged( scoped_ptr cursor_proto( new protocol::CursorShapeInfo()); - cursor_proto->set_width(cursor_shape->size.width()); - cursor_proto->set_height(cursor_shape->size.height()); - cursor_proto->set_hotspot_x(cursor_shape->hotspot.x()); - cursor_proto->set_hotspot_y(cursor_shape->hotspot.y()); - cursor_proto->set_data(cursor_shape->data); + cursor_proto->set_width(cursor->image()->size().width()); + cursor_proto->set_height(cursor->image()->size().height()); + cursor_proto->set_hotspot_x(cursor->hotspot().x()); + cursor_proto->set_hotspot_y(cursor->hotspot().y()); + + std::string data; + uint8_t* current_row = cursor->image()->data(); + for (int y = 0; y < cursor->image()->size().height(); ++y) { + cursor_proto->mutable_data()->append( + current_row, + current_row + cursor->image()->size().width() * + webrtc::DesktopFrame::kBytesPerPixel); + current_row += cursor->image()->stride(); + } network_task_runner_->PostTask( FROM_HERE, base::Bind(&VideoScheduler::SendCursorShape, this, base::Passed(&cursor_proto))); } +void VideoScheduler::OnMouseCursorPosition( + webrtc::MouseCursorMonitor::CursorState state, + const webrtc::DesktopVector& position) { + // We're not subscribing to mouse position changes. + NOTREACHED(); +} + void VideoScheduler::Start() { DCHECK(network_task_runner_->BelongsToCurrentThread()); @@ -204,6 +223,7 @@ void VideoScheduler::SetLosslessColor(bool want_lossless) { VideoScheduler::~VideoScheduler() { // Destroy the capturer and encoder on their respective threads. capture_task_runner_->DeleteSoon(FROM_HERE, capturer_.release()); + capture_task_runner_->DeleteSoon(FROM_HERE, mouse_cursor_monitor_.release()); encode_task_runner_->DeleteSoon(FROM_HERE, encoder_.release()); } @@ -213,8 +233,10 @@ void VideoScheduler::StartOnCaptureThread() { DCHECK(capture_task_runner_->BelongsToCurrentThread()); DCHECK(!capture_timer_); - // Start the capturer and let it notify us if cursor shape changes. - capturer_->SetMouseShapeObserver(this); + // Start mouse cursor monitor. + mouse_cursor_monitor_->Init(this, webrtc::MouseCursorMonitor::SHAPE_ONLY); + + // Start the capturer. capturer_->Start(this); capture_timer_.reset(new base::OneShotTimer()); @@ -222,7 +244,7 @@ void VideoScheduler::StartOnCaptureThread() { FROM_HERE, base::TimeDelta::FromMilliseconds(kKeepAlivePacketIntervalMs), this, &VideoScheduler::SendKeepAlivePacket)); - // Capture first frame immedately. + // Capture first frame immediately. CaptureNextFrame(); } @@ -272,6 +294,9 @@ void VideoScheduler::CaptureNextFrame() { capture_pending_ = true; + // Capture the mouse shape. + mouse_cursor_monitor_->Capture(); + // And finally perform one capture. capturer_->Capture(webrtc::DesktopRegion()); } @@ -349,11 +374,15 @@ void VideoScheduler::EncodeFrame( base::TimeTicks timestamp) { DCHECK(encode_task_runner_->BelongsToCurrentThread()); - // Drop the frame if there were no changes. + // If there is nothing to encode then send an empty packet. if (!frame || frame->updated_region().is_empty()) { capture_task_runner_->DeleteSoon(FROM_HERE, frame.release()); - capture_task_runner_->PostTask( - FROM_HERE, base::Bind(&VideoScheduler::FrameCaptureCompleted, this)); + scoped_ptr packet(new VideoPacket()); + packet->set_client_sequence_number(sequence_number); + network_task_runner_->PostTask( + FROM_HERE, + base::Bind( + &VideoScheduler::SendVideoPacket, this, base::Passed(&packet))); return; } diff --git a/remoting/host/video_scheduler.h b/remoting/host/video_scheduler.h index 3b745ff51af80..13ef218b0ca47 100644 --- a/remoting/host/video_scheduler.h +++ b/remoting/host/video_scheduler.h @@ -15,14 +15,15 @@ #include "remoting/codec/video_encoder.h" #include "remoting/host/capture_scheduler.h" #include "remoting/proto/video.pb.h" -#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor_monitor.h" namespace base { class SingleThreadTaskRunner; } // namespace base namespace media { -class ScreenCapturer; +class DesktopCapturer; } // namespace media namespace remoting { @@ -36,7 +37,7 @@ class VideoStub; } // namespace protocol // Class responsible for scheduling frame captures from a -// webrtc::ScreenCapturer, delivering them to a VideoEncoder to encode, and +// webrtc::DesktopCapturer, delivering them to a VideoEncoder to encode, and // finally passing the encoded video packets to the specified VideoStub to send // on the network. // @@ -74,7 +75,7 @@ class VideoStub; class VideoScheduler : public base::RefCountedThreadSafe, public webrtc::DesktopCapturer::Callback, - public webrtc::ScreenCapturer::MouseShapeObserver { + public webrtc::MouseCursorMonitor::Callback { public: // Enables timestamps for generated frames. Used for testing. static void EnableTimestampsForTests(); @@ -87,7 +88,8 @@ class VideoScheduler : public base::RefCountedThreadSafe, scoped_refptr capture_task_runner, scoped_refptr encode_task_runner, scoped_refptr network_task_runner, - scoped_ptr capturer, + scoped_ptr capturer, + scoped_ptr mouse_cursor_monitor, scoped_ptr encoder, protocol::CursorShapeStub* cursor_stub, protocol::VideoStub* video_stub); @@ -96,9 +98,12 @@ class VideoScheduler : public base::RefCountedThreadSafe, virtual webrtc::SharedMemory* CreateSharedMemory(size_t size) OVERRIDE; virtual void OnCaptureCompleted(webrtc::DesktopFrame* frame) OVERRIDE; - // webrtc::ScreenCapturer::MouseShapeObserver implementation. - virtual void OnCursorShapeChanged( - webrtc::MouseCursorShape* cursor_shape) OVERRIDE; + // webrtc::MouseCursorMonitor::Callback implementation. + virtual void OnMouseCursor( + webrtc::MouseCursor* mouse_cursor) OVERRIDE; + virtual void OnMouseCursorPosition( + webrtc::MouseCursorMonitor::CursorState state, + const webrtc::DesktopVector& position) OVERRIDE; // Starts scheduling frame captures. void Start(); @@ -175,7 +180,10 @@ class VideoScheduler : public base::RefCountedThreadSafe, scoped_refptr network_task_runner_; // Used to capture frames. Always accessed on the capture thread. - scoped_ptr capturer_; + scoped_ptr capturer_; + + // Used to capture mouse cursor shapes. Always accessed on the capture thread. + scoped_ptr mouse_cursor_monitor_; // Used to encode captured frames. Always accessed on the encode thread. scoped_ptr encoder_; diff --git a/remoting/host/video_scheduler_unittest.cc b/remoting/host/video_scheduler_unittest.cc index 3d7375e811dc3..1d76d589c7b64 100644 --- a/remoting/host/video_scheduler_unittest.cc +++ b/remoting/host/video_scheduler_unittest.cc @@ -10,13 +10,18 @@ #include "base/single_thread_task_runner.h" #include "remoting/base/auto_thread.h" #include "remoting/base/auto_thread_task_runner.h" +#include "remoting/codec/video_encoder.h" #include "remoting/codec/video_encoder_verbatim.h" -#include "remoting/host/fake_screen_capturer.h" +#include "remoting/host/fake_desktop_capturer.h" +#include "remoting/host/fake_mouse_cursor_monitor.h" +#include "remoting/host/host_mock_objects.h" +#include "remoting/proto/control.pb.h" #include "remoting/proto/video.pb.h" #include "remoting/protocol/protocol_mock_objects.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" +#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h" using ::remoting::protocol::MockClientStub; @@ -51,6 +56,10 @@ ACTION(FinishSend) { static const int kWidth = 640; static const int kHeight = 480; +static const int kCursorWidth = 64; +static const int kCursorHeight = 32; +static const int kHotspotX = 11; +static const int kHotspotY = 12; class MockVideoEncoder : public VideoEncoder { public: @@ -83,20 +92,36 @@ class ThreadCheckVideoEncoder : public VideoEncoderVerbatim { DISALLOW_COPY_AND_ASSIGN(ThreadCheckVideoEncoder); }; -class ThreadCheckScreenCapturer : public FakeScreenCapturer { +class ThreadCheckDesktopCapturer : public FakeDesktopCapturer { public: - ThreadCheckScreenCapturer( + ThreadCheckDesktopCapturer( scoped_refptr task_runner) : task_runner_(task_runner) { } - virtual ~ThreadCheckScreenCapturer() { + virtual ~ThreadCheckDesktopCapturer() { EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); } private: scoped_refptr task_runner_; - DISALLOW_COPY_AND_ASSIGN(ThreadCheckScreenCapturer); + DISALLOW_COPY_AND_ASSIGN(ThreadCheckDesktopCapturer); +}; + +class ThreadCheckMouseCursorMonitor : public FakeMouseCursorMonitor { + public: + ThreadCheckMouseCursorMonitor( + scoped_refptr task_runner) + : task_runner_(task_runner) { + } + virtual ~ThreadCheckMouseCursorMonitor() { + EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); + } + + private: + scoped_refptr task_runner_; + + DISALLOW_COPY_AND_ASSIGN(ThreadCheckMouseCursorMonitor); }; class VideoSchedulerTest : public testing::Test { @@ -107,14 +132,22 @@ class VideoSchedulerTest : public testing::Test { virtual void TearDown() OVERRIDE; void StartVideoScheduler( - scoped_ptr capturer, - scoped_ptr encoder); + scoped_ptr capturer, + scoped_ptr encoder, + scoped_ptr mouse_monitor); void StopVideoScheduler(); - // webrtc::ScreenCapturer mocks. - void OnCapturerStart(webrtc::ScreenCapturer::Callback* callback); + // webrtc::DesktopCapturer mocks. + void OnCapturerStart(webrtc::DesktopCapturer::Callback* callback); void OnCaptureFrame(const webrtc::DesktopRegion& region); + // webrtc::MouseCursorMonitor mocks. + void OnMouseCursorMonitorInit( + webrtc::MouseCursorMonitor::Callback* callback, + webrtc::MouseCursorMonitor::Mode mode); + void OnCaptureMouse(); + void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape); + protected: base::MessageLoop message_loop_; base::RunLoop run_loop_; @@ -126,17 +159,19 @@ class VideoSchedulerTest : public testing::Test { MockClientStub client_stub_; MockVideoStub video_stub_; - scoped_ptr frame_; + // Points to the callback passed to webrtc::DesktopCapturer::Start(). + webrtc::DesktopCapturer::Callback* capturer_callback_; - // Points to the callback passed to webrtc::ScreenCapturer::Start(). - webrtc::ScreenCapturer::Callback* capturer_callback_; + // Points to the callback passed to webrtc::MouseCursor::Init(). + webrtc::MouseCursorMonitor::Callback* mouse_monitor_callback_; private: DISALLOW_COPY_AND_ASSIGN(VideoSchedulerTest); }; VideoSchedulerTest::VideoSchedulerTest() - : capturer_callback_(NULL) { + : capturer_callback_(NULL), + mouse_monitor_callback_(NULL) { } void VideoSchedulerTest::SetUp() { @@ -157,13 +192,15 @@ void VideoSchedulerTest::TearDown() { } void VideoSchedulerTest::StartVideoScheduler( - scoped_ptr capturer, - scoped_ptr encoder) { + scoped_ptr capturer, + scoped_ptr encoder, + scoped_ptr mouse_monitor) { scheduler_ = new VideoScheduler( capture_task_runner_, encode_task_runner_, main_task_runner_, capturer.Pass(), + mouse_monitor.Pass(), encoder.Pass(), &client_stub_, &video_stub_); @@ -176,7 +213,7 @@ void VideoSchedulerTest::StopVideoScheduler() { } void VideoSchedulerTest::OnCapturerStart( - webrtc::ScreenCapturer::Callback* callback) { + webrtc::DesktopCapturer::Callback* callback) { EXPECT_FALSE(capturer_callback_); EXPECT_TRUE(callback); @@ -184,9 +221,47 @@ void VideoSchedulerTest::OnCapturerStart( } void VideoSchedulerTest::OnCaptureFrame(const webrtc::DesktopRegion& region) { - frame_->mutable_updated_region()->SetRect( + scoped_ptr frame( + new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))); + frame->mutable_updated_region()->SetRect( webrtc::DesktopRect::MakeXYWH(0, 0, 10, 10)); - capturer_callback_->OnCaptureCompleted(frame_.release()); + capturer_callback_->OnCaptureCompleted(frame.release()); +} + +void VideoSchedulerTest::OnCaptureMouse() { + EXPECT_TRUE(mouse_monitor_callback_); + + scoped_ptr mouse_cursor( + new webrtc::MouseCursor( + new webrtc::BasicDesktopFrame( + webrtc::DesktopSize(kCursorWidth, kCursorHeight)), + webrtc::DesktopVector(kHotspotX, kHotspotY))); + + mouse_monitor_callback_->OnMouseCursor(mouse_cursor.release()); +} + +void VideoSchedulerTest::OnMouseCursorMonitorInit( + webrtc::MouseCursorMonitor::Callback* callback, + webrtc::MouseCursorMonitor::Mode mode) { + EXPECT_FALSE(mouse_monitor_callback_); + EXPECT_TRUE(callback); + + mouse_monitor_callback_ = callback; +} + +void VideoSchedulerTest::SetCursorShape( + const protocol::CursorShapeInfo& cursor_shape) { + EXPECT_TRUE(cursor_shape.has_width()); + EXPECT_EQ(kCursorWidth, cursor_shape.width()); + EXPECT_TRUE(cursor_shape.has_height()); + EXPECT_EQ(kCursorHeight, cursor_shape.height()); + EXPECT_TRUE(cursor_shape.has_hotspot_x()); + EXPECT_EQ(kHotspotX, cursor_shape.hotspot_x()); + EXPECT_TRUE(cursor_shape.has_hotspot_y()); + EXPECT_EQ(kHotspotY, cursor_shape.hotspot_y()); + EXPECT_TRUE(cursor_shape.has_data()); + EXPECT_EQ(kCursorWidth * kCursorHeight * webrtc::DesktopFrame::kBytesPerPixel, + static_cast(cursor_shape.data().size())); } // This test mocks capturer, encoder and network layer to simulate one capture @@ -196,13 +271,24 @@ void VideoSchedulerTest::OnCaptureFrame(const webrtc::DesktopRegion& region) { TEST_F(VideoSchedulerTest, StartAndStop) { scoped_ptr capturer( new webrtc::MockScreenCapturer()); + scoped_ptr cursor_monitor( + new MockMouseCursorMonitor()); + + { + InSequence s; + + EXPECT_CALL(*cursor_monitor, Init(_, _)) + .WillOnce( + Invoke(this, &VideoSchedulerTest::OnMouseCursorMonitorInit)); + + EXPECT_CALL(*cursor_monitor, Capture()) + .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureMouse)); + } + Expectation capturer_start = EXPECT_CALL(*capturer, Start(_)) .WillOnce(Invoke(this, &VideoSchedulerTest::OnCapturerStart)); - frame_.reset(new webrtc::BasicDesktopFrame( - webrtc::DesktopSize(kWidth, kHeight))); - // First the capturer is called. Expectation capturer_capture = EXPECT_CALL(*capturer, Capture(_)) .After(capturer_start) @@ -225,24 +311,39 @@ TEST_F(VideoSchedulerTest, StartAndStop) { InvokeWithoutArgs(this, &VideoSchedulerTest::StopVideoScheduler))) .RetiresOnSaturation(); + EXPECT_CALL(client_stub_, SetCursorShape(_)) + .WillOnce(Invoke(this, &VideoSchedulerTest::SetCursorShape)); + // Start video frame capture. - StartVideoScheduler(capturer.PassAs(), - encoder.PassAs()); + scoped_ptr mouse_cursor_monitor( + new FakeMouseCursorMonitor()); + StartVideoScheduler(capturer.PassAs(), + encoder.PassAs(), + cursor_monitor.PassAs()); + + // Run until there are no more pending tasks from the VideoScheduler. + // Otherwise, a lingering frame capture might attempt to trigger a capturer + // expectation action and crash. + base::RunLoop().RunUntilIdle(); } -// Verify that the capturer and encoder are torn down on the correct threads. +// Verify that the capturer, encoder and mouse monitor are torn down on the +// correct threads. TEST_F(VideoSchedulerTest, DeleteOnThreads) { -capture_task_runner_ = AutoThread::Create("capture", main_task_runner_); -encode_task_runner_ = AutoThread::Create("encode", main_task_runner_); + capture_task_runner_ = AutoThread::Create("capture", main_task_runner_); + encode_task_runner_ = AutoThread::Create("encode", main_task_runner_); - scoped_ptr capturer( - new ThreadCheckScreenCapturer(capture_task_runner_)); + scoped_ptr capturer( + new ThreadCheckDesktopCapturer(capture_task_runner_)); scoped_ptr encoder( new ThreadCheckVideoEncoder(encode_task_runner_)); + scoped_ptr mouse_cursor_monitor( + new ThreadCheckMouseCursorMonitor(capture_task_runner_)); - // Start and stop the scheduler, so it will tear down the screen capturer and - // video encoder. - StartVideoScheduler(capturer.Pass(), encoder.Pass()); + // Start and stop the scheduler, so it will tear down the screen capturer, + // video encoder and mouse monitor. + StartVideoScheduler(capturer.Pass(), encoder.Pass(), + mouse_cursor_monitor.Pass()); StopVideoScheduler(); } diff --git a/remoting/ios/Chromoting/Base.lproj/Main.storyboard b/remoting/ios/Chromoting/Base.lproj/Main.storyboard deleted file mode 100644 index 075e459bde1f8..0000000000000 --- a/remoting/ios/Chromoting/Base.lproj/Main.storyboard +++ /dev/null @@ -1,426 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/remoting/ios/Chromoting/Chromoting-Info.plist b/remoting/ios/Chromoting/Chromoting-Info.plist deleted file mode 100644 index c2d0be62087fa..0000000000000 --- a/remoting/ios/Chromoting/Chromoting-Info.plist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - net.fusionlabs.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 2.4 - LSRequiresIPhoneOS - - UIMainStoryboardFile - Main - UIRequiredDeviceCapabilities - - armv7 - - UISupportedInterfaceOrientations - - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationPortrait - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/remoting/ios/Chromoting/ChromotingModel.xcdatamodeld/.xccurrentversion b/remoting/ios/Chromoting/ChromotingModel.xcdatamodeld/.xccurrentversion deleted file mode 100644 index 86e8cb1fe44c2..0000000000000 --- a/remoting/ios/Chromoting/ChromotingModel.xcdatamodeld/.xccurrentversion +++ /dev/null @@ -1,8 +0,0 @@ - - - - - _XCCurrentVersionName - ChromotingModel.xcdatamodel - - diff --git a/remoting/ios/Chromoting/ChromotingModel.xcdatamodeld/ChromotingModel.xcdatamodel/contents b/remoting/ios/Chromoting/ChromotingModel.xcdatamodeld/ChromotingModel.xcdatamodel/contents deleted file mode 100644 index b8783eb242e8c..0000000000000 --- a/remoting/ios/Chromoting/ChromotingModel.xcdatamodeld/ChromotingModel.xcdatamodel/contents +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/remoting/ios/Chromoting/GTMOAuth2ViewTouch.xib b/remoting/ios/Chromoting/GTMOAuth2ViewTouch.xib deleted file mode 100644 index 4f91fa4ad20e9..0000000000000 --- a/remoting/ios/Chromoting/GTMOAuth2ViewTouch.xib +++ /dev/null @@ -1,494 +0,0 @@ - - - - 1024 - 12C60 - 2840 - 1187.34 - 625.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 1926 - - - YES - IBProxyObject - IBUIActivityIndicatorView - IBUIBarButtonItem - IBUIButton - IBUINavigationItem - IBUIView - IBUIWebView - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - PluginDependencyRecalculationVersion - - - - YES - - IBFilesOwner - IBCocoaTouchFramework - - - IBFirstResponder - IBCocoaTouchFramework - - - OAuth - IBCocoaTouchFramework - - - IBCocoaTouchFramework - 1 - - - - 292 - - YES - - - 292 - {30, 30} - - - - NO - NO - IBCocoaTouchFramework - 0 - 0 - {0, -2} - - - 3 - MQA - - - 2 - MC41OTYwNzg0NiAwLjY4NjI3NDUzIDAuOTUyOTQxMjQgMC42MDAwMDAwMgA - - - - 3 - MC41AA - - - Helvetica-Bold - Helvetica - 2 - 24 - - - Helvetica-Bold - 24 - 16 - - - - - 292 - {{30, 0}, {30, 30}} - - - - NO - NO - IBCocoaTouchFramework - 0 - 0 - {0, -2} - - - - 2 - MC41ODQzMTM3NSAwLjY3NDUwOTgyIDAuOTUyOTQxMjQgMC42MDAwMDAwMgA - - - - - - - - {60, 30} - - - - - 3 - MSAwAA - - NO - NO - - 3 - 3 - - IBCocoaTouchFramework - - - - 274 - - YES - - - 274 - {320, 460} - - - - - 1 - MSAxIDEAA - - YES - YES - IBCocoaTouchFramework - 1 - YES - - - - 301 - {{150, 115}, {20, 20}} - - - - _NS:9 - NO - IBCocoaTouchFramework - NO - YES - 2 - - - {320, 460} - - - - - 3 - MQA - - 2 - - - IBCocoaTouchFramework - - - - - YES - - - rightBarButtonItem - - - - 20 - - - - navButtonsView - - - - 22 - - - - backButton - - - - 25 - - - - forwardButton - - - - 26 - - - - view - - - - 28 - - - - webView - - - - 29 - - - - initialActivityIndicator - - - - 33 - - - - delegate - - - - 9 - - - - rightBarButtonItem - - - - 14 - - - - goBack - - - 7 - - 18 - - - - goForward - - - 7 - - 19 - - - - - YES - - 0 - - YES - - - - - - -1 - - - File's Owner - - - -2 - - - - - 6 - - - YES - - - - - 10 - - - - - 15 - - - YES - - - - - - - 16 - - - - - 17 - - - - - 27 - - - YES - - - - - - - 4 - - - - - 31 - - - - - - - YES - - YES - -1.CustomClassName - -1.IBPluginDependency - -2.CustomClassName - -2.IBPluginDependency - 10.IBPluginDependency - 15.IBPluginDependency - 16.IBPluginDependency - 17.IBPluginDependency - 27.IBPluginDependency - 31.IBPluginDependency - 4.IBPluginDependency - 6.IBPluginDependency - - - YES - GTMOAuth2ViewControllerTouch - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - UIResponder - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - - - - - YES - - - - - 33 - - - - YES - - GTMOAuth2ViewControllerTouch - UIViewController - - YES - - YES - backButton - forwardButton - initialActivityIndicator - navButtonsView - rightBarButtonItem - webView - - - YES - UIButton - UIButton - UIActivityIndicatorView - UIView - UIBarButtonItem - UIWebView - - - - YES - - YES - backButton - forwardButton - initialActivityIndicator - navButtonsView - rightBarButtonItem - webView - - - YES - - backButton - UIButton - - - forwardButton - UIButton - - - initialActivityIndicator - UIActivityIndicatorView - - - navButtonsView - UIView - - - rightBarButtonItem - UIBarButtonItem - - - webView - UIWebView - - - - - IBProjectSource - ./Classes/GTMOAuth2ViewControllerTouch.h - - - - - 0 - IBCocoaTouchFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - 3 - 1926 - - diff --git a/remoting/ios/Chromoting/Images.xcassets/AppIcon.appiconset/Contents.json b/remoting/ios/Chromoting/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2818410b90da..0000000000000 --- a/remoting/ios/Chromoting/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "1x" - }, - { - "idiom" : "iphone", - "size" : "57x57", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "chromoting120.png", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "50x50", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "72x72", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "chromoting76.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "chromoting152.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/remoting/ios/Chromoting/Images.xcassets/LaunchImage.launchimage/Contents.json b/remoting/ios/Chromoting/Images.xcassets/LaunchImage.launchimage/Contents.json deleted file mode 100644 index a0ad363c85ee8..0000000000000 --- a/remoting/ios/Chromoting/Images.xcassets/LaunchImage.launchimage/Contents.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "images" : [ - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/remoting/ios/Chromoting/en.lproj/InfoPlist.strings b/remoting/ios/Chromoting/en.lproj/InfoPlist.strings deleted file mode 100644 index 653031a96c208..0000000000000 --- a/remoting/ios/Chromoting/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,3 +0,0 @@ -/* This file is required */ -/* Localized versions of Info.plist keys */ - diff --git a/remoting/ios/Chromoting/main.mm b/remoting/ios/Chromoting/main.mm deleted file mode 100644 index cd068167b2878..0000000000000 --- a/remoting/ios/Chromoting/main.mm +++ /dev/null @@ -1,44 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import - -#include "base/at_exit.h" -#include "base/command_line.h" -#include "media/base/yuv_convert.h" -#include "net/socket/ssl_server_socket.h" - -#import "app_delegate.h" - -int main(int argc, char* argv[]) { - // This class is designed to fulfill the dependents needs when it goes out of - // scope and gets destructed - base::AtExitManager exitManager; - - // Publicize the CommandLine - CommandLine::Init(argc, argv); - -#ifdef DEBUG - // Set min log level for debug builds. For some reason this has to be - // negative. - logging::SetMinLogLevel(-1); -#endif - - // Allows later decoding of video frames. - media::InitializeCPUSpecificYUVConversions(); - - // Enable support for SSL server sockets, which must be done as early as - // possible, preferably before any NSS SSL sockets (client or server) have - // been created. - net::EnableSSLServerSockets(); - - @autoreleasepool { - return UIApplicationMain( - argc, argv, nil, NSStringFromClass([AppDelegate class])); - } -} diff --git a/remoting/ios/Chromoting_unittests/Chromoting_unittests-Info.plist b/remoting/ios/Chromoting_unittests/Chromoting_unittests-Info.plist deleted file mode 100644 index e48cdc62316a7..0000000000000 --- a/remoting/ios/Chromoting_unittests/Chromoting_unittests-Info.plist +++ /dev/null @@ -1,48 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIdentifier - net.fusionlabs.${PRODUCT_NAME:rfc1034identifier} - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 2.4 - LSRequiresIPhoneOS - - UIRequiredDeviceCapabilities - - armv7 - - UIStatusBarHidden - - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - UIInterfaceOrientationPortraitUpsideDown - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - - diff --git a/remoting/ios/Chromoting_unittests/Chromoting_unittests.xcdatamodeld/.xccurrentversion b/remoting/ios/Chromoting_unittests/Chromoting_unittests.xcdatamodeld/.xccurrentversion deleted file mode 100644 index 855b6eeccad1a..0000000000000 --- a/remoting/ios/Chromoting_unittests/Chromoting_unittests.xcdatamodeld/.xccurrentversion +++ /dev/null @@ -1,8 +0,0 @@ - - - - - _XCCurrentVersionName - Chromoting_unittests.xcdatamodel - - diff --git a/remoting/ios/Chromoting_unittests/Chromoting_unittests.xcdatamodeld/Chromoting_unittests.xcdatamodel/contents b/remoting/ios/Chromoting_unittests/Chromoting_unittests.xcdatamodeld/Chromoting_unittests.xcdatamodel/contents deleted file mode 100644 index 193f33c9c4d16..0000000000000 --- a/remoting/ios/Chromoting_unittests/Chromoting_unittests.xcdatamodeld/Chromoting_unittests.xcdatamodel/contents +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/remoting/ios/Chromoting_unittests/Images.xcassets/AppIcon.appiconset/Contents.json b/remoting/ios/Chromoting_unittests/Images.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 91bf9c14a7303..0000000000000 --- a/remoting/ios/Chromoting_unittests/Images.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "images" : [ - { - "idiom" : "iphone", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "iphone", - "size" : "60x60", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "29x29", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "40x40", - "scale" : "2x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "1x" - }, - { - "idiom" : "ipad", - "size" : "76x76", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/remoting/ios/Chromoting_unittests/Images.xcassets/LaunchImage.launchimage/Contents.json b/remoting/ios/Chromoting_unittests/Images.xcassets/LaunchImage.launchimage/Contents.json deleted file mode 100644 index 6f870a4629d2b..0000000000000 --- a/remoting/ios/Chromoting_unittests/Images.xcassets/LaunchImage.launchimage/Contents.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "images" : [ - { - "orientation" : "portrait", - "idiom" : "iphone", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "portrait", - "idiom" : "iphone", - "subtype" : "retina4", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "1x" - }, - { - "orientation" : "portrait", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - }, - { - "orientation" : "landscape", - "idiom" : "ipad", - "extent" : "full-screen", - "minimum-system-version" : "7.0", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} \ No newline at end of file diff --git a/remoting/ios/Chromoting_unittests/en.lproj/InfoPlist.strings b/remoting/ios/Chromoting_unittests/en.lproj/InfoPlist.strings deleted file mode 100644 index 477b28ff8f86a..0000000000000 --- a/remoting/ios/Chromoting_unittests/en.lproj/InfoPlist.strings +++ /dev/null @@ -1,2 +0,0 @@ -/* Localized versions of Info.plist keys */ - diff --git a/remoting/ios/Chromoting_unittests/main.mm b/remoting/ios/Chromoting_unittests/main.mm deleted file mode 100644 index 3efc1cdb24ed8..0000000000000 --- a/remoting/ios/Chromoting_unittests/main.mm +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import - -#include "base/memory/scoped_ptr.h" -#include "base/test/test_suite.h" -#include "testing/gtest_mac.h" - -#import "remoting/ios/Chromoting_unittests/main_no_arc.h" - -int main(int argc, char* argv[]) { - // Initialization that must occur with no Automatic Reference Counting (ARC) - remoting::main_no_arc::init(); - - testing::InitGoogleTest(&argc, argv); - scoped_ptr runner(new base::TestSuite(argc, argv)); - runner.get()->Run(); -} diff --git a/remoting/ios/Chromoting_unittests/main_no_arc.cc b/remoting/ios/Chromoting_unittests/main_no_arc.cc deleted file mode 100644 index 2bfbd872b6dc0..0000000000000 --- a/remoting/ios/Chromoting_unittests/main_no_arc.cc +++ /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. - -#include "remoting/ios/Chromoting_unittests/main_no_arc.h" - -#include "base/message_loop/message_loop.h" - -namespace remoting { -namespace main_no_arc { - -void init() { - // Declare the pump factory before the test suite can declare it. The test - // suite assumed we are running in x86 simulator, but this test project runs - // on an actual device - base::MessageLoop::InitMessagePumpForUIFactory(&base::MessagePumpMac::Create); -} - -} // main_no_arc -} // remoting diff --git a/remoting/ios/Chromoting_unittests/main_no_arc.h b/remoting/ios/Chromoting_unittests/main_no_arc.h deleted file mode 100644 index 1d97761ce175e..0000000000000 --- a/remoting/ios/Chromoting_unittests/main_no_arc.h +++ /dev/null @@ -1,11 +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. - -namespace remoting { -namespace main_no_arc { - -void init(); - -} // main_no_arc -} // remoting diff --git a/remoting/ios/DEPS b/remoting/ios/DEPS deleted file mode 100644 index c3c25f4764a25..0000000000000 --- a/remoting/ios/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+remoting/host", -] diff --git a/remoting/ios/app_delegate.h b/remoting/ios/app_delegate.h deleted file mode 100644 index bfd60ed6be4d9..0000000000000 --- a/remoting/ios/app_delegate.h +++ /dev/null @@ -1,17 +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 REMOTING_IOS_APP_DELEGATE_H_ -#define REMOTING_IOS_APP_DELEGATE_H_ - -#import - -// Default created delegate class for the entire application -@interface AppDelegate : UIResponder - -@property(strong, nonatomic) UIWindow* window; - -@end - -#endif // REMOTING_IOS_APP_DELEGATE_H_ \ No newline at end of file diff --git a/remoting/ios/app_delegate.mm b/remoting/ios/app_delegate.mm deleted file mode 100644 index 7b8fc366383fa..0000000000000 --- a/remoting/ios/app_delegate.mm +++ /dev/null @@ -1,18 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/app_delegate.h" - -@implementation AppDelegate - -- (BOOL)application:(UIApplication*)application - didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { - return YES; -} - -@end diff --git a/remoting/ios/authorize.h b/remoting/ios/authorize.h deleted file mode 100644 index 1b3e0eab657d4..0000000000000 --- a/remoting/ios/authorize.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_IOS_AUTHORIZE_H_ -#define REMOTING_IOS_AUTHORIZE_H_ - -#import - -// TODO (aboone) This include is for The Google Toolbox for Mac OAuth 2 -// https://code.google.com/p/gtm-oauth2/ This may need to be added as a -// third-party or locate the proper project in Chromium. -#import "GTMOAuth2Authentication.h" - -@interface Authorize : NSObject - -+ (GTMOAuth2Authentication*)getAnyExistingAuthorization; - -+ (void)beginRequest:(GTMOAuth2Authentication*)authorization - delegate:self - didFinishSelector:(SEL)sel; - -+ (void)appendCredentials:(NSMutableURLRequest*)request; - -+ (UINavigationController*)createLoginController:(id)delegate - finishedSelector:(SEL)finishedSelector; - -@end - -#endif // REMOTING_IOS_AUTHORIZE_H_ diff --git a/remoting/ios/authorize.mm b/remoting/ios/authorize.mm deleted file mode 100644 index 57f85f6edd03e..0000000000000 --- a/remoting/ios/authorize.mm +++ /dev/null @@ -1,122 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -// TODO (aboone) This include is for The Google Toolbox for Mac OAuth 2 -// Controllers https://code.google.com/p/gtm-oauth2/ This may need to be added -// as a third-party or locate the proper project in Chromium. -#import "GTMOAuth2ViewControllerTouch.h" - -#include "google_apis/google_api_keys.h" -#import "remoting/ios/authorize.h" -#include "remoting/base/service_urls.h" -// TODO (aboone) Pulling in some service values from the host side. The cc's -// are also compiled as part of this project because the target remoting_host -// does not build on iOS right now. -#include "remoting/host/setup/oauth_helper.h" - -namespace { -static NSString* const kKeychainItemName = @"Google Chromoting iOS"; - -NSString* ClientId() { - return - [NSString stringWithUTF8String:google_apis::GetOAuth2ClientID( - google_apis::CLIENT_REMOTING).c_str()]; -} - -NSString* ClientSecret() { - return - [NSString stringWithUTF8String:google_apis::GetOAuth2ClientSecret( - google_apis::CLIENT_REMOTING).c_str()]; -} - -NSString* Scopes() { - return [NSString stringWithUTF8String:remoting::GetOauthScope().c_str()]; -} - -NSMutableString* HostURL() { - return - [NSMutableString stringWithUTF8String:remoting::ServiceUrls::GetInstance() - ->directory_hosts_url() - .c_str()]; -} - -NSString* APIKey() { - return [NSString stringWithUTF8String:google_apis::GetAPIKey().c_str()]; -} - -} // namespace - -@implementation Authorize - -+ (GTMOAuth2Authentication*)getAnyExistingAuthorization { - // Ensure the google_apis lib has keys - // If this check fails then google_apis was not built right - // TODO (aboone) For now we specify the preprocessor macros for - // GOOGLE_CLIENT_SECRET_REMOTING and GOOGLE_CLIENT_ID_REMOTING when building - // the google_apis target. The values may be developer specific, and should - // be well know to the project staff. - // See http://www.chromium.org/developers/how-tos/api-keys for more general - // information. - DCHECK(![ClientId() isEqualToString:@"dummytoken"]); - - return [GTMOAuth2ViewControllerTouch - authForGoogleFromKeychainForName:kKeychainItemName - clientID:ClientId() - clientSecret:ClientSecret()]; -} - -+ (void)beginRequest:(GTMOAuth2Authentication*)authReq - delegate:(id)delegate - didFinishSelector:(SEL)sel { - // Build request URL using API HTTP endpoint, and our api key - NSMutableString* hostsUrl = HostURL(); - [hostsUrl appendString:@"?key="]; - [hostsUrl appendString:APIKey()]; - - NSMutableURLRequest* theRequest = - [NSMutableURLRequest requestWithURL:[NSURL URLWithString:hostsUrl]]; - - // Add scopes if needed - NSString* scope = authReq.scope; - - if ([scope rangeOfString:Scopes()].location == NSNotFound) { - scope = [GTMOAuth2Authentication scopeWithStrings:scope, Scopes(), nil]; - authReq.scope = scope; - } - - // Execute request async - [authReq authorizeRequest:theRequest delegate:delegate didFinishSelector:sel]; -} - -+ (void)appendCredentials:(NSMutableURLRequest*)request { - // Add credentials for service - [request addValue:ClientId() forHTTPHeaderField:@"client_id"]; - [request addValue:ClientSecret() forHTTPHeaderField:@"client_secret"]; -} - -+ (UINavigationController*)createLoginController:(id)delegate - finishedSelector:(SEL)finishedSelector { - [GTMOAuth2ViewControllerTouch - removeAuthFromKeychainForName:kKeychainItemName]; - - // When the sign in is complete a http redirection occurs, and the - // user would see the output. We do not want the user to notice this - // transition. Wrapping the oAuth2 Controller in a - // UINavigationController causes the view to render as a blank/black - // page when a http redirection occurs. - return [[UINavigationController alloc] - initWithRootViewController:[[GTMOAuth2ViewControllerTouch alloc] - initWithScope:Scopes() - clientID:ClientId() - clientSecret:ClientSecret() - keychainItemName:kKeychainItemName - delegate:delegate - finishedSelector:finishedSelector]]; -} - -@end diff --git a/remoting/ios/bridge/DEPS b/remoting/ios/bridge/DEPS deleted file mode 100644 index 8ef25d431ced5..0000000000000 --- a/remoting/ios/bridge/DEPS +++ /dev/null @@ -1,8 +0,0 @@ -include_rules = [ - "+net/url_request", - - "-remoting/host", - "+remoting/client", - "+remoting/protocol", - "+remoting/signaling", -] diff --git a/remoting/ios/bridge/client_instance.cc b/remoting/ios/bridge/client_instance.cc deleted file mode 100644 index faeb7450aef43..0000000000000 --- a/remoting/ios/bridge/client_instance.cc +++ /dev/null @@ -1,384 +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 "remoting/ios/bridge/client_instance.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/synchronization/waitable_event.h" -#include "net/socket/client_socket_factory.h" -#include "remoting/base/url_request_context_getter.h" -#include "remoting/client/audio_player.h" -#include "remoting/client/plugin/delegating_signal_strategy.h" -#include "remoting/ios/bridge/client_proxy.h" -#include "remoting/protocol/chromium_port_allocator.h" -#include "remoting/protocol/host_stub.h" -#include "remoting/protocol/libjingle_transport_factory.h" -#include "remoting/protocol/negotiating_client_authenticator.h" - -namespace { -const char* const kXmppServer = "talk.google.com"; -const int kXmppPort = 5222; -const bool kXmppUseTls = true; - -void DoNothing() {} -} // namespace - -namespace remoting { - -ClientInstance::ClientInstance(const base::WeakPtr& proxy, - const std::string& username, - const std::string& auth_token, - const std::string& host_jid, - const std::string& host_id, - const std::string& host_pubkey, - const std::string& pairing_id, - const std::string& pairing_secret) - : proxyToClient_(proxy), - host_id_(host_id), - host_jid_(host_jid), - create_pairing_(false) { - if (!base::MessageLoop::current()) { - VLOG(1) << "Starting main message loop"; - ui_loop_ = new base::MessageLoopForUI(); - ui_loop_->Attach(); - } else { - VLOG(1) << "Using existing main message loop"; - ui_loop_ = base::MessageLoopForUI::current(); - } - - VLOG(1) << "Spawning additional threads"; - - // |ui_loop_| runs on the main thread, so |ui_task_runner_| will run on the - // main thread. We can not kill the main thread when the message loop becomes - // idle so the callback function does nothing (as opposed to the typical - // base::MessageLoop::QuitClosure()) - ui_task_runner_ = new AutoThreadTaskRunner(ui_loop_->message_loop_proxy(), - base::Bind(&::DoNothing)); - - network_task_runner_ = AutoThread::CreateWithType( - "native_net", ui_task_runner_, base::MessageLoop::TYPE_IO); - - url_requester_ = new URLRequestContextGetter(network_task_runner_); - - client_context_.reset(new ClientContext(network_task_runner_)); - - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - - // Initialize XMPP config. - xmpp_config_.host = kXmppServer; - xmpp_config_.port = kXmppPort; - xmpp_config_.use_tls = kXmppUseTls; - xmpp_config_.username = username; - xmpp_config_.auth_token = auth_token; - xmpp_config_.auth_service = "oauth2"; - - // Initialize |authenticator_|. - scoped_ptr - token_fetcher(new TokenFetcherProxy( - base::Bind(&ChromotingJniInstance::FetchThirdPartyToken, - weak_factory_.GetWeakPtr()), - host_pubkey)); - - std::vector auth_methods; - auth_methods.push_back(protocol::AuthenticationMethod::Spake2Pair()); - auth_methods.push_back(protocol::AuthenticationMethod::Spake2( - protocol::AuthenticationMethod::HMAC_SHA256)); - auth_methods.push_back(protocol::AuthenticationMethod::Spake2( - protocol::AuthenticationMethod::NONE)); - - authenticator_.reset(new protocol::NegotiatingClientAuthenticator( - pairing_id, pairing_secret, host_id_, - base::Bind(&ClientInstance::FetchSecret, this), - token_fetcher.Pass(), auth_methods)); -} - -ClientInstance::~ClientInstance() {} - -void ClientInstance::Start() { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - - view_.reset(new FrameConsumerBridge( - base::Bind(&ClientProxy::RedrawCanvas, proxyToClient_))); - - // |consumer_proxy| must be created on the UI thread to proxy calls from the - // network or decode thread to the UI thread, but ownership will belong to a - // SoftwareVideoRenderer which runs on the network thread. - scoped_refptr consumer_proxy = - new FrameConsumerProxy(ui_task_runner_, view_->AsWeakPtr()); - - // Post a task to start connection - base::WaitableEvent done_event(true, false); - network_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ClientInstance::ConnectToHostOnNetworkThread, - this, - consumer_proxy, - base::Bind(&base::WaitableEvent::Signal, - base::Unretained(&done_event)))); - // Wait until initialization completes before continuing - done_event.Wait(); -} - -void ClientInstance::Cleanup() { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - - // |view_| must be destroyed on the UI thread before the producer is gone. - view_.reset(); - - base::WaitableEvent done_event(true, false); - network_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ClientInstance::DisconnectFromHostOnNetworkThread, - this, - base::Bind(&base::WaitableEvent::Signal, - base::Unretained(&done_event)))); - // Wait until we are fully disconnected before continuing - done_event.Wait(); -} - -// HOST attempts to continue automatically with previously supplied credentials, -// if it can't it requests the user's PIN. -void ClientInstance::FetchSecret( - bool pairable, - const protocol::SecretFetchedCallback& callback) { - if (!ui_task_runner_->BelongsToCurrentThread()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ClientInstance::FetchSecret, this, pairable, callback)); - return; - } - - pin_callback_ = callback; - - if (proxyToClient_) { - // Delete pairing credentials if they exist. - proxyToClient_->CommitPairingCredentials(host_id_, "", ""); - - proxyToClient_->DisplayAuthenticationPrompt(pairable); - } -} - -void ClientInstance::ProvideSecret(const std::string& pin, - bool create_pairing) { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - create_pairing_ = create_pairing; - - // Before this function can complete, FetchSecret must be called - DCHECK(!pin_callback_.is_null()); - network_task_runner_->PostTask(FROM_HERE, base::Bind(pin_callback_, pin)); -} - -void ClientInstance::PerformMouseAction( - const webrtc::DesktopVector& position, - const webrtc::DesktopVector& wheel_delta, - int /* protocol::MouseEvent_MouseButton */ whichButton, - bool button_down) { - if (!network_task_runner_->BelongsToCurrentThread()) { - network_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ClientInstance::PerformMouseAction, - this, - position, - wheel_delta, - whichButton, - button_down)); - return; - } - - protocol::MouseEvent_MouseButton mButton; - - // Button must be within the bounds of the MouseEvent_MouseButton enum. - switch (whichButton) { - case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT: - mButton = - protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_LEFT; - break; - case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX: - mButton = - protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MAX; - break; - case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_MIDDLE: - mButton = protocol::MouseEvent_MouseButton:: - MouseEvent_MouseButton_BUTTON_MIDDLE; - break; - case protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT: - mButton = - protocol::MouseEvent_MouseButton::MouseEvent_MouseButton_BUTTON_RIGHT; - break; - case protocol::MouseEvent_MouseButton:: - MouseEvent_MouseButton_BUTTON_UNDEFINED: - mButton = protocol::MouseEvent_MouseButton:: - MouseEvent_MouseButton_BUTTON_UNDEFINED; - break; - default: - LOG(FATAL) << "Invalid constant for MouseEvent_MouseButton"; - mButton = protocol::MouseEvent_MouseButton:: - MouseEvent_MouseButton_BUTTON_UNDEFINED; - break; - } - - protocol::MouseEvent action; - action.set_x(position.x()); - action.set_y(position.y()); - action.set_wheel_delta_x(wheel_delta.x()); - action.set_wheel_delta_y(wheel_delta.y()); - action.set_button(mButton); - if (mButton != protocol::MouseEvent::BUTTON_UNDEFINED) - action.set_button_down(button_down); - - client_->input_stub()->InjectMouseEvent(action); -} - -void ClientInstance::PerformKeyboardAction(int key_code, bool key_down) { - if (!network_task_runner_->BelongsToCurrentThread()) { - network_task_runner_->PostTask( - FROM_HERE, - base::Bind( - &ClientInstance::PerformKeyboardAction, this, key_code, key_down)); - return; - } - - protocol::KeyEvent action; - action.set_usb_keycode(key_code); - action.set_pressed(key_down); - client_->input_stub()->InjectKeyEvent(action); -} - -void ClientInstance::OnConnectionState(protocol::ConnectionToHost::State state, - protocol::ErrorCode error) { - if (!ui_task_runner_->BelongsToCurrentThread()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ClientInstance::OnConnectionState, this, state, error)); - return; - } - - // TODO (aboone) This functionality is not scheduled for QA yet. - // if (create_pairing_ && state == protocol::ConnectionToHost::CONNECTED) { - // VLOG(1) << "Attempting to pair with host"; - // protocol::PairingRequest request; - // request.set_client_name("iOS"); - // client_->host_stub()->RequestPairing(request); - // } - - if (proxyToClient_) - proxyToClient_->ReportConnectionStatus(state, error); -} - -void ClientInstance::OnConnectionReady(bool ready) { - // We ignore this message, since OnConnectionState tells us the same thing. -} - -void ClientInstance::OnRouteChanged(const std::string& channel_name, - const protocol::TransportRoute& route) { - VLOG(1) << "Using " << protocol::TransportRoute::GetTypeString(route.type) - << " connection for " << channel_name << " channel"; -} - -void ClientInstance::SetCapabilities(const std::string& capabilities) { -} - -void ClientInstance::SetPairingResponse( - const protocol::PairingResponse& response) { - if (!ui_task_runner_->BelongsToCurrentThread()) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&ClientInstance::SetPairingResponse, this, response)); - return; - } - - VLOG(1) << "Successfully established pairing with host"; - - if (proxyToClient_) - proxyToClient_->CommitPairingCredentials( - host_id_, response.client_id(), response.shared_secret()); -} - -void ClientInstance::DeliverHostMessage( - const protocol::ExtensionMessage& message) { - NOTIMPLEMENTED(); -} - -// Returning interface of protocol::ClipboardStub -protocol::ClipboardStub* ClientInstance::GetClipboardStub() { return this; } - -// Returning interface of protocol::CursorShapeStub -protocol::CursorShapeStub* ClientInstance::GetCursorShapeStub() { return this; } - -void ClientInstance::InjectClipboardEvent( - const protocol::ClipboardEvent& event) { - NOTIMPLEMENTED(); -} - -void ClientInstance::SetCursorShape(const protocol::CursorShapeInfo& shape) { - if (!ui_task_runner_->BelongsToCurrentThread()) { - ui_task_runner_->PostTask( - FROM_HERE, base::Bind(&ClientInstance::SetCursorShape, this, shape)); - return; - } - if (proxyToClient_) - proxyToClient_->UpdateCursorShape(shape); -} - -void ClientInstance::ConnectToHostOnNetworkThread( - scoped_refptr consumer_proxy, - const base::Closure& done) { - DCHECK(network_task_runner_->BelongsToCurrentThread()); - - client_context_->Start(); - - video_renderer_.reset( - new SoftwareVideoRenderer(client_context_->main_task_runner(), - client_context_->decode_task_runner(), - consumer_proxy)); - - view_->Initialize(video_renderer_.get()); - - client_.reset(new ChromotingClient(client_context_.get(), - this, - video_renderer_.get(), - scoped_ptr())); - - signaling_.reset( - new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), - url_requester_, - xmpp_config_)); - - protocol::NetworkSettings network_settings( - protocol::NetworkSettings::NAT_TRAVERSAL_ENABLED); - - scoped_ptr port_allocator( - protocol::ChromiumPortAllocator::Create(url_requester_, - network_settings)); - - scoped_ptr transport_factory( - new protocol::LibjingleTransportFactory( - signaling_.get(), - port_allocator.PassAs(), - network_settings)); - - client_->Start(signaling_.get(), authenticator_.Pass(), - transport_factory.Pass(), host_jid_, std::string()); - - if (!done.is_null()) - done.Run(); -} - -void ClientInstance::DisconnectFromHostOnNetworkThread( - const base::Closure& done) { - DCHECK(network_task_runner_->BelongsToCurrentThread()); - - host_id_.clear(); - - // |client_| must be torn down before |signaling_|. - client_.reset(); - signaling_.reset(); - video_renderer_.reset(); - client_context_->Stop(); - if (!done.is_null()) - done.Run(); -} - -} // namespace remoting diff --git a/remoting/ios/bridge/client_instance.h b/remoting/ios/bridge/client_instance.h deleted file mode 100644 index 1cd738e489480..0000000000000 --- a/remoting/ios/bridge/client_instance.h +++ /dev/null @@ -1,158 +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 REMOTING_IOS_BRIDGE_CLIENT_INSTANCE_H_ -#define REMOTING_IOS_BRIDGE_CLIENT_INSTANCE_H_ - -#include - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/weak_ptr.h" -#include "base/message_loop/message_loop.h" -#include "net/url_request/url_request_context_getter.h" -#include "remoting/base/auto_thread.h" -#include "remoting/client/chromoting_client.h" -#include "remoting/client/client_context.h" -#include "remoting/client/client_user_interface.h" -#include "remoting/client/frame_consumer_proxy.h" -#include "remoting/client/software_video_renderer.h" -#include "remoting/ios/bridge/frame_consumer_bridge.h" -#include "remoting/protocol/clipboard_stub.h" -#include "remoting/protocol/cursor_shape_stub.h" -#include "remoting/protocol/network_settings.h" -#include "remoting/signaling/xmpp_signal_strategy.h" - -namespace remoting { - -class ClientProxy; - -// ClientUserInterface that indirectly makes and receives OBJ_C calls from the -// UI application -class ClientInstance : public ClientUserInterface, - public protocol::ClipboardStub, - public protocol::CursorShapeStub, - public base::RefCountedThreadSafe { - public: - // Initiates a connection with the specified host. Call from the UI thread. To - // connect with an unpaired host, pass in |pairing_id| and |pairing_secret| as - // empty strings. - ClientInstance(const base::WeakPtr& proxy, - const std::string& username, - const std::string& auth_token, - const std::string& host_jid, - const std::string& host_id, - const std::string& host_pubkey, - const std::string& pairing_id, - const std::string& pairing_secret); - - // Begins the connection process. Should not be called again until after - // |CleanUp| - void Start(); - - // Terminates the current connection (if it hasn't already failed) and cleans - // up. Must be called before destruction can occur or a memory leak may occur. - void Cleanup(); - - // Notifies the user interface that the user needs to enter a PIN. The - // current authentication attempt is put on hold until |callback| is invoked. - // May be called on any thread. - void FetchSecret(bool pairable, - const protocol::SecretFetchedCallback& callback); - - // Provides the user's PIN and resumes the host authentication attempt. Call - // on the UI thread once the user has finished entering this PIN into the UI, - // but only after the UI has been asked to provide a PIN (via FetchSecret()). - void ProvideSecret(const std::string& pin, bool create_pair); - - // Moves the host's cursor to the specified coordinates, optionally with some - // mouse button depressed. If |button| is BUTTON_UNDEFINED, no click is made. - void PerformMouseAction( - const webrtc::DesktopVector& position, - const webrtc::DesktopVector& wheel_delta, - int /* protocol::MouseEvent_MouseButton */ whichButton, - bool button_down); - - // Sends the provided keyboard scan code to the host. - void PerformKeyboardAction(int key_code, bool key_down); - - // ClientUserInterface implementation. - virtual void OnConnectionState(protocol::ConnectionToHost::State state, - protocol::ErrorCode error) OVERRIDE; - virtual void OnConnectionReady(bool ready) OVERRIDE; - virtual void OnRouteChanged(const std::string& channel_name, - const protocol::TransportRoute& route) OVERRIDE; - virtual void SetCapabilities(const std::string& capabilities) OVERRIDE; - virtual void SetPairingResponse(const protocol::PairingResponse& response) - OVERRIDE; - virtual void DeliverHostMessage(const protocol::ExtensionMessage& message) - OVERRIDE; - virtual protocol::ClipboardStub* GetClipboardStub() OVERRIDE; - virtual protocol::CursorShapeStub* GetCursorShapeStub() OVERRIDE; - - // CursorShapeStub implementation. - virtual void InjectClipboardEvent(const protocol::ClipboardEvent& event) - OVERRIDE; - - // ClipboardStub implementation. - virtual void SetCursorShape(const protocol::CursorShapeInfo& shape) OVERRIDE; - - private: - // This object is ref-counted, so it cleans itself up. - virtual ~ClientInstance(); - - void ConnectToHostOnNetworkThread( - scoped_refptr consumer_proxy, - const base::Closure& done); - void DisconnectFromHostOnNetworkThread(const base::Closure& done); - - // Proxy to exchange messages between the - // common Chromoting protocol and UI Application. - base::WeakPtr proxyToClient_; - - // ID of the host we are connecting to. - std::string host_id_; - std::string host_jid_; - - // This group of variables is to be used on the display thread. - scoped_ptr video_renderer_; - scoped_ptr view_; - - // This group of variables is to be used on the network thread. - scoped_ptr client_context_; - scoped_ptr authenticator_; - scoped_ptr client_; - XmppSignalStrategy::XmppServerConfig xmpp_config_; - scoped_ptr signaling_; // Must outlive client_ - - // Pass this the user's PIN once we have it. To be assigned and accessed on - // the UI thread, but must be posted to the network thread to call it. - protocol::SecretFetchedCallback pin_callback_; - - // Indicates whether to establish a new pairing with this host. This is - // modified in ProvideSecret(), but thereafter to be used only from the - // network thread. (This is safe because ProvideSecret() is invoked at most - // once per run, and always before any reference to this flag.) - bool create_pairing_; - - // Chromium code's connection to the OBJ_C message loop. Once created the - // MessageLoop will live for the life of the program. An attempt was made to - // create the primary message loop earlier in the programs life, but a - // MessageLoop requires ARC to be disabled. - base::MessageLoopForUI* ui_loop_; - - // References to native threads. - scoped_refptr ui_task_runner_; - scoped_refptr network_task_runner_; - - scoped_refptr url_requester_; - - friend class base::RefCountedThreadSafe; - - DISALLOW_COPY_AND_ASSIGN(ClientInstance); -}; - -} // namespace remoting - -#endif // REMOTING_IOS_BRIDGE_CLIENT_INSTANCE_H_ diff --git a/remoting/ios/bridge/client_instance_unittest.mm b/remoting/ios/bridge/client_instance_unittest.mm deleted file mode 100644 index 8470e7accdff2..0000000000000 --- a/remoting/ios/bridge/client_instance_unittest.mm +++ /dev/null @@ -1,319 +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 "remoting/ios/bridge/client_instance.h" - -#include "base/compiler_specific.h" -#include "base/run_loop.h" -#include "base/mac/scoped_nsobject.h" -#include "base/synchronization/waitable_event.h" -#include "remoting/protocol/libjingle_transport_factory.h" -#import "testing/gtest_mac.h" - -#include "remoting/ios/data_store.h" -#include "remoting/ios/bridge/client_proxy.h" -#include "remoting/ios/bridge/client_proxy_delegate_wrapper.h" - -@interface ClientProxyDelegateForClientInstanceTester - : NSObject - -- (void)resetDidReceiveSomething; - -// Validating what was received is outside of the scope for this test unit. See -// ClientProxyUnittest for those tests. -@property(nonatomic, assign) BOOL didReceiveSomething; - -@end - -@implementation ClientProxyDelegateForClientInstanceTester - -@synthesize didReceiveSomething = _didReceiveSomething; - -- (void)resetDidReceiveSomething { - _didReceiveSomething = false; -} - -- (void)requestHostPin:(BOOL)pairingSupported { - _didReceiveSomething = true; -} - -- (void)connected { - _didReceiveSomething = true; -} - -- (void)connectionStatus:(NSString*)statusMessage { - _didReceiveSomething = true; -} - -- (void)connectionFailed:(NSString*)errorMessage { - _didReceiveSomething = true; -} - -- (void)applyFrame:(const webrtc::DesktopSize&)size - stride:(NSInteger)stride - data:(uint8_t*)data - regions:(const std::vector&)regions { - _didReceiveSomething = true; -} - -- (void)applyCursor:(const webrtc::DesktopSize&)size - hotspot:(const webrtc::DesktopVector&)hotspot - cursorData:(uint8_t*)data { - _didReceiveSomething = true; -} - -@end - -namespace remoting { - -namespace { - -const std::string kHostId = "HostIdTest"; -const std::string kPairingId = "PairingIdTest"; -const std::string kPairingSecret = "PairingSecretTest"; -const std::string kSecretPin = "SecretPinTest"; - -// TODO(aboone) should be able to call RunLoop().RunUntilIdle() instead but -// MessagePumpUIApplication::DoRun is marked NOTREACHED() -void RunCFMessageLoop() { - int result; - do { // Repeat until no messages remain - result = CFRunLoopRunInMode( - kCFRunLoopDefaultMode, - 0, // Execute queued messages, do not wait for additional messages - YES); // Do only one message at a time - } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished && - result != kCFRunLoopRunTimedOut); -} - -void SecretPinCallBack(const std::string& secret) { - ASSERT_STREQ(kSecretPin.c_str(), secret.c_str()); -} - -} // namespace - -class ClientInstanceTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - testDelegate_.reset( - [[ClientProxyDelegateForClientInstanceTester alloc] init]); - proxy_.reset(new ClientProxy( - [ClientProxyDelegateWrapper wrapDelegate:testDelegate_])); - instance_ = - new ClientInstance(proxy_->AsWeakPtr(), "", "", "", "", "", "", ""); - } - virtual void TearDown() OVERRIDE { - // Ensure memory is not leaking - // Notice Cleanup is safe to call, regardless of if Start() was ever called. - instance_->Cleanup(); - RunCFMessageLoop(); - // An object on the network thread which owns a reference to |instance_| may - // be cleaned up 'soon', but not immediately. Lets wait it out, up to 1 - // second. - for (int i = 0; i < 100; i++) { - if (!instance_->HasOneRef()) { - [NSThread sleepForTimeInterval:.01]; - } else { - break; - } - } - - // Remove the last reference from |instance_|, and destructor is called. - ASSERT_TRUE(instance_->HasOneRef()); - instance_ = NULL; - } - - void AssertAcknowledged(BOOL wasAcknowledged) { - ASSERT_EQ(wasAcknowledged, [testDelegate_ didReceiveSomething]); - // Reset for the next test - [testDelegate_ resetDidReceiveSomething]; - } - - void TestStatusAndError(protocol::ConnectionToHost::State state, - protocol::ErrorCode error) { - instance_->OnConnectionState(state, error); - AssertAcknowledged(true); - } - - void TestConnectionStatus(protocol::ConnectionToHost::State state) { - TestStatusAndError(state, protocol::ErrorCode::OK); - TestStatusAndError(state, protocol::ErrorCode::PEER_IS_OFFLINE); - TestStatusAndError(state, protocol::ErrorCode::SESSION_REJECTED); - TestStatusAndError(state, protocol::ErrorCode::INCOMPATIBLE_PROTOCOL); - TestStatusAndError(state, protocol::ErrorCode::AUTHENTICATION_FAILED); - TestStatusAndError(state, protocol::ErrorCode::CHANNEL_CONNECTION_ERROR); - TestStatusAndError(state, protocol::ErrorCode::SIGNALING_ERROR); - TestStatusAndError(state, protocol::ErrorCode::SIGNALING_TIMEOUT); - TestStatusAndError(state, protocol::ErrorCode::HOST_OVERLOAD); - TestStatusAndError(state, protocol::ErrorCode::UNKNOWN_ERROR); - } - - base::scoped_nsobject - testDelegate_; - scoped_ptr proxy_; - scoped_refptr instance_; -}; - -TEST_F(ClientInstanceTest, Create) { - // This is a test for memory leaking. Ensure a completely unused instance of - // ClientInstance is destructed. - - ASSERT_TRUE(instance_ != NULL); - ASSERT_TRUE(instance_->HasOneRef()); -} - -TEST_F(ClientInstanceTest, CreateAndStart) { - // This is a test for memory leaking. Ensure a properly used instance of - // ClientInstance is destructed. - - ASSERT_TRUE(instance_ != NULL); - ASSERT_TRUE(instance_->HasOneRef()); - - instance_->Start(); - RunCFMessageLoop(); - ASSERT_TRUE(!instance_->HasOneRef()); // more than one -} - -TEST_F(ClientInstanceTest, SecretPin) { - NSString* hostId = [NSString stringWithUTF8String:kHostId.c_str()]; - NSString* pairingId = [NSString stringWithUTF8String:kPairingId.c_str()]; - NSString* pairingSecret = - [NSString stringWithUTF8String:kPairingSecret.c_str()]; - - DataStore* store = [DataStore sharedStore]; - - const HostPreferences* host = [store createHost:hostId]; - host.pairId = pairingId; - host.pairSecret = pairingSecret; - [store saveChanges]; - - // Suggesting that our pairing Id is known, but since its obviously not we - // expect it to be discarded when requesting the PIN. - instance_ = new ClientInstance( - proxy_->AsWeakPtr(), "", "", "", kHostId, "", kPairingId, kPairingSecret); - - instance_->Start(); - RunCFMessageLoop(); - - instance_->FetchSecret(false, base::Bind(&SecretPinCallBack)); - RunCFMessageLoop(); - AssertAcknowledged(true); - - // The pairing information was discarded - host = [store getHostForId:hostId]; - ASSERT_NSEQ(@"", host.pairId); - ASSERT_NSEQ(@"", host.pairSecret); - - instance_->ProvideSecret(kSecretPin, false); - RunCFMessageLoop(); -} - -TEST_F(ClientInstanceTest, NoProxy) { - // After the proxy is released, we still expect quite a few functions to be - // able to run, but not produce any output. Some of these are just being - // executed for code coverage, the outputs are not pertinent to this test - // unit. - proxy_.reset(); - - instance_->Start(); - RunCFMessageLoop(); - - instance_->FetchSecret(false, base::Bind(&SecretPinCallBack)); - AssertAcknowledged(false); - - instance_->ProvideSecret(kSecretPin, false); - AssertAcknowledged(false); - - instance_->PerformMouseAction( - webrtc::DesktopVector(0, 0), webrtc::DesktopVector(0, 0), 0, false); - AssertAcknowledged(false); - - instance_->PerformKeyboardAction(0, false); - AssertAcknowledged(false); - - instance_->OnConnectionState(protocol::ConnectionToHost::State::CONNECTED, - protocol::ErrorCode::OK); - AssertAcknowledged(false); - - instance_->OnConnectionReady(false); - AssertAcknowledged(false); - - instance_->OnRouteChanged("", protocol::TransportRoute()); - AssertAcknowledged(false); - - // SetCapabilities requires a host connection to be established - // instance_->SetCapabilities(""); - // AssertAcknowledged(false); - - instance_->SetPairingResponse(protocol::PairingResponse()); - AssertAcknowledged(false); - - instance_->DeliverHostMessage(protocol::ExtensionMessage()); - AssertAcknowledged(false); - - ASSERT_TRUE(instance_->GetClipboardStub() != NULL); - ASSERT_TRUE(instance_->GetCursorShapeStub() != NULL); - ASSERT_TRUE(instance_->GetTokenFetcher("") == NULL); - - instance_->InjectClipboardEvent(protocol::ClipboardEvent()); - AssertAcknowledged(false); - - instance_->SetCursorShape(protocol::CursorShapeInfo()); - AssertAcknowledged(false); -} - -TEST_F(ClientInstanceTest, OnConnectionStateINITIALIZING) { - TestConnectionStatus(protocol::ConnectionToHost::State::INITIALIZING); -} - -TEST_F(ClientInstanceTest, OnConnectionStateCONNECTING) { - TestConnectionStatus(protocol::ConnectionToHost::State::CONNECTING); -} - -TEST_F(ClientInstanceTest, OnConnectionStateAUTHENTICATED) { - TestConnectionStatus(protocol::ConnectionToHost::State::AUTHENTICATED); -} - -TEST_F(ClientInstanceTest, OnConnectionStateCONNECTED) { - TestConnectionStatus(protocol::ConnectionToHost::State::CONNECTED); -} - -TEST_F(ClientInstanceTest, OnConnectionStateFAILED) { - TestConnectionStatus(protocol::ConnectionToHost::State::FAILED); -} - -TEST_F(ClientInstanceTest, OnConnectionStateCLOSED) { - TestConnectionStatus(protocol::ConnectionToHost::State::CLOSED); -} - -TEST_F(ClientInstanceTest, OnConnectionReady) { - instance_->OnConnectionReady(true); - AssertAcknowledged(false); - instance_->OnConnectionReady(false); - AssertAcknowledged(false); -} - -TEST_F(ClientInstanceTest, OnRouteChanged) { - // Not expecting anything to happen - protocol::TransportRoute route; - - route.type = protocol::TransportRoute::DIRECT; - instance_->OnRouteChanged("", route); - AssertAcknowledged(false); - - route.type = protocol::TransportRoute::STUN; - instance_->OnRouteChanged("", route); - AssertAcknowledged(false); - - route.type = protocol::TransportRoute::RELAY; - instance_->OnRouteChanged("", route); - AssertAcknowledged(false); -} - -TEST_F(ClientInstanceTest, SetCursorShape) { - instance_->SetCursorShape(protocol::CursorShapeInfo()); - AssertAcknowledged(true); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/bridge/client_proxy.h b/remoting/ios/bridge/client_proxy.h deleted file mode 100644 index 8088570550f56..0000000000000 --- a/remoting/ios/bridge/client_proxy.h +++ /dev/null @@ -1,62 +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 REMOTING_IOS_BRIDGE_HOST_PROXY_H_ -#define REMOTING_IOS_BRIDGE_HOST_PROXY_H_ - -#include - -#include -#include "base/memory/weak_ptr.h" -#include "remoting/protocol/connection_to_host.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" - -#if defined(__OBJC__) -@class ClientProxyDelegateWrapper; -#else // __OBJC__ -class ClientProxyDelegateWrapper; -#endif // __OBJC__ - -namespace remoting { - -// Proxies incoming common Chromoting protocol (HOST) to the UI Application -// (CLIENT). The HOST will have a Weak reference to call member functions on -// the UI Thread. -class ClientProxy : public base::SupportsWeakPtr { - public: - ClientProxy(ClientProxyDelegateWrapper* wrapper); - - // Notifies the user of the current connection status. - void ReportConnectionStatus(protocol::ConnectionToHost::State state, - protocol::ErrorCode error); - - // Display a dialog box asking the user to enter a PIN. - void DisplayAuthenticationPrompt(bool pairing_supported); - - // Saves new pairing credentials to permanent storage. - void CommitPairingCredentials(const std::string& hostId, - const std::string& pairId, - const std::string& pairSecret); - - // Delivers the latest image buffer for the canvas. - void RedrawCanvas(const webrtc::DesktopSize& view_size, - webrtc::DesktopFrame* buffer, - const webrtc::DesktopRegion& region); - - // Updates cursor. - void UpdateCursorShape(const protocol::CursorShapeInfo& cursor_shape); - - private: - // Pointer to the UI application which implements the ClientProxyDelegate. - // (id) is similar to a (void*) |delegate_| is set from accepting a - // strongly typed @interface which wraps the @protocol ClientProxyDelegate. - // see comments for host_proxy_delegate_wrapper.h - id delegate_; - - DISALLOW_COPY_AND_ASSIGN(ClientProxy); -}; - -} // namespace remoting - -#endif // REMOTING_IOS_BRIDGE_HOST_PROXY_H_ diff --git a/remoting/ios/bridge/client_proxy.mm b/remoting/ios/bridge/client_proxy.mm deleted file mode 100644 index 8568b9d55ec65..0000000000000 --- a/remoting/ios/bridge/client_proxy.mm +++ /dev/null @@ -1,150 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#include "remoting/ios/bridge/client_proxy.h" - -#import "remoting/ios/data_store.h" -#import "remoting/ios/host_preferences.h" -#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h" - -namespace { -// The value indicating a successful connection has been established via a call -// to ReportConnectionStatus -const static int kSuccessfulConnection = 3; - -// Translate a connection status code integer to a NSString description -NSString* GetStatusMsgFromInteger(NSInteger code) { - switch (code) { - case 0: // INITIALIZING - return @"Initializing connection"; - case 1: // CONNECTING - return @"Connecting"; - case 2: // AUTHENTICATED - return @"Authenticated"; - case 3: // CONNECTED - return @"Connected"; - case 4: // FAILED - return @"Connection Failed"; - case 5: // CLOSED - return @"Connection closed"; - default: - return @"Unknown connection state"; - } -} - -// Translate a connection error code integer to a NSString description -NSString* GetErrorMsgFromInteger(NSInteger code) { - switch (code) { - case 1: // PEER_IS_OFFLINE - return @"Requested host is offline."; - case 2: // SESSION_REJECTED - return @"Session was rejected by the host."; - case 3: // INCOMPATIBLE_PROTOCOL - return @"Incompatible Protocol."; - case 4: // AUTHENTICATION_FAILED - return @"Authentication Failed."; - case 5: // CHANNEL_CONNECTION_ERROR - return @"Channel Connection Error"; - case 6: // SIGNALING_ERROR - return @"Signaling Error"; - case 7: // SIGNALING_TIMEOUT - return @"Signaling Timeout"; - case 8: // HOST_OVERLOAD - return @"Host Overload"; - case 9: // UNKNOWN_ERROR - return @"An unknown error has occurred, preventing the session " - "from opening."; - default: - return @"An unknown error code has occurred."; - } -} - -} // namespace - -namespace remoting { - -ClientProxy::ClientProxy(ClientProxyDelegateWrapper* wrapper) { - delegate_ = [wrapper delegate]; -} - -void ClientProxy::ReportConnectionStatus( - protocol::ConnectionToHost::State state, - protocol::ErrorCode error) { - DCHECK(delegate_); - if (state <= kSuccessfulConnection && error == protocol::ErrorCode::OK) { - // Report Progress - [delegate_ connectionStatus:GetStatusMsgFromInteger(state)]; - - if (state == kSuccessfulConnection) { - [delegate_ connected]; - } - } else { - [delegate_ connectionStatus:GetStatusMsgFromInteger(state)]; - if (error != protocol::ErrorCode::OK) { - [delegate_ connectionFailed:GetErrorMsgFromInteger(error)]; - } - } -} - -void ClientProxy::DisplayAuthenticationPrompt(bool pairing_supported) { - DCHECK(delegate_); - [delegate_ requestHostPin:pairing_supported]; -} - -void ClientProxy::CommitPairingCredentials(const std::string& hostId, - const std::string& pairId, - const std::string& pairSecret) { - DCHECK(delegate_); - NSString* nsHostId = [[NSString alloc] initWithUTF8String:hostId.c_str()]; - NSString* nsPairId = [[NSString alloc] initWithUTF8String:pairId.c_str()]; - NSString* nsPairSecret = - [[NSString alloc] initWithUTF8String:pairSecret.c_str()]; - - const HostPreferences* hostPrefs = - [[DataStore sharedStore] getHostForId:nsHostId]; - if (hostPrefs == nil) { - hostPrefs = [[DataStore sharedStore] createHost:nsHostId]; - } - if (hostPrefs) { - hostPrefs.pairId = nsPairId; - hostPrefs.pairSecret = nsPairSecret; - - [[DataStore sharedStore] saveChanges]; - } -} - -void ClientProxy::RedrawCanvas(const webrtc::DesktopSize& view_size, - webrtc::DesktopFrame* buffer, - const webrtc::DesktopRegion& region) { - DCHECK(delegate_); - std::vector regions; - - for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { - const webrtc::DesktopRect& rect(i.rect()); - - regions.push_back(webrtc::DesktopRect::MakeXYWH( - rect.left(), rect.top(), rect.width(), rect.height())); - } - - [delegate_ applyFrame:view_size - stride:buffer->stride() - data:buffer->data() - regions:regions]; -} - -void ClientProxy::UpdateCursorShape( - const protocol::CursorShapeInfo& cursor_shape) { - DCHECK(delegate_); - [delegate_ applyCursor:webrtc::DesktopSize(cursor_shape.width(), - cursor_shape.height()) - hotspot:webrtc::DesktopVector(cursor_shape.hotspot_x(), - cursor_shape.hotspot_y()) - cursorData:(uint8_t*)cursor_shape.data().c_str()]; -} - -} // namespace remoting diff --git a/remoting/ios/bridge/client_proxy_delegate.h b/remoting/ios/bridge/client_proxy_delegate.h deleted file mode 100644 index 7810a2a8a96f1..0000000000000 --- a/remoting/ios/bridge/client_proxy_delegate.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_H_ -#define REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_H_ - -#import -#import - -#include - -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -// Contract to provide for callbacks from the common Chromoting protocol to the -// UI Application. -@protocol ClientProxyDelegate - -// HOST request for client to input their PIN. -- (void)requestHostPin:(BOOL)pairingSupported; - -// HOST notification that a connection has been successfully opened. -- (void)connected; - -// HOST notification for a change in connections status. -- (void)connectionStatus:(NSString*)statusMessage; - -// HOST notification that a connection has failed. -- (void)connectionFailed:(NSString*)errorMessage; - -// A new Canvas (desktop) update has arrived. -- (void)applyFrame:(const webrtc::DesktopSize&)size - stride:(NSInteger)stride - data:(uint8_t*)data - regions:(const std::vector&)regions; - -// A new Cursor (mouse) update has arrived. -- (void)applyCursor:(const webrtc::DesktopSize&)size - hotspot:(const webrtc::DesktopVector&)hotspot - cursorData:(uint8_t*)data; -@end - -#endif // REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_H_ \ No newline at end of file diff --git a/remoting/ios/bridge/client_proxy_delegate_wrapper.h b/remoting/ios/bridge/client_proxy_delegate_wrapper.h deleted file mode 100644 index e57045bce3500..0000000000000 --- a/remoting/ios/bridge/client_proxy_delegate_wrapper.h +++ /dev/null @@ -1,33 +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 REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_WRAPPER_H_ -#define REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_WRAPPER_H_ - -#import -#import - -#include - -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -#import "remoting/ios/bridge/client_proxy_delegate.h" - -// Wraps ClientProxyDelegate in a class so C++ can accept a strongly typed -// pointer. C++ does not understand the id<> convention of passing around a -// OBJ_C @protocol pointer. So the @protocol is wrapped and the class is passed -// around. After accepting an instance of ClientProxyDelegateWrapper, the -// @protocol can be referenced as type (id), which is similar to a (void*), - -@interface ClientProxyDelegateWrapper : NSObject - -@property(nonatomic, retain, readonly) id delegate; - -- (id)init __unavailable; - -+ (id)wrapDelegate:(id)delegate; - -@end - -#endif // REMOTING_IOS_BRIDGE_HOST_PROXY_DELEGATE_WRAPPER_H_ diff --git a/remoting/ios/bridge/client_proxy_delegate_wrapper.mm b/remoting/ios/bridge/client_proxy_delegate_wrapper.mm deleted file mode 100644 index af558dc8714b4..0000000000000 --- a/remoting/ios/bridge/client_proxy_delegate_wrapper.mm +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h" - -@interface ClientProxyDelegateWrapper (Private) -- (id)initWithDelegate:(id)delegate; -@end - -@implementation ClientProxyDelegateWrapper - -@synthesize delegate = _delegate; - -- (id)initWithDelegate:(id)delegate { - self = [super init]; - if (self) { - _delegate = delegate; - } - return self; -} - -+ (id)wrapDelegate:(id)delegate { - return [[ClientProxyDelegateWrapper alloc] initWithDelegate:delegate]; -} - -@end diff --git a/remoting/ios/bridge/client_proxy_unittest.mm b/remoting/ios/bridge/client_proxy_unittest.mm deleted file mode 100644 index 16242983f1f76..0000000000000 --- a/remoting/ios/bridge/client_proxy_unittest.mm +++ /dev/null @@ -1,366 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/bridge/client_proxy.h" - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -#import "remoting/ios/data_store.h" -#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h" - -@interface ClientProxyDelegateTester : NSObject -@property(nonatomic, assign) BOOL isConnected; -@property(nonatomic, copy) NSString* statusMessage; -@property(nonatomic, copy) NSString* errorMessage; -@property(nonatomic, assign) BOOL isPairingSupported; -@property(nonatomic, assign) webrtc::DesktopSize size; -@property(nonatomic, assign) NSInteger stride; -@property(nonatomic, assign) uint8_t* data; -@property(nonatomic, assign) std::vector regions; -@property(nonatomic, assign) webrtc::DesktopVector hotspot; -@end - -@implementation ClientProxyDelegateTester - -@synthesize isConnected = _isConnected; -@synthesize statusMessage = _statusMessage; -@synthesize errorMessage = _errorMessage; -@synthesize isPairingSupported = _isPairingSupported; -@synthesize size = _size; -@synthesize stride = _stride; -@synthesize data = _data; -@synthesize regions = _regions; -@synthesize hotspot = _hotspot; - -- (void)connected { - _isConnected = true; -} - -- (void)connectionStatus:(NSString*)statusMessage { - _statusMessage = statusMessage; -} - -- (void)connectionFailed:(NSString*)errorMessage { - _errorMessage = errorMessage; -} - -- (void)requestHostPin:(BOOL)pairingSupported { - _isPairingSupported = pairingSupported; -} - -- (void)applyFrame:(const webrtc::DesktopSize&)size - stride:(NSInteger)stride - data:(uint8_t*)data - regions:(const std::vector&)regions { - _size = size; - _stride = stride; - _data = data; - _regions.assign(regions.begin(), regions.end()); -} - -- (void)applyCursor:(const webrtc::DesktopSize&)size - hotspot:(const webrtc::DesktopVector&)hotspot - cursorData:(uint8_t*)data { - _size = size; - _hotspot = hotspot; - _data = data; -} - -@end - -namespace remoting { - -namespace { - -NSString* kStatusINITIALIZING = @"Initializing connection"; -NSString* kStatusCONNECTING = @"Connecting"; -NSString* kStatusAUTHENTICATED = @"Authenticated"; -NSString* kStatusCONNECTED = @"Connected"; -NSString* kStatusFAILED = @"Connection Failed"; -NSString* kStatusCLOSED = @"Connection closed"; -NSString* kStatusDEFAULT = @"Unknown connection state"; - -NSString* kErrorPEER_IS_OFFLINE = @"Requested host is offline."; -NSString* kErrorSESSION_REJECTED = @"Session was rejected by the host."; -NSString* kErrorINCOMPATIBLE_PROTOCOL = @"Incompatible Protocol."; -NSString* kErrorAUTHENTICATION_FAILED = @"Authentication Failed."; -NSString* kErrorCHANNEL_CONNECTION_ERROR = @"Channel Connection Error"; -NSString* kErrorSIGNALING_ERROR = @"Signaling Error"; -NSString* kErrorSIGNALING_TIMEOUT = @"Signaling Timeout"; -NSString* kErrorHOST_OVERLOAD = @"Host Overload"; -NSString* kErrorUNKNOWN_ERROR = - @"An unknown error has occurred, preventing the session from opening."; -NSString* kErrorDEFAULT = @"An unknown error code has occurred."; - -const webrtc::DesktopSize kFrameSize(100, 100); - -// Note these are disjoint regions. Testing intersecting regions is beyond the -// scope of this test class. -const webrtc::DesktopRect kFrameSubRect1 = - webrtc::DesktopRect::MakeXYWH(0, 0, 10, 10); -const webrtc::DesktopRect kFrameSubRect2 = - webrtc::DesktopRect::MakeXYWH(11, 11, 10, 10); -const webrtc::DesktopRect kFrameSubRect3 = - webrtc::DesktopRect::MakeXYWH(22, 22, 10, 10); - -const int kCursorHeight = 10; -const int kCursorWidth = 20; -const int kCursorHotSpotX = 4; -const int kCursorHotSpotY = 8; -// |kCursorDataLength| is assumed to be evenly divisible by 4 -const int kCursorDataLength = kCursorHeight * kCursorWidth; -const uint32_t kCursorDataPattern = 0xF0E1D2C3; - -const std::string kHostName = "ClientProxyHostNameTest"; -const std::string kPairingId = "ClientProxyPairingIdTest"; -const std::string kPairingSecret = "ClientProxyPairingSecretTest"; - -} // namespace - -class ClientProxyTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - delegateTester_ = [[ClientProxyDelegateTester alloc] init]; - clientProxy_.reset(new ClientProxy( - [ClientProxyDelegateWrapper wrapDelegate:delegateTester_])); - } - - void ResetIsConnected() { delegateTester_.isConnected = false; } - - void TestConnnectionStatus(protocol::ConnectionToHost::State state, - NSString* expectedStatusMsg) { - ResetIsConnected(); - clientProxy_->ReportConnectionStatus(state, protocol::ErrorCode::OK); - EXPECT_NSEQ(expectedStatusMsg, delegateTester_.statusMessage); - - if (state == protocol::ConnectionToHost::State::CONNECTED) { - EXPECT_TRUE(delegateTester_.isConnected); - } else { - EXPECT_FALSE(delegateTester_.isConnected); - } - - TestErrorMessages(state, expectedStatusMsg); - } - - void TestForError(protocol::ConnectionToHost::State state, - protocol::ErrorCode errorCode, - NSString* expectedStatusMsg, - NSString* expectedErrorMsg) { - ResetIsConnected(); - clientProxy_->ReportConnectionStatus(state, errorCode); - EXPECT_FALSE(delegateTester_.isConnected); - EXPECT_NSEQ(expectedStatusMsg, delegateTester_.statusMessage); - EXPECT_NSEQ(expectedErrorMsg, delegateTester_.errorMessage); - } - - void TestErrorMessages(protocol::ConnectionToHost::State state, - NSString* expectedStatusMsg) { - TestForError(state, - protocol::ErrorCode::AUTHENTICATION_FAILED, - expectedStatusMsg, - kErrorAUTHENTICATION_FAILED); - TestForError(state, - protocol::ErrorCode::CHANNEL_CONNECTION_ERROR, - expectedStatusMsg, - kErrorCHANNEL_CONNECTION_ERROR); - TestForError(state, - protocol::ErrorCode::HOST_OVERLOAD, - expectedStatusMsg, - kErrorHOST_OVERLOAD); - TestForError(state, - protocol::ErrorCode::INCOMPATIBLE_PROTOCOL, - expectedStatusMsg, - kErrorINCOMPATIBLE_PROTOCOL); - TestForError(state, - protocol::ErrorCode::PEER_IS_OFFLINE, - expectedStatusMsg, - kErrorPEER_IS_OFFLINE); - TestForError(state, - protocol::ErrorCode::SESSION_REJECTED, - expectedStatusMsg, - kErrorSESSION_REJECTED); - TestForError(state, - protocol::ErrorCode::SIGNALING_ERROR, - expectedStatusMsg, - kErrorSIGNALING_ERROR); - TestForError(state, - protocol::ErrorCode::SIGNALING_TIMEOUT, - expectedStatusMsg, - kErrorSIGNALING_TIMEOUT); - TestForError(state, - protocol::ErrorCode::UNKNOWN_ERROR, - expectedStatusMsg, - kErrorUNKNOWN_ERROR); - TestForError(state, - static_cast(999), - expectedStatusMsg, - kErrorDEFAULT); - } - - void ValidateHost(const std::string& hostName, - const std::string& pairingId, - const std::string& pairingSecret) { - DataStore* store = [DataStore sharedStore]; - NSString* hostNameAsNSString = - [NSString stringWithUTF8String:hostName.c_str()]; - const HostPreferences* host = [store getHostForId:hostNameAsNSString]; - if (host != nil) { - [store removeHost:host]; - } - - clientProxy_->CommitPairingCredentials(hostName, pairingId, pairingSecret); - - host = [store getHostForId:hostNameAsNSString]; - - ASSERT_TRUE(host != nil); - ASSERT_STREQ(hostName.c_str(), [host.hostId UTF8String]); - ASSERT_STREQ(pairingId.c_str(), [host.pairId UTF8String]); - ASSERT_STREQ(pairingSecret.c_str(), [host.pairSecret UTF8String]); - } - - scoped_ptr clientProxy_; - ClientProxyDelegateTester* delegateTester_; - ClientProxyDelegateWrapper* delegateWrapper_; -}; - -TEST_F(ClientProxyTest, ReportConnectionStatusINITIALIZING) { - TestConnnectionStatus(protocol::ConnectionToHost::State::INITIALIZING, - kStatusINITIALIZING); -} - -TEST_F(ClientProxyTest, ReportConnectionStatusCONNECTING) { - TestConnnectionStatus(protocol::ConnectionToHost::State::CONNECTING, - kStatusCONNECTING); -} - -TEST_F(ClientProxyTest, ReportConnectionStatusAUTHENTICATED) { - TestConnnectionStatus(protocol::ConnectionToHost::State::AUTHENTICATED, - kStatusAUTHENTICATED); -} - -TEST_F(ClientProxyTest, ReportConnectionStatusCONNECTED) { - TestConnnectionStatus(protocol::ConnectionToHost::State::CONNECTED, - kStatusCONNECTED); -} - -TEST_F(ClientProxyTest, ReportConnectionStatusFAILED) { - TestConnnectionStatus(protocol::ConnectionToHost::State::FAILED, - kStatusFAILED); -} - -TEST_F(ClientProxyTest, ReportConnectionStatusCLOSED) { - TestConnnectionStatus(protocol::ConnectionToHost::State::CLOSED, - kStatusCLOSED); -} - -TEST_F(ClientProxyTest, ReportConnectionStatusDEFAULT) { - TestConnnectionStatus(static_cast(999), - kStatusDEFAULT); -} - -TEST_F(ClientProxyTest, DisplayAuthenticationPrompt) { - clientProxy_->DisplayAuthenticationPrompt(true); - ASSERT_TRUE(delegateTester_.isPairingSupported); - clientProxy_->DisplayAuthenticationPrompt(false); - ASSERT_FALSE(delegateTester_.isPairingSupported); -} - -TEST_F(ClientProxyTest, CommitPairingCredentialsBasic) { - ValidateHost("", "", ""); -} - -TEST_F(ClientProxyTest, CommitPairingCredentialsExtended) { - ValidateHost(kHostName, kPairingId, kPairingSecret); -} - -TEST_F(ClientProxyTest, RedrawCanvasBasic) { - - webrtc::BasicDesktopFrame frame(webrtc::DesktopSize(1, 1)); - webrtc::DesktopRegion regions; - regions.AddRect(webrtc::DesktopRect::MakeLTRB(0, 0, 1, 1)); - - clientProxy_->RedrawCanvas(webrtc::DesktopSize(1, 1), &frame, regions); - - ASSERT_TRUE(webrtc::DesktopSize(1, 1).equals(delegateTester_.size)); - ASSERT_EQ(4, delegateTester_.stride); - ASSERT_TRUE(delegateTester_.data != NULL); - ASSERT_EQ(1, delegateTester_.regions.size()); - ASSERT_TRUE(delegateTester_.regions[0].equals( - webrtc::DesktopRect::MakeLTRB(0, 0, 1, 1))); -} -TEST_F(ClientProxyTest, RedrawCanvasExtended) { - - webrtc::BasicDesktopFrame frame(kFrameSize); - webrtc::DesktopRegion regions; - regions.AddRect(kFrameSubRect1); - regions.AddRect(kFrameSubRect2); - regions.AddRect(kFrameSubRect3); - - clientProxy_->RedrawCanvas(kFrameSize, &frame, regions); - - ASSERT_TRUE(kFrameSize.equals(delegateTester_.size)); - ASSERT_EQ(kFrameSize.width() * webrtc::DesktopFrame::kBytesPerPixel, - delegateTester_.stride); - ASSERT_TRUE(delegateTester_.data != NULL); - ASSERT_EQ(3, delegateTester_.regions.size()); - ASSERT_TRUE(delegateTester_.regions[0].equals(kFrameSubRect1)); - ASSERT_TRUE(delegateTester_.regions[1].equals(kFrameSubRect2)); - ASSERT_TRUE(delegateTester_.regions[2].equals(kFrameSubRect3)); -} - -TEST_F(ClientProxyTest, UpdateCursorBasic) { - protocol::CursorShapeInfo cursor_proto; - cursor_proto.set_width(1); - cursor_proto.set_height(1); - cursor_proto.set_hotspot_x(0); - cursor_proto.set_hotspot_y(0); - - char data[4]; - memset(data, 0xFF, 4); - - cursor_proto.set_data(data); - - clientProxy_->UpdateCursorShape(cursor_proto); - - ASSERT_EQ(1, delegateTester_.size.width()); - ASSERT_EQ(1, delegateTester_.size.height()); - ASSERT_EQ(0, delegateTester_.hotspot.x()); - ASSERT_EQ(0, delegateTester_.hotspot.y()); - ASSERT_TRUE(delegateTester_.data != NULL); - for (int i = 0; i < 4; i++) { - ASSERT_EQ(0xFF, delegateTester_.data[i]); - } -} - -TEST_F(ClientProxyTest, UpdateCursorExtended) { - protocol::CursorShapeInfo cursor_proto; - cursor_proto.set_width(kCursorWidth); - cursor_proto.set_height(kCursorHeight); - cursor_proto.set_hotspot_x(kCursorHotSpotX); - cursor_proto.set_hotspot_y(kCursorHotSpotY); - - char data[kCursorDataLength]; - memset_pattern4(data, &kCursorDataPattern, kCursorDataLength); - - cursor_proto.set_data(data); - - clientProxy_->UpdateCursorShape(cursor_proto); - - ASSERT_EQ(kCursorWidth, delegateTester_.size.width()); - ASSERT_EQ(kCursorHeight, delegateTester_.size.height()); - ASSERT_EQ(kCursorHotSpotX, delegateTester_.hotspot.x()); - ASSERT_EQ(kCursorHotSpotY, delegateTester_.hotspot.y()); - ASSERT_TRUE(delegateTester_.data != NULL); - for (int i = 0; i < kCursorDataLength / 4; i++) { - ASSERT_TRUE(memcmp(&delegateTester_.data[i * 4], &kCursorDataPattern, 4) == - 0); - } -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/bridge/frame_consumer_bridge.cc b/remoting/ios/bridge/frame_consumer_bridge.cc deleted file mode 100644 index fbc96066d38fc..0000000000000 --- a/remoting/ios/bridge/frame_consumer_bridge.cc +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "remoting/ios/bridge/frame_consumer_bridge.h" - -#include "base/bind.h" -#include "base/logging.h" -#include "base/synchronization/waitable_event.h" -#include "remoting/base/util.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" - -namespace remoting { - -FrameConsumerBridge::FrameConsumerBridge(OnFrameCallback callback) - : callback_(callback), frame_producer_(NULL) {} - -FrameConsumerBridge::~FrameConsumerBridge() { - // The producer should now return any pending buffers. At this point, however, - // the buffers are returned via tasks which may not be scheduled before the - // producer, so we free all the buffers once the producer's queue is empty. - // And the scheduled tasks will die quietly. - if (frame_producer_) { - base::WaitableEvent done_event(true, false); - frame_producer_->RequestReturnBuffers(base::Bind( - &base::WaitableEvent::Signal, base::Unretained(&done_event))); - done_event.Wait(); - } -} - -void FrameConsumerBridge::Initialize(FrameProducer* producer) { - DCHECK(!frame_producer_); - frame_producer_ = producer; - DCHECK(frame_producer_); -} - -void FrameConsumerBridge::ApplyBuffer(const webrtc::DesktopSize& view_size, - const webrtc::DesktopRect& clip_area, - webrtc::DesktopFrame* buffer, - const webrtc::DesktopRegion& region, - const webrtc::DesktopRegion& shape) { - DCHECK(frame_producer_); - if (!view_size_.equals(view_size)) { - // Drop the frame, since the data belongs to the previous generation, - // before SetSourceSize() called SetOutputSizeAndClip(). - ReturnBuffer(buffer); - return; - } - - // This call completes synchronously. - callback_.Run(view_size, buffer, region); - - // Recycle |buffer| by returning it to |frame_producer_| as the next buffer - frame_producer_->DrawBuffer(buffer); -} - -void FrameConsumerBridge::ReturnBuffer(webrtc::DesktopFrame* buffer) { - DCHECK(frame_producer_); - ScopedVector::iterator it = - std::find(buffers_.begin(), buffers_.end(), buffer); - - DCHECK(it != buffers_.end()); - buffers_.erase(it); -} - -void FrameConsumerBridge::SetSourceSize(const webrtc::DesktopSize& source_size, - const webrtc::DesktopVector& dpi) { - DCHECK(frame_producer_); - view_size_ = source_size; - webrtc::DesktopRect clip_area = webrtc::DesktopRect::MakeSize(view_size_); - frame_producer_->SetOutputSizeAndClip(view_size_, clip_area); - - // Now that the size is well known, ask the producer to start drawing - DrawWithNewBuffer(); -} - -FrameConsumerBridge::PixelFormat FrameConsumerBridge::GetPixelFormat() { - return FORMAT_RGBA; -} - -void FrameConsumerBridge::DrawWithNewBuffer() { - DCHECK(frame_producer_); - webrtc::DesktopFrame* buffer = new webrtc::BasicDesktopFrame(view_size_); - buffers_.push_back(buffer); - frame_producer_->DrawBuffer(buffer); -} - -} // namespace remoting diff --git a/remoting/ios/bridge/frame_consumer_bridge.h b/remoting/ios/bridge/frame_consumer_bridge.h deleted file mode 100644 index 70eca2f0de91a..0000000000000 --- a/remoting/ios/bridge/frame_consumer_bridge.h +++ /dev/null @@ -1,65 +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 REMOTING_IOS_BRIDGE_FRAME_CONSUMER_BRIDGE_H_ -#define REMOTING_IOS_BRIDGE_FRAME_CONSUMER_BRIDGE_H_ - -#include - -#include "base/callback.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_vector.h" -#include "base/memory/weak_ptr.h" -#include "remoting/client/frame_consumer.h" -#include "remoting/client/frame_producer.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -namespace remoting { - -class FrameConsumerBridge : public base::SupportsWeakPtr, - public FrameConsumer { - public: - typedef base::Callback - OnFrameCallback; - - // A callback is provided to return frame updates asynchronously. - explicit FrameConsumerBridge(OnFrameCallback callback); - - virtual ~FrameConsumerBridge(); - // This must be called before any other functional use. - void Initialize(FrameProducer* producer); - - // FrameConsumer implementation. - virtual void ApplyBuffer(const webrtc::DesktopSize& view_size, - const webrtc::DesktopRect& clip_area, - webrtc::DesktopFrame* buffer, - const webrtc::DesktopRegion& region, - const webrtc::DesktopRegion& shape) OVERRIDE; - virtual void ReturnBuffer(webrtc::DesktopFrame* buffer) OVERRIDE; - virtual void SetSourceSize(const webrtc::DesktopSize& source_size, - const webrtc::DesktopVector& dpi) OVERRIDE; - virtual PixelFormat GetPixelFormat() OVERRIDE; - - private: - // Allocates a new buffer of |view_size_|, and tells the producer to draw onto - // it. This can be called as soon as the producer is known, but is not - // required until ready to receive frames. - void DrawWithNewBuffer(); - - OnFrameCallback callback_; - - FrameProducer* frame_producer_; - webrtc::DesktopSize view_size_; - - // List of allocated image buffers. - ScopedVector buffers_; - - DISALLOW_COPY_AND_ASSIGN(FrameConsumerBridge); -}; - -} // namespace remoting - -#endif // REMOTING_IOS_BRIDGE_FRAME_CONSUMER_BRIDGE_H_ diff --git a/remoting/ios/bridge/frame_consumer_bridge_unittest.cc b/remoting/ios/bridge/frame_consumer_bridge_unittest.cc deleted file mode 100644 index 30d63d0e326b0..0000000000000 --- a/remoting/ios/bridge/frame_consumer_bridge_unittest.cc +++ /dev/null @@ -1,138 +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 "remoting/ios/bridge/frame_consumer_bridge.h" - -#include -#include - -#include "base/bind.h" -#include "base/memory/scoped_ptr.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" - -namespace { -const webrtc::DesktopSize kFrameSize(100, 100); -const webrtc::DesktopVector kDpi(100, 100); - -const webrtc::DesktopRect FrameRect() { - return webrtc::DesktopRect::MakeSize(kFrameSize); -} - -webrtc::DesktopRegion FrameRegion() { - return webrtc::DesktopRegion(FrameRect()); -} - -void FrameDelivery(const webrtc::DesktopSize& view_size, - webrtc::DesktopFrame* buffer, - const webrtc::DesktopRegion& region) { - ASSERT_TRUE(view_size.equals(kFrameSize)); - ASSERT_TRUE(region.Equals(FrameRegion())); -}; - -} // namespace - -namespace remoting { - -class FrameProducerTester : public FrameProducer { - public: - virtual ~FrameProducerTester() {}; - - virtual void DrawBuffer(webrtc::DesktopFrame* buffer) OVERRIDE { - frames.push(buffer); - }; - - virtual void InvalidateRegion(const webrtc::DesktopRegion& region) OVERRIDE { - NOTIMPLEMENTED(); - }; - - virtual void RequestReturnBuffers(const base::Closure& done) OVERRIDE { - // Don't have to actually return the buffers. This function is really - // saying don't use the references anymore, they are now invalid. - while (!frames.empty()) { - frames.pop(); - } - done.Run(); - }; - - virtual void SetOutputSizeAndClip(const webrtc::DesktopSize& view_size, - const webrtc::DesktopRect& clip_area) - OVERRIDE { - viewSize = view_size; - clipArea = clip_area; - }; - - std::queue frames; - webrtc::DesktopSize viewSize; - webrtc::DesktopRect clipArea; -}; - -class FrameConsumerBridgeTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - frameProducer_.reset(new FrameProducerTester()); - frameConsumer_.reset(new FrameConsumerBridge(base::Bind(&FrameDelivery))); - frameConsumer_->Initialize(frameProducer_.get()); - } - virtual void TearDown() OVERRIDE {} - - scoped_ptr frameProducer_; - scoped_ptr frameConsumer_; -}; - -TEST(FrameConsumerBridgeTest_NotInitialized, CreateAndRelease) { - scoped_ptr frameConsumer_( - new FrameConsumerBridge(base::Bind(&FrameDelivery))); - ASSERT_TRUE(frameConsumer_.get() != NULL); - frameConsumer_.reset(); - ASSERT_TRUE(frameConsumer_.get() == NULL); -} - -TEST_F(FrameConsumerBridgeTest, ApplyBuffer) { - webrtc::DesktopFrame* frame = NULL; - ASSERT_EQ(0, frameProducer_->frames.size()); - frameConsumer_->SetSourceSize(kFrameSize, kDpi); - ASSERT_EQ(1, frameProducer_->frames.size()); - - // Return the frame, and ensure we get it back - frame = frameProducer_->frames.front(); - frameProducer_->frames.pop(); - ASSERT_EQ(0, frameProducer_->frames.size()); - frameConsumer_->ApplyBuffer( - kFrameSize, FrameRect(), frame, FrameRegion(), FrameRegion()); - ASSERT_EQ(1, frameProducer_->frames.size()); - ASSERT_TRUE(frame == frameProducer_->frames.front()); - ASSERT_TRUE(frame->data() == frameProducer_->frames.front()->data()); - - // Change the SourceSize, we should get a new frame, but when the old frame is - // submitted we will not get it back. - frameConsumer_->SetSourceSize(webrtc::DesktopSize(1, 1), kDpi); - ASSERT_EQ(2, frameProducer_->frames.size()); - frame = frameProducer_->frames.front(); - frameProducer_->frames.pop(); - ASSERT_EQ(1, frameProducer_->frames.size()); - frameConsumer_->ApplyBuffer( - kFrameSize, FrameRect(), frame, FrameRegion(), FrameRegion()); - ASSERT_EQ(1, frameProducer_->frames.size()); -} - -TEST_F(FrameConsumerBridgeTest, SetSourceSize) { - frameConsumer_->SetSourceSize(webrtc::DesktopSize(0, 0), - webrtc::DesktopVector(0, 0)); - ASSERT_TRUE(frameProducer_->viewSize.equals(webrtc::DesktopSize(0, 0))); - ASSERT_TRUE(frameProducer_->clipArea.equals( - webrtc::DesktopRect::MakeLTRB(0, 0, 0, 0))); - ASSERT_EQ(1, frameProducer_->frames.size()); - ASSERT_TRUE( - frameProducer_->frames.front()->size().equals(webrtc::DesktopSize(0, 0))); - - frameConsumer_->SetSourceSize(kFrameSize, kDpi); - ASSERT_TRUE(frameProducer_->viewSize.equals(kFrameSize)); - ASSERT_TRUE(frameProducer_->clipArea.equals(FrameRect())); - ASSERT_EQ(2, frameProducer_->frames.size()); - frameProducer_->frames.pop(); - ASSERT_TRUE(frameProducer_->frames.front()->size().equals(kFrameSize)); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/bridge/host_proxy.h b/remoting/ios/bridge/host_proxy.h deleted file mode 100644 index 3c0f129701abc..0000000000000 --- a/remoting/ios/bridge/host_proxy.h +++ /dev/null @@ -1,67 +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 REMOTING_IOS_BRIDGE_CLIENT_PROXY_H_ -#define REMOTING_IOS_BRIDGE_CLIENT_PROXY_H_ - -#import -#import - -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -#import "remoting/ios/bridge/client_proxy_delegate_wrapper.h" - -namespace remoting { -class ClientInstance; -class ClientProxy; -} // namespace remoting - -// HostProxy is one channel of a bridge from the UI Application (CLIENT) and the -// common Chromoting protocol (HOST). HostProxy proxies message from the UI -// application to the host. The reverse channel, ClientProxy, is owned by the -// HostProxy to control deconstruction order, but is shared with the -// ClientInstance to perform work. - -@interface HostProxy : NSObject { - @private - // Host to Client channel - scoped_ptr _hostToClientChannel; - // Client to Host channel, must be released before |_hostToClientChannel| - scoped_refptr _clientToHostChannel; - // Connection state - BOOL _isConnected; -} - -// TRUE when a connection has been established successfully. -- (BOOL)isConnected; - -// Forwards credentials from CLIENT and to HOST and begins establishing a -// connection. -- (void)connectToHost:(NSString*)username - authToken:(NSString*)token - jabberId:(NSString*)jid - hostId:(NSString*)hostId - publicKey:(NSString*)hostPublicKey - delegate:(id)delegate; - -// Report from CLIENT with the user's PIN. -- (void)authenticationResponse:(NSString*)pin createPair:(BOOL)createPair; - -// CLIENT initiated disconnection -- (void)disconnectFromHost; - -// Report from CLIENT of mouse input -- (void)mouseAction:(const webrtc::DesktopVector&)position - wheelDelta:(const webrtc::DesktopVector&)wheelDelta - whichButton:(NSInteger)buttonPressed - buttonDown:(BOOL)buttonIsDown; - -// Report from CLIENT of keyboard input -- (void)keyboardAction:(NSInteger)keyCode keyDown:(BOOL)keyIsDown; - -@end - -#endif // REMOTING_IOS_BRIDGE_CLIENT_PROXY_H_ diff --git a/remoting/ios/bridge/host_proxy.mm b/remoting/ios/bridge/host_proxy.mm deleted file mode 100644 index 1573f9c83a518..0000000000000 --- a/remoting/ios/bridge/host_proxy.mm +++ /dev/null @@ -1,119 +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 "remoting/ios/bridge/host_proxy.h" - -#import "remoting/ios/data_store.h" -#import "remoting/ios/host_preferences.h" -#import "remoting/ios/bridge/client_instance.h" -#import "remoting/ios/bridge/client_proxy.h" - -@implementation HostProxy - -// Override default constructor and initialize internals -- (id)init { - self = [super init]; - if (self) { - _isConnected = false; - } - return self; -} - -// Override default destructor -- (void)dealloc { - if (_isConnected) { - [self disconnectFromHost]; - } - - [super dealloc]; -} - -- (BOOL)isConnected { - return _isConnected; -} - -- (void)connectToHost:(NSString*)username - authToken:(NSString*)token - jabberId:(NSString*)jid - hostId:(NSString*)hostId - publicKey:(NSString*)hostPublicKey - delegate:(id)delegate { - // Implicitly, if currently connected, discard the connection and begin a new - // connection. - [self disconnectFromHost]; - - NSString* pairId = @""; - NSString* pairSecret = @""; - - const HostPreferences* hostPrefs = - [[DataStore sharedStore] getHostForId:hostId]; - - // Use the pairing id and secret when known - if (hostPrefs && hostPrefs.pairId && hostPrefs.pairSecret) { - pairId = [hostPrefs.pairId copy]; - pairSecret = [hostPrefs.pairSecret copy]; - } - - _hostToClientChannel.reset(new remoting::ClientProxy( - [ClientProxyDelegateWrapper wrapDelegate:delegate])); - - DCHECK(!_clientToHostChannel); - _clientToHostChannel = - new remoting::ClientInstance(_hostToClientChannel->AsWeakPtr(), - [username UTF8String], - [token UTF8String], - [jid UTF8String], - [hostId UTF8String], - [hostPublicKey UTF8String], - [pairId UTF8String], - [pairSecret UTF8String]); - - _clientToHostChannel->Start(); - _isConnected = YES; -} - -- (void)authenticationResponse:(NSString*)pin createPair:(BOOL)createPair { - if (_isConnected) { - _clientToHostChannel->ProvideSecret([pin UTF8String], createPair); - } -} - -- (void)disconnectFromHost { - if (_isConnected) { - VLOG(1) << "Disconnecting from Host"; - - // |_clientToHostChannel| must be closed before releasing - // |_hostToClientChannel| - - // |_clientToHostChannel| owns several objects that have references to - // itself. These objects need to be cleaned up before we can release - // |_clientToHostChannel|. - _clientToHostChannel->Cleanup(); - // All other references to |_clientToHostChannel| should now be free. When - // the next statement is executed the destructor is called automatically. - _clientToHostChannel = NULL; - - _hostToClientChannel.reset(); - - _isConnected = NO; - } -} - -- (void)mouseAction:(const webrtc::DesktopVector&)position - wheelDelta:(const webrtc::DesktopVector&)wheelDelta - whichButton:(NSInteger)buttonPressed - buttonDown:(BOOL)buttonIsDown { - if (_isConnected) { - _clientToHostChannel->PerformMouseAction( - position, wheelDelta, buttonPressed, buttonIsDown); - } -} - -- (void)keyboardAction:(NSInteger)keyCode keyDown:(BOOL)keyIsDown { - if (_isConnected) { - _clientToHostChannel->PerformKeyboardAction(keyCode, keyIsDown); - } -} - -@end diff --git a/remoting/ios/bridge/host_proxy_unittest.mm b/remoting/ios/bridge/host_proxy_unittest.mm deleted file mode 100644 index 9641e63d71656..0000000000000 --- a/remoting/ios/bridge/host_proxy_unittest.mm +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/bridge/host_proxy.h" - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -namespace remoting { - -class HostProxyTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { hostProxy_ = [[HostProxy alloc] init]; } - - void CallPassThroughFunctions() { - [hostProxy_ mouseAction:webrtc::DesktopVector(0, 0) - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:0 - buttonDown:NO]; - [hostProxy_ keyboardAction:0 keyDown:NO]; - } - - HostProxy* hostProxy_; -}; - -TEST_F(HostProxyTest, ConnectDisconnect) { - CallPassThroughFunctions(); - - ASSERT_FALSE([hostProxy_ isConnected]); - [hostProxy_ connectToHost:@"" - authToken:@"" - jabberId:@"" - hostId:@"" - publicKey:@"" - delegate:nil]; - ASSERT_TRUE([hostProxy_ isConnected]); - - CallPassThroughFunctions(); - - [hostProxy_ disconnectFromHost]; - ASSERT_FALSE([hostProxy_ isConnected]); - - CallPassThroughFunctions(); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/data_store.h b/remoting/ios/data_store.h deleted file mode 100644 index 0e4a34a0c8a8d..0000000000000 --- a/remoting/ios/data_store.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_IOS_DATA_STORE_H_ -#define REMOTING_IOS_DATA_STORE_H_ - -#import - -#import "remoting/ios/host_preferences.h" - -// A local data store backed by SQLLite to hold instances of HostPreferences. -// HostPreference is defined by the Core Data Model templates see -// ChromotingModel.xcdatamodel -@interface DataStore : NSObject - -// Static pointer to the managed data store -+ (DataStore*)sharedStore; - -// General methods -- (BOOL)saveChanges; - -// Access methods for Hosts -- (NSArray*)allHosts; -- (const HostPreferences*)createHost:(NSString*)hostId; -- (void)removeHost:(const HostPreferences*)p; -- (const HostPreferences*)getHostForId:(NSString*)hostId; - -@end - -#endif // REMOTING_IOS_DATA_STORE_H_ diff --git a/remoting/ios/data_store.mm b/remoting/ios/data_store.mm deleted file mode 100644 index 1cfbc3f9ac2bb..0000000000000 --- a/remoting/ios/data_store.mm +++ /dev/null @@ -1,176 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/data_store.h" - -@interface DataStore (Private) -- (NSString*)itemArchivePath; -@end - -@implementation DataStore { - @private - NSMutableArray* _allHosts; - NSManagedObjectContext* _context; - NSManagedObjectModel* _model; -} - -// Create or Get a static data store -+ (DataStore*)sharedStore { - static DataStore* sharedStore = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, - ^{ sharedStore = [[super allocWithZone:nil] init]; }); - - return sharedStore; -} - -// General methods -+ (id)allocWithZone:(NSZone*)zone { - return [self sharedStore]; -} - -// Load data store from SQLLite backing store -- (id)init { - self = [super init]; - - if (self) { - // Read in ChromotingModel.xdatamodeld - _model = [NSManagedObjectModel mergedModelFromBundles:nil]; - - NSPersistentStoreCoordinator* psc = [[NSPersistentStoreCoordinator alloc] - initWithManagedObjectModel:_model]; - - NSString* path = [self itemArchivePath]; - NSURL* storeUrl = [NSURL fileURLWithPath:path]; - - NSError* error = nil; - - NSDictionary* tryOptions = @{ - NSMigratePersistentStoresAutomaticallyOption : @YES, - NSInferMappingModelAutomaticallyOption : @YES - }; - NSDictionary* makeOptions = - @{NSMigratePersistentStoresAutomaticallyOption : @YES}; - - if (![psc addPersistentStoreWithType:NSSQLiteStoreType - configuration:nil - URL:storeUrl - options:tryOptions - error:&error]) { - // An incompatible version of the store exists, delete it and start over - [[NSFileManager defaultManager] removeItemAtURL:storeUrl error:nil]; - - [psc addPersistentStoreWithType:NSSQLiteStoreType - configuration:nil - URL:storeUrl - options:makeOptions - error:&error]; - [NSException raise:@"Open failed" - format:@"Reason: %@", [error localizedDescription]]; - } - - // Create the managed object context - _context = [[NSManagedObjectContext alloc] init]; - [_context setPersistentStoreCoordinator:psc]; - - // The managed object context can manage undo, but we don't need it - [_context setUndoManager:nil]; - - _allHosts = nil; - } - return self; -} - -// Committing to backing store -- (BOOL)saveChanges { - NSError* err = nil; - BOOL successful = [_context save:&err]; - return successful; -} - -// Looking up the backing store path -- (NSString*)itemArchivePath { - NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains( - NSDocumentDirectory, NSUserDomainMask, YES); - - // Get one and only document directory from that list - NSString* documentDirectory = [documentDirectories objectAtIndex:0]; - - return [documentDirectory stringByAppendingPathComponent:@"store.data"]; -} - -// Return an array of all known hosts, if the list hasn't been loaded yet, then -// load it now -- (NSArray*)allHosts { - if (!_allHosts) { - NSFetchRequest* request = [[NSFetchRequest alloc] init]; - - NSEntityDescription* e = - [[_model entitiesByName] objectForKey:@"HostPreferences"]; - - [request setEntity:e]; - - NSError* error; - NSArray* result = [_context executeFetchRequest:request error:&error]; - if (!result) { - [NSException raise:@"Fetch failed" - format:@"Reason: %@", [error localizedDescription]]; - } - _allHosts = [result mutableCopy]; - } - - return _allHosts; -} - -// Return a HostPreferences if it already exists, otherwise create a new -// HostPreferences to use -- (const HostPreferences*)createHost:(NSString*)hostId { - - const HostPreferences* p = [self getHostForId:hostId]; - - if (p == nil) { - p = [NSEntityDescription insertNewObjectForEntityForName:@"HostPreferences" - inManagedObjectContext:_context]; - p.hostId = hostId; - [_allHosts addObject:p]; - } - return p; -} - -- (void)removeHost:(HostPreferences*)p { - [_context deleteObject:p]; - [_allHosts removeObjectIdenticalTo:p]; -} - -// Search the store for any matching HostPreferences -// return the 1st match or nil -- (const HostPreferences*)getHostForId:(NSString*)hostId { - NSFetchRequest* request = [[NSFetchRequest alloc] init]; - - NSEntityDescription* e = - [[_model entitiesByName] objectForKey:@"HostPreferences"]; - [request setEntity:e]; - - NSPredicate* predicate = - [NSPredicate predicateWithFormat:@"(hostId = %@)", hostId]; - [request setPredicate:predicate]; - - NSError* error; - NSArray* result = [_context executeFetchRequest:request error:&error]; - if (!result) { - [NSException raise:@"Fetch failed" - format:@"Reason: %@", [error localizedDescription]]; - } - - for (HostPreferences* curHost in result) { - return curHost; - } - return nil; -} - -@end diff --git a/remoting/ios/data_store_unittest.mm b/remoting/ios/data_store_unittest.mm deleted file mode 100644 index f75dc28ff31cf..0000000000000 --- a/remoting/ios/data_store_unittest.mm +++ /dev/null @@ -1,119 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/data_store.h" - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -namespace remoting { - -namespace { - -NSString* kHostId = @"testHost"; -NSString* kHostPin = @"testHostPin"; -NSString* kPairId = @"testPairId"; -NSString* kPairSecret = @"testPairSecret"; - -} // namespace - -class DataStoreTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - store_ = [[DataStore allocWithZone:nil] init]; - RemoveAllHosts(); - EXPECT_EQ(0, HostCount()); - } - virtual void TearDown() OVERRIDE { RemoveAllHosts(); } - - int HostCount() { return [[store_ allHosts] count]; } - - void RemoveAllHosts() { - while (HostCount() > 0) { - [store_ removeHost:[store_ allHosts].firstObject]; - } - [store_ saveChanges]; - } - - DataStore* store_; -}; - -TEST(DataStoreTest_Static, IsSingleInstance) { - DataStore* firstStore = [DataStore sharedStore]; - - ASSERT_NSEQ(firstStore, [DataStore sharedStore]); -} - -TEST(DataStoreTest_Static, RemoveAllHost) { - // Test this functionality independently before expecting the fixture to do - // this correctly during cleanup - DataStore* store = [DataStore sharedStore]; - - while ([[store allHosts] count]) { - [store removeHost:[store allHosts].firstObject]; - } - - ASSERT_EQ(0, [[store allHosts] count]); - store = nil; -} - -TEST_F(DataStoreTest, CreateHost) { - - const HostPreferences* host = [store_ createHost:kHostId]; - ASSERT_STREQ([kHostId UTF8String], [host.hostId UTF8String]); - ASSERT_EQ(1, HostCount()); -} - -TEST_F(DataStoreTest, GetHostForId) { - const HostPreferences* host = [store_ getHostForId:kHostId]; - ASSERT_TRUE(host == nil); - - [store_ createHost:kHostId]; - - host = [store_ getHostForId:kHostId]; - - ASSERT_TRUE(host != nil); - ASSERT_STREQ([kHostId UTF8String], [host.hostId UTF8String]); -} - -TEST_F(DataStoreTest, SaveChanges) { - - const HostPreferences* newHost = [store_ createHost:kHostId]; - - ASSERT_EQ(1, HostCount()); - - // Default values for a new host - ASSERT_TRUE([newHost.askForPin boolValue] == NO); - ASSERT_TRUE(newHost.hostPin == nil); - ASSERT_TRUE(newHost.pairId == nil); - ASSERT_TRUE(newHost.pairSecret == nil); - - // Set new values and save - newHost.askForPin = [NSNumber numberWithBool:YES]; - newHost.hostPin = kHostPin; - newHost.pairId = kPairId; - newHost.pairSecret = kPairSecret; - - [store_ saveChanges]; - - // The next time the store is loaded the host will still be present, even - // though we are about to release and reinit a new object - store_ = nil; - store_ = [[DataStore allocWithZone:nil] init]; - ASSERT_EQ(1, HostCount()); - - const HostPreferences* host = [store_ getHostForId:kHostId]; - ASSERT_TRUE(host != nil); - ASSERT_STREQ([kHostId UTF8String], [host.hostId UTF8String]); - ASSERT_TRUE([host.askForPin boolValue] == YES); - ASSERT_STREQ([kHostPin UTF8String], [host.hostPin UTF8String]); - ASSERT_STREQ([kPairId UTF8String], [host.pairId UTF8String]); - ASSERT_STREQ([kPairSecret UTF8String], [host.pairSecret UTF8String]); -} - -} // namespace remoting diff --git a/remoting/ios/host.h b/remoting/ios/host.h deleted file mode 100644 index 62b4fbd6af103..0000000000000 --- a/remoting/ios/host.h +++ /dev/null @@ -1,28 +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 REMOTING_IOS_HOST_H_ -#define REMOTING_IOS_HOST_H_ - -#import - -// A detail record for a Chromoting Host -@interface Host : NSObject - -// Various properties of the Chromoting Host -@property(nonatomic, copy) NSString* createdTime; -@property(nonatomic, copy) NSString* hostId; -@property(nonatomic, copy) NSString* hostName; -@property(nonatomic, copy) NSString* hostVersion; -@property(nonatomic, copy) NSString* jabberId; -@property(nonatomic, copy) NSString* kind; -@property(nonatomic, copy) NSString* publicKey; -@property(nonatomic, copy) NSString* status; -@property(nonatomic, copy) NSString* updatedTime; - -+ (NSMutableArray*)parseListFromJSON:(NSMutableData*)data; - -@end - -#endif // REMOTING_IOS_HOST_H_ diff --git a/remoting/ios/host.mm b/remoting/ios/host.mm deleted file mode 100644 index 592be879146f9..0000000000000 --- a/remoting/ios/host.mm +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/host.h" - -@implementation Host - -@synthesize createdTime = _createdTime; -@synthesize hostId = _hostId; -@synthesize hostName = _hostName; -@synthesize hostVersion = _hostVersion; -@synthesize jabberId = _jabberId; -@synthesize kind = _kind; -@synthesize publicKey = _publicKey; -@synthesize status = _status; -@synthesize updatedTime = _updatedTime; - -// Parse jsonData into Host list -+ (NSMutableArray*)parseListFromJSON:(NSMutableData*)data { - NSError* error; - - NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data - options:kNilOptions - error:&error]; - - NSDictionary* dataDict = [json objectForKey:@"data"]; - - NSArray* availableServers = [dataDict objectForKey:@"items"]; - - NSMutableArray* serverList = [[NSMutableArray alloc] init]; - - NSUInteger idx = 0; - NSDictionary* svr; - NSUInteger count = [availableServers count]; - - while (idx < count) { - svr = [availableServers objectAtIndex:idx++]; - Host* host = [[Host alloc] init]; - host.createdTime = [svr objectForKey:@"createdTime"]; - host.hostId = [svr objectForKey:@"hostId"]; - host.hostName = [svr objectForKey:@"hostName"]; - host.hostVersion = [svr objectForKey:@"hostVersion"]; - host.jabberId = [svr objectForKey:@"jabberId"]; - host.kind = [svr objectForKey:@"kind"]; - host.publicKey = [svr objectForKey:@"publicKey"]; - host.status = [svr objectForKey:@"status"]; - host.updatedTime = [svr objectForKey:@"updatedTime"]; - [serverList addObject:host]; - } - - return serverList; -} - -@end diff --git a/remoting/ios/host_cell.h b/remoting/ios/host_cell.h deleted file mode 100644 index d18b93359de17..0000000000000 --- a/remoting/ios/host_cell.h +++ /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. - -#ifndef REMOTING_IOS_HOST_CELL_H_ -#define REMOTING_IOS_HOST_CELL_H_ - -#import - -// HostCell represents a Host as a row in a tableView, where the row -// contains a single cell. Several button and outlet are reserved here for -// future functionality -@interface HostCell : UITableViewCell - -@property(weak, nonatomic) IBOutlet UILabel* labelHostName; -@property(weak, nonatomic) IBOutlet UILabel* labelStatus; - -@end - -#endif // REMOTING_IOS_HOST_CELL_H_ diff --git a/remoting/ios/host_cell.mm b/remoting/ios/host_cell.mm deleted file mode 100644 index 490a94e7402c7..0000000000000 --- a/remoting/ios/host_cell.mm +++ /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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/host_cell.h" - -@implementation HostCell - -// Override UITableViewCell -- (id)initWithStyle:(UITableViewCellStyle)style - reuseIdentifier:(NSString*)reuseIdentifier { - self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; - return self; -} - -@end diff --git a/remoting/ios/host_preferences.h b/remoting/ios/host_preferences.h deleted file mode 100644 index 6becae59a5d76..0000000000000 --- a/remoting/ios/host_preferences.h +++ /dev/null @@ -1,32 +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 REMOTING_IOS_HOST_PREFERENCES_H_ -#define REMOTING_IOS_HOST_PREFERENCES_H_ - -#import - -// A HostPreferences contains details to negotiate and maintain a connection -// to a remote Chromoting host. This is a entity in a backing store. The -// implementation file is ChromotingModel.xcdatamodeld. If this file is -// updated, also update the model. The model MUST be properly versioned to -// ensure backwards compatibility. -// https://developer.apple.com/library/ios/recipes/xcode_help-core_data_modeling_tool/Articles/creating_new_version.html -// Or the app must be uninstalled, and reinstalled which will erase the previous -// version of the backing store. -@interface HostPreferences : NSManagedObject - -// Is a prompt is needed to reconnect or continue the connection to -// the host -@property(nonatomic, copy) NSNumber* askForPin; -// Several properties are populated from the jabber jump server -@property(nonatomic, copy) NSString* hostId; -// Supplied by client via UI interaction -@property(nonatomic, copy) NSString* hostPin; -@property(nonatomic, copy) NSString* pairId; -@property(nonatomic, copy) NSString* pairSecret; - -@end - -#endif // REMOTING_IOS_HOST_PREFERENCES_H_ \ No newline at end of file diff --git a/remoting/ios/host_refresh.h b/remoting/ios/host_refresh.h deleted file mode 100644 index 8cdef3efd59b9..0000000000000 --- a/remoting/ios/host_refresh.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_IOS_HOST_REFRESH_H_ -#define REMOTING_IOS_HOST_REFRESH_H_ - -#import - -#import "GTMOAuth2Authentication.h" - -// HostRefresh encapsulates a fetch of the Chromoting host list from -// the jabber service. - -// Contract to handle the host list result of a Chromoting host list fetch. -@protocol HostRefreshDelegate - -- (void)hostListRefresh:(NSArray*)hostList errorMessage:(NSString*)errorMessage; - -@end - -// Fetches the host list from the jabber service async. Authenticates, -// and parses the results to provide to a HostListViewController -@interface HostRefresh : NSObject - -// Store data read while the connection is active, and can be used after the -// connection has been discarded -@property(nonatomic, copy) NSMutableData* jsonData; -@property(nonatomic, copy) NSString* errorMessage; -@property(nonatomic, assign) id delegate; - -- (void)refreshHostList:(GTMOAuth2Authentication*)authReq - delegate:(id)delegate; - -@end - -#endif // REMOTING_IOS_HOST_REFRESH_H_ diff --git a/remoting/ios/host_refresh.mm b/remoting/ios/host_refresh.mm deleted file mode 100644 index bf5e67ee5bd10..0000000000000 --- a/remoting/ios/host_refresh.mm +++ /dev/null @@ -1,132 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/host_refresh.h" - -#import "remoting/ios/authorize.h" -#import "remoting/ios/host.h" -#import "remoting/ios/utility.h" - -namespace { -NSString* kDefaultErrorMessage = @"The Host list refresh is not available at " - @"this time. Please try again later."; -} // namespace - -@interface HostRefresh (Private) -- (void)authentication:(GTMOAuth2Authentication*)auth - request:(NSMutableURLRequest*)request - error:(NSError*)error; -- (void)formatErrorMessage:(NSString*)error; -- (void)notifyDelegate; -@end - -// Logic flow begins with refreshHostList, and continues until an error occurs, -// or the host list is returned to the delegate -@implementation HostRefresh - -@synthesize jsonData = _jsonData; -@synthesize errorMessage = _errorMessage; -@synthesize delegate = _delegate; - -// Override default constructor and initialize internals -- (id)init { - self = [super init]; - if (self) { - _jsonData = [[NSMutableData alloc] init]; - } - return self; -} - -// Begin the authentication and authorization process. Begin the process by -// creating an oAuth2 request to google api's including the needed scopes to -// fetch the users host list. -- (void)refreshHostList:(GTMOAuth2Authentication*)authReq - delegate:(id)delegate { - - CHECK(_delegate == nil); // Do not reuse an instance of this class - - _delegate = delegate; - - [Authorize beginRequest:authReq - delegate:self - didFinishSelector:@selector(authentication:request:error:)]; -} - -// Handle completion of the authorization process. Append service credentials -// for jabber. If an error occurred, notify user. -- (void)authentication:(NSObject*)auth - request:(NSMutableURLRequest*)request - error:(NSError*)error { - if (error != nil) { - [self formatErrorMessage:error.localizedDescription]; - } else { - // Add credentials for service - [Authorize appendCredentials:request]; - - // Begin connection, the returned reference is not useful right now and - // marked as __unused - __unused NSURLConnection* connection = - [[NSURLConnection alloc] initWithRequest:request delegate:self]; - } -} - -// @protocol NSURLConnectionDelegate, handle any error during connection -- (void)connection:(NSURLConnection*)connection - didFailWithError:(NSError*)error { - [self formatErrorMessage:[error localizedDescription]]; - - [self notifyDelegate]; -} - -// @protocol NSURLConnectionDataDelegate, may be called async multiple times. -// Each call appends the new data to the known data until completed. -- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data { - [_jsonData appendData:data]; -} - -// @protocol NSURLConnectionDataDelegate -// Ensure connection succeeded: HTTP 200 OK -- (void)connection:(NSURLConnection*)connection - didReceiveResponse:(NSURLResponse*)response { - NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; - if ([response respondsToSelector:@selector(allHeaderFields)]) { - NSNumber* responseCode = - [[NSNumber alloc] initWithInteger:[httpResponse statusCode]]; - if (responseCode.intValue != 200) { - [self formatErrorMessage:[NSString - stringWithFormat:@"HTTP STATUS CODE: %d", - [httpResponse statusCode]]]; - } - } -} - -// @protocol NSURLConnectionDataDelegate handle a completed connection, parse -// received data, and return host list to delegate -- (void)connectionDidFinishLoading:(NSURLConnection*)connection { - [self notifyDelegate]; -} - -// Store a formatted error message to return later -- (void)formatErrorMessage:(NSString*)error { - _errorMessage = kDefaultErrorMessage; - if (error != nil && error.length > 0) { - _errorMessage = [_errorMessage - stringByAppendingString:[@" " stringByAppendingString:error]]; - } -} - -// The connection has finished, call to delegate -- (void)notifyDelegate { - if (_jsonData.length == 0 && _errorMessage == nil) { - [self formatErrorMessage:nil]; - } - - [_delegate hostListRefresh:[Host parseListFromJSON:_jsonData] - errorMessage:_errorMessage]; -} -@end diff --git a/remoting/ios/host_refresh_test_helper.h b/remoting/ios/host_refresh_test_helper.h deleted file mode 100644 index aac365b31a2f1..0000000000000 --- a/remoting/ios/host_refresh_test_helper.h +++ /dev/null @@ -1,102 +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 REMOTING_IOS_HOST_REFRESH_TEST_HELPER_H_ -#define REMOTING_IOS_HOST_REFRESH_TEST_HELPER_H_ - -#import - -namespace remoting { - -class HostRefreshTestHelper { - public: - constexpr static NSString* CloseTag = @"\","; - - constexpr static NSString* CreatedTimeTag = @"\"createdTime\":\""; - constexpr static NSString* HostIdTag = @"\"hostId\":\""; - constexpr static NSString* HostNameTag = @"\"hostName\":\""; - constexpr static NSString* HostVersionTag = @"\"hostVersion\":\""; - constexpr static NSString* KindTag = @"\"kind\":\""; - constexpr static NSString* JabberIdTag = @"\"jabberId\":\""; - constexpr static NSString* PublicKeyTag = @"\"publicKey\":\""; - constexpr static NSString* StatusTag = @"\"status\":\""; - constexpr static NSString* UpdatedTimeTag = @"\"updatedTime\":\""; - - constexpr static NSString* CreatedTimeTest = @"2000-01-01T00:00:01.000Z"; - constexpr static NSString* HostIdTest = @"Host1"; - constexpr static NSString* HostNameTest = @"HostName1"; - constexpr static NSString* HostVersionTest = @"2.22.5.4"; - constexpr static NSString* KindTest = @"chromoting#host"; - constexpr static NSString* JabberIdTest = @"JabberingOn"; - constexpr static NSString* PublicKeyTest = @"AAAAABBBBBZZZZZ"; - constexpr static NSString* StatusTest = @"TESTING"; - constexpr static NSString* UpdatedTimeTest = @"2004-01-01T00:00:01.000Z"; - - static NSMutableData* GetHostList(int numHosts) { - return [NSMutableData - dataWithData:[GetMultipleHosts(numHosts) - dataUsingEncoding:NSUTF8StringEncoding]]; - } - - static NSMutableData* GetHostList(NSString* hostList) { - return [NSMutableData - dataWithData:[hostList dataUsingEncoding:NSUTF8StringEncoding]]; - } - - static NSString* GetMultipleHosts(int numHosts) { - NSString* client = [NSString - stringWithFormat: - @"%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@%@", - @"{", - CreatedTimeTag, - CreatedTimeTest, - CloseTag, - HostIdTag, - HostIdTest, - CloseTag, - HostNameTag, - HostNameTest, - CloseTag, - HostNameTag, - HostNameTest, - CloseTag, - HostVersionTag, - HostVersionTest, - CloseTag, - KindTag, - KindTest, - CloseTag, - JabberIdTag, - JabberIdTest, - CloseTag, - PublicKeyTag, - PublicKeyTest, - CloseTag, - StatusTag, - StatusTest, - CloseTag, - UpdatedTimeTag, - UpdatedTimeTest, - @"\"}"]; - - NSMutableString* hostList = [NSMutableString - stringWithString: - @"{\"data\":{\"kind\":\"chromoting#hostList\",\"items\":["]; - - for (int i = 0; i < numHosts; i++) { - [hostList appendString:client]; - if (i < numHosts - 1) { - [hostList appendString:@","]; // common separated - } - } - - [hostList appendString:@"]}}"]; - - return [hostList copy]; - } -}; - -} // namespace remoting - -#endif // REMOTING_IOS_HOST_REFRESH_TEST_HELPER_H_ \ No newline at end of file diff --git a/remoting/ios/host_refresh_unittest.mm b/remoting/ios/host_refresh_unittest.mm deleted file mode 100644 index 54f2ee7c265c0..0000000000000 --- a/remoting/ios/host_refresh_unittest.mm +++ /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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/host_refresh.h" - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -#import "remoting/ios/host.h" -#import "remoting/ios/host_refresh_test_helper.h" - -@interface HostRefreshDelegateTester : NSObject - -@property(nonatomic) NSArray* hostList; -@property(nonatomic) NSString* errorMessage; - -@end - -@implementation HostRefreshDelegateTester - -@synthesize hostList = _hostList; -@synthesize errorMessage = _errorMessage; - -- (void)hostListRefresh:(NSArray*)hostList - errorMessage:(NSString*)errorMessage { - _hostList = hostList; - _errorMessage = errorMessage; -} - -- (bool)receivedHosts { - return (_hostList.count > 0); -} - -- (bool)receivedErrorMessage { - return (_errorMessage != nil); -} - -@end - -namespace remoting { - -namespace { - -NSString* kErrorMessageTest = @"TestErrorMessage"; - -} // namespace - -class HostRefreshTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - hostRefreshProcessor_ = [[HostRefresh allocWithZone:nil] init]; - delegateTester_ = [[HostRefreshDelegateTester alloc] init]; - [hostRefreshProcessor_ setDelegate:delegateTester_]; - } - - void CreateHostList(int numHosts) { - [hostRefreshProcessor_ - setJsonData:HostRefreshTestHelper::GetHostList(numHosts)]; - } - - NSError* CreateErrorFromString(NSString* message) { - NSDictionary* errorDictionary = nil; - - if (message != nil) { - errorDictionary = @{NSLocalizedDescriptionKey : message}; - } - - return [[NSError alloc] initWithDomain:@"HostRefreshTest" - code:EPERM - userInfo:errorDictionary]; - } - - HostRefresh* hostRefreshProcessor_; - HostRefreshDelegateTester* delegateTester_; -}; - -TEST_F(HostRefreshTest, ErrorFormatter) { - [hostRefreshProcessor_ connection:nil - didFailWithError:CreateErrorFromString(nil)]; - ASSERT_FALSE(hostRefreshProcessor_.errorMessage == nil); - - [hostRefreshProcessor_ connection:nil - didFailWithError:CreateErrorFromString(@"")]; - ASSERT_FALSE(hostRefreshProcessor_.errorMessage == nil); - - [hostRefreshProcessor_ connection:nil - didFailWithError:CreateErrorFromString(kErrorMessageTest)]; - ASSERT_TRUE([hostRefreshProcessor_.errorMessage - rangeOfString:kErrorMessageTest].location != NSNotFound); -} - -TEST_F(HostRefreshTest, JSONParsing) { - // There were no hosts returned - CreateHostList(0); - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_TRUE(delegateTester_.hostList.count == 0); - - CreateHostList(1); - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_TRUE(delegateTester_.hostList.count == 1); - - Host* host = static_cast([delegateTester_.hostList objectAtIndex:0]); - ASSERT_NSEQ(HostRefreshTestHelper::CreatedTimeTest, host.createdTime); - ASSERT_NSEQ(HostRefreshTestHelper::HostIdTest, host.hostId); - ASSERT_NSEQ(HostRefreshTestHelper::HostNameTest, host.hostName); - ASSERT_NSEQ(HostRefreshTestHelper::HostVersionTest, host.hostVersion); - ASSERT_NSEQ(HostRefreshTestHelper::KindTest, host.kind); - ASSERT_NSEQ(HostRefreshTestHelper::JabberIdTest, host.jabberId); - ASSERT_NSEQ(HostRefreshTestHelper::PublicKeyTest, host.publicKey); - ASSERT_NSEQ(HostRefreshTestHelper::StatusTest, host.status); - ASSERT_NSEQ(HostRefreshTestHelper::UpdatedTimeTest, host.updatedTime); - - CreateHostList(11); - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_TRUE(delegateTester_.hostList.count == 11); - - // An error in parsing returns no hosts - [hostRefreshProcessor_ - setJsonData: - [NSMutableData - dataWithData: - [@"{\"dataaaaaafa\":{\"kiffffnd\":\"chromoting#hostList\"}}" - dataUsingEncoding:NSUTF8StringEncoding]]]; - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_TRUE(delegateTester_.hostList.count == 0); -} - -TEST_F(HostRefreshTest, HostListDelegateNoList) { - // Hosts were not processed at all - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_FALSE([delegateTester_ receivedHosts]); - ASSERT_TRUE([delegateTester_ receivedErrorMessage]); - - // There were no hosts returned - CreateHostList(0); - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_FALSE([delegateTester_ receivedHosts]); - ASSERT_TRUE([delegateTester_ receivedErrorMessage]); -} - -TEST_F(HostRefreshTest, HostListDelegateHasList) { - CreateHostList(1); - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_TRUE([delegateTester_ receivedHosts]); - ASSERT_FALSE([delegateTester_ receivedErrorMessage]); -} - -TEST_F(HostRefreshTest, HostListDelegateHasListWithError) { - CreateHostList(1); - - [hostRefreshProcessor_ connection:nil - didFailWithError:CreateErrorFromString(kErrorMessageTest)]; - - [hostRefreshProcessor_ connectionDidFinishLoading:nil]; - ASSERT_TRUE([delegateTester_ receivedHosts]); - ASSERT_TRUE([delegateTester_ receivedErrorMessage]); -} - -TEST_F(HostRefreshTest, ConnectionFailed) { - [hostRefreshProcessor_ connection:nil didFailWithError:nil]; - ASSERT_FALSE([delegateTester_ receivedHosts]); - ASSERT_TRUE([delegateTester_ receivedErrorMessage]); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/key_input.h b/remoting/ios/key_input.h deleted file mode 100644 index 302a63cd28e6d..0000000000000 --- a/remoting/ios/key_input.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_IOS_KEY_INPUT_H_ -#define REMOTING_IOS_KEY_INPUT_H_ - -#import -#import - -// Key codes are translated from the on screen keyboard to the scan codes -// needed for Chromoting input. We don't have a good automated approach to do -// this. Instead we have created a mapping manually via trial and error. To -// support other keyboards in this context we would have to test and create a -// mapping for each keyboard manually. - -// Contract to handle translated key presses from the on-screen keyboard to -// the format required for Chromoting keyboard input -@protocol KeyInputDelegate - -- (void)keyboardActionKeyCode:(uint32_t)keyPressed isKeyDown:(BOOL)keyDown; - -- (void)keyboardDismissed; - -@end - -@interface KeyInput : UIView - -@property(weak, nonatomic) id delegate; - -- (void)ctrlAltDel; - -@end - -#endif // REMOTING_IOS_KEY_INPUT_H_ \ No newline at end of file diff --git a/remoting/ios/key_input.mm b/remoting/ios/key_input.mm deleted file mode 100644 index 0e51efa7ac61a..0000000000000 --- a/remoting/ios/key_input.mm +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/key_input.h" -#import "remoting/ios/key_map_us.h" - -@interface KeyInput (Private) -- (void)transmitAppropriateKeyCode:(NSString*)text; -- (void)transmitKeyCode:(NSInteger)keyCode needShift:(bool)needShift; -@end - -@implementation KeyInput - -@synthesize delegate = _delegate; - -// Override UIKeyInput::UITextInputTraits property -- (UIKeyboardType)keyboardType { - return UIKeyboardTypeAlphabet; -} - -// Override UIView::UIResponder, when this interface is the first responder -// on-screen keyboard input will create events for Chromoting keyboard input -- (BOOL)canBecomeFirstResponder { - return YES; -} - -// Override UIView::UIResponder -// Keyboard was dismissed -- (BOOL)resignFirstResponder { - BOOL wasFirstResponder = self.isFirstResponder; - BOOL didResignFirstReponder = - [super resignFirstResponder]; // I'm not sure that this returns YES when - // first responder was resigned, but for - // now I don't actually need to know what - // the return from super means. - if (wasFirstResponder) { - [_delegate keyboardDismissed]; - } - - return didResignFirstReponder; -} - -// @protocol UIKeyInput, Send backspace -- (void)deleteBackward { - [self transmitKeyCode:kKeyCodeUS[kBackspaceIndex] needShift:false]; -} - -// @protocol UIKeyInput, Assume this is a text input -- (BOOL)hasText { - return YES; -} - -// @protocol UIKeyInput, Translate inserted text to key presses, one char at a -// time -- (void)insertText:(NSString*)text { - [self transmitAppropriateKeyCode:text]; -} - -- (void)ctrlAltDel { - if (_delegate) { - [_delegate keyboardActionKeyCode:kKeyCodeUS[kCtrlIndex] isKeyDown:YES]; - [_delegate keyboardActionKeyCode:kKeyCodeUS[kAltIndex] isKeyDown:YES]; - [_delegate keyboardActionKeyCode:kKeyCodeUS[kDelIndex] isKeyDown:YES]; - [_delegate keyboardActionKeyCode:kKeyCodeUS[kDelIndex] isKeyDown:NO]; - [_delegate keyboardActionKeyCode:kKeyCodeUS[kAltIndex] isKeyDown:NO]; - [_delegate keyboardActionKeyCode:kKeyCodeUS[kCtrlIndex] isKeyDown:NO]; - } -} - -// When inserting multiple characters, process them one at a time. |text| is as -// it was output on the device. The shift key is not naturally presented in the -// input stream, and must be inserted by inspecting each char and considering -// that if the key was input on a traditional keyboard that the character would -// have required a shift. Assume caps lock does not exist. -- (void)transmitAppropriateKeyCode:(NSString*)text { - for (int i = 0; i < [text length]; ++i) { - NSInteger charToSend = [text characterAtIndex:i]; - - if (charToSend <= kKeyboardKeyMaxUS) { - [self transmitKeyCode:kKeyCodeUS[charToSend] - needShift:kIsShiftRequiredUS[charToSend]]; - } - } -} - -// |charToSend| is as it was output on the device. Some call this a -// 'key press'. For Chromoting this must be transferred as a key down (press -// down with a finger), followed by a key up (finger is removed from the -// keyboard) -// -// The delivery may be an upper case or special character. Chromoting is just -// interested in the button that was pushed, so to create an upper case -// character, first send a shift press, then the button, then release shift -- (void)transmitKeyCode:(NSInteger)keyCode needShift:(bool)needShift { - if (keyCode > 0 && _delegate) { - if (needShift) { - [_delegate keyboardActionKeyCode:kKeyCodeUS[kShiftIndex] isKeyDown:YES]; - } - [_delegate keyboardActionKeyCode:keyCode isKeyDown:YES]; - [_delegate keyboardActionKeyCode:keyCode isKeyDown:NO]; - if (needShift) { - [_delegate keyboardActionKeyCode:kKeyCodeUS[kShiftIndex] isKeyDown:NO]; - } - } -} -@end diff --git a/remoting/ios/key_input_unittest.mm b/remoting/ios/key_input_unittest.mm deleted file mode 100644 index b893b9f94d307..0000000000000 --- a/remoting/ios/key_input_unittest.mm +++ /dev/null @@ -1,124 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/key_input.h" -#import "remoting/ios/key_map_us.h" - -#include - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -@interface KeyInputDelegateTester : NSObject { - @private - std::vector _keyList; -} - -@property(nonatomic, assign) int numKeysDown; -@property(nonatomic, assign) BOOL wasDismissed; - -- (std::vector&)getKeyList; - -@end - -@implementation KeyInputDelegateTester - -- (std::vector&)getKeyList { - return _keyList; -} - -- (void)keyboardDismissed { - // This can not be tested, because we can not set |keyInput_| as - // FirstResponder in this test harness - _wasDismissed = true; -} - -- (void)keyboardActionKeyCode:(uint32_t)keyPressed isKeyDown:(BOOL)keyDown { - if (keyDown) { - _keyList.push_back(keyPressed); - _numKeysDown++; - } else { - _numKeysDown--; - } -} - -@end - -namespace remoting { - -class KeyInputTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - keyInput_ = [[KeyInput allocWithZone:nil] init]; - delegateTester_ = [[KeyInputDelegateTester alloc] init]; - keyInput_.delegate = delegateTester_; - } - - KeyInput* keyInput_; - KeyInputDelegateTester* delegateTester_; -}; - -TEST_F(KeyInputTest, SendKey) { - // Empty - [keyInput_ insertText:@""]; - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(0, [delegateTester_ getKeyList].size()); - - // Value is out of bounds - [keyInput_ insertText:@"ó"]; - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(0, [delegateTester_ getKeyList].size()); - - // Lower case - [keyInput_ insertText:@"a"]; - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(1, [delegateTester_ getKeyList].size()); - ASSERT_EQ(kKeyCodeUS['a'], [delegateTester_ getKeyList][0]); - // Upper Case - [delegateTester_ getKeyList].clear(); - [keyInput_ insertText:@"A"]; - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(2, [delegateTester_ getKeyList].size()); - ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][0]); - ASSERT_EQ(kKeyCodeUS['A'], [delegateTester_ getKeyList][1]); - - // Multiple characters and mixed case - [delegateTester_ getKeyList].clear(); - [keyInput_ insertText:@"ABCabc"]; - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(9, [delegateTester_ getKeyList].size()); - ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][0]); - ASSERT_EQ(kKeyCodeUS['A'], [delegateTester_ getKeyList][1]); - ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][2]); - ASSERT_EQ(kKeyCodeUS['B'], [delegateTester_ getKeyList][3]); - ASSERT_EQ(kKeyCodeUS[kShiftIndex], [delegateTester_ getKeyList][4]); - ASSERT_EQ(kKeyCodeUS['C'], [delegateTester_ getKeyList][5]); - ASSERT_EQ(kKeyCodeUS['a'], [delegateTester_ getKeyList][6]); - ASSERT_EQ(kKeyCodeUS['b'], [delegateTester_ getKeyList][7]); - ASSERT_EQ(kKeyCodeUS['c'], [delegateTester_ getKeyList][8]); -} - -TEST_F(KeyInputTest, CtrlAltDel) { - [keyInput_ ctrlAltDel]; - - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(3, [delegateTester_ getKeyList].size()); - ASSERT_EQ(kKeyCodeUS[kCtrlIndex], [delegateTester_ getKeyList][0]); - ASSERT_EQ(kKeyCodeUS[kAltIndex], [delegateTester_ getKeyList][1]); - ASSERT_EQ(kKeyCodeUS[kDelIndex], [delegateTester_ getKeyList][2]); -} - -TEST_F(KeyInputTest, Backspace) { - [keyInput_ deleteBackward]; - - ASSERT_EQ(0, delegateTester_.numKeysDown); - ASSERT_EQ(1, [delegateTester_ getKeyList].size()); - ASSERT_EQ(kKeyCodeUS[kBackspaceIndex], [delegateTester_ getKeyList][0]); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/key_map_us.h b/remoting/ios/key_map_us.h deleted file mode 100644 index c8283f06ffda4..0000000000000 --- a/remoting/ios/key_map_us.h +++ /dev/null @@ -1,288 +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 REMOTING_IOS_KEY_MAP_US_H_ -#define REMOTING_IOS_KEY_MAP_US_H_ - -// A mapping for the US keyboard on a US IPAD to Chromoting Scancodes - -// This must be less than or equal to the size of -// kIsShiftRequiredUS and kKeyCodeUS. -const int kKeyboardKeyMaxUS = 126; - -// Index for specific keys -const uint32_t kShiftIndex = 128; -const uint32_t kBackspaceIndex = 129; -const uint32_t kCtrlIndex = 130; -const uint32_t kAltIndex = 131; -const uint32_t kDelIndex = 132; - -const BOOL kIsShiftRequiredUS[] = { - NO, // [0] Numbering fields by index, not by count - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // [10] ENTER - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // [20] - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // - NO, // [30] - NO, // - NO, // SPACE - YES, // ! - YES, // " - YES, // # - YES, // $ - YES, // % - YES, // & - NO, // ' - YES, // [40] ( - YES, // ) - YES, // * - YES, // + - NO, // , - NO, // - - NO, // . - NO, // / - NO, // 0 - NO, // 1 - NO, // [50] 2 - NO, // 3 - NO, // 4 - NO, // 5 - NO, // 6 - NO, // 7 - NO, // 8 - NO, // 9 - YES, // : - NO, // ; - YES, // [60] < - NO, // = - YES, // > - YES, // ? - YES, // @ - YES, // A - YES, // B - YES, // C - YES, // D - YES, // E - YES, // [70] F - YES, // G - YES, // H - YES, // I - YES, // J - YES, // K - YES, // L - YES, // M - YES, // N - YES, // O - YES, // [80] P - YES, // Q - YES, // R - YES, // S - YES, // T - YES, // U - YES, // V - YES, // W - YES, // X - YES, // Y - YES, // [90] Z - NO, // [ - NO, // BACKSLASH - NO, // ] - YES, // ^ - YES, // _ - NO, // - NO, // a - NO, // b - NO, // c - NO, // [100] d - NO, // e - NO, // f - NO, // g - NO, // h - NO, // i - NO, // j - NO, // k - NO, // l - NO, // m - NO, // [110] n - NO, // o - NO, // p - NO, // q - NO, // r - NO, // s - NO, // t - NO, // u - NO, // v - NO, // w - NO, // [120] x - NO, // y - NO, // z - YES, // { - YES, // | - YES, // } - YES, // ~ - NO // [127] -}; - -const uint32_t kKeyCodeUS[] = { - 0, // [0] Numbering fields by index, not by count - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0x070028, // [10] ENTER - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // [20] - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // - 0, // [30] - 0, // - 0x07002c, // SPACE - 0x07001e, // ! - 0x070034, // " - 0x070020, // # - 0x070021, // $ - 0x070022, // % - 0x070024, // & - 0x070034, // ' - 0x070026, // [40] ( - 0x070027, // ) - 0x070025, // * - 0x07002e, // + - 0x070036, // , - 0x07002d, // - - 0x070037, // . - 0x070038, // / - 0x070027, // 0 - 0x07001e, // 1 - 0x07001f, // [50] 2 - 0x070020, // 3 - 0x070021, // 4 - 0x070022, // 5 - 0x070023, // 6 - 0x070024, // 7 - 0x070025, // 8 - 0x070026, // 9 - 0x070033, // : - 0x070033, // ; - 0x070036, // [60] < - 0x07002e, // = - 0x070037, // > - 0x070038, // ? - 0x07001f, // @ - 0x070004, // A - 0x070005, // B - 0x070006, // C - 0x070007, // D - 0x070008, // E - 0x070009, // [70] F - 0x07000a, // G - 0x07000b, // H - 0x07000c, // I - 0x07000d, // J - 0x07000e, // K - 0x07000f, // L - 0x070010, // M - 0x070011, // N - 0x070012, // O - 0x070013, // [80] P - 0x070014, // Q - 0x070015, // R - 0x070016, // S - 0x070017, // T - 0x070018, // U - 0x070019, // V - 0x07001a, // W - 0x07001b, // X - 0x07001c, // Y - 0x07001d, // [90] Z - 0x07002f, // [ - 0x070031, // BACKSLASH - 0x070030, // ] - 0x070023, // ^ - 0x07002d, // _ - 0, // - 0x070004, // a - 0x070005, // b - 0x070006, // c - 0x070007, // [100] d - 0x070008, // e - 0x070009, // f - 0x07000a, // g - 0x07000b, // h - 0x07000c, // i - 0x07000d, // j - 0x07000e, // k - 0x07000f, // l - 0x070010, // m - 0x070011, // [110] n - 0x070012, // o - 0x070013, // p - 0x070014, // q - 0x070015, // r - 0x070016, // s - 0x070017, // t - 0x070018, // u - 0x070019, // v - 0x07001a, // w - 0x07001b, // [120] x - 0x07001c, // y - 0x07001d, // z - 0x07002f, // { - 0x070031, // | - 0x070030, // } - 0x070035, // ~ - 0, // [127] - 0x0700e1, // SHIFT - 0x07002a, // BACKSPACE - 0x0700e0, // CTRL - 0x0700e2, // ALT - 0x07004c, // DEL -}; - -#endif // REMOTING_IOS_KEY_MAP_US_H_ diff --git a/remoting/ios/ui/cursor_texture.h b/remoting/ios/ui/cursor_texture.h deleted file mode 100644 index 398a42467bae3..0000000000000 --- a/remoting/ios/ui/cursor_texture.h +++ /dev/null @@ -1,58 +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 REMOTING_IOS_UI_CURSOR_TEXTURE_H_ -#define REMOTING_IOS_UI_CURSOR_TEXTURE_H_ - -#import -#import - -#import "base/memory/scoped_ptr.h" - -#import "remoting/ios/utility.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" -#include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h" - -@interface CursorTexture : NSObject { - @private - // GL name - GLuint _textureId; - webrtc::DesktopSize _textureSize; - BOOL _needInitialize; - - // The current cursor - scoped_ptr _cursor; - - BOOL _needCursorRedraw; - - // Rectangle of the most recent cursor drawn to a GL Texture. On each - // successive frame when a new cursor is available this region is cleared on - // the GL Texture, so that the GL Texture is completely transparent again, and - // the cursor is then redrawn. - webrtc::DesktopRect _cursorDrawnToGL; -} - -- (const webrtc::DesktopSize&)textureSize; - -- (void)setTextureSize:(const webrtc::DesktopSize&)size; - -- (const webrtc::MouseCursor&)cursor; - -- (void)setCursor:(webrtc::MouseCursor*)cursor; - -// bind this object to an effect's via the effects properties -- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty; - -// True if the cursor has changed in a way that requires it to be redrawn -- (BOOL)needDrawAtPosition:(const webrtc::DesktopVector&)position; - -// needDrawAtPosition must be checked prior to calling drawWithMousePosition. -// Draw mouse at the new position. -- (void)drawWithMousePosition:(const webrtc::DesktopVector&)position; - -- (void)releaseTexture; - -@end - -#endif // REMOTING_IOS_UI_CURSOR_TEXTURE_H_ \ No newline at end of file diff --git a/remoting/ios/ui/cursor_texture.mm b/remoting/ios/ui/cursor_texture.mm deleted file mode 100644 index 9ffa5f7f12208..0000000000000 --- a/remoting/ios/ui/cursor_texture.mm +++ /dev/null @@ -1,181 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/cursor_texture.h" - -@implementation CursorTexture - -- (id)init { - self = [super init]; - if (self) { - _needCursorRedraw = NO; - _cursorDrawnToGL = webrtc::DesktopRect::MakeXYWH(0, 0, 0, 0); - } - return self; -} - -- (const webrtc::DesktopSize&)textureSize { - return _textureSize; -} - -- (void)setTextureSize:(const webrtc::DesktopSize&)size { - if (!_textureSize.equals(size)) { - _textureSize.set(size.width(), size.height()); - _needInitialize = true; - } -} - -- (const webrtc::MouseCursor&)cursor { - return *_cursor.get(); -} - -- (void)setCursor:(webrtc::MouseCursor*)cursor { - _cursor.reset(cursor); - - if (_cursor.get() != NULL && _cursor->image().data()) { - _needCursorRedraw = true; - } -} - -- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty { - glGenTextures(1, &_textureId); - [Utility bindTextureForIOS:_textureId]; - - // This is the Cursor layer, and is stamped on top of Desktop as a - // transparent image - effectProperty.target = GLKTextureTarget2D; - effectProperty.name = _textureId; - effectProperty.envMode = GLKTextureEnvModeDecal; - effectProperty.enabled = GL_TRUE; - - [Utility logGLErrorCode:@"CursorTexture bindToTexture"]; - // Release context - glBindTexture(GL_TEXTURE_2D, 0); -} - -- (BOOL)needDrawAtPosition:(const webrtc::DesktopVector&)position { - return (_cursor.get() != NULL && - (_needInitialize || _needCursorRedraw == YES || - _cursorDrawnToGL.left() != position.x() - _cursor->hotspot().x() || - _cursorDrawnToGL.top() != position.y() - _cursor->hotspot().y())); -} - -- (void)drawWithMousePosition:(const webrtc::DesktopVector&)position { - if (_textureSize.height() == 0 && _textureSize.width() == 0) { - return; - } - - [Utility bindTextureForIOS:_textureId]; - - if (_needInitialize) { - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - _textureSize.width(), - _textureSize.height(), - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - NULL); - - [Utility logGLErrorCode:@"CursorTexture initializeTextureSurfaceWithSize"]; - _needInitialize = false; - } - // When the cursor needs to be redraw in a different spot then we must clear - // the previous area. - - DCHECK([self needDrawAtPosition:position]); - - if (_cursorDrawnToGL.width() > 0 && _cursorDrawnToGL.height() > 0) { - webrtc::BasicDesktopFrame transparentCursor(_cursorDrawnToGL.size()); - - if (transparentCursor.data() != NULL) { - DCHECK(transparentCursor.kBytesPerPixel == - _cursor->image().kBytesPerPixel); - memset(transparentCursor.data(), - 0, - transparentCursor.stride() * transparentCursor.size().height()); - - [Utility drawSubRectToGLFromRectOfSize:_textureSize - subRect:_cursorDrawnToGL - data:transparentCursor.data()]; - - // there is no longer any cursor drawn to screen - _cursorDrawnToGL = webrtc::DesktopRect::MakeXYWH(0, 0, 0, 0); - } - } - - if (_cursor.get() != NULL) { - - CGRect screen = - CGRectMake(0.0, 0.0, _textureSize.width(), _textureSize.height()); - CGRect cursor = CGRectMake(position.x() - _cursor->hotspot().x(), - position.y() - _cursor->hotspot().y(), - _cursor->image().size().width(), - _cursor->image().size().height()); - - if (CGRectContainsRect(screen, cursor)) { - _cursorDrawnToGL = webrtc::DesktopRect::MakeXYWH(cursor.origin.x, - cursor.origin.y, - cursor.size.width, - cursor.size.height); - - [Utility drawSubRectToGLFromRectOfSize:_textureSize - subRect:_cursorDrawnToGL - data:_cursor->image().data()]; - - } else if (CGRectIntersectsRect(screen, cursor)) { - // Some of the cursor falls off screen, need to clip it - CGRect intersection = CGRectIntersection(screen, cursor); - _cursorDrawnToGL = - webrtc::DesktopRect::MakeXYWH(intersection.origin.x, - intersection.origin.y, - intersection.size.width, - intersection.size.height); - - webrtc::BasicDesktopFrame partialCursor(_cursorDrawnToGL.size()); - - if (partialCursor.data()) { - DCHECK(partialCursor.kBytesPerPixel == _cursor->image().kBytesPerPixel); - - uint32_t src_stride = _cursor->image().stride(); - uint32_t dst_stride = partialCursor.stride(); - - uint8_t* source = _cursor->image().data(); - source += abs((static_cast(cursor.origin.y) - - _cursorDrawnToGL.top())) * - src_stride; - source += abs((static_cast(cursor.origin.x) - - _cursorDrawnToGL.left())) * - _cursor->image().kBytesPerPixel; - uint8_t* dst = partialCursor.data(); - - for (uint32_t y = 0; y < _cursorDrawnToGL.height(); y++) { - memcpy(dst, source, dst_stride); - source += src_stride; - dst += dst_stride; - } - - [Utility drawSubRectToGLFromRectOfSize:_textureSize - subRect:_cursorDrawnToGL - data:partialCursor.data()]; - } - } - } - - _needCursorRedraw = false; - [Utility logGLErrorCode:@"CursorTexture drawWithMousePosition"]; - // Release context - glBindTexture(GL_TEXTURE_2D, 0); -} - -- (void)releaseTexture { - glDeleteTextures(1, &_textureId); -} - -@end diff --git a/remoting/ios/ui/desktop_texture.h b/remoting/ios/ui/desktop_texture.h deleted file mode 100644 index 28a330c509317..0000000000000 --- a/remoting/ios/ui/desktop_texture.h +++ /dev/null @@ -1,38 +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 REMOTING_IOS_UI_DESKTOP_TEXTURE_H_ -#define REMOTING_IOS_UI_DESKTOP_TEXTURE_H_ - -#import -#import - -#import "remoting/ios/utility.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -@interface DesktopTexture : NSObject { - @private - // GL name - GLuint _textureId; - webrtc::DesktopSize _textureSize; - BOOL _needInitialize; -} - -- (const webrtc::DesktopSize&)textureSize; - -- (void)setTextureSize:(const webrtc::DesktopSize&)size; - -// bind this object to an effect's via the effects properties -- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty; - -- (BOOL)needDraw; - -// draw a region of the texture -- (void)drawRegion:(GLRegion*)region rect:(CGRect)rect; - -- (void)releaseTexture; - -@end - -#endif // REMOTING_IOS_UI_DESKTOP_TEXTURE_H_ \ No newline at end of file diff --git a/remoting/ios/ui/desktop_texture.mm b/remoting/ios/ui/desktop_texture.mm deleted file mode 100644 index d806dee8d74e8..0000000000000 --- a/remoting/ios/ui/desktop_texture.mm +++ /dev/null @@ -1,83 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/desktop_texture.h" - -@implementation DesktopTexture - -- (const webrtc::DesktopSize&)textureSize { - return _textureSize; -} - -- (void)setTextureSize:(const webrtc::DesktopSize&)size { - if (!_textureSize.equals(size)) { - _textureSize.set(size.width(), size.height()); - _needInitialize = true; - } -} - -- (void)bindToEffect:(GLKEffectPropertyTexture*)effectProperty { - glGenTextures(1, &_textureId); - [Utility bindTextureForIOS:_textureId]; - - // This is the HOST Desktop layer, and each draw will always replace what is - // currently in the draw context - effectProperty.target = GLKTextureTarget2D; - effectProperty.name = _textureId; - effectProperty.envMode = GLKTextureEnvModeReplace; - effectProperty.enabled = GL_TRUE; - - [Utility logGLErrorCode:@"DesktopTexture bindToTexture"]; - // Release context - glBindTexture(GL_TEXTURE_2D, 0); -} - -- (BOOL)needDraw { - return _needInitialize; -} - -- (void)drawRegion:(GLRegion*)region rect:(CGRect)rect { - if (_textureSize.height() == 0 && _textureSize.width() == 0) { - return; - } - - [Utility bindTextureForIOS:_textureId]; - - if (_needInitialize) { - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA, - _textureSize.width(), - _textureSize.height(), - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - NULL); - - [Utility logGLErrorCode:@"DesktopTexture initializeTextureSurfaceWithSize"]; - _needInitialize = false; - } - - [Utility drawSubRectToGLFromRectOfSize:_textureSize - subRect:webrtc::DesktopRect::MakeXYWH( - region->offset->x(), - region->offset->y(), - region->image->size().width(), - region->image->size().height()) - data:region->image->data()]; - - [Utility logGLErrorCode:@"DesktopTexture drawRegion"]; - // Release context - glBindTexture(GL_TEXTURE_2D, 0); -} - -- (void)releaseTexture { - glDeleteTextures(1, &_textureId); -} - -@end diff --git a/remoting/ios/ui/help_view_controller.h b/remoting/ios/ui/help_view_controller.h deleted file mode 100644 index 036a7b5da7d8e..0000000000000 --- a/remoting/ios/ui/help_view_controller.h +++ /dev/null @@ -1,17 +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 REMOTING_IOS_UI_HELP_VIEW_CONTROLLER_H_ -#define REMOTING_IOS_UI_HELP_VIEW_CONTROLLER_H_ - -#import - -@interface HelpViewController : UIViewController { - @private - IBOutlet UIWebView* _webView; -} - -@end - -#endif // REMOTING_IOS_UI_HELP_VIEW_CONTROLLER_H_ \ No newline at end of file diff --git a/remoting/ios/ui/help_view_controller.mm b/remoting/ios/ui/help_view_controller.mm deleted file mode 100644 index 1a3c70562bda7..0000000000000 --- a/remoting/ios/ui/help_view_controller.mm +++ /dev/null @@ -1,21 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/help_view_controller.h" - -@implementation HelpViewController - -// Override UIViewController -- (void)viewWillAppear:(BOOL)animated { - [self.navigationController setNavigationBarHidden:NO animated:YES]; - NSString* string = @"https://support.google.com/chrome/answer/1649523"; - NSURL* url = [NSURL URLWithString:string]; - [_webView loadRequest:[NSURLRequest requestWithURL:url]]; -} - -@end diff --git a/remoting/ios/ui/host_list_view_controller.h b/remoting/ios/ui/host_list_view_controller.h deleted file mode 100644 index 5f3c1bfd8bb1d..0000000000000 --- a/remoting/ios/ui/host_list_view_controller.h +++ /dev/null @@ -1,39 +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 REMOTING_IOS_UI_HOST_LIST_VIEW_CONTROLLER_H_ -#define REMOTING_IOS_UI_HOST_LIST_VIEW_CONTROLLER_H_ - -#import -#import - -#import "host_refresh.h" - -// HostListViewController presents the user with a list of hosts which has -// been shared from other platforms to connect to -@interface HostListViewController : UIViewController { - @private - IBOutlet UITableView* _tableHostList; - IBOutlet UIButton* _btnAccount; - IBOutlet UIActivityIndicatorView* _refreshActivityIndicator; - IBOutlet UIBarButtonItem* _versionInfo; - - NSArray* _hostList; -} - -@property(nonatomic, readonly) GTMOAuth2Authentication* authorization; -@property(nonatomic, readonly) NSString* userEmail; - -// Triggered by UI 'refresh' button -- (IBAction)btnRefreshHostListPressed:(id)sender; -// Triggered by UI 'log in' button, if user is already logged in then the user -// is logged out and a new session begins by requesting the user to log in, -// possibly with a different account -- (IBAction)btnAccountPressed:(id)sender; - -@end - -#endif // REMOTING_IOS_UI_HOST_LIST_VIEW_CONTROLLER_H_ \ No newline at end of file diff --git a/remoting/ios/ui/host_list_view_controller.mm b/remoting/ios/ui/host_list_view_controller.mm deleted file mode 100644 index 7dd7fa2b3c949..0000000000000 --- a/remoting/ios/ui/host_list_view_controller.mm +++ /dev/null @@ -1,229 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/host_list_view_controller.h" - -#import "remoting/ios/authorize.h" -#import "remoting/ios/host.h" -#import "remoting/ios/host_cell.h" -#import "remoting/ios/host_refresh.h" -#import "remoting/ios/utility.h" -#import "remoting/ios/ui/host_view_controller.h" - -@interface HostListViewController (Private) -- (void)refreshHostList; -- (void)checkUserAndRefreshHostList; -- (BOOL)isSignedIn; -- (void)signInUser; -// Callback from [Authorize createLoginController...] -- (void)viewController:(UIViewController*)viewController - finishedWithAuth:(GTMOAuth2Authentication*)authResult - error:(NSError*)error; -@end - -@implementation HostListViewController - -@synthesize userEmail = _userEmail; -@synthesize authorization = _authorization; - -// Override default setter -- (void)setAuthorization:(GTMOAuth2Authentication*)authorization { - _authorization = authorization; - if (_authorization.canAuthorize) { - _userEmail = _authorization.userEmail; - } else { - _userEmail = nil; - } - - NSString* userName = _userEmail; - - if (userName == nil) { - userName = @"Not logged in"; - } - - [_btnAccount setTitle:userName forState:UIControlStateNormal]; - - [self refreshHostList]; -} - -// Override UIViewController -// Create google+ service for google authentication and oAuth2 authorization. -- (void)viewDidLoad { - [super viewDidLoad]; - - [_tableHostList setDataSource:self]; - [_tableHostList setDelegate:self]; - - _versionInfo.title = [Utility appVersionNumberDisplayString]; -} - -// Override UIViewController -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:animated]; - [self.navigationController setNavigationBarHidden:NO animated:NO]; - [self setAuthorization:[Authorize getAnyExistingAuthorization]]; -} - -// Override UIViewController -// Cancel segue when host status is not online -- (BOOL)shouldPerformSegueWithIdentifier:(NSString*)identifier - sender:(id)sender { - if ([identifier isEqualToString:@"ConnectToHost"]) { - Host* host = [self hostAtIndex:[_tableHostList indexPathForCell:sender]]; - if (![host.status isEqualToString:@"ONLINE"]) { - return NO; - } - } - return YES; -} - -// Override UIViewController -// check for segues defined in the storyboard by identifier, and set a few -// properties before transitioning -- (void)prepareForSegue:(UIStoryboardSegue*)segue sender:(id)sender { - if ([segue.identifier isEqualToString:@"ConnectToHost"]) { - // the designationViewController type is defined by the storyboard - HostViewController* hostView = - static_cast(segue.destinationViewController); - - NSString* authToken = - [_authorization.parameters valueForKey:@"access_token"]; - - if (authToken == nil) { - authToken = _authorization.authorizationTokenKey; - } - - [hostView setHostDetails:[self hostAtIndex:[_tableHostList - indexPathForCell:sender]] - userEmail:_userEmail - authorizationToken:authToken]; - } -} - -// @protocol HostRefreshDelegate, remember received host list for the table -// view to refresh from -- (void)hostListRefresh:(NSArray*)hostList - errorMessage:(NSString*)errorMessage { - if (hostList != nil) { - _hostList = hostList; - [_tableHostList reloadData]; - } - [_refreshActivityIndicator stopAnimating]; - if (errorMessage != nil) { - [Utility showAlert:@"Host Refresh Failed" message:errorMessage]; - } -} - -// @protocol UITableViewDataSource -// Only have 1 section and it contains all the hosts -- (NSInteger)tableView:(UITableView*)tableView - numberOfRowsInSection:(NSInteger)section { - return [_hostList count]; -} - -// @protocol UITableViewDataSource -// Convert a host entry to a table row -- (HostCell*)tableView:(UITableView*)tableView - cellForRowAtIndexPath:(NSIndexPath*)indexPath { - static NSString* CellIdentifier = @"HostStatusCell"; - - HostCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier - forIndexPath:indexPath]; - - Host* host = [self hostAtIndex:indexPath]; - cell.labelHostName.text = host.hostName; - cell.labelStatus.text = host.status; - - UIColor* statColor = nil; - if ([host.status isEqualToString:@"ONLINE"]) { - statColor = [[UIColor alloc] initWithRed:0 green:1 blue:0 alpha:1]; - } else { - statColor = [[UIColor alloc] initWithRed:1 green:0 blue:0 alpha:1]; - } - [cell.labelStatus setTextColor:statColor]; - - return cell; -} - -// @protocol UITableViewDataSource -// Rows are not editable via standard UI mechanisms -- (BOOL)tableView:(UITableView*)tableView - canEditRowAtIndexPath:(NSIndexPath*)indexPath { - return NO; -} - -- (IBAction)btnRefreshHostListPressed:(id)sender { - [self refreshHostList]; -} - -- (IBAction)btnAccountPressed:(id)sender { - [self signInUser]; -} - -- (void)refreshHostList { - [_refreshActivityIndicator startAnimating]; - _hostList = [[NSArray alloc] init]; - [_tableHostList reloadData]; - - // Insert a small delay so the user is well informed that something is - // happening by the animating activity indicator - [self performSelector:@selector(checkUserAndRefreshHostList) - withObject:nil - afterDelay:.5]; -} - -// Most likely you want to call refreshHostList -- (void)checkUserAndRefreshHostList { - if (![self isSignedIn]) { - [self signInUser]; - } else { - HostRefresh* hostRefresh = [[HostRefresh alloc] init]; - [hostRefresh refreshHostList:_authorization delegate:self]; - } -} - -- (BOOL)isSignedIn { - return (_userEmail != nil); -} - -// Launch the google.com authentication and authorization process. If a user is -// already signed in, begin by signing out so another account could be -// signed in. -- (void)signInUser { - [self presentViewController: - [Authorize createLoginController:self - finishedSelector:@selector(viewController: - finishedWithAuth: - error:)] - animated:YES - completion:nil]; -} - -// Callback from [Authorize createLoginController...] -// Handle completion of the authentication process, and updates the service -// with the new credentials. -- (void)viewController:(UIViewController*)viewController - finishedWithAuth:(GTMOAuth2Authentication*)authResult - error:(NSError*)error { - [viewController.presentingViewController dismissViewControllerAnimated:NO - completion:nil]; - - if (error != nil) { - [Utility showAlert:@"Authentication Error" - message:error.localizedDescription]; - [self setAuthorization:nil]; - } else { - [self setAuthorization:authResult]; - } -} - -- (Host*)hostAtIndex:(NSIndexPath*)indexPath { - return [_hostList objectAtIndex:indexPath.row]; -} - -@end diff --git a/remoting/ios/ui/host_list_view_controller_unittest.mm b/remoting/ios/ui/host_list_view_controller_unittest.mm deleted file mode 100644 index 7e75739c0586e..0000000000000 --- a/remoting/ios/ui/host_list_view_controller_unittest.mm +++ /dev/null @@ -1,90 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/host_list_view_controller.h" - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -#import "remoting/ios/host.h" -#import "remoting/ios/host_refresh_test_helper.h" -#import "remoting/ios/ui/host_view_controller.h" - -namespace remoting { - -class HostListViewControllerTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - controller_ = [[HostListViewController alloc] init]; - SetHostByCount(1); - } - - void SetHostByCount(int numHosts) { - NSArray* array = - [Host parseListFromJSON:HostRefreshTestHelper::GetHostList(numHosts)]; - RefreshHostList(array); - } - - void SetHostByString(NSString* string) { - NSArray* array = - [Host parseListFromJSON:HostRefreshTestHelper::GetHostList(string)]; - RefreshHostList(array); - } - - void RefreshHostList(NSArray* array) { - [controller_ hostListRefresh:array errorMessage:nil]; - } - - HostListViewController* controller_; -}; - -TEST_F(HostListViewControllerTest, DefaultAuthorization) { - ASSERT_TRUE(controller_.authorization == nil); - - [controller_ viewWillAppear:YES]; - - ASSERT_TRUE(controller_.authorization != nil); -} - -TEST_F(HostListViewControllerTest, hostListRefresh) { - SetHostByCount(2); - ASSERT_EQ(2, [controller_ tableView:nil numberOfRowsInSection:0]); - - SetHostByCount(10); - ASSERT_EQ(10, [controller_ tableView:nil numberOfRowsInSection:0]); -} - -TEST_F(HostListViewControllerTest, - ShouldPerformSegueWithIdentifierOfConnectToHost) { - ASSERT_FALSE([controller_ shouldPerformSegueWithIdentifier:@"ConnectToHost" - sender:nil]); - - NSString* host = HostRefreshTestHelper::GetMultipleHosts(1); - host = [host stringByReplacingOccurrencesOfString:@"TESTING" - withString:@"ONLINE"]; - SetHostByString(host); - ASSERT_TRUE([controller_ shouldPerformSegueWithIdentifier:@"ConnectToHost" - sender:nil]); -} - -TEST_F(HostListViewControllerTest, prepareSegueWithIdentifierOfConnectToHost) { - HostViewController* destination = [[HostViewController alloc] init]; - - ASSERT_NSNE(HostRefreshTestHelper::HostNameTest, destination.host.hostName); - - UIStoryboardSegue* seque = - [[UIStoryboardSegue alloc] initWithIdentifier:@"ConnectToHost" - source:controller_ - destination:destination]; - - [controller_ prepareForSegue:seque sender:nil]; - - ASSERT_NSEQ(HostRefreshTestHelper::HostNameTest, destination.host.hostName); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/ui/host_view_controller.h b/remoting/ios/ui/host_view_controller.h deleted file mode 100644 index 6e0f287948806..0000000000000 --- a/remoting/ios/ui/host_view_controller.h +++ /dev/null @@ -1,115 +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 REMOTING_IOS_UI_HOST_VIEW_CONTROLLER_H_ -#define REMOTING_IOS_UI_HOST_VIEW_CONTROLLER_H_ - -#import - -#include "base/memory/scoped_ptr.h" -#include "base/memory/scoped_vector.h" - -#import "remoting/ios/host.h" -#import "remoting/ios/key_input.h" -#import "remoting/ios/utility.h" -#import "remoting/ios/bridge/host_proxy.h" -#import "remoting/ios/ui/desktop_texture.h" -#import "remoting/ios/ui/cursor_texture.h" -#import "remoting/ios/ui/pin_entry_view_controller.h" -#import "remoting/ios/ui/scene_view.h" - -@interface HostViewController - : GLKViewController { - @private - IBOutlet UIActivityIndicatorView* _busyIndicator; - IBOutlet UIButton* _barBtnDisconnect; - IBOutlet UIButton* _barBtnKeyboard; - IBOutlet UIButton* _barBtnNavigation; - IBOutlet UIButton* _barBtnCtrlAltDel; - IBOutlet UILongPressGestureRecognizer* _longPressRecognizer; - IBOutlet UIPanGestureRecognizer* _panRecognizer; - IBOutlet UIPanGestureRecognizer* _threeFingerPanRecognizer; - IBOutlet UIPinchGestureRecognizer* _pinchRecognizer; - IBOutlet UITapGestureRecognizer* _singleTapRecognizer; - IBOutlet UITapGestureRecognizer* _twoFingerTapRecognizer; - IBOutlet UITapGestureRecognizer* _threeFingerTapRecognizer; - IBOutlet UIToolbar* _toolbar; - IBOutlet UIToolbar* _hiddenToolbar; - IBOutlet NSLayoutConstraint* _toolBarYPosition; - IBOutlet NSLayoutConstraint* _hiddenToolbarYPosition; - - KeyInput* _keyEntryView; - NSString* _statusMessage; - - // The GLES2 context being drawn too. - EAGLContext* _context; - - // GLKBaseEffect encapsulates the GL Shaders needed to draw at most two - // textures |_textureIds| given vertex information. The draw surface consists - // of two layers (GL Textures). The bottom layer is the desktop of the HOST. - // The top layer is mostly transparent and is used to overlay the current - // cursor. - GLKBaseEffect* _effect; - - // All the details needed to draw our GL Scene, and our two textures. - SceneView* _scene; - DesktopTexture* _desktop; - CursorTexture* _mouse; - - // List of regions and data that have pending draws to |_desktop| . - ScopedVector _glRegions; - - // Lock for |_glRegions|, regions are delivered from HOST on another thread, - // and drawn to |_desktop| from a GL Context thread - NSLock* _glBufferLock; - - // Lock for |_mouse.cursor|, cursor updates are delivered from HOST on another - // thread, and drawn to |_mouse| from a GL Context thread - NSLock* _glCursorLock; - - // Communication channel from CLIENT to HOST - HostProxy* _clientToHostProxy; -} - -// Details for the host and user -@property(nonatomic, readonly) Host* host; -@property(nonatomic, readonly) NSString* userEmail; -@property(nonatomic, readonly) NSString* userAuthorizationToken; - -- (void)setHostDetails:(Host*)host - userEmail:(NSString*)userEmail - authorizationToken:(NSString*)authorizationToken; - -// Zoom in/out -- (IBAction)pinchGestureTriggered:(UIPinchGestureRecognizer*)sender; -// Left mouse click, moves cursor -- (IBAction)tapGestureTriggered:(UITapGestureRecognizer*)sender; -// Scroll the view in 2d -- (IBAction)panGestureTriggered:(UIPanGestureRecognizer*)sender; -// Right mouse click and drag, moves cursor -- (IBAction)longPressGestureTriggered:(UILongPressGestureRecognizer*)sender; -// Right mouse click -- (IBAction)twoFingerTapGestureTriggered:(UITapGestureRecognizer*)sender; -// Middle mouse click -- (IBAction)threeFingerTapGestureTriggered:(UITapGestureRecognizer*)sender; -// Show hidden menus. Swipe up for keyboard, swipe down for navigation menu -- (IBAction)threeFingerPanGestureTriggered:(UIPanGestureRecognizer*)sender; - -// Do navigation 'back' -- (IBAction)barBtnNavigationBackPressed:(id)sender; -// Show keyboard -- (IBAction)barBtnKeyboardPressed:(id)sender; -// Trigger |_toolbar| animation -- (IBAction)barBtnToolBarHidePressed:(id)sender; -// Send Keys for ctrl, atl, delete -- (IBAction)barBtnCtrlAltDelPressed:(id)sender; - -@end - -#endif // REMOTING_IOS_UI_HOST_VIEW_CONTROLLER_H_ diff --git a/remoting/ios/ui/host_view_controller.mm b/remoting/ios/ui/host_view_controller.mm deleted file mode 100644 index d87e7674bdc9d..0000000000000 --- a/remoting/ios/ui/host_view_controller.mm +++ /dev/null @@ -1,676 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/host_view_controller.h" - -#include - -#import "remoting/ios/data_store.h" - -namespace { - -// TODO (aboone) Some of the layout is not yet set in stone, so variables have -// been used to position and turn items on and off. Eventually these may be -// stabilized and removed. - -// Scroll speed multiplier for mouse wheel -const static int kMouseWheelSensitivity = 20; - -// Area the navigation bar consumes when visible in pixels -const static int kTopMargin = 20; -// Area the footer consumes when visible (no footer currently exists) -const static int kBottomMargin = 0; - -} // namespace - -@interface HostViewController (Private) -- (void)setupGL; -- (void)tearDownGL; -- (void)goBack; -- (void)updateLabels; -- (BOOL)isToolbarHidden; -- (void)updatePanVelocityShouldCancel:(bool)canceled; -- (void)orientationChanged:(NSNotification*)note; -- (void)applySceneChange:(CGPoint)translation scaleBy:(float)ratio; -- (void)showToolbar:(BOOL)visible; -@end - -@implementation HostViewController - -@synthesize host = _host; -@synthesize userEmail = _userEmail; -@synthesize userAuthorizationToken = _userAuthorizationToken; - -// Override UIViewController -- (void)viewDidLoad { - [super viewDidLoad]; - - _context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; - DCHECK(_context); - static_cast(self.view).context = _context; - - [_keyEntryView setDelegate:self]; - - _clientToHostProxy = [[HostProxy alloc] init]; - - // There is a 1 pixel top border which is actually the background not being - // covered. There is no obvious way to remove that pixel 'border'. Set the - // background clear, and also reset the backgroundimage and shawdowimage to an - // empty image any time the view is moved. - _hiddenToolbar.backgroundColor = [UIColor clearColor]; - if ([_hiddenToolbar respondsToSelector:@selector(setBackgroundImage: - forToolbarPosition: - barMetrics:)]) { - [_hiddenToolbar setBackgroundImage:[UIImage new] - forToolbarPosition:UIToolbarPositionAny - barMetrics:UIBarMetricsDefault]; - } - if ([_hiddenToolbar - respondsToSelector:@selector(setShadowImage:forToolbarPosition:)]) { - [_hiddenToolbar setShadowImage:[UIImage new] - forToolbarPosition:UIToolbarPositionAny]; - } - - // 1/2 circle rotation for an icon ~ 180 degree ~ 1 radian - _barBtnNavigation.imageView.transform = CGAffineTransformMakeRotation(M_PI); - - _scene = [[SceneView alloc] init]; - [_scene setMarginsFromLeft:0 right:0 top:kTopMargin bottom:kBottomMargin]; - _desktop = [[DesktopTexture alloc] init]; - _mouse = [[CursorTexture alloc] init]; - - _glBufferLock = [[NSLock alloc] init]; - _glCursorLock = [[NSLock alloc] init]; - - [_scene - setContentSize:[Utility getOrientatedSize:self.view.bounds.size - shouldWidthBeLongestSide:[Utility isInLandscapeMode]]]; - [self showToolbar:YES]; - [self updateLabels]; - - [self setupGL]; - - [_singleTapRecognizer requireGestureRecognizerToFail:_twoFingerTapRecognizer]; - [_twoFingerTapRecognizer - requireGestureRecognizerToFail:_threeFingerTapRecognizer]; - //[_pinchRecognizer requireGestureRecognizerToFail:_twoFingerTapRecognizer]; - [_panRecognizer requireGestureRecognizerToFail:_singleTapRecognizer]; - [_threeFingerPanRecognizer - requireGestureRecognizerToFail:_threeFingerTapRecognizer]; - //[_pinchRecognizer requireGestureRecognizerToFail:_threeFingerPanRecognizer]; - - // Subscribe to changes in orientation - [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(orientationChanged:) - name:UIDeviceOrientationDidChangeNotification - object:[UIDevice currentDevice]]; -} - -- (void)setupGL { - [EAGLContext setCurrentContext:_context]; - - _effect = [[GLKBaseEffect alloc] init]; - [Utility logGLErrorCode:@"setupGL begin"]; - - // Initialize each texture - [_desktop bindToEffect:[_effect texture2d0]]; - [_mouse bindToEffect:[_effect texture2d1]]; - [Utility logGLErrorCode:@"setupGL textureComplete"]; -} - -// Override UIViewController -- (void)viewDidUnload { - [super viewDidUnload]; - [self tearDownGL]; - - if ([EAGLContext currentContext] == _context) { - [EAGLContext setCurrentContext:nil]; - } - _context = nil; -} - -- (void)tearDownGL { - [EAGLContext setCurrentContext:_context]; - - // Release Textures - [_desktop releaseTexture]; - [_mouse releaseTexture]; -} - -// Override UIViewController -- (void)viewWillAppear:(BOOL)animated { - [super viewWillAppear:NO]; - [self.navigationController setNavigationBarHidden:YES animated:YES]; - [self updateLabels]; - if (![_clientToHostProxy isConnected]) { - [_busyIndicator startAnimating]; - - [_clientToHostProxy connectToHost:_userEmail - authToken:_userAuthorizationToken - jabberId:_host.jabberId - hostId:_host.hostId - publicKey:_host.publicKey - delegate:self]; - } -} - -// Override UIViewController -- (void)viewWillDisappear:(BOOL)animated { - [super viewWillDisappear:NO]; - NSArray* viewControllers = self.navigationController.viewControllers; - if (viewControllers.count > 1 && - [viewControllers objectAtIndex:viewControllers.count - 2] == self) { - // View is disappearing because a new view controller was pushed onto the - // stack - } else if ([viewControllers indexOfObject:self] == NSNotFound) { - // View is disappearing because it was popped from the stack - [_clientToHostProxy disconnectFromHost]; - } -} - -// "Back" goes to the root controller for now -- (void)goBack { - [self.navigationController popToRootViewControllerAnimated:YES]; -} - -// @protocol PinEntryViewControllerDelegate -// Return the PIN input by User, indicate if the User should be prompted to -// re-enter the pin in the future -- (void)connectToHostWithPin:(UIViewController*)controller - hostPin:(NSString*)hostPin - shouldPrompt:(BOOL)shouldPrompt { - const HostPreferences* hostPrefs = - [[DataStore sharedStore] getHostForId:_host.hostId]; - if (!hostPrefs) { - hostPrefs = [[DataStore sharedStore] createHost:_host.hostId]; - } - if (hostPrefs) { - hostPrefs.hostPin = hostPin; - hostPrefs.askForPin = [NSNumber numberWithBool:shouldPrompt]; - [[DataStore sharedStore] saveChanges]; - } - - [[controller presentingViewController] dismissViewControllerAnimated:NO - completion:nil]; - - [_clientToHostProxy authenticationResponse:hostPin createPair:!shouldPrompt]; -} - -// @protocol PinEntryViewControllerDelegate -// Returns if the user canceled while entering their PIN -- (void)cancelledConnectToHostWithPin:(UIViewController*)controller { - [[controller presentingViewController] dismissViewControllerAnimated:NO - completion:nil]; - - [self goBack]; -} - -- (void)setHostDetails:(Host*)host - userEmail:(NSString*)userEmail - authorizationToken:(NSString*)authorizationToken { - DCHECK(host.jabberId); - _host = host; - _userEmail = userEmail; - _userAuthorizationToken = authorizationToken; -} - -// Set various labels on the form for iPad vs iPhone, and orientation -- (void)updateLabels { - if (![Utility isPad] && ![Utility isInLandscapeMode]) { - [_barBtnDisconnect setTitle:@"" forState:(UIControlStateNormal)]; - [_barBtnCtrlAltDel setTitle:@"CtAtD" forState:UIControlStateNormal]; - } else { - [_barBtnCtrlAltDel setTitle:@"Ctrl+Alt+Del" forState:UIControlStateNormal]; - - NSString* hostStatus = _host.hostName; - if (![_statusMessage isEqual:@"Connected"]) { - hostStatus = [NSString - stringWithFormat:@"%@ - %@", _host.hostName, _statusMessage]; - } - [_barBtnDisconnect setTitle:hostStatus forState:UIControlStateNormal]; - } - - [_barBtnDisconnect sizeToFit]; - [_barBtnCtrlAltDel sizeToFit]; -} - -// Resize the view of the desktop - Zoom in/out. This can occur during a Pan. -- (IBAction)pinchGestureTriggered:(UIPinchGestureRecognizer*)sender { - if ([sender state] == UIGestureRecognizerStateChanged) { - [self applySceneChange:CGPointMake(0.0, 0.0) scaleBy:sender.scale]; - - sender.scale = 1.0; // reset scale so next iteration is a relative ratio - } -} - -- (IBAction)tapGestureTriggered:(UITapGestureRecognizer*)sender { - if ([_scene containsTouchPoint:[sender locationInView:self.view]]) { - [Utility leftClickOn:_clientToHostProxy at:_scene.mousePosition]; - } -} - -// Change position of scene. This can occur during a pinch or longpress. -// Or perform a Mouse Wheel Scroll -- (IBAction)panGestureTriggered:(UIPanGestureRecognizer*)sender { - CGPoint translation = [sender translationInView:self.view]; - - // If we start with 2 touches, and the pinch gesture is not in progress yet, - // then disable it, so mouse scrolling and zoom do not occur at the same - // time. - if ([sender numberOfTouches] == 2 && - [sender state] == UIGestureRecognizerStateBegan && - !(_pinchRecognizer.state == UIGestureRecognizerStateBegan || - _pinchRecognizer.state == UIGestureRecognizerStateChanged)) { - _pinchRecognizer.enabled = NO; - } - - if (!_pinchRecognizer.enabled) { - // Began with 2 touches, so this is a scroll event - translation.x *= kMouseWheelSensitivity; - translation.y *= kMouseWheelSensitivity; - [Utility mouseScroll:_clientToHostProxy - at:_scene.mousePosition - delta:webrtc::DesktopVector(translation.x, translation.y)]; - } else { - // Did not begin with 2 touches, doing a pan event - if ([sender state] == UIGestureRecognizerStateChanged) { - CGPoint translation = [sender translationInView:self.view]; - - [self applySceneChange:translation scaleBy:1.0]; - - } else if ([sender state] == UIGestureRecognizerStateEnded) { - // After user removes their fingers from the screen, apply an acceleration - // effect - [_scene setPanVelocity:[sender velocityInView:self.view]]; - } - } - - // Finished the event chain - if (!([sender state] == UIGestureRecognizerStateBegan || - [sender state] == UIGestureRecognizerStateChanged)) { - _pinchRecognizer.enabled = YES; - } - - // Reset translation so next iteration is relative. - [sender setTranslation:CGPointZero inView:self.view]; -} - -// Click-Drag mouse operation. This can occur during a Pan. -- (IBAction)longPressGestureTriggered:(UILongPressGestureRecognizer*)sender { - - if ([sender state] == UIGestureRecognizerStateBegan) { - [_clientToHostProxy mouseAction:_scene.mousePosition - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:1 - buttonDown:YES]; - } else if (!([sender state] == UIGestureRecognizerStateBegan || - [sender state] == UIGestureRecognizerStateChanged)) { - [_clientToHostProxy mouseAction:_scene.mousePosition - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:1 - buttonDown:NO]; - } -} - -- (IBAction)twoFingerTapGestureTriggered:(UITapGestureRecognizer*)sender { - if ([_scene containsTouchPoint:[sender locationInView:self.view]]) { - [Utility rightClickOn:_clientToHostProxy at:_scene.mousePosition]; - } -} - -- (IBAction)threeFingerTapGestureTriggered:(UITapGestureRecognizer*)sender { - - if ([_scene containsTouchPoint:[sender locationInView:self.view]]) { - [Utility middleClickOn:_clientToHostProxy at:_scene.mousePosition]; - } -} - -- (IBAction)threeFingerPanGestureTriggered:(UIPanGestureRecognizer*)sender { - if ([sender state] == UIGestureRecognizerStateChanged) { - CGPoint translation = [sender translationInView:self.view]; - if (translation.y > 0) { - // Swiped down - [self showToolbar:YES]; - } else if (translation.y < 0) { - // Swiped up - [_keyEntryView becomeFirstResponder]; - [self updateLabels]; - } - [sender setTranslation:CGPointZero inView:self.view]; - } -} - -- (IBAction)barBtnNavigationBackPressed:(id)sender { - [self goBack]; -} - -- (IBAction)barBtnKeyboardPressed:(id)sender { - if ([_keyEntryView isFirstResponder]) { - [_keyEntryView endEditing:NO]; - } else { - [_keyEntryView becomeFirstResponder]; - } - - [self updateLabels]; -} - -- (IBAction)barBtnToolBarHidePressed:(id)sender { - [self showToolbar:[self isToolbarHidden]]; // Toolbar is either on - // screen or off screen -} - -- (IBAction)barBtnCtrlAltDelPressed:(id)sender { - [_keyEntryView ctrlAltDel]; -} - -// Override UIResponder -// When any gesture begins, remove any acceleration effects currently being -// applied. Example, Panning view and let it shoot off into the distance, but -// then I see a spot I'm interested in so I will touch to capture that locations -// focus. -- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - [self updatePanVelocityShouldCancel:YES]; - [super touchesBegan:touches withEvent:event]; -} - -// @protocol UIGestureRecognizerDelegate -// Allow panning and zooming to occur simultaneously. -// Allow panning and long press to occur simultaneously. -// Pinch requires 2 touches, and long press requires a single touch, so they are -// mutually exclusive regardless of if panning is the initiating gesture -- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer - shouldRecognizeSimultaneouslyWithGestureRecognizer: - (UIGestureRecognizer*)otherGestureRecognizer { - if (gestureRecognizer == _pinchRecognizer || - (gestureRecognizer == _panRecognizer)) { - if (otherGestureRecognizer == _pinchRecognizer || - otherGestureRecognizer == _panRecognizer) { - return YES; - } - } - - if (gestureRecognizer == _longPressRecognizer || - gestureRecognizer == _panRecognizer) { - if (otherGestureRecognizer == _longPressRecognizer || - otherGestureRecognizer == _panRecognizer) { - return YES; - } - } - return NO; -} - -// @protocol ClientControllerDelegate -// Prompt the user for their PIN if pairing has not already been established -- (void)requestHostPin:(BOOL)pairingSupported { - BOOL requestPin = YES; - const HostPreferences* hostPrefs = - [[DataStore sharedStore] getHostForId:_host.hostId]; - if (hostPrefs) { - requestPin = [hostPrefs.askForPin boolValue]; - if (!requestPin) { - if (hostPrefs.hostPin == nil || hostPrefs.hostPin.length == 0) { - requestPin = YES; - } - } - } - if (requestPin == YES) { - PinEntryViewController* pinEntry = [[PinEntryViewController alloc] init]; - [pinEntry setDelegate:self]; - [pinEntry setHostName:_host.hostName]; - [pinEntry setShouldPrompt:YES]; - [pinEntry setPairingSupported:pairingSupported]; - - [self presentViewController:pinEntry animated:YES completion:nil]; - } else { - [_clientToHostProxy authenticationResponse:hostPrefs.hostPin - createPair:pairingSupported]; - } -} - -// @protocol ClientControllerDelegate -// Occurs when a connection to a HOST is established successfully -- (void)connected { - // Everything is good, nothing to do -} - -// @protocol ClientControllerDelegate -- (void)connectionStatus:(NSString*)statusMessage { - _statusMessage = statusMessage; - - if ([_statusMessage isEqual:@"Connection closed"]) { - [self goBack]; - } else { - [self updateLabels]; - } -} - -// @protocol ClientControllerDelegate -// Occurs when a connection to a HOST has failed -- (void)connectionFailed:(NSString*)errorMessage { - [_busyIndicator stopAnimating]; - NSString* errorMsg; - if ([_clientToHostProxy isConnected]) { - errorMsg = @"Lost Connection"; - } else { - errorMsg = @"Unable to connect"; - } - [Utility showAlert:errorMsg message:errorMessage]; - [self goBack]; -} - -// @protocol ClientControllerDelegate -// Copy the updated regions to a backing store to be consumed by the GL Context -// on a different thread. A region is stored in disjoint memory locations, and -// must be transformed to a contiguous memory buffer for a GL Texture write. -// /-----\ -// | 2-4| This buffer is 5x3 bytes large, a region exists at bytes 2 to 4 and -// | 7-9| bytes 7 to 9. The region is extracted to a new contiguous buffer -// | | of 6 bytes in length. -// \-----/ -// More than 1 region may exist in the frame from each call, in which case a new -// buffer is created for each region -- (void)applyFrame:(const webrtc::DesktopSize&)size - stride:(NSInteger)stride - data:(uint8_t*)data - regions:(const std::vector&)regions { - [_glBufferLock lock]; // going to make changes to |_glRegions| - - if (!_scene.frameSize.equals(size)) { - // When this is the initial frame, the busyIndicator is still spinning. Now - // is a good time to stop it. - [_busyIndicator stopAnimating]; - - // If the |_toolbar| is still showing, hide it. - [self showToolbar:NO]; - [_scene setContentSize: - [Utility getOrientatedSize:self.view.bounds.size - shouldWidthBeLongestSide:[Utility isInLandscapeMode]]]; - [_scene setFrameSize:size]; - [_desktop setTextureSize:size]; - [_mouse setTextureSize:size]; - } - - uint32_t src_stride = stride; - - for (uint32_t i = 0; i < regions.size(); i++) { - scoped_ptr region(new GLRegion()); - - if (region.get()) { - webrtc::DesktopRect rect = regions.at(i); - - webrtc::DesktopSize(rect.width(), rect.height()); - region->offset.reset(new webrtc::DesktopVector(rect.left(), rect.top())); - region->image.reset(new webrtc::BasicDesktopFrame( - webrtc::DesktopSize(rect.width(), rect.height()))); - - if (region->image->data()) { - uint32_t bytes_per_row = - region->image->kBytesPerPixel * region->image->size().width(); - - uint32_t offset = - (src_stride * region->offset->y()) + // row - (region->offset->x() * region->image->kBytesPerPixel); // column - - uint8_t* src_buffer = data + offset; - uint8_t* dst_buffer = region->image->data(); - - // row by row copy - for (uint32_t j = 0; j < region->image->size().height(); j++) { - memcpy(dst_buffer, src_buffer, bytes_per_row); - dst_buffer += bytes_per_row; - src_buffer += src_stride; - } - _glRegions.push_back(region.release()); - } - } - } - [_glBufferLock unlock]; // done making changes to |_glRegions| -} - -// @protocol ClientControllerDelegate -// Copy the delivered cursor to a backing store to be consumed by the GL Context -// on a different thread. Note only the most recent cursor is of importance, -// discard the previous cursor. -- (void)applyCursor:(const webrtc::DesktopSize&)size - hotspot:(const webrtc::DesktopVector&)hotspot - cursorData:(uint8_t*)data { - - [_glCursorLock lock]; // going to make changes to |_cursor| - - // MouseCursor takes ownership of DesktopFrame - [_mouse setCursor:new webrtc::MouseCursor(new webrtc::BasicDesktopFrame(size), - hotspot)]; - - if (_mouse.cursor.image().data()) { - memcpy(_mouse.cursor.image().data(), - data, - size.width() * size.height() * _mouse.cursor.image().kBytesPerPixel); - } else { - [_mouse setCursor:NULL]; - } - - [_glCursorLock unlock]; // done making changes to |_cursor| -} - -// @protocol GLKViewDelegate -// There is quite a few gotchas involved in working with this function. For -// sanity purposes, I've just assumed calls to the function are on a different -// thread which I've termed GL Context. Any variables consumed by this function -// should be thread safe. -// -// Clear Screen, update desktop, update cursor, define position, and finally -// present -// -// In general, avoid expensive work in this function to maximize frame rate. -- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { - [self updatePanVelocityShouldCancel:NO]; - - // Clear to black, to give the background color - glClearColor(0.0, 0.0, 0.0, 1.0); - glClear(GL_COLOR_BUFFER_BIT); - - [Utility logGLErrorCode:@"drawInRect bindBuffer"]; - - if (_glRegions.size() > 0 || [_desktop needDraw]) { - [_glBufferLock lock]; - - for (uint32_t i = 0; i < _glRegions.size(); i++) { - // |_glRegions[i].data| has been properly ordered by [self applyFrame] - [_desktop drawRegion:_glRegions[i] rect:rect]; - } - - _glRegions.clear(); - [_glBufferLock unlock]; - } - - if ([_mouse needDrawAtPosition:_scene.mousePosition]) { - [_glCursorLock lock]; - [_mouse drawWithMousePosition:_scene.mousePosition]; - [_glCursorLock unlock]; - } - - [_effect transform].projectionMatrix = _scene.projectionMatrix; - [_effect transform].modelviewMatrix = _scene.modelViewMatrix; - [_effect prepareToDraw]; - - [Utility logGLErrorCode:@"drawInRect prepareToDrawComplete"]; - - [_scene draw]; -} - -// @protocol KeyInputDelegate -- (void)keyboardDismissed { - [self updateLabels]; -} - -// @protocol KeyInputDelegate -// Send keyboard input to HOST -- (void)keyboardActionKeyCode:(uint32_t)keyPressed isKeyDown:(BOOL)keyDown { - [_clientToHostProxy keyboardAction:keyPressed keyDown:keyDown]; -} - -- (BOOL)isToolbarHidden { - return (_toolbar.frame.origin.y < 0); -} - -// Update the scene acceleration vector -- (void)updatePanVelocityShouldCancel:(bool)canceled { - if (canceled) { - [_scene setPanVelocity:CGPointMake(0, 0)]; - } - BOOL inMotion = [_scene tickPanVelocity]; - - _singleTapRecognizer.enabled = !inMotion; - _longPressRecognizer.enabled = !inMotion; -} - -- (void)applySceneChange:(CGPoint)translation scaleBy:(float)ratio { - [_scene panAndZoom:translation scaleBy:ratio]; - // Notify HOST that the mouse moved - [Utility moveMouse:_clientToHostProxy at:_scene.mousePosition]; -} - -// Callback from NSNotificationCenter when the User changes orientation -- (void)orientationChanged:(NSNotification*)note { - [_scene - setContentSize:[Utility getOrientatedSize:self.view.bounds.size - shouldWidthBeLongestSide:[Utility isInLandscapeMode]]]; - [self showToolbar:![self isToolbarHidden]]; - [self updateLabels]; -} - -// Animate |_toolbar| by moving it on or offscreen -- (void)showToolbar:(BOOL)visible { - CGRect frame = [_toolbar frame]; - - _toolBarYPosition.constant = -frame.size.height; - int topOffset = kTopMargin; - - if (visible) { - topOffset += frame.size.height; - _toolBarYPosition.constant = kTopMargin; - } - - _hiddenToolbarYPosition.constant = topOffset; - [_scene setMarginsFromLeft:0 right:0 top:topOffset bottom:kBottomMargin]; - - // hidden when |_toolbar| is |visible| - _hiddenToolbar.hidden = (visible == YES); - - [UIView animateWithDuration:0.5 - animations:^{ [self.view layoutIfNeeded]; } - completion:^(BOOL finished) {// Nothing to do for now - }]; - - // Center view if needed for any reason. - // Specificallly, if the top anchor is active. - [self applySceneChange:CGPointMake(0.0, 0.0) scaleBy:1.0]; -} -@end diff --git a/remoting/ios/ui/pin_entry_view_controller.h b/remoting/ios/ui/pin_entry_view_controller.h deleted file mode 100644 index aaf854a1455d0..0000000000000 --- a/remoting/ios/ui/pin_entry_view_controller.h +++ /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. - -#ifndef REMOTING_IOS_UI_PIN_ENTRY_VIEW_CONTROLLER_H_ -#define REMOTING_IOS_UI_PIN_ENTRY_VIEW_CONTROLLER_H_ - -#import - -// Contract to handle finalization for Pin Prompt -@protocol PinEntryViewControllerDelegate - -// Returns with user's Pin. Pin has not been validated with the server yet. -// |shouldPrompt| indicates whether a prompt should be needed for the next login -// attempt with this host. -- (void)connectToHostWithPin:(UIViewController*)controller - hostPin:(NSString*)hostPin - shouldPrompt:(BOOL)shouldPrompt; - -// Returns when the user has cancelled the input, effectively closing the -// connection attempt. -- (void)cancelledConnectToHostWithPin:(UIViewController*)controller; - -@end - -// Dialog for user's Pin input. If a host has |pairingSupported| then user has -// the option to save a token for authentication. -@interface PinEntryViewController : UIViewController { - @private - IBOutlet UIView* _controlView; - IBOutlet UIButton* _cancelButton; - IBOutlet UIButton* _connectButton; - IBOutlet UILabel* _host; - IBOutlet UISwitch* _switchAskAgain; - IBOutlet UILabel* _shouldSavePin; - IBOutlet UITextField* _hostPin; -} - -@property(weak, nonatomic) id delegate; -@property(nonatomic, copy) NSString* hostName; -@property(nonatomic) BOOL shouldPrompt; -@property(nonatomic) BOOL pairingSupported; - -- (IBAction)buttonCancelClicked:(id)sender; -- (IBAction)buttonConnectClicked:(id)sender; - -@end - -#endif // REMOTING_IOS_UI_PIN_ENTRY_VIEW_CONTROLLER_H_ \ No newline at end of file diff --git a/remoting/ios/ui/pin_entry_view_controller.mm b/remoting/ios/ui/pin_entry_view_controller.mm deleted file mode 100644 index 9a37677a50478..0000000000000 --- a/remoting/ios/ui/pin_entry_view_controller.mm +++ /dev/null @@ -1,71 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/pin_entry_view_controller.h" - -#import "remoting/ios/utility.h" - -@implementation PinEntryViewController - -@synthesize delegate = _delegate; -@synthesize shouldPrompt = _shouldPrompt; -@synthesize pairingSupported = _pairingSupported; - -// Override UIViewController -- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil { - // NibName is the * part of your *.xib file - - if ([Utility isPad]) { - self = [super initWithNibName:@"pin_entry_view_controller_ipad" bundle:nil]; - } else { - self = - [super initWithNibName:@"pin_entry_view_controller_iphone" bundle:nil]; - } - if (self) { - // Custom initialization - } - return self; -} - -// Override UIViewController -// Controls are not created immediately, properties must be set before the form -// is displayed -- (void)viewWillAppear:(BOOL)animated { - _host.text = _hostName; - - [_switchAskAgain setOn:!_shouldPrompt]; - - // TODO (aboone) The switch is being hidden in all cases, this functionality - // is not scheduled for QA yet. - // if (!_pairingSupported) { - _switchAskAgain.hidden = YES; - _shouldSavePin.hidden = YES; - _switchAskAgain.enabled = NO; - //} - [_hostPin becomeFirstResponder]; -} - -// @protocol UITextFieldDelegate, called when the 'enter' key is pressed -- (BOOL)textFieldShouldReturn:(UITextField*)textField { - [textField resignFirstResponder]; - if (textField == _hostPin) - [self buttonConnectClicked:self]; - return YES; -} - -- (IBAction)buttonCancelClicked:(id)sender { - [_delegate cancelledConnectToHostWithPin:self]; -} - -- (IBAction)buttonConnectClicked:(id)sender { - [_delegate connectToHostWithPin:self - hostPin:_hostPin.text - shouldPrompt:!_switchAskAgain.isOn]; -} - -@end diff --git a/remoting/ios/ui/pin_entry_view_controller_ipad.xib b/remoting/ios/ui/pin_entry_view_controller_ipad.xib deleted file mode 100644 index 6846c811bc102..0000000000000 --- a/remoting/ios/ui/pin_entry_view_controller_ipad.xib +++ /dev/null @@ -1,103 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/remoting/ios/ui/pin_entry_view_controller_iphone.xib b/remoting/ios/ui/pin_entry_view_controller_iphone.xib deleted file mode 100644 index f7bb2a2a11358..0000000000000 --- a/remoting/ios/ui/pin_entry_view_controller_iphone.xib +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/remoting/ios/ui/scene_view.h b/remoting/ios/ui/scene_view.h deleted file mode 100644 index 8f082ff9910b0..0000000000000 --- a/remoting/ios/ui/scene_view.h +++ /dev/null @@ -1,171 +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 REMOTING_IOS_UI_SCENE_VIEW_H_ -#define REMOTING_IOS_UI_SCENE_VIEW_H_ - -#import -#import - -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -typedef struct { - bool left; - bool right; - bool top; - bool bottom; -} AnchorPosition; - -typedef struct { - int left; - int right; - int top; - int bottom; -} MarginQuad; - -typedef struct { - CGPoint geometryVertex; - CGPoint textureVertex; -} TexturedVertex; - -typedef struct { - TexturedVertex bl; - TexturedVertex br; - TexturedVertex tl; - TexturedVertex tr; -} TexturedQuad; - -@interface SceneView : NSObject { - @private - - // GL name - GLuint _textureId; - - GLKMatrix4 _projectionMatrix; - GLKMatrix4 _modelViewMatrix; - - // The draw surface is a triangle strip (triangles defined by the intersecting - // vertexes) to create a rectangle surface. - // 1****3 - // | / | - // | / | - // 2****4 - // This also determines the resolution of our surface, being a unit (NxN) grid - // with finite divisions. For our surface N = 1, and the number of divisions - // respects the CLIENT's desktop resolution. - TexturedQuad _glQuad; - - // Cache of the CLIENT's desktop resolution. - webrtc::DesktopSize _contentSize; - // Cache of the HOST's desktop resolution. - webrtc::DesktopSize _frameSize; - - // Location of the mouse according to the CLIENT in the prospective of the - // HOST resolution - webrtc::DesktopVector _mousePosition; - - // When a user pans they expect the view to experience acceleration after - // they release the pan gesture. We track that velocity vector as a position - // delta factored over the frame rate of the GL Context. Velocity is - // accounted as a float. - CGPoint _panVelocity; -} - -// The position of the scene is tracked in the prospective of the CLIENT -// resolution. The Z-axis is used to track the scale of the render, our scene -// never changes position on the Z-axis. -@property(nonatomic, readonly) GLKVector3 position; - -// Space around border consumed by non-scene elements, we can not draw here -@property(nonatomic, readonly) MarginQuad margin; - -@property(nonatomic, readonly) AnchorPosition anchored; - -- (const GLKMatrix4&)projectionMatrix; - -// calculate and return the current model view matrix -- (const GLKMatrix4&)modelViewMatrix; - -- (const webrtc::DesktopSize&)contentSize; - -// Update the CLIENT resolution and draw scene size, accounting for margins -- (void)setContentSize:(const CGSize&)size; - -- (const webrtc::DesktopSize&)frameSize; - -// Update the HOST resolution and reinitialize the scene positioning -- (void)setFrameSize:(const webrtc::DesktopSize&)size; - -- (const webrtc::DesktopVector&)mousePosition; - -- (void)setPanVelocity:(const CGPoint&)delta; - -- (void)setMarginsFromLeft:(int)left - right:(int)right - top:(int)top - bottom:(int)bottom; - -// Draws to a GL Context -- (void)draw; - -- (BOOL)containsTouchPoint:(CGPoint)point; - -// Applies translation and zoom. Translation is bounded to screen edges. -// Zooming is bounded on the lower side to the maximum of width and height, and -// on the upper side by a constant, experimentally chosen. -- (void)panAndZoom:(CGPoint)translation scaleBy:(float)scale; - -// Mouse is tracked in the perspective of the HOST desktop, but the projection -// to the user is in the perspective of the CLIENT resolution. Find the HOST -// position that is the center of the current CLIENT view. If the mouse is in -// the half of the CLIENT screen that is closest to an anchor, then move the -// mouse, otherwise the mouse should be centered. -- (void)updateMousePositionAndAnchorsWithTranslation:(CGPoint)translation - scale:(float)scale; - -// When zoom is changed the scene is translated to keep an anchored point -// (an anchored edge, or the spot the user is touching) at the same place in the -// User's perspective. Return the delta of the position of the lower endpoint -// of the axis -+ (float)positionDeltaFromScaling:(float)ratio - position:(float)position - length:(float)length - anchor:(float)anchor; - -// Return the delta of the position of the lower endpoint of the axis -+ (int)positionDeltaFromTranslation:(int)translation - position:(int)position - freeSpace:(int)freeSpace - scaleingPositionDelta:(int)scaleingPositionDelta - isAnchoredLow:(BOOL)isAnchoredLow - isAnchoredHigh:(BOOL)isAnchoredHigh; - -// |position + delta| is snapped to the bounds, return the delta in respect to -// the bounding. -+ (int)boundDeltaFromPosition:(float)position - delta:(int)delta - lowerBound:(int)lowerBound - upperBound:(int)upperBound; - -// Return |nextPosition| when it is anchored and still in the respective 1/2 of -// the screen. When |nextPosition| is outside scene's edge, snap to edge. -// Otherwise return |centerPosition| -+ (int)boundMouseGivenNextPosition:(int)nextPosition - maxPosition:(int)maxPosition - centerPosition:(int)centerPosition - isAnchoredLow:(BOOL)isAnchoredLow - isAnchoredHigh:(BOOL)isAnchoredHigh; - -// If the mouse is at an edge return zero, otherwise return |velocity| -+ (float)boundVelocity:(float)velocity - axisLength:(int)axisLength - mousePosition:(int)mousePosition; - -// Update the scene acceleration vector. -// Returns true if velocity before 'ticking' is non-zero. -- (BOOL)tickPanVelocity; - -@end - -#endif // REMOTING_IOS_UI_SCENE_VIEW_H_ \ No newline at end of file diff --git a/remoting/ios/ui/scene_view.mm b/remoting/ios/ui/scene_view.mm deleted file mode 100644 index 97a577df77584..0000000000000 --- a/remoting/ios/ui/scene_view.mm +++ /dev/null @@ -1,642 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/scene_view.h" - -#import "remoting/ios/utility.h" - -namespace { - -// TODO (aboone) Some of the layout is not yet set in stone, so variables have -// been used to position and turn items on and off. Eventually these may be -// stabilized and removed. - -// Scroll speed multiplier for swiping -const static int kMouseSensitivity = 2.5; - -// Input Axis inversion -// 1 for standard, -1 for inverted -const static int kXAxisInversion = -1; -const static int kYAxisInversion = -1; - -// Experimental value for bounding the maximum zoom ratio -const static int kMaxZoomSize = 3; -} // namespace - -@interface SceneView (Private) -// Returns the number of pixels displayed per device pixel when the scaling is -// such that the entire frame would fit perfectly in content. Note the ratios -// are different for width and height, some people have multiple monitors, some -// have 16:9 or 4:3 while iPad is always single screen, but different iOS -// devices have different resolutions. -- (CGPoint)pixelRatio; - -// Return the FrameSize in perspective of the CLIENT resolution -- (webrtc::DesktopSize)frameSizeToScale:(float)scale; - -// When bounded on the top and right, this point is where the scene must be -// positioned given a scene size -- (webrtc::DesktopVector)getBoundsForSize:(const webrtc::DesktopSize&)size; - -// Converts a point in the the CLIENT resolution to a similar point in the HOST -// resolution. Additionally, CLIENT resolution is expressed in float values -// while HOST operates in integer values. -- (BOOL)convertTouchPointToMousePoint:(CGPoint)touchPoint - targetPoint:(webrtc::DesktopVector&)desktopPoint; - -// Converts a point in the the HOST resolution to a similar point in the CLIENT -// resolution. Additionally, CLIENT resolution is expressed in float values -// while HOST operates in integer values. -- (BOOL)convertMousePointToTouchPoint:(const webrtc::DesktopVector&)mousePoint - targetPoint:(CGPoint&)touchPoint; -@end - -@implementation SceneView - -- (id)init { - self = [super init]; - if (self) { - - _frameSize = webrtc::DesktopSize(1, 1); - _contentSize = webrtc::DesktopSize(1, 1); - _mousePosition = webrtc::DesktopVector(0, 0); - - _position = GLKVector3Make(0, 0, 1); - _margin.left = 0; - _margin.right = 0; - _margin.top = 0; - _margin.bottom = 0; - _anchored.left = false; - _anchored.right = false; - _anchored.top = false; - _anchored.bottom = false; - } - return self; -} - -- (const GLKMatrix4&)projectionMatrix { - return _projectionMatrix; -} - -- (const GLKMatrix4&)modelViewMatrix { - // Start by using the entire scene - _modelViewMatrix = GLKMatrix4Identity; - - // Position scene according to any panning or bounds - _modelViewMatrix = GLKMatrix4Translate(_modelViewMatrix, - _position.x + _margin.left, - _position.y + _margin.bottom, - 0.0); - - // Apply zoom - _modelViewMatrix = GLKMatrix4Scale(_modelViewMatrix, - _position.z / self.pixelRatio.x, - _position.z / self.pixelRatio.y, - 1.0); - - // We are directly above the screen and looking down. - static const GLKMatrix4 viewMatrix = GLKMatrix4MakeLookAt( - 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // center view - - _modelViewMatrix = GLKMatrix4Multiply(viewMatrix, _modelViewMatrix); - - return _modelViewMatrix; -} - -- (const webrtc::DesktopSize&)contentSize { - return _contentSize; -} - -- (void)setContentSize:(const CGSize&)size { - - _contentSize.set(size.width, size.height); - - _projectionMatrix = GLKMatrix4MakeOrtho( - 0.0, _contentSize.width(), 0.0, _contentSize.height(), 1.0, -1.0); - - TexturedQuad newQuad; - newQuad.bl.geometryVertex = CGPointMake(0.0, 0.0); - newQuad.br.geometryVertex = CGPointMake(_contentSize.width(), 0.0); - newQuad.tl.geometryVertex = CGPointMake(0.0, _contentSize.height()); - newQuad.tr.geometryVertex = - CGPointMake(_contentSize.width(), _contentSize.height()); - - newQuad.bl.textureVertex = CGPointMake(0.0, 1.0); - newQuad.br.textureVertex = CGPointMake(1.0, 1.0); - newQuad.tl.textureVertex = CGPointMake(0.0, 0.0); - newQuad.tr.textureVertex = CGPointMake(1.0, 0.0); - - _glQuad = newQuad; -} - -- (const webrtc::DesktopSize&)frameSize { - return _frameSize; -} - -- (void)setFrameSize:(const webrtc::DesktopSize&)size { - DCHECK(size.width() > 0 && size.height() > 0); - // Don't do anything if the size has not changed. - if (_frameSize.equals(size)) - return; - - _frameSize.set(size.width(), size.height()); - - _position.x = 0; - _position.y = 0; - - float verticalPixelScaleRatio = - (static_cast(_contentSize.height() - _margin.top - - _margin.bottom) / - static_cast(_frameSize.height())) / - _position.z; - - // Anchored at the position (0,0) - _anchored.left = YES; - _anchored.right = NO; - _anchored.top = NO; - _anchored.bottom = YES; - - [self panAndZoom:CGPointMake(0.0, 0.0) scaleBy:verticalPixelScaleRatio]; - - // Center the mouse on the CLIENT screen - webrtc::DesktopVector centerMouseLocation; - if ([self convertTouchPointToMousePoint:CGPointMake(_contentSize.width() / 2, - _contentSize.height() / 2) - targetPoint:centerMouseLocation]) { - _mousePosition.set(centerMouseLocation.x(), centerMouseLocation.y()); - } - -#if DEBUG - NSLog(@"resized frame:%d:%d scale:%f", - _frameSize.width(), - _frameSize.height(), - _position.z); -#endif // DEBUG -} - -- (const webrtc::DesktopVector&)mousePosition { - return _mousePosition; -} - -- (void)setPanVelocity:(const CGPoint&)delta { - _panVelocity.x = delta.x; - _panVelocity.y = delta.y; -} - -- (void)setMarginsFromLeft:(int)left - right:(int)right - top:(int)top - bottom:(int)bottom { - _margin.left = left; - _margin.right = right; - _margin.top = top; - _margin.bottom = bottom; -} - -- (void)draw { - glEnableVertexAttribArray(GLKVertexAttribPosition); - glEnableVertexAttribArray(GLKVertexAttribTexCoord0); - glEnableVertexAttribArray(GLKVertexAttribTexCoord1); - - // Define our scene space - glVertexAttribPointer(GLKVertexAttribPosition, - 2, - GL_FLOAT, - GL_FALSE, - sizeof(TexturedVertex), - &(_glQuad.bl.geometryVertex)); - // Define the desktop plane - glVertexAttribPointer(GLKVertexAttribTexCoord0, - 2, - GL_FLOAT, - GL_FALSE, - sizeof(TexturedVertex), - &(_glQuad.bl.textureVertex)); - // Define the cursor plane - glVertexAttribPointer(GLKVertexAttribTexCoord1, - 2, - GL_FLOAT, - GL_FALSE, - sizeof(TexturedVertex), - &(_glQuad.bl.textureVertex)); - - // Draw! - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - [Utility logGLErrorCode:@"SceneView draw"]; -} - -- (CGPoint)pixelRatio { - - CGPoint r = CGPointMake(static_cast(_contentSize.width()) / - static_cast(_frameSize.width()), - static_cast(_contentSize.height()) / - static_cast(_frameSize.height())); - return r; -} - -- (webrtc::DesktopSize)frameSizeToScale:(float)scale { - return webrtc::DesktopSize(_frameSize.width() * scale, - _frameSize.height() * scale); -} - -- (webrtc::DesktopVector)getBoundsForSize:(const webrtc::DesktopSize&)size { - webrtc::DesktopVector r( - _contentSize.width() - _margin.left - _margin.right - size.width(), - _contentSize.height() - _margin.bottom - _margin.top - size.height()); - - if (r.x() > 0) { - r.set((_contentSize.width() - size.width()) / 2, r.y()); - } - - if (r.y() > 0) { - r.set(r.x(), (_contentSize.height() - size.height()) / 2); - } - - return r; -} - -- (BOOL)containsTouchPoint:(CGPoint)point { - // Here frame is from the top-left corner, most other calculations are framed - // from the bottom left. - CGRect frame = - CGRectMake(_margin.left, - _margin.top, - _contentSize.width() - _margin.left - _margin.right, - _contentSize.height() - _margin.top - _margin.bottom); - return CGRectContainsPoint(frame, point); -} - -- (BOOL)convertTouchPointToMousePoint:(CGPoint)touchPoint - targetPoint:(webrtc::DesktopVector&)mousePoint { - if (![self containsTouchPoint:touchPoint]) { - return NO; - } - // A touch location occurs in respect to the user's entire view surface. - - // The GL Context is upside down from the User's perspective so flip it. - CGPoint glOrientedTouchPoint = - CGPointMake(touchPoint.x, _contentSize.height() - touchPoint.y); - - // The GL surface generally is not at the same origination point as the touch, - // so translate by the scene's position. - CGPoint glOrientedPointInRespectToFrame = - CGPointMake(glOrientedTouchPoint.x - _position.x, - glOrientedTouchPoint.y - _position.y); - - // The perspective exists in relative to the CLIENT resolution at 1:1, zoom - // our perspective so we are relative to the HOST at 1:1 - CGPoint glOrientedPointInFrame = - CGPointMake(glOrientedPointInRespectToFrame.x / _position.z, - glOrientedPointInRespectToFrame.y / _position.z); - - // Finally, flip the perspective back over to the Users, but this time in - // respect to the HOST desktop. Floor to ensure the result is always in - // frame. - CGPoint deskTopOrientedPointInFrame = - CGPointMake(floorf(glOrientedPointInFrame.x), - floorf(_frameSize.height() - glOrientedPointInFrame.y)); - - // Convert from float to integer - mousePoint.set(deskTopOrientedPointInFrame.x, deskTopOrientedPointInFrame.y); - - return CGRectContainsPoint( - CGRectMake(0, 0, _frameSize.width(), _frameSize.height()), - deskTopOrientedPointInFrame); -} - -- (BOOL)convertMousePointToTouchPoint:(const webrtc::DesktopVector&)mousePoint - targetPoint:(CGPoint&)touchPoint { - // A mouse point is in respect to the desktop frame. - - // Flip the perspective back over to the Users, in - // respect to the HOST desktop. - CGPoint deskTopOrientedPointInFrame = - CGPointMake(mousePoint.x(), _frameSize.height() - mousePoint.y()); - - // The perspective exists in relative to the CLIENT resolution at 1:1, zoom - // our perspective so we are relative to the HOST at 1:1 - CGPoint glOrientedPointInFrame = - CGPointMake(deskTopOrientedPointInFrame.x * _position.z, - deskTopOrientedPointInFrame.y * _position.z); - - // The GL surface generally is not at the same origination point as the touch, - // so translate by the scene's position. - CGPoint glOrientedPointInRespectToFrame = - CGPointMake(glOrientedPointInFrame.x + _position.x, - glOrientedPointInFrame.y + _position.y); - - // Convert from float to integer - touchPoint.x = floorf(glOrientedPointInRespectToFrame.x); - touchPoint.y = floorf(glOrientedPointInRespectToFrame.y); - - return [self containsTouchPoint:touchPoint]; -} - -- (void)panAndZoom:(CGPoint)translation scaleBy:(float)ratio { - CGPoint ratios = [self pixelRatio]; - - // New Scaling factor bounded by a min and max - float resultScale = _position.z * ratio; - float scaleUpperBound = MAX(ratios.x, MAX(ratios.y, kMaxZoomSize)); - float scaleLowerBound = MIN(ratios.x, ratios.y); - - if (resultScale < scaleLowerBound) { - resultScale = scaleLowerBound; - } else if (resultScale > scaleUpperBound) { - resultScale = scaleUpperBound; - } - - DCHECK(isnormal(resultScale) && resultScale > 0); - - // The GL perspective is upside down in relation to the User's view, so flip - // the translation - translation.y = -translation.y; - - // The constants here could be user options later. - translation.x = - translation.x * kXAxisInversion * (1 / (ratios.x * kMouseSensitivity)); - translation.y = - translation.y * kYAxisInversion * (1 / (ratios.y * kMouseSensitivity)); - - CGPoint delta = CGPointMake(0, 0); - CGPoint scaleDelta = CGPointMake(0, 0); - - webrtc::DesktopSize currentSize = [self frameSizeToScale:_position.z]; - - { - // Closure for this variable, so the variable is not available to the rest - // of this function - webrtc::DesktopVector currentBounds = [self getBoundsForSize:currentSize]; - // There are rounding errors in the scope of this function, see the - // butterfly effect. In successive calls, the resulting position isn't - // always exactly the calculated position. If we know we are Anchored, then - // go ahead and reposition it to the values above. - if (_anchored.right) { - _position.x = currentBounds.x(); - } - - if (_anchored.top) { - _position.y = currentBounds.y(); - } - } - - if (_position.z != resultScale) { - // When scaling the scene, the origination of scaling is the mouse's - // location. But when the frame is anchored, adjust the origination to the - // anchor point. - - CGPoint mousePositionInClientResolution; - [self convertMousePointToTouchPoint:_mousePosition - targetPoint:mousePositionInClientResolution]; - - // Prefer to zoom based on the left anchor when there is a choice - if (_anchored.left) { - mousePositionInClientResolution.x = 0; - } else if (_anchored.right) { - mousePositionInClientResolution.x = _contentSize.width(); - } - - // Prefer to zoom out from the top anchor when there is a choice - if (_anchored.top) { - mousePositionInClientResolution.y = _contentSize.height(); - } else if (_anchored.bottom) { - mousePositionInClientResolution.y = 0; - } - - scaleDelta.x -= - [SceneView positionDeltaFromScaling:ratio - position:_position.x - length:currentSize.width() - anchor:mousePositionInClientResolution.x]; - - scaleDelta.y -= - [SceneView positionDeltaFromScaling:ratio - position:_position.y - length:currentSize.height() - anchor:mousePositionInClientResolution.y]; - } - - delta.x = [SceneView - positionDeltaFromTranslation:translation.x - position:_position.x - freeSpace:_contentSize.width() - currentSize.width() - scaleingPositionDelta:scaleDelta.x - isAnchoredLow:_anchored.left - isAnchoredHigh:_anchored.right]; - - delta.y = [SceneView - positionDeltaFromTranslation:translation.y - position:_position.y - freeSpace:_contentSize.height() - currentSize.height() - scaleingPositionDelta:scaleDelta.y - isAnchoredLow:_anchored.bottom - isAnchoredHigh:_anchored.top]; - { - // Closure for this variable, so the variable is not available to the rest - // of this function - webrtc::DesktopVector bounds = - [self getBoundsForSize:[self frameSizeToScale:resultScale]]; - - delta.x = [SceneView boundDeltaFromPosition:_position.x - delta:delta.x - lowerBound:bounds.x() - upperBound:0]; - - delta.y = [SceneView boundDeltaFromPosition:_position.y - delta:delta.y - lowerBound:bounds.y() - upperBound:0]; - } - - BOOL isLeftAndRightAnchored = _anchored.left && _anchored.right; - BOOL isTopAndBottomAnchored = _anchored.top && _anchored.bottom; - - [self updateMousePositionAndAnchorsWithTranslation:translation - scale:resultScale]; - - // If both anchors were lost, then keep the one that is easier to predict - if (isLeftAndRightAnchored && !_anchored.left && !_anchored.right) { - delta.x = -_position.x; - _anchored.left = YES; - } - - // If both anchors were lost, then keep the one that is easier to predict - if (isTopAndBottomAnchored && !_anchored.top && !_anchored.bottom) { - delta.y = -_position.y; - _anchored.bottom = YES; - } - - // FINALLY, update the scene's position - _position.x += delta.x; - _position.y += delta.y; - _position.z = resultScale; -} - -- (void)updateMousePositionAndAnchorsWithTranslation:(CGPoint)translation - scale:(float)scale { - webrtc::DesktopVector centerMouseLocation; - [self convertTouchPointToMousePoint:CGPointMake(_contentSize.width() / 2, - _contentSize.height() / 2) - targetPoint:centerMouseLocation]; - - webrtc::DesktopVector currentBounds = - [self getBoundsForSize:[self frameSizeToScale:_position.z]]; - webrtc::DesktopVector nextBounds = - [self getBoundsForSize:[self frameSizeToScale:scale]]; - - webrtc::DesktopVector predictedMousePosition( - _mousePosition.x() - translation.x, _mousePosition.y() + translation.y); - - _mousePosition.set( - [SceneView boundMouseGivenNextPosition:predictedMousePosition.x() - maxPosition:_frameSize.width() - centerPosition:centerMouseLocation.x() - isAnchoredLow:_anchored.left - isAnchoredHigh:_anchored.right], - [SceneView boundMouseGivenNextPosition:predictedMousePosition.y() - maxPosition:_frameSize.height() - centerPosition:centerMouseLocation.y() - isAnchoredLow:_anchored.top - isAnchoredHigh:_anchored.bottom]); - - _panVelocity.x = [SceneView boundVelocity:_panVelocity.x - axisLength:_frameSize.width() - mousePosition:_mousePosition.x()]; - _panVelocity.y = [SceneView boundVelocity:_panVelocity.y - axisLength:_frameSize.height() - mousePosition:_mousePosition.y()]; - - _anchored.left = (nextBounds.x() >= 0) || - (_position.x == 0 && - predictedMousePosition.x() <= centerMouseLocation.x()); - - _anchored.right = - (nextBounds.x() >= 0) || - (_position.x == currentBounds.x() && - predictedMousePosition.x() >= centerMouseLocation.x()) || - (_mousePosition.x() == _frameSize.width() - 1 && !_anchored.left); - - _anchored.bottom = (nextBounds.y() >= 0) || - (_position.y == 0 && - predictedMousePosition.y() >= centerMouseLocation.y()); - - _anchored.top = - (nextBounds.y() >= 0) || - (_position.y == currentBounds.y() && - predictedMousePosition.y() <= centerMouseLocation.y()) || - (_mousePosition.y() == _frameSize.height() - 1 && !_anchored.bottom); -} - -+ (float)positionDeltaFromScaling:(float)ratio - position:(float)position - length:(float)length - anchor:(float)anchor { - float newSize = length * ratio; - float scaleXBy = fabs(position - anchor) / length; - float delta = (newSize - length) * scaleXBy; - return delta; -} - -+ (int)positionDeltaFromTranslation:(int)translation - position:(int)position - freeSpace:(int)freeSpace - scaleingPositionDelta:(int)scaleingPositionDelta - isAnchoredLow:(BOOL)isAnchoredLow - isAnchoredHigh:(BOOL)isAnchoredHigh { - if (isAnchoredLow && isAnchoredHigh) { - // center the view - return (freeSpace / 2) - position; - } else if (isAnchoredLow) { - return 0; - } else if (isAnchoredHigh) { - return scaleingPositionDelta; - } else { - return translation + scaleingPositionDelta; - } -} - -+ (int)boundDeltaFromPosition:(float)position - delta:(int)delta - lowerBound:(int)lowerBound - upperBound:(int)upperBound { - int result = position + delta; - - if (lowerBound < upperBound) { // the view is larger than the bounds - if (result > upperBound) { - result = upperBound; - } else if (result < lowerBound) { - result = lowerBound; - } - } else { - // the view is smaller than the bounds so we'll always be at the lowerBound - result = lowerBound; - } - return result - position; -} - -+ (int)boundMouseGivenNextPosition:(int)nextPosition - maxPosition:(int)maxPosition - centerPosition:(int)centerPosition - isAnchoredLow:(BOOL)isAnchoredLow - isAnchoredHigh:(BOOL)isAnchoredHigh { - if (nextPosition < 0) { - return 0; - } - if (nextPosition > maxPosition - 1) { - return maxPosition - 1; - } - - if ((isAnchoredLow && nextPosition <= centerPosition) || - (isAnchoredHigh && nextPosition >= centerPosition)) { - return nextPosition; - } - - return centerPosition; -} - -+ (float)boundVelocity:(float)velocity - axisLength:(int)axisLength - mousePosition:(int)mousePosition { - if (velocity != 0) { - if (mousePosition <= 0 || mousePosition >= (axisLength - 1)) { - return 0; - } - } - - return velocity; -} - -- (BOOL)tickPanVelocity { - BOOL inMotion = ((_panVelocity.x != 0.0) || (_panVelocity.y != 0.0)); - - if (inMotion) { - - uint32_t divisor = 50 / _position.z; - float reducer = .95; - - if (_panVelocity.x != 0.0 && ABS(_panVelocity.x) < divisor) { - _panVelocity = CGPointMake(0.0, _panVelocity.y); - } - - if (_panVelocity.y != 0.0 && ABS(_panVelocity.y) < divisor) { - _panVelocity = CGPointMake(_panVelocity.x, 0.0); - } - - [self panAndZoom:CGPointMake(_panVelocity.x / divisor, - _panVelocity.y / divisor) - scaleBy:1.0]; - - _panVelocity.x *= reducer; - _panVelocity.y *= reducer; - } - - return inMotion; -} - -@end \ No newline at end of file diff --git a/remoting/ios/ui/scene_view_unittest.mm b/remoting/ios/ui/scene_view_unittest.mm deleted file mode 100644 index d1dfabcd7dbd4..0000000000000 --- a/remoting/ios/ui/scene_view_unittest.mm +++ /dev/null @@ -1,1219 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "remoting/ios/ui/scene_view.h" - -#import "base/compiler_specific.h" -#import "testing/gtest_mac.h" - -namespace remoting { - -namespace { -const int kClientWidth = 200; -const int kClientHeight = 100; -const webrtc::DesktopSize kClientSize(kClientWidth, kClientHeight); -// Smaller then ClientSize -const webrtc::DesktopSize kSmall(50, 75); -// Inverted - The vertical is closer to an edge than the horizontal -const webrtc::DesktopSize kSmallInversed(175, 50); -// Larger then ClientSize -const webrtc::DesktopSize kLarge(800, 125); -const webrtc::DesktopSize kLargeInversed(225, 400); -} // namespace - -class SceneViewTest : public ::testing::Test { - protected: - virtual void SetUp() OVERRIDE { - scene_ = [[SceneView alloc] init]; - [scene_ - setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - [scene_ setFrameSize:kLarge]; - } - - void MakeLarge() { [scene_ setFrameSize:kLarge]; } - - SceneView* scene_; -}; - -TEST(SceneViewTest_Property, ContentSize) { - SceneView* scene = [[SceneView alloc] init]; - - [scene setContentSize:CGSizeMake(0, 0)]; - EXPECT_EQ(0, scene.contentSize.width()); - EXPECT_EQ(0, scene.contentSize.height()); - float zeros[16] = {1.0f / 0.0f, 0, 0, 0, 0, 1.0f / 0.0f, 0, 0, - 0, 0, 1, 0, 0.0f / 0.0f, 0.0f / 0.0f, 0, 1}; - - ASSERT_TRUE(memcmp(zeros, scene.projectionMatrix.m, 16 * sizeof(float)) == 0); - - [scene setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - EXPECT_EQ(kClientSize.width(), scene.contentSize.width()); - EXPECT_EQ(kClientSize.height(), scene.contentSize.height()); - - EXPECT_TRUE(memcmp(GLKMatrix4MakeOrtho( - 0.0, kClientWidth, 0.0, kClientHeight, 1.0, -1.0).m, - scene.projectionMatrix.m, - 16 * sizeof(float)) == 0); -} - -TEST(SceneViewTest_Property, FrameSizeInit) { - SceneView* scene = [[SceneView alloc] init]; - [scene setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - - [scene setFrameSize:webrtc::DesktopSize(1, 1)]; - EXPECT_EQ(1, scene.frameSize.width()); - EXPECT_EQ(1, scene.frameSize.height()); - - EXPECT_EQ(0, scene.position.x); - EXPECT_EQ(0, scene.position.y); - EXPECT_EQ(1, scene.position.z); - - EXPECT_FALSE(scene.anchored.left); - EXPECT_FALSE(scene.anchored.right); - EXPECT_FALSE(scene.anchored.top); - EXPECT_FALSE(scene.anchored.bottom); - - EXPECT_EQ(0, scene.mousePosition.x()); - EXPECT_EQ(0, scene.mousePosition.y()); -} - -TEST(SceneViewTest_Property, FrameSizeLarge) { - SceneView* scene = [[SceneView alloc] init]; - [scene setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - [scene setFrameSize:kLarge]; - EXPECT_EQ(kLarge.width(), scene.frameSize.width()); - EXPECT_EQ(kLarge.height(), scene.frameSize.height()); - - // Screen is positioned in the lower,left corner, zoomed until the vertical - // fits exactly, and then centered horizontally - // HOST - // CLIENT ------------------------------------------------ - // ------------ | | - // | | | | - // | | | | - // | | | | - // ------------ ------------------------------------------------ - // RESULT - ONSCREEN is completely covered, with some of the HOST off screen - // (-.-) the mouse cursor - // ----------------------------------------- - // | ONSCREEN | OFFSCREEN | - // | -.- | | - // | | | - // ----------------------------------------- - float scale = static_cast(kClientSize.height()) / - static_cast(kLarge.height()); - // vertical fits exactly - EXPECT_EQ(scale, scene.position.z); - - // sitting on both Axis - EXPECT_EQ(0, scene.position.x); - EXPECT_EQ(0, scene.position.y); - - // bound on 3 sides, not on the right - EXPECT_TRUE(scene.anchored.left); - EXPECT_FALSE(scene.anchored.right); - EXPECT_TRUE(scene.anchored.top); - EXPECT_TRUE(scene.anchored.bottom); - - // mouse is off center on the left horizontal - EXPECT_EQ(kClientSize.width() / (scale * 2), scene.mousePosition.x()); - // mouse is centered vertical - EXPECT_EQ(kLarge.height() / 2, scene.mousePosition.y()); -} - -TEST(SceneViewTest_Property, FrameSizeLargeInversed) { - SceneView* scene = [[SceneView alloc] init]; - [scene setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - [scene setFrameSize:kLargeInversed]; - EXPECT_EQ(kLargeInversed.width(), scene.frameSize.width()); - EXPECT_EQ(kLargeInversed.height(), scene.frameSize.height()); - - // Screen is positioned in the lower,left corner, zoomed until the vertical - // fits exactly, and then centered horizontally - // HOST - // --------------- - // | | - // | | - // | | - // | | - // | | - // | | - // | | - // CLIENT | | - // ------------- | | - // | | | | - // | | | | - // | | | | - // ------------- --------------- - // RESULT, entire HOST is on screen - // (-.-) the mouse cursor, XX is black backdrop - // ------------- - // |XX| |XX| - // |XX| -.- |XX| - // |XX| |XX| - // ------------- - float scale = static_cast(kClientSize.height()) / - static_cast(kLargeInversed.height()); - // Vertical fits exactly - EXPECT_EQ(scale, scene.position.z); - - // centered - EXPECT_EQ( - (kClientSize.width() - static_cast(scale * kLargeInversed.width())) / - 2, - scene.position.x); - // sits on Axis - EXPECT_EQ(0, scene.position.y); - - // bound on all 4 sides - EXPECT_TRUE(scene.anchored.left); - EXPECT_TRUE(scene.anchored.right); - EXPECT_TRUE(scene.anchored.top); - EXPECT_TRUE(scene.anchored.bottom); - - // mouse is in centered both vertical and horizontal - EXPECT_EQ(kLargeInversed.width() / 2, scene.mousePosition.x()); - EXPECT_EQ(kLargeInversed.height() / 2, scene.mousePosition.y()); -} - -TEST(SceneViewTest_Property, FrameSizeSmall) { - SceneView* scene = [[SceneView alloc] init]; - [scene setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - [scene setFrameSize:kSmall]; - EXPECT_EQ(kSmall.width(), scene.frameSize.width()); - EXPECT_EQ(kSmall.height(), scene.frameSize.height()); - - // Screen is positioned in the lower,left corner, zoomed until the vertical - // fits exactly, and then centered horizontally - // CLIENT - // --------------------------- - // | | HOST - // | | ------- - // | | | | - // | | | | - // | | | | - // | | | | - // | | | | - // --------------------------- ------- - // RESULT, entire HOST is on screen - // (-.-) the mouse cursor, XX is black backdrop - // --------------------------- - // |XXXXXXXXX| |XXXXXXXXX| - // |XXXXXXXXX| |XXXXXXXXX| - // |XXXXXXXXX| |XXXXXXXXX| - // |XXXXXXXXX| -.- |XXXXXXXXX| - // |XXXXXXXXX| |XXXXXXXXX| - // |XXXXXXXXX| |XXXXXXXXX| - // |XXXXXXXXX| |XXXXXXXXX| - // --------------------------- - float scale = static_cast(kClientSize.height()) / - static_cast(kSmall.height()); - // Vertical fits exactly - EXPECT_EQ(scale, scene.position.z); - - // centered - EXPECT_EQ( - (kClientSize.width() - static_cast(scale * kSmall.width())) / 2, - scene.position.x); - // sits on Axis - EXPECT_EQ(0, scene.position.y); - - // bound on all 4 sides - EXPECT_TRUE(scene.anchored.left); - EXPECT_TRUE(scene.anchored.right); - EXPECT_TRUE(scene.anchored.top); - EXPECT_TRUE(scene.anchored.bottom); - - // mouse is in centered both vertical and horizontal - EXPECT_EQ((kSmall.width() / 2) - 1, // -1 for pixel rounding - scene.mousePosition.x()); - EXPECT_EQ(kSmall.height() / 2, scene.mousePosition.y()); -} - -TEST(SceneViewTest_Property, FrameSizeSmallInversed) { - SceneView* scene = [[SceneView alloc] init]; - [scene setContentSize:CGSizeMake(kClientSize.width(), kClientSize.height())]; - [scene setFrameSize:kSmallInversed]; - EXPECT_EQ(kSmallInversed.width(), scene.frameSize.width()); - EXPECT_EQ(kSmallInversed.height(), scene.frameSize.height()); - - // Screen is positioned in the lower,left corner, zoomed until the vertical - // fits exactly, and then centered horizontally - // CLIENT - // --------------------------- - // | | - // | | - // | | HOST - // | | ---------------------- - // | | | | - // | | | | - // | | | | - // --------------------------- ---------------------- - // RESULT - ONSCREEN is completely covered, with some of the HOST off screen - // (-.-) the mouse cursor - // -------------------------------------------- - // | ONSCREEN | OFFSCREEN | - // | | | - // | | | - // | -.- | | - // | | | - // | | | - // | | | - // -------------------------------------------- - float scale = static_cast(kClientSize.height()) / - static_cast(kSmallInversed.height()); - // vertical fits exactly - EXPECT_EQ(scale, scene.position.z); - - // sitting on both Axis - EXPECT_EQ(0, scene.position.x); - EXPECT_EQ(0, scene.position.y); - - // bound on 3 sides, not on the right - EXPECT_TRUE(scene.anchored.left); - EXPECT_FALSE(scene.anchored.right); - EXPECT_TRUE(scene.anchored.top); - EXPECT_TRUE(scene.anchored.bottom); - - // mouse is off center on the left horizontal - EXPECT_EQ(kClientSize.width() / (scale * 2), scene.mousePosition.x()); - // mouse is centered vertical - EXPECT_EQ(kSmallInversed.height() / 2, scene.mousePosition.y()); -} - -TEST_F(SceneViewTest, ContainsTouchPoint) { - int midWidth = kClientWidth / 2; - int midHeight = kClientHeight / 2; - // left - EXPECT_FALSE([scene_ containsTouchPoint:CGPointMake(-1, midHeight)]); - EXPECT_TRUE([scene_ containsTouchPoint:CGPointMake(0, midHeight)]); - // right - EXPECT_FALSE( - [scene_ containsTouchPoint:CGPointMake(kClientWidth, midHeight)]); - EXPECT_TRUE( - [scene_ containsTouchPoint:CGPointMake(kClientWidth - 1, midHeight)]); - // top - EXPECT_FALSE( - [scene_ containsTouchPoint:CGPointMake(midWidth, kClientHeight)]); - EXPECT_TRUE( - [scene_ containsTouchPoint:CGPointMake(midWidth, kClientHeight - 1)]); - // bottom - EXPECT_FALSE([scene_ containsTouchPoint:CGPointMake(midWidth, -1)]); - EXPECT_TRUE([scene_ containsTouchPoint:CGPointMake(midWidth, 0)]); - - [scene_ setMarginsFromLeft:10 right:10 top:10 bottom:10]; - - // left - EXPECT_FALSE([scene_ containsTouchPoint:CGPointMake(9, midHeight)]); - EXPECT_TRUE([scene_ containsTouchPoint:CGPointMake(10, midHeight)]); - // right - EXPECT_FALSE( - [scene_ containsTouchPoint:CGPointMake(kClientWidth - 10, midHeight)]); - EXPECT_TRUE( - [scene_ containsTouchPoint:CGPointMake(kClientWidth - 11, midHeight)]); - // top - EXPECT_FALSE( - [scene_ containsTouchPoint:CGPointMake(midWidth, kClientHeight - 10)]); - EXPECT_TRUE( - [scene_ containsTouchPoint:CGPointMake(midWidth, kClientHeight - 11)]); - // bottom - EXPECT_FALSE([scene_ containsTouchPoint:CGPointMake(midWidth, 9)]); - EXPECT_TRUE([scene_ containsTouchPoint:CGPointMake(midWidth, 10)]); -} - -TEST_F(SceneViewTest, - UpdateMousePositionAndAnchorsWithTranslationNoMovement) { - - webrtc::DesktopVector originalPosition = scene_.mousePosition; - AnchorPosition originalAnchors = scene_.anchored; - - [scene_ updateMousePositionAndAnchorsWithTranslation:CGPointMake(0, 0) - scale:1]; - - webrtc::DesktopVector newPosition = scene_.mousePosition; - - EXPECT_EQ(0, abs(originalPosition.x() - newPosition.x())); - EXPECT_EQ(0, abs(originalPosition.y() - newPosition.y())); - - EXPECT_EQ(originalAnchors.right, scene_.anchored.right); - EXPECT_EQ(originalAnchors.top, scene_.anchored.top); - EXPECT_EQ(originalAnchors.left, scene_.anchored.left); - EXPECT_EQ(originalAnchors.bottom, scene_.anchored.bottom); - - EXPECT_FALSE(scene_.tickPanVelocity); -} - -TEST_F(SceneViewTest, - UpdateMousePositionAndAnchorsWithTranslationTowardLeftAndTop) { - // Translation is in a coordinate space where (0,0) is the bottom left of the - // view. Mouse position in in a coordinate space where (0,0) is the top left - // of the view. So |y| is moved in the negative direction. - - webrtc::DesktopVector originalPosition = scene_.mousePosition; - - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ updateMousePositionAndAnchorsWithTranslation:CGPointMake(2, -1) - scale:1]; - - webrtc::DesktopVector newPosition = scene_.mousePosition; - - // We could do these checks as a single test, for a positive vs negative - // difference. But this style has a clearer meaning that the position moved - // toward or away from the origin. - EXPECT_LT(newPosition.x(), originalPosition.x()); - EXPECT_LT(newPosition.y(), originalPosition.y()); - EXPECT_EQ(2, abs(originalPosition.x() - newPosition.x())); - EXPECT_EQ(1, abs(originalPosition.y() - newPosition.y())); - - EXPECT_TRUE(scene_.anchored.left); - EXPECT_TRUE(scene_.anchored.top); - - EXPECT_FALSE(scene_.anchored.right); - EXPECT_FALSE(scene_.anchored.bottom); - - EXPECT_TRUE(scene_.tickPanVelocity); - - // move much further than the bounds allow - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ - updateMousePositionAndAnchorsWithTranslation:CGPointMake(10000, -10000) - scale:1]; - - newPosition = scene_.mousePosition; - - EXPECT_EQ(0, newPosition.x()); - EXPECT_EQ(0, newPosition.y()); - - EXPECT_TRUE(scene_.anchored.left); - EXPECT_TRUE(scene_.anchored.top); - - EXPECT_FALSE(scene_.anchored.right); - EXPECT_FALSE(scene_.anchored.bottom); - - EXPECT_FALSE(scene_.tickPanVelocity); -} - -TEST_F(SceneViewTest, - UpdateMousePositionAndAnchorsWithTranslationTowardLeftAndBottom) { - webrtc::DesktopVector originalPosition = scene_.mousePosition; - - // see notes for Test - // UpdateMousePositionAndAnchorsWithTranslationTowardLeftAndTop - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ updateMousePositionAndAnchorsWithTranslation:CGPointMake(2, 1) - scale:1]; - webrtc::DesktopVector newPosition = scene_.mousePosition; - - EXPECT_LT(newPosition.x(), originalPosition.x()); - EXPECT_GT(newPosition.y(), originalPosition.y()); - EXPECT_EQ(2, abs(originalPosition.x() - newPosition.x())); - EXPECT_EQ(1, abs(originalPosition.y() - newPosition.y())); - - EXPECT_TRUE(scene_.anchored.left); - EXPECT_TRUE(scene_.anchored.bottom); - - EXPECT_FALSE(scene_.anchored.right); - EXPECT_FALSE(scene_.anchored.top); - - EXPECT_TRUE(scene_.tickPanVelocity); - - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ updateMousePositionAndAnchorsWithTranslation:CGPointMake(10000, 10000) - scale:1]; - newPosition = scene_.mousePosition; - - EXPECT_EQ(0, newPosition.x()); - EXPECT_EQ(scene_.frameSize.height() - 1, newPosition.y()); - - EXPECT_TRUE(scene_.anchored.left); - EXPECT_TRUE(scene_.anchored.bottom); - - EXPECT_FALSE(scene_.anchored.right); - EXPECT_FALSE(scene_.anchored.top); - - EXPECT_FALSE(scene_.tickPanVelocity); -} - -TEST_F(SceneViewTest, - UpdateMousePositionAndAnchorsWithTranslationTowardRightAndTop) { - webrtc::DesktopVector originalPosition = scene_.mousePosition; - - // see notes for Test - // UpdateMousePositionAndAnchorsWithTranslationTowardLeftAndTop - - // When moving to the right the mouse remains centered since the horizontal - // display space is larger than the view space - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ updateMousePositionAndAnchorsWithTranslation:CGPointMake(-2, -1) - scale:1]; - webrtc::DesktopVector newPosition = scene_.mousePosition; - - EXPECT_LT(newPosition.y(), originalPosition.y()); - EXPECT_EQ(0, abs(originalPosition.x() - newPosition.x())); - EXPECT_EQ(1, abs(originalPosition.y() - newPosition.y())); - - EXPECT_TRUE(scene_.anchored.top); - - EXPECT_FALSE(scene_.anchored.left); - EXPECT_FALSE(scene_.anchored.right); - EXPECT_FALSE(scene_.anchored.bottom); - - EXPECT_TRUE(scene_.tickPanVelocity); - - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ - updateMousePositionAndAnchorsWithTranslation:CGPointMake(-10000, -10000) - scale:1]; - newPosition = scene_.mousePosition; - - EXPECT_EQ(scene_.frameSize.width() - 1, newPosition.x()); - EXPECT_EQ(0, newPosition.y()); - - EXPECT_TRUE(scene_.anchored.right); - EXPECT_TRUE(scene_.anchored.top); - - EXPECT_FALSE(scene_.anchored.left); - EXPECT_FALSE(scene_.anchored.bottom); - - EXPECT_FALSE(scene_.tickPanVelocity); -} - -TEST_F(SceneViewTest, - UpdateMousePositionAndAnchorsWithTranslationTowardRightAndBottom) { - webrtc::DesktopVector originalPosition = scene_.mousePosition; - - // see notes for Test - // UpdateMousePositionAndAnchorsWithTranslationTowardLeftAndTop - - // When moving to the right the mouse remains centered since the horizontal - // display space is larger than the view space - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ updateMousePositionAndAnchorsWithTranslation:CGPointMake(-2, 1) - scale:1]; - webrtc::DesktopVector newPosition = scene_.mousePosition; - - EXPECT_GT(newPosition.y(), originalPosition.y()); - EXPECT_EQ(0, abs(originalPosition.x() - newPosition.x())); - EXPECT_EQ(1, abs(originalPosition.y() - newPosition.y())); - - EXPECT_TRUE(scene_.anchored.bottom); - - EXPECT_FALSE(scene_.anchored.left); - EXPECT_FALSE(scene_.anchored.right); - EXPECT_FALSE(scene_.anchored.top); - - EXPECT_TRUE(scene_.tickPanVelocity); - - [scene_ setPanVelocity:CGPointMake(1, 1)]; - [scene_ - updateMousePositionAndAnchorsWithTranslation:CGPointMake(-10000, 10000) - scale:1]; - newPosition = scene_.mousePosition; - - EXPECT_EQ(scene_.frameSize.width() - 1, newPosition.x()); - EXPECT_EQ(scene_.frameSize.height() - 1, newPosition.y()); - - EXPECT_TRUE(scene_.anchored.right); - EXPECT_TRUE(scene_.anchored.bottom); - - EXPECT_FALSE(scene_.anchored.left); - EXPECT_FALSE(scene_.anchored.top); - - EXPECT_FALSE(scene_.tickPanVelocity); -} - -TEST(SceneViewTest_Static, PositionDeltaFromScaling) { - - // Legend: - // * anchored point or end point - // | unanchored endpoint - // - onscreen - // # offscreen - - // *---| - // *-------| - EXPECT_EQ( - 0, - [SceneView positionDeltaFromScaling:2.0F position:0 length:100 anchor:0]); - // *---| - // *-| - EXPECT_EQ( - 0, - [SceneView positionDeltaFromScaling:0.5F position:0 length:100 anchor:0]); - // |---* - // |-------* - EXPECT_EQ(100, - [SceneView positionDeltaFromScaling:2.0F - position:0 - length:100 - anchor:100]); - // |----* - // |--* - EXPECT_EQ(-50, - [SceneView positionDeltaFromScaling:0.5F - position:0 - length:100 - anchor:100]); - // |*---| - // |-*-------| - EXPECT_EQ(25, - [SceneView positionDeltaFromScaling:2.0F - position:0 - length:100 - anchor:25]); - // |-*--| - // |*-| - EXPECT_EQ(-12.5, - [SceneView positionDeltaFromScaling:0.5F - position:0 - length:100 - anchor:25]); - // |---*| - // |------*-| - EXPECT_EQ(75, - [SceneView positionDeltaFromScaling:2.0F - position:0 - length:100 - anchor:75]); - // |--*-| - // |-*| - EXPECT_EQ(-37.5, - [SceneView positionDeltaFromScaling:0.5F - position:0 - length:100 - anchor:75]); - // |-*-| - // |---*---| - EXPECT_EQ(50, - [SceneView positionDeltaFromScaling:2.0F - position:0 - length:100 - anchor:50]); - // |--*--| - // |*| - EXPECT_EQ(-25, - [SceneView positionDeltaFromScaling:0.5F - position:0 - length:100 - anchor:50]); - ////////////////////////////////// - // Change position to 50, anchor is relatively the same - ////////////////////////////////// - EXPECT_EQ(0, - [SceneView positionDeltaFromScaling:2.0F - position:50 - length:100 - anchor:50]); - EXPECT_EQ(0, - [SceneView positionDeltaFromScaling:0.5F - position:50 - length:100 - anchor:50]); - EXPECT_EQ(100, - [SceneView positionDeltaFromScaling:2.0F - position:50 - length:100 - anchor:150]); - EXPECT_EQ(-50, - [SceneView positionDeltaFromScaling:0.5F - position:50 - length:100 - anchor:150]); - EXPECT_EQ(25, - [SceneView positionDeltaFromScaling:2.0F - position:50 - length:100 - anchor:75]); - EXPECT_EQ(-12.5, - [SceneView positionDeltaFromScaling:0.5F - position:50 - length:100 - anchor:75]); - EXPECT_EQ(75, - [SceneView positionDeltaFromScaling:2.0F - position:50 - length:100 - anchor:125]); - EXPECT_EQ(-37.5, - [SceneView positionDeltaFromScaling:0.5F - position:50 - length:100 - anchor:125]); - EXPECT_EQ(50, - [SceneView positionDeltaFromScaling:2.0F - position:50 - length:100 - anchor:100]); - EXPECT_EQ(-25, - [SceneView positionDeltaFromScaling:0.5F - position:50 - length:100 - anchor:100]); - - ////////////////////////////////// - // Change position to -50, length to 200, anchor is relatively the same - ////////////////////////////////// - EXPECT_EQ(0, - [SceneView positionDeltaFromScaling:2.0F - position:-50 - length:200 - anchor:-50]); - EXPECT_EQ(0, - [SceneView positionDeltaFromScaling:0.5F - position:-50 - length:200 - anchor:-50]); - EXPECT_EQ(200, - [SceneView positionDeltaFromScaling:2.0F - position:-50 - length:200 - anchor:150]); - EXPECT_EQ(-100, - [SceneView positionDeltaFromScaling:0.5F - position:-50 - length:200 - anchor:150]); - EXPECT_EQ(50, - [SceneView positionDeltaFromScaling:2.0F - position:-50 - length:200 - anchor:0]); - EXPECT_EQ(-25, - [SceneView positionDeltaFromScaling:0.5F - position:-50 - length:200 - anchor:0]); - EXPECT_EQ(150, - [SceneView positionDeltaFromScaling:2.0F - position:-50 - length:200 - anchor:100]); - EXPECT_EQ(-75, - [SceneView positionDeltaFromScaling:0.5F - position:-50 - length:200 - anchor:100]); - EXPECT_EQ(100, - [SceneView positionDeltaFromScaling:2.0F - position:-50 - length:200 - anchor:50]); - EXPECT_EQ(-50, - [SceneView positionDeltaFromScaling:0.5F - position:-50 - length:200 - anchor:50]); -} - -TEST(SceneViewTest_Static, PositionDeltaFromTranslation) { - // Anchored on both sides. Center it by using 1/2 the free space, offset by - // the current position - EXPECT_EQ(50, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(50, - [SceneView positionDeltaFromTranslation:100 - position:0 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(-50, - [SceneView positionDeltaFromTranslation:0 - position:100 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(50, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:100 - scaleingPositionDelta:100 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(100, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:200 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:YES]); - - // Anchored only on the left. Don't move it - EXPECT_EQ(0, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(0, - [SceneView positionDeltaFromTranslation:100 - position:0 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(0, - [SceneView positionDeltaFromTranslation:0 - position:100 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(0, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:200 - scaleingPositionDelta:100 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(0, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:200 - scaleingPositionDelta:0 - isAnchoredLow:YES - isAnchoredHigh:NO]); - // Anchored only on the right. Move by the scaling delta - EXPECT_EQ(25, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:100 - scaleingPositionDelta:25 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(50, - [SceneView positionDeltaFromTranslation:100 - position:0 - freeSpace:100 - scaleingPositionDelta:50 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(75, - [SceneView positionDeltaFromTranslation:0 - position:100 - freeSpace:100 - scaleingPositionDelta:75 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(100, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:100 - scaleingPositionDelta:100 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(125, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:200 - scaleingPositionDelta:125 - isAnchoredLow:NO - isAnchoredHigh:YES]); - // Not anchored, translate and move by the scaling delta - EXPECT_EQ(0, - [SceneView positionDeltaFromTranslation:0 - position:0 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(25, - [SceneView positionDeltaFromTranslation:25 - position:0 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(50, - [SceneView positionDeltaFromTranslation:50 - position:100 - freeSpace:100 - scaleingPositionDelta:0 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(175, - [SceneView positionDeltaFromTranslation:75 - position:0 - freeSpace:100 - scaleingPositionDelta:100 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(100, - [SceneView positionDeltaFromTranslation:100 - position:0 - freeSpace:200 - scaleingPositionDelta:0 - isAnchoredLow:NO - isAnchoredHigh:NO]); -} - -TEST(SceneViewTest_Static, BoundDeltaFromPosition) { - // Entire entity fits in our view, lower bound is not less than the - // upperBound. The delta is bounded to the lowerBound. - EXPECT_EQ(200, - [SceneView boundDeltaFromPosition:0 - delta:0 - lowerBound:200 - upperBound:100]); - EXPECT_EQ(100, - [SceneView boundDeltaFromPosition:100 - delta:0 - lowerBound:200 - upperBound:100]); - EXPECT_EQ(200, - [SceneView boundDeltaFromPosition:0 - delta:100 - lowerBound:200 - upperBound:100]); - EXPECT_EQ(150, - [SceneView boundDeltaFromPosition:50 - delta:100 - lowerBound:200 - upperBound:200]); - // Entity does not fit in our view. The result would be out of bounds on the - // high bound. The delta is bounded to the upper bound and the delta from the - // position is returned. - EXPECT_EQ(100, - [SceneView boundDeltaFromPosition:0 - delta:1000 - lowerBound:0 - upperBound:100]); - EXPECT_EQ(99, - [SceneView boundDeltaFromPosition:1 - delta:1000 - lowerBound:0 - upperBound:100]); - EXPECT_EQ(-50, - [SceneView boundDeltaFromPosition:150 - delta:1000 - lowerBound:50 - upperBound:100]); - EXPECT_EQ(100, - [SceneView boundDeltaFromPosition:100 - delta:1000 - lowerBound:0 - upperBound:200]); - // Entity does not fit in our view. The result would be out of bounds on the - // low bound. The delta is bounded to the lower bound and the delta from the - // position is returned. - EXPECT_EQ(0, - [SceneView boundDeltaFromPosition:0 - delta:-1000 - lowerBound:0 - upperBound:100]); - EXPECT_EQ(-20, - [SceneView boundDeltaFromPosition:20 - delta:-1000 - lowerBound:0 - upperBound:100]); - EXPECT_EQ(21, - [SceneView boundDeltaFromPosition:29 - delta:-1000 - lowerBound:50 - upperBound:100]); - EXPECT_EQ(1, - [SceneView boundDeltaFromPosition:-1 - delta:-1000 - lowerBound:0 - upperBound:200]); - // Entity does not fit in our view. The result is in bounds. The delta is - // returned unchanged. - EXPECT_EQ(50, - [SceneView boundDeltaFromPosition:0 - delta:50 - lowerBound:0 - upperBound:100]); - EXPECT_EQ(-10, - [SceneView boundDeltaFromPosition:20 - delta:-10 - lowerBound:0 - upperBound:100]); - EXPECT_EQ(31, - [SceneView boundDeltaFromPosition:29 - delta:31 - lowerBound:50 - upperBound:100]); - EXPECT_EQ(50, - [SceneView boundDeltaFromPosition:100 - delta:50 - lowerBound:0 - upperBound:200]); -} - -TEST(SceneViewTest_Static, BoundMouseGivenNextPosition) { - // Mouse would move off screen in the negative - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:-1 - maxPosition:50 - centerPosition:2 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:-1 - maxPosition:25 - centerPosition:99 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:-11 - maxPosition:0 - centerPosition:-52 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:-11 - maxPosition:-100 - centerPosition:44 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:-1 - maxPosition:50 - centerPosition:-20 - isAnchoredLow:YES - isAnchoredHigh:YES]); - - // Mouse would move off screen in the positive - EXPECT_EQ(49, - [SceneView boundMouseGivenNextPosition:50 - maxPosition:50 - centerPosition:2 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(24, - [SceneView boundMouseGivenNextPosition:26 - maxPosition:25 - centerPosition:99 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(-1, - [SceneView boundMouseGivenNextPosition:1 - maxPosition:0 - centerPosition:-52 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(-101, - [SceneView boundMouseGivenNextPosition:0 - maxPosition:-100 - centerPosition:44 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(49, - [SceneView boundMouseGivenNextPosition:60 - maxPosition:50 - centerPosition:-20 - isAnchoredLow:YES - isAnchoredHigh:YES]); - - // Mouse is not out of bounds, and not anchored. The Center is returned. - EXPECT_EQ(2, - [SceneView boundMouseGivenNextPosition:0 - maxPosition:100 - centerPosition:2 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(99, - [SceneView boundMouseGivenNextPosition:25 - maxPosition:100 - centerPosition:99 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(-52, - [SceneView boundMouseGivenNextPosition:99 - maxPosition:100 - centerPosition:-52 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(44, - [SceneView boundMouseGivenNextPosition:120 - maxPosition:200 - centerPosition:44 - isAnchoredLow:NO - isAnchoredHigh:NO]); - EXPECT_EQ(-20, - [SceneView boundMouseGivenNextPosition:180 - maxPosition:200 - centerPosition:-20 - isAnchoredLow:NO - isAnchoredHigh:NO]); - - // Mouse is not out of bounds, and anchored. The position closest - // to the anchor is returned. - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:0 - maxPosition:100 - centerPosition:2 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(25, - [SceneView boundMouseGivenNextPosition:25 - maxPosition:100 - centerPosition:99 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(-52, - [SceneView boundMouseGivenNextPosition:99 - maxPosition:100 - centerPosition:-52 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(44, - [SceneView boundMouseGivenNextPosition:120 - maxPosition:200 - centerPosition:44 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(-20, - [SceneView boundMouseGivenNextPosition:180 - maxPosition:200 - centerPosition:-20 - isAnchoredLow:YES - isAnchoredHigh:NO]); - EXPECT_EQ(2, - [SceneView boundMouseGivenNextPosition:0 - maxPosition:100 - centerPosition:2 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(99, - [SceneView boundMouseGivenNextPosition:25 - maxPosition:100 - centerPosition:99 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(99, - [SceneView boundMouseGivenNextPosition:99 - maxPosition:100 - centerPosition:-52 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(120, - [SceneView boundMouseGivenNextPosition:120 - maxPosition:200 - centerPosition:44 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(180, - [SceneView boundMouseGivenNextPosition:180 - maxPosition:200 - centerPosition:-20 - isAnchoredLow:NO - isAnchoredHigh:YES]); - EXPECT_EQ(0, - [SceneView boundMouseGivenNextPosition:0 - maxPosition:100 - centerPosition:2 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(25, - [SceneView boundMouseGivenNextPosition:25 - maxPosition:100 - centerPosition:99 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(99, - [SceneView boundMouseGivenNextPosition:99 - maxPosition:100 - centerPosition:-52 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(120, - [SceneView boundMouseGivenNextPosition:120 - maxPosition:200 - centerPosition:44 - isAnchoredLow:YES - isAnchoredHigh:YES]); - EXPECT_EQ(180, - [SceneView boundMouseGivenNextPosition:180 - maxPosition:200 - centerPosition:-20 - isAnchoredLow:YES - isAnchoredHigh:YES]); -} - -TEST(SceneViewTest_Static, BoundVelocity) { - // Outside bounds of the axis - EXPECT_EQ(0, [SceneView boundVelocity:5.0f axisLength:100 mousePosition:0]); - EXPECT_EQ(0, [SceneView boundVelocity:5.0f axisLength:100 mousePosition:99]); - EXPECT_EQ(0, [SceneView boundVelocity:5.0f axisLength:200 mousePosition:200]); - // Not outside bounds of the axis - EXPECT_EQ(5.0f, - [SceneView boundVelocity:5.0f axisLength:100 mousePosition:1]); - EXPECT_EQ(5.0f, - [SceneView boundVelocity:5.0f axisLength:100 mousePosition:98]); - EXPECT_EQ(5.0f, - [SceneView boundVelocity:5.0f axisLength:200 mousePosition:100]); -} - -TEST_F(SceneViewTest, TickPanVelocity) { - // We are in the large frame, which can pan left and right but not up and - // down. Start by resizing it to allow panning up and down. - - [scene_ panAndZoom:CGPointMake(0, 0) scaleBy:2.0f]; - - // Going up and right - [scene_ setPanVelocity:CGPointMake(1000, 1000)]; - [scene_ tickPanVelocity]; - - webrtc::DesktopVector pos = scene_.mousePosition; - int loopLimit = 0; - bool didMove = false; - bool inMotion = true; - - while (inMotion && loopLimit < 100) { - inMotion = [scene_ tickPanVelocity]; - if (inMotion) { - ASSERT_TRUE(pos.x() <= scene_.mousePosition.x()) << " after " << loopLimit - << " iterations."; - ASSERT_TRUE(pos.y() <= scene_.mousePosition.y()) << " after " << loopLimit - << " iterations."; - didMove = true; - } - pos = scene_.mousePosition; - loopLimit++; - } - - EXPECT_LT(1, loopLimit); - EXPECT_TRUE(!inMotion); - EXPECT_TRUE(didMove); - - // Going down and left - [scene_ setPanVelocity:CGPointMake(-1000, -1000)]; - [scene_ tickPanVelocity]; - - pos = scene_.mousePosition; - loopLimit = 0; - didMove = false; - inMotion = true; - - while (inMotion && loopLimit < 100) { - inMotion = [scene_ tickPanVelocity]; - if (inMotion) { - ASSERT_TRUE(pos.x() >= scene_.mousePosition.x()) << " after " << loopLimit - << " iterations."; - ASSERT_TRUE(pos.y() >= scene_.mousePosition.y()) << " after " << loopLimit - << " iterations."; - didMove = true; - } - pos = scene_.mousePosition; - loopLimit++; - } - - EXPECT_LT(1, loopLimit); - EXPECT_TRUE(!inMotion); - EXPECT_TRUE(didMove); -} - -} // namespace remoting \ No newline at end of file diff --git a/remoting/ios/utility.h b/remoting/ios/utility.h deleted file mode 100644 index ac6a2a43e2422..0000000000000 --- a/remoting/ios/utility.h +++ /dev/null @@ -1,64 +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 REMOTING_IOS_UTILITY_H_ -#define REMOTING_IOS_UTILITY_H_ - -#import - -#include "base/memory/scoped_ptr.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" -#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" - -#import "remoting/ios/bridge/host_proxy.h" - -typedef struct { - scoped_ptr image; - scoped_ptr offset; -} GLRegion; - -@interface Utility : NSObject - -+ (BOOL)isPad; - -+ (BOOL)isInLandscapeMode; - -// Return the resolution in respect to orientation -+ (CGSize)getOrientatedSize:(CGSize)size - shouldWidthBeLongestSide:(BOOL)shouldWidthBeLongestSide; - -+ (void)showAlert:(NSString*)title message:(NSString*)message; - -+ (NSString*)appVersionNumberDisplayString; - -// GL Binding Context requires some specific flags for the type of textures -// being drawn -+ (void)bindTextureForIOS:(GLuint)glName; - -// Sometimes its necessary to read gl errors. This is called in various places -// while working in the GL Context -+ (void)logGLErrorCode:(NSString*)funcName; - -+ (void)drawSubRectToGLFromRectOfSize:(const webrtc::DesktopSize&)rectSize - subRect:(const webrtc::DesktopRect&)subRect - data:(const uint8_t*)data; - -+ (void)moveMouse:(HostProxy*)controller at:(const webrtc::DesktopVector&)point; - -+ (void)leftClickOn:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point; - -+ (void)middleClickOn:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point; - -+ (void)rightClickOn:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point; - -+ (void)mouseScroll:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point - delta:(const webrtc::DesktopVector&)delta; - -@end - -#endif // REMOTING_IOS_UTILITY_H_ diff --git a/remoting/ios/utility.mm b/remoting/ios/utility.mm deleted file mode 100644 index 212299783b701..0000000000000 --- a/remoting/ios/utility.mm +++ /dev/null @@ -1,150 +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. - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "Utility.h" - -@implementation Utility - -+ (BOOL)isPad { - return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); -} - -+ (BOOL)isInLandscapeMode { - UIInterfaceOrientation orientation = - [UIApplication sharedApplication].statusBarOrientation; - - if ((orientation == UIInterfaceOrientationLandscapeLeft) || - (orientation == UIInterfaceOrientationLandscapeRight)) { - return YES; - } - return NO; -} - -+ (CGSize)getOrientatedSize:(CGSize)size - shouldWidthBeLongestSide:(BOOL)shouldWidthBeLongestSide { - if (shouldWidthBeLongestSide && (size.height > size.width)) { - return CGSizeMake(size.height, size.width); - } - return size; -} - -+ (void)showAlert:(NSString*)title message:(NSString*)message { - UIAlertView* alert; - alert = [[UIAlertView alloc] init]; - alert.title = title; - alert.message = message; - alert.delegate = nil; - [alert addButtonWithTitle:@"OK"]; - [alert show]; -} - -+ (NSString*)appVersionNumberDisplayString { - NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary]; - - NSString* majorVersion = - [infoDictionary objectForKey:@"CFBundleShortVersionString"]; - NSString* minorVersion = [infoDictionary objectForKey:@"CFBundleVersion"]; - - return [NSString - stringWithFormat:@"Version %@ (%@)", majorVersion, minorVersion]; -} - -+ (void)bindTextureForIOS:(GLuint)glName { - glBindTexture(GL_TEXTURE_2D, glName); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - -+ (void)logGLErrorCode:(NSString*)funcName { - GLenum errorCode = 1; - - while (errorCode != 0) { - errorCode = glGetError(); // I don't know why this is returning an error - // on the first call to this function, but if I - // don't read it, then stuff doesn't work... -#if DEBUG - if (errorCode != 0) { - NSLog(@"glerror in %@: %X", funcName, errorCode); - } -#endif // DEBUG - } -} - -+ (void)drawSubRectToGLFromRectOfSize:(const webrtc::DesktopSize&)rectSize - subRect:(const webrtc::DesktopRect&)subRect - data:(const uint8_t*)data { - DCHECK(rectSize.width() >= subRect.width()); - DCHECK(rectSize.height() >= subRect.height()); - DCHECK(rectSize.width() >= (subRect.left() + subRect.width())); - DCHECK(rectSize.height() >= (subRect.top() + subRect.height())); - DCHECK(data); - - glTexSubImage2D(GL_TEXTURE_2D, - 0, - subRect.left(), - subRect.top(), - subRect.width(), - subRect.height(), - GL_RGBA, - GL_UNSIGNED_BYTE, - data); -} - -+ (void)moveMouse:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point { - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:0 - buttonDown:NO]; -} - -+ (void)leftClickOn:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point { - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:1 - buttonDown:YES]; - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:1 - buttonDown:NO]; -} - -+ (void)middleClickOn:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point { - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:2 - buttonDown:YES]; - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:2 - buttonDown:NO]; -} - -+ (void)rightClickOn:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point { - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:3 - buttonDown:YES]; - [controller mouseAction:point - wheelDelta:webrtc::DesktopVector(0, 0) - whichButton:3 - buttonDown:NO]; -} - -+ (void)mouseScroll:(HostProxy*)controller - at:(const webrtc::DesktopVector&)point - delta:(const webrtc::DesktopVector&)delta { - [controller mouseAction:point wheelDelta:delta whichButton:0 buttonDown:NO]; -} - -@end \ No newline at end of file diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index f1979164b25d1..80f89b8615a9a 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -9,6 +9,9 @@ # 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, + 'variables': { 'conditions': [ # Enable the multi-process host on Windows by default. @@ -165,7 +168,7 @@ 'host/win/host_messages.mc.jinja2', 'host/win/version.rc.jinja2', 'resources/play_store_resources.cc', - 'webapp/background.js', + 'webapp/background/background.js', 'webapp/butter_bar.js', 'webapp/client_screen.js', 'webapp/error.js', diff --git a/remoting/remoting_android.gypi b/remoting/remoting_android.gypi index 6b2766ea9ba13..744b302ae197a 100644 --- a/remoting/remoting_android.gypi +++ b/remoting/remoting_android.gypi @@ -56,8 +56,16 @@ { 'target_name': 'remoting_apk_manifest', 'type': 'none', - 'sources': [ - 'android/java/AndroidManifest.xml.jinja2', + 'conditions': [ + ['enable_cast==1', { + 'sources': [ + 'android/cast/AndroidManifest.xml.jinja2' + ], + }, { # 'enable_cast != 1' + 'sources': [ + 'android/java/AndroidManifest.xml.jinja2', + ], + }], ], 'rules': [{ 'rule_name': 'generate_manifest', @@ -97,8 +105,23 @@ '../base/base.gyp:base_java', '../ui/android/ui_android.gyp:ui_java', 'remoting_android_resources', + '../third_party/android_tools/android_tools.gyp:android_support_v7_appcompat_javalib', + '../third_party/android_tools/android_tools.gyp:android_support_v7_mediarouter_javalib', + '../third_party/android_tools/android_tools.gyp:android_support_v13_javalib', ], 'includes': [ '../build/java.gypi' ], + 'conditions' : [ + ['enable_cast==1', { + 'variables': { + 'additional_src_dirs': [ + 'android/cast', + ], + }, + 'dependencies': [ + 'google_play_services_javalib', + ], + }], + ], }, { 'target_name': 'remoting_apk', @@ -132,7 +155,47 @@ }, 'includes': [ '../build/java_apk.gypi' ], }, # end of target 'remoting_test_apk' - ], # end of 'targets' + ], # end of 'targets' + 'conditions': [ + ['enable_cast==1', { + 'targets': [ + { + # This jar contains the Google Play services library without the + # resources needed for the library to work. See crbug.com/274697 or + # ../third_party/android_tools/android_tools.gyp for more info. + # This target will fail to build unless you have a local version + # of the Google Play services jar. + 'target_name': 'google_play_services_javalib_no_res', + 'type': 'none', + 'variables': { + 'jar_path': 'android/google-play-services_lib/libs/google-play-services.jar', + }, + 'includes': ['../build/java_prebuilt.gypi'], + + }, # end of target 'google_play_services_javalib_no_res' + { + # This target contains the Google Play services library with the + # resources needed. It will fail to build unless you have a local + # version of the Google Play services libary project. + # TODO(aiguha): Solve issue of needing to use local version. Also, + # watch crbug.com/274697. + 'target_name': 'google_play_services_javalib', + 'type': 'none', + 'variables': { + 'java_in_dir': 'android/google-play-services_lib/', + 'R_package': ['com.google.android.gms'], + 'R_package_relpath': ['com/google/android/gms'], + 'has_java_resources': 1, + 'res_v14_verify_only': 1, + }, + 'dependencies': [ + 'google_play_services_javalib_no_res', + ], + 'includes': ['../build/java.gypi'], + }, # end of target 'google_play_services_javalib' + ], # end of targets + }], + ], }], # 'OS=="android"' ['OS=="android"', { diff --git a/remoting/remoting_client.gypi b/remoting/remoting_client.gypi index d7d66f16b0580..3da83aef22b01 100644 --- a/remoting/remoting_client.gypi +++ b/remoting/remoting_client.gypi @@ -85,6 +85,22 @@ '--js', '<@(remoting_webapp_wcs_sandbox_html_js_files)', ], }, + { + 'action_name': 'Build Remoting Webapp background.html', + 'inputs': [ + 'webapp/build-html.py', + '<(remoting_webapp_template_background)', + ], + 'outputs': [ + '<(SHARED_INTERMEDIATE_DIR)/background.html', + ], + 'action': [ + 'python', 'webapp/build-html.py', + '<(SHARED_INTERMEDIATE_DIR)/background.html', + '<(remoting_webapp_template_background)', + '--js', '<@(remoting_webapp_background_js_files)', + ], + }, ], }, # end of target 'remoting_webapp_html' @@ -114,7 +130,6 @@ 'variables': { 'output_dir': '<(PRODUCT_DIR)/remoting/remoting.webapp.v2', 'zip_path': '<(PRODUCT_DIR)/remoting-webapp.v2.zip', - 'extra_files': [ 'webapp/background.js' ], }, 'conditions': [ ['disable_nacl==0 and disable_nacl_untrusted==0', { diff --git a/remoting/remoting_host.gypi b/remoting/remoting_host.gypi index 725286fd86c3f..2236fd314f6be 100644 --- a/remoting/remoting_host.gypi +++ b/remoting/remoting_host.gypi @@ -170,6 +170,8 @@ 'host/ipc_host_event_logger.h', 'host/ipc_input_injector.cc', 'host/ipc_input_injector.h', + 'host/ipc_mouse_cursor_monitor.cc', + 'host/ipc_mouse_cursor_monitor.h', 'host/ipc_screen_controls.cc', 'host/ipc_screen_controls.h', 'host/ipc_util.h', @@ -234,10 +236,16 @@ 'host/server_log_entry_host.h', 'host/session_manager_factory.cc', 'host/session_manager_factory.h', - 'host/shaped_screen_capturer.cc', - 'host/shaped_screen_capturer.h', + 'host/shaped_desktop_capturer.cc', + 'host/shaped_desktop_capturer.h', 'host/signaling_connector.cc', 'host/signaling_connector.h', + 'host/single_window_desktop_environment.cc', + 'host/single_window_desktop_environment.h', + 'host/single_window_input_injector.h', + 'host/single_window_input_injector_linux.cc', + 'host/single_window_input_injector_mac.cc', + 'host/single_window_input_injector_win.cc', 'host/token_validator_base.cc', 'host/token_validator_base.h', 'host/token_validator_factory_impl.cc', @@ -360,6 +368,10 @@ '../third_party/webrtc/modules/modules.gyp:desktop_capture', '../third_party/libjingle/libjingle.gyp:libpeerconnection', ], + 'sources': [ + 'host/cast_video_capturer_adapter.cc', + 'host/cast_video_capturer_adapter.h' + ], }], ], }, # end of target 'remoting_host' diff --git a/remoting/remoting_nacl.gyp b/remoting/remoting_nacl.gyp index 0dd96b18256a5..b9000ba1e90cd 100644 --- a/remoting/remoting_nacl.gyp +++ b/remoting/remoting_nacl.gyp @@ -146,8 +146,6 @@ 'build_glibc': 0, 'build_newlib': 0, 'build_pnacl_newlib': 1, - 'enable_x86_32': 0, - 'enable_x86_64': 0, 'extra_deps_pnacl_newlib': [ '>(tc_lib_dir_pnacl_newlib)/libbase_i18n_nacl.a', '>(tc_lib_dir_pnacl_newlib)/libbase_nacl.a', diff --git a/remoting/remoting_srcs.gypi b/remoting/remoting_srcs.gypi index 5790eca1e9757..9ff6551002fd8 100644 --- a/remoting/remoting_srcs.gypi +++ b/remoting/remoting_srcs.gypi @@ -238,6 +238,8 @@ 'client/plugin/normalizing_input_filter_cros.h', 'client/plugin/normalizing_input_filter_mac.cc', 'client/plugin/normalizing_input_filter_mac.h', + 'client/plugin/pepper_address_resolver.cc', + 'client/plugin/pepper_address_resolver.h', 'client/plugin/pepper_audio_player.cc', 'client/plugin/pepper_audio_player.h', 'client/plugin/pepper_input_handler.cc', diff --git a/remoting/remoting_test.gypi b/remoting/remoting_test.gypi index c365d6a942ba7..6227b3a497657 100644 --- a/remoting/remoting_test.gypi +++ b/remoting/remoting_test.gypi @@ -19,11 +19,13 @@ 'remoting_resources', ], 'sources': [ + 'host/fake_desktop_capturer.cc', + 'host/fake_desktop_capturer.h', 'host/fake_desktop_environment.cc', 'host/fake_desktop_environment.h', 'host/fake_host_status_monitor.h', - 'host/fake_screen_capturer.cc', - 'host/fake_screen_capturer.h', + 'host/fake_mouse_cursor_monitor.cc', + 'host/fake_mouse_cursor_monitor.h', 'host/policy_hack/fake_policy_watcher.cc', 'host/policy_hack/fake_policy_watcher.h', 'host/policy_hack/mock_policy_callback.cc', @@ -125,11 +127,11 @@ 'host/daemon_process_unittest.cc', 'host/desktop_process_unittest.cc', 'host/desktop_shape_tracker_unittest.cc', + 'host/fake_desktop_capturer.cc', + 'host/fake_desktop_capturer.h', 'host/fake_host_extension.cc', 'host/fake_host_extension.h', 'host/fake_host_status_monitor.h', - 'host/fake_screen_capturer.cc', - 'host/fake_screen_capturer.h', 'host/gnubby_auth_handler_posix_unittest.cc', 'host/heartbeat_sender_unittest.cc', 'host/host_change_notification_listener_unittest.cc', @@ -160,7 +162,7 @@ 'host/setup/me2me_native_messaging_host_unittest.cc', 'host/setup/oauth_helper_unittest.cc', 'host/setup/pin_validator_unittest.cc', - 'host/shaped_screen_capturer_unittest.cc', + 'host/shaped_desktop_capturer_unittest.cc', 'host/token_validator_factory_impl_unittest.cc', 'host/video_frame_recorder_unittest.cc', 'host/video_scheduler_unittest.cc', @@ -295,6 +297,7 @@ 'webapp_js_files': [ '<@(remoting_webapp_main_html_js_files)', '<@(remoting_webapp_js_wcs_sandbox_files)', + '<@(remoting_webapp_background_js_files)', ] }, 'copies': [ @@ -324,7 +327,7 @@ 'destination': '<(output_dir)', 'files': [ '<@(webapp_js_files)', - '<@(remoting_webapp_unittest_cases)', + '<@(remoting_webapp_unittest_js_files)', '<@(remoting_webapp_unittest_additional_files)' ], }, @@ -336,7 +339,7 @@ 'webapp/build-html.py', '<(remoting_webapp_unittest_template_main)', '<@(webapp_js_files)', - '<@(remoting_webapp_unittest_cases)' + '<@(remoting_webapp_unittest_js_files)' ], 'outputs': [ '<(output_dir)/unittest.html', @@ -350,7 +353,7 @@ # instrumentedjs flag or else GYP will ignore the files in the # exclude list. '--exclude-js', '<@(remoting_webapp_unittest_exclude_files)', - '--js', '<@(remoting_webapp_unittest_cases)', + '--js', '<@(remoting_webapp_unittest_js_files)', '--instrument-js', '<@(webapp_js_files)', ], }, diff --git a/remoting/remoting_webapp.gypi b/remoting/remoting_webapp.gypi index ea498b655a481..19692d1edd092 100644 --- a/remoting/remoting_webapp.gypi +++ b/remoting/remoting_webapp.gypi @@ -11,6 +11,7 @@ 'generated_html_files': [ '<(SHARED_INTERMEDIATE_DIR)/main.html', '<(SHARED_INTERMEDIATE_DIR)/wcs_sandbox.html', + '<(SHARED_INTERMEDIATE_DIR)/background.html', ], }, 'dependencies': [ diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi index 070f89900c26b..aaa59f84d9bc8 100644 --- a/remoting/remoting_webapp_files.gypi +++ b/remoting/remoting_webapp_files.gypi @@ -46,6 +46,7 @@ 'webapp/client_screen.js', 'webapp/client_session.js', 'webapp/clipboard.js', + 'webapp/hangout_session.js', 'webapp/media_source_renderer.js', 'webapp/session_connector.js', 'webapp/smart_reconnector.js', @@ -96,6 +97,7 @@ 'webapp/host_screen.js', 'webapp/host_setup_dialog.js', 'webapp/host_install_dialog.js', + 'webapp/host_installer.js', 'webapp/paired_client_manager.js', ], # UI files for displaying (in the client) info about available hosts. @@ -130,16 +132,22 @@ ], # These product files are excluded from our JavaScript unittest 'remoting_webapp_unittest_exclude_files': [ + # background.js is where the onLoad handler is defined, which + # makes it the entry point of the background page. + 'webapp/background/background.js', # event_handlers.js is where the onLoad handler is defined, which # makes it the entry point of the webapp. 'webapp/event_handlers.js', ], # The unit test cases for the webapp - 'remoting_webapp_unittest_cases': [ + 'remoting_webapp_unittest_js_files': [ 'webapp/js_proto/chrome_proto.js', + 'webapp/unittests/chrome_mocks.js', 'webapp/unittests/base_unittest.js', 'webapp/unittests/l10n_unittest.js', 'webapp/unittests/menu_button_unittest.js', + 'webapp/unittests/it2me_helper_channel_unittest.js', + 'webapp/unittests/it2me_service_unittest.js' ], 'remoting_webapp_unittest_additional_files': [ 'webapp/menu_button.css', @@ -164,7 +172,18 @@ '<@(remoting_webapp_js_wcs_container_files)', # Uncomment this line to include browser test files in the web app # to expedite debugging or local development. - '<@(remoting_webapp_js_browser_test_files)' + # '<@(remoting_webapp_js_browser_test_files)' + ], + + # The JavaScript files that are used as background pages. + 'remoting_webapp_background_js_files': [ + 'webapp/base.js', + 'webapp/client_session.js', + 'webapp/typecheck.js', + 'webapp/background/app_launcher.js', + 'webapp/background/background.js', + 'webapp/background/it2me_helper_channel.js', + 'webapp/background/it2me_service.js', ], # The JavaScript files required by wcs_sandbox.html. @@ -178,6 +197,7 @@ 'remoting_webapp_all_js_files': [ # JS files for main.html. '<@(remoting_webapp_main_html_js_files)', + '<@(remoting_webapp_background_js_files)', # JS files for wcs_sandbox.html. # Use r_w_js_wcs_sandbox_files instead of r_w_wcs_sandbox_html_js_files # so that we don't double include error.js and plugin_settings.js. @@ -230,6 +250,9 @@ 'remoting_webapp_template_wcs_sandbox': 'webapp/html/template_wcs_sandbox.html', + 'remoting_webapp_template_background': + 'webapp/html/template_background.html', + 'remoting_webapp_template_files': [ 'webapp/html/butterbar.html', 'webapp/html/client_plugin.html', diff --git a/remoting/resources/remoting_strings.grd b/remoting/resources/remoting_strings.grd index cd3c7d14703db..0af6d943b32c2 100644 --- a/remoting/resources/remoting_strings.grd +++ b/remoting/resources/remoting_strings.grd @@ -424,6 +424,15 @@ Host is offline. + + Failed to connect to Cast device. + + + Closed connection to Cast device. + + + Cast + | | + * |------connect message-------->| | + * | |-------appLauncher.launch()---->| + * | |<------runtime.connect()------- | + * | |<-----sessionStateChanged------ | + * |<----sessionStateChanged------| | + * + * Disconnection can be initiated from either side: + * 1. In the normal flow initiated from hangout + * Hangout It2MeHelperChannel Chrome Remote Desktop + * |-----disconnect message------>| | + * |<-sessionStateChanged(CLOSED)-| | + * | |-----appLauncher.close()------>| + * + * 2. In the normal flow initiated from webapp + * Hangout It2MeHelperChannel Chrome Remote Desktop + * | |<-sessionStateChanged(CLOSED)--| + * | |<--------port.disconnect()-----| + * |<--------port.disconnect()----| | + * + * 2. If hangout crashes + * Hangout It2MeHelperChannel Chrome Remote Desktop + * |---------port.disconnect()--->| | + * | |--------port.disconnect()----->| + * | |------appLauncher.close()----->| + * + * 3. If webapp crashes + * Hangout It2MeHelperChannel Chrome Remote Desktop + * | |<-------port.disconnect()------| + * |<-sessionStateChanged(FAILED)-| | + * |<--------port.disconnect()----| | + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @param {remoting.AppLauncher} appLauncher + * @param {chrome.runtime.Port} hangoutPort Represents an active connection to + * Hangouts. + * @param {function(remoting.It2MeHelperChannel)} onDisconnectCallback Callback + * to notify when the connection is torn down. IT2MeService uses this + * callback to dispose of the channel object. + * @constructor + */ +remoting.It2MeHelperChannel = + function(appLauncher, hangoutPort, onDisconnectCallback) { + + /** + * @type {remoting.AppLauncher} + * @private + */ + this.appLauncher_ = appLauncher; + + /** + * @type {chrome.runtime.Port} + * @private + */ + this.hangoutPort_ = hangoutPort; + + /** + * @type {chrome.runtime.Port} + * @private + */ + this.webappPort_ = null; + + /** + * @type {string} + * @private + */ + this.instanceId_ = ''; + + /** + * @type {remoting.ClientSession.State} + * @private + */ + this.sessionState_ = remoting.ClientSession.State.CONNECTING; + + /** + * @type {?function(remoting.It2MeHelperChannel)} + * @private + */ + this.onDisconnectCallback_ = onDisconnectCallback; + + this.onWebappMessageRef_ = this.onWebappMessage_.bind(this); + this.onWebappDisconnectRef_ = this.onWebappDisconnect_.bind(this); + this.onHangoutMessageRef_ = this.onHangoutMessage_.bind(this); + this.onHangoutDisconnectRef_ = this.onHangoutDisconnect_.bind(this); +}; + +/** @enum {string} */ +remoting.It2MeHelperChannel.HangoutMessageTypes = { + CONNECT: 'connect', + DISCONNECT: 'disconnect' +}; + +/** @enum {string} */ +remoting.It2MeHelperChannel.WebappMessageTypes = { + SESSION_STATE_CHANGED: 'sessionStateChanged' +}; + +remoting.It2MeHelperChannel.prototype.init = function() { + this.hangoutPort_.onMessage.addListener(this.onHangoutMessageRef_); + this.hangoutPort_.onDisconnect.addListener(this.onHangoutDisconnectRef_); +}; + +/** @return {string} */ +remoting.It2MeHelperChannel.prototype.instanceId = function() { + return this.instanceId_; +}; + +/** + * @param {{method:string, data:Object.}} message + * @return {boolean} whether the message is handled or not. + * @private + */ +remoting.It2MeHelperChannel.prototype.onHangoutMessage_ = function(message) { + try { + var MessageTypes = remoting.It2MeHelperChannel.HangoutMessageTypes; + switch (message.method) { + case MessageTypes.CONNECT: + this.launchWebapp_(message); + return true; + case MessageTypes.DISCONNECT: + this.closeWebapp_(message); + return true; + } + } catch(e) { + var error = /** @type {Error} */ e; + console.error(error); + this.hangoutPort_.postMessage({ + method: message.method + 'Response', + error: error.message + }); + } + return false; +}; + +/** + * Disconnect the existing connection to the helpee. + * + * @param {{method:string, data:Object.}} message + * @private + */ +remoting.It2MeHelperChannel.prototype.closeWebapp_ = + function(message) { + // TODO(kelvinp): Closing the v2 app currently doesn't disconnect the IT2me + // session (crbug.com/402137), so send an explicit notification to Hangouts. + this.sessionState_ = remoting.ClientSession.State.CLOSED; + this.hangoutPort_.postMessage({ + method: 'sessionStateChanged', + state: this.sessionState_ + }); + this.appLauncher_.close(this.instanceId_); +}; + +/** + * Launches the web app. + * + * @param {{method:string, data:Object.}} message + * @private + */ +remoting.It2MeHelperChannel.prototype.launchWebapp_ = + function(message) { + var accessCode = getStringAttr(message, 'accessCode'); + if (!accessCode) { + throw new Error('Access code is missing'); + } + + // Launch the webapp. + this.appLauncher_.launch({ + mode: 'hangout', + accessCode: accessCode + }).then( + /** + * @this {remoting.It2MeHelperChannel} + * @param {string} instanceId + */ + function(instanceId){ + this.instanceId_ = instanceId; + }.bind(this)); +}; + +/** + * @private + */ +remoting.It2MeHelperChannel.prototype.onHangoutDisconnect_ = function() { + this.appLauncher_.close(this.instanceId_); + this.unhookPorts_(); +}; + +/** + * @param {chrome.runtime.Port} port The port represents a connection to the + * webapp. + * @param {string} id The id of the tab or window that is hosting the webapp. + */ +remoting.It2MeHelperChannel.prototype.onWebappConnect = function(port, id) { + base.debug.assert(id === this.instanceId_); + base.debug.assert(this.hangoutPort_ !== null); + + // Hook listeners. + port.onMessage.addListener(this.onWebappMessageRef_); + port.onDisconnect.addListener(this.onWebappDisconnectRef_); + this.webappPort_ = port; +}; + +/** @param {chrome.runtime.Port} port The webapp port. */ +remoting.It2MeHelperChannel.prototype.onWebappDisconnect_ = function(port) { + // If the webapp port got disconnected while the session is still connected, + // treat it as an error. + var States = remoting.ClientSession.State; + if (this.sessionState_ === States.CONNECTING || + this.sessionState_ === States.CONNECTED) { + this.sessionState_ = States.FAILED; + this.hangoutPort_.postMessage({ + method: 'sessionStateChanged', + state: this.sessionState_ + }); + } + this.unhookPorts_(); +}; + +/** + * @param {{method:string, data:Object.}} message + * @private + */ +remoting.It2MeHelperChannel.prototype.onWebappMessage_ = function(message) { + try { + console.log('It2MeHelperChannel id=' + this.instanceId_ + + ' incoming message method=' + message.method); + var MessageTypes = remoting.It2MeHelperChannel.WebappMessageTypes; + switch (message.method) { + case MessageTypes.SESSION_STATE_CHANGED: + var state = getNumberAttr(message, 'state'); + this.sessionState_ = + /** @type {remoting.ClientSession.State} */ state; + this.hangoutPort_.postMessage(message); + return true; + } + throw new Error('Unknown message method=' + message.method); + } catch(e) { + var error = /** @type {Error} */ e; + console.error(error); + this.webappPort_.postMessage({ + method: message.method + 'Response', + error: error.message + }); + } + return false; +}; + +remoting.It2MeHelperChannel.prototype.unhookPorts_ = function() { + if (this.webappPort_) { + this.webappPort_.onMessage.removeListener(this.onWebappMessageRef_); + this.webappPort_.onDisconnect.removeListener(this.onWebappDisconnectRef_); + this.webappPort_.disconnect(); + this.webappPort_ = null; + } + + if (this.hangoutPort_) { + this.hangoutPort_.onMessage.removeListener(this.onHangoutMessageRef_); + this.hangoutPort_.onDisconnect.removeListener(this.onHangoutDisconnectRef_); + this.hangoutPort_.disconnect(); + this.hangoutPort_ = null; + } + + if (this.onDisconnectCallback_) { + this.onDisconnectCallback_(this); + this.onDisconnectCallback_ = null; + } +}; diff --git a/remoting/webapp/background/it2me_service.js b/remoting/webapp/background/it2me_service.js new file mode 100644 index 0000000000000..9656864577fa8 --- /dev/null +++ b/remoting/webapp/background/it2me_service.js @@ -0,0 +1,184 @@ +// 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. + +/** + * @fileoverview + * It2MeService listens to incoming connections requests from Hangouts + * and the webapp and creates a It2MeHelperChannel between them. + * It supports multiple helper sessions, but only a single helpee. + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @param {remoting.AppLauncher} appLauncher + * @constructor + */ +remoting.It2MeService = function(appLauncher) { + /** + * @type {remoting.AppLauncher} + * @private + */ + this.appLauncher_ = appLauncher; + + /** + * @type {Array.} + * @private + */ + this.helpers_ = []; + + /** @private */ + this.helpee_ = null; + + this.onWebappConnectRef_ = this.onWebappConnect_.bind(this); + this.onMessageExternalRef_ = this.onMessageExternal_.bind(this); + this.onConnectExternalRef_ = this.onConnectExternal_.bind(this); +}; + +/** @enum {string} */ +remoting.It2MeService.ConnectionTypes = { + HELPER_HANGOUT: 'it2me.helper.hangout', + HELPEE_HANGOUT: 'it2me.helpee.hangout', + HELPER_WEBAPP: 'it2me.helper.webapp' +}; + +/** + * Starts listening to external connection from Hangouts and the webapp. + */ +remoting.It2MeService.prototype.init = function() { + chrome.runtime.onConnect.addListener(this.onWebappConnectRef_); + chrome.runtime.onMessageExternal.addListener(this.onMessageExternalRef_); + chrome.runtime.onConnectExternal.addListener(this.onConnectExternalRef_); +}; + +remoting.It2MeService.prototype.dispose = function() { + chrome.runtime.onConnect.removeListener(this.onWebappConnectRef_); + chrome.runtime.onMessageExternal.removeListener( + this.onMessageExternalRef_); + chrome.runtime.onConnectExternal.removeListener( + this.onConnectExternalRef_); +}; + +/** + * This function is called when a runtime message is received from an external + * web page (hangout) or extension. + * + * @param {{method:string, data:Object.}} message + * @param {chrome.runtime.MessageSender} sender + * @param {function(*):void} sendResponse + * @private + */ +remoting.It2MeService.prototype.onMessageExternal_ = + function(message, sender, sendResponse) { + try { + var method = message.method; + if (method == 'hello') { + // The hello message is used by hangouts to detect whether the app is + // installed and what features are supported. + sendResponse({ + method: 'helloResponse', + supportedFeatures: ['it2me'] + }); + return true; + } + throw new Error('Unknown method: ' + method); + } catch (e) { + var error = /** @type {Error} */ e; + console.error(error); + sendResponse({ + method: message.method + 'Response', + error: error.message + }); + } + return false; +}; + +/** + * This function is called when Hangouts connects via chrome.runtime.connect. + * Only web pages that are white-listed in the manifest are allowed to connect. + * + * @param {chrome.runtime.Port} port + * @private + */ +remoting.It2MeService.prototype.onConnectExternal_ = function(port) { + var ConnectionTypes = remoting.It2MeService.ConnectionTypes; + try { + switch (port.name) { + case ConnectionTypes.HELPER_HANGOUT: + this.handleExternalHelperConnection_(port); + return true; + default: + throw new Error('Unsupported port - ' + port.name); + } + } catch (e) { + var error = /**@type {Error} */ e; + console.error(error); + port.disconnect(); + } + return false; +}; + +/** + * @param {chrome.runtime.Port} port + * @private + */ +remoting.It2MeService.prototype.onWebappConnect_ = function(port) { + try { + console.log('Incoming helper connection from webapp.'); + + // The senderId (tabId or windowId) of the webapp is embedded in the port + // name with the format port_name@senderId. + var parts = port.name.split('@'); + var portName = parts[0]; + var senderId = parts[1]; + var ConnectionTypes = remoting.It2MeService.ConnectionTypes; + if (portName === ConnectionTypes.HELPER_WEBAPP && senderId !== undefined) { + for (var i = 0; i < this.helpers_.length; i++) { + var helper = this.helpers_[i]; + if (helper.instanceId() === senderId) { + helper.onWebappConnect(port, senderId); + return; + } + } + } + throw new Error('No matching hangout connection found for ' + port.name); + } catch (e) { + var error = /** @type {Error} */ e; + console.error(error); + port.disconnect(); + } +}; + +/** + * @param {remoting.It2MeHelperChannel} helper + */ +remoting.It2MeService.prototype.onHelperChannelDisconnected = function(helper) { + for (var i = 0; i < this.helpers_.length; i++) { + if (helper === this.helpers_[i]) { + this.helpers_.splice(i, 1); + } + } +}; + +/** + * @param {chrome.runtime.Port} port + * @private + */ +remoting.It2MeService.prototype.handleExternalHelperConnection_ = + function(port) { + if (this.helpee_) { + console.error( + 'Cannot start a helper session while a helpee session is in process.'); + port.disconnect(); + } + + console.log('Incoming helper connection from Hangouts'); + var helper = new remoting.It2MeHelperChannel( + this.appLauncher_, port, this.onHelperChannelDisconnected.bind(this)); + helper.init(); + this.helpers_.push(helper); +}; diff --git a/remoting/webapp/base.js b/remoting/webapp/base.js index 4771e53714a1c..d12db235d23a6 100644 --- a/remoting/webapp/base.js +++ b/remoting/webapp/base.js @@ -112,6 +112,26 @@ base.values = function(dict) { }); }; + +/** + * Joins the |url| with optional query parameters defined in |opt_params| + * See unit test for usage. + * @param {string} url + * @param {Object.=} opt_params + * @return {string} + */ +base.urlJoin = function(url, opt_params) { + if (!opt_params) { + return url; + } + var queryParameters = []; + for (var key in opt_params) { + queryParameters.push(encodeURIComponent(key) + "=" + + encodeURIComponent(opt_params[key])); + } + return url + '?' + queryParameters.join('&'); +}; + base.Promise = function() {}; /** diff --git a/remoting/webapp/client_plugin.js b/remoting/webapp/client_plugin.js index 7b854a20698c7..4e95626af6549 100644 --- a/remoting/webapp/client_plugin.js +++ b/remoting/webapp/client_plugin.js @@ -17,14 +17,17 @@ var remoting = remoting || {}; /** - * @param {remoting.ViewerPlugin} plugin The plugin embed element. + * @param {Element} container The container for the embed element. * @param {function(string, string):boolean} onExtensionMessage The handler for * protocol extension messages. Returns true if a message is recognized; * false otherwise. * @constructor */ -remoting.ClientPlugin = function(plugin, onExtensionMessage) { - this.plugin = plugin; +remoting.ClientPlugin = function(container, onExtensionMessage) { + this.plugin_ = remoting.ClientPlugin.createPluginElement_(); + this.plugin_.id = 'session-client-plugin'; + container.appendChild(this.plugin_); + this.onExtensionMessage_ = onExtensionMessage; this.desktopWidth = 0; @@ -87,7 +90,7 @@ remoting.ClientPlugin = function(plugin, onExtensionMessage) { /** @type {remoting.ClientPlugin} */ var that = this; /** @param {Event} event Message event from the plugin. */ - this.plugin.addEventListener('message', function(event) { + this.plugin_.addEventListener('message', function(event) { that.handleMessage_(event.data); }, false); @@ -96,6 +99,45 @@ remoting.ClientPlugin = function(plugin, onExtensionMessage) { } }; +/** + * Creates plugin element without adding it to a container. + * + * @return {remoting.ViewerPlugin} Plugin element + */ +remoting.ClientPlugin.createPluginElement_ = function() { + var plugin = /** @type {remoting.ViewerPlugin} */ + document.createElement('embed'); + if (remoting.settings.CLIENT_PLUGIN_TYPE == 'pnacl') { + plugin.src = 'remoting_client_pnacl.nmf'; + plugin.type = 'application/x-pnacl'; + } else if (remoting.settings.CLIENT_PLUGIN_TYPE == 'nacl') { + plugin.src = 'remoting_client_nacl.nmf'; + plugin.type = 'application/x-nacl'; + } else { + plugin.src = 'about://none'; + plugin.type = 'application/vnd.chromium.remoting-viewer'; + } + plugin.width = 0; + plugin.height = 0; + plugin.tabIndex = 0; // Required, otherwise focus() doesn't work. + return plugin; +} + +/** + * Preloads the plugin to make instantiation faster when the user tries + * to connect. + */ +remoting.ClientPlugin.preload = function() { + if (remoting.settings.CLIENT_PLUGIN_TYPE != 'pnacl') { + return; + } + + var plugin = remoting.ClientPlugin.createPluginElement_(); + plugin.addEventListener( + 'loadend', function() { document.body.removeChild(plugin); }, false); + document.body.appendChild(plugin); +} + /** * Set of features for which hasFeature() can be used to test. * @@ -380,14 +422,17 @@ remoting.ClientPlugin.prototype.handleMessageMethod_ = function(message) { * Deletes the plugin. */ remoting.ClientPlugin.prototype.cleanup = function() { - this.plugin.parentNode.removeChild(this.plugin); + if (this.plugin_) { + this.plugin_.parentNode.removeChild(this.plugin_); + this.plugin_ = null; + } }; /** - * @return {HTMLEmbedElement} HTML element that correspods to the plugin. + * @return {HTMLEmbedElement} HTML element that corresponds to the plugin. */ remoting.ClientPlugin.prototype.element = function() { - return this.plugin; + return this.plugin_; }; /** @@ -438,8 +483,8 @@ remoting.ClientPlugin.prototype.isInjectKeyEventSupported = function() { * @param {string} iq Incoming IQ stanza. */ remoting.ClientPlugin.prototype.onIncomingIq = function(iq) { - if (this.plugin && this.plugin.postMessage) { - this.plugin.postMessage(JSON.stringify( + if (this.plugin_ && this.plugin_.postMessage) { + this.plugin_.postMessage(JSON.stringify( { method: 'incomingIq', data: { iq: iq } })); } else { // plugin.onIq may not be set after the plugin has been shut @@ -475,9 +520,9 @@ remoting.ClientPlugin.prototype.connect = function( } else if (remoting.platformIsChromeOS()) { keyFilter = 'cros'; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'delegateLargeCursors', data: {} })); - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'connect', data: { hostJid: hostJid, hostPublicKey: hostPublicKey, @@ -497,7 +542,7 @@ remoting.ClientPlugin.prototype.connect = function( * Release all currently pressed keys. */ remoting.ClientPlugin.prototype.releaseAllKeys = function() { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'releaseAllKeys', data: {} })); }; @@ -509,7 +554,7 @@ remoting.ClientPlugin.prototype.releaseAllKeys = function() { */ remoting.ClientPlugin.prototype.injectKeyEvent = function(usbKeycode, pressed) { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'injectKeyEvent', data: { 'usbKeycode': usbKeycode, 'pressed': pressed} @@ -524,7 +569,7 @@ remoting.ClientPlugin.prototype.injectKeyEvent = */ remoting.ClientPlugin.prototype.remapKey = function(fromKeycode, toKeycode) { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'remapKey', data: { 'fromKeycode': fromKeycode, 'toKeycode': toKeycode} @@ -538,7 +583,7 @@ remoting.ClientPlugin.prototype.remapKey = * @param {Boolean} trap True to enable trapping, False to disable. */ remoting.ClientPlugin.prototype.trapKey = function(keycode, trap) { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'trapKey', data: { 'keycode': keycode, 'trap': trap} @@ -564,7 +609,7 @@ remoting.ClientPlugin.prototype.sendClipboardItem = function(mimeType, item) { if (!this.hasFeature(remoting.ClientPlugin.Feature.SEND_CLIPBOARD_ITEM)) return; - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'sendClipboardItem', data: { mimeType: mimeType, item: item }})); }; @@ -580,7 +625,7 @@ remoting.ClientPlugin.prototype.notifyClientResolution = function(width, height, device_scale) { if (this.hasFeature(remoting.ClientPlugin.Feature.NOTIFY_CLIENT_RESOLUTION)) { var dpi = Math.floor(device_scale * 96); - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'notifyClientResolution', data: { width: Math.floor(width * device_scale), height: Math.floor(height * device_scale), @@ -596,10 +641,10 @@ remoting.ClientPlugin.prototype.notifyClientResolution = remoting.ClientPlugin.prototype.pauseVideo = function(pause) { if (this.hasFeature(remoting.ClientPlugin.Feature.VIDEO_CONTROL)) { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'videoControl', data: { pause: pause }})); } else if (this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_VIDEO)) { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'pauseVideo', data: { pause: pause }})); } }; @@ -614,7 +659,7 @@ remoting.ClientPlugin.prototype.pauseAudio = if (!this.hasFeature(remoting.ClientPlugin.Feature.PAUSE_AUDIO)) { return; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'pauseAudio', data: { pause: pause }})); }; @@ -628,7 +673,7 @@ remoting.ClientPlugin.prototype.setLosslessEncode = if (!this.hasFeature(remoting.ClientPlugin.Feature.VIDEO_CONTROL)) { return; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'videoControl', data: { losslessEncode: wantLossless }})); }; @@ -642,7 +687,7 @@ remoting.ClientPlugin.prototype.setLosslessColor = if (!this.hasFeature(remoting.ClientPlugin.Feature.VIDEO_CONTROL)) { return; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'videoControl', data: { losslessColor: wantLossless }})); }; @@ -656,7 +701,7 @@ remoting.ClientPlugin.prototype.onPinFetched = if (!this.hasFeature(remoting.ClientPlugin.Feature.ASYNC_PIN)) { return; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'onPinFetched', data: { pin: pin }})); }; @@ -668,7 +713,7 @@ remoting.ClientPlugin.prototype.useAsyncPinDialog = if (!this.hasFeature(remoting.ClientPlugin.Feature.ASYNC_PIN)) { return; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'useAsyncPinDialog', data: {} })); }; @@ -680,7 +725,7 @@ remoting.ClientPlugin.prototype.useAsyncPinDialog = */ remoting.ClientPlugin.prototype.onThirdPartyTokenFetched = function( token, sharedSecret) { - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'onThirdPartyTokenFetched', data: { token: token, sharedSecret: sharedSecret}})); }; @@ -698,7 +743,7 @@ remoting.ClientPlugin.prototype.requestPairing = return; } this.onPairingComplete_ = onDone; - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'requestPairing', data: { clientName: clientName } })); }; @@ -713,7 +758,7 @@ remoting.ClientPlugin.prototype.sendClientMessage = if (!this.hasFeature(remoting.ClientPlugin.Feature.EXTENSION_MESSAGE)) { return; } - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'extensionMessage', data: { type: type, data: message } })); @@ -730,7 +775,7 @@ remoting.ClientPlugin.prototype.enableMediaSourceRendering = return; } this.mediaSourceRenderer_ = mediaSourceRenderer; - this.plugin.postMessage(JSON.stringify( + this.plugin_.postMessage(JSON.stringify( { method: 'enableMediaSourceRendering', data: {} })); }; @@ -744,14 +789,14 @@ remoting.ClientPlugin.prototype.showPluginForClickToPlay_ = function() { if (!this.helloReceived_) { var width = 200; var height = 200; - this.plugin.style.width = width + 'px'; - this.plugin.style.height = height + 'px'; + this.plugin_.style.width = width + 'px'; + this.plugin_.style.height = height + 'px'; // Center the plugin just underneath the "Connnecting..." dialog. var dialog = document.getElementById('client-dialog'); var dialogRect = dialog.getBoundingClientRect(); - this.plugin.style.top = (dialogRect.bottom + 16) + 'px'; - this.plugin.style.left = (window.innerWidth - width) / 2 + 'px'; - this.plugin.style.position = 'fixed'; + this.plugin_.style.top = (dialogRect.bottom + 16) + 'px'; + this.plugin_.style.left = (window.innerWidth - width) / 2 + 'px'; + this.plugin_.style.position = 'fixed'; } }; @@ -760,9 +805,9 @@ remoting.ClientPlugin.prototype.showPluginForClickToPlay_ = function() { * @private */ remoting.ClientPlugin.prototype.hidePluginForClickToPlay_ = function() { - this.plugin.style.width = ''; - this.plugin.style.height = ''; - this.plugin.style.top = ''; - this.plugin.style.left = ''; - this.plugin.style.position = ''; + this.plugin_.style.width = ''; + this.plugin_.style.height = ''; + this.plugin_.style.top = ''; + this.plugin_.style.left = ''; + this.plugin_.style.position = ''; }; diff --git a/remoting/webapp/client_screen.js b/remoting/webapp/client_screen.js index cb54aa358610d..61270fef278a2 100644 --- a/remoting/webapp/client_screen.js +++ b/remoting/webapp/client_screen.js @@ -55,7 +55,7 @@ remoting.onVisibilityChanged = function() { remoting.clientSession.pauseVideo( ('hidden' in document) ? document.hidden : document.webkitHidden); } -} +}; /** * Disconnect the remoting client. @@ -89,6 +89,9 @@ function onClientStateChange_(state) { if (remoting.clientSession.getMode() == remoting.ClientSession.Mode.IT2ME) { remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME); + remoting.hangoutSessionEvents.raiseEvent( + remoting.hangoutSessionEvents.sessionStateChanged, + remoting.ClientSession.State.CLOSED); } else { remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME); } @@ -133,6 +136,10 @@ function showConnectError_(errorTag) { : remoting.connector.getConnectionMode(); if (mode == remoting.ClientSession.Mode.IT2ME) { remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_IT2ME); + remoting.hangoutSessionEvents.raiseEvent( + remoting.hangoutSessionEvents.sessionStateChanged, + remoting.ClientSession.State.FAILED + ); } else { remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME); } @@ -317,6 +324,10 @@ remoting.onConnected = function(clientSession) { remoting.toolbar.preview(); remoting.clipboard.startSession(); updateStatistics_(); + remoting.hangoutSessionEvents.raiseEvent( + remoting.hangoutSessionEvents.sessionStateChanged, + remoting.ClientSession.State.CONNECTED + ); if (remoting.connector.pairingRequested) { /** * @param {string} clientId diff --git a/remoting/webapp/client_session.js b/remoting/webapp/client_session.js index 3579b0d55327a..85610cdc03783 100644 --- a/remoting/webapp/client_session.js +++ b/remoting/webapp/client_session.js @@ -355,13 +355,6 @@ remoting.ClientSession.KEY_REMAP_KEYS = 'remapKeys'; remoting.ClientSession.KEY_RESIZE_TO_CLIENT = 'resizeToClient'; remoting.ClientSession.KEY_SHRINK_TO_FIT = 'shrinkToFit'; -/** - * The id of the client plugin - * - * @const - */ -remoting.ClientSession.prototype.PLUGIN_ID = 'session-client-plugin'; - /** * Set of capabilities for which hasCapability_() can be used to test. * @@ -397,39 +390,6 @@ remoting.ClientSession.prototype.hasCapability_ = function(capability) { return this.capabilities_.indexOf(capability) > -1; }; -/** - * @param {string} id Id to use for the plugin element . - * @param {function(string, string):boolean} onExtensionMessage The handler for - * protocol extension messages. Returns true if a message is recognized; - * false otherwise. - * @return {remoting.ClientPlugin} Create plugin object for the locally - * installed plugin. - */ -remoting.ClientSession.prototype.createClientPlugin_ = - function(id, onExtensionMessage) { - var plugin = /** @type {remoting.ViewerPlugin} */ - document.createElement('embed'); - - plugin.id = id; - if (remoting.settings.CLIENT_PLUGIN_TYPE == 'pnacl') { - plugin.src = 'remoting_client_pnacl.nmf'; - plugin.type = 'application/x-pnacl'; - } else if (remoting.settings.CLIENT_PLUGIN_TYPE == 'nacl') { - plugin.src = 'remoting_client_nacl.nmf'; - plugin.type = 'application/x-nacl'; - } else { - plugin.src = 'about://none'; - plugin.type = 'application/vnd.chromium.remoting-viewer'; - } - - plugin.width = 0; - plugin.height = 0; - plugin.tabIndex = 0; // Required, otherwise focus() doesn't work. - this.container_.querySelector('.client-plugin-container').appendChild(plugin); - - return new remoting.ClientPlugin(plugin, onExtensionMessage); -}; - /** * Callback function called when the plugin element gets focus. */ @@ -449,8 +409,7 @@ remoting.ClientSession.prototype.pluginLostFocus_ = function() { // Due to crbug.com/246335, we can't restore the focus immediately, // otherwise the plugin gets confused about whether or not it has focus. window.setTimeout( - this.plugin_.element().focus.bind(this.plugin_.element()), - 0); + this.plugin_.element().focus.bind(this.plugin_.element()), 0); } } }; @@ -464,7 +423,9 @@ remoting.ClientSession.prototype.pluginLostFocus_ = function() { */ remoting.ClientSession.prototype.createPluginAndConnect = function(onExtensionMessage) { - this.plugin_ = this.createClientPlugin_(this.PLUGIN_ID, onExtensionMessage); + this.plugin_ = new remoting.ClientPlugin( + this.container_.querySelector('.client-plugin-container'), + onExtensionMessage); remoting.HostSettings.load(this.hostId_, this.onHostSettingsLoaded_.bind(this)); }; @@ -515,7 +476,7 @@ remoting.ClientSession.prototype.setFocusHandlers_ = function() { */ remoting.ClientSession.prototype.resetWithError_ = function(error) { this.plugin_.cleanup(); - delete this.plugin_; + this.plugin_ = null; this.error_ = error; this.setState_(remoting.ClientSession.State.FAILED); } @@ -1369,9 +1330,6 @@ remoting.ClientSession.prototype.onShowOptionsMenu_ = function() { * @private */ remoting.ClientSession.prototype.scroll_ = function(dx, dy) { - var plugin = this.plugin_.element(); - var style = plugin.style; - /** * Helper function for x- and y-scrolling * @param {number|string} curr The current margin, eg. "10px". @@ -1391,6 +1349,9 @@ remoting.ClientSession.prototype.scroll_ = function(dx, dy) { return result + 'px'; }; + var plugin = this.plugin_.element(); + var style = this.container_.style; + var stopX = { stop: false }; var clientArea = this.getClientArea_(); style.marginLeft = adjustMargin(style.marginLeft, dx, clientArea.width, @@ -1404,11 +1365,8 @@ remoting.ClientSession.prototype.scroll_ = function(dx, dy) { }; remoting.ClientSession.prototype.resetScroll_ = function() { - if (this.plugin_) { - var plugin = this.plugin_.element(); - plugin.style.marginTop = '0px'; - plugin.style.marginLeft = '0px'; - } + this.container_.style.marginTop = '0px'; + this.container_.style.marginLeft = '0px'; }; /** @@ -1571,8 +1529,7 @@ remoting.ClientSession.prototype.updateMouseCursorImage_ = * @return {{top: number, left:number}} The top-left corner of the plugin. */ remoting.ClientSession.prototype.getPluginPositionForTesting = function() { - var plugin = this.plugin_.element(); - var style = plugin.style; + var style = this.container_.style; return { top: parseFloat(style.marginTop), left: parseFloat(style.marginLeft) diff --git a/remoting/webapp/hangout_session.js b/remoting/webapp/hangout_session.js new file mode 100644 index 0000000000000..bfcda8687b16c --- /dev/null +++ b/remoting/webapp/hangout_session.js @@ -0,0 +1,82 @@ +// 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. + +/** + * @fileoverview + * Class to communicate with the background scripts via chrome runtime + * messages to + * 1. Forward session state notifications + * 2. Closes the window when the session terminates + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @constructor + * @param {string} senderId id of the current tab or window. + */ +remoting.HangoutSession = function(senderId) { + /** + * @private + * @type {chrome.runtime.Port} + */ + this.port_ = null; + + /** + * @private + * @type {string} + */ + this.senderId_ = senderId; +}; + +remoting.HangoutSession.prototype.init = function() { + var portName = 'it2me.helper.webapp@' + this.senderId_; + this.port_ = chrome.runtime.connect({name: portName}); + + remoting.hangoutSessionEvents.addEventListener( + remoting.hangoutSessionEvents.sessionStateChanged, + this.onSessionStateChanged_.bind(this)); +}; + +/** + * @param {remoting.ClientSession.State} state + */ +remoting.HangoutSession.prototype.onSessionStateChanged_ = function(state) { + var State = remoting.ClientSession.State; + try { + this.port_.postMessage({method: 'sessionStateChanged', state: state}); + } catch (e) { + // postMessage will throw an exception if the port is disconnected. + // We can safely ignore this exception. + var error = /** @type {Error} */ e; + console.error(error); + } finally { + if (state === State.FAILED || state === State.CLOSED) { + // close the current window + if (remoting.isAppsV2) { + chrome.app.window.current().close(); + } else { + window.close(); + } + } + } +}; + + +/** + * remoting.clientSession does not exist until the session is connected. + * hangoutSessionEvents serves as a global event source to plumb session + * state changes until we cleanup clientSession and sessionConnector. + * @type {base.EventSource} + */ +remoting.hangoutSessionEvents = new base.EventSource(); + +/** @type {string} */ +remoting.hangoutSessionEvents.sessionStateChanged = "sessionStateChanged"; + +remoting.hangoutSessionEvents.defineEvents( + [remoting.hangoutSessionEvents.sessionStateChanged]); \ No newline at end of file diff --git a/remoting/webapp/host_daemon_facade.js b/remoting/webapp/host_daemon_facade.js index 5b1567aaece2a..abd9719a23a20 100644 --- a/remoting/webapp/host_daemon_facade.js +++ b/remoting/webapp/host_daemon_facade.js @@ -28,7 +28,7 @@ remoting.HostDaemonFacade = function() { */ this.pendingReplies_ = {}; - /** @type {?chrome.extension.Port} @private */ + /** @type {?chrome.runtime.Port} @private */ this.port_ = null; /** @type {string} @private */ diff --git a/remoting/webapp/host_install_dialog.js b/remoting/webapp/host_install_dialog.js index 37e5e6b7d49de..ab072e830eeca 100644 --- a/remoting/webapp/host_install_dialog.js +++ b/remoting/webapp/host_install_dialog.js @@ -28,22 +28,16 @@ remoting.HostInstallDialog = function() { this.cancelInstallButton_.disabled = false; /** @private*/ - this.onDoneHandler_ = function() {} + this.onDoneHandler_ = function() {}; /** @param {remoting.Error} error @private */ - this.onErrorHandler_ = function(error) {} -}; + this.onErrorHandler_ = function(error) {}; -/** @type {Object.} */ -remoting.HostInstallDialog.hostDownloadUrls = { - 'Win32' : 'http://dl.google.com/dl/edgedl/chrome-remote-desktop/' + - 'chromeremotedesktophost.msi', - 'MacIntel' : 'https://dl.google.com/chrome-remote-desktop/' + - 'chromeremotedesktop.dmg', - 'Linux x86_64' : 'https://dl.google.com/linux/direct/' + - 'chrome-remote-desktop_current_amd64.deb', - 'Linux i386' : 'https://dl.google.com/linux/direct/' + - 'chrome-remote-desktop_current_i386.deb' + /** + * @type {remoting.HostInstaller} + * @private + */ + this.hostInstaller_ = new remoting.HostInstaller(); }; /** @@ -63,28 +57,26 @@ remoting.HostInstallDialog.prototype.show = function(onDone, onError) { 'click', this.onCancelClickedHandler_, false); remoting.setMode(remoting.AppMode.HOST_INSTALL_PROMPT); - var hostPackageUrl = - remoting.HostInstallDialog.hostDownloadUrls[navigator.platform]; - if (hostPackageUrl === undefined) { - this.onErrorHandler_(remoting.Error.CANCELLED); - return; - } - - // Start downloading the package. - if (remoting.isAppsV2) { - // TODO(jamiewalch): Use chrome.downloads when it is available to - // apps v2 (http://crbug.com/174046) - window.open(hostPackageUrl); - } else { - window.location = hostPackageUrl; - } - /** @type {function():void} */ this.onDoneHandler_ = onDone; /** @type {function(remoting.Error):void} */ this.onErrorHandler_ = onError; -} + + /** @type {remoting.HostInstaller} */ + var hostInstaller = new remoting.HostInstaller(); + + /** @type {remoting.HostInstallDialog} */ + var that = this; + + this.hostInstaller_.downloadAndWaitForInstall().then(function() { + that.continueInstallButton_.click(); + that.hostInstaller_.cancel(); + }, function(){ + that.onErrorHandler_(remoting.Error.CANCELLED); + that.hostInstaller_.cancel(); + }); +}; /** * In manual host installation, onDone handler must call this method if it @@ -108,15 +100,16 @@ remoting.HostInstallDialog.prototype.onOkClicked_ = function() { this.cancelInstallButton_.disabled = true; this.onDoneHandler_(); -} +}; remoting.HostInstallDialog.prototype.onCancelClicked_ = function() { this.continueInstallButton_.removeEventListener( 'click', this.onOkClickedHandler_, false); this.cancelInstallButton_.removeEventListener( 'click', this.onCancelClickedHandler_, false); + this.hostInstaller_.cancel(); this.onErrorHandler_(remoting.Error.CANCELLED); -} +}; remoting.HostInstallDialog.prototype.onRetryClicked_ = function() { this.retryInstallButton_.removeEventListener( diff --git a/remoting/webapp/host_installer.js b/remoting/webapp/host_installer.js new file mode 100644 index 0000000000000..45dbadde70338 --- /dev/null +++ b/remoting/webapp/host_installer.js @@ -0,0 +1,162 @@ +// 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. + +/** + * @fileoverview + * + * HostInstaller allows the caller to download the host binary and monitor the + * install progress of the host by pinging the host periodically via native + * messaging. + * + * To download the host and wait for install: + * var hostInstaller = new remoting.HostInstaller(); + * hostInstaller.downloadAndWaitForInstall().then(function() { + * // Install has completed. + * }, function(){ + * // Download has failed. + * }) + * + * To stop listening to the install progress: + * hostInstaller.cancel(); + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @constructor + */ +remoting.HostInstaller = function() { + /** + * @type {Promise} + * @private + */ + this.downloadAndWaitForInstallPromise_ = null; + + /** + * @type {?number} + * @private + */ + this.checkInstallIntervalId_ = null; +}; + +/** + * @return {Promise} The promise will resolve to a boolean value indicating + * whether the host is installed or not. + */ +remoting.HostInstaller.prototype.isInstalled = function() { + // 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'); + + 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); + } + + port.onDisconnect.addListener(onDisconnected); + port.onMessage.addListener(onMessage); + port.postMessage({type: 'hello'}); + }); +}; + +/** + * @throws {Error} Throws if there is no matching host binary for the current + * platform. + * @private + */ +remoting.HostInstaller.prototype.download_ = function() { + /** @type {Object.} */ + var hostDownloadUrls = { + 'Win32' : 'http://dl.google.com/dl/edgedl/chrome-remote-desktop/' + + 'chromeremotedesktophost.msi', + 'MacIntel' : 'https://dl.google.com/chrome-remote-desktop/' + + 'chromeremotedesktop.dmg', + 'Linux x86_64' : 'https://dl.google.com/linux/direct/' + + 'chrome-remote-desktop_current_amd64.deb', + 'Linux i386' : 'https://dl.google.com/linux/direct/' + + 'chrome-remote-desktop_current_i386.deb' + }; + + var hostPackageUrl = hostDownloadUrls[navigator.platform]; + if (hostPackageUrl === undefined) { + throw new Error(remoting.Error.CANCELLED); + } + + // Start downloading the package. + if (remoting.isAppsV2) { + // TODO(jamiewalch): Use chrome.downloads when it is available to + // apps v2 (http://crbug.com/174046) + window.open(hostPackageUrl); + } else { + window.location = hostPackageUrl; + } +}; + +/** @return {Promise} */ +remoting.HostInstaller.prototype.downloadAndWaitForInstall = function() { + /** @type {remoting.HostInstaller} */ + var that = this; + /** + * @type {number} + * @const + */ + var CHECK_INSTALL_INTERVAL_IN_MILLISECONDS = 1000; + + /** @param {boolean} installed */ + return this.isInstalled().then(function(installed){ + if (installed) { + return Promise.resolve(true); + } + + if (that.downloadAndWaitForInstallPromise_ !== null) { + that.downloadAndWaitForInstallPromise_ = new Promise( + /** @param {Function} resolve */ + function(resolve){ + that.download_(); + that.checkInstallIntervalId_ = window.setInterval(function() { + /** @param {boolean} installed */ + that.isInstalled().then(function(installed) { + if (installed) { + that.cancel(); + resolve(); + } + }); + }, CHECK_INSTALL_INTERVAL_IN_MILLISECONDS); + }); + } + return that.downloadAndWaitForInstallPromise_; + }); +}; + +/** + * Stops waiting for the host to be installed. + * For example + * var promise = hostInstaller.downloadAndWaitForInstall(); + * hostInstaller.cancel(); // This will prevent |promise| from fulfilling. + */ +remoting.HostInstaller.prototype.cancel = function() { + if (this.checkInstallIntervalId_ !== null) { + window.clearInterval(this.checkInstallIntervalId_); + this.checkInstallIntervalId_ = null; + } + this.downloadAndWaitForInstallPromise_ = null; +}; \ No newline at end of file diff --git a/remoting/webapp/html/template_background.html b/remoting/webapp/html/template_background.html new file mode 100644 index 0000000000000..c9d416b2d47de --- /dev/null +++ b/remoting/webapp/html/template_background.html @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/remoting/webapp/html/template_main.html b/remoting/webapp/html/template_main.html index e49551dc4f182..f0a29de61431d 100644 --- a/remoting/webapp/html/template_main.html +++ b/remoting/webapp/html/template_main.html @@ -38,7 +38,7 @@
    -